]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - lib/libugidfw/ugidfw.c
MFC r335921:
[FreeBSD/stable/10.git] / lib / libugidfw / ugidfw.c
1 /*-
2  * Copyright (c) 2002-2005 Networks Associates Technology, Inc.
3  * All rights reserved.
4  *
5  * This software was developed for the FreeBSD Project by Network Associates
6  * Laboratories, the Security Research Division of Network Associates, Inc.
7  * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
8  * DARPA CHATS research program.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * $FreeBSD$
32  */
33 #include <sys/param.h>
34 #include <sys/errno.h>
35 #include <sys/jail.h>
36 #include <sys/time.h>
37 #include <sys/sysctl.h>
38 #include <sys/ucred.h>
39 #include <sys/uio.h>
40 #include <sys/mount.h>
41
42 #include <security/mac_bsdextended/mac_bsdextended.h>
43
44 #include <grp.h>
45 #include <pwd.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49
50 #include "ugidfw.h"
51
52 /*
53  * Text format for rules: rules contain subject and object elements, mode.
54  * The total form is "subject [s_element] object [o_element] mode [mode]".
55  * At least * one of a uid or gid entry must be present; both may also be
56  * present.
57  */
58
59 #define MIB     "security.mac.bsdextended"
60
61 int
62 bsde_rule_to_string(struct mac_bsdextended_rule *rule, char *buf, size_t buflen)
63 {
64         struct group *grp;
65         struct passwd *pwd;
66         struct statfs *mntbuf;
67         char *cur, type[sizeof(rule->mbr_object.mbo_type) * CHAR_BIT + 1];
68         size_t left, len;
69         int anymode, unknownmode, truncated, numfs, i, notdone;
70
71         cur = buf;
72         left = buflen;
73         truncated = 0;
74
75         len = snprintf(cur, left, "subject ");
76         if (len < 0 || len > left)
77                 goto truncated;
78         left -= len;
79         cur += len;
80         if (rule->mbr_subject.mbs_flags) {
81                 if (rule->mbr_subject.mbs_neg == MBS_ALL_FLAGS) {
82                         len = snprintf(cur, left, "not ");
83                         if (len < 0 || len > left)
84                                 goto truncated;
85                         left -= len;
86                         cur += len;
87                         notdone = 1;
88                 } else {
89                         notdone = 0;
90                 }
91
92                 if (!notdone && (rule->mbr_subject.mbs_neg & MBO_UID_DEFINED)) {
93                         len = snprintf(cur, left, "! ");
94                         if (len < 0 || len > left)
95                                 goto truncated;
96                         left -= len;
97                         cur += len;
98                 }
99                 if (rule->mbr_subject.mbs_flags & MBO_UID_DEFINED) {
100                         pwd = getpwuid(rule->mbr_subject.mbs_uid_min);
101                         if (pwd != NULL) {
102                                 len = snprintf(cur, left, "uid %s",
103                                     pwd->pw_name);
104                                 if (len < 0 || len > left)
105                                         goto truncated;
106                                 left -= len;
107                                 cur += len;
108                         } else {
109                                 len = snprintf(cur, left, "uid %u",
110                                     rule->mbr_subject.mbs_uid_min);
111                                 if (len < 0 || len > left)
112                                         goto truncated;
113                                 left -= len;
114                                 cur += len;
115                         }
116                         if (rule->mbr_subject.mbs_uid_min !=
117                             rule->mbr_subject.mbs_uid_max) {
118                                 pwd = getpwuid(rule->mbr_subject.mbs_uid_max);
119                                 if (pwd != NULL) {
120                                         len = snprintf(cur, left, ":%s ",
121                                             pwd->pw_name);
122                                         if (len < 0 || len > left)
123                                                 goto truncated;
124                                         left -= len;
125                                         cur += len;
126                                 } else {
127                                         len = snprintf(cur, left, ":%u ",
128                                             rule->mbr_subject.mbs_uid_max);
129                                         if (len < 0 || len > left)
130                                                 goto truncated;
131                                         left -= len;
132                                         cur += len;
133                                 }
134                         } else {
135                                 len = snprintf(cur, left, " ");
136                                 if (len < 0 || len > left)
137                                         goto truncated;
138                                 left -= len;
139                                 cur += len;
140                         }
141                 }
142                 if (!notdone && (rule->mbr_subject.mbs_neg & MBO_GID_DEFINED)) {
143                         len = snprintf(cur, left, "! ");
144                         if (len < 0 || len > left)
145                                 goto truncated;
146                         left -= len;
147                         cur += len;
148                 }
149                 if (rule->mbr_subject.mbs_flags & MBO_GID_DEFINED) {
150                         grp = getgrgid(rule->mbr_subject.mbs_gid_min);
151                         if (grp != NULL) {
152                                 len = snprintf(cur, left, "gid %s",
153                                     grp->gr_name);
154                                 if (len < 0 || len > left)
155                                         goto truncated;
156                                 left -= len;
157                                 cur += len;
158                         } else {
159                                 len = snprintf(cur, left, "gid %u",
160                                     rule->mbr_subject.mbs_gid_min);
161                                 if (len < 0 || len > left)
162                                         goto truncated;
163                                 left -= len;
164                                 cur += len;
165                         }
166                         if (rule->mbr_subject.mbs_gid_min !=
167                             rule->mbr_subject.mbs_gid_max) {
168                                 grp = getgrgid(rule->mbr_subject.mbs_gid_max);
169                                 if (grp != NULL) {
170                                         len = snprintf(cur, left, ":%s ",
171                                             grp->gr_name);
172                                         if (len < 0 || len > left)
173                                                 goto truncated;
174                                         left -= len;
175                                         cur += len;
176                                 } else {
177                                         len = snprintf(cur, left, ":%u ",
178                                             rule->mbr_subject.mbs_gid_max);
179                                         if (len < 0 || len > left)
180                                                 goto truncated;
181                                         left -= len;
182                                         cur += len;
183                                 }
184                         } else {
185                                 len = snprintf(cur, left, " ");
186                                 if (len < 0 || len > left)
187                                         goto truncated;
188                                 left -= len;
189                                 cur += len;
190                         }
191                 }
192                 if (!notdone && (rule->mbr_subject.mbs_neg & MBS_PRISON_DEFINED)) {
193                         len = snprintf(cur, left, "! ");
194                         if (len < 0 || len > left)
195                                 goto truncated;
196                         left -= len;
197                         cur += len;
198                 }
199                 if (rule->mbr_subject.mbs_flags & MBS_PRISON_DEFINED) {
200                         len = snprintf(cur, left, "jailid %d ", 
201                             rule->mbr_subject.mbs_prison);
202                         if (len < 0 || len > left)
203                                 goto truncated;
204                         left -= len;
205                         cur += len;
206                 }
207         }
208
209         len = snprintf(cur, left, "object ");
210         if (len < 0 || len > left)
211                 goto truncated;
212         left -= len;
213         cur += len;
214         if (rule->mbr_object.mbo_flags) {
215                 if (rule->mbr_object.mbo_neg == MBO_ALL_FLAGS) {
216                         len = snprintf(cur, left, "not ");
217                         if (len < 0 || len > left)
218                                 goto truncated;
219                         left -= len;
220                         cur += len;
221                         notdone = 1;
222                 } else {
223                         notdone = 0;
224                 }
225
226                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_UID_DEFINED)) {
227                         len = snprintf(cur, left, "! ");
228                         if (len < 0 || len > left)
229                                 goto truncated;
230                         left -= len;
231                         cur += len;
232                 }
233                 if (rule->mbr_object.mbo_flags & MBO_UID_DEFINED) {
234                         pwd = getpwuid(rule->mbr_object.mbo_uid_min);
235                         if (pwd != NULL) {
236                                 len = snprintf(cur, left, "uid %s",
237                                     pwd->pw_name);
238                                 if (len < 0 || len > left)
239                                         goto truncated;
240                                 left -= len;
241                                 cur += len;
242                         } else {
243                                 len = snprintf(cur, left, "uid %u",
244                                     rule->mbr_object.mbo_uid_min);
245                                 if (len < 0 || len > left)
246                                         goto truncated;
247                                 left -= len;
248                                 cur += len;
249                         }
250                         if (rule->mbr_object.mbo_uid_min !=
251                             rule->mbr_object.mbo_uid_max) {
252                                 pwd = getpwuid(rule->mbr_object.mbo_uid_max);
253                                 if (pwd != NULL) {
254                                         len = snprintf(cur, left, ":%s ",
255                                             pwd->pw_name);
256                                         if (len < 0 || len > left)
257                                                 goto truncated;
258                                         left -= len;
259                                         cur += len;
260                                 } else {
261                                         len = snprintf(cur, left, ":%u ",
262                                             rule->mbr_object.mbo_uid_max);
263                                         if (len < 0 || len > left)
264                                                 goto truncated;
265                                         left -= len;
266                                         cur += len;
267                                 }
268                         } else {
269                                 len = snprintf(cur, left, " ");
270                                 if (len < 0 || len > left)
271                                         goto truncated;
272                                 left -= len;
273                                 cur += len;
274                         }
275                 }
276                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_GID_DEFINED)) {
277                         len = snprintf(cur, left, "! ");
278                         if (len < 0 || len > left)
279                                 goto truncated;
280                         left -= len;
281                         cur += len;
282                 }
283                 if (rule->mbr_object.mbo_flags & MBO_GID_DEFINED) {
284                         grp = getgrgid(rule->mbr_object.mbo_gid_min);
285                         if (grp != NULL) {
286                                 len = snprintf(cur, left, "gid %s",
287                                     grp->gr_name);
288                                 if (len < 0 || len > left)
289                                         goto truncated;
290                                 left -= len;
291                                 cur += len;
292                         } else {
293                                 len = snprintf(cur, left, "gid %u",
294                                     rule->mbr_object.mbo_gid_min);
295                                 if (len < 0 || len > left)
296                                         goto truncated;
297                                 left -= len;
298                                 cur += len;
299                         }
300                         if (rule->mbr_object.mbo_gid_min !=
301                             rule->mbr_object.mbo_gid_max) {
302                                 grp = getgrgid(rule->mbr_object.mbo_gid_max);
303                                 if (grp != NULL) {
304                                         len = snprintf(cur, left, ":%s ",
305                                             grp->gr_name);
306                                         if (len < 0 || len > left)
307                                                 goto truncated;
308                                         left -= len;
309                                         cur += len;
310                                 } else {
311                                         len = snprintf(cur, left, ":%u ",
312                                             rule->mbr_object.mbo_gid_max);
313                                         if (len < 0 || len > left)
314                                                 goto truncated;
315                                         left -= len;
316                                         cur += len;
317                                 }
318                         } else {
319                                 len = snprintf(cur, left, " ");
320                                 if (len < 0 || len > left)
321                                         goto truncated;
322                                 left -= len;
323                                 cur += len;
324                         }
325                 }
326                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_FSID_DEFINED)) {
327                         len = snprintf(cur, left, "! ");
328                         if (len < 0 || len > left)
329                                 goto truncated;
330                         left -= len;
331                         cur += len;
332                 }
333                 if (rule->mbr_object.mbo_flags & MBO_FSID_DEFINED) {
334                         numfs = getmntinfo(&mntbuf, MNT_NOWAIT);
335                         for (i = 0; i < numfs; i++)
336                                 if (memcmp(&(rule->mbr_object.mbo_fsid),
337                                     &(mntbuf[i].f_fsid),
338                                     sizeof(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 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 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 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;
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 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 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 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         uid_t uid_min, uid_max;
809         gid_t gid_min, gid_max;
810         int type;
811         struct fsid fsid;
812
813         current = 0;
814         flags = 0;
815         neg = 0;
816         nextnot = 0;
817
818         if (strcmp("not", argv[current]) == 0) {
819                 not_seen = 1;
820                 current++;
821         } else
822                 not_seen = 0;
823
824         while (current < argc) {
825                 if (strcmp(argv[current], "uid") == 0) {
826                         if (current + 2 > argc) {
827                                 snprintf(errstr, buflen, "uid short");
828                                 return (-1);
829                         }
830                         if (flags & MBO_UID_DEFINED) {
831                                 snprintf(errstr, buflen, "one uid only");
832                                 return (-1);
833                         }
834                         if (bsde_parse_uidrange(argv[current+1],
835                             &uid_min, &uid_max, buflen, errstr) < 0)
836                                 return (-1);
837                         flags |= MBO_UID_DEFINED;
838                         if (nextnot) {
839                                 neg ^= MBO_UID_DEFINED;
840                                 nextnot = 0;
841                         }
842                         current += 2;
843                 } else if (strcmp(argv[current], "gid") == 0) {
844                         if (current + 2 > argc) {
845                                 snprintf(errstr, buflen, "gid short");
846                                 return (-1);
847                         }
848                         if (flags & MBO_GID_DEFINED) {
849                                 snprintf(errstr, buflen, "one gid only");
850                                 return (-1);
851                         }
852                         if (bsde_parse_gidrange(argv[current+1],
853                             &gid_min, &gid_max, buflen, errstr) < 0)
854                                 return (-1);
855                         flags |= MBO_GID_DEFINED;
856                         if (nextnot) {
857                                 neg ^= MBO_GID_DEFINED;
858                                 nextnot = 0;
859                         }
860                         current += 2;
861                 } else if (strcmp(argv[current], "filesys") == 0) {
862                         if (current + 2 > argc) {
863                                 snprintf(errstr, buflen, "filesys short");
864                                 return (-1);
865                         }
866                         if (flags & MBO_FSID_DEFINED) {
867                                 snprintf(errstr, buflen, "one fsid only");
868                                 return (-1);
869                         }
870                         if (bsde_parse_fsid(argv[current+1], &fsid,
871                             buflen, errstr) < 0)
872                                 return (-1);
873                         flags |= MBO_FSID_DEFINED;
874                         if (nextnot) {
875                                 neg ^= MBO_FSID_DEFINED;
876                                 nextnot = 0;
877                         }
878                         current += 2;
879                 } else if (strcmp(argv[current], "suid") == 0) {
880                         flags |= MBO_SUID;
881                         if (nextnot) {
882                                 neg ^= MBO_SUID;
883                                 nextnot = 0;
884                         }
885                         current += 1;
886                 } else if (strcmp(argv[current], "sgid") == 0) {
887                         flags |= MBO_SGID;
888                         if (nextnot) {
889                                 neg ^= MBO_SGID;
890                                 nextnot = 0;
891                         }
892                         current += 1;
893                 } else if (strcmp(argv[current], "uid_of_subject") == 0) {
894                         flags |= MBO_UID_SUBJECT;
895                         if (nextnot) {
896                                 neg ^= MBO_UID_SUBJECT;
897                                 nextnot = 0;
898                         }
899                         current += 1;
900                 } else if (strcmp(argv[current], "gid_of_subject") == 0) {
901                         flags |= MBO_GID_SUBJECT;
902                         if (nextnot) {
903                                 neg ^= MBO_GID_SUBJECT;
904                                 nextnot = 0;
905                         }
906                         current += 1;
907                 } else if (strcmp(argv[current], "type") == 0) {
908                         if (current + 2 > argc) {
909                                 snprintf(errstr, buflen, "type short");
910                                 return (-1);
911                         }
912                         if (flags & MBO_TYPE_DEFINED) {
913                                 snprintf(errstr, buflen, "one type only");
914                                 return (-1);
915                         }
916                         if (bsde_parse_type(argv[current+1], &type,
917                             buflen, errstr) < 0)
918                                 return (-1);
919                         flags |= MBO_TYPE_DEFINED;
920                         if (nextnot) {
921                                 neg ^= MBO_TYPE_DEFINED;
922                                 nextnot = 0;
923                         }
924                         current += 2;
925                 } else if (strcmp(argv[current], "!") == 0) {
926                         if (nextnot) {
927                                 snprintf(errstr, buflen,
928                                     "double negative'");
929                                 return (-1);
930                         }
931                         nextnot = 1;
932                         current += 1;
933                 } else {
934                         snprintf(errstr, buflen, "'%s' not expected",
935                             argv[current]);
936                         return (-1);
937                 }
938         }
939
940         object->mbo_flags = flags;
941         if (not_seen)
942                 object->mbo_neg = MBO_ALL_FLAGS ^ neg;
943         else
944                 object->mbo_neg = neg;
945         if (flags & MBO_UID_DEFINED) {
946                 object->mbo_uid_min = uid_min;
947                 object->mbo_uid_max = uid_max;
948         }
949         if (flags & MBO_GID_DEFINED) {
950                 object->mbo_gid_min = gid_min;
951                 object->mbo_gid_max = gid_max;
952         }
953         if (flags & MBO_FSID_DEFINED)
954                 object->mbo_fsid = fsid;
955         if (flags & MBO_TYPE_DEFINED)
956                 object->mbo_type = type;
957
958         return (0);
959 }
960
961 int
962 bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen,
963     char *errstr)
964 {
965         int i;
966
967         if (argc == 0) {
968                 snprintf(errstr, buflen, "mode expects mode value");
969                 return (-1);
970         }
971
972         if (argc != 1) {
973                 snprintf(errstr, buflen, "'%s' unexpected", argv[1]);
974                 return (-1);
975         }
976
977         *mode = 0;
978         for (i = 0; i < strlen(argv[0]); i++) {
979                 switch (argv[0][i]) {
980                 case 'a':
981                         *mode |= MBI_ADMIN;
982                         break;
983                 case 'r':
984                         *mode |= MBI_READ;
985                         break;
986                 case 's':
987                         *mode |= MBI_STAT;
988                         break;
989                 case 'w':
990                         *mode |= MBI_WRITE;
991                         break;
992                 case 'x':
993                         *mode |= MBI_EXEC;
994                         break;
995                 case 'n':
996                         /* ignore */
997                         break;
998                 default:
999                         snprintf(errstr, buflen, "Unknown mode letter: %c",
1000                             argv[0][i]);
1001                         return (-1);
1002                 } 
1003         }
1004
1005         return (0);
1006 }
1007
1008 int
1009 bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule,
1010     size_t buflen, char *errstr)
1011 {
1012         int subject, subject_elements, subject_elements_length;
1013         int object, object_elements, object_elements_length;
1014         int mode, mode_elements, mode_elements_length;
1015         int error, i;
1016
1017         bzero(rule, sizeof(*rule));
1018
1019         if (argc < 1) {
1020                 snprintf(errstr, buflen, "Rule must begin with subject");
1021                 return (-1);
1022         }
1023
1024         if (strcmp(argv[0], "subject") != 0) {
1025                 snprintf(errstr, buflen, "Rule must begin with subject");
1026                 return (-1);
1027         }
1028         subject = 0;
1029         subject_elements = 1;
1030
1031         /* Search forward for object. */
1032
1033         object = -1;
1034         for (i = 1; i < argc; i++)
1035                 if (strcmp(argv[i], "object") == 0)
1036                         object = i;
1037
1038         if (object == -1) {
1039                 snprintf(errstr, buflen, "Rule must contain an object");
1040                 return (-1);
1041         }
1042
1043         /* Search forward for mode. */
1044         mode = -1;
1045         for (i = object; i < argc; i++)
1046                 if (strcmp(argv[i], "mode") == 0)
1047                         mode = i;
1048
1049         if (mode == -1) {
1050                 snprintf(errstr, buflen, "Rule must contain mode");
1051                 return (-1);
1052         }
1053
1054         subject_elements_length = object - subject - 1;
1055         object_elements = object + 1;
1056         object_elements_length = mode - object_elements;
1057         mode_elements = mode + 1;
1058         mode_elements_length = argc - mode_elements;
1059
1060         error = bsde_parse_subject(subject_elements_length,
1061             argv + subject_elements, &rule->mbr_subject, buflen, errstr);
1062         if (error)
1063                 return (-1);
1064
1065         error = bsde_parse_object(object_elements_length,
1066             argv + object_elements, &rule->mbr_object, buflen, errstr);
1067         if (error)
1068                 return (-1);
1069
1070         error = bsde_parse_mode(mode_elements_length, argv + mode_elements,
1071             &rule->mbr_mode, buflen, errstr);
1072         if (error)
1073                 return (-1);
1074
1075         return (0);
1076 }
1077
1078 int
1079 bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule,
1080     size_t buflen, char *errstr)
1081 {
1082         char *stringdup, *stringp, *argv[100], **ap;
1083         int argc, error;
1084
1085         stringp = stringdup = strdup(string);
1086         while (*stringp == ' ' || *stringp == '\t')
1087                 stringp++;
1088
1089         argc = 0;
1090         for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) {
1091                 argc++;
1092                 if (**ap != '\0')
1093                         if (++ap >= &argv[100])
1094                                 break;
1095         }
1096
1097         error = bsde_parse_rule(argc, argv, rule, buflen, errstr);
1098
1099         free(stringdup);
1100
1101         return (error);
1102 }
1103
1104 int
1105 bsde_get_mib(const char *string, int *name, size_t *namelen)
1106 {
1107         size_t len;
1108         int error;
1109
1110         len = *namelen;
1111         error = sysctlnametomib(string, name, &len);
1112         if (error)
1113                 return (error);
1114
1115         *namelen = len;
1116         return (0);
1117 }
1118
1119 int
1120 bsde_check_version(size_t buflen, char *errstr)
1121 {
1122         size_t len;
1123         int error;
1124         int version;
1125
1126         len = sizeof(version);
1127         error = sysctlbyname(MIB ".rule_version", &version, &len, NULL, 0);
1128         if (error) {
1129                 snprintf(errstr, buflen, "version check failed: %s",
1130                     strerror(errno));
1131                 return (-1);
1132         }
1133         if (version != MB_VERSION) {
1134                 snprintf(errstr, buflen, "module v%d != library v%d",
1135                     version, MB_VERSION);
1136                 return (-1);
1137         }
1138         return (0);
1139 }
1140
1141 int
1142 bsde_get_rule_count(size_t buflen, char *errstr)
1143 {
1144         size_t len;
1145         int error;
1146         int rule_count;
1147
1148         len = sizeof(rule_count);
1149         error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0);
1150         if (error) {
1151                 snprintf(errstr, buflen, "%s", strerror(errno));
1152                 return (-1);
1153         }
1154         if (len != sizeof(rule_count)) {
1155                 snprintf(errstr, buflen, "Data error in %s.rule_count",
1156                     MIB);
1157                 return (-1);
1158         }
1159
1160         return (rule_count);
1161 }
1162
1163 int
1164 bsde_get_rule_slots(size_t buflen, char *errstr)
1165 {
1166         size_t len;
1167         int error;
1168         int rule_slots;
1169
1170         len = sizeof(rule_slots);
1171         error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0);
1172         if (error) {
1173                 snprintf(errstr, buflen, "%s", strerror(errno));
1174                 return (-1);
1175         }
1176         if (len != sizeof(rule_slots)) {
1177                 snprintf(errstr, buflen, "Data error in %s.rule_slots", MIB);
1178                 return (-1);
1179         }
1180
1181         return (rule_slots);
1182 }
1183
1184 /*
1185  * Returns 0 for success;
1186  * Returns -1 for failure;
1187  * Returns -2 for not present
1188  */
1189 int
1190 bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen,
1191     char *errstr)
1192 {
1193         int name[10];
1194         size_t len, size;
1195         int error;
1196
1197         if (bsde_check_version(errlen, errstr) != 0)
1198                 return (-1);
1199
1200         len = 10;
1201         error = bsde_get_mib(MIB ".rules", name, &len);
1202         if (error) {
1203                 snprintf(errstr, errlen, "%s: %s", MIB ".rules",
1204                     strerror(errno));
1205                 return (-1);
1206         }
1207
1208         size = sizeof(*rule);
1209         name[len] = rulenum;
1210         len++;
1211         error = sysctl(name, len, rule, &size, NULL, 0);
1212         if (error  == -1 && errno == ENOENT)
1213                 return (-2);
1214         if (error) {
1215                 snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules",
1216                     rulenum, strerror(errno));
1217                 return (-1);
1218         } else if (size != sizeof(*rule)) {
1219                 snprintf(errstr, errlen, "Data error in %s.%d: %s",
1220                     MIB ".rules", rulenum, strerror(errno));
1221                 return (-1);
1222         }
1223
1224         return (0);
1225 }
1226
1227 int
1228 bsde_delete_rule(int rulenum, size_t buflen, char *errstr)
1229 {
1230         struct mac_bsdextended_rule rule;
1231         int name[10];
1232         size_t len, size;
1233         int error;
1234
1235         if (bsde_check_version(buflen, errstr) != 0)
1236                 return (-1);
1237
1238         len = 10;
1239         error = bsde_get_mib(MIB ".rules", name, &len);
1240         if (error) {
1241                 snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1242                     strerror(errno));
1243                 return (-1);
1244         }
1245
1246         name[len] = rulenum;
1247         len++;
1248
1249         size = sizeof(rule);
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, size;
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         size = sizeof(*rule);
1283         error = sysctl(name, len, NULL, NULL, rule, size);
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, size;
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         size = sizeof(*rule);
1324         error = sysctl(name, len, NULL, NULL, rule, size);
1325         if (error) {
1326                 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1327                     rule_slots, strerror(errno));
1328                 return (-1);
1329         }
1330
1331         if (rulenum != NULL)
1332                 *rulenum = rule_slots;
1333
1334         return (0);
1335 }