]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libugidfw/ugidfw.c
This commit was generated by cvs2svn to compensate for changes in r153816,
[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
38 #include <security/mac_bsdextended/mac_bsdextended.h>
39
40 #include <grp.h>
41 #include <pwd.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include "ugidfw.h"
47
48 /*
49  * Text format for rules: rules contain subject and object elements, mode.
50  * Each element takes the form "[not] [uid number] [gid number]".
51  * The total form is "subject [element] object [element] mode [mode]".
52  * At least * one of a uid or gid entry must be present; both may also be
53  * present.
54  */
55
56 #define MIB     "security.mac.bsdextended"
57
58 int
59 bsde_rule_to_string(struct mac_bsdextended_rule *rule, char *buf, size_t buflen)
60 {
61         struct group *grp;
62         struct passwd *pwd;
63         char *cur;
64         size_t left, len;
65         int anymode, unknownmode, truncated;
66
67         cur = buf;
68         left = buflen;
69         truncated = 0;
70
71         if (rule->mbr_subject.mbi_flags & (MBI_UID_DEFINED |
72             MBI_GID_DEFINED)) {
73                 len = snprintf(cur, left, "subject ");
74                 if (len < 0 || len > left)
75                         goto truncated;
76                 left -= len;
77                 cur += len;
78
79                 if (rule->mbr_subject.mbi_flags & MBI_NEGATED) {
80                         len = snprintf(cur, left, "not ");
81                         if (len < 0 || len > left)
82                                 goto truncated;
83                         left -= len;
84                         cur += len;
85                 }
86                 if (rule->mbr_subject.mbi_flags & MBI_UID_DEFINED) {
87                         pwd = getpwuid(rule->mbr_subject.mbi_uid);
88                         if (pwd != NULL) {
89                                 len = snprintf(cur, left, "uid %s ",
90                                     pwd->pw_name);
91                                 if (len < 0 || len > left)
92                                         goto truncated;
93                                 left -= len;
94                                 cur += len;
95                         } else {
96                                 len = snprintf(cur, left, "uid %u ",
97                                     rule->mbr_subject.mbi_uid);
98                                 if (len < 0 || len > left)
99                                         goto truncated;
100                                 left -= len;
101                                 cur += len;
102                         }
103                 }
104                 if (rule->mbr_subject.mbi_flags & MBI_GID_DEFINED) {
105                         grp = getgrgid(rule->mbr_subject.mbi_gid);
106                         if (grp != NULL) {
107                                 len = snprintf(cur, left, "gid %s ",
108                                     grp->gr_name);
109                                 if (len < 0 || len > left)
110                                         goto truncated;
111                                 left -= len;
112                                 cur += len;
113                         } else {
114                                 len = snprintf(cur, left, "gid %u ",
115                                     rule->mbr_subject.mbi_gid);
116                                 if (len < 0 || len > left)
117                                         goto truncated;
118                                 left -= len;
119                                 cur += len;
120                         }
121                 }
122         }
123         if (rule->mbr_object.mbi_flags & (MBI_UID_DEFINED |
124             MBI_GID_DEFINED)) {
125                 len = snprintf(cur, left, "object ");
126                 if (len < 0 || len > left)
127                         goto truncated;
128                 left -= len;
129                 cur += len;
130
131                 if (rule->mbr_object.mbi_flags & MBI_NEGATED) {
132                         len = snprintf(cur, left, "not ");
133                         if (len < 0 || len > left)
134                                 goto truncated;
135                         left -= len;
136                         cur += len;
137                 }
138                 if (rule->mbr_object.mbi_flags & MBI_UID_DEFINED) {
139                         pwd = getpwuid(rule->mbr_object.mbi_uid);
140                         if (pwd != NULL) {
141                                 len = snprintf(cur, left, "uid %s ",
142                                     pwd->pw_name);
143                                 if (len < 0 || len > left)
144                                         goto truncated;
145                                 left -= len;
146                                 cur += len;
147                         } else {
148                                 len = snprintf(cur, left, "uid %u ",
149                                     rule->mbr_object.mbi_uid);
150                                 left -= len;
151                                 cur += len;
152                         }
153                 }
154                 if (rule->mbr_object.mbi_flags & MBI_GID_DEFINED) {
155                         grp = getgrgid(rule->mbr_object.mbi_gid);
156                         if (grp != NULL) {
157                                 len = snprintf(cur, left, "gid %s ",
158                                     grp->gr_name);
159                                 if (len < 0 || len > left)
160                                         goto truncated;
161                                 left -= len;
162                                 cur += len;
163                         } else {
164                                 len = snprintf(cur, left, "gid %u ",
165                                     rule->mbr_object.mbi_gid);
166                                 if (len < 0 || len > left)
167                                         goto truncated;
168                                 left -= len;
169                                 cur += len;
170                         }
171                 }
172         }
173
174         len = snprintf(cur, left, "mode ");
175         if (len < 0 || len > left)
176                 goto truncated;
177         left -= len;
178         cur += len;
179
180         anymode = (rule->mbr_mode & MBI_ALLPERM);
181         unknownmode = (rule->mbr_mode & ~MBI_ALLPERM);
182
183         if (rule->mbr_mode & MBI_ADMIN) {
184                 len = snprintf(cur, left, "a");
185                 if (len < 0 || len > left)
186                         goto truncated;
187
188                 left -= len;
189                 cur += len;
190         }
191         if (rule->mbr_mode & MBI_READ) {
192                 len = snprintf(cur, left, "r");
193                 if (len < 0 || len > left)
194                         goto truncated;
195
196                 left -= len;
197                 cur += len;
198         }
199         if (rule->mbr_mode & MBI_STAT) {
200                 len = snprintf(cur, left, "s");
201                 if (len < 0 || len > left)
202                         goto truncated;
203
204                 left -= len;
205                 cur += len;
206         }
207         if (rule->mbr_mode & MBI_WRITE) {
208                 len = snprintf(cur, left, "w");
209                 if (len < 0 || len > left)
210                         goto truncated;
211
212                 left -= len;
213                 cur += len;
214         }
215         if (rule->mbr_mode & MBI_EXEC) {
216                 len = snprintf(cur, left, "x");
217                 if (len < 0 || len > left)
218                         goto truncated;
219
220                 left -= len;
221                 cur += len;
222         }
223         if (!anymode) {
224                 len = snprintf(cur, left, "n");
225                 if (len < 0 || len > left)
226                         goto truncated;
227
228                 left -= len;
229                 cur += len;
230         }
231         if (unknownmode) {
232                 len = snprintf(cur, left, "?");
233                 if (len < 0 || len > left)
234                         goto truncated;
235
236                 left -= len;
237                 cur += len;
238         }
239
240         return (0);
241
242 truncated:
243         return (-1);
244 }
245
246 int
247 bsde_parse_identity(int argc, char *argv[],
248     struct mac_bsdextended_identity *identity, size_t buflen, char *errstr)
249 {
250         struct group *grp;
251         struct passwd *pwd;
252         int uid_seen, gid_seen, not_seen;
253         int current;
254         char *endp;
255         long value;
256         uid_t uid;
257         gid_t gid;
258         size_t len;
259
260         if (argc == 0) {
261                 len = snprintf(errstr, buflen, "Identity must not be empty");
262                 return (-1);
263         }
264
265         current = 0;
266
267         /* First element might be "not". */
268         if (strcmp("not", argv[0]) == 0) {
269                 not_seen = 1;
270                 current++;
271         } else
272                 not_seen = 0;
273
274         if (current >= argc) {
275                 len = snprintf(errstr, buflen, "Identity short");
276                 return (-1);
277         }
278
279         uid_seen = 0;
280         uid = 0;
281         gid_seen = 0;
282         gid = 0;
283
284         /* First phrase: uid [uid] or gid [gid]. */
285         if (strcmp("uid", argv[current]) == 0) {
286                 if (current + 2 > argc) {
287                         len = snprintf(errstr, buflen, "uid short");
288                         return (-1);
289                 }
290                 pwd = getpwnam(argv[current+1]);
291                 if (pwd != NULL)
292                         uid = pwd->pw_uid;
293                 else {
294                         value = strtol(argv[current+1], &endp, 10);
295                         if (*endp != '\0') {
296                                 len = snprintf(errstr, buflen,
297                                     "invalid uid: '%s'",
298                                     argv[current+1]);
299                                 return (-1);
300                         }
301                         uid = value;
302                 }
303                 uid_seen = 1;
304                 current += 2;
305         } else if (strcmp("gid", argv[current]) == 0) {
306                 if (current + 2 > argc) {
307                         len = snprintf(errstr, buflen, "gid short");
308                         return (-1);
309                 }
310                 grp = getgrnam(argv[current+1]);
311                 if (grp != NULL)
312                         gid = grp->gr_gid;
313                 else {
314                         value = strtol(argv[current+1], &endp, 10);
315                         if (*endp != '\0') {
316                                 len = snprintf(errstr, buflen,
317                                     "invalid gid: '%s'",
318                                     argv[current+1]);
319                                 return (-1);
320                         }
321                         gid = value;
322                 }
323                 gid_seen = 1;
324                 current += 2;
325         } else {
326                 len = snprintf(errstr, buflen, "'%s' not expected",
327                     argv[current]);
328                 return (-1);
329         }
330
331         /* Onto optional second phrase. */
332         if (current + 1 < argc) {
333                 /* Second phrase: uid [uid] or gid [gid], but not a repeat. */
334                 if (strcmp("uid", argv[current]) == 0) {
335                         if (uid_seen) {
336                                 len = snprintf(errstr, buflen,
337                                     "Only one uid permitted per identity clause");
338                                 return (-1);
339                         }
340                         if (current + 2 > argc) {
341                                 len = snprintf(errstr, buflen, "uid short");
342                                 return (-1);
343                         }
344                         pwd = getpwnam(argv[current+1]);
345                         if (pwd != NULL)
346                                 uid = pwd->pw_uid;
347                         else {
348                                 value = strtol(argv[current+1], &endp, 10);
349                                 if (*endp != '\0') {
350                                         len = snprintf(errstr, buflen,
351                                             "invalid uid: '%s'",
352                                             argv[current+1]);
353                                         return (-1);
354                                 }
355                                 uid = value;
356                         }
357                         uid_seen = 1;
358                         current += 2;
359                 } else if (strcmp("gid", argv[current]) == 0) {
360                         if (gid_seen) {
361                                 len = snprintf(errstr, buflen,
362                                     "Only one gid permitted per identity clause");
363                                 return (-1);
364                         }
365                         if (current + 2 > argc) {
366                                 len = snprintf(errstr, buflen, "gid short");
367                                 return (-1);
368                         }
369                         grp = getgrnam(argv[current+1]);
370                         if (grp != NULL)
371                                 gid = grp->gr_gid;
372                         else {
373                                 value = strtol(argv[current+1], &endp, 10);
374                                 if (*endp != '\0') {
375                                         len = snprintf(errstr, buflen,
376                                             "invalid gid: '%s'",
377                                             argv[current+1]);
378                                         return (-1);
379                                 }
380                                 gid = value;
381                         }
382                         gid_seen = 1;
383                         current += 2;
384                 } else {
385                         len = snprintf(errstr, buflen, "'%s' not expected",
386                             argv[current]);
387                         return (-1);
388                 } 
389         }
390
391         if (current +1 < argc) {
392                 len = snprintf(errstr, buflen, "'%s' not expected",
393                     argv[current]);
394                 return (-1);
395         }
396
397         /* Fill out the identity. */
398         identity->mbi_flags = 0;
399
400         if (not_seen)
401                 identity->mbi_flags |= MBI_NEGATED;
402
403         if (uid_seen) {
404                 identity->mbi_flags |= MBI_UID_DEFINED;
405                 identity->mbi_uid = uid;
406         } else
407                 identity->mbi_uid = 0;
408
409         if (gid_seen) {
410                 identity->mbi_flags |= MBI_GID_DEFINED;
411                 identity->mbi_gid = gid;
412         } else
413                 identity->mbi_gid = 0;
414
415         return (0);
416 }
417
418 int
419 bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen,
420     char *errstr)
421 {
422         size_t len;
423         int i;
424
425         if (argc == 0) {
426                 len = snprintf(errstr, buflen, "mode expects mode value");
427                 return (-1);
428         }
429
430         if (argc != 1) {
431                 len = snprintf(errstr, buflen, "'%s' unexpected", argv[1]);
432                 return (-1);
433         }
434
435         *mode = 0;
436         for (i = 0; i < strlen(argv[0]); i++) {
437                 switch (argv[0][i]) {
438                 case 'a':
439                         *mode |= MBI_ADMIN;
440                         break;
441                 case 'r':
442                         *mode |= MBI_READ;
443                         break;
444                 case 's':
445                         *mode |= MBI_STAT;
446                         break;
447                 case 'w':
448                         *mode |= MBI_WRITE;
449                         break;
450                 case 'x':
451                         *mode |= MBI_EXEC;
452                         break;
453                 case 'n':
454                         /* ignore */
455                         break;
456                 default:
457                         len = snprintf(errstr, buflen, "Unknown mode letter: %c",
458                             argv[0][i]);
459                         return (-1);
460                 } 
461         }
462
463         return (0);
464 }
465
466 int
467 bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule,
468     size_t buflen, char *errstr)
469 {
470         int subject, subject_elements, subject_elements_length;
471         int object, object_elements, object_elements_length;
472         int mode, mode_elements, mode_elements_length;
473         int error, i;
474         size_t len;
475
476         bzero(rule, sizeof(*rule));
477
478         if (argc < 1) {
479                 len = snprintf(errstr, buflen, "Rule must begin with subject");
480                 return (-1);
481         }
482
483         if (strcmp(argv[0], "subject") != 0) {
484                 len = snprintf(errstr, buflen, "Rule must begin with subject");
485                 return (-1);
486         }
487         subject = 0;
488         subject_elements = 1;
489
490         /* Search forward for object. */
491
492         object = -1;
493         for (i = 1; i < argc; i++)
494                 if (strcmp(argv[i], "object") == 0)
495                         object = i;
496
497         if (object == -1) {
498                 len = snprintf(errstr, buflen, "Rule must contain an object");
499                 return (-1);
500         }
501
502         /* Search forward for mode. */
503         mode = -1;
504         for (i = object; i < argc; i++)
505                 if (strcmp(argv[i], "mode") == 0)
506                         mode = i;
507
508         if (mode == -1) {
509                 len = snprintf(errstr, buflen, "Rule must contain mode");
510                 return (-1);
511         }
512
513         subject_elements_length = object - subject - 1;
514         object_elements = object + 1;
515         object_elements_length = mode - object_elements;
516         mode_elements = mode + 1;
517         mode_elements_length = argc - mode_elements;
518
519         error = bsde_parse_identity(subject_elements_length,
520             argv + subject_elements, &rule->mbr_subject, buflen, errstr);
521         if (error)
522                 return (-1);
523
524         error = bsde_parse_identity(object_elements_length,
525             argv + object_elements, &rule->mbr_object, buflen, errstr);
526         if (error)
527                 return (-1);
528
529         error = bsde_parse_mode(mode_elements_length, argv + mode_elements,
530             &rule->mbr_mode, buflen, errstr);
531         if (error)
532                 return (-1);
533
534         return (0);
535 }
536
537 int
538 bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule,
539     size_t buflen, char *errstr)
540 {
541         char *stringdup, *stringp, *argv[20], **ap;
542         int argc, error;
543
544         stringp = stringdup = strdup(string);
545         while (*stringp == ' ' || *stringp == '\t')
546                 stringp++;
547
548         argc = 0;
549         for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) {
550                 argc++;
551                 if (**ap != '\0')
552                         if (++ap >= &argv[20])
553                                 break;
554         }
555
556         error = bsde_parse_rule(argc, argv, rule, buflen, errstr);
557
558         free(stringdup);
559
560         return (error);
561 }
562
563 int
564 bsde_get_mib(const char *string, int *name, size_t *namelen)
565 {
566         size_t len;
567         int error;
568
569         len = *namelen;
570         error = sysctlnametomib(string, name, &len);
571         if (error)
572                 return (error);
573
574         *namelen = len;
575         return (0);
576 }
577
578 int
579 bsde_get_rule_count(size_t buflen, char *errstr)
580 {
581         size_t len;
582         int error;
583         int rule_count;
584
585         len = sizeof(rule_count);
586         error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0);
587         if (error) {
588                 len = snprintf(errstr, buflen, strerror(errno));
589                 return (-1);
590         }
591         if (len != sizeof(rule_count)) {
592                 len = snprintf(errstr, buflen, "Data error in %s.rule_count",
593                     MIB);
594                 return (-1);
595         }
596
597         return (rule_count);
598 }
599
600 int
601 bsde_get_rule_slots(size_t buflen, char *errstr)
602 {
603         size_t len;
604         int error;
605         int rule_slots;
606
607         len = sizeof(rule_slots);
608         error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0);
609         if (error) {
610                 len = snprintf(errstr, buflen, strerror(errno));
611                 return (-1);
612         }
613         if (len != sizeof(rule_slots)) {
614                 len = snprintf(errstr, buflen, "Data error in %s.rule_slots",
615                     MIB);
616                 return (-1);
617         }
618
619         return (rule_slots);
620 }
621
622 /*
623  * Returns 0 for success;
624  * Returns -1 for failure;
625  * Returns -2 for not present
626  */
627 int
628 bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen,
629     char *errstr)
630 {
631         int name[10];
632         size_t len, size;
633         int error;
634
635         len = 10;
636         error = bsde_get_mib(MIB ".rules", name, &len);
637         if (error) {
638                 len = snprintf(errstr, errlen, "%s: %s", MIB ".rules",
639                     strerror(errno));
640                 return (-1);
641         }
642
643         size = sizeof(*rule);
644         name[len] = rulenum;
645         len++;
646         error = sysctl(name, len, rule, &size, NULL, 0);
647         if (error  == -1 && errno == ENOENT)
648                 return (-2);
649         if (error) {
650                 len = snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules",
651                     rulenum, strerror(errno));
652                 return (-1);
653         } else if (size != sizeof(*rule)) {
654                 len = snprintf(errstr, errlen, "Data error in %s.%d: %s",
655                     MIB ".rules", rulenum, strerror(errno));
656                 return (-1);
657         }
658
659         return (0);
660 }
661
662 int
663 bsde_delete_rule(int rulenum, size_t buflen, char *errstr)
664 {
665         struct mac_bsdextended_rule rule;
666         int name[10];
667         size_t len, size;
668         int error;
669
670         len = 10;
671         error = bsde_get_mib(MIB ".rules", name, &len);
672         if (error) {
673                 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
674                     strerror(errno));
675                 return (-1);
676         }
677
678         name[len] = rulenum;
679         len++;
680
681         size = sizeof(rule);
682         error = sysctl(name, len, NULL, NULL, &rule, 0);
683         if (error) {
684                 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
685                     rulenum, strerror(errno));
686                 return (-1);
687         }
688
689         return (0);
690 }
691
692 int
693 bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
694     char *errstr)
695 {
696         int name[10];
697         size_t len, size;
698         int error;
699
700         len = 10;
701         error = bsde_get_mib(MIB ".rules", name, &len);
702         if (error) {
703                 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
704                     strerror(errno));
705                 return (-1);
706         }
707
708         name[len] = rulenum;
709         len++;
710
711         size = sizeof(*rule);
712         error = sysctl(name, len, NULL, NULL, rule, size);
713         if (error) {
714                 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
715                     rulenum, strerror(errno));
716                 return (-1);
717         }
718
719         return (0);
720 }
721
722 int
723 bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
724     char *errstr)
725 {
726         char charstr[BUFSIZ];
727         int name[10];
728         size_t len, size;
729         int error, rule_slots;
730
731         len = 10;
732         error = bsde_get_mib(MIB ".rules", name, &len);
733         if (error) {
734                 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
735                     strerror(errno));
736                 return (-1);
737         }
738
739         rule_slots = bsde_get_rule_slots(BUFSIZ, charstr);
740         if (rule_slots == -1) {
741                 len = snprintf(errstr, buflen, "unable to get rule slots: %s",
742                     strerror(errno));
743                 return (-1);
744         }
745
746         name[len] = rule_slots;
747         len++;
748
749         size = sizeof(*rule);
750         error = sysctl(name, len, NULL, NULL, rule, size);
751         if (error) {
752                 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
753                     rule_slots, strerror(errno));
754                 return (-1);
755         }
756
757         if (rulenum != NULL)
758                 *rulenum = rule_slots;
759
760         return (0);
761 }