]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libugidfw/ugidfw.c
libfdt: Update to 1.4.6, switch to using libfdt for overlay support
[FreeBSD/FreeBSD.git] / lib / libugidfw / ugidfw.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2002-2005 Networks Associates Technology, Inc.
5  * All rights reserved.
6  *
7  * This software was developed for the FreeBSD Project by Network Associates
8  * Laboratories, the Security Research Division of Network Associates, Inc.
9  * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
10  * DARPA CHATS research program.
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 AUTHOR 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 AUTHOR 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  * $FreeBSD$
34  */
35 #include <sys/param.h>
36 #include <sys/errno.h>
37 #include <sys/time.h>
38 #include <sys/sysctl.h>
39 #include <sys/ucred.h>
40 #include <sys/mount.h>
41
42 #include <security/mac_bsdextended/mac_bsdextended.h>
43
44 #include <grp.h>
45 #include <pwd.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49
50 #include "ugidfw.h"
51
52 /*
53  * Text format for rules: rules contain subject and object elements, mode.
54  * The total form is "subject [s_element] object [o_element] mode [mode]".
55  * At least * one of a uid or gid entry must be present; both may also be
56  * present.
57  */
58
59 #define MIB     "security.mac.bsdextended"
60
61 int
62 bsde_rule_to_string(struct mac_bsdextended_rule *rule, char *buf, size_t buflen)
63 {
64         struct group *grp;
65         struct passwd *pwd;
66         struct statfs *mntbuf;
67         char *cur, type[sizeof(rule->mbr_object.mbo_type) * CHAR_BIT + 1];
68         size_t left, len;
69         int anymode, unknownmode, numfs, i, notdone;
70
71         cur = buf;
72         left = buflen;
73
74         len = snprintf(cur, left, "subject ");
75         if (len < 0 || len > left)
76                 goto truncated;
77         left -= len;
78         cur += len;
79         if (rule->mbr_subject.mbs_flags) {
80                 if (rule->mbr_subject.mbs_neg == MBS_ALL_FLAGS) {
81                         len = snprintf(cur, left, "not ");
82                         if (len < 0 || len > left)
83                                 goto truncated;
84                         left -= len;
85                         cur += len;
86                         notdone = 1;
87                 } else {
88                         notdone = 0;
89                 }
90
91                 if (!notdone && (rule->mbr_subject.mbs_neg & MBO_UID_DEFINED)) {
92                         len = snprintf(cur, left, "! ");
93                         if (len < 0 || len > left)
94                                 goto truncated;
95                         left -= len;
96                         cur += len;
97                 }
98                 if (rule->mbr_subject.mbs_flags & MBO_UID_DEFINED) {
99                         pwd = getpwuid(rule->mbr_subject.mbs_uid_min);
100                         if (pwd != NULL) {
101                                 len = snprintf(cur, left, "uid %s",
102                                     pwd->pw_name);
103                                 if (len < 0 || len > left)
104                                         goto truncated;
105                                 left -= len;
106                                 cur += len;
107                         } else {
108                                 len = snprintf(cur, left, "uid %u",
109                                     rule->mbr_subject.mbs_uid_min);
110                                 if (len < 0 || len > left)
111                                         goto truncated;
112                                 left -= len;
113                                 cur += len;
114                         }
115                         if (rule->mbr_subject.mbs_uid_min !=
116                             rule->mbr_subject.mbs_uid_max) {
117                                 pwd = getpwuid(rule->mbr_subject.mbs_uid_max);
118                                 if (pwd != NULL) {
119                                         len = snprintf(cur, left, ":%s ",
120                                             pwd->pw_name);
121                                         if (len < 0 || len > left)
122                                                 goto truncated;
123                                         left -= len;
124                                         cur += len;
125                                 } else {
126                                         len = snprintf(cur, left, ":%u ",
127                                             rule->mbr_subject.mbs_uid_max);
128                                         if (len < 0 || len > left)
129                                                 goto truncated;
130                                         left -= len;
131                                         cur += len;
132                                 }
133                         } else {
134                                 len = snprintf(cur, left, " ");
135                                 if (len < 0 || len > left)
136                                         goto truncated;
137                                 left -= len;
138                                 cur += len;
139                         }
140                 }
141                 if (!notdone && (rule->mbr_subject.mbs_neg & MBO_GID_DEFINED)) {
142                         len = snprintf(cur, left, "! ");
143                         if (len < 0 || len > left)
144                                 goto truncated;
145                         left -= len;
146                         cur += len;
147                 }
148                 if (rule->mbr_subject.mbs_flags & MBO_GID_DEFINED) {
149                         grp = getgrgid(rule->mbr_subject.mbs_gid_min);
150                         if (grp != NULL) {
151                                 len = snprintf(cur, left, "gid %s",
152                                     grp->gr_name);
153                                 if (len < 0 || len > left)
154                                         goto truncated;
155                                 left -= len;
156                                 cur += len;
157                         } else {
158                                 len = snprintf(cur, left, "gid %u",
159                                     rule->mbr_subject.mbs_gid_min);
160                                 if (len < 0 || len > left)
161                                         goto truncated;
162                                 left -= len;
163                                 cur += len;
164                         }
165                         if (rule->mbr_subject.mbs_gid_min !=
166                             rule->mbr_subject.mbs_gid_max) {
167                                 grp = getgrgid(rule->mbr_subject.mbs_gid_max);
168                                 if (grp != NULL) {
169                                         len = snprintf(cur, left, ":%s ",
170                                             grp->gr_name);
171                                         if (len < 0 || len > left)
172                                                 goto truncated;
173                                         left -= len;
174                                         cur += len;
175                                 } else {
176                                         len = snprintf(cur, left, ":%u ",
177                                             rule->mbr_subject.mbs_gid_max);
178                                         if (len < 0 || len > left)
179                                                 goto truncated;
180                                         left -= len;
181                                         cur += len;
182                                 }
183                         } else {
184                                 len = snprintf(cur, left, " ");
185                                 if (len < 0 || len > left)
186                                         goto truncated;
187                                 left -= len;
188                                 cur += len;
189                         }
190                 }
191                 if (!notdone && (rule->mbr_subject.mbs_neg & MBS_PRISON_DEFINED)) {
192                         len = snprintf(cur, left, "! ");
193                         if (len < 0 || len > left)
194                                 goto truncated;
195                         left -= len;
196                         cur += len;
197                 }
198                 if (rule->mbr_subject.mbs_flags & MBS_PRISON_DEFINED) {
199                         len = snprintf(cur, left, "jailid %d ",
200                             rule->mbr_subject.mbs_prison);
201                         if (len < 0 || len > left)
202                                 goto truncated;
203                         left -= len;
204                         cur += len;
205                 }
206         }
207
208         len = snprintf(cur, left, "object ");
209         if (len < 0 || len > left)
210                 goto truncated;
211         left -= len;
212         cur += len;
213         if (rule->mbr_object.mbo_flags) {
214                 if (rule->mbr_object.mbo_neg == MBO_ALL_FLAGS) {
215                         len = snprintf(cur, left, "not ");
216                         if (len < 0 || len > left)
217                                 goto truncated;
218                         left -= len;
219                         cur += len;
220                         notdone = 1;
221                 } else {
222                         notdone = 0;
223                 }
224
225                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_UID_DEFINED)) {
226                         len = snprintf(cur, left, "! ");
227                         if (len < 0 || len > left)
228                                 goto truncated;
229                         left -= len;
230                         cur += len;
231                 }
232                 if (rule->mbr_object.mbo_flags & MBO_UID_DEFINED) {
233                         pwd = getpwuid(rule->mbr_object.mbo_uid_min);
234                         if (pwd != NULL) {
235                                 len = snprintf(cur, left, "uid %s",
236                                     pwd->pw_name);
237                                 if (len < 0 || len > left)
238                                         goto truncated;
239                                 left -= len;
240                                 cur += len;
241                         } else {
242                                 len = snprintf(cur, left, "uid %u",
243                                     rule->mbr_object.mbo_uid_min);
244                                 if (len < 0 || len > left)
245                                         goto truncated;
246                                 left -= len;
247                                 cur += len;
248                         }
249                         if (rule->mbr_object.mbo_uid_min !=
250                             rule->mbr_object.mbo_uid_max) {
251                                 pwd = getpwuid(rule->mbr_object.mbo_uid_max);
252                                 if (pwd != NULL) {
253                                         len = snprintf(cur, left, ":%s ",
254                                             pwd->pw_name);
255                                         if (len < 0 || len > left)
256                                                 goto truncated;
257                                         left -= len;
258                                         cur += len;
259                                 } else {
260                                         len = snprintf(cur, left, ":%u ",
261                                             rule->mbr_object.mbo_uid_max);
262                                         if (len < 0 || len > left)
263                                                 goto truncated;
264                                         left -= len;
265                                         cur += len;
266                                 }
267                         } else {
268                                 len = snprintf(cur, left, " ");
269                                 if (len < 0 || len > left)
270                                         goto truncated;
271                                 left -= len;
272                                 cur += len;
273                         }
274                 }
275                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_GID_DEFINED)) {
276                         len = snprintf(cur, left, "! ");
277                         if (len < 0 || len > left)
278                                 goto truncated;
279                         left -= len;
280                         cur += len;
281                 }
282                 if (rule->mbr_object.mbo_flags & MBO_GID_DEFINED) {
283                         grp = getgrgid(rule->mbr_object.mbo_gid_min);
284                         if (grp != NULL) {
285                                 len = snprintf(cur, left, "gid %s",
286                                     grp->gr_name);
287                                 if (len < 0 || len > left)
288                                         goto truncated;
289                                 left -= len;
290                                 cur += len;
291                         } else {
292                                 len = snprintf(cur, left, "gid %u",
293                                     rule->mbr_object.mbo_gid_min);
294                                 if (len < 0 || len > left)
295                                         goto truncated;
296                                 left -= len;
297                                 cur += len;
298                         }
299                         if (rule->mbr_object.mbo_gid_min !=
300                             rule->mbr_object.mbo_gid_max) {
301                                 grp = getgrgid(rule->mbr_object.mbo_gid_max);
302                                 if (grp != NULL) {
303                                         len = snprintf(cur, left, ":%s ",
304                                             grp->gr_name);
305                                         if (len < 0 || len > left)
306                                                 goto truncated;
307                                         left -= len;
308                                         cur += len;
309                                 } else {
310                                         len = snprintf(cur, left, ":%u ",
311                                             rule->mbr_object.mbo_gid_max);
312                                         if (len < 0 || len > left)
313                                                 goto truncated;
314                                         left -= len;
315                                         cur += len;
316                                 }
317                         } else {
318                                 len = snprintf(cur, left, " ");
319                                 if (len < 0 || len > left)
320                                         goto truncated;
321                                 left -= len;
322                                 cur += len;
323                         }
324                 }
325                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_FSID_DEFINED)) {
326                         len = snprintf(cur, left, "! ");
327                         if (len < 0 || len > left)
328                                 goto truncated;
329                         left -= len;
330                         cur += len;
331                 }
332                 if (rule->mbr_object.mbo_flags & MBO_FSID_DEFINED) {
333                         numfs = getmntinfo(&mntbuf, MNT_NOWAIT);
334                         for (i = 0; i < numfs; i++)
335                                 if (memcmp(&(rule->mbr_object.mbo_fsid),
336                                     &(mntbuf[i].f_fsid),
337                                     sizeof(mntbuf[i].f_fsid)) == 0)
338                                         break;
339                         len = snprintf(cur, left, "filesys %s ",
340                             i == numfs ? "???" : mntbuf[i].f_mntonname);
341                         if (len < 0 || len > left)
342                                 goto truncated;
343                         left -= len;
344                         cur += len;
345                 }
346                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_SUID)) {
347                         len = snprintf(cur, left, "! ");
348                         if (len < 0 || len > left)
349                                 goto truncated;
350                         left -= len;
351                         cur += len;
352                 }
353                 if (rule->mbr_object.mbo_flags & MBO_SUID) {
354                         len = snprintf(cur, left, "suid ");
355                         if (len < 0 || len > left)
356                                 goto truncated;
357                         left -= len;
358                         cur += len;
359                 }
360                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_SGID)) {
361                         len = snprintf(cur, left, "! ");
362                         if (len < 0 || len > left)
363                                 goto truncated;
364                         left -= len;
365                         cur += len;
366                 }
367                 if (rule->mbr_object.mbo_flags & MBO_SGID) {
368                         len = snprintf(cur, left, "sgid ");
369                         if (len < 0 || len > left)
370                                 goto truncated;
371                         left -= len;
372                         cur += len;
373                 }
374                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_UID_SUBJECT)) {
375                         len = snprintf(cur, left, "! ");
376                         if (len < 0 || len > left)
377                                 goto truncated;
378                         left -= len;
379                         cur += len;
380                 }
381                 if (rule->mbr_object.mbo_flags & MBO_UID_SUBJECT) {
382                         len = snprintf(cur, left, "uid_of_subject ");
383                         if (len < 0 || len > left)
384                                 goto truncated;
385                         left -= len;
386                         cur += len;
387                 }
388                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_GID_SUBJECT)) {
389                         len = snprintf(cur, left, "! ");
390                         if (len < 0 || len > left)
391                                 goto truncated;
392                         left -= len;
393                         cur += len;
394                 }
395                 if (rule->mbr_object.mbo_flags & MBO_GID_SUBJECT) {
396                         len = snprintf(cur, left, "gid_of_subject ");
397                         if (len < 0 || len > left)
398                                 goto truncated;
399                         left -= len;
400                         cur += len;
401                 }
402                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_TYPE_DEFINED)) {
403                         len = snprintf(cur, left, "! ");
404                         if (len < 0 || len > left)
405                                 goto truncated;
406                         left -= len;
407                         cur += len;
408                 }
409                 if (rule->mbr_object.mbo_flags & MBO_TYPE_DEFINED) {
410                         i = 0;
411                         if (rule->mbr_object.mbo_type & MBO_TYPE_REG)
412                                 type[i++] = 'r';
413                         if (rule->mbr_object.mbo_type & MBO_TYPE_DIR)
414                                 type[i++] = 'd';
415                         if (rule->mbr_object.mbo_type & MBO_TYPE_BLK)
416                                 type[i++] = 'b';
417                         if (rule->mbr_object.mbo_type & MBO_TYPE_CHR)
418                                 type[i++] = 'c';
419                         if (rule->mbr_object.mbo_type & MBO_TYPE_LNK)
420                                 type[i++] = 'l';
421                         if (rule->mbr_object.mbo_type & MBO_TYPE_SOCK)
422                                 type[i++] = 's';
423                         if (rule->mbr_object.mbo_type & MBO_TYPE_FIFO)
424                                 type[i++] = 'p';
425                         if (rule->mbr_object.mbo_type == MBO_ALL_TYPE) {
426                                 i = 0;
427                                 type[i++] = 'a';
428                         }
429                         type[i++] = '\0';
430                         len = snprintf(cur, left, "type %s ", type);
431                         if (len < 0 || len > left)
432                                 goto truncated;
433                         left -= len;
434                         cur += len;
435                 }
436         }
437
438         len = snprintf(cur, left, "mode ");
439         if (len < 0 || len > left)
440                 goto truncated;
441         left -= len;
442         cur += len;
443
444         anymode = (rule->mbr_mode & MBI_ALLPERM);
445         unknownmode = (rule->mbr_mode & ~MBI_ALLPERM);
446
447         if (rule->mbr_mode & MBI_ADMIN) {
448                 len = snprintf(cur, left, "a");
449                 if (len < 0 || len > left)
450                         goto truncated;
451
452                 left -= len;
453                 cur += len;
454         }
455         if (rule->mbr_mode & MBI_READ) {
456                 len = snprintf(cur, left, "r");
457                 if (len < 0 || len > left)
458                         goto truncated;
459
460                 left -= len;
461                 cur += len;
462         }
463         if (rule->mbr_mode & MBI_STAT) {
464                 len = snprintf(cur, left, "s");
465                 if (len < 0 || len > left)
466                         goto truncated;
467
468                 left -= len;
469                 cur += len;
470         }
471         if (rule->mbr_mode & MBI_WRITE) {
472                 len = snprintf(cur, left, "w");
473                 if (len < 0 || len > left)
474                         goto truncated;
475
476                 left -= len;
477                 cur += len;
478         }
479         if (rule->mbr_mode & MBI_EXEC) {
480                 len = snprintf(cur, left, "x");
481                 if (len < 0 || len > left)
482                         goto truncated;
483
484                 left -= len;
485                 cur += len;
486         }
487         if (!anymode) {
488                 len = snprintf(cur, left, "n");
489                 if (len < 0 || len > left)
490                         goto truncated;
491
492                 left -= len;
493                 cur += len;
494         }
495         if (unknownmode) {
496                 len = snprintf(cur, left, "?");
497                 if (len < 0 || len > left)
498                         goto truncated;
499
500                 left -= len;
501                 cur += len;
502         }
503
504         return (0);
505
506 truncated:
507         return (-1);
508 }
509
510 static int
511 bsde_parse_uidrange(char *spec, uid_t *min, uid_t *max,
512     size_t buflen, char *errstr){
513         struct passwd *pwd;
514         uid_t uid1, uid2;
515         char *spec1, *spec2, *endp;
516         unsigned long value;
517
518         spec2 = spec;
519         spec1 = strsep(&spec2, ":");
520
521         pwd = getpwnam(spec1);
522         if (pwd != NULL)
523                 uid1 = pwd->pw_uid;
524         else {
525                 value = strtoul(spec1, &endp, 10);
526                 if (*endp != '\0') {
527                         snprintf(errstr, buflen, "invalid uid: '%s'", spec1);
528                         return (-1);
529                 }
530                 uid1 = value;
531         }
532
533         if (spec2 == NULL) {
534                 *max = *min = uid1;
535                 return (0);
536         }
537
538         pwd = getpwnam(spec2);
539         if (pwd != NULL)
540                 uid2 = pwd->pw_uid;
541         else {
542                 value = strtoul(spec2, &endp, 10);
543                 if (*endp != '\0') {
544                         snprintf(errstr, buflen, "invalid uid: '%s'", spec2);
545                         return (-1);
546                 }
547                 uid2 = value;
548         }
549
550         *min = uid1;
551         *max = uid2;
552
553         return (0);
554 }
555
556 static int
557 bsde_parse_gidrange(char *spec, gid_t *min, gid_t *max,
558     size_t buflen, char *errstr){
559         struct group *grp;
560         gid_t gid1, gid2;
561         char *spec1, *spec2, *endp;
562         unsigned long value;
563
564         spec2 = spec;
565         spec1 = strsep(&spec2, ":");
566
567         grp = getgrnam(spec1);
568         if (grp != NULL)
569                 gid1 = grp->gr_gid;
570         else {
571                 value = strtoul(spec1, &endp, 10);
572                 if (*endp != '\0') {
573                         snprintf(errstr, buflen, "invalid gid: '%s'", spec1);
574                         return (-1);
575                 }
576                 gid1 = value;
577         }
578
579         if (spec2 == NULL) {
580                 *max = *min = gid1;
581                 return (0);
582         }
583
584         grp = getgrnam(spec2);
585         if (grp != NULL)
586                 gid2 = grp->gr_gid;
587         else {
588                 value = strtoul(spec2, &endp, 10);
589                 if (*endp != '\0') {
590                         snprintf(errstr, buflen, "invalid gid: '%s'", spec2);
591                         return (-1);
592                 }
593                 gid2 = value;
594         }
595
596         *min = gid1;
597         *max = gid2;
598
599         return (0);
600 }
601
602 static int
603 bsde_parse_subject(int argc, char *argv[],
604     struct mac_bsdextended_subject *subject, size_t buflen, char *errstr)
605 {
606         int not_seen, flags;
607         int current, neg, nextnot;
608         char *endp;
609         uid_t uid_min, uid_max;
610         gid_t gid_min, gid_max;
611         int jid = 0;
612         long value;
613
614         current = 0;
615         flags = 0;
616         neg = 0;
617         nextnot = 0;
618
619         if (strcmp("not", argv[current]) == 0) {
620                 not_seen = 1;
621                 current++;
622         } else
623                 not_seen = 0;
624
625         while (current < argc) {
626                 if (strcmp(argv[current], "uid") == 0) {
627                         if (current + 2 > argc) {
628                                 snprintf(errstr, buflen, "uid short");
629                                 return (-1);
630                         }
631                         if (flags & MBS_UID_DEFINED) {
632                                 snprintf(errstr, buflen, "one uid only");
633                                 return (-1);
634                         }
635                         if (bsde_parse_uidrange(argv[current+1],
636                             &uid_min, &uid_max, buflen, errstr) < 0)
637                                 return (-1);
638                         flags |= MBS_UID_DEFINED;
639                         if (nextnot) {
640                                 neg ^= MBS_UID_DEFINED;
641                                 nextnot = 0;
642                         }
643                         current += 2;
644                 } else if (strcmp(argv[current], "gid") == 0) {
645                         if (current + 2 > argc) {
646                                 snprintf(errstr, buflen, "gid short");
647                                 return (-1);
648                         }
649                         if (flags & MBS_GID_DEFINED) {
650                                 snprintf(errstr, buflen, "one gid only");
651                                 return (-1);
652                         }
653                         if (bsde_parse_gidrange(argv[current+1],
654                             &gid_min, &gid_max, buflen, errstr) < 0)
655                                 return (-1);
656                         flags |= MBS_GID_DEFINED;
657                         if (nextnot) {
658                                 neg ^= MBS_GID_DEFINED;
659                                 nextnot = 0;
660                         }
661                         current += 2;
662                 } else if (strcmp(argv[current], "jailid") == 0) {
663                         if (current + 2 > argc) {
664                                 snprintf(errstr, buflen, "prison short");
665                                 return (-1);
666                         }
667                         if (flags & MBS_PRISON_DEFINED) {
668                                 snprintf(errstr, buflen, "one jail only");
669                                 return (-1);
670                         }
671                         value = strtol(argv[current+1], &endp, 10);
672                         if (*endp != '\0') {
673                                 snprintf(errstr, buflen, "invalid jid: '%s'",
674                                     argv[current+1]);
675                                 return (-1);
676                         }
677                         jid = value;
678                         flags |= MBS_PRISON_DEFINED;
679                         if (nextnot) {
680                                 neg ^= MBS_PRISON_DEFINED;
681                                 nextnot = 0;
682                         }
683                         current += 2;
684                 } else if (strcmp(argv[current], "!") == 0) {
685                         if (nextnot) {
686                                 snprintf(errstr, buflen, "double negative");
687                                 return (-1);
688                         }
689                         nextnot = 1;
690                         current += 1;
691                 } else {
692                         snprintf(errstr, buflen, "'%s' not expected",
693                             argv[current]);
694                         return (-1);
695                 }
696         }
697
698         subject->mbs_flags = flags;
699         if (not_seen)
700                 subject->mbs_neg = MBS_ALL_FLAGS ^ neg;
701         else
702                 subject->mbs_neg = neg;
703         if (flags & MBS_UID_DEFINED) {
704                 subject->mbs_uid_min = uid_min;
705                 subject->mbs_uid_max = uid_max;
706         }
707         if (flags & MBS_GID_DEFINED) {
708                 subject->mbs_gid_min = gid_min;
709                 subject->mbs_gid_max = gid_max;
710         }
711         if (flags & MBS_PRISON_DEFINED)
712                 subject->mbs_prison = jid;
713
714         return (0);
715 }
716
717 static int
718 bsde_parse_type(char *spec, int *type, size_t buflen, char *errstr)
719 {
720         int i;
721
722         *type = 0;
723         for (i = 0; i < strlen(spec); i++) {
724                 switch (spec[i]) {
725                 case 'r':
726                 case '-':
727                         *type |= MBO_TYPE_REG;
728                         break;
729                 case 'd':
730                         *type |= MBO_TYPE_DIR;
731                         break;
732                 case 'b':
733                         *type |= MBO_TYPE_BLK;
734                         break;
735                 case 'c':
736                         *type |= MBO_TYPE_CHR;
737                         break;
738                 case 'l':
739                         *type |= MBO_TYPE_LNK;
740                         break;
741                 case 's':
742                         *type |= MBO_TYPE_SOCK;
743                         break;
744                 case 'p':
745                         *type |= MBO_TYPE_FIFO;
746                         break;
747                 case 'a':
748                         *type |= MBO_ALL_TYPE;
749                         break;
750                 default:
751                         snprintf(errstr, buflen, "Unknown type code: %c",
752                             spec[i]);
753                         return (-1);
754                 }
755         }
756
757         return (0);
758 }
759
760 static int
761 bsde_parse_fsid(char *spec, struct fsid *fsid, size_t buflen, char *errstr)
762 {
763         struct statfs buf;
764
765         if (statfs(spec, &buf) < 0) {
766                 snprintf(errstr, buflen, "Unable to get id for %s: %s",
767                     spec, strerror(errno));
768                 return (-1);
769         }
770
771         *fsid = buf.f_fsid;
772
773         return (0);
774 }
775
776 static int
777 bsde_parse_object(int argc, char *argv[],
778     struct mac_bsdextended_object *object, size_t buflen, char *errstr)
779 {
780         int not_seen, flags;
781         int current, neg, nextnot;
782         int type;
783         uid_t uid_min, uid_max;
784         gid_t gid_min, gid_max;
785         struct fsid fsid;
786
787         current = 0;
788         flags = 0;
789         neg = 0;
790         nextnot = 0;
791         type = 0;
792
793         if (strcmp("not", argv[current]) == 0) {
794                 not_seen = 1;
795                 current++;
796         } else
797                 not_seen = 0;
798
799         while (current < argc) {
800                 if (strcmp(argv[current], "uid") == 0) {
801                         if (current + 2 > argc) {
802                                 snprintf(errstr, buflen, "uid short");
803                                 return (-1);
804                         }
805                         if (flags & MBO_UID_DEFINED) {
806                                 snprintf(errstr, buflen, "one uid only");
807                                 return (-1);
808                         }
809                         if (bsde_parse_uidrange(argv[current+1],
810                             &uid_min, &uid_max, buflen, errstr) < 0)
811                                 return (-1);
812                         flags |= MBO_UID_DEFINED;
813                         if (nextnot) {
814                                 neg ^= MBO_UID_DEFINED;
815                                 nextnot = 0;
816                         }
817                         current += 2;
818                 } else if (strcmp(argv[current], "gid") == 0) {
819                         if (current + 2 > argc) {
820                                 snprintf(errstr, buflen, "gid short");
821                                 return (-1);
822                         }
823                         if (flags & MBO_GID_DEFINED) {
824                                 snprintf(errstr, buflen, "one gid only");
825                                 return (-1);
826                         }
827                         if (bsde_parse_gidrange(argv[current+1],
828                             &gid_min, &gid_max, buflen, errstr) < 0)
829                                 return (-1);
830                         flags |= MBO_GID_DEFINED;
831                         if (nextnot) {
832                                 neg ^= MBO_GID_DEFINED;
833                                 nextnot = 0;
834                         }
835                         current += 2;
836                 } else if (strcmp(argv[current], "filesys") == 0) {
837                         if (current + 2 > argc) {
838                                 snprintf(errstr, buflen, "filesys short");
839                                 return (-1);
840                         }
841                         if (flags & MBO_FSID_DEFINED) {
842                                 snprintf(errstr, buflen, "one fsid only");
843                                 return (-1);
844                         }
845                         if (bsde_parse_fsid(argv[current+1], &fsid,
846                             buflen, errstr) < 0)
847                                 return (-1);
848                         flags |= MBO_FSID_DEFINED;
849                         if (nextnot) {
850                                 neg ^= MBO_FSID_DEFINED;
851                                 nextnot = 0;
852                         }
853                         current += 2;
854                 } else if (strcmp(argv[current], "suid") == 0) {
855                         flags |= MBO_SUID;
856                         if (nextnot) {
857                                 neg ^= MBO_SUID;
858                                 nextnot = 0;
859                         }
860                         current += 1;
861                 } else if (strcmp(argv[current], "sgid") == 0) {
862                         flags |= MBO_SGID;
863                         if (nextnot) {
864                                 neg ^= MBO_SGID;
865                                 nextnot = 0;
866                         }
867                         current += 1;
868                 } else if (strcmp(argv[current], "uid_of_subject") == 0) {
869                         flags |= MBO_UID_SUBJECT;
870                         if (nextnot) {
871                                 neg ^= MBO_UID_SUBJECT;
872                                 nextnot = 0;
873                         }
874                         current += 1;
875                 } else if (strcmp(argv[current], "gid_of_subject") == 0) {
876                         flags |= MBO_GID_SUBJECT;
877                         if (nextnot) {
878                                 neg ^= MBO_GID_SUBJECT;
879                                 nextnot = 0;
880                         }
881                         current += 1;
882                 } else if (strcmp(argv[current], "type") == 0) {
883                         if (current + 2 > argc) {
884                                 snprintf(errstr, buflen, "type short");
885                                 return (-1);
886                         }
887                         if (flags & MBO_TYPE_DEFINED) {
888                                 snprintf(errstr, buflen, "one type only");
889                                 return (-1);
890                         }
891                         if (bsde_parse_type(argv[current+1], &type,
892                             buflen, errstr) < 0)
893                                 return (-1);
894                         flags |= MBO_TYPE_DEFINED;
895                         if (nextnot) {
896                                 neg ^= MBO_TYPE_DEFINED;
897                                 nextnot = 0;
898                         }
899                         current += 2;
900                 } else if (strcmp(argv[current], "!") == 0) {
901                         if (nextnot) {
902                                 snprintf(errstr, buflen,
903                                     "double negative'");
904                                 return (-1);
905                         }
906                         nextnot = 1;
907                         current += 1;
908                 } else {
909                         snprintf(errstr, buflen, "'%s' not expected",
910                             argv[current]);
911                         return (-1);
912                 }
913         }
914
915         object->mbo_flags = flags;
916         if (not_seen)
917                 object->mbo_neg = MBO_ALL_FLAGS ^ neg;
918         else
919                 object->mbo_neg = neg;
920         if (flags & MBO_UID_DEFINED) {
921                 object->mbo_uid_min = uid_min;
922                 object->mbo_uid_max = uid_max;
923         }
924         if (flags & MBO_GID_DEFINED) {
925                 object->mbo_gid_min = gid_min;
926                 object->mbo_gid_max = gid_max;
927         }
928         if (flags & MBO_FSID_DEFINED)
929                 object->mbo_fsid = fsid;
930         if (flags & MBO_TYPE_DEFINED)
931                 object->mbo_type = type;
932
933         return (0);
934 }
935
936 int
937 bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen,
938     char *errstr)
939 {
940         int i;
941
942         if (argc == 0) {
943                 snprintf(errstr, buflen, "mode expects mode value");
944                 return (-1);
945         }
946
947         if (argc != 1) {
948                 snprintf(errstr, buflen, "'%s' unexpected", argv[1]);
949                 return (-1);
950         }
951
952         *mode = 0;
953         for (i = 0; i < strlen(argv[0]); i++) {
954                 switch (argv[0][i]) {
955                 case 'a':
956                         *mode |= MBI_ADMIN;
957                         break;
958                 case 'r':
959                         *mode |= MBI_READ;
960                         break;
961                 case 's':
962                         *mode |= MBI_STAT;
963                         break;
964                 case 'w':
965                         *mode |= MBI_WRITE;
966                         break;
967                 case 'x':
968                         *mode |= MBI_EXEC;
969                         break;
970                 case 'n':
971                         /* ignore */
972                         break;
973                 default:
974                         snprintf(errstr, buflen, "Unknown mode letter: %c",
975                             argv[0][i]);
976                         return (-1);
977                 }
978         }
979
980         return (0);
981 }
982
983 int
984 bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule,
985     size_t buflen, char *errstr)
986 {
987         int subject, subject_elements, subject_elements_length;
988         int object, object_elements, object_elements_length;
989         int mode, mode_elements, mode_elements_length;
990         int error, i;
991
992         bzero(rule, sizeof(*rule));
993
994         if (argc < 1) {
995                 snprintf(errstr, buflen, "Rule must begin with subject");
996                 return (-1);
997         }
998
999         if (strcmp(argv[0], "subject") != 0) {
1000                 snprintf(errstr, buflen, "Rule must begin with subject");
1001                 return (-1);
1002         }
1003         subject = 0;
1004         subject_elements = 1;
1005
1006         /* Search forward for object. */
1007
1008         object = -1;
1009         for (i = 1; i < argc; i++)
1010                 if (strcmp(argv[i], "object") == 0)
1011                         object = i;
1012
1013         if (object == -1) {
1014                 snprintf(errstr, buflen, "Rule must contain an object");
1015                 return (-1);
1016         }
1017
1018         /* Search forward for mode. */
1019         mode = -1;
1020         for (i = object; i < argc; i++)
1021                 if (strcmp(argv[i], "mode") == 0)
1022                         mode = i;
1023
1024         if (mode == -1) {
1025                 snprintf(errstr, buflen, "Rule must contain mode");
1026                 return (-1);
1027         }
1028
1029         subject_elements_length = object - subject - 1;
1030         object_elements = object + 1;
1031         object_elements_length = mode - object_elements;
1032         mode_elements = mode + 1;
1033         mode_elements_length = argc - mode_elements;
1034
1035         error = bsde_parse_subject(subject_elements_length,
1036             argv + subject_elements, &rule->mbr_subject, buflen, errstr);
1037         if (error)
1038                 return (-1);
1039
1040         error = bsde_parse_object(object_elements_length,
1041             argv + object_elements, &rule->mbr_object, buflen, errstr);
1042         if (error)
1043                 return (-1);
1044
1045         error = bsde_parse_mode(mode_elements_length, argv + mode_elements,
1046             &rule->mbr_mode, buflen, errstr);
1047         if (error)
1048                 return (-1);
1049
1050         return (0);
1051 }
1052
1053 int
1054 bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule,
1055     size_t buflen, char *errstr)
1056 {
1057         char *stringdup, *stringp, *argv[100], **ap;
1058         int argc, error;
1059
1060         stringp = stringdup = strdup(string);
1061         while (*stringp == ' ' || *stringp == '\t')
1062                 stringp++;
1063
1064         argc = 0;
1065         for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) {
1066                 argc++;
1067                 if (**ap != '\0')
1068                         if (++ap >= &argv[100])
1069                                 break;
1070         }
1071
1072         error = bsde_parse_rule(argc, argv, rule, buflen, errstr);
1073
1074         free(stringdup);
1075
1076         return (error);
1077 }
1078
1079 int
1080 bsde_get_mib(const char *string, int *name, size_t *namelen)
1081 {
1082         size_t len;
1083         int error;
1084
1085         len = *namelen;
1086         error = sysctlnametomib(string, name, &len);
1087         if (error)
1088                 return (error);
1089
1090         *namelen = len;
1091         return (0);
1092 }
1093
1094 static int
1095 bsde_check_version(size_t buflen, char *errstr)
1096 {
1097         size_t len;
1098         int error;
1099         int version;
1100
1101         len = sizeof(version);
1102         error = sysctlbyname(MIB ".rule_version", &version, &len, NULL, 0);
1103         if (error) {
1104                 snprintf(errstr, buflen, "version check failed: %s",
1105                     strerror(errno));
1106                 return (-1);
1107         }
1108         if (version != MB_VERSION) {
1109                 snprintf(errstr, buflen, "module v%d != library v%d",
1110                     version, MB_VERSION);
1111                 return (-1);
1112         }
1113         return (0);
1114 }
1115
1116 int
1117 bsde_get_rule_count(size_t buflen, char *errstr)
1118 {
1119         size_t len;
1120         int error;
1121         int rule_count;
1122
1123         len = sizeof(rule_count);
1124         error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0);
1125         if (error) {
1126                 snprintf(errstr, buflen, "%s", strerror(errno));
1127                 return (-1);
1128         }
1129         if (len != sizeof(rule_count)) {
1130                 snprintf(errstr, buflen, "Data error in %s.rule_count",
1131                     MIB);
1132                 return (-1);
1133         }
1134
1135         return (rule_count);
1136 }
1137
1138 int
1139 bsde_get_rule_slots(size_t buflen, char *errstr)
1140 {
1141         size_t len;
1142         int error;
1143         int rule_slots;
1144
1145         len = sizeof(rule_slots);
1146         error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0);
1147         if (error) {
1148                 snprintf(errstr, buflen, "%s", strerror(errno));
1149                 return (-1);
1150         }
1151         if (len != sizeof(rule_slots)) {
1152                 snprintf(errstr, buflen, "Data error in %s.rule_slots", MIB);
1153                 return (-1);
1154         }
1155
1156         return (rule_slots);
1157 }
1158
1159 /*
1160  * Returns 0 for success;
1161  * Returns -1 for failure;
1162  * Returns -2 for not present
1163  */
1164 int
1165 bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen,
1166     char *errstr)
1167 {
1168         int name[10];
1169         size_t len, size;
1170         int error;
1171
1172         if (bsde_check_version(errlen, errstr) != 0)
1173                 return (-1);
1174
1175         len = 10;
1176         error = bsde_get_mib(MIB ".rules", name, &len);
1177         if (error) {
1178                 snprintf(errstr, errlen, "%s: %s", MIB ".rules",
1179                     strerror(errno));
1180                 return (-1);
1181         }
1182
1183         size = sizeof(*rule);
1184         name[len] = rulenum;
1185         len++;
1186         error = sysctl(name, len, rule, &size, NULL, 0);
1187         if (error  == -1 && errno == ENOENT)
1188                 return (-2);
1189         if (error) {
1190                 snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules",
1191                     rulenum, strerror(errno));
1192                 return (-1);
1193         } else if (size != sizeof(*rule)) {
1194                 snprintf(errstr, errlen, "Data error in %s.%d: %s",
1195                     MIB ".rules", rulenum, strerror(errno));
1196                 return (-1);
1197         }
1198
1199         return (0);
1200 }
1201
1202 int
1203 bsde_delete_rule(int rulenum, size_t buflen, char *errstr)
1204 {
1205         struct mac_bsdextended_rule rule;
1206         int name[10];
1207         size_t len;
1208         int error;
1209
1210         if (bsde_check_version(buflen, errstr) != 0)
1211                 return (-1);
1212
1213         len = 10;
1214         error = bsde_get_mib(MIB ".rules", name, &len);
1215         if (error) {
1216                 snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1217                     strerror(errno));
1218                 return (-1);
1219         }
1220
1221         name[len] = rulenum;
1222         len++;
1223
1224         error = sysctl(name, len, NULL, NULL, &rule, 0);
1225         if (error) {
1226                 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1227                     rulenum, strerror(errno));
1228                 return (-1);
1229         }
1230
1231         return (0);
1232 }
1233
1234 int
1235 bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1236     char *errstr)
1237 {
1238         int name[10];
1239         size_t len;
1240         int error;
1241
1242         if (bsde_check_version(buflen, errstr) != 0)
1243                 return (-1);
1244
1245         len = 10;
1246         error = bsde_get_mib(MIB ".rules", name, &len);
1247         if (error) {
1248                 snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1249                     strerror(errno));
1250                 return (-1);
1251         }
1252
1253         name[len] = rulenum;
1254         len++;
1255
1256         error = sysctl(name, len, NULL, NULL, rule, sizeof(*rule));
1257         if (error) {
1258                 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1259                     rulenum, strerror(errno));
1260                 return (-1);
1261         }
1262
1263         return (0);
1264 }
1265
1266 int
1267 bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1268     char *errstr)
1269 {
1270         char charstr[BUFSIZ];
1271         int name[10];
1272         size_t len;
1273         int error, rule_slots;
1274
1275         if (bsde_check_version(buflen, errstr) != 0)
1276                 return (-1);
1277
1278         len = 10;
1279         error = bsde_get_mib(MIB ".rules", name, &len);
1280         if (error) {
1281                 snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1282                     strerror(errno));
1283                 return (-1);
1284         }
1285
1286         rule_slots = bsde_get_rule_slots(BUFSIZ, charstr);
1287         if (rule_slots == -1) {
1288                 snprintf(errstr, buflen, "unable to get rule slots: %s",
1289                     strerror(errno));
1290                 return (-1);
1291         }
1292
1293         name[len] = rule_slots;
1294         len++;
1295
1296         error = sysctl(name, len, NULL, NULL, rule, sizeof(*rule));
1297         if (error) {
1298                 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1299                     rule_slots, strerror(errno));
1300                 return (-1);
1301         }
1302
1303         if (rulenum != NULL)
1304                 *rulenum = rule_slots;
1305
1306         return (0);
1307 }