]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libugidfw/ugidfw.c
Merge llvm, clang, lld, lldb, compiler-rt and libc++ r304222, and update
[FreeBSD/FreeBSD.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, numfs, i, notdone;
68
69         cur = buf;
70         left = buflen;
71
72         len = snprintf(cur, left, "subject ");
73         if (len < 0 || len > left)
74                 goto truncated;
75         left -= len;
76         cur += len;
77         if (rule->mbr_subject.mbs_flags) {
78                 if (rule->mbr_subject.mbs_neg == MBS_ALL_FLAGS) {
79                         len = snprintf(cur, left, "not ");
80                         if (len < 0 || len > left)
81                                 goto truncated;
82                         left -= len;
83                         cur += len;
84                         notdone = 1;
85                 } else {
86                         notdone = 0;
87                 }
88
89                 if (!notdone && (rule->mbr_subject.mbs_neg & MBO_UID_DEFINED)) {
90                         len = snprintf(cur, left, "! ");
91                         if (len < 0 || len > left)
92                                 goto truncated;
93                         left -= len;
94                         cur += len;
95                 }
96                 if (rule->mbr_subject.mbs_flags & MBO_UID_DEFINED) {
97                         pwd = getpwuid(rule->mbr_subject.mbs_uid_min);
98                         if (pwd != NULL) {
99                                 len = snprintf(cur, left, "uid %s",
100                                     pwd->pw_name);
101                                 if (len < 0 || len > left)
102                                         goto truncated;
103                                 left -= len;
104                                 cur += len;
105                         } else {
106                                 len = snprintf(cur, left, "uid %u",
107                                     rule->mbr_subject.mbs_uid_min);
108                                 if (len < 0 || len > left)
109                                         goto truncated;
110                                 left -= len;
111                                 cur += len;
112                         }
113                         if (rule->mbr_subject.mbs_uid_min !=
114                             rule->mbr_subject.mbs_uid_max) {
115                                 pwd = getpwuid(rule->mbr_subject.mbs_uid_max);
116                                 if (pwd != NULL) {
117                                         len = snprintf(cur, left, ":%s ",
118                                             pwd->pw_name);
119                                         if (len < 0 || len > left)
120                                                 goto truncated;
121                                         left -= len;
122                                         cur += len;
123                                 } else {
124                                         len = snprintf(cur, left, ":%u ",
125                                             rule->mbr_subject.mbs_uid_max);
126                                         if (len < 0 || len > left)
127                                                 goto truncated;
128                                         left -= len;
129                                         cur += len;
130                                 }
131                         } else {
132                                 len = snprintf(cur, left, " ");
133                                 if (len < 0 || len > left)
134                                         goto truncated;
135                                 left -= len;
136                                 cur += len;
137                         }
138                 }
139                 if (!notdone && (rule->mbr_subject.mbs_neg & MBO_GID_DEFINED)) {
140                         len = snprintf(cur, left, "! ");
141                         if (len < 0 || len > left)
142                                 goto truncated;
143                         left -= len;
144                         cur += len;
145                 }
146                 if (rule->mbr_subject.mbs_flags & MBO_GID_DEFINED) {
147                         grp = getgrgid(rule->mbr_subject.mbs_gid_min);
148                         if (grp != NULL) {
149                                 len = snprintf(cur, left, "gid %s",
150                                     grp->gr_name);
151                                 if (len < 0 || len > left)
152                                         goto truncated;
153                                 left -= len;
154                                 cur += len;
155                         } else {
156                                 len = snprintf(cur, left, "gid %u",
157                                     rule->mbr_subject.mbs_gid_min);
158                                 if (len < 0 || len > left)
159                                         goto truncated;
160                                 left -= len;
161                                 cur += len;
162                         }
163                         if (rule->mbr_subject.mbs_gid_min !=
164                             rule->mbr_subject.mbs_gid_max) {
165                                 grp = getgrgid(rule->mbr_subject.mbs_gid_max);
166                                 if (grp != NULL) {
167                                         len = snprintf(cur, left, ":%s ",
168                                             grp->gr_name);
169                                         if (len < 0 || len > left)
170                                                 goto truncated;
171                                         left -= len;
172                                         cur += len;
173                                 } else {
174                                         len = snprintf(cur, left, ":%u ",
175                                             rule->mbr_subject.mbs_gid_max);
176                                         if (len < 0 || len > left)
177                                                 goto truncated;
178                                         left -= len;
179                                         cur += len;
180                                 }
181                         } else {
182                                 len = snprintf(cur, left, " ");
183                                 if (len < 0 || len > left)
184                                         goto truncated;
185                                 left -= len;
186                                 cur += len;
187                         }
188                 }
189                 if (!notdone && (rule->mbr_subject.mbs_neg & MBS_PRISON_DEFINED)) {
190                         len = snprintf(cur, left, "! ");
191                         if (len < 0 || len > left)
192                                 goto truncated;
193                         left -= len;
194                         cur += len;
195                 }
196                 if (rule->mbr_subject.mbs_flags & MBS_PRISON_DEFINED) {
197                         len = snprintf(cur, left, "jailid %d ", 
198                             rule->mbr_subject.mbs_prison);
199                         if (len < 0 || len > left)
200                                 goto truncated;
201                         left -= len;
202                         cur += len;
203                 }
204         }
205
206         len = snprintf(cur, left, "object ");
207         if (len < 0 || len > left)
208                 goto truncated;
209         left -= len;
210         cur += len;
211         if (rule->mbr_object.mbo_flags) {
212                 if (rule->mbr_object.mbo_neg == MBO_ALL_FLAGS) {
213                         len = snprintf(cur, left, "not ");
214                         if (len < 0 || len > left)
215                                 goto truncated;
216                         left -= len;
217                         cur += len;
218                         notdone = 1;
219                 } else {
220                         notdone = 0;
221                 }
222
223                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_UID_DEFINED)) {
224                         len = snprintf(cur, left, "! ");
225                         if (len < 0 || len > left)
226                                 goto truncated;
227                         left -= len;
228                         cur += len;
229                 }
230                 if (rule->mbr_object.mbo_flags & MBO_UID_DEFINED) {
231                         pwd = getpwuid(rule->mbr_object.mbo_uid_min);
232                         if (pwd != NULL) {
233                                 len = snprintf(cur, left, "uid %s",
234                                     pwd->pw_name);
235                                 if (len < 0 || len > left)
236                                         goto truncated;
237                                 left -= len;
238                                 cur += len;
239                         } else {
240                                 len = snprintf(cur, left, "uid %u",
241                                     rule->mbr_object.mbo_uid_min);
242                                 if (len < 0 || len > left)
243                                         goto truncated;
244                                 left -= len;
245                                 cur += len;
246                         }
247                         if (rule->mbr_object.mbo_uid_min !=
248                             rule->mbr_object.mbo_uid_max) {
249                                 pwd = getpwuid(rule->mbr_object.mbo_uid_max);
250                                 if (pwd != NULL) {
251                                         len = snprintf(cur, left, ":%s ",
252                                             pwd->pw_name);
253                                         if (len < 0 || len > left)
254                                                 goto truncated;
255                                         left -= len;
256                                         cur += len;
257                                 } else {
258                                         len = snprintf(cur, left, ":%u ",
259                                             rule->mbr_object.mbo_uid_max);
260                                         if (len < 0 || len > left)
261                                                 goto truncated;
262                                         left -= len;
263                                         cur += len;
264                                 }
265                         } else {
266                                 len = snprintf(cur, left, " ");
267                                 if (len < 0 || len > left)
268                                         goto truncated;
269                                 left -= len;
270                                 cur += len;
271                         }
272                 }
273                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_GID_DEFINED)) {
274                         len = snprintf(cur, left, "! ");
275                         if (len < 0 || len > left)
276                                 goto truncated;
277                         left -= len;
278                         cur += len;
279                 }
280                 if (rule->mbr_object.mbo_flags & MBO_GID_DEFINED) {
281                         grp = getgrgid(rule->mbr_object.mbo_gid_min);
282                         if (grp != NULL) {
283                                 len = snprintf(cur, left, "gid %s",
284                                     grp->gr_name);
285                                 if (len < 0 || len > left)
286                                         goto truncated;
287                                 left -= len;
288                                 cur += len;
289                         } else {
290                                 len = snprintf(cur, left, "gid %u",
291                                     rule->mbr_object.mbo_gid_min);
292                                 if (len < 0 || len > left)
293                                         goto truncated;
294                                 left -= len;
295                                 cur += len;
296                         }
297                         if (rule->mbr_object.mbo_gid_min !=
298                             rule->mbr_object.mbo_gid_max) {
299                                 grp = getgrgid(rule->mbr_object.mbo_gid_max);
300                                 if (grp != NULL) {
301                                         len = snprintf(cur, left, ":%s ",
302                                             grp->gr_name);
303                                         if (len < 0 || len > left)
304                                                 goto truncated;
305                                         left -= len;
306                                         cur += len;
307                                 } else {
308                                         len = snprintf(cur, left, ":%u ",
309                                             rule->mbr_object.mbo_gid_max);
310                                         if (len < 0 || len > left)
311                                                 goto truncated;
312                                         left -= len;
313                                         cur += len;
314                                 }
315                         } else {
316                                 len = snprintf(cur, left, " ");
317                                 if (len < 0 || len > left)
318                                         goto truncated;
319                                 left -= len;
320                                 cur += len;
321                         }
322                 }
323                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_FSID_DEFINED)) {
324                         len = snprintf(cur, left, "! ");
325                         if (len < 0 || len > left)
326                                 goto truncated;
327                         left -= len;
328                         cur += len;
329                 }
330                 if (rule->mbr_object.mbo_flags & MBO_FSID_DEFINED) {
331                         numfs = getmntinfo(&mntbuf, MNT_NOWAIT);
332                         for (i = 0; i < numfs; i++)
333                                 if (memcmp(&(rule->mbr_object.mbo_fsid),
334                                     &(mntbuf[i].f_fsid),
335                                     sizeof(mntbuf[i].f_fsid)) == 0)
336                                         break;
337                         len = snprintf(cur, left, "filesys %s ", 
338                             i == numfs ? "???" : mntbuf[i].f_mntonname);
339                         if (len < 0 || len > left)
340                                 goto truncated;
341                         left -= len;
342                         cur += len;
343                 }
344                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_SUID)) {
345                         len = snprintf(cur, left, "! ");
346                         if (len < 0 || len > left)
347                                 goto truncated;
348                         left -= len;
349                         cur += len;
350                 }
351                 if (rule->mbr_object.mbo_flags & MBO_SUID) {
352                         len = snprintf(cur, left, "suid ");
353                         if (len < 0 || len > left)
354                                 goto truncated;
355                         left -= len;
356                         cur += len;
357                 }
358                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_SGID)) {
359                         len = snprintf(cur, left, "! ");
360                         if (len < 0 || len > left)
361                                 goto truncated;
362                         left -= len;
363                         cur += len;
364                 }
365                 if (rule->mbr_object.mbo_flags & MBO_SGID) {
366                         len = snprintf(cur, left, "sgid ");
367                         if (len < 0 || len > left)
368                                 goto truncated;
369                         left -= len;
370                         cur += len;
371                 }
372                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_UID_SUBJECT)) {
373                         len = snprintf(cur, left, "! ");
374                         if (len < 0 || len > left)
375                                 goto truncated;
376                         left -= len;
377                         cur += len;
378                 }
379                 if (rule->mbr_object.mbo_flags & MBO_UID_SUBJECT) {
380                         len = snprintf(cur, left, "uid_of_subject ");
381                         if (len < 0 || len > left)
382                                 goto truncated;
383                         left -= len;
384                         cur += len;
385                 }
386                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_GID_SUBJECT)) {
387                         len = snprintf(cur, left, "! ");
388                         if (len < 0 || len > left)
389                                 goto truncated;
390                         left -= len;
391                         cur += len;
392                 }
393                 if (rule->mbr_object.mbo_flags & MBO_GID_SUBJECT) {
394                         len = snprintf(cur, left, "gid_of_subject ");
395                         if (len < 0 || len > left)
396                                 goto truncated;
397                         left -= len;
398                         cur += len;
399                 }
400                 if (!notdone && (rule->mbr_object.mbo_neg & MBO_TYPE_DEFINED)) {
401                         len = snprintf(cur, left, "! ");
402                         if (len < 0 || len > left)
403                                 goto truncated;
404                         left -= len;
405                         cur += len;
406                 }
407                 if (rule->mbr_object.mbo_flags & MBO_TYPE_DEFINED) {
408                         i = 0;
409                         if (rule->mbr_object.mbo_type & MBO_TYPE_REG)
410                                 type[i++] = 'r';
411                         if (rule->mbr_object.mbo_type & MBO_TYPE_DIR)
412                                 type[i++] = 'd';
413                         if (rule->mbr_object.mbo_type & MBO_TYPE_BLK)
414                                 type[i++] = 'b';
415                         if (rule->mbr_object.mbo_type & MBO_TYPE_CHR)
416                                 type[i++] = 'c';
417                         if (rule->mbr_object.mbo_type & MBO_TYPE_LNK)
418                                 type[i++] = 'l';
419                         if (rule->mbr_object.mbo_type & MBO_TYPE_SOCK)
420                                 type[i++] = 's';
421                         if (rule->mbr_object.mbo_type & MBO_TYPE_FIFO)
422                                 type[i++] = 'p';
423                         if (rule->mbr_object.mbo_type == MBO_ALL_TYPE) {
424                                 i = 0;
425                                 type[i++] = 'a';
426                         }
427                         type[i++] = '\0';
428                         len = snprintf(cur, left, "type %s ", type);
429                         if (len < 0 || len > left)
430                                 goto truncated;
431                         left -= len;
432                         cur += len;
433                 }
434         }
435
436         len = snprintf(cur, left, "mode ");
437         if (len < 0 || len > left)
438                 goto truncated;
439         left -= len;
440         cur += len;
441
442         anymode = (rule->mbr_mode & MBI_ALLPERM);
443         unknownmode = (rule->mbr_mode & ~MBI_ALLPERM);
444
445         if (rule->mbr_mode & MBI_ADMIN) {
446                 len = snprintf(cur, left, "a");
447                 if (len < 0 || len > left)
448                         goto truncated;
449
450                 left -= len;
451                 cur += len;
452         }
453         if (rule->mbr_mode & MBI_READ) {
454                 len = snprintf(cur, left, "r");
455                 if (len < 0 || len > left)
456                         goto truncated;
457
458                 left -= len;
459                 cur += len;
460         }
461         if (rule->mbr_mode & MBI_STAT) {
462                 len = snprintf(cur, left, "s");
463                 if (len < 0 || len > left)
464                         goto truncated;
465
466                 left -= len;
467                 cur += len;
468         }
469         if (rule->mbr_mode & MBI_WRITE) {
470                 len = snprintf(cur, left, "w");
471                 if (len < 0 || len > left)
472                         goto truncated;
473
474                 left -= len;
475                 cur += len;
476         }
477         if (rule->mbr_mode & MBI_EXEC) {
478                 len = snprintf(cur, left, "x");
479                 if (len < 0 || len > left)
480                         goto truncated;
481
482                 left -= len;
483                 cur += len;
484         }
485         if (!anymode) {
486                 len = snprintf(cur, left, "n");
487                 if (len < 0 || len > left)
488                         goto truncated;
489
490                 left -= len;
491                 cur += len;
492         }
493         if (unknownmode) {
494                 len = snprintf(cur, left, "?");
495                 if (len < 0 || len > left)
496                         goto truncated;
497
498                 left -= len;
499                 cur += len;
500         }
501
502         return (0);
503
504 truncated:
505         return (-1);
506 }
507
508 static int
509 bsde_parse_uidrange(char *spec, uid_t *min, uid_t *max,
510     size_t buflen, char *errstr){
511         struct passwd *pwd;
512         uid_t uid1, uid2;
513         char *spec1, *spec2, *endp;
514         unsigned long value;
515         size_t len;
516
517         spec2 = spec;
518         spec1 = strsep(&spec2, ":");
519
520         pwd = getpwnam(spec1);
521         if (pwd != NULL)
522                 uid1 = pwd->pw_uid;
523         else {
524                 value = strtoul(spec1, &endp, 10);
525                 if (*endp != '\0') {
526                         len = snprintf(errstr, buflen,
527                             "invalid uid: '%s'", spec1);
528                         return (-1);
529                 }
530                 uid1 = value;
531         }
532
533         if (spec2 == NULL) {
534                 *max = *min = uid1;
535                 return (0);
536         }
537
538         pwd = getpwnam(spec2);
539         if (pwd != NULL)
540                 uid2 = pwd->pw_uid;
541         else {
542                 value = strtoul(spec2, &endp, 10);
543                 if (*endp != '\0') {
544                         len = snprintf(errstr, buflen,
545                             "invalid uid: '%s'", spec2);
546                         return (-1);
547                 }
548                 uid2 = value;
549         }
550
551         *min = uid1;
552         *max = uid2;
553
554         return (0);
555 }
556
557 static int
558 bsde_parse_gidrange(char *spec, gid_t *min, gid_t *max,
559     size_t buflen, char *errstr){
560         struct group *grp;
561         gid_t gid1, gid2;
562         char *spec1, *spec2, *endp;
563         unsigned long value;
564         size_t len;
565
566         spec2 = spec;
567         spec1 = strsep(&spec2, ":");
568
569         grp = getgrnam(spec1);
570         if (grp != NULL)
571                 gid1 = grp->gr_gid;
572         else {
573                 value = strtoul(spec1, &endp, 10);
574                 if (*endp != '\0') {
575                         len = snprintf(errstr, buflen,
576                             "invalid gid: '%s'", spec1);
577                         return (-1);
578                 }
579                 gid1 = value;
580         }
581
582         if (spec2 == NULL) {
583                 *max = *min = gid1;
584                 return (0);
585         }
586
587         grp = getgrnam(spec2);
588         if (grp != NULL)
589                 gid2 = grp->gr_gid;
590         else {
591                 value = strtoul(spec2, &endp, 10);
592                 if (*endp != '\0') {
593                         len = snprintf(errstr, buflen,
594                             "invalid gid: '%s'", spec2);
595                         return (-1);
596                 }
597                 gid2 = value;
598         }
599
600         *min = gid1;
601         *max = gid2;
602
603         return (0);
604 }
605
606 static int
607 bsde_parse_subject(int argc, char *argv[],
608     struct mac_bsdextended_subject *subject, size_t buflen, char *errstr)
609 {
610         int not_seen, flags;
611         int current, neg, nextnot;
612         char *endp;
613         uid_t uid_min, uid_max;
614         gid_t gid_min, gid_max;
615         int jid = 0;
616         size_t len;
617         long value;
618
619         current = 0;
620         flags = 0;
621         neg = 0;
622         nextnot = 0;
623
624         if (strcmp("not", argv[current]) == 0) {
625                 not_seen = 1;
626                 current++;
627         } else
628                 not_seen = 0;
629
630         while (current < argc) {
631                 if (strcmp(argv[current], "uid") == 0) {
632                         if (current + 2 > argc) {
633                                 len = snprintf(errstr, buflen, "uid short");
634                                 return (-1);
635                         }
636                         if (flags & MBS_UID_DEFINED) {
637                                 len = snprintf(errstr, buflen, "one uid only");
638                                 return (-1);
639                         }
640                         if (bsde_parse_uidrange(argv[current+1],
641                             &uid_min, &uid_max, buflen, errstr) < 0)
642                                 return (-1);
643                         flags |= MBS_UID_DEFINED;
644                         if (nextnot) {
645                                 neg ^= MBS_UID_DEFINED;
646                                 nextnot = 0;
647                         }
648                         current += 2;
649                 } else if (strcmp(argv[current], "gid") == 0) {
650                         if (current + 2 > argc) {
651                                 len = snprintf(errstr, buflen, "gid short");
652                                 return (-1);
653                         }
654                         if (flags & MBS_GID_DEFINED) {
655                                 len = snprintf(errstr, buflen, "one gid only");
656                                 return (-1);
657                         }
658                         if (bsde_parse_gidrange(argv[current+1],
659                             &gid_min, &gid_max, buflen, errstr) < 0)
660                                 return (-1);
661                         flags |= MBS_GID_DEFINED;
662                         if (nextnot) {
663                                 neg ^= MBS_GID_DEFINED;
664                                 nextnot = 0;
665                         }
666                         current += 2;
667                 } else if (strcmp(argv[current], "jailid") == 0) {
668                         if (current + 2 > argc) {
669                                 len = snprintf(errstr, buflen, "prison short");
670                                 return (-1);
671                         }
672                         if (flags & MBS_PRISON_DEFINED) {
673                                 len = snprintf(errstr, buflen, "one jail only");
674                                 return (-1);
675                         }
676                         value = strtol(argv[current+1], &endp, 10);
677                         if (*endp != '\0') {
678                                 len = snprintf(errstr, buflen,
679                                     "invalid jid: '%s'", argv[current+1]);
680                                 return (-1);
681                         }
682                         jid = value;
683                         flags |= MBS_PRISON_DEFINED;
684                         if (nextnot) {
685                                 neg ^= MBS_PRISON_DEFINED;
686                                 nextnot = 0;
687                         }
688                         current += 2;
689                 } else if (strcmp(argv[current], "!") == 0) {
690                         if (nextnot) {
691                                 len = snprintf(errstr, buflen,
692                                     "double negative");
693                                 return (-1);
694                         }
695                         nextnot = 1;
696                         current += 1;
697                 } else {
698                         len = snprintf(errstr, buflen, "'%s' not expected",
699                             argv[current]);
700                         return (-1);
701                 }
702         }
703
704         subject->mbs_flags = flags;
705         if (not_seen)
706                 subject->mbs_neg = MBS_ALL_FLAGS ^ neg;
707         else
708                 subject->mbs_neg = neg;
709         if (flags & MBS_UID_DEFINED) {
710                 subject->mbs_uid_min = uid_min;
711                 subject->mbs_uid_max = uid_max;
712         }
713         if (flags & MBS_GID_DEFINED) {
714                 subject->mbs_gid_min = gid_min;
715                 subject->mbs_gid_max = gid_max;
716         }
717         if (flags & MBS_PRISON_DEFINED)
718                 subject->mbs_prison = jid;
719
720         return (0);
721 }
722
723 static int
724 bsde_parse_type(char *spec, int *type, size_t buflen, char *errstr)
725 {
726         size_t len;
727         int i;
728
729         *type = 0;
730         for (i = 0; i < strlen(spec); i++) {
731                 switch (spec[i]) {
732                 case 'r':
733                 case '-':
734                         *type |= MBO_TYPE_REG;
735                         break;
736                 case 'd':
737                         *type |= MBO_TYPE_DIR;
738                         break;
739                 case 'b':
740                         *type |= MBO_TYPE_BLK;
741                         break;
742                 case 'c':
743                         *type |= MBO_TYPE_CHR;
744                         break;
745                 case 'l':
746                         *type |= MBO_TYPE_LNK;
747                         break;
748                 case 's':
749                         *type |= MBO_TYPE_SOCK;
750                         break;
751                 case 'p':
752                         *type |= MBO_TYPE_FIFO;
753                         break;
754                 case 'a':
755                         *type |= MBO_ALL_TYPE;
756                         break;
757                 default:
758                         len = snprintf(errstr, buflen, "Unknown type code: %c",
759                             spec[i]);
760                         return (-1);
761                 } 
762         }
763
764         return (0);
765 }
766
767 static int
768 bsde_parse_fsid(char *spec, struct fsid *fsid, size_t buflen, char *errstr)
769 {
770         size_t len;
771         struct statfs buf;
772
773         if (statfs(spec, &buf) < 0) {
774                 len = snprintf(errstr, buflen, "Unable to get id for %s: %s",
775                     spec, strerror(errno));
776                 return (-1);
777         }
778
779         *fsid = buf.f_fsid;
780
781         return (0);
782 }
783
784 static int
785 bsde_parse_object(int argc, char *argv[],
786     struct mac_bsdextended_object *object, size_t buflen, char *errstr)
787 {
788         int not_seen, flags;
789         int current, neg, nextnot;
790         int type;
791         uid_t uid_min, uid_max;
792         gid_t gid_min, gid_max;
793         struct fsid fsid;
794         size_t len;
795
796         current = 0;
797         flags = 0;
798         neg = 0;
799         nextnot = 0;
800         type = 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 static 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;
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         error = sysctl(name, len, NULL, NULL, &rule, 0);
1237         if (error) {
1238                 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1239                     rulenum, strerror(errno));
1240                 return (-1);
1241         }
1242
1243         return (0);
1244 }
1245
1246 int
1247 bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1248     char *errstr)
1249 {
1250         int name[10];
1251         size_t len;
1252         int error;
1253
1254         if (bsde_check_version(buflen, errstr) != 0)
1255                 return (-1);
1256
1257         len = 10;
1258         error = bsde_get_mib(MIB ".rules", name, &len);
1259         if (error) {
1260                 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1261                     strerror(errno));
1262                 return (-1);
1263         }
1264
1265         name[len] = rulenum;
1266         len++;
1267
1268         error = sysctl(name, len, NULL, NULL, rule, sizeof(*rule));
1269         if (error) {
1270                 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1271                     rulenum, strerror(errno));
1272                 return (-1);
1273         }
1274
1275         return (0);
1276 }
1277
1278 int
1279 bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1280     char *errstr)
1281 {
1282         char charstr[BUFSIZ];
1283         int name[10];
1284         size_t len;
1285         int error, rule_slots;
1286
1287         if (bsde_check_version(buflen, errstr) != 0)
1288                 return (-1);
1289
1290         len = 10;
1291         error = bsde_get_mib(MIB ".rules", name, &len);
1292         if (error) {
1293                 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1294                     strerror(errno));
1295                 return (-1);
1296         }
1297
1298         rule_slots = bsde_get_rule_slots(BUFSIZ, charstr);
1299         if (rule_slots == -1) {
1300                 len = snprintf(errstr, buflen, "unable to get rule slots: %s",
1301                     strerror(errno));
1302                 return (-1);
1303         }
1304
1305         name[len] = rule_slots;
1306         len++;
1307
1308         error = sysctl(name, len, NULL, NULL, rule, sizeof(*rule));
1309         if (error) {
1310                 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1311                     rule_slots, strerror(errno));
1312                 return (-1);
1313         }
1314
1315         if (rulenum != NULL)
1316                 *rulenum = rule_slots;
1317
1318         return (0);
1319 }