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