]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libugidfw/ugidfw.c
Remove spurious newline
[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 (memcmp(&(rule->mbr_object.mbo_fsid),
338                                     &(mntbuf[i].f_fsid),
339                                     sizeof(mntbuf[i].f_fsid)) == 0)
340                                         break;
341                         len = snprintf(cur, left, "filesys %s ",
342                             i == numfs ? "???" : mntbuf[i].f_mntonname);
343                         if (len < 0 || len > left)
344                                 goto truncated;
345                         left -= len;
346                         cur += len;
347                 }
348                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_SUID)) {
349                         len = snprintf(cur, left, "! ");
350                         if (len < 0 || len > left)
351                                 goto truncated;
352                         left -= len;
353                         cur += len;
354                 }
355                 if (rule->mbr_object.mbo_flags & MBO_SUID) {
356                         len = snprintf(cur, left, "suid ");
357                         if (len < 0 || len > left)
358                                 goto truncated;
359                         left -= len;
360                         cur += len;
361                 }
362                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_SGID)) {
363                         len = snprintf(cur, left, "! ");
364                         if (len < 0 || len > left)
365                                 goto truncated;
366                         left -= len;
367                         cur += len;
368                 }
369                 if (rule->mbr_object.mbo_flags & MBO_SGID) {
370                         len = snprintf(cur, left, "sgid ");
371                         if (len < 0 || len > left)
372                                 goto truncated;
373                         left -= len;
374                         cur += len;
375                 }
376                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_UID_SUBJECT)) {
377                         len = snprintf(cur, left, "! ");
378                         if (len < 0 || len > left)
379                                 goto truncated;
380                         left -= len;
381                         cur += len;
382                 }
383                 if (rule->mbr_object.mbo_flags & MBO_UID_SUBJECT) {
384                         len = snprintf(cur, left, "uid_of_subject ");
385                         if (len < 0 || len > left)
386                                 goto truncated;
387                         left -= len;
388                         cur += len;
389                 }
390                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_GID_SUBJECT)) {
391                         len = snprintf(cur, left, "! ");
392                         if (len < 0 || len > left)
393                                 goto truncated;
394                         left -= len;
395                         cur += len;
396                 }
397                 if (rule->mbr_object.mbo_flags & MBO_GID_SUBJECT) {
398                         len = snprintf(cur, left, "gid_of_subject ");
399                         if (len < 0 || len > left)
400                                 goto truncated;
401                         left -= len;
402                         cur += len;
403                 }
404                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_TYPE_DEFINED)) {
405                         len = snprintf(cur, left, "! ");
406                         if (len < 0 || len > left)
407                                 goto truncated;
408                         left -= len;
409                         cur += len;
410                 }
411                 if (rule->mbr_object.mbo_flags & MBO_TYPE_DEFINED) {
412                         i = 0;
413                         if (rule->mbr_object.mbo_type & MBO_TYPE_REG)
414                                 type[i++] = 'r';
415                         if (rule->mbr_object.mbo_type & MBO_TYPE_DIR)
416                                 type[i++] = 'd';
417                         if (rule->mbr_object.mbo_type & MBO_TYPE_BLK)
418                                 type[i++] = 'b';
419                         if (rule->mbr_object.mbo_type & MBO_TYPE_CHR)
420                                 type[i++] = 'c';
421                         if (rule->mbr_object.mbo_type & MBO_TYPE_LNK)
422                                 type[i++] = 'l';
423                         if (rule->mbr_object.mbo_type & MBO_TYPE_SOCK)
424                                 type[i++] = 's';
425                         if (rule->mbr_object.mbo_type & MBO_TYPE_FIFO)
426                                 type[i++] = 'p';
427                         if (rule->mbr_object.mbo_type == MBO_ALL_TYPE) {
428                                 i = 0;
429                                 type[i++] = 'a';
430                         }
431                         type[i++] = '\0';
432                         len = snprintf(cur, left, "type %s ", type);
433                         if (len < 0 || len > left)
434                                 goto truncated;
435                         left -= len;
436                         cur += len;
437                 }
438         }
439
440         len = snprintf(cur, left, "mode ");
441         if (len < 0 || len > left)
442                 goto truncated;
443         left -= len;
444         cur += len;
445
446         anymode = (rule->mbr_mode & MBI_ALLPERM);
447         unknownmode = (rule->mbr_mode & ~MBI_ALLPERM);
448
449         if (rule->mbr_mode & MBI_ADMIN) {
450                 len = snprintf(cur, left, "a");
451                 if (len < 0 || len > left)
452                         goto truncated;
453
454                 left -= len;
455                 cur += len;
456         }
457         if (rule->mbr_mode & MBI_READ) {
458                 len = snprintf(cur, left, "r");
459                 if (len < 0 || len > left)
460                         goto truncated;
461
462                 left -= len;
463                 cur += len;
464         }
465         if (rule->mbr_mode & MBI_STAT) {
466                 len = snprintf(cur, left, "s");
467                 if (len < 0 || len > left)
468                         goto truncated;
469
470                 left -= len;
471                 cur += len;
472         }
473         if (rule->mbr_mode & MBI_WRITE) {
474                 len = snprintf(cur, left, "w");
475                 if (len < 0 || len > left)
476                         goto truncated;
477
478                 left -= len;
479                 cur += len;
480         }
481         if (rule->mbr_mode & MBI_EXEC) {
482                 len = snprintf(cur, left, "x");
483                 if (len < 0 || len > left)
484                         goto truncated;
485
486                 left -= len;
487                 cur += len;
488         }
489         if (!anymode) {
490                 len = snprintf(cur, left, "n");
491                 if (len < 0 || len > left)
492                         goto truncated;
493
494                 left -= len;
495                 cur += len;
496         }
497         if (unknownmode) {
498                 len = snprintf(cur, left, "?");
499                 if (len < 0 || len > left)
500                         goto truncated;
501
502                 left -= len;
503                 cur += len;
504         }
505
506         return (0);
507
508 truncated:
509         return (-1);
510 }
511
512 static int
513 bsde_parse_uidrange(char *spec, uid_t *min, uid_t *max,
514     size_t buflen, char *errstr){
515         struct passwd *pwd;
516         uid_t uid1, uid2;
517         char *spec1, *spec2, *endp;
518         unsigned long value;
519
520         spec2 = spec;
521         spec1 = strsep(&spec2, ":");
522
523         pwd = getpwnam(spec1);
524         if (pwd != NULL)
525                 uid1 = pwd->pw_uid;
526         else {
527                 value = strtoul(spec1, &endp, 10);
528                 if (*endp != '\0') {
529                         snprintf(errstr, buflen, "invalid uid: '%s'", spec1);
530                         return (-1);
531                 }
532                 uid1 = value;
533         }
534
535         if (spec2 == NULL) {
536                 *max = *min = uid1;
537                 return (0);
538         }
539
540         pwd = getpwnam(spec2);
541         if (pwd != NULL)
542                 uid2 = pwd->pw_uid;
543         else {
544                 value = strtoul(spec2, &endp, 10);
545                 if (*endp != '\0') {
546                         snprintf(errstr, buflen, "invalid uid: '%s'", spec2);
547                         return (-1);
548                 }
549                 uid2 = value;
550         }
551
552         *min = uid1;
553         *max = uid2;
554
555         return (0);
556 }
557
558 static int
559 bsde_parse_gidrange(char *spec, gid_t *min, gid_t *max,
560     size_t buflen, char *errstr){
561         struct group *grp;
562         gid_t gid1, gid2;
563         char *spec1, *spec2, *endp;
564         unsigned long value;
565
566         spec2 = spec;
567         spec1 = strsep(&spec2, ":");
568
569         grp = getgrnam(spec1);
570         if (grp != NULL)
571                 gid1 = grp->gr_gid;
572         else {
573                 value = strtoul(spec1, &endp, 10);
574                 if (*endp != '\0') {
575                         snprintf(errstr, buflen, "invalid gid: '%s'", spec1);
576                         return (-1);
577                 }
578                 gid1 = value;
579         }
580
581         if (spec2 == NULL) {
582                 *max = *min = gid1;
583                 return (0);
584         }
585
586         grp = getgrnam(spec2);
587         if (grp != NULL)
588                 gid2 = grp->gr_gid;
589         else {
590                 value = strtoul(spec2, &endp, 10);
591                 if (*endp != '\0') {
592                         snprintf(errstr, buflen, "invalid gid: '%s'", spec2);
593                         return (-1);
594                 }
595                 gid2 = value;
596         }
597
598         *min = gid1;
599         *max = gid2;
600
601         return (0);
602 }
603
604 static int
605 bsde_get_jailid(const char *name, size_t buflen, char *errstr)
606 {
607         char *ep;
608         int jid;
609         struct iovec jiov[4];
610
611         /* Copy jail_getid(3) instead of messing with library dependancies */
612         jid = strtoul(name, &ep, 10);
613         if (*name && !*ep)
614                 return jid;
615         jiov[0].iov_base = __DECONST(char *, "name");
616         jiov[0].iov_len = sizeof("name");
617         jiov[1].iov_len = strlen(name) + 1;
618         jiov[1].iov_base = alloca(jiov[1].iov_len);
619         strcpy(jiov[1].iov_base, name);
620         if (errstr && buflen) {
621                 jiov[2].iov_base = __DECONST(char *, "errmsg");
622                 jiov[2].iov_len = sizeof("errmsg");
623                 jiov[3].iov_base = errstr;
624                 jiov[3].iov_len = buflen;
625                 errstr[0] = 0;
626                 jid = jail_get(jiov, 4, 0);
627                 if (jid < 0 && !errstr[0])
628                         snprintf(errstr, buflen, "jail_get: %s",
629                             strerror(errno));
630         } else
631                 jid = jail_get(jiov, 2, 0);
632         return jid;
633 }
634
635 static int
636 bsde_parse_subject(int argc, char *argv[],
637     struct mac_bsdextended_subject *subject, size_t buflen, char *errstr)
638 {
639         int not_seen, flags;
640         int current, neg, nextnot;
641         uid_t uid_min, uid_max;
642         gid_t gid_min, gid_max;
643         int jid = 0;
644
645         current = 0;
646         flags = 0;
647         neg = 0;
648         nextnot = 0;
649
650         if (strcmp("not", argv[current]) == 0) {
651                 not_seen = 1;
652                 current++;
653         } else
654                 not_seen = 0;
655
656         while (current < argc) {
657                 if (strcmp(argv[current], "uid") == 0) {
658                         if (current + 2 > argc) {
659                                 snprintf(errstr, buflen, "uid short");
660                                 return (-1);
661                         }
662                         if (flags & MBS_UID_DEFINED) {
663                                 snprintf(errstr, buflen, "one uid only");
664                                 return (-1);
665                         }
666                         if (bsde_parse_uidrange(argv[current+1],
667                             &uid_min, &uid_max, buflen, errstr) < 0)
668                                 return (-1);
669                         flags |= MBS_UID_DEFINED;
670                         if (nextnot) {
671                                 neg ^= MBS_UID_DEFINED;
672                                 nextnot = 0;
673                         }
674                         current += 2;
675                 } else if (strcmp(argv[current], "gid") == 0) {
676                         if (current + 2 > argc) {
677                                 snprintf(errstr, buflen, "gid short");
678                                 return (-1);
679                         }
680                         if (flags & MBS_GID_DEFINED) {
681                                 snprintf(errstr, buflen, "one gid only");
682                                 return (-1);
683                         }
684                         if (bsde_parse_gidrange(argv[current+1],
685                             &gid_min, &gid_max, buflen, errstr) < 0)
686                                 return (-1);
687                         flags |= MBS_GID_DEFINED;
688                         if (nextnot) {
689                                 neg ^= MBS_GID_DEFINED;
690                                 nextnot = 0;
691                         }
692                         current += 2;
693                 } else if (strcmp(argv[current], "jailid") == 0) {
694                         if (current + 2 > argc) {
695                                 snprintf(errstr, buflen, "prison short");
696                                 return (-1);
697                         }
698                         if (flags & MBS_PRISON_DEFINED) {
699                                 snprintf(errstr, buflen, "one jail only");
700                                 return (-1);
701                         }
702                         jid = bsde_get_jailid(argv[current+1], buflen, errstr);
703                         if (jid < 0)
704                                 return (-1);
705                         flags |= MBS_PRISON_DEFINED;
706                         if (nextnot) {
707                                 neg ^= MBS_PRISON_DEFINED;
708                                 nextnot = 0;
709                         }
710                         current += 2;
711                 } else if (strcmp(argv[current], "!") == 0) {
712                         if (nextnot) {
713                                 snprintf(errstr, buflen, "double negative");
714                                 return (-1);
715                         }
716                         nextnot = 1;
717                         current += 1;
718                 } else {
719                         snprintf(errstr, buflen, "'%s' not expected",
720                             argv[current]);
721                         return (-1);
722                 }
723         }
724
725         subject->mbs_flags = flags;
726         if (not_seen)
727                 subject->mbs_neg = MBS_ALL_FLAGS ^ neg;
728         else
729                 subject->mbs_neg = neg;
730         if (flags & MBS_UID_DEFINED) {
731                 subject->mbs_uid_min = uid_min;
732                 subject->mbs_uid_max = uid_max;
733         }
734         if (flags & MBS_GID_DEFINED) {
735                 subject->mbs_gid_min = gid_min;
736                 subject->mbs_gid_max = gid_max;
737         }
738         if (flags & MBS_PRISON_DEFINED)
739                 subject->mbs_prison = jid;
740
741         return (0);
742 }
743
744 static int
745 bsde_parse_type(char *spec, int *type, size_t buflen, char *errstr)
746 {
747         int i;
748
749         *type = 0;
750         for (i = 0; i < strlen(spec); i++) {
751                 switch (spec[i]) {
752                 case 'r':
753                 case '-':
754                         *type |= MBO_TYPE_REG;
755                         break;
756                 case 'd':
757                         *type |= MBO_TYPE_DIR;
758                         break;
759                 case 'b':
760                         *type |= MBO_TYPE_BLK;
761                         break;
762                 case 'c':
763                         *type |= MBO_TYPE_CHR;
764                         break;
765                 case 'l':
766                         *type |= MBO_TYPE_LNK;
767                         break;
768                 case 's':
769                         *type |= MBO_TYPE_SOCK;
770                         break;
771                 case 'p':
772                         *type |= MBO_TYPE_FIFO;
773                         break;
774                 case 'a':
775                         *type |= MBO_ALL_TYPE;
776                         break;
777                 default:
778                         snprintf(errstr, buflen, "Unknown type code: %c",
779                             spec[i]);
780                         return (-1);
781                 }
782         }
783
784         return (0);
785 }
786
787 static int
788 bsde_parse_fsid(char *spec, struct fsid *fsid, size_t buflen, char *errstr)
789 {
790         struct statfs buf;
791
792         if (statfs(spec, &buf) < 0) {
793                 snprintf(errstr, buflen, "Unable to get id for %s: %s",
794                     spec, strerror(errno));
795                 return (-1);
796         }
797
798         *fsid = buf.f_fsid;
799
800         return (0);
801 }
802
803 static int
804 bsde_parse_object(int argc, char *argv[],
805     struct mac_bsdextended_object *object, size_t buflen, char *errstr)
806 {
807         int not_seen, flags;
808         int current, neg, nextnot;
809         int type;
810         uid_t uid_min, uid_max;
811         gid_t gid_min, gid_max;
812         struct fsid fsid;
813
814         current = 0;
815         flags = 0;
816         neg = 0;
817         nextnot = 0;
818         type = 0;
819
820         if (strcmp("not", argv[current]) == 0) {
821                 not_seen = 1;
822                 current++;
823         } else
824                 not_seen = 0;
825
826         while (current < argc) {
827                 if (strcmp(argv[current], "uid") == 0) {
828                         if (current + 2 > argc) {
829                                 snprintf(errstr, buflen, "uid short");
830                                 return (-1);
831                         }
832                         if (flags & MBO_UID_DEFINED) {
833                                 snprintf(errstr, buflen, "one uid only");
834                                 return (-1);
835                         }
836                         if (bsde_parse_uidrange(argv[current+1],
837                             &uid_min, &uid_max, buflen, errstr) < 0)
838                                 return (-1);
839                         flags |= MBO_UID_DEFINED;
840                         if (nextnot) {
841                                 neg ^= MBO_UID_DEFINED;
842                                 nextnot = 0;
843                         }
844                         current += 2;
845                 } else if (strcmp(argv[current], "gid") == 0) {
846                         if (current + 2 > argc) {
847                                 snprintf(errstr, buflen, "gid short");
848                                 return (-1);
849                         }
850                         if (flags & MBO_GID_DEFINED) {
851                                 snprintf(errstr, buflen, "one gid only");
852                                 return (-1);
853                         }
854                         if (bsde_parse_gidrange(argv[current+1],
855                             &gid_min, &gid_max, buflen, errstr) < 0)
856                                 return (-1);
857                         flags |= MBO_GID_DEFINED;
858                         if (nextnot) {
859                                 neg ^= MBO_GID_DEFINED;
860                                 nextnot = 0;
861                         }
862                         current += 2;
863                 } else if (strcmp(argv[current], "filesys") == 0) {
864                         if (current + 2 > argc) {
865                                 snprintf(errstr, buflen, "filesys short");
866                                 return (-1);
867                         }
868                         if (flags & MBO_FSID_DEFINED) {
869                                 snprintf(errstr, buflen, "one fsid only");
870                                 return (-1);
871                         }
872                         if (bsde_parse_fsid(argv[current+1], &fsid,
873                             buflen, errstr) < 0)
874                                 return (-1);
875                         flags |= MBO_FSID_DEFINED;
876                         if (nextnot) {
877                                 neg ^= MBO_FSID_DEFINED;
878                                 nextnot = 0;
879                         }
880                         current += 2;
881                 } else if (strcmp(argv[current], "suid") == 0) {
882                         flags |= MBO_SUID;
883                         if (nextnot) {
884                                 neg ^= MBO_SUID;
885                                 nextnot = 0;
886                         }
887                         current += 1;
888                 } else if (strcmp(argv[current], "sgid") == 0) {
889                         flags |= MBO_SGID;
890                         if (nextnot) {
891                                 neg ^= MBO_SGID;
892                                 nextnot = 0;
893                         }
894                         current += 1;
895                 } else if (strcmp(argv[current], "uid_of_subject") == 0) {
896                         flags |= MBO_UID_SUBJECT;
897                         if (nextnot) {
898                                 neg ^= MBO_UID_SUBJECT;
899                                 nextnot = 0;
900                         }
901                         current += 1;
902                 } else if (strcmp(argv[current], "gid_of_subject") == 0) {
903                         flags |= MBO_GID_SUBJECT;
904                         if (nextnot) {
905                                 neg ^= MBO_GID_SUBJECT;
906                                 nextnot = 0;
907                         }
908                         current += 1;
909                 } else if (strcmp(argv[current], "type") == 0) {
910                         if (current + 2 > argc) {
911                                 snprintf(errstr, buflen, "type short");
912                                 return (-1);
913                         }
914                         if (flags & MBO_TYPE_DEFINED) {
915                                 snprintf(errstr, buflen, "one type only");
916                                 return (-1);
917                         }
918                         if (bsde_parse_type(argv[current+1], &type,
919                             buflen, errstr) < 0)
920                                 return (-1);
921                         flags |= MBO_TYPE_DEFINED;
922                         if (nextnot) {
923                                 neg ^= MBO_TYPE_DEFINED;
924                                 nextnot = 0;
925                         }
926                         current += 2;
927                 } else if (strcmp(argv[current], "!") == 0) {
928                         if (nextnot) {
929                                 snprintf(errstr, buflen,
930                                     "double negative'");
931                                 return (-1);
932                         }
933                         nextnot = 1;
934                         current += 1;
935                 } else {
936                         snprintf(errstr, buflen, "'%s' not expected",
937                             argv[current]);
938                         return (-1);
939                 }
940         }
941
942         object->mbo_flags = flags;
943         if (not_seen)
944                 object->mbo_neg = MBO_ALL_FLAGS ^ neg;
945         else
946                 object->mbo_neg = neg;
947         if (flags & MBO_UID_DEFINED) {
948                 object->mbo_uid_min = uid_min;
949                 object->mbo_uid_max = uid_max;
950         }
951         if (flags & MBO_GID_DEFINED) {
952                 object->mbo_gid_min = gid_min;
953                 object->mbo_gid_max = gid_max;
954         }
955         if (flags & MBO_FSID_DEFINED)
956                 object->mbo_fsid = fsid;
957         if (flags & MBO_TYPE_DEFINED)
958                 object->mbo_type = type;
959
960         return (0);
961 }
962
963 int
964 bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen,
965     char *errstr)
966 {
967         int i;
968
969         if (argc == 0) {
970                 snprintf(errstr, buflen, "mode expects mode value");
971                 return (-1);
972         }
973
974         if (argc != 1) {
975                 snprintf(errstr, buflen, "'%s' unexpected", argv[1]);
976                 return (-1);
977         }
978
979         *mode = 0;
980         for (i = 0; i < strlen(argv[0]); i++) {
981                 switch (argv[0][i]) {
982                 case 'a':
983                         *mode |= MBI_ADMIN;
984                         break;
985                 case 'r':
986                         *mode |= MBI_READ;
987                         break;
988                 case 's':
989                         *mode |= MBI_STAT;
990                         break;
991                 case 'w':
992                         *mode |= MBI_WRITE;
993                         break;
994                 case 'x':
995                         *mode |= MBI_EXEC;
996                         break;
997                 case 'n':
998                         /* ignore */
999                         break;
1000                 default:
1001                         snprintf(errstr, buflen, "Unknown mode letter: %c",
1002                             argv[0][i]);
1003                         return (-1);
1004                 }
1005         }
1006
1007         return (0);
1008 }
1009
1010 int
1011 bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule,
1012     size_t buflen, char *errstr)
1013 {
1014         int subject, subject_elements, subject_elements_length;
1015         int object, object_elements, object_elements_length;
1016         int mode, mode_elements, mode_elements_length;
1017         int error, i;
1018
1019         bzero(rule, sizeof(*rule));
1020
1021         if (argc < 1) {
1022                 snprintf(errstr, buflen, "Rule must begin with subject");
1023                 return (-1);
1024         }
1025
1026         if (strcmp(argv[0], "subject") != 0) {
1027                 snprintf(errstr, buflen, "Rule must begin with subject");
1028                 return (-1);
1029         }
1030         subject = 0;
1031         subject_elements = 1;
1032
1033         /* Search forward for object. */
1034
1035         object = -1;
1036         for (i = 1; i < argc; i++)
1037                 if (strcmp(argv[i], "object") == 0)
1038                         object = i;
1039
1040         if (object == -1) {
1041                 snprintf(errstr, buflen, "Rule must contain an object");
1042                 return (-1);
1043         }
1044
1045         /* Search forward for mode. */
1046         mode = -1;
1047         for (i = object; i < argc; i++)
1048                 if (strcmp(argv[i], "mode") == 0)
1049                         mode = i;
1050
1051         if (mode == -1) {
1052                 snprintf(errstr, buflen, "Rule must contain mode");
1053                 return (-1);
1054         }
1055
1056         subject_elements_length = object - subject - 1;
1057         object_elements = object + 1;
1058         object_elements_length = mode - object_elements;
1059         mode_elements = mode + 1;
1060         mode_elements_length = argc - mode_elements;
1061
1062         error = bsde_parse_subject(subject_elements_length,
1063             argv + subject_elements, &rule->mbr_subject, buflen, errstr);
1064         if (error)
1065                 return (-1);
1066
1067         error = bsde_parse_object(object_elements_length,
1068             argv + object_elements, &rule->mbr_object, buflen, errstr);
1069         if (error)
1070                 return (-1);
1071
1072         error = bsde_parse_mode(mode_elements_length, argv + mode_elements,
1073             &rule->mbr_mode, buflen, errstr);
1074         if (error)
1075                 return (-1);
1076
1077         return (0);
1078 }
1079
1080 int
1081 bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule,
1082     size_t buflen, char *errstr)
1083 {
1084         char *stringdup, *stringp, *argv[100], **ap;
1085         int argc, error;
1086
1087         stringp = stringdup = strdup(string);
1088         while (*stringp == ' ' || *stringp == '\t')
1089                 stringp++;
1090
1091         argc = 0;
1092         for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) {
1093                 argc++;
1094                 if (**ap != '\0')
1095                         if (++ap >= &argv[100])
1096                                 break;
1097         }
1098
1099         error = bsde_parse_rule(argc, argv, rule, buflen, errstr);
1100
1101         free(stringdup);
1102
1103         return (error);
1104 }
1105
1106 int
1107 bsde_get_mib(const char *string, int *name, size_t *namelen)
1108 {
1109         size_t len;
1110         int error;
1111
1112         len = *namelen;
1113         error = sysctlnametomib(string, name, &len);
1114         if (error)
1115                 return (error);
1116
1117         *namelen = len;
1118         return (0);
1119 }
1120
1121 static int
1122 bsde_check_version(size_t buflen, char *errstr)
1123 {
1124         size_t len;
1125         int error;
1126         int version;
1127
1128         len = sizeof(version);
1129         error = sysctlbyname(MIB ".rule_version", &version, &len, NULL, 0);
1130         if (error) {
1131                 snprintf(errstr, buflen, "version check failed: %s",
1132                     strerror(errno));
1133                 return (-1);
1134         }
1135         if (version != MB_VERSION) {
1136                 snprintf(errstr, buflen, "module v%d != library v%d",
1137                     version, MB_VERSION);
1138                 return (-1);
1139         }
1140         return (0);
1141 }
1142
1143 int
1144 bsde_get_rule_count(size_t buflen, char *errstr)
1145 {
1146         size_t len;
1147         int error;
1148         int rule_count;
1149
1150         len = sizeof(rule_count);
1151         error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0);
1152         if (error) {
1153                 snprintf(errstr, buflen, "%s", strerror(errno));
1154                 return (-1);
1155         }
1156         if (len != sizeof(rule_count)) {
1157                 snprintf(errstr, buflen, "Data error in %s.rule_count",
1158                     MIB);
1159                 return (-1);
1160         }
1161
1162         return (rule_count);
1163 }
1164
1165 int
1166 bsde_get_rule_slots(size_t buflen, char *errstr)
1167 {
1168         size_t len;
1169         int error;
1170         int rule_slots;
1171
1172         len = sizeof(rule_slots);
1173         error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0);
1174         if (error) {
1175                 snprintf(errstr, buflen, "%s", strerror(errno));
1176                 return (-1);
1177         }
1178         if (len != sizeof(rule_slots)) {
1179                 snprintf(errstr, buflen, "Data error in %s.rule_slots", MIB);
1180                 return (-1);
1181         }
1182
1183         return (rule_slots);
1184 }
1185
1186 /*
1187  * Returns 0 for success;
1188  * Returns -1 for failure;
1189  * Returns -2 for not present
1190  */
1191 int
1192 bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen,
1193     char *errstr)
1194 {
1195         int name[10];
1196         size_t len, size;
1197         int error;
1198
1199         if (bsde_check_version(errlen, errstr) != 0)
1200                 return (-1);
1201
1202         len = 10;
1203         error = bsde_get_mib(MIB ".rules", name, &len);
1204         if (error) {
1205                 snprintf(errstr, errlen, "%s: %s", MIB ".rules",
1206                     strerror(errno));
1207                 return (-1);
1208         }
1209
1210         size = sizeof(*rule);
1211         name[len] = rulenum;
1212         len++;
1213         error = sysctl(name, len, rule, &size, NULL, 0);
1214         if (error  == -1 && errno == ENOENT)
1215                 return (-2);
1216         if (error) {
1217                 snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules",
1218                     rulenum, strerror(errno));
1219                 return (-1);
1220         } else if (size != sizeof(*rule)) {
1221                 snprintf(errstr, errlen, "Data error in %s.%d: %s",
1222                     MIB ".rules", rulenum, strerror(errno));
1223                 return (-1);
1224         }
1225
1226         return (0);
1227 }
1228
1229 int
1230 bsde_delete_rule(int rulenum, size_t buflen, char *errstr)
1231 {
1232         struct mac_bsdextended_rule rule;
1233         int name[10];
1234         size_t len;
1235         int error;
1236
1237         if (bsde_check_version(buflen, errstr) != 0)
1238                 return (-1);
1239
1240         len = 10;
1241         error = bsde_get_mib(MIB ".rules", name, &len);
1242         if (error) {
1243                 snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1244                     strerror(errno));
1245                 return (-1);
1246         }
1247
1248         name[len] = rulenum;
1249         len++;
1250
1251         error = sysctl(name, len, NULL, NULL, &rule, 0);
1252         if (error) {
1253                 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1254                     rulenum, strerror(errno));
1255                 return (-1);
1256         }
1257
1258         return (0);
1259 }
1260
1261 int
1262 bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1263     char *errstr)
1264 {
1265         int name[10];
1266         size_t len;
1267         int error;
1268
1269         if (bsde_check_version(buflen, errstr) != 0)
1270                 return (-1);
1271
1272         len = 10;
1273         error = bsde_get_mib(MIB ".rules", name, &len);
1274         if (error) {
1275                 snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1276                     strerror(errno));
1277                 return (-1);
1278         }
1279
1280         name[len] = rulenum;
1281         len++;
1282
1283         error = sysctl(name, len, NULL, NULL, rule, sizeof(*rule));
1284         if (error) {
1285                 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1286                     rulenum, strerror(errno));
1287                 return (-1);
1288         }
1289
1290         return (0);
1291 }
1292
1293 int
1294 bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1295     char *errstr)
1296 {
1297         char charstr[BUFSIZ];
1298         int name[10];
1299         size_t len;
1300         int error, rule_slots;
1301
1302         if (bsde_check_version(buflen, errstr) != 0)
1303                 return (-1);
1304
1305         len = 10;
1306         error = bsde_get_mib(MIB ".rules", name, &len);
1307         if (error) {
1308                 snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1309                     strerror(errno));
1310                 return (-1);
1311         }
1312
1313         rule_slots = bsde_get_rule_slots(BUFSIZ, charstr);
1314         if (rule_slots == -1) {
1315                 snprintf(errstr, buflen, "unable to get rule slots: %s",
1316                     strerror(errno));
1317                 return (-1);
1318         }
1319
1320         name[len] = rule_slots;
1321         len++;
1322
1323         error = sysctl(name, len, NULL, NULL, rule, sizeof(*rule));
1324         if (error) {
1325                 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1326                     rule_slots, strerror(errno));
1327                 return (-1);
1328         }
1329
1330         if (rulenum != NULL)
1331                 *rulenum = rule_slots;
1332
1333         return (0);
1334 }