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