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