]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libugidfw/ugidfw.c
Merge branch 'releng/11.3' into releng-CDN/11.3
[FreeBSD/FreeBSD.git] / lib / libugidfw / ugidfw.c
1 /*-
2  * Copyright (c) 2002-2005 Networks Associates Technology, Inc.
3  * All rights reserved.
4  *
5  * This software was developed for the FreeBSD Project by Network Associates
6  * Laboratories, the Security Research Division of Network Associates, Inc.
7  * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
8  * DARPA CHATS research program.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * $FreeBSD$
32  */
33 #include <sys/param.h>
34 #include <sys/errno.h>
35 #include <sys/jail.h>
36 #include <sys/time.h>
37 #include <sys/sysctl.h>
38 #include <sys/ucred.h>
39 #include <sys/uio.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_get_jailid(const char *name, size_t buflen, char *errstr)
604 {
605         char *ep;
606         int jid;
607         struct iovec jiov[4];
608
609         /* Copy jail_getid(3) instead of messing with library dependancies */
610         jid = strtoul(name, &ep, 10);
611         if (*name && !*ep)
612                 return jid;
613         jiov[0].iov_base = __DECONST(char *, "name");
614         jiov[0].iov_len = sizeof("name");
615         jiov[1].iov_len = strlen(name) + 1;
616         jiov[1].iov_base = alloca(jiov[1].iov_len);
617         strcpy(jiov[1].iov_base, name);
618         if (errstr && buflen) {
619                 jiov[2].iov_base = __DECONST(char *, "errmsg");
620                 jiov[2].iov_len = sizeof("errmsg");
621                 jiov[3].iov_base = errstr;
622                 jiov[3].iov_len = buflen;
623                 errstr[0] = 0;
624                 jid = jail_get(jiov, 4, 0);
625                 if (jid < 0 && !errstr[0])
626                         snprintf(errstr, buflen, "jail_get: %s",
627                             strerror(errno));
628         } else
629                 jid = jail_get(jiov, 2, 0);
630         return jid;
631 }
632
633 static int
634 bsde_parse_subject(int argc, char *argv[],
635     struct mac_bsdextended_subject *subject, size_t buflen, char *errstr)
636 {
637         int not_seen, flags;
638         int current, neg, nextnot;
639         uid_t uid_min, uid_max;
640         gid_t gid_min, gid_max;
641         int jid = 0;
642
643         current = 0;
644         flags = 0;
645         neg = 0;
646         nextnot = 0;
647
648         if (strcmp("not", argv[current]) == 0) {
649                 not_seen = 1;
650                 current++;
651         } else
652                 not_seen = 0;
653
654         while (current < argc) {
655                 if (strcmp(argv[current], "uid") == 0) {
656                         if (current + 2 > argc) {
657                                 snprintf(errstr, buflen, "uid short");
658                                 return (-1);
659                         }
660                         if (flags & MBS_UID_DEFINED) {
661                                 snprintf(errstr, buflen, "one uid only");
662                                 return (-1);
663                         }
664                         if (bsde_parse_uidrange(argv[current+1],
665                             &uid_min, &uid_max, buflen, errstr) < 0)
666                                 return (-1);
667                         flags |= MBS_UID_DEFINED;
668                         if (nextnot) {
669                                 neg ^= MBS_UID_DEFINED;
670                                 nextnot = 0;
671                         }
672                         current += 2;
673                 } else if (strcmp(argv[current], "gid") == 0) {
674                         if (current + 2 > argc) {
675                                 snprintf(errstr, buflen, "gid short");
676                                 return (-1);
677                         }
678                         if (flags & MBS_GID_DEFINED) {
679                                 snprintf(errstr, buflen, "one gid only");
680                                 return (-1);
681                         }
682                         if (bsde_parse_gidrange(argv[current+1],
683                             &gid_min, &gid_max, buflen, errstr) < 0)
684                                 return (-1);
685                         flags |= MBS_GID_DEFINED;
686                         if (nextnot) {
687                                 neg ^= MBS_GID_DEFINED;
688                                 nextnot = 0;
689                         }
690                         current += 2;
691                 } else if (strcmp(argv[current], "jailid") == 0) {
692                         if (current + 2 > argc) {
693                                 snprintf(errstr, buflen, "prison short");
694                                 return (-1);
695                         }
696                         if (flags & MBS_PRISON_DEFINED) {
697                                 snprintf(errstr, buflen, "one jail only");
698                                 return (-1);
699                         }
700                         jid = bsde_get_jailid(argv[current+1], buflen, errstr);
701                         if (jid < 0)
702                                 return (-1);
703                         flags |= MBS_PRISON_DEFINED;
704                         if (nextnot) {
705                                 neg ^= MBS_PRISON_DEFINED;
706                                 nextnot = 0;
707                         }
708                         current += 2;
709                 } else if (strcmp(argv[current], "!") == 0) {
710                         if (nextnot) {
711                                 snprintf(errstr, buflen, "double negative");
712                                 return (-1);
713                         }
714                         nextnot = 1;
715                         current += 1;
716                 } else {
717                         snprintf(errstr, buflen, "'%s' not expected",
718                             argv[current]);
719                         return (-1);
720                 }
721         }
722
723         subject->mbs_flags = flags;
724         if (not_seen)
725                 subject->mbs_neg = MBS_ALL_FLAGS ^ neg;
726         else
727                 subject->mbs_neg = neg;
728         if (flags & MBS_UID_DEFINED) {
729                 subject->mbs_uid_min = uid_min;
730                 subject->mbs_uid_max = uid_max;
731         }
732         if (flags & MBS_GID_DEFINED) {
733                 subject->mbs_gid_min = gid_min;
734                 subject->mbs_gid_max = gid_max;
735         }
736         if (flags & MBS_PRISON_DEFINED)
737                 subject->mbs_prison = jid;
738
739         return (0);
740 }
741
742 static int
743 bsde_parse_type(char *spec, int *type, size_t buflen, char *errstr)
744 {
745         int i;
746
747         *type = 0;
748         for (i = 0; i < strlen(spec); i++) {
749                 switch (spec[i]) {
750                 case 'r':
751                 case '-':
752                         *type |= MBO_TYPE_REG;
753                         break;
754                 case 'd':
755                         *type |= MBO_TYPE_DIR;
756                         break;
757                 case 'b':
758                         *type |= MBO_TYPE_BLK;
759                         break;
760                 case 'c':
761                         *type |= MBO_TYPE_CHR;
762                         break;
763                 case 'l':
764                         *type |= MBO_TYPE_LNK;
765                         break;
766                 case 's':
767                         *type |= MBO_TYPE_SOCK;
768                         break;
769                 case 'p':
770                         *type |= MBO_TYPE_FIFO;
771                         break;
772                 case 'a':
773                         *type |= MBO_ALL_TYPE;
774                         break;
775                 default:
776                         snprintf(errstr, buflen, "Unknown type code: %c",
777                             spec[i]);
778                         return (-1);
779                 }
780         }
781
782         return (0);
783 }
784
785 static int
786 bsde_parse_fsid(char *spec, struct fsid *fsid, size_t buflen, char *errstr)
787 {
788         struct statfs buf;
789
790         if (statfs(spec, &buf) < 0) {
791                 snprintf(errstr, buflen, "Unable to get id for %s: %s",
792                     spec, strerror(errno));
793                 return (-1);
794         }
795
796         *fsid = buf.f_fsid;
797
798         return (0);
799 }
800
801 static int
802 bsde_parse_object(int argc, char *argv[],
803     struct mac_bsdextended_object *object, size_t buflen, char *errstr)
804 {
805         int not_seen, flags;
806         int current, neg, nextnot;
807         int type;
808         uid_t uid_min, uid_max;
809         gid_t gid_min, gid_max;
810         struct fsid fsid;
811
812         current = 0;
813         flags = 0;
814         neg = 0;
815         nextnot = 0;
816         type = 0;
817
818         if (strcmp("not", argv[current]) == 0) {
819                 not_seen = 1;
820                 current++;
821         } else
822                 not_seen = 0;
823
824         while (current < argc) {
825                 if (strcmp(argv[current], "uid") == 0) {
826                         if (current + 2 > argc) {
827                                 snprintf(errstr, buflen, "uid short");
828                                 return (-1);
829                         }
830                         if (flags & MBO_UID_DEFINED) {
831                                 snprintf(errstr, buflen, "one uid only");
832                                 return (-1);
833                         }
834                         if (bsde_parse_uidrange(argv[current+1],
835                             &uid_min, &uid_max, buflen, errstr) < 0)
836                                 return (-1);
837                         flags |= MBO_UID_DEFINED;
838                         if (nextnot) {
839                                 neg ^= MBO_UID_DEFINED;
840                                 nextnot = 0;
841                         }
842                         current += 2;
843                 } else if (strcmp(argv[current], "gid") == 0) {
844                         if (current + 2 > argc) {
845                                 snprintf(errstr, buflen, "gid short");
846                                 return (-1);
847                         }
848                         if (flags & MBO_GID_DEFINED) {
849                                 snprintf(errstr, buflen, "one gid only");
850                                 return (-1);
851                         }
852                         if (bsde_parse_gidrange(argv[current+1],
853                             &gid_min, &gid_max, buflen, errstr) < 0)
854                                 return (-1);
855                         flags |= MBO_GID_DEFINED;
856                         if (nextnot) {
857                                 neg ^= MBO_GID_DEFINED;
858                                 nextnot = 0;
859                         }
860                         current += 2;
861                 } else if (strcmp(argv[current], "filesys") == 0) {
862                         if (current + 2 > argc) {
863                                 snprintf(errstr, buflen, "filesys short");
864                                 return (-1);
865                         }
866                         if (flags & MBO_FSID_DEFINED) {
867                                 snprintf(errstr, buflen, "one fsid only");
868                                 return (-1);
869                         }
870                         if (bsde_parse_fsid(argv[current+1], &fsid,
871                             buflen, errstr) < 0)
872                                 return (-1);
873                         flags |= MBO_FSID_DEFINED;
874                         if (nextnot) {
875                                 neg ^= MBO_FSID_DEFINED;
876                                 nextnot = 0;
877                         }
878                         current += 2;
879                 } else if (strcmp(argv[current], "suid") == 0) {
880                         flags |= MBO_SUID;
881                         if (nextnot) {
882                                 neg ^= MBO_SUID;
883                                 nextnot = 0;
884                         }
885                         current += 1;
886                 } else if (strcmp(argv[current], "sgid") == 0) {
887                         flags |= MBO_SGID;
888                         if (nextnot) {
889                                 neg ^= MBO_SGID;
890                                 nextnot = 0;
891                         }
892                         current += 1;
893                 } else if (strcmp(argv[current], "uid_of_subject") == 0) {
894                         flags |= MBO_UID_SUBJECT;
895                         if (nextnot) {
896                                 neg ^= MBO_UID_SUBJECT;
897                                 nextnot = 0;
898                         }
899                         current += 1;
900                 } else if (strcmp(argv[current], "gid_of_subject") == 0) {
901                         flags |= MBO_GID_SUBJECT;
902                         if (nextnot) {
903                                 neg ^= MBO_GID_SUBJECT;
904                                 nextnot = 0;
905                         }
906                         current += 1;
907                 } else if (strcmp(argv[current], "type") == 0) {
908                         if (current + 2 > argc) {
909                                 snprintf(errstr, buflen, "type short");
910                                 return (-1);
911                         }
912                         if (flags & MBO_TYPE_DEFINED) {
913                                 snprintf(errstr, buflen, "one type only");
914                                 return (-1);
915                         }
916                         if (bsde_parse_type(argv[current+1], &type,
917                             buflen, errstr) < 0)
918                                 return (-1);
919                         flags |= MBO_TYPE_DEFINED;
920                         if (nextnot) {
921                                 neg ^= MBO_TYPE_DEFINED;
922                                 nextnot = 0;
923                         }
924                         current += 2;
925                 } else if (strcmp(argv[current], "!") == 0) {
926                         if (nextnot) {
927                                 snprintf(errstr, buflen,
928                                     "double negative'");
929                                 return (-1);
930                         }
931                         nextnot = 1;
932                         current += 1;
933                 } else {
934                         snprintf(errstr, buflen, "'%s' not expected",
935                             argv[current]);
936                         return (-1);
937                 }
938         }
939
940         object->mbo_flags = flags;
941         if (not_seen)
942                 object->mbo_neg = MBO_ALL_FLAGS ^ neg;
943         else
944                 object->mbo_neg = neg;
945         if (flags & MBO_UID_DEFINED) {
946                 object->mbo_uid_min = uid_min;
947                 object->mbo_uid_max = uid_max;
948         }
949         if (flags & MBO_GID_DEFINED) {
950                 object->mbo_gid_min = gid_min;
951                 object->mbo_gid_max = gid_max;
952         }
953         if (flags & MBO_FSID_DEFINED)
954                 object->mbo_fsid = fsid;
955         if (flags & MBO_TYPE_DEFINED)
956                 object->mbo_type = type;
957
958         return (0);
959 }
960
961 int
962 bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen,
963     char *errstr)
964 {
965         int i;
966
967         if (argc == 0) {
968                 snprintf(errstr, buflen, "mode expects mode value");
969                 return (-1);
970         }
971
972         if (argc != 1) {
973                 snprintf(errstr, buflen, "'%s' unexpected", argv[1]);
974                 return (-1);
975         }
976
977         *mode = 0;
978         for (i = 0; i < strlen(argv[0]); i++) {
979                 switch (argv[0][i]) {
980                 case 'a':
981                         *mode |= MBI_ADMIN;
982                         break;
983                 case 'r':
984                         *mode |= MBI_READ;
985                         break;
986                 case 's':
987                         *mode |= MBI_STAT;
988                         break;
989                 case 'w':
990                         *mode |= MBI_WRITE;
991                         break;
992                 case 'x':
993                         *mode |= MBI_EXEC;
994                         break;
995                 case 'n':
996                         /* ignore */
997                         break;
998                 default:
999                         snprintf(errstr, buflen, "Unknown mode letter: %c",
1000                             argv[0][i]);
1001                         return (-1);
1002                 }
1003         }
1004
1005         return (0);
1006 }
1007
1008 int
1009 bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule,
1010     size_t buflen, char *errstr)
1011 {
1012         int subject, subject_elements, subject_elements_length;
1013         int object, object_elements, object_elements_length;
1014         int mode, mode_elements, mode_elements_length;
1015         int error, i;
1016
1017         bzero(rule, sizeof(*rule));
1018
1019         if (argc < 1) {
1020                 snprintf(errstr, buflen, "Rule must begin with subject");
1021                 return (-1);
1022         }
1023
1024         if (strcmp(argv[0], "subject") != 0) {
1025                 snprintf(errstr, buflen, "Rule must begin with subject");
1026                 return (-1);
1027         }
1028         subject = 0;
1029         subject_elements = 1;
1030
1031         /* Search forward for object. */
1032
1033         object = -1;
1034         for (i = 1; i < argc; i++)
1035                 if (strcmp(argv[i], "object") == 0)
1036                         object = i;
1037
1038         if (object == -1) {
1039                 snprintf(errstr, buflen, "Rule must contain an object");
1040                 return (-1);
1041         }
1042
1043         /* Search forward for mode. */
1044         mode = -1;
1045         for (i = object; i < argc; i++)
1046                 if (strcmp(argv[i], "mode") == 0)
1047                         mode = i;
1048
1049         if (mode == -1) {
1050                 snprintf(errstr, buflen, "Rule must contain mode");
1051                 return (-1);
1052         }
1053
1054         subject_elements_length = object - subject - 1;
1055         object_elements = object + 1;
1056         object_elements_length = mode - object_elements;
1057         mode_elements = mode + 1;
1058         mode_elements_length = argc - mode_elements;
1059
1060         error = bsde_parse_subject(subject_elements_length,
1061             argv + subject_elements, &rule->mbr_subject, buflen, errstr);
1062         if (error)
1063                 return (-1);
1064
1065         error = bsde_parse_object(object_elements_length,
1066             argv + object_elements, &rule->mbr_object, buflen, errstr);
1067         if (error)
1068                 return (-1);
1069
1070         error = bsde_parse_mode(mode_elements_length, argv + mode_elements,
1071             &rule->mbr_mode, buflen, errstr);
1072         if (error)
1073                 return (-1);
1074
1075         return (0);
1076 }
1077
1078 int
1079 bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule,
1080     size_t buflen, char *errstr)
1081 {
1082         char *stringdup, *stringp, *argv[100], **ap;
1083         int argc, error;
1084
1085         stringp = stringdup = strdup(string);
1086         while (*stringp == ' ' || *stringp == '\t')
1087                 stringp++;
1088
1089         argc = 0;
1090         for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) {
1091                 argc++;
1092                 if (**ap != '\0')
1093                         if (++ap >= &argv[100])
1094                                 break;
1095         }
1096
1097         error = bsde_parse_rule(argc, argv, rule, buflen, errstr);
1098
1099         free(stringdup);
1100
1101         return (error);
1102 }
1103
1104 int
1105 bsde_get_mib(const char *string, int *name, size_t *namelen)
1106 {
1107         size_t len;
1108         int error;
1109
1110         len = *namelen;
1111         error = sysctlnametomib(string, name, &len);
1112         if (error)
1113                 return (error);
1114
1115         *namelen = len;
1116         return (0);
1117 }
1118
1119 static int
1120 bsde_check_version(size_t buflen, char *errstr)
1121 {
1122         size_t len;
1123         int error;
1124         int version;
1125
1126         len = sizeof(version);
1127         error = sysctlbyname(MIB ".rule_version", &version, &len, NULL, 0);
1128         if (error) {
1129                 snprintf(errstr, buflen, "version check failed: %s",
1130                     strerror(errno));
1131                 return (-1);
1132         }
1133         if (version != MB_VERSION) {
1134                 snprintf(errstr, buflen, "module v%d != library v%d",
1135                     version, MB_VERSION);
1136                 return (-1);
1137         }
1138         return (0);
1139 }
1140
1141 int
1142 bsde_get_rule_count(size_t buflen, char *errstr)
1143 {
1144         size_t len;
1145         int error;
1146         int rule_count;
1147
1148         len = sizeof(rule_count);
1149         error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0);
1150         if (error) {
1151                 snprintf(errstr, buflen, "%s", strerror(errno));
1152                 return (-1);
1153         }
1154         if (len != sizeof(rule_count)) {
1155                 snprintf(errstr, buflen, "Data error in %s.rule_count",
1156                     MIB);
1157                 return (-1);
1158         }
1159
1160         return (rule_count);
1161 }
1162
1163 int
1164 bsde_get_rule_slots(size_t buflen, char *errstr)
1165 {
1166         size_t len;
1167         int error;
1168         int rule_slots;
1169
1170         len = sizeof(rule_slots);
1171         error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0);
1172         if (error) {
1173                 snprintf(errstr, buflen, "%s", strerror(errno));
1174                 return (-1);
1175         }
1176         if (len != sizeof(rule_slots)) {
1177                 snprintf(errstr, buflen, "Data error in %s.rule_slots", MIB);
1178                 return (-1);
1179         }
1180
1181         return (rule_slots);
1182 }
1183
1184 /*
1185  * Returns 0 for success;
1186  * Returns -1 for failure;
1187  * Returns -2 for not present
1188  */
1189 int
1190 bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen,
1191     char *errstr)
1192 {
1193         int name[10];
1194         size_t len, size;
1195         int error;
1196
1197         if (bsde_check_version(errlen, errstr) != 0)
1198                 return (-1);
1199
1200         len = 10;
1201         error = bsde_get_mib(MIB ".rules", name, &len);
1202         if (error) {
1203                 snprintf(errstr, errlen, "%s: %s", MIB ".rules",
1204                     strerror(errno));
1205                 return (-1);
1206         }
1207
1208         size = sizeof(*rule);
1209         name[len] = rulenum;
1210         len++;
1211         error = sysctl(name, len, rule, &size, NULL, 0);
1212         if (error  == -1 && errno == ENOENT)
1213                 return (-2);
1214         if (error) {
1215                 snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules",
1216                     rulenum, strerror(errno));
1217                 return (-1);
1218         } else if (size != sizeof(*rule)) {
1219                 snprintf(errstr, errlen, "Data error in %s.%d: %s",
1220                     MIB ".rules", rulenum, strerror(errno));
1221                 return (-1);
1222         }
1223
1224         return (0);
1225 }
1226
1227 int
1228 bsde_delete_rule(int rulenum, size_t buflen, char *errstr)
1229 {
1230         struct mac_bsdextended_rule rule;
1231         int name[10];
1232         size_t len;
1233         int error;
1234
1235         if (bsde_check_version(buflen, errstr) != 0)
1236                 return (-1);
1237
1238         len = 10;
1239         error = bsde_get_mib(MIB ".rules", name, &len);
1240         if (error) {
1241                 snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1242                     strerror(errno));
1243                 return (-1);
1244         }
1245
1246         name[len] = rulenum;
1247         len++;
1248
1249         error = sysctl(name, len, NULL, NULL, &rule, 0);
1250         if (error) {
1251                 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1252                     rulenum, strerror(errno));
1253                 return (-1);
1254         }
1255
1256         return (0);
1257 }
1258
1259 int
1260 bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1261     char *errstr)
1262 {
1263         int name[10];
1264         size_t len;
1265         int error;
1266
1267         if (bsde_check_version(buflen, errstr) != 0)
1268                 return (-1);
1269
1270         len = 10;
1271         error = bsde_get_mib(MIB ".rules", name, &len);
1272         if (error) {
1273                 snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1274                     strerror(errno));
1275                 return (-1);
1276         }
1277
1278         name[len] = rulenum;
1279         len++;
1280
1281         error = sysctl(name, len, NULL, NULL, rule, sizeof(*rule));
1282         if (error) {
1283                 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1284                     rulenum, strerror(errno));
1285                 return (-1);
1286         }
1287
1288         return (0);
1289 }
1290
1291 int
1292 bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1293     char *errstr)
1294 {
1295         char charstr[BUFSIZ];
1296         int name[10];
1297         size_t len;
1298         int error, rule_slots;
1299
1300         if (bsde_check_version(buflen, errstr) != 0)
1301                 return (-1);
1302
1303         len = 10;
1304         error = bsde_get_mib(MIB ".rules", name, &len);
1305         if (error) {
1306                 snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1307                     strerror(errno));
1308                 return (-1);
1309         }
1310
1311         rule_slots = bsde_get_rule_slots(BUFSIZ, charstr);
1312         if (rule_slots == -1) {
1313                 snprintf(errstr, buflen, "unable to get rule slots: %s",
1314                     strerror(errno));
1315                 return (-1);
1316         }
1317
1318         name[len] = rule_slots;
1319         len++;
1320
1321         error = sysctl(name, len, NULL, NULL, rule, sizeof(*rule));
1322         if (error) {
1323                 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1324                     rule_slots, strerror(errno));
1325                 return (-1);
1326         }
1327
1328         if (rulenum != NULL)
1329                 *rulenum = rule_slots;
1330
1331         return (0);
1332 }