]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - lib/libugidfw/ugidfw.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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         int i;
774
775         if (statfs(spec, &buf) < 0) {
776                 len = snprintf(errstr, buflen, "Unable to get id for %s: %s",
777                     spec, strerror(errno));
778                 return (-1);
779         }
780
781         *fsid = buf.f_fsid;
782
783         return (0);
784 }
785
786 int
787 bsde_parse_object(int argc, char *argv[],
788     struct mac_bsdextended_object *object, size_t buflen, char *errstr)
789 {
790         int not_seen, flags;
791         int current, neg, nextnot;
792         uid_t uid_min, uid_max;
793         gid_t gid_min, gid_max;
794         int type;
795         struct fsid fsid;
796         size_t len;
797
798         current = 0;
799         flags = 0;
800         neg = 0;
801         nextnot = 0;
802
803         if (strcmp("not", argv[current]) == 0) {
804                 not_seen = 1;
805                 current++;
806         } else
807                 not_seen = 0;
808
809         while (current < argc) {
810                 if (strcmp(argv[current], "uid") == 0) {
811                         if (current + 2 > argc) {
812                                 len = snprintf(errstr, buflen, "uid short");
813                                 return (-1);
814                         }
815                         if (flags & MBO_UID_DEFINED) {
816                                 len = snprintf(errstr, buflen, "one uid only");
817                                 return (-1);
818                         }
819                         if (bsde_parse_uidrange(argv[current+1],
820                             &uid_min, &uid_max, buflen, errstr) < 0)
821                                 return (-1);
822                         flags |= MBO_UID_DEFINED;
823                         if (nextnot) {
824                                 neg ^= MBO_UID_DEFINED;
825                                 nextnot = 0;
826                         }
827                         current += 2;
828                 } else if (strcmp(argv[current], "gid") == 0) {
829                         if (current + 2 > argc) {
830                                 len = snprintf(errstr, buflen, "gid short");
831                                 return (-1);
832                         }
833                         if (flags & MBO_GID_DEFINED) {
834                                 len = snprintf(errstr, buflen, "one gid only");
835                                 return (-1);
836                         }
837                         if (bsde_parse_gidrange(argv[current+1],
838                             &gid_min, &gid_max, buflen, errstr) < 0)
839                                 return (-1);
840                         flags |= MBO_GID_DEFINED;
841                         if (nextnot) {
842                                 neg ^= MBO_GID_DEFINED;
843                                 nextnot = 0;
844                         }
845                         current += 2;
846                 } else if (strcmp(argv[current], "filesys") == 0) {
847                         if (current + 2 > argc) {
848                                 len = snprintf(errstr, buflen, "filesys short");
849                                 return (-1);
850                         }
851                         if (flags & MBO_FSID_DEFINED) {
852                                 len = snprintf(errstr, buflen, "one fsid only");
853                                 return (-1);
854                         }
855                         if (bsde_parse_fsid(argv[current+1], &fsid,
856                             buflen, errstr) < 0)
857                                 return (-1);
858                         flags |= MBO_FSID_DEFINED;
859                         if (nextnot) {
860                                 neg ^= MBO_FSID_DEFINED;
861                                 nextnot = 0;
862                         }
863                         current += 2;
864                 } else if (strcmp(argv[current], "suid") == 0) {
865                         flags |= MBO_SUID;
866                         if (nextnot) {
867                                 neg ^= MBO_SUID;
868                                 nextnot = 0;
869                         }
870                         current += 1;
871                 } else if (strcmp(argv[current], "sgid") == 0) {
872                         flags |= MBO_SGID;
873                         if (nextnot) {
874                                 neg ^= MBO_SGID;
875                                 nextnot = 0;
876                         }
877                         current += 1;
878                 } else if (strcmp(argv[current], "uid_of_subject") == 0) {
879                         flags |= MBO_UID_SUBJECT;
880                         if (nextnot) {
881                                 neg ^= MBO_UID_SUBJECT;
882                                 nextnot = 0;
883                         }
884                         current += 1;
885                 } else if (strcmp(argv[current], "gid_of_subject") == 0) {
886                         flags |= MBO_GID_SUBJECT;
887                         if (nextnot) {
888                                 neg ^= MBO_GID_SUBJECT;
889                                 nextnot = 0;
890                         }
891                         current += 1;
892                 } else if (strcmp(argv[current], "type") == 0) {
893                         if (current + 2 > argc) {
894                                 len = snprintf(errstr, buflen, "type short");
895                                 return (-1);
896                         }
897                         if (flags & MBO_TYPE_DEFINED) {
898                                 len = snprintf(errstr, buflen, "one type only");
899                                 return (-1);
900                         }
901                         if (bsde_parse_type(argv[current+1], &type,
902                             buflen, errstr) < 0)
903                                 return (-1);
904                         flags |= MBO_TYPE_DEFINED;
905                         if (nextnot) {
906                                 neg ^= MBO_TYPE_DEFINED;
907                                 nextnot = 0;
908                         }
909                         current += 2;
910                 } else if (strcmp(argv[current], "!") == 0) {
911                         if (nextnot) {
912                                 len = snprintf(errstr, buflen,
913                                     "double negative'");
914                                 return (-1);
915                         }
916                         nextnot = 1;
917                         current += 1;
918                 } else {
919                         len = snprintf(errstr, buflen, "'%s' not expected",
920                             argv[current]);
921                         return (-1);
922                 }
923         }
924
925         object->mbo_flags = flags;
926         if (not_seen)
927                 object->mbo_neg = MBO_ALL_FLAGS ^ neg;
928         else
929                 object->mbo_neg = neg;
930         if (flags & MBO_UID_DEFINED) {
931                 object->mbo_uid_min = uid_min;
932                 object->mbo_uid_max = uid_max;
933         }
934         if (flags & MBO_GID_DEFINED) {
935                 object->mbo_gid_min = gid_min;
936                 object->mbo_gid_max = gid_max;
937         }
938         if (flags & MBO_FSID_DEFINED)
939                 object->mbo_fsid = fsid;
940         if (flags & MBO_TYPE_DEFINED)
941                 object->mbo_type = type;
942
943         return (0);
944 }
945
946 int
947 bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen,
948     char *errstr)
949 {
950         size_t len;
951         int i;
952
953         if (argc == 0) {
954                 len = snprintf(errstr, buflen, "mode expects mode value");
955                 return (-1);
956         }
957
958         if (argc != 1) {
959                 len = snprintf(errstr, buflen, "'%s' unexpected", argv[1]);
960                 return (-1);
961         }
962
963         *mode = 0;
964         for (i = 0; i < strlen(argv[0]); i++) {
965                 switch (argv[0][i]) {
966                 case 'a':
967                         *mode |= MBI_ADMIN;
968                         break;
969                 case 'r':
970                         *mode |= MBI_READ;
971                         break;
972                 case 's':
973                         *mode |= MBI_STAT;
974                         break;
975                 case 'w':
976                         *mode |= MBI_WRITE;
977                         break;
978                 case 'x':
979                         *mode |= MBI_EXEC;
980                         break;
981                 case 'n':
982                         /* ignore */
983                         break;
984                 default:
985                         len = snprintf(errstr, buflen, "Unknown mode letter: %c",
986                             argv[0][i]);
987                         return (-1);
988                 } 
989         }
990
991         return (0);
992 }
993
994 int
995 bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule,
996     size_t buflen, char *errstr)
997 {
998         int subject, subject_elements, subject_elements_length;
999         int object, object_elements, object_elements_length;
1000         int mode, mode_elements, mode_elements_length;
1001         int error, i;
1002         size_t len;
1003
1004         bzero(rule, sizeof(*rule));
1005
1006         if (argc < 1) {
1007                 len = snprintf(errstr, buflen, "Rule must begin with subject");
1008                 return (-1);
1009         }
1010
1011         if (strcmp(argv[0], "subject") != 0) {
1012                 len = snprintf(errstr, buflen, "Rule must begin with subject");
1013                 return (-1);
1014         }
1015         subject = 0;
1016         subject_elements = 1;
1017
1018         /* Search forward for object. */
1019
1020         object = -1;
1021         for (i = 1; i < argc; i++)
1022                 if (strcmp(argv[i], "object") == 0)
1023                         object = i;
1024
1025         if (object == -1) {
1026                 len = snprintf(errstr, buflen, "Rule must contain an object");
1027                 return (-1);
1028         }
1029
1030         /* Search forward for mode. */
1031         mode = -1;
1032         for (i = object; i < argc; i++)
1033                 if (strcmp(argv[i], "mode") == 0)
1034                         mode = i;
1035
1036         if (mode == -1) {
1037                 len = snprintf(errstr, buflen, "Rule must contain mode");
1038                 return (-1);
1039         }
1040
1041         subject_elements_length = object - subject - 1;
1042         object_elements = object + 1;
1043         object_elements_length = mode - object_elements;
1044         mode_elements = mode + 1;
1045         mode_elements_length = argc - mode_elements;
1046
1047         error = bsde_parse_subject(subject_elements_length,
1048             argv + subject_elements, &rule->mbr_subject, buflen, errstr);
1049         if (error)
1050                 return (-1);
1051
1052         error = bsde_parse_object(object_elements_length,
1053             argv + object_elements, &rule->mbr_object, buflen, errstr);
1054         if (error)
1055                 return (-1);
1056
1057         error = bsde_parse_mode(mode_elements_length, argv + mode_elements,
1058             &rule->mbr_mode, buflen, errstr);
1059         if (error)
1060                 return (-1);
1061
1062         return (0);
1063 }
1064
1065 int
1066 bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule,
1067     size_t buflen, char *errstr)
1068 {
1069         char *stringdup, *stringp, *argv[100], **ap;
1070         int argc, error;
1071
1072         stringp = stringdup = strdup(string);
1073         while (*stringp == ' ' || *stringp == '\t')
1074                 stringp++;
1075
1076         argc = 0;
1077         for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) {
1078                 argc++;
1079                 if (**ap != '\0')
1080                         if (++ap >= &argv[100])
1081                                 break;
1082         }
1083
1084         error = bsde_parse_rule(argc, argv, rule, buflen, errstr);
1085
1086         free(stringdup);
1087
1088         return (error);
1089 }
1090
1091 int
1092 bsde_get_mib(const char *string, int *name, size_t *namelen)
1093 {
1094         size_t len;
1095         int error;
1096
1097         len = *namelen;
1098         error = sysctlnametomib(string, name, &len);
1099         if (error)
1100                 return (error);
1101
1102         *namelen = len;
1103         return (0);
1104 }
1105
1106 int
1107 bsde_check_version(size_t buflen, char *errstr)
1108 {
1109         size_t len;
1110         int error;
1111         int version;
1112
1113         len = sizeof(version);
1114         error = sysctlbyname(MIB ".rule_version", &version, &len, NULL, 0);
1115         if (error) {
1116                 len = snprintf(errstr, buflen, "version check failed: %s",
1117                     strerror(errno));
1118                 return (-1);
1119         }
1120         if (version != MB_VERSION) {
1121                 len = snprintf(errstr, buflen, "module v%d != library v%d",
1122                     version, MB_VERSION);
1123                 return (-1);
1124         }
1125         return (0);
1126 }
1127
1128 int
1129 bsde_get_rule_count(size_t buflen, char *errstr)
1130 {
1131         size_t len;
1132         int error;
1133         int rule_count;
1134
1135         len = sizeof(rule_count);
1136         error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0);
1137         if (error) {
1138                 len = snprintf(errstr, buflen, strerror(errno));
1139                 return (-1);
1140         }
1141         if (len != sizeof(rule_count)) {
1142                 len = snprintf(errstr, buflen, "Data error in %s.rule_count",
1143                     MIB);
1144                 return (-1);
1145         }
1146
1147         return (rule_count);
1148 }
1149
1150 int
1151 bsde_get_rule_slots(size_t buflen, char *errstr)
1152 {
1153         size_t len;
1154         int error;
1155         int rule_slots;
1156
1157         len = sizeof(rule_slots);
1158         error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0);
1159         if (error) {
1160                 len = snprintf(errstr, buflen, strerror(errno));
1161                 return (-1);
1162         }
1163         if (len != sizeof(rule_slots)) {
1164                 len = snprintf(errstr, buflen, "Data error in %s.rule_slots",
1165                     MIB);
1166                 return (-1);
1167         }
1168
1169         return (rule_slots);
1170 }
1171
1172 /*
1173  * Returns 0 for success;
1174  * Returns -1 for failure;
1175  * Returns -2 for not present
1176  */
1177 int
1178 bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen,
1179     char *errstr)
1180 {
1181         int name[10];
1182         size_t len, size;
1183         int error;
1184
1185         if (bsde_check_version(errlen, errstr) != 0)
1186                 return (-1);
1187
1188         len = 10;
1189         error = bsde_get_mib(MIB ".rules", name, &len);
1190         if (error) {
1191                 len = snprintf(errstr, errlen, "%s: %s", MIB ".rules",
1192                     strerror(errno));
1193                 return (-1);
1194         }
1195
1196         size = sizeof(*rule);
1197         name[len] = rulenum;
1198         len++;
1199         error = sysctl(name, len, rule, &size, NULL, 0);
1200         if (error  == -1 && errno == ENOENT)
1201                 return (-2);
1202         if (error) {
1203                 len = snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules",
1204                     rulenum, strerror(errno));
1205                 return (-1);
1206         } else if (size != sizeof(*rule)) {
1207                 len = snprintf(errstr, errlen, "Data error in %s.%d: %s",
1208                     MIB ".rules", rulenum, strerror(errno));
1209                 return (-1);
1210         }
1211
1212         return (0);
1213 }
1214
1215 int
1216 bsde_delete_rule(int rulenum, size_t buflen, char *errstr)
1217 {
1218         struct mac_bsdextended_rule rule;
1219         int name[10];
1220         size_t len, size;
1221         int error;
1222
1223         if (bsde_check_version(buflen, errstr) != 0)
1224                 return (-1);
1225
1226         len = 10;
1227         error = bsde_get_mib(MIB ".rules", name, &len);
1228         if (error) {
1229                 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1230                     strerror(errno));
1231                 return (-1);
1232         }
1233
1234         name[len] = rulenum;
1235         len++;
1236
1237         size = sizeof(rule);
1238         error = sysctl(name, len, NULL, NULL, &rule, 0);
1239         if (error) {
1240                 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1241                     rulenum, strerror(errno));
1242                 return (-1);
1243         }
1244
1245         return (0);
1246 }
1247
1248 int
1249 bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1250     char *errstr)
1251 {
1252         int name[10];
1253         size_t len, size;
1254         int error;
1255
1256         if (bsde_check_version(buflen, errstr) != 0)
1257                 return (-1);
1258
1259         len = 10;
1260         error = bsde_get_mib(MIB ".rules", name, &len);
1261         if (error) {
1262                 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1263                     strerror(errno));
1264                 return (-1);
1265         }
1266
1267         name[len] = rulenum;
1268         len++;
1269
1270         size = sizeof(*rule);
1271         error = sysctl(name, len, NULL, NULL, rule, size);
1272         if (error) {
1273                 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1274                     rulenum, strerror(errno));
1275                 return (-1);
1276         }
1277
1278         return (0);
1279 }
1280
1281 int
1282 bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1283     char *errstr)
1284 {
1285         char charstr[BUFSIZ];
1286         int name[10];
1287         size_t len, size;
1288         int error, rule_slots;
1289
1290         if (bsde_check_version(buflen, errstr) != 0)
1291                 return (-1);
1292
1293         len = 10;
1294         error = bsde_get_mib(MIB ".rules", name, &len);
1295         if (error) {
1296                 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1297                     strerror(errno));
1298                 return (-1);
1299         }
1300
1301         rule_slots = bsde_get_rule_slots(BUFSIZ, charstr);
1302         if (rule_slots == -1) {
1303                 len = snprintf(errstr, buflen, "unable to get rule slots: %s",
1304                     strerror(errno));
1305                 return (-1);
1306         }
1307
1308         name[len] = rule_slots;
1309         len++;
1310
1311         size = sizeof(*rule);
1312         error = sysctl(name, len, NULL, NULL, rule, size);
1313         if (error) {
1314                 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1315                     rule_slots, strerror(errno));
1316                 return (-1);
1317         }
1318
1319         if (rulenum != NULL)
1320                 *rulenum = rule_slots;
1321
1322         return (0);
1323 }