pax_global_header00006660000000000000000000000064147621173720014524gustar00rootroot0000000000000052 comment=8e9157bbeea1899b7b8b257e7eaa71efef3fffed checkpolicy-3.8.1/000077500000000000000000000000001476211737200140325ustar00rootroot00000000000000checkpolicy-3.8.1/.gitignore000066400000000000000000000001261476211737200160210ustar00rootroot00000000000000checkmodule checkpolicy lex.yy.c y.tab.c y.tab.h tests/testpol.bin tests/testpol.conf checkpolicy-3.8.1/LICENSE000066400000000000000000000431311476211737200150410ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. checkpolicy-3.8.1/Makefile000066400000000000000000000042371476211737200155000ustar00rootroot00000000000000# # Makefile for building the checkpolicy program # LINGUAS ?= PREFIX ?= /usr BINDIR ?= $(PREFIX)/bin MANDIR ?= $(PREFIX)/share/man TARGETS = checkpolicy checkmodule LEX = flex YACC = bison -y CFLAGS ?= -g -Wall -Werror -Wshadow -O2 -fno-strict-aliasing # If no specific libsepol.a is specified, fall back on LDFLAGS search path # Otherwise, as $(LIBSEPOLA) already appears in the dependencies, there # is no need to define a value for LDLIBS_LIBSEPOLA ifeq ($(LIBSEPOLA),) LDLIBS_LIBSEPOLA := -l:libsepol.a endif CHECKOBJS = y.tab.o lex.yy.o queue.o module_compiler.o parse_util.o \ policy_define.o CHECKPOLOBJS = $(CHECKOBJS) checkpolicy.o CHECKMODOBJS = $(CHECKOBJS) checkmodule.o GENERATED=lex.yy.c y.tab.c y.tab.h all: $(TARGETS) $(MAKE) -C test checkpolicy: $(CHECKPOLOBJS) $(LIBSEPOLA) $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LDLIBS_LIBSEPOLA) checkmodule: $(CHECKMODOBJS) $(LIBSEPOLA) $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LDLIBS_LIBSEPOLA) %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $< y.tab.o: y.tab.c $(CC) $(filter-out -Werror, $(CPPFLAGS) $(CFLAGS)) -o $@ -c $< lex.yy.o: lex.yy.c $(CC) $(filter-out -Werror, $(CPPFLAGS) $(CFLAGS)) -o $@ -c $< y.tab.c: policy_parse.y $(YACC) -d policy_parse.y lex.yy.c: policy_scan.l y.tab.c $(LEX) policy_scan.l .PHONY: test test: checkpolicy ./tests/test_roundtrip.sh # helper target for fuzzing checkobjects: $(CHECKOBJS) install: all -mkdir -p $(DESTDIR)$(BINDIR) -mkdir -p $(DESTDIR)$(MANDIR)/man8 install -m 755 $(TARGETS) $(DESTDIR)$(BINDIR) install -m 644 checkpolicy.8 $(DESTDIR)$(MANDIR)/man8 install -m 644 checkmodule.8 $(DESTDIR)$(MANDIR)/man8 for lang in $(LINGUAS) ; do \ if [ -e $${lang} ] ; then \ mkdir -p $(DESTDIR)$(MANDIR)/$${lang}/man8 ; \ install -m 644 $${lang}/*.8 $(DESTDIR)$(MANDIR)/$${lang}/man8 ; \ fi ; \ done relabel: install /sbin/restorecon $(DESTDIR)$(BINDIR)/checkpolicy /sbin/restorecon $(DESTDIR)$(BINDIR)/checkmodule clean: -rm -f $(TARGETS) $(CHECKPOLOBJS) $(CHECKMODOBJS) y.tab.c y.tab.h lex.yy.c tests/testpol.conf tests/testpol.bin $(MAKE) -C test clean indent: ../scripts/Lindent $(filter-out $(GENERATED),$(wildcard *.[ch])) checkpolicy-3.8.1/VERSION000066400000000000000000000000061476211737200150760ustar00rootroot000000000000003.8.1 checkpolicy-3.8.1/checkmodule.8000066400000000000000000000044321476211737200164110ustar00rootroot00000000000000.TH CHECKMODULE 8 .SH NAME checkmodule \- SELinux policy module compiler .SH SYNOPSIS .B checkmodule .I "[\-h] [\-b] [\-c policy_version] [\-C] [\-E] [\-m] [\-M] [\-N] [\-U handle_unknown] [\-V] [\-o output_file] [input_file]" .SH "DESCRIPTION" This manual page describes the .BR checkmodule command. .PP .B checkmodule is a program that checks and compiles a SELinux security policy module into a binary representation. It can generate either a base policy module (default) or a non-base policy module (\-m option); typically, you would build a non-base policy module to add to an existing module store that already has a base module provided by the base policy. Use .B semodule_package(8) to combine this module with its optional file contexts to create a policy package, and then use .B semodule(8) to install the module package into the module store and load the resulting policy. .SH OPTIONS .TP .B \-b,\-\-binary Read an existing binary policy module file rather than a source policy module file. This option is a development/debugging aid. .TP .B \-C,\-\-cil Write CIL policy file rather than binary policy file. .TP .B \-E,\-\-werror Treat warnings as errors .TP .B \-h,\-\-help Print usage. .TP .B \-m Generate a non-base policy module. .TP .B \-M,\-\-mls Enable the MLS/MCS support when checking and compiling the policy module. .TP .B \-N,\-\-disable-neverallow Do not check neverallow rules. .TP .B \-V,\-\-version Show policy versions created by this program. .TP .B \-o,\-\-output filename Write a binary policy module file to the specified filename. Otherwise, checkmodule will only check the syntax of the module source file and will not generate a binary module at all. .TP .B \-U,\-\-handle-unknown Specify how the kernel should handle unknown classes or permissions (deny, allow or reject). .TP .B \-c policyvers Specify the policy version, defaults to the latest. .SH EXAMPLE .nf # Build a MLS/MCS-enabled non-base policy module. $ checkmodule \-M \-m httpd.te \-o httpd.mod .fi .SH "SEE ALSO" .B semodule(8), semodule_package(8) SELinux Reference Policy documentation at https://github.com/SELinuxProject/refpolicy/wiki .SH AUTHOR This manual page was copied from the checkpolicy man page written by Árpád Magosányi , and edited by Dan Walsh . checkpolicy-3.8.1/checkmodule.c000066400000000000000000000223761476211737200164730ustar00rootroot00000000000000/* * Authors: Joshua Brindle * Karl MacMillan * Jason Tang * * * Copyright (C) 2004-5 Tresys Technology, LLC * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "queue.h" #include "parse_util.h" static sidtab_t sidtab; extern int mlspol; extern int werror; static int handle_unknown = SEPOL_DENY_UNKNOWN; static const char *txtfile = "policy.conf"; static const char *binfile = "policy"; static int read_binary_policy(policydb_t * p, const char *file, const char *progname) { int fd; struct stat sb; void *map; struct policy_file f, *fp; fd = open(file, O_RDONLY); if (fd < 0) { fprintf(stderr, "Can't open '%s': %s\n", file, strerror(errno)); return -1; } if (fstat(fd, &sb) < 0) { fprintf(stderr, "Can't stat '%s': %s\n", file, strerror(errno)); close(fd); return -1; } map = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); close(fd); if (map == MAP_FAILED) { fprintf(stderr, "Can't map '%s': %s\n", file, strerror(errno)); return -1; } policy_file_init(&f); f.type = PF_USE_MEMORY; f.data = map; f.len = sb.st_size; fp = &f; if (policydb_init(p)) { fprintf(stderr, "%s: policydb_init: Out of memory!\n", progname); return -1; } if (policydb_read(p, fp, 1)) { fprintf(stderr, "%s: error(s) encountered while parsing configuration\n", progname); return -1; } /* Check Policy Consistency */ if (p->mls) { if (!mlspol) { fprintf(stderr, "%s: MLS policy, but non-MLS" " is specified\n", progname); return -1; } } else { if (mlspol) { fprintf(stderr, "%s: non-MLS policy, but MLS" " is specified\n", progname); return -1; } } return 0; } static int write_binary_policy(policydb_t * p, FILE *outfp, unsigned int policy_type, unsigned int policyvers) { struct policy_file pf; p->policy_type = policy_type; p->policyvers = policyvers; p->handle_unknown = handle_unknown; policy_file_init(&pf); pf.type = PF_USE_STDIO; pf.fp = outfp; return policydb_write(p, &pf); } static __attribute__((__noreturn__)) void usage(const char *progname) { printf("usage: %s [-h] [-V] [-b] [-C] [-E] [-U handle_unknown] [-m] [-M] [-N] [-o FILE] [-c VERSION] [INPUT]\n", progname); printf("Build base and policy modules.\n"); printf("Options:\n"); printf(" INPUT build module from INPUT (else read from \"%s\")\n", txtfile); printf(" -V show policy versions created by this program\n"); printf(" -b treat input as a binary policy file\n"); printf(" -C output CIL policy instead of binary policy\n"); printf(" -E treat warnings as errors\n"); printf(" -h print usage\n"); printf(" -U OPTION How to handle unknown classes and permissions\n"); printf(" deny: Deny unknown kernel checks\n"); printf(" reject: Reject loading of policy with unknowns\n"); printf(" allow: Allow unknown kernel checks\n"); printf(" -m build a policy module instead of a base module\n"); printf(" -M enable MLS policy\n"); printf(" -N do not check neverallow rules\n"); printf(" -o FILE write module to FILE (else just check syntax)\n"); printf(" -c VERSION build a policy module targeting a modular policy version (%d-%d)\n", MOD_POLICYDB_VERSION_MIN, MOD_POLICYDB_VERSION_MAX); exit(1); } int main(int argc, char **argv) { const char *file = txtfile, *outfile = NULL; unsigned int binary = 0, cil = 0, disable_neverallow = 0; unsigned int policy_type = POLICY_BASE; unsigned int policyvers = MOD_POLICYDB_VERSION_MAX; int ch; int show_version = 0; policydb_t modpolicydb; const struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"output", required_argument, NULL, 'o'}, {"binary", no_argument, NULL, 'b'}, {"version", no_argument, NULL, 'V'}, {"handle-unknown", required_argument, NULL, 'U'}, {"mls", no_argument, NULL, 'M'}, {"disable-neverallow", no_argument, NULL, 'N'}, {"cil", no_argument, NULL, 'C'}, {"werror", no_argument, NULL, 'E'}, {NULL, 0, NULL, 0} }; while ((ch = getopt_long(argc, argv, "ho:bVEU:mMNCc:", long_options, NULL)) != -1) { switch (ch) { case 'h': usage(argv[0]); break; case 'o': outfile = optarg; break; case 'b': binary = 1; file = binfile; break; case 'V': show_version = 1; break; case 'E': werror = 1; break; case 'U': if (!strcasecmp(optarg, "deny")) { handle_unknown = DENY_UNKNOWN; break; } if (!strcasecmp(optarg, "reject")) { handle_unknown = REJECT_UNKNOWN; break; } if (!strcasecmp(optarg, "allow")) { handle_unknown = ALLOW_UNKNOWN; break; } usage(argv[0]); case 'm': policy_type = POLICY_MOD; break; case 'M': mlspol = 1; break; case 'N': disable_neverallow = 1; break; case 'C': cil = 1; break; case 'c': { long int n; errno = 0; n = strtol(optarg, NULL, 10); if (errno) { fprintf(stderr, "Invalid policyvers specified: %s\n", optarg); usage(argv[0]); } if (n < MOD_POLICYDB_VERSION_MIN || n > MOD_POLICYDB_VERSION_MAX) { fprintf(stderr, "policyvers value %ld not in range %d-%d\n", n, MOD_POLICYDB_VERSION_MIN, MOD_POLICYDB_VERSION_MAX); usage(argv[0]); } policyvers = n; break; } default: usage(argv[0]); } } if (show_version) { printf("Module versions %d-%d\n", MOD_POLICYDB_VERSION_MIN, MOD_POLICYDB_VERSION_MAX); exit(0); } if (handle_unknown && (policy_type != POLICY_BASE)) { fprintf(stderr, "%s: Handling of unknown classes and permissions is only valid in the base module.\n", argv[0]); exit(1); } if (binary && (policy_type != POLICY_BASE)) { fprintf(stderr, "%s: -b and -m are incompatible with each other.\n", argv[0]); exit(1); } if (optind != argc) { file = argv[optind++]; if (optind != argc) usage(argv[0]); } /* Set policydb and sidtab used by libsepol service functions to my structures, so that I can directly populate and manipulate them. */ sepol_set_policydb(&modpolicydb); sepol_set_sidtab(&sidtab); if (binary) { if (read_binary_policy(&modpolicydb, file, argv[0]) == -1) { exit(1); } } else { if (policydb_init(&modpolicydb)) { fprintf(stderr, "%s: out of memory!\n", argv[0]); exit(1); } modpolicydb.policy_type = policy_type; modpolicydb.mls = mlspol; modpolicydb.handle_unknown = handle_unknown; modpolicydb.policyvers = policyvers; if (read_source_policy(&modpolicydb, file, argv[0]) == -1) { exit(1); } if (hierarchy_check_constraints(NULL, &modpolicydb)) { exit(1); } } if (policy_type != POLICY_BASE && outfile) { char *out_name; char *separator; char *mod_name = modpolicydb.name; char *out_path = strdup(outfile); if (out_path == NULL) { fprintf(stderr, "%s: out of memory\n", argv[0]); exit(1); } out_name = basename(out_path); separator = strrchr(out_name, '.'); if (separator) { *separator = '\0'; } if (strcmp(mod_name, out_name) != 0) { fprintf(stderr, "%s: Module name %s is different than the output base filename %s\n", argv[0], mod_name, out_name); exit(1); } free(out_path); } if (modpolicydb.policy_type == POLICY_BASE && !cil) { /* Verify that we can successfully expand the base module. */ policydb_t kernpolicydb; if (policydb_init(&kernpolicydb)) { fprintf(stderr, "%s: policydb_init failed\n", argv[0]); exit(1); } if (link_modules(NULL, &modpolicydb, NULL, 0, 0)) { fprintf(stderr, "%s: link modules failed\n", argv[0]); exit(1); } if (expand_module(NULL, &modpolicydb, &kernpolicydb, /*verbose=*/0, !disable_neverallow)) { fprintf(stderr, "%s: expand module failed\n", argv[0]); exit(1); } policydb_destroy(&kernpolicydb); } if (policydb_load_isids(&modpolicydb, &sidtab)) exit(1); sepol_sidtab_destroy(&sidtab); if (outfile) { FILE *outfp = fopen(outfile, "w"); if (!outfp) { fprintf(stderr, "%s: error opening %s: %s\n", argv[0], outfile, strerror(errno)); exit(1); } if (!cil) { if (write_binary_policy(&modpolicydb, outfp, policy_type, policyvers) != 0) { fprintf(stderr, "%s: error writing %s\n", argv[0], outfile); exit(1); } } else { if (sepol_module_policydb_to_cil(outfp, &modpolicydb, 0) != 0) { fprintf(stderr, "%s: error writing %s\n", argv[0], outfile); exit(1); } } if (fclose(outfp)) { fprintf(stderr, "%s: error closing %s: %s\n", argv[0], outfile, strerror(errno)); exit(1); } } else if (cil) { fprintf(stderr, "%s: No file to write CIL was specified\n", argv[0]); exit(1); } policydb_destroy(&modpolicydb); return 0; } /* FLASK */ checkpolicy-3.8.1/checkpolicy.8000066400000000000000000000055321476211737200164250ustar00rootroot00000000000000.TH CHECKPOLICY 8 .SH NAME checkpolicy \- SELinux policy compiler .SH SYNOPSIS .B checkpolicy .I "[\-b[F]] [\-C] [\-d] [\-U handle_unknown (allow,deny,reject)] [\-M] [\-N] [\-c policyvers] [\-o output_file|\-] [\-S] [\-t target_platform (selinux,xen)] [\-O] [\-E] [\-V] [input_file]" .br .SH "DESCRIPTION" This manual page describes the .BR checkpolicy command. .PP .B checkpolicy is a program that checks and compiles a SELinux security policy configuration into a binary representation that can be loaded into the kernel. If no input file name is specified, .B checkpolicy will attempt to read from policy.conf or policy, depending on whether the \-b flag is specified. .SH OPTIONS .TP .B \-b,\-\-binary Read an existing binary policy file rather than a source policy.conf file. .TP .B \-F,\-\-conf Write policy.conf file rather than binary policy file. Can only be used with binary policy file. .TP .B \-C,\-\-cil Write CIL policy file rather than binary policy file. .TP .B \-d,\-\-debug Enter debug mode after loading the policy. .TP .B \-U,\-\-handle-unknown Specify how the kernel should handle unknown classes or permissions (deny, allow or reject). .TP .B \-M,\-\-mls Enable the MLS policy when checking and compiling the policy. .TP .B \-N,\-\-disable-neverallow Do not check neverallow rules. .TP .B \-c policyvers Specify the policy version, defaults to the latest. .TP .B \-o,\-\-output filename Write a policy file (binary, policy.conf, or CIL policy) to the specified filename. If - is given as filename, write it to standard output. .TP .B \-S,\-\-sort Sort ocontexts before writing out the binary policy. This option makes output of checkpolicy consistent with binary policies created by semanage and secilc. .TP .B \-t,\-\-target Specify the target platform (selinux or xen). .TP .B \-O,\-\-optimize Optimize the final kernel policy (remove redundant rules). .TP .B \-E,\-\-werror Treat warnings as errors .TP .B \-V,\-\-version Show version information. .TP .B \-h,\-\-help Show usage information. .SH EXAMPLE .nf Generate policy.conf based on the system policy # checkpolicy -b -M -F /etc/selinux/targeted/policy/policy.33 -o policy.conf Recompile system policy so that unknown permissions are denied (uses policy.conf from ^^). Note that binary policy extension represents its version, which is subject to change # checkpolicy -M -U deny -o /etc/selinux/targeted/policy/policy.33 policy.conf # load_policy Generate CIL representation of current system policy # checkpolicy -b -M -C /etc/selinux/targeted/policy/policy.33 -o policy.out .SH "SEE ALSO" SELinux Reference Policy documentation at https://github.com/SELinuxProject/refpolicy/wiki .SH AUTHOR This manual page was written by Árpád Magosányi , and edited by Stephen Smalley . The program was written by Stephen Smalley . checkpolicy-3.8.1/checkpolicy.c000066400000000000000000000746171476211737200165120ustar00rootroot00000000000000 /* * Author : Stephen Smalley, */ /* * Updated: Trusted Computer Solutions, Inc. * * Support for enhanced MLS infrastructure. * * Updated: Karl MacMillan * * Added conditional policy language extensions * * Updated: James Morris * * Added IPv6 support. * * Updated: Joshua Brindle * Karl MacMillan * Jason Tang * * Policy Module support. * * Copyright (C) 2017 Mellanox Technologies Inc. * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * Copyright (C) 2003 - 2005 Tresys Technology, LLC * Copyright (C) 2003 Red Hat, Inc., James Morris * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ /* FLASK */ /* * checkpolicy * * Load and check a policy configuration. * * A policy configuration is created in a text format, * and then compiled into a binary format for use by * the security server. By default, checkpolicy reads * the text format. If '-b' is specified, then checkpolicy * reads the binary format instead. * * If '-o output_file' is specified, then checkpolicy * writes the binary format version of the configuration * to the specified output file. * * If '-d' is specified, then checkpolicy permits the user * to interactively test the security server functions with * the loaded policy configuration. * * If '-c' is specified, then the supplied parameter is used to * determine which policy version to use for generating binary * policy. This is for compatibility with older kernels. If any * booleans or conditional rules are thrown away a warning is printed. */ #include #include #include #include #include #include #include #include #ifndef IPPROTO_DCCP #define IPPROTO_DCCP 33 #endif #ifndef IPPROTO_SCTP #define IPPROTO_SCTP 132 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "queue.h" #include "parse_util.h" static policydb_t policydb; static sidtab_t sidtab; extern policydb_t *policydbp; extern int mlspol; extern int werror; static int handle_unknown = SEPOL_DENY_UNKNOWN; static const char *txtfile = "policy.conf"; static const char *binfile = "policy"; static __attribute__((__noreturn__)) void usage(const char *progname) { printf ("usage: %s [-b[F]] [-C] [-d] [-U handle_unknown (allow,deny,reject)] [-M] " "[-N] [-c policyvers (%d-%d)] [-o output_file|-] [-S] [-O] " "[-t target_platform (selinux,xen)] [-E] [-V] [input_file]\n", progname, POLICYDB_VERSION_MIN, POLICYDB_VERSION_MAX); exit(1); } #define FGETS(out, size, in) \ do { \ if (fgets(out,size,in)==NULL) { \ fprintf(stderr, "fgets failed at line %d: %s\n", __LINE__, \ strerror(errno)); \ exit(1);\ } \ } while (0) static int print_sid(sepol_security_id_t sid, context_struct_t * context __attribute__ ((unused)), void *data __attribute__ ((unused))) { sepol_security_context_t scontext; size_t scontext_len; int rc; rc = sepol_sid_to_context(sid, &scontext, &scontext_len); if (rc) printf("sid %d -> error %d\n", sid, rc); else { printf("sid %d -> scontext %s\n", sid, scontext); free(scontext); } return 0; } struct val_to_name { unsigned int val; char *name; }; static int find_perm(hashtab_key_t key, hashtab_datum_t datum, void *p) { struct val_to_name *v = p; perm_datum_t *perdatum; perdatum = (perm_datum_t *) datum; if (v->val == perdatum->s.value) { v->name = key; return 1; } return 0; } #ifdef EQUIVTYPES static int insert_type_rule(avtab_key_t * k, avtab_datum_t * d, struct avtab_node *type_rules) { struct avtab_node *p, *c, *n; for (p = type_rules, c = type_rules->next; c; p = c, c = c->next) { /* * Find the insertion point, keeping the list * ordered by source type, then target type, then * target class. */ if (k->source_type < c->key.source_type) break; if (k->source_type == c->key.source_type && k->target_type < c->key.target_type) break; if (k->source_type == c->key.source_type && k->target_type == c->key.target_type && k->target_class < c->key.target_class) break; } /* Insert the rule */ n = malloc(sizeof(struct avtab_node)); if (!n) { fprintf(stderr, "out of memory\n"); exit(1); } n->key = *k; n->datum = *d; n->next = p->next; p->next = n; return 0; } static int create_type_rules(avtab_key_t * k, avtab_datum_t * d, void *args) { struct avtab_node *type_rules = args; if (d->specified & AVTAB_ALLOWED) { /* * Insert the rule into the lists for both * the source type and the target type. */ if (insert_type_rule(k, d, &type_rules[k->source_type - 1])) return -1; if (insert_type_rule(k, d, &type_rules[k->target_type - 1])) return -1; } return 0; } static void free_type_rules(struct avtab_node *l) { struct avtab_node *tmp; while (l) { tmp = l; l = l->next; free(tmp); } } static int identify_equiv_types(void) { struct avtab_node *type_rules, *l1, *l2; int i, j; /* * Create a list of access vector rules for each type * from the access vector table. */ type_rules = malloc(sizeof(struct avtab_node) * policydb.p_types.nprim); if (!type_rules) { fprintf(stderr, "out of memory\n"); exit(1); } memset(type_rules, 0, sizeof(struct avtab_node) * policydb.p_types.nprim); if (avtab_map(&policydb.te_avtab, create_type_rules, type_rules)) exit(1); /* * Compare the type lists and identify equivalent types. */ for (i = 0; i < policydb.p_types.nprim - 1; i++) { if (!type_rules[i].next) continue; for (j = i + 1; j < policydb.p_types.nprim; j++) { for (l1 = type_rules[i].next, l2 = type_rules[j].next; l1 && l2; l1 = l1->next, l2 = l2->next) { if (l2->key.source_type == (j + 1)) { if (l1->key.source_type != (i + 1)) break; } else { if (l1->key.source_type != l2->key.source_type) break; } if (l2->key.target_type == (j + 1)) { if (l1->key.target_type != (i + 1)) break; } else { if (l1->key.target_type != l2->key.target_type) break; } if (l1->key.target_class != l2->key.target_class || l1->datum.allowed != l2->datum.allowed) break; } if (l1 || l2) continue; free_type_rules(type_rules[j].next); type_rules[j].next = NULL; printf("Types %s and %s are equivalent.\n", policydb.p_type_val_to_name[i], policydb.p_type_val_to_name[j]); } free_type_rules(type_rules[i].next); type_rules[i].next = NULL; } free(type_rules); return 0; } #endif static int display_bools(void) { uint32_t i; for (i = 0; i < policydbp->p_bools.nprim; i++) { printf("%s : %d\n", policydbp->p_bool_val_to_name[i], policydbp->bool_val_to_struct[i]->state); } return 0; } static void display_expr(const cond_expr_t * exp) { const cond_expr_t *cur; for (cur = exp; cur != NULL; cur = cur->next) { switch (cur->expr_type) { case COND_BOOL: printf("%s ", policydbp->p_bool_val_to_name[cur->boolean - 1]); break; case COND_NOT: printf("! "); break; case COND_OR: printf("|| "); break; case COND_AND: printf("&& "); break; case COND_XOR: printf("^ "); break; case COND_EQ: printf("== "); break; case COND_NEQ: printf("!= "); break; default: printf("error!"); break; } } } static int display_cond_expressions(void) { const cond_node_t *cur; for (cur = policydbp->cond_list; cur != NULL; cur = cur->next) { printf("expression: "); display_expr(cur->expr); printf("current state: %d\n", cur->cur_state); } return 0; } static int change_bool(const char *name, int state) { cond_bool_datum_t *boolean; boolean = hashtab_search(policydbp->p_bools.table, name); if (boolean == NULL) { printf("Could not find bool %s\n", name); return -1; } boolean->state = state; evaluate_conds(policydbp); return 0; } static int check_level(hashtab_key_t key, hashtab_datum_t datum, void *arg __attribute__ ((unused))) { level_datum_t *levdatum = (level_datum_t *) datum; if (!levdatum->isalias && levdatum->notdefined) { fprintf(stderr, "Error: sensitivity %s was not used in a level definition!\n", key); return -1; } return 0; } int main(int argc, char **argv) { policydb_t parse_policy; sepol_security_class_t tclass; sepol_security_id_t ssid, tsid, *sids, oldsid, newsid, tasksid; sepol_security_context_t scontext; struct sepol_av_decision avd; class_datum_t *cladatum; const char *file = txtfile; char ans[80 + 1], *path, *fstype; const char *outfile = NULL; size_t scontext_len, pathlen; unsigned int i; unsigned int protocol, port; unsigned int binary = 0, debug = 0, sort = 0, cil = 0, conf = 0, optimize = 0, disable_neverallow = 0; struct val_to_name v; int ret, ch, fd, target = SEPOL_TARGET_SELINUX; unsigned int policyvers = 0; unsigned int nel, uret; struct stat sb; void *map; FILE *outfp = NULL; char *name; int state; int show_version = 0; char *reason_buf = NULL; unsigned int reason; int flags; struct policy_file pf; const struct option long_options[] = { {"output", required_argument, NULL, 'o'}, {"target", required_argument, NULL, 't'}, {"binary", no_argument, NULL, 'b'}, {"debug", no_argument, NULL, 'd'}, {"version", no_argument, NULL, 'V'}, {"handle-unknown", required_argument, NULL, 'U'}, {"mls", no_argument, NULL, 'M'}, {"disable-neverallow", no_argument, NULL, 'N'}, {"cil", no_argument, NULL, 'C'}, {"conf",no_argument, NULL, 'F'}, {"sort", no_argument, NULL, 'S'}, {"optimize", no_argument, NULL, 'O'}, {"werror", no_argument, NULL, 'E'}, {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0} }; while ((ch = getopt_long(argc, argv, "o:t:dbU:MNCFSVc:OEh", long_options, NULL)) != -1) { switch (ch) { case 'o': outfile = optarg; break; case 't': if (!strcasecmp(optarg, "Xen")) target = SEPOL_TARGET_XEN; else if (!strcasecmp(optarg, "SELinux")) target = SEPOL_TARGET_SELINUX; else{ fprintf(stderr, "%s: Unknown target platform:" "%s\n", argv[0], optarg); exit(1); } break; case 'b': binary = 1; file = binfile; break; case 'd': debug = 1; break; case 'V': show_version = 1; break; case 'U': if (!strcasecmp(optarg, "deny")) { handle_unknown = DENY_UNKNOWN; break; } if (!strcasecmp(optarg, "allow")) { handle_unknown = ALLOW_UNKNOWN; break; } if (!strcasecmp(optarg, "reject")) { handle_unknown = REJECT_UNKNOWN; break; } usage(argv[0]); case 'S': sort = 1; break; case 'O': optimize = 1; break; case 'M': mlspol = 1; break; case 'N': disable_neverallow = 1; break; case 'C': cil = 1; break; case 'F': conf = 1; break; case 'c':{ long int n; errno = 0; n = strtol(optarg, NULL, 10); if (errno) { fprintf(stderr, "Invalid policyvers specified: %s\n", optarg); usage(argv[0]); exit(1); } if (n < POLICYDB_VERSION_MIN || n > POLICYDB_VERSION_MAX) { fprintf(stderr, "policyvers value %ld not in range %d-%d\n", n, POLICYDB_VERSION_MIN, POLICYDB_VERSION_MAX); usage(argv[0]); exit(1); } policyvers = n; break; } case 'E': werror = 1; break; case 'h': default: usage(argv[0]); } } if (show_version) { printf("%d (compatibility range %d-%d)\n", policyvers ? policyvers : POLICYDB_VERSION_MAX , POLICYDB_VERSION_MAX, POLICYDB_VERSION_MIN); exit(0); } if (optind != argc) { file = argv[optind++]; if (optind != argc) usage(argv[0]); } /* Set policydb and sidtab used by libsepol service functions to my structures, so that I can directly populate and manipulate them. */ sepol_set_policydb(&policydb); sepol_set_sidtab(&sidtab); if (cil && conf) { fprintf(stderr, "Can't convert to CIL and policy.conf at the same time\n"); exit(1); } if (binary) { fd = open(file, O_RDONLY); if (fd < 0) { fprintf(stderr, "Can't open '%s': %s\n", file, strerror(errno)); exit(1); } if (fstat(fd, &sb) < 0) { fprintf(stderr, "Can't stat '%s': %s\n", file, strerror(errno)); exit(1); } map = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (map == MAP_FAILED) { fprintf(stderr, "Can't map '%s': %s\n", file, strerror(errno)); exit(1); } policy_file_init(&pf); pf.type = PF_USE_MEMORY; pf.data = map; pf.len = sb.st_size; if (policydb_init(&policydb)) { fprintf(stderr, "%s: policydb_init: Out of memory!\n", argv[0]); exit(1); } ret = policydb_read(&policydb, &pf, 1); if (ret) { fprintf(stderr, "%s: error(s) encountered while parsing configuration\n", argv[0]); exit(1); } policydbp = &policydb; /* Check Policy Consistency */ if (policydbp->mls) { if (!mlspol) { fprintf(stderr, "%s: MLS policy, but non-MLS" " is specified\n", argv[0]); exit(1); } } else { if (mlspol) { fprintf(stderr, "%s: non-MLS policy, but MLS" " is specified\n", argv[0]); exit(1); } } if (policydbp->policyvers <= POLICYDB_VERSION_PERMISSIVE) { if (policyvers > policydbp->policyvers) { fprintf(stderr, "Binary policies with version <= %u cannot be upgraded\n", POLICYDB_VERSION_PERMISSIVE); } else if (policyvers) { policydbp->policyvers = policyvers; } } else { policydbp->policyvers = policyvers ? policyvers : POLICYDB_VERSION_MAX; } } else { if (conf) { fprintf(stderr, "Can only generate policy.conf from binary policy\n"); exit(1); } if (policydb_init(&parse_policy)) exit(1); /* We build this as a base policy first since that is all the parser understands */ parse_policy.policy_type = POLICY_BASE; policydb_set_target_platform(&parse_policy, target); /* Let sepol know if we are dealing with MLS support */ parse_policy.mls = mlspol; parse_policy.handle_unknown = handle_unknown; parse_policy.policyvers = policyvers ? policyvers : POLICYDB_VERSION_MAX; policydbp = &parse_policy; if (read_source_policy(policydbp, file, "checkpolicy") < 0) exit(1); if (hashtab_map(policydbp->p_levels.table, check_level, NULL)) exit(1); /* Linking takes care of optional avrule blocks */ if (link_modules(NULL, policydbp, NULL, 0, 0)) { fprintf(stderr, "Error while resolving optionals\n"); exit(1); } if (!cil) { if (policydb_init(&policydb)) { fprintf(stderr, "%s: policydb_init failed\n", argv[0]); exit(1); } if (expand_module(NULL, policydbp, &policydb, /*verbose=*/0, !disable_neverallow)) { fprintf(stderr, "Error while expanding policy\n"); exit(1); } policydb.policyvers = policyvers ? policyvers : POLICYDB_VERSION_MAX; policydb_destroy(policydbp); policydbp = &policydb; } } if (policydb_load_isids(&policydb, &sidtab)) exit(1); if (optimize && policydbp->policy_type == POLICY_KERN) { ret = policydb_optimize(policydbp); if (ret) { fprintf(stderr, "%s: error optimizing policy\n", argv[0]); exit(1); } } if (outfile) { if (!strcmp(outfile, "-")) { outfp = stdout; outfile = ""; } else { outfp = fopen(outfile, "w"); if (!outfp) { perror(outfile); exit(1); } } if (!cil) { if (!conf) { policydb.policy_type = POLICY_KERN; policy_file_init(&pf); pf.type = PF_USE_STDIO; pf.fp = outfp; if (sort) { ret = policydb_sort_ocontexts(&policydb); if (ret) { fprintf(stderr, "%s: error sorting ocontexts\n", argv[0]); exit(1); } } ret = policydb_write(&policydb, &pf); } else { ret = sepol_kernel_policydb_to_conf(outfp, policydbp); } if (ret) { fprintf(stderr, "%s: error writing %s\n", argv[0], outfile); exit(1); } } else { if (binary) { ret = sepol_kernel_policydb_to_cil(outfp, policydbp); } else { ret = sepol_module_policydb_to_cil(outfp, policydbp, 1); } if (ret) { fprintf(stderr, "%s: error writing %s\n", argv[0], outfile); exit(1); } } if (outfp != stdout) { if(fclose(outfp)) { fprintf(stderr, "%s: error closing %s: %s\n", argv[0], outfile, strerror(errno)); exit(1); } } } else if (cil) { fprintf(stderr, "%s: No file to write CIL was specified\n", argv[0]); exit(1); } if (!debug) { policydb_destroy(&policydb); sepol_sidtab_destroy(&sidtab); exit(0); } menu: printf("\nSelect an option:\n"); printf("0) Call compute_access_vector\n"); printf("1) Call sid_to_context\n"); printf("2) Call context_to_sid\n"); printf("3) Call transition_sid\n"); printf("4) Call member_sid\n"); printf("5) Call change_sid\n"); printf("6) Call list_sids\n"); printf("7) Call load_policy\n"); printf("8) Call fs_sid\n"); printf("9) Call port_sid\n"); printf("a) Call netif_sid\n"); printf("b) Call node_sid\n"); printf("c) Call fs_use\n"); printf("d) Call genfs_sid\n"); printf("e) Call get_user_sids\n"); printf("f) display conditional bools\n"); printf("g) display conditional expressions\n"); printf("h) change a boolean value\n"); printf("i) display constraint expressions\n"); printf("j) display validatetrans expressions\n"); printf("k) Call ibpkey_sid\n"); printf("l) Call ibendport_sid\n"); #ifdef EQUIVTYPES printf("z) Show equivalent types\n"); #endif printf("m) Show menu again\n"); printf("q) Exit\n"); while (1) { printf("\nChoose: "); FGETS(ans, sizeof(ans), stdin); switch (ans[0]) { case '0': printf("source sid? "); FGETS(ans, sizeof(ans), stdin); ssid = atoi(ans); printf("target sid? "); FGETS(ans, sizeof(ans), stdin); tsid = atoi(ans); printf("target class? "); FGETS(ans, sizeof(ans), stdin); if (isdigit(ans[0])) { tclass = atoi(ans); if (!tclass || tclass > policydb.p_classes.nprim) { printf("\nNo such class.\n"); break; } cladatum = policydb.class_val_to_struct[tclass - 1]; } else { ans[strlen(ans) - 1] = 0; cladatum = (class_datum_t *) hashtab_search(policydb. p_classes. table, ans); if (!cladatum) { printf("\nNo such class\n"); break; } tclass = cladatum->s.value; } if (!cladatum->comdatum && !cladatum->permissions.nprim) { printf ("\nNo access vector definition for that class\n"); break; } ret = sepol_compute_av(ssid, tsid, tclass, 0, &avd); switch (ret) { case 0: printf("\nallowed {"); for (i = 1; i <= sizeof(avd.allowed) * 8; i++) { if (avd.allowed & (UINT32_C(1) << (i - 1))) { v.val = i; ret = hashtab_map(cladatum-> permissions. table, find_perm, &v); if (!ret && cladatum->comdatum) { ret = hashtab_map (cladatum-> comdatum-> permissions.table, find_perm, &v); } if (ret) printf(" %s", v.name); } } printf(" }\n"); break; case -EINVAL: printf("\ninvalid sid\n"); break; default: printf("return code 0x%x\n", ret); } break; case '1': printf("sid? "); FGETS(ans, sizeof(ans), stdin); ssid = atoi(ans); ret = sepol_sid_to_context(ssid, &scontext, &scontext_len); switch (ret) { case 0: printf("\nscontext %s\n", scontext); free(scontext); break; case -EINVAL: printf("\ninvalid sid\n"); break; case -ENOMEM: printf("\nout of memory\n"); break; default: printf("return code 0x%x\n", ret); } break; case '2': printf("scontext? "); FGETS(ans, sizeof(ans), stdin); scontext_len = strlen(ans); ans[scontext_len - 1] = 0; ret = sepol_context_to_sid(ans, scontext_len, &ssid); switch (ret) { case 0: printf("\nsid %d\n", ssid); break; case -EINVAL: printf("\ninvalid context\n"); break; case -ENOMEM: printf("\nout of memory\n"); break; default: printf("return code 0x%x\n", ret); } break; case '3': case '4': case '5': ch = ans[0]; printf("source sid? "); FGETS(ans, sizeof(ans), stdin); ssid = atoi(ans); printf("target sid? "); FGETS(ans, sizeof(ans), stdin); tsid = atoi(ans); printf("object class? "); FGETS(ans, sizeof(ans), stdin); if (isdigit(ans[0])) { tclass = atoi(ans); if (!tclass || tclass > policydb.p_classes.nprim) { printf("\nNo such class.\n"); break; } } else { ans[strlen(ans) - 1] = 0; cladatum = (class_datum_t *) hashtab_search(policydb. p_classes. table, ans); if (!cladatum) { printf("\nNo such class\n"); break; } tclass = cladatum->s.value; } if (ch == '3') ret = sepol_transition_sid(ssid, tsid, tclass, &ssid); else if (ch == '4') ret = sepol_member_sid(ssid, tsid, tclass, &ssid); else ret = sepol_change_sid(ssid, tsid, tclass, &ssid); switch (ret) { case 0: printf("\nsid %d\n", ssid); break; case -EINVAL: printf("\ninvalid sid\n"); break; case -ENOMEM: printf("\nout of memory\n"); break; default: printf("return code 0x%x\n", ret); } break; case '6': sepol_sidtab_map(&sidtab, print_sid, 0); break; case '7': printf("pathname? "); FGETS(ans, sizeof(ans), stdin); pathlen = strlen(ans); ans[pathlen - 1] = 0; fd = open(ans, O_RDONLY); if (fd < 0) { fprintf(stderr, "Can't open '%s': %s\n", ans, strerror(errno)); break; } if (fstat(fd, &sb) < 0) { fprintf(stderr, "Can't stat '%s': %s\n", ans, strerror(errno)); break; } map = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (map == MAP_FAILED) { fprintf(stderr, "Can't map '%s': %s\n", ans, strerror(errno)); break; } ret = sepol_load_policy(map, sb.st_size); switch (ret) { case 0: printf("\nsuccess\n"); break; case -EINVAL: printf("\ninvalid policy\n"); break; case -ENOMEM: printf("\nout of memory\n"); break; default: printf("return code 0x%x\n", ret); } break; case '8': printf("fs kdevname? "); FGETS(ans, sizeof(ans), stdin); ans[strlen(ans) - 1] = 0; ret = sepol_fs_sid(ans, &ssid, &tsid); if (ret) { printf("unknown fs kdevname\n"); } else { printf("fs_sid %d default_file_sid %d\n", ssid, tsid); } break; case '9': printf("protocol? "); FGETS(ans, sizeof(ans), stdin); ans[strlen(ans) - 1] = 0; if (!strcmp(ans, "tcp") || !strcmp(ans, "TCP")) protocol = IPPROTO_TCP; else if (!strcmp(ans, "udp") || !strcmp(ans, "UDP")) protocol = IPPROTO_UDP; else if (!strcmp(ans, "dccp") || !strcmp(ans, "DCCP")) protocol = IPPROTO_DCCP; else if (!strcmp(ans, "sctp") || !strcmp(ans, "SCTP")) protocol = IPPROTO_SCTP; else { printf("unknown protocol\n"); break; } printf("port? "); FGETS(ans, sizeof(ans), stdin); port = atoi(ans); sepol_port_sid(0, 0, protocol, port, &ssid); printf("sid %d\n", ssid); break; case 'a': printf("netif name? "); FGETS(ans, sizeof(ans), stdin); ans[strlen(ans) - 1] = 0; ret = sepol_netif_sid(ans, &ssid, &tsid); if (ret) { printf("unknown name\n"); } else { printf("if_sid %d default_msg_sid %d\n", ssid, tsid); } break; case 'b':{ char *p; int family, len; struct in_addr addr4; struct in6_addr addr6; printf("protocol family? "); FGETS(ans, sizeof(ans), stdin); ans[strlen(ans) - 1] = 0; if (!strcasecmp(ans, "ipv4")) family = AF_INET; else if (!strcasecmp(ans, "ipv6")) family = AF_INET6; else { printf("unknown protocol family\n"); break; } printf("node address? "); FGETS(ans, sizeof(ans), stdin); ans[strlen(ans) - 1] = 0; if (family == AF_INET) { p = (char *)&addr4; len = sizeof(addr4); } else { p = (char *)&addr6; len = sizeof(addr6); } if (inet_pton(family, ans, p) < 1) { printf("error parsing address\n"); break; } sepol_node_sid(family, p, len, &ssid); printf("sid %d\n", ssid); break; } case 'c': printf("fstype? "); FGETS(ans, sizeof(ans), stdin); ans[strlen(ans) - 1] = 0; sepol_fs_use(ans, &uret, &ssid); switch (uret) { case SECURITY_FS_USE_XATTR: printf("use xattr\n"); break; case SECURITY_FS_USE_TRANS: printf("use transition SIDs\n"); break; case SECURITY_FS_USE_TASK: printf("use task SIDs\n"); break; case SECURITY_FS_USE_GENFS: printf("use genfs\n"); break; case SECURITY_FS_USE_NONE: printf("no labeling support\n"); break; } printf("sid %d\n", ssid); break; case 'd': printf("fstype? "); FGETS(ans, sizeof(ans), stdin); ans[strlen(ans) - 1] = 0; fstype = strdup(ans); printf("path? "); FGETS(ans, sizeof(ans), stdin); ans[strlen(ans) - 1] = 0; path = strdup(ans); printf("object class? "); FGETS(ans, sizeof(ans), stdin); if (isdigit(ans[0])) { tclass = atoi(ans); if (!tclass || tclass > policydb.p_classes.nprim) { printf("\nNo such class.\n"); break; } } else { ans[strlen(ans) - 1] = 0; cladatum = (class_datum_t *) hashtab_search(policydb. p_classes. table, ans); if (!cladatum) { printf("\nNo such class\n"); break; } tclass = cladatum->s.value; } sepol_genfs_sid(fstype, path, tclass, &ssid); printf("sid %d\n", ssid); free(fstype); free(path); break; case 'e': printf("from SID? "); FGETS(ans, sizeof(ans), stdin); ans[strlen(ans) - 1] = 0; ssid = atoi(ans); printf("username? "); FGETS(ans, sizeof(ans), stdin); ans[strlen(ans) - 1] = 0; ret = sepol_get_user_sids(ssid, ans, &sids, &nel); switch (ret) { case 0: if (!nel) printf("\nnone\n"); for (i = 0; i < nel; i++) print_sid(sids[i], NULL, NULL); free(sids); break; case -ENOMEM: printf("\nout of memory\n"); break; case -EINVAL: printf("\ninvalid argument\n"); break; default: printf("\nerror\n"); break; } break; case 'f': display_bools(); break; case 'g': display_cond_expressions(); break; case 'h': printf("name? "); FGETS(ans, sizeof(ans), stdin); ans[strlen(ans) - 1] = 0; name = strdup(ans); if (name == NULL) { fprintf(stderr, "couldn't strdup string.\n"); break; } printf("state? "); FGETS(ans, sizeof(ans), stdin); ans[strlen(ans) - 1] = 0; if (atoi(ans)) state = 1; else state = 0; change_bool(name, state); free(name); break; case 'i': printf("source sid? "); FGETS(ans, sizeof(ans), stdin); ssid = atoi(ans); printf("target sid? "); FGETS(ans, sizeof(ans), stdin); tsid = atoi(ans); printf("target class? "); FGETS(ans, sizeof(ans), stdin); if (isdigit(ans[0])) { tclass = atoi(ans); if (!tclass || tclass > policydb.p_classes.nprim) { printf("\nNo such class.\n"); break; } } else { ans[strlen(ans) - 1] = 0; cladatum = (class_datum_t *) hashtab_search(policydb. p_classes. table, ans); if (!cladatum) { printf("\nNo such class\n"); break; } tclass = cladatum->s.value; } flags = SHOW_GRANTED; if (sepol_compute_av_reason_buffer(ssid, tsid, tclass, 0, &avd, &reason, &reason_buf, flags)) { printf("\nconstraint error\n"); break; } if (reason_buf) { printf("\nConstraint expressions:\n%s", reason_buf); free(reason_buf); } else { printf("\nNo constraints found.\n"); } break; case 'j': printf("old sid? "); FGETS(ans, sizeof(ans), stdin); oldsid = atoi(ans); printf("new sid? "); FGETS(ans, sizeof(ans), stdin); newsid = atoi(ans); printf("task sid? "); FGETS(ans, sizeof(ans), stdin); tasksid = atoi(ans); printf("target class? "); FGETS(ans, sizeof(ans), stdin); if (isdigit(ans[0])) { tclass = atoi(ans); if (!tclass || tclass > policydb.p_classes.nprim) { printf("\nNo such class.\n"); break; } } else { ans[strlen(ans) - 1] = 0; cladatum = (class_datum_t *) hashtab_search(policydb. p_classes. table, ans); if (!cladatum) { printf("\nNo such class\n"); break; } tclass = cladatum->s.value; } flags = SHOW_GRANTED; if (sepol_validate_transition_reason_buffer(oldsid, newsid, tasksid, tclass, &reason_buf, flags)) { printf("\nvalidatetrans error\n"); break; } if (reason_buf) { printf("\nValidatetrans expressions:\n%s", reason_buf); free(reason_buf); } else { printf( "\nNo validatetrans expressions found.\n"); } break; case 'k': { char *p; struct in6_addr addr6; uint64_t subnet_prefix; unsigned int pkey; printf("subnet prefix? "); FGETS(ans, sizeof(ans), stdin); ans[strlen(ans) - 1] = 0; p = (char *)&addr6; if (inet_pton(AF_INET6, ans, p) < 1) { printf("error parsing subnet prefix\n"); break; } memcpy(&subnet_prefix, p, sizeof(subnet_prefix)); printf("pkey? "); FGETS(ans, sizeof(ans), stdin); pkey = atoi(ans); sepol_ibpkey_sid(subnet_prefix, pkey, &ssid); printf("sid %d\n", ssid); } break; case 'l': printf("device name (eg. mlx4_0)? "); FGETS(ans, sizeof(ans), stdin); ans[strlen(ans) - 1] = 0; name = strdup(ans); if (!name) { fprintf(stderr, "couldn't strdup string.\n"); break; } printf("port? "); FGETS(ans, sizeof(ans), stdin); port = atoi(ans); sepol_ibendport_sid(name, port, &ssid); printf("sid %d\n", ssid); free(name); break; #ifdef EQUIVTYPES case 'z': identify_equiv_types(); break; #endif case 'm': goto menu; case 'q': exit(0); break; default: printf("\nUnknown option %s.\n", ans); } } return 0; } /* FLASK */ checkpolicy-3.8.1/fuzz/000077500000000000000000000000001476211737200150305ustar00rootroot00000000000000checkpolicy-3.8.1/fuzz/checkpolicy-fuzzer.c000066400000000000000000000137361476211737200210260ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include "module_compiler.h" #include "queue.h" extern int policydb_validate(sepol_handle_t *handle, const policydb_t *p); extern int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); extern int mlspol; extern policydb_t *policydbp; extern queue_t id_queue; extern unsigned int policydb_errors; extern int yynerrs; extern FILE *yyin; extern void init_parser(int pass, const char *input_name); extern int yyparse(void); extern void yyrestart(FILE *); extern int yylex_destroy(void); jmp_buf fuzzing_pre_parse_stack_state; // Set to 1 for verbose libsepol logging #define VERBOSE 0 static ssize_t full_write(int fd, const void *buf, size_t count) { ssize_t written = 0; while (count > 0) { ssize_t ret = write(fd, buf, count); if (ret < 0) { if (errno == EINTR) continue; return ret; } if (ret == 0) break; written += ret; buf = (const unsigned char *)buf + (size_t)ret; count -= (size_t)ret; } return written; } static int read_source_policy(policydb_t *p, const uint8_t *data, size_t size) { int fd, rc; ssize_t wr; fd = memfd_create("fuzz-input", MFD_CLOEXEC); if (fd < 0) return -1; wr = full_write(fd, data, size); if (wr < 0 || (size_t)wr != size) { close(fd); return -1; } fsync(fd); yynerrs = 0; yyin = fdopen(fd, "r"); if (!yyin) { close(fd); return -1; } rewind(yyin); id_queue = queue_create(); if (id_queue == NULL) { fclose(yyin); yylex_destroy(); return -1; } policydbp = p; mlspol = p->mls; init_parser(1, "fuzz-input-1"); if (setjmp(fuzzing_pre_parse_stack_state) != 0) { queue_destroy(id_queue); fclose(yyin); yylex_destroy(); return -1; } rc = yyparse(); // TODO: drop global variable policydb_errors if proven to be redundant assert(rc || !policydb_errors); if (rc || policydb_errors) { queue_destroy(id_queue); fclose(yyin); yylex_destroy(); return -1; } rewind(yyin); init_parser(2, "fuzz-input-2"); yyrestart(yyin); rc = yyparse(); assert(rc || !policydb_errors); if (rc || policydb_errors) { queue_destroy(id_queue); fclose(yyin); yylex_destroy(); return -1; } queue_destroy(id_queue); fclose(yyin); yylex_destroy(); return 0; } static int write_binary_policy(FILE *outfp, policydb_t *p) { struct policy_file pf; policy_file_init(&pf); pf.type = PF_USE_STDIO; pf.fp = outfp; return policydb_write(p, &pf); } int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { policydb_t parsepolicydb = {}; policydb_t kernpolicydb = {}; policydb_t *finalpolicydb; sidtab_t sidtab = {}; FILE *devnull = NULL; int mls, platform, policyvers; sepol_debug(VERBOSE); /* * Take the first byte whether to generate a SELinux or Xen policy, * the second byte whether to parse as MLS policy, * and the second byte as policy version. */ if (size < 3) return 0; switch (data[0]) { case 'S': platform = SEPOL_TARGET_SELINUX; break; case 'X': platform = SEPOL_TARGET_XEN; break; default: return 0; } switch (data[1]) { case '0': mls = 0; break; case '1': mls = 1; break; default: return 0; } static_assert(0x7F - 'A' >= POLICYDB_VERSION_MAX, "Max policy version should be representable"); policyvers = data[2] - 'A'; if (policyvers < POLICYDB_VERSION_MIN || policyvers > POLICYDB_VERSION_MAX) return 0; data += 3; size -= 3; if (policydb_init(&parsepolicydb)) goto exit; parsepolicydb.policy_type = POLICY_BASE; parsepolicydb.mls = mls; parsepolicydb.handle_unknown = DENY_UNKNOWN; parsepolicydb.policyvers = policyvers; policydb_set_target_platform(&parsepolicydb, platform); if (read_source_policy(&parsepolicydb, data, size)) goto exit; if (parsepolicydb.policy_type == POLICY_BASE) { if (link_modules(NULL, &parsepolicydb, NULL, 0, VERBOSE)) goto exit; if (policydb_init(&kernpolicydb)) goto exit; if (expand_module(NULL, &parsepolicydb, &kernpolicydb, VERBOSE, /*check_assertions=*/0)) goto exit; (void) check_assertions(NULL, &kernpolicydb, kernpolicydb.global->branch_list->avrules); (void) hierarchy_check_constraints(NULL, &kernpolicydb); kernpolicydb.policyvers = policyvers; assert(kernpolicydb.policy_type == POLICY_KERN); assert(kernpolicydb.handle_unknown == SEPOL_DENY_UNKNOWN); assert(kernpolicydb.mls == mls); assert(kernpolicydb.target_platform == platform); finalpolicydb = &kernpolicydb; } else { assert(parsepolicydb.policy_type == POLICY_MOD); assert(parsepolicydb.handle_unknown == SEPOL_DENY_UNKNOWN); assert(parsepolicydb.mls == mls); assert(parsepolicydb.target_platform == platform); finalpolicydb = &parsepolicydb; } if (policydb_load_isids(finalpolicydb, &sidtab)) goto exit; if (finalpolicydb->policy_type == POLICY_KERN && policydb_optimize(finalpolicydb)) goto exit; if (policydb_sort_ocontexts(finalpolicydb)) goto exit; if (policydb_validate(NULL, finalpolicydb)) /* never generate an invalid policy */ abort(); devnull = fopen("/dev/null", "we"); if (devnull == NULL) goto exit; if (write_binary_policy(devnull, finalpolicydb)) abort(); if (finalpolicydb->policy_type == POLICY_KERN && sepol_kernel_policydb_to_conf(devnull, finalpolicydb)) abort(); if (finalpolicydb->policy_type == POLICY_KERN && sepol_kernel_policydb_to_cil(devnull, finalpolicydb)) abort(); if (finalpolicydb->policy_type == POLICY_MOD && sepol_module_policydb_to_cil(devnull, finalpolicydb, /*linked=*/0)) abort(); exit: if (devnull != NULL) fclose(devnull); sepol_sidtab_destroy(&sidtab); policydb_destroy(&kernpolicydb); policydb_destroy(&parsepolicydb); id_queue = NULL; policydbp = NULL; module_compiler_reset(); /* Non-zero return values are reserved for future use. */ return 0; } checkpolicy-3.8.1/fuzz/checkpolicy-fuzzer.dict000066400000000000000000000020421476211737200215130ustar00rootroot00000000000000# Keyword dictionary "clone" "common" "class" "constrain" "validatetrans" "inherits" "sid" "role" "roles" "roleattribute" "attribute_role" "types" "typealias" "typeattribute" "typebounds" "type" "bool" "tunable" "if" "else" "alias" "attribute" "expandattribute" "type_transition" "type_member" "type_change" "role_transition" "range_transition" "sensitivity" "dominance" "category" "level" "range" "mlsconstrain" "mlsvalidatetrans" "user" "neverallow" "allow" "auditallow" "auditdeny" "dontaudit" "allowxperm" "auditallowxperm" "dontauditxperm" "neverallowxperm" "source" "target" "sameuser" "module" "require" "optional" "or" "and" "not" "xor" "eq" "true" "false" "dom" "domby" "incomp" "fscon" "ibpkeycon" "ibendportcon" "portcon" "netifcon" "nodecon" "pirqcon" "iomemcon" "ioportcon" "pcidevicecon" "devicetreecon" "fs_use_xattr" "fs_use_task" "fs_use_trans" "genfscon" "r1" "r2" "r3" "u1" "u2" "u3" "t1" "t2" "t3" "l1" "l2" "h1" "h2" "policycap" "permissive" "default_user" "default_role" "default_type" "default_range" "low-high" "high" "low" "glblub" checkpolicy-3.8.1/fuzz/min_pol.conf000066400000000000000000000025571476211737200173450ustar00rootroot00000000000000class process class blk_file class chr_file class dir class fifo_file class file class lnk_file class sock_file sid kernel sid security sid unlabeled sid fs sid file sid file_labels sid init sid any_socket sid port sid netif sid netmsg sid node sid igmp_packet sid icmp_socket sid tcp_socket sid sysctl_modprobe sid sysctl sid sysctl_fs sid sysctl_kernel sid sysctl_net sid sysctl_net_unix sid sysctl_vm sid sysctl_dev sid kmod sid policy sid scmp_packet sid devnull class process { dyntransition transition } default_role { blk_file } source; default_role { chr_file } source; default_role { dir } source; default_role { fifo_file } source; default_role { file } source; default_role { lnk_file } source; default_role { sock_file } source; type sys_isid; typealias sys_isid alias { dpkg_script_t rpm_script_t }; allow sys_isid self : process { dyntransition transition }; role sys_role; role sys_role types { sys_isid }; user sys_user roles sys_role; sid kernel sys_user:sys_role:sys_isid sid security sys_user:sys_role:sys_isid sid unlabeled sys_user:sys_role:sys_isid sid file sys_user:sys_role:sys_isid sid port sys_user:sys_role:sys_isid sid netif sys_user:sys_role:sys_isid sid netmsg sys_user:sys_role:sys_isid sid node sys_user:sys_role:sys_isid sid devnull sys_user:sys_role:sys_isid fs_use_trans devpts sys_user:sys_role:sys_isid; fs_use_trans devtmpfs sys_user:sys_role:sys_isid; checkpolicy-3.8.1/fuzz/min_pol.mls.conf000066400000000000000000000030171476211737200201270ustar00rootroot00000000000000class process class blk_file class chr_file class dir class fifo_file class file class lnk_file class sock_file sid kernel sid security sid unlabeled sid fs sid file sid file_labels sid init sid any_socket sid port sid netif sid netmsg sid node sid igmp_packet sid icmp_socket sid tcp_socket sid sysctl_modprobe sid sysctl sid sysctl_fs sid sysctl_kernel sid sysctl_net sid sysctl_net_unix sid sysctl_vm sid sysctl_dev sid kmod sid policy sid scmp_packet sid devnull class process { dyntransition transition } default_role { blk_file } source; default_role { chr_file } source; default_role { dir } source; default_role { fifo_file } source; default_role { file } source; default_role { lnk_file } source; default_role { sock_file } source; sensitivity s0; dominance { s0 } category c0; level s0:c0; mlsconstrain process transition t1 eq t2; type sys_isid; typealias sys_isid alias { dpkg_script_t rpm_script_t }; allow sys_isid self : process { dyntransition transition }; role sys_role; role sys_role types { sys_isid }; user sys_user roles sys_role level s0 range s0 - s0:c0; sid kernel sys_user:sys_role:sys_isid:s0 sid security sys_user:sys_role:sys_isid:s0 sid unlabeled sys_user:sys_role:sys_isid:s0 sid file sys_user:sys_role:sys_isid:s0 sid port sys_user:sys_role:sys_isid:s0 sid netif sys_user:sys_role:sys_isid:s0 sid netmsg sys_user:sys_role:sys_isid:s0 sid node sys_user:sys_role:sys_isid:s0 sid devnull sys_user:sys_role:sys_isid:s0 fs_use_trans devpts sys_user:sys_role:sys_isid:s0; fs_use_trans devtmpfs sys_user:sys_role:sys_isid:s0; checkpolicy-3.8.1/module_compiler.c000066400000000000000000001052631476211737200173640ustar00rootroot00000000000000/* Author : Joshua Brindle * Karl MacMillan * Jason Tang * Added support for binary policy modules * * Copyright (C) 2004 - 2005 Tresys Technology, LLC * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ #include #include #include #include #include #include #include #include "queue.h" #include "module_compiler.h" typedef struct scope_stack { int type; /* 1 = avrule block, 2 = conditional */ avrule_decl_t *decl; /* if in an avrule block, which * declaration is current */ avrule_t *last_avrule; int in_else; /* if in an avrule block, within ELSE branch */ int require_given; /* 1 if this block had at least one require */ struct scope_stack *parent; } scope_stack_t; extern policydb_t *policydbp; extern queue_t id_queue; extern int yyerror(const char *msg); __attribute__ ((format(printf, 1, 2))) extern void yyerror2(const char *fmt, ...); static int push_stack(int stack_type, ...); static void pop_stack(void); /* keep track of the last item added to the stack */ static scope_stack_t *stack_top = NULL; static avrule_block_t *last_block; static uint32_t next_decl_id = 1; static const char * const flavor_str[SYM_NUM] = { [SYM_COMMONS] = "common", [SYM_CLASSES] = "class", [SYM_ROLES] = "role", [SYM_TYPES] = "type", [SYM_USERS] = "user", [SYM_BOOLS] = "bool", [SYM_LEVELS] = "level", [SYM_CATS] = "cat" }; static void print_error_msg(int ret, uint32_t symbol_type) { switch (ret) { case -3: yyerror("Out of memory!"); break; case -2: yyerror2("Duplicate declaration of %s", flavor_str[symbol_type]); break; case -1: yyerror2("Could not declare %s here", flavor_str[symbol_type]); break; default: yyerror2("Unknown error %d", ret); } } int define_policy(int pass, int module_header_given) { char *id; if (module_header_given) { if (policydbp->policy_type != POLICY_MOD) { yyerror ("Module specification found while not building a policy module."); return -1; } if (pass == 2) { while ((id = queue_remove(id_queue)) != NULL) free(id); } else { id = (char *)queue_remove(id_queue); if (!id) { yyerror("no module name"); return -1; } free(policydbp->name); policydbp->name = id; if ((policydbp->version = queue_remove(id_queue)) == NULL) { yyerror ("Expected a module version but none was found."); return -1; } } } else { if (policydbp->policy_type == POLICY_MOD) { yyerror ("Building a policy module, but no module specification found."); return -1; } } /* the first declaration within the global avrule block will always have an id of 1 */ next_decl_id = 2; /* reset the scoping stack */ while (stack_top != NULL) { pop_stack(); } if (push_stack(1, policydbp->global, policydbp->global->branch_list) == -1) { return -1; } last_block = policydbp->global; return 0; } /* Given the current parse stack, returns 1 if a declaration or require would * be allowed here or 0 if not. For example, declarations and requirements are * not allowed in conditionals, so if there are any conditionals in the * current scope stack then this would return a 0. */ static int is_creation_allowed(void) { if (stack_top->type != 1 || stack_top->in_else) { return 0; } return 1; } /* Attempt to declare or require a symbol within the current scope. * Returns: * 0: Success - Symbol had not been previously created. * 1: Success - Symbol had already been created and caller must free datum. * -1: Failure - Symbol cannot be created here * -2: Failure - Duplicate declaration or type/attribute mismatch * -3: Failure - Out of memory or some other error */ static int create_symbol(uint32_t symbol_type, hashtab_key_t key, hashtab_datum_t datum, uint32_t * dest_value, uint32_t scope) { avrule_decl_t *decl = stack_top->decl; int ret; if (!is_creation_allowed()) { return -1; } ret = symtab_insert(policydbp, symbol_type, key, datum, scope, decl->decl_id, dest_value); if (ret == 1 && dest_value) { hashtab_datum_t s = hashtab_search(policydbp->symtab[symbol_type].table, key); assert(s != NULL); if (symbol_type == SYM_LEVELS) { *dest_value = ((level_datum_t *)s)->level->sens; } else { *dest_value = ((symtab_datum_t *)s)->value; } } else if (ret == -2) { return -2; } else if (ret < 0) { return -3; } return ret; } /* Attempt to declare a symbol within the current declaration. If * currently within a non-conditional and in a non-else branch then * insert the symbol, return 0 on success if symbol was undeclared. * For roles and users, it is legal to have multiple declarations; as * such return 1 to indicate that caller must free() the datum because * it was not added. If symbols may not be declared here return -1. * For duplicate declarations return -2. For all else, including out * of memory, return -3. Note that dest_value and datum_value might * not be restricted pointers. */ int declare_symbol(uint32_t symbol_type, hashtab_key_t key, hashtab_datum_t datum, uint32_t * dest_value, const uint32_t * datum_value) { avrule_decl_t *decl = stack_top->decl; int ret = create_symbol(symbol_type, key, datum, dest_value, SCOPE_DECL); if (ret < 0) { return ret; } if (ebitmap_set_bit(decl->declared.scope + symbol_type, *datum_value - 1, 1)) { return -3; } return ret; } static int role_implicit_bounds(hashtab_t roles_tab, char *role_id, role_datum_t *role) { role_datum_t *bounds; char *bounds_id, *delim; delim = strrchr(role_id, '.'); if (!delim) return 0; /* no implicit boundary */ bounds_id = strdup(role_id); if (!bounds_id) { yyerror("out of memory"); return -1; } bounds_id[(size_t)(delim - role_id)] = '\0'; bounds = hashtab_search(roles_tab, bounds_id); if (!bounds) { yyerror2("role %s doesn't exist, is implicit bounds of %s", bounds_id, role_id); free(bounds_id); return -1; } if (!role->bounds) role->bounds = bounds->s.value; else if (role->bounds != bounds->s.value) { yyerror2("role %s has inconsistent bounds %s/%s", role_id, bounds_id, policydbp->p_role_val_to_name[role->bounds - 1]); free(bounds_id); return -1; } free(bounds_id); return 0; } static int create_role(uint32_t scope, unsigned char isattr, role_datum_t **role, char **key) { char *id = queue_remove(id_queue); role_datum_t *datum = NULL; int ret; uint32_t value; *role = NULL; *key = NULL; isattr = isattr ? ROLE_ATTRIB : ROLE_ROLE; if (id == NULL) { yyerror("no role name"); return -1; } datum = malloc(sizeof(*datum)); if (datum == NULL) { yyerror("Out of memory!"); free(id); return -1; } role_datum_init(datum); datum->flavor = isattr; if (scope == SCOPE_DECL) { ret = declare_symbol(SYM_ROLES, id, datum, &value, &value); } else { ret = require_symbol(SYM_ROLES, id, datum, &value, &value); } if (ret == 0) { datum->s.value = value; *role = datum; *key = strdup(id); if (*key == NULL) { yyerror("Out of memory!"); return -1; } } else if (ret == 1) { *role = hashtab_search(policydbp->symtab[SYM_ROLES].table, id); if (*role && (isattr != (*role)->flavor)) { yyerror2("Identifier %s used as both an attribute and a role", id); free(id); role_datum_destroy(datum); free(datum); return -1; } datum->s.value = value; *role = datum; *key = id; } else { print_error_msg(ret, SYM_ROLES); free(id); role_datum_destroy(datum); free(datum); } return ret; } role_datum_t *declare_role(unsigned char isattr) { char *key = NULL; role_datum_t *role = NULL; role_datum_t *dest_role = NULL; hashtab_t roles_tab; int ret, ret2; ret = create_role(SCOPE_DECL, isattr, &role, &key); if (ret < 0) { return NULL; } /* create a new role_datum_t for this decl, if necessary */ assert(stack_top->type == 1); if (stack_top->parent == NULL) { /* in parent, so use global symbol table */ roles_tab = policydbp->p_roles.table; } else { roles_tab = stack_top->decl->p_roles.table; } dest_role = hashtab_search(roles_tab, key); if (dest_role == NULL) { if (ret == 0) { dest_role = malloc(sizeof(*dest_role)); if (dest_role == NULL) { yyerror("Out of memory!"); free(key); return NULL; } role_datum_init(dest_role); dest_role->s.value = role->s.value; dest_role->flavor = role->flavor; } else { dest_role = role; } ret2 = role_implicit_bounds(roles_tab, key, dest_role); if (ret2 != 0) { free(key); role_datum_destroy(dest_role); free(dest_role); return NULL; } ret2 = hashtab_insert(roles_tab, key, dest_role); if (ret2 != 0) { yyerror("Out of memory!"); free(key); role_datum_destroy(dest_role); free(dest_role); return NULL; } } else { free(key); if (ret == 1) { role_datum_destroy(role); free(role); } } if (ret == 0) { ret2 = ebitmap_set_bit(&dest_role->dominates, dest_role->s.value - 1, 1); if (ret2 != 0) { yyerror("out of memory"); return NULL; } } return dest_role; } static int create_type(uint32_t scope, unsigned char isattr, type_datum_t **type) { char *id; type_datum_t *datum; int ret; uint32_t value = 0; *type = NULL; isattr = isattr ? TYPE_ATTRIB : TYPE_TYPE; id = (char *)queue_remove(id_queue); if (!id) { yyerror("no type/attribute name?"); return -1; } if (strcmp(id, "self") == 0) { yyerror("\"self\" is a reserved type name."); free(id); return -1; } datum = malloc(sizeof(*datum)); if (!datum) { yyerror("Out of memory!"); free(id); return -1; } type_datum_init(datum); datum->primary = 1; datum->flavor = isattr; if (scope == SCOPE_DECL) { ret = declare_symbol(SYM_TYPES, id, datum, &value, &value); } else { ret = require_symbol(SYM_TYPES, id, datum, &value, &value); } if (ret == 0) { datum->s.value = value; *type = datum; } else if (ret == 1) { type_datum_destroy(datum); free(datum); *type = hashtab_search(policydbp->symtab[SYM_TYPES].table, id); if (*type && (isattr != (*type)->flavor)) { yyerror2("Identifier %s used as both an attribute and a type", id); free(id); return -1; } free(id); } else { print_error_msg(ret, SYM_TYPES); free(id); type_datum_destroy(datum); free(datum); } return ret; } type_datum_t *declare_type(unsigned char primary, unsigned char isattr) { type_datum_t *type = NULL; int ret = create_type(SCOPE_DECL, isattr, &type); if (ret == 0) { type->primary = primary; } return type; } static int user_implicit_bounds(hashtab_t users_tab, char *user_id, user_datum_t *user) { user_datum_t *bounds; char *bounds_id, *delim; delim = strrchr(user_id, '.'); if (!delim) return 0; /* no implicit boundary */ bounds_id = strdup(user_id); if (!bounds_id) { yyerror("out of memory"); return -1; } bounds_id[(size_t)(delim - user_id)] = '\0'; bounds = hashtab_search(users_tab, bounds_id); if (!bounds) { yyerror2("user %s doesn't exist, is implicit bounds of %s", bounds_id, user_id); free(bounds_id); return -1; } if (!user->bounds) user->bounds = bounds->s.value; else if (user->bounds != bounds->s.value) { yyerror2("user %s has inconsistent bounds %s/%s", user_id, bounds_id, policydbp->p_role_val_to_name[user->bounds - 1]); free(bounds_id); return -1; } free(bounds_id); return 0; } static int create_user(uint32_t scope, user_datum_t **user, char **key) { char *id = queue_remove(id_queue); user_datum_t *datum = NULL; int ret; uint32_t value; *user = NULL; *key = NULL; if (id == NULL) { yyerror("no user name"); return -1; } datum = malloc(sizeof(*datum)); if (datum == NULL) { yyerror("Out of memory!"); free(id); return -1; } user_datum_init(datum); if (scope == SCOPE_DECL) { ret = declare_symbol(SYM_USERS, id, datum, &value, &value); } else { ret = require_symbol(SYM_USERS, id, datum, &value, &value); } if (ret == 0) { datum->s.value = value; *user = datum; *key = strdup(id); if (*key == NULL) { yyerror("Out of memory!"); return -1; } } else if (ret == 1) { datum->s.value = value; *user = datum; *key = id; } else { print_error_msg(ret, SYM_USERS); free(id); user_datum_destroy(datum); free(datum); } return ret; } user_datum_t *declare_user(void) { char *key = NULL; user_datum_t *user = NULL; user_datum_t *dest_user = NULL; hashtab_t users_tab; int ret, ret2; ret = create_user(SCOPE_DECL, &user, &key); if (ret < 0) { return NULL; } /* create a new user_datum_t for this decl, if necessary */ assert(stack_top->type == 1); if (stack_top->parent == NULL) { /* in parent, so use global symbol table */ users_tab = policydbp->p_users.table; } else { users_tab = stack_top->decl->p_users.table; } dest_user = hashtab_search(users_tab, key); if (dest_user == NULL) { if (ret == 0) { dest_user = malloc(sizeof(*dest_user)); if (dest_user == NULL) { yyerror("Out of memory!"); free(key); return NULL; } user_datum_init(dest_user); dest_user->s.value = user->s.value; } else { dest_user = user; } ret2 = user_implicit_bounds(users_tab, key, dest_user); if (ret2 != 0) { free(key); user_datum_destroy(dest_user); free(dest_user); return NULL; } ret2 = hashtab_insert(users_tab, key, dest_user); if (ret2 != 0) { yyerror("Out of memory!"); free(key); user_datum_destroy(dest_user); free(dest_user); return NULL; } } else { free(key); if (ret == 1) { user_datum_destroy(user); free(user); } } return dest_user; } /* Return a type_datum_t for the local avrule_decl with the given ID. * If it does not exist, create one with the same value as 'value'. * This function assumes that the ID is within scope. c.f., * is_id_in_scope(). * * NOTE: this function usurps ownership of id afterwards. The caller * shall not reference it nor free() it afterwards. */ type_datum_t *get_local_type(char *id, uint32_t value, unsigned char isattr) { type_datum_t *dest_typdatum; hashtab_t types_tab; assert(stack_top->type == 1); if (stack_top->parent == NULL) { /* in global, so use global symbol table */ types_tab = policydbp->p_types.table; } else { types_tab = stack_top->decl->p_types.table; } dest_typdatum = hashtab_search(types_tab, id); if (!dest_typdatum) { dest_typdatum = (type_datum_t *) malloc(sizeof(type_datum_t)); if (dest_typdatum == NULL) { free(id); return NULL; } type_datum_init(dest_typdatum); dest_typdatum->s.value = value; dest_typdatum->flavor = isattr ? TYPE_ATTRIB : TYPE_TYPE; dest_typdatum->primary = 1; if (hashtab_insert(types_tab, id, dest_typdatum)) { free(id); type_datum_destroy(dest_typdatum); free(dest_typdatum); return NULL; } } else { free(id); if (dest_typdatum->flavor != isattr ? TYPE_ATTRIB : TYPE_TYPE) { return NULL; } } return dest_typdatum; } /* Return a role_datum_t for the local avrule_decl with the given ID. * If it does not exist, create one with the same value as 'value'. * This function assumes that the ID is within scope. c.f., * is_id_in_scope(). * * NOTE: this function usurps ownership of id afterwards. The caller * shall not reference it nor free() it afterwards. */ role_datum_t *get_local_role(char *id, uint32_t value, unsigned char isattr) { role_datum_t *dest_roledatum; hashtab_t roles_tab; assert(stack_top->type == 1); if (stack_top->parent == NULL) { /* in global, so use global symbol table */ roles_tab = policydbp->p_roles.table; } else { roles_tab = stack_top->decl->p_roles.table; } dest_roledatum = hashtab_search(roles_tab, id); if (!dest_roledatum) { dest_roledatum = (role_datum_t *)malloc(sizeof(role_datum_t)); if (dest_roledatum == NULL) { free(id); return NULL; } role_datum_init(dest_roledatum); dest_roledatum->s.value = value; dest_roledatum->flavor = isattr ? ROLE_ATTRIB : ROLE_ROLE; if (hashtab_insert(roles_tab, id, dest_roledatum)) { free(id); role_datum_destroy(dest_roledatum); free(dest_roledatum); return NULL; } } else { free(id); if (dest_roledatum->flavor != isattr ? ROLE_ATTRIB : ROLE_ROLE) return NULL; } return dest_roledatum; } /* Attempt to require a symbol within the current scope. If currently * within an optional (and not its else branch), add the symbol to the * required list. Return 0 on success, 1 if caller needs to free() * datum. If symbols may not be declared here return -1. For duplicate * declarations return -2. For all else, including out of memory, * return -3.. Note that dest_value and datum_value might not be * restricted pointers. */ int require_symbol(uint32_t symbol_type, hashtab_key_t key, hashtab_datum_t datum, uint32_t * dest_value, uint32_t * datum_value) { avrule_decl_t *decl = stack_top->decl; int ret = create_symbol(symbol_type, key, datum, dest_value, SCOPE_REQ); if (ret < 0) { return ret; } if (ebitmap_set_bit(decl->required.scope + symbol_type, *datum_value - 1, 1)) { return -3; } stack_top->require_given = 1; return ret; } int add_perm_to_class(uint32_t perm_value, uint32_t class_value) { avrule_decl_t *decl = stack_top->decl; scope_index_t *scope; assert(perm_value >= 1); assert(class_value >= 1); scope = &decl->required; if (class_value > scope->class_perms_len) { uint32_t i; ebitmap_t *new_map = realloc(scope->class_perms_map, class_value * sizeof(*new_map)); if (new_map == NULL) { return -1; } scope->class_perms_map = new_map; for (i = scope->class_perms_len; i < class_value; i++) { ebitmap_init(scope->class_perms_map + i); } scope->class_perms_len = class_value; } if (ebitmap_set_bit(scope->class_perms_map + class_value - 1, perm_value - 1, 1)) { return -1; } return 0; } static int perm_destroy(hashtab_key_t key, hashtab_datum_t datum, void *p __attribute__ ((unused))) { if (key) free(key); free(datum); return 0; } static void class_datum_destroy(class_datum_t * cladatum) { if (cladatum != NULL) { hashtab_map(cladatum->permissions.table, perm_destroy, NULL); hashtab_destroy(cladatum->permissions.table); free(cladatum); } } int require_class(int pass) { char *class_id = queue_remove(id_queue); char *perm_id = NULL; class_datum_t *datum = NULL; perm_datum_t *perm = NULL; int ret; if (pass == 2) { free(class_id); while ((perm_id = queue_remove(id_queue)) != NULL) free(perm_id); return 0; } /* first add the class if it is not already there */ if (class_id == NULL) { yyerror("no class name for class definition?"); return -1; } if ((datum = calloc(1, sizeof(*datum))) == NULL || symtab_init(&datum->permissions, PERM_SYMTAB_SIZE)) { yyerror("Out of memory!"); class_datum_destroy(datum); return -1; } ret = require_symbol(SYM_CLASSES, class_id, datum, &datum->s.value, &datum->s.value); if (ret < 0) { print_error_msg(ret, SYM_CLASSES); free(class_id); class_datum_destroy(datum); return -1; } if (ret == 0) { /* a new class was added; reindex everything */ if (policydb_index_classes(policydbp)) { yyerror("Out of memory!"); return -1; } } else { class_datum_destroy(datum); datum = hashtab_search(policydbp->p_classes.table, class_id); assert(datum); /* the class datum should have existed */ free(class_id); } /* now add each of the permissions to this class's requirements */ while ((perm_id = queue_remove(id_queue)) != NULL) { int allocated = 0; /* Is the permission already in the table? */ perm = hashtab_search(datum->permissions.table, perm_id); if (!perm && datum->comdatum) perm = hashtab_search(datum->comdatum->permissions.table, perm_id); if (perm) { /* Yes, drop the name. */ free(perm_id); } else { /* No - allocate and insert an entry for it. */ if (policydbp->policy_type == POLICY_BASE) { yyerror2 ("Base policy - require of permission %s without prior declaration.", perm_id); free(perm_id); return -1; } if (datum->permissions.nprim >= PERM_SYMTAB_SIZE) { yyerror2("Class %s would have too many permissions " "to fit in an access vector with permission %s", policydbp->p_class_val_to_name[datum->s.value - 1], perm_id); free(perm_id); return -1; } allocated = 1; if ((perm = malloc(sizeof(*perm))) == NULL) { yyerror("Out of memory!"); free(perm_id); return -1; } memset(perm, 0, sizeof(*perm)); ret = hashtab_insert(datum->permissions.table, perm_id, perm); if (ret) { yyerror("Out of memory!"); free(perm_id); free(perm); return -1; } perm->s.value = datum->permissions.nprim + 1; } if (add_perm_to_class(perm->s.value, datum->s.value) == -1) { yyerror("Out of memory!"); return -1; } /* Update number of primitives if we allocated one. */ if (allocated) datum->permissions.nprim++; } return 0; } static int require_role_or_attribute(int pass, unsigned char isattr) { char *key = NULL; role_datum_t *role = NULL; int ret; if (pass == 2) { free(queue_remove(id_queue)); return 0; } ret = create_role(SCOPE_REQ, isattr, &role, &key); if (ret < 0) { return -1; } free(key); if (ret == 0) { ret = ebitmap_set_bit(&role->dominates, role->s.value - 1, 1); if (ret != 0) { yyerror("Out of memory"); return -1; } } else { role_datum_destroy(role); free(role); } return 0; } int require_role(int pass) { return require_role_or_attribute(pass, 0); } int require_attribute_role(int pass) { return require_role_or_attribute(pass, 1); } static int require_type_or_attribute(int pass, unsigned char isattr) { type_datum_t *type = NULL; int ret; if (pass == 2) { free(queue_remove(id_queue)); return 0; } ret = create_type(SCOPE_REQ, isattr, &type); if (ret < 0) { return -1; } return 0; } int require_type(int pass) { return require_type_or_attribute(pass, 0); } int require_attribute(int pass) { return require_type_or_attribute(pass, 1); } int require_user(int pass) { char *key = NULL; user_datum_t *user = NULL; int ret; if (pass == 1) { free(queue_remove(id_queue)); return 0; } ret = create_user(SCOPE_REQ, &user, &key); if (ret < 0) { return -1; } free(key); if (ret == 1) { user_datum_destroy(user); free(user); } return 0; } static int require_bool_tunable(int pass, int is_tunable) { char *id = queue_remove(id_queue); cond_bool_datum_t *booldatum = NULL; int retval; if (pass == 2) { free(id); return 0; } if (id == NULL) { yyerror("no boolean name"); return -1; } if ((booldatum = calloc(1, sizeof(*booldatum))) == NULL) { cond_destroy_bool(id, booldatum, NULL); yyerror("Out of memory!"); return -1; } if (is_tunable) booldatum->flags |= COND_BOOL_FLAGS_TUNABLE; retval = require_symbol(SYM_BOOLS, id, booldatum, &booldatum->s.value, &booldatum->s.value); if (retval != 0) { cond_destroy_bool(id, booldatum, NULL); if (retval < 0) { print_error_msg(retval, SYM_BOOLS); return -1; } } return 0; } int require_bool(int pass) { return require_bool_tunable(pass, 0); } int require_tunable(int pass) { return require_bool_tunable(pass, 1); } int require_sens(int pass) { char *id = queue_remove(id_queue); level_datum_t *level = NULL; int retval; if (pass == 2) { free(id); return 0; } if (!id) { yyerror("no sensitivity name"); return -1; } level = malloc(sizeof(level_datum_t)); if (!level) { free(id); yyerror("Out of memory!"); return -1; } level_datum_init(level); level->level = malloc(sizeof(mls_level_t)); if (!level->level) { free(id); level_datum_destroy(level); free(level); yyerror("Out of memory!"); return -1; } mls_level_init(level->level); retval = require_symbol(SYM_LEVELS, id, level, &level->level->sens, &level->level->sens); if (retval != 0) { free(id); mls_level_destroy(level->level); free(level->level); level_datum_destroy(level); free(level); if (retval < 0) { print_error_msg(retval, SYM_LEVELS); return -1; } } return 0; } int require_cat(int pass) { char *id = queue_remove(id_queue); cat_datum_t *cat = NULL; int retval; if (pass == 2) { free(id); return 0; } if (!id) { yyerror("no category name"); return -1; } cat = malloc(sizeof(cat_datum_t)); if (!cat) { free(id); yyerror("Out of memory!"); return -1; } cat_datum_init(cat); retval = require_symbol(SYM_CATS, id, cat, &cat->s.value, &cat->s.value); if (retval != 0) { free(id); cat_datum_destroy(cat); free(cat); if (retval < 0) { print_error_msg(retval, SYM_CATS); return -1; } } return 0; } static int is_scope_in_stack(const scope_datum_t * scope, const scope_stack_t * stack) { uint32_t i; if (stack == NULL) { return 0; /* no matching scope found */ } if (stack->type == 1) { const avrule_decl_t *decl = stack->decl; for (i = 0; i < scope->decl_ids_len; i++) { if (scope->decl_ids[i] == decl->decl_id) { return 1; } } } else { /* note that conditionals can't declare or require * symbols, so skip this level */ } /* not within scope of this stack, so try its parent */ return is_scope_in_stack(scope, stack->parent); } int is_id_in_scope(uint32_t symbol_type, const_hashtab_key_t id) { const scope_datum_t *scope = (scope_datum_t *) hashtab_search(policydbp->scope[symbol_type]. table, id); if (scope == NULL) { return 1; /* id is not known, so return success */ } return is_scope_in_stack(scope, stack_top); } static int is_perm_in_scope_index(uint32_t perm_value, uint32_t class_value, const scope_index_t * scope) { if (class_value > scope->class_perms_len) { return 1; } if (ebitmap_get_bit(scope->class_perms_map + class_value - 1, perm_value - 1)) { return 1; } return 0; } static int is_perm_in_stack(uint32_t perm_value, uint32_t class_value, const scope_stack_t * stack) { if (stack == NULL) { return 0; /* no matching scope found */ } if (stack->type == 1) { avrule_decl_t *decl = stack->decl; if (is_perm_in_scope_index (perm_value, class_value, &decl->required) || is_perm_in_scope_index(perm_value, class_value, &decl->declared)) { return 1; } } else { /* note that conditionals can't declare or require * symbols, so skip this level */ } /* not within scope of this stack, so try its parent */ return is_perm_in_stack(perm_value, class_value, stack->parent); } int is_perm_in_scope(const_hashtab_key_t perm_id, const_hashtab_key_t class_id) { const class_datum_t *cladatum = (class_datum_t *) hashtab_search(policydbp->p_classes.table, class_id); const perm_datum_t *perdatum; if (cladatum == NULL) { return 1; } perdatum = (perm_datum_t *) hashtab_search(cladatum->permissions.table, perm_id); if (perdatum == NULL) { return 1; } return is_perm_in_stack(perdatum->s.value, cladatum->s.value, stack_top); } cond_list_t *get_current_cond_list(cond_list_t * cond) { /* FIX ME: do something different here if in a nested * conditional? */ avrule_decl_t *decl = stack_top->decl; return get_decl_cond_list(policydbp, decl, cond); } /* Append the new conditional node to the existing ones. During * expansion the list will be reversed -- i.e., the last AV rule will * be the first one listed in the policy. This matches the behavior * of the upstream compiler. */ void append_cond_list(cond_list_t * cond) { cond_list_t *old_cond = get_current_cond_list(cond); avrule_t *tmp; assert(old_cond != NULL); /* probably out of memory */ if (old_cond->avtrue_list == NULL) { old_cond->avtrue_list = cond->avtrue_list; } else { for (tmp = old_cond->avtrue_list; tmp->next != NULL; tmp = tmp->next) ; tmp->next = cond->avtrue_list; } if (old_cond->avfalse_list == NULL) { old_cond->avfalse_list = cond->avfalse_list; } else { for (tmp = old_cond->avfalse_list; tmp->next != NULL; tmp = tmp->next) ; tmp->next = cond->avfalse_list; } old_cond->flags |= cond->flags; } void append_avrule(avrule_t * avrule) { avrule_decl_t *decl = stack_top->decl; /* currently avrules follow a completely different code path * for handling avrules and compute types * (define_cond_avrule_te_avtab, define_cond_compute_type); * therefore there ought never be a conditional on top of the * scope stack */ assert(stack_top->type == 1); if (stack_top->last_avrule == NULL) { decl->avrules = avrule; } else { stack_top->last_avrule->next = avrule; } stack_top->last_avrule = avrule; } /* this doesn't actually append, but really prepends it */ void append_role_trans(role_trans_rule_t * role_tr_rules) { avrule_decl_t *decl = stack_top->decl; /* role transitions are not allowed within conditionals */ assert(stack_top->type == 1); role_tr_rules->next = decl->role_tr_rules; decl->role_tr_rules = role_tr_rules; } /* this doesn't actually append, but really prepends it */ void append_role_allow(role_allow_rule_t * role_allow_rules) { avrule_decl_t *decl = stack_top->decl; /* role allows are not allowed within conditionals */ assert(stack_top->type == 1); role_allow_rules->next = decl->role_allow_rules; decl->role_allow_rules = role_allow_rules; } /* this doesn't actually append, but really prepends it */ void append_filename_trans(filename_trans_rule_t * filename_trans_rules) { avrule_decl_t *decl = stack_top->decl; /* filename transitions are not allowed within conditionals */ assert(stack_top->type == 1); filename_trans_rules->next = decl->filename_trans_rules; decl->filename_trans_rules = filename_trans_rules; } /* this doesn't actually append, but really prepends it */ void append_range_trans(range_trans_rule_t * range_tr_rules) { avrule_decl_t *decl = stack_top->decl; /* range transitions are not allowed within conditionals */ assert(stack_top->type == 1); range_tr_rules->next = decl->range_tr_rules; decl->range_tr_rules = range_tr_rules; } int begin_optional(int pass) { avrule_block_t *block = NULL; avrule_decl_t *decl; if (pass == 1) { /* allocate a new avrule block for this optional block */ if ((block = avrule_block_create()) == NULL || (decl = avrule_decl_create(next_decl_id)) == NULL) { goto cleanup; } block->flags |= AVRULE_OPTIONAL; block->branch_list = decl; last_block->next = block; } else { /* select the next block from the chain built during pass 1 */ block = last_block->next; assert(block != NULL && block->branch_list != NULL && block->branch_list->decl_id == next_decl_id); decl = block->branch_list; } if (push_stack(1, block, decl) == -1) { goto cleanup; } stack_top->last_avrule = NULL; last_block = block; next_decl_id++; return 0; cleanup: yyerror("Out of memory!"); avrule_block_destroy(block); return -1; } int end_optional(int pass __attribute__ ((unused))) { /* once nested conditionals are allowed, do the stack unfolding here */ pop_stack(); return 0; } int begin_optional_else(int pass) { avrule_decl_t *decl; assert(stack_top->type == 1 && stack_top->in_else == 0); if (pass == 1) { /* allocate a new declaration and add it to the * current chain */ if ((decl = avrule_decl_create(next_decl_id)) == NULL) { yyerror("Out of memory!"); return -1; } stack_top->decl->next = decl; } else { /* pick the (hopefully last) declaration of this avrule block, built from pass 1 */ decl = stack_top->decl->next; assert(decl != NULL && decl->next == NULL && decl->decl_id == next_decl_id); } stack_top->in_else = 1; stack_top->decl = decl; stack_top->last_avrule = NULL; stack_top->require_given = 0; next_decl_id++; return 0; } static int copy_requirements(avrule_decl_t * dest, const scope_stack_t * stack) { uint32_t i; if (stack == NULL) { return 0; } if (stack->type == 1) { const scope_index_t *src_scope = &stack->decl->required; scope_index_t *dest_scope = &dest->required; for (i = 0; i < SYM_NUM; i++) { const ebitmap_t *src_bitmap = &src_scope->scope[i]; ebitmap_t *dest_bitmap = &dest_scope->scope[i]; if (ebitmap_union(dest_bitmap, src_bitmap)) { yyerror("Out of memory!"); return -1; } } /* now copy class permissions */ if (src_scope->class_perms_len > dest_scope->class_perms_len) { ebitmap_t *new_map = realloc(dest_scope->class_perms_map, src_scope->class_perms_len * sizeof(*new_map)); if (new_map == NULL) { yyerror("Out of memory!"); return -1; } dest_scope->class_perms_map = new_map; for (i = dest_scope->class_perms_len; i < src_scope->class_perms_len; i++) { ebitmap_init(dest_scope->class_perms_map + i); } dest_scope->class_perms_len = src_scope->class_perms_len; } for (i = 0; i < src_scope->class_perms_len; i++) { const ebitmap_t *src_bitmap = &src_scope->class_perms_map[i]; ebitmap_t *dest_bitmap = &dest_scope->class_perms_map[i]; if (ebitmap_union(dest_bitmap, src_bitmap)) { yyerror("Out of memory!"); return -1; } } } return copy_requirements(dest, stack->parent); } /* During pass 1, check that at least one thing was required within * this block, for those places where a REQUIRED is necessary. During * pass 2, have this block inherit its parents' requirements. Return * 0 on success, -1 on failure. */ int end_avrule_block(int pass) { avrule_decl_t *decl = stack_top->decl; assert(stack_top->type == 1); if (pass == 2) { /* this avrule_decl inherits all of its parents' * requirements */ if (copy_requirements(decl, stack_top->parent) == -1) { return -1; } return 0; } if (!stack_top->in_else && !stack_top->require_given) { if (policydbp->policy_type == POLICY_BASE && stack_top->parent != NULL) { /* if this is base no require should be in the global block */ return 0; } else { /* non-ELSE branches must have at least one thing required */ yyerror("This block has no require section."); return -1; } } return 0; } /* Push a new scope on to the stack and update the 'last' pointer. * Return 0 on success, -1 if out * of memory. */ static int push_stack(int stack_type, ...) { scope_stack_t *s = calloc(1, sizeof(*s)); va_list ap; if (s == NULL) { return -1; } va_start(ap, stack_type); switch (s->type = stack_type) { case 1:{ va_arg(ap, avrule_block_t *); s->decl = va_arg(ap, avrule_decl_t *); break; } case 2:{ va_arg(ap, cond_list_t *); break; } default: /* invalid stack type given */ assert(0); } va_end(ap); s->parent = stack_top; stack_top = s; return 0; } /* Pop off the most recently added from the stack. Update the 'last' * pointer. */ static void pop_stack(void) { scope_stack_t *parent; assert(stack_top != NULL); parent = stack_top->parent; free(stack_top); stack_top = parent; } #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION void module_compiler_reset(void) { while (stack_top) pop_stack(); last_block = NULL; next_decl_id = 1; } #endif checkpolicy-3.8.1/module_compiler.h000066400000000000000000000107761476211737200173750ustar00rootroot00000000000000/* Author : Joshua Brindle * Karl MacMillan * Jason Tang * Added support for binary policy modules * * Copyright (C) 2004 - 2005 Tresys Technology, LLC * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ #ifndef MODULE_COMPILER_H #define MODULE_COMPILER_H #include /* Called when checkpolicy begins to parse a policy -- either at the * very beginning for a kernel/base policy, or after the module header * for policy modules. Initialize the memory structures within. * Return 0 on success, -1 on error. */ int define_policy(int pass, int module_header_given); /* Declare a symbol declaration to the current avrule_decl. Check * that insertion is allowed here and that the symbol does not already * exist. Returns 0 on success, 1 if symbol was already there (caller * needs to free() the datum), -1 if declarations not allowed, -2 for * duplicate declarations, -3 for all else. */ int declare_symbol(uint32_t symbol_type, hashtab_key_t key, hashtab_datum_t datum, uint32_t * dest_value, const uint32_t * datum_value); role_datum_t *declare_role(unsigned char isattr); type_datum_t *declare_type(unsigned char primary, unsigned char isattr); user_datum_t *declare_user(void); type_datum_t *get_local_type(char *id, uint32_t value, unsigned char isattr); role_datum_t *get_local_role(char *id, uint32_t value, unsigned char isattr); /* Add a symbol to the current avrule_block's require section. Note * that a module may not both declare and require the same symbol. * Returns 0 on success, -1 on error. */ int require_symbol(uint32_t symbol_type, hashtab_key_t key, hashtab_datum_t datum, uint32_t * dest_value, uint32_t * datum_value); /* Enable a permission for a class within the current avrule_decl. * Return 0 on success, -1 if out of memory. */ int add_perm_to_class(uint32_t perm_value, uint32_t class_value); /* Functions called from REQUIRE blocks. Add the first symbol on the * id_queue to this avrule_decl's scope if not already there. * c.f. require_symbol(). */ int require_class(int pass); int require_role(int pass); int require_type(int pass); int require_attribute(int pass); int require_attribute_role(int pass); int require_user(int pass); int require_bool(int pass); int require_tunable(int pass); int require_sens(int pass); int require_cat(int pass); /* Check if an identifier is within the scope of the current * declaration or any of its parents. Return 1 if it is, 0 if not. * If the identifier is not known at all then return 1 (truth). */ int is_id_in_scope(uint32_t symbol_type, const_hashtab_key_t id); /* Check if a particular permission is within the scope of the current * declaration or any of its parents. Return 1 if it is, 0 if not. * If the identifier is not known at all then return 1 (truth). */ int is_perm_in_scope(const_hashtab_key_t perm_id, const_hashtab_key_t class_id); /* Search the current avrules block for a conditional with the same * expression as 'cond'. If the conditional does not exist then * create one. Either way, return the conditional. */ cond_list_t *get_current_cond_list(cond_list_t * cond); /* Append rule to the current avrule_block. */ void append_cond_list(cond_list_t * cond); void append_avrule(avrule_t * avrule); void append_role_trans(role_trans_rule_t * role_tr_rules); void append_role_allow(role_allow_rule_t * role_allow_rules); void append_range_trans(range_trans_rule_t * range_tr_rules); void append_filename_trans(filename_trans_rule_t * filename_trans_rules); /* Create a new optional block and add it to the global policy. * During the second pass resolve the block's requirements. Return 0 * on success, -1 on error. */ int begin_optional(int pass); int end_optional(int pass); /* ELSE blocks are similar to normal blocks with the following two * limitations: * - no declarations are allowed within else branches * - no REQUIRES are allowed; the else branch inherits the parent's * requirements */ int begin_optional_else(int pass); /* Called whenever existing an avrule block. Check that the block had * a non-empty REQUIRE section. If so pop the block off of the scop * stack and return 0. If not then send an error to yyerror and * return -1. */ int end_avrule_block(int pass); #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION void module_compiler_reset(void); #endif #endif checkpolicy-3.8.1/parse_util.c000066400000000000000000000043261476211737200163520ustar00rootroot00000000000000/* * Author: Karl MacMillan * * Copyright (C) 2006 Tresys Technology, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "parse_util.h" #include "queue.h" /* these are defined in policy_parse.y and are needed for read_source_policy */ extern FILE *yyin; extern void init_parser(int pass, const char *input_name); extern int yyparse(void); extern void yyrestart(FILE *); extern int yylex_destroy(void); extern queue_t id_queue; extern unsigned int policydb_errors; extern policydb_t *policydbp; extern int mlspol; int read_source_policy(policydb_t * p, const char *file, const char *progname) { int rc = -1; yyin = fopen(file, "r"); if (!yyin) { fprintf(stderr, "%s: unable to open %s: %s\n", progname, file, strerror(errno)); return -1; } id_queue = queue_create(); if (id_queue == NULL) { fprintf(stderr, "%s: out of memory!\n", progname); goto cleanup; } mlspol = p->mls; policydbp = p; policydbp->name = strdup(file); if (!policydbp->name) { fprintf(stderr, "%s: out of memory!\n", progname); goto cleanup; } init_parser(1, file); if (yyparse() || policydb_errors) { fprintf(stderr, "%s: error(s) encountered while parsing configuration\n", progname); goto cleanup; } rewind(yyin); init_parser(2, file); yyrestart(yyin); if (yyparse() || policydb_errors) { fprintf(stderr, "%s: error(s) encountered while parsing configuration\n", progname); goto cleanup; } rc = 0; cleanup: queue_destroy(id_queue); fclose(yyin); yylex_destroy(); return rc; } checkpolicy-3.8.1/parse_util.h000066400000000000000000000025251476211737200163560ustar00rootroot00000000000000/* * Author: Karl MacMillan * * Copyright (C) 2006 Tresys Technology, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* Utility functions shared by checkpolicy and checkmodule */ #ifndef __PARSE_UTIL_H__ #define __PARSE_UTIL_H__ #include /* Read a source policy and populate the policydb passed in. The * policydb must already have been created and configured (e.g., * expected policy type set. The string progname is used for * error messages. No checking of assertions, hierarchy, etc. * is done. */ int read_source_policy(policydb_t * p, const char *file, const char *progname); #endif checkpolicy-3.8.1/policy_define.c000066400000000000000000003763651476211737200170330ustar00rootroot00000000000000/* * Author : Stephen Smalley, */ /* * Updated: Trusted Computer Solutions, Inc. * * Support for enhanced MLS infrastructure. * * Updated: David Caplan, * * Added conditional policy language extensions * * Updated: Joshua Brindle * Karl MacMillan * Jason Tang * * Added support for binary policy modules * * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * Copyright (C) 2003 - 2008 Tresys Technology, LLC * Copyright (C) 2007 Red Hat Inc. * Copyright (C) 2017 Mellanox Techonologies Inc. * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ /* FLASK */ #include #include #include #include #include #include #include #include #include #ifndef IPPROTO_DCCP #define IPPROTO_DCCP 33 #endif #ifndef IPPROTO_SCTP #define IPPROTO_SCTP 132 #endif #include #include #include #include #include #include #include #include #include #include #include "queue.h" #include "module_compiler.h" #include "policy_define.h" extern void init_parser(int pass_number, const char *input_name); __attribute__ ((format(printf, 1, 2))) extern void yyerror2(const char *fmt, ...); policydb_t *policydbp; queue_t id_queue = 0; unsigned int pass; int mlspol = 0; extern unsigned long policydb_lineno; extern unsigned long source_lineno; extern unsigned int policydb_errors; extern char source_file[PATH_MAX]; extern void set_source_file(const char *name); extern int yywarn(const char *msg); extern int yyerror(const char *msg); /* initialize all of the state variables for the scanner/parser */ void init_parser(int pass_number, const char *input_name) { policydb_lineno = 1; source_lineno = 1; policydb_errors = 0; pass = pass_number; set_source_file(input_name); queue_clear(id_queue); } void yyerror2(const char *fmt, ...) { char errormsg[256]; va_list ap; va_start(ap, fmt); vsnprintf(errormsg, sizeof(errormsg), fmt, ap); yyerror(errormsg); va_end(ap); } __attribute__ ((format(printf, 1, 2))) static void yywarn2(const char *fmt, ...) { char warnmsg[256]; va_list ap; va_start(ap, fmt); vsnprintf(warnmsg, sizeof(warnmsg), fmt, ap); yywarn(warnmsg); va_end(ap); } int insert_separator(int push) { int error; if (push) error = queue_push(id_queue, 0); else error = queue_insert(id_queue, 0); if (error) { yyerror("queue overflow"); return -1; } return 0; } int insert_id(const char *id, int push) { char *newid = 0; int error; newid = strdup(id); if (!newid) { yyerror("out of memory"); return -1; } if (push) error = queue_push(id_queue, (queue_element_t) newid); else error = queue_insert(id_queue, (queue_element_t) newid); if (error) { yyerror("queue overflow"); free(newid); return -1; } return 0; } /* If the identifier has a dot within it and that its first character is not a dot then return 1, else return 0. */ static int id_has_dot(const char *id) { if (strchr(id, '.') >= id + 1) { return 1; } return 0; } int define_class(void) { char *id = NULL; class_datum_t *datum = NULL; int ret = -1; uint32_t value; if (pass == 2) { id = queue_remove(id_queue); free(id); return 0; } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no class name for class definition?"); return -1; } datum = (class_datum_t *) malloc(sizeof(class_datum_t)); if (!datum) { yyerror("out of memory"); goto cleanup; } memset(datum, 0, sizeof(class_datum_t)); ret = declare_symbol(SYM_CLASSES, id, datum, &value, &value); switch (ret) { case -3:{ yyerror("Out of memory!"); goto cleanup; } case -2:{ yyerror2("duplicate declaration of class %s", id); goto cleanup; } case -1:{ yyerror("could not declare class here"); goto cleanup; } case 0: { break; } case 1:{ goto cleanup; } default:{ assert(0); /* should never get here */ } } datum->s.value = value; return 0; cleanup: free(id); free(datum); return ret == 1 ? 0 : -1; } int define_permissive(void) { char *type = NULL; struct type_datum *t; int rc = 0; type = queue_remove(id_queue); if (!type) { yyerror2("forgot to include type in permissive definition?"); rc = -1; goto out; } if (pass == 1) goto out; if (!is_id_in_scope(SYM_TYPES, type)) { yyerror2("type %s is not within scope", type); rc = -1; goto out; } t = hashtab_search(policydbp->p_types.table, type); if (!t) { yyerror2("type is not defined: %s", type); rc = -1; goto out; } if (t->flavor == TYPE_ATTRIB) { yyerror2("attributes may not be permissive: %s", type); rc = -1; goto out; } t->flags |= TYPE_FLAGS_PERMISSIVE; out: free(type); return rc; } int define_polcap(void) { char *id = 0; int capnum; if (pass == 2) { id = queue_remove(id_queue); free(id); return 0; } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no capability name for policycap definition?"); goto bad; } /* Check for valid cap name -> number mapping */ capnum = sepol_polcap_getnum(id); if (capnum < 0) { yyerror2("invalid policy capability name %s", id); goto bad; } /* Store it */ if (ebitmap_set_bit(&policydbp->policycaps, capnum, TRUE)) { yyerror("out of memory"); goto bad; } free(id); return 0; bad: free(id); return -1; } int define_initial_sid(void) { char *id = 0; ocontext_t *newc = 0, *c, *head; if (pass == 2) { id = queue_remove(id_queue); free(id); return 0; } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no sid name for SID definition?"); return -1; } newc = (ocontext_t *) malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); goto bad; } memset(newc, 0, sizeof(ocontext_t)); newc->u.name = id; context_init(&newc->context[0]); head = policydbp->ocontexts[OCON_ISID]; for (c = head; c; c = c->next) { if (!strcmp(newc->u.name, c->u.name)) { yyerror2("duplicate initial SID %s", id); goto bad; } } if (head) { newc->sid[0] = head->sid[0] + 1; } else { newc->sid[0] = 1; } newc->next = head; policydbp->ocontexts[OCON_ISID] = newc; return 0; bad: if (id) free(id); if (newc) free(newc); return -1; } static int read_classes(ebitmap_t *e_classes) { char *id; class_datum_t *cladatum; while ((id = queue_remove(id_queue))) { if (!is_id_in_scope(SYM_CLASSES, id)) { yyerror2("class %s is not within scope", id); free(id); return -1; } cladatum = hashtab_search(policydbp->p_classes.table, id); if (!cladatum) { yyerror2("unknown class %s", id); free(id); return -1; } free(id); if (ebitmap_set_bit(e_classes, cladatum->s.value - 1, TRUE)) { yyerror("Out of memory"); return -1; } } return 0; } int define_default_user(int which) { char *id; class_datum_t *cladatum; if (pass == 1) { while ((id = queue_remove(id_queue))) free(id); return 0; } while ((id = queue_remove(id_queue))) { if (!is_id_in_scope(SYM_CLASSES, id)) { yyerror2("class %s is not within scope", id); free(id); return -1; } cladatum = hashtab_search(policydbp->p_classes.table, id); if (!cladatum) { yyerror2("unknown class %s", id); free(id); return -1; } if (cladatum->default_user && cladatum->default_user != which) { yyerror2("conflicting default user information for class %s", id); free(id); return -1; } cladatum->default_user = which; free(id); } return 0; } int define_default_role(int which) { char *id; class_datum_t *cladatum; if (pass == 1) { while ((id = queue_remove(id_queue))) free(id); return 0; } while ((id = queue_remove(id_queue))) { if (!is_id_in_scope(SYM_CLASSES, id)) { yyerror2("class %s is not within scope", id); free(id); return -1; } cladatum = hashtab_search(policydbp->p_classes.table, id); if (!cladatum) { yyerror2("unknown class %s", id); free(id); return -1; } if (cladatum->default_role && cladatum->default_role != which) { yyerror2("conflicting default role information for class %s", id); free(id); return -1; } cladatum->default_role = which; free(id); } return 0; } int define_default_type(int which) { char *id; class_datum_t *cladatum; if (pass == 1) { while ((id = queue_remove(id_queue))) free(id); return 0; } while ((id = queue_remove(id_queue))) { if (!is_id_in_scope(SYM_CLASSES, id)) { yyerror2("class %s is not within scope", id); free(id); return -1; } cladatum = hashtab_search(policydbp->p_classes.table, id); if (!cladatum) { yyerror2("unknown class %s", id); free(id); return -1; } if (cladatum->default_type && cladatum->default_type != which) { yyerror2("conflicting default type information for class %s", id); free(id); return -1; } cladatum->default_type = which; free(id); } return 0; } int define_default_range(int which) { char *id; class_datum_t *cladatum; if (pass == 1) { while ((id = queue_remove(id_queue))) free(id); return 0; } while ((id = queue_remove(id_queue))) { if (!is_id_in_scope(SYM_CLASSES, id)) { yyerror2("class %s is not within scope", id); free(id); return -1; } cladatum = hashtab_search(policydbp->p_classes.table, id); if (!cladatum) { yyerror2("unknown class %s", id); free(id); return -1; } if (cladatum->default_range && cladatum->default_range != which) { yyerror2("conflicting default range information for class %s", id); free(id); return -1; } cladatum->default_range = which; free(id); } return 0; } int define_common_perms(void) { char *id = 0, *perm = 0; common_datum_t *comdatum = 0; perm_datum_t *perdatum = 0; int ret; if (pass == 2) { while ((id = queue_remove(id_queue))) free(id); return 0; } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no common name for common perm definition?"); return -1; } comdatum = hashtab_search(policydbp->p_commons.table, id); if (comdatum) { yyerror2("duplicate declaration for common %s", id); free(id); return -1; } comdatum = (common_datum_t *) malloc(sizeof(common_datum_t)); if (!comdatum) { yyerror("out of memory"); goto bad; } memset(comdatum, 0, sizeof(common_datum_t)); ret = hashtab_insert(policydbp->p_commons.table, (hashtab_key_t) id, (hashtab_datum_t) comdatum); if (ret == SEPOL_EEXIST) { yyerror("duplicate common definition"); goto bad; } if (ret == SEPOL_ENOMEM) { yyerror("hash table overflow"); goto bad; } comdatum->s.value = policydbp->p_commons.nprim + 1; if (symtab_init(&comdatum->permissions, PERM_SYMTAB_SIZE)) { yyerror("out of memory"); goto bad; } policydbp->p_commons.nprim++; while ((perm = queue_remove(id_queue))) { perdatum = (perm_datum_t *) malloc(sizeof(perm_datum_t)); if (!perdatum) { yyerror("out of memory"); goto bad_perm; } memset(perdatum, 0, sizeof(perm_datum_t)); perdatum->s.value = comdatum->permissions.nprim + 1; if (perdatum->s.value > (sizeof(sepol_access_vector_t) * 8)) { yyerror2 ("too many permissions (%d) to fit in an access vector", perdatum->s.value); goto bad_perm; } ret = hashtab_insert(comdatum->permissions.table, (hashtab_key_t) perm, (hashtab_datum_t) perdatum); if (ret == SEPOL_EEXIST) { yyerror2("duplicate permission %s in common %s", perm, id); goto bad_perm; } if (ret == SEPOL_ENOMEM) { yyerror("hash table overflow"); goto bad_perm; } comdatum->permissions.nprim++; } return 0; bad: if (id) free(id); if (comdatum) free(comdatum); return -1; bad_perm: if (perm) free(perm); if (perdatum) free(perdatum); return -1; } int define_av_perms(int inherits) { char *id; class_datum_t *cladatum; common_datum_t *comdatum; perm_datum_t *perdatum = 0, *perdatum2 = 0; int ret; if (pass == 2) { while ((id = queue_remove(id_queue))) free(id); return 0; } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no tclass name for av perm definition?"); return -1; } cladatum = (class_datum_t *) hashtab_search(policydbp->p_classes.table, (hashtab_key_t) id); if (!cladatum) { yyerror2("class %s is not defined", id); goto bad; } if (cladatum->comdatum || cladatum->permissions.nprim) { yyerror2("duplicate access vector definition for class %s", id); goto bad; } free(id); id = NULL; if (symtab_init(&cladatum->permissions, PERM_SYMTAB_SIZE)) { yyerror("out of memory"); return -1; } if (inherits) { id = (char *)queue_remove(id_queue); if (!id) { yyerror ("no inherits name for access vector definition?"); return -1; } comdatum = (common_datum_t *) hashtab_search(policydbp->p_commons. table, (hashtab_key_t) id); if (!comdatum) { yyerror2("common %s is not defined", id); goto bad; } cladatum->comkey = id; cladatum->comdatum = comdatum; /* * Class-specific permissions start with values * after the last common permission. */ cladatum->permissions.nprim += comdatum->permissions.nprim; } while ((id = queue_remove(id_queue))) { perdatum = (perm_datum_t *) malloc(sizeof(perm_datum_t)); if (!perdatum) { yyerror("out of memory"); goto bad; } memset(perdatum, 0, sizeof(perm_datum_t)); perdatum->s.value = ++cladatum->permissions.nprim; if (perdatum->s.value > (sizeof(sepol_access_vector_t) * 8)) { yyerror2 ("too many permissions (%d) to fit in an access vector", perdatum->s.value); goto bad; } if (inherits) { /* * Class-specific permissions and * common permissions exist in the same * name space. */ perdatum2 = (perm_datum_t *) hashtab_search(cladatum->comdatum-> permissions.table, (hashtab_key_t) id); if (perdatum2) { yyerror2("permission %s conflicts with an " "inherited permission", id); goto bad; } } ret = hashtab_insert(cladatum->permissions.table, (hashtab_key_t) id, (hashtab_datum_t) perdatum); if (ret == SEPOL_EEXIST) { yyerror2("duplicate permission %s", id); goto bad; } if (ret == SEPOL_ENOMEM) { yyerror("hash table overflow"); goto bad; } if (add_perm_to_class(perdatum->s.value, cladatum->s.value)) { yyerror("out of memory"); goto bad; } } return 0; bad: if (id) free(id); if (perdatum) free(perdatum); return -1; } int define_sens(void) { char *id; mls_level_t *level = 0; level_datum_t *datum = 0, *aliasdatum = 0; int ret; uint32_t value; /* dummy variable -- its value is never used */ if (!mlspol) { yyerror("sensitivity definition in non-MLS configuration"); return -1; } if (pass == 2) { while ((id = queue_remove(id_queue))) free(id); return 0; } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no sensitivity name for sensitivity definition?"); return -1; } if (id_has_dot(id)) { yyerror2("sensitivity identifier %s may not contain periods", id); goto bad; } level = (mls_level_t *) malloc(sizeof(mls_level_t)); if (!level) { yyerror("out of memory"); goto bad; } mls_level_init(level); level->sens = 0; /* actual value set in define_dominance */ ebitmap_init(&level->cat); /* actual value set in define_level */ datum = (level_datum_t *) malloc(sizeof(level_datum_t)); if (!datum) { yyerror("out of memory"); goto bad; } level_datum_init(datum); datum->isalias = FALSE; datum->level = level; datum->notdefined = TRUE; ret = declare_symbol(SYM_LEVELS, id, datum, &value, &value); switch (ret) { case -3:{ yyerror("Out of memory!"); goto bad; } case -2:{ yyerror2("duplicate declaration of sensitivity level %s", id); goto bad; } case -1:{ yyerror2("could not declare sensitivity level %s here", id); goto bad; } case 0: { break; } case 1:{ level_datum_destroy(datum); free(datum); free(id); break; } default:{ assert(0); /* should never get here */ } } while ((id = queue_remove(id_queue))) { if (id_has_dot(id)) { yyerror2("sensitivity alias %s may not contain periods", id); free(id); return -1; } aliasdatum = (level_datum_t *) malloc(sizeof(level_datum_t)); if (!aliasdatum) { yyerror("out of memory"); free(id); return -1; } level_datum_init(aliasdatum); aliasdatum->isalias = TRUE; aliasdatum->level = level; aliasdatum->notdefined = TRUE; ret = declare_symbol(SYM_LEVELS, id, aliasdatum, NULL, &value); switch (ret) { case -3:{ yyerror("Out of memory!"); goto bad_alias; } case -2:{ yyerror2 ("duplicate declaration of sensitivity alias %s", id); goto bad_alias; } case -1:{ yyerror2 ("could not declare sensitivity alias %s here", id); goto bad_alias; } case 0: { break; } case 1:{ level_datum_destroy(aliasdatum); free(aliasdatum); free(id); break; } default:{ assert(0); /* should never get here */ } } } return 0; bad: if (id) free(id); if (level) free(level); if (datum) { level_datum_destroy(datum); free(datum); } return -1; bad_alias: if (id) free(id); if (aliasdatum) { level_datum_destroy(aliasdatum); free(aliasdatum); } return -1; } int define_dominance(void) { level_datum_t *datum; uint32_t order; char *id; if (!mlspol) { yyerror("dominance definition in non-MLS configuration"); return -1; } if (pass == 2) { while ((id = queue_remove(id_queue))) free(id); return 0; } order = 0; while ((id = (char *)queue_remove(id_queue))) { datum = (level_datum_t *) hashtab_search(policydbp->p_levels.table, (hashtab_key_t) id); if (!datum) { yyerror2("unknown sensitivity %s used in dominance " "definition", id); free(id); return -1; } if (datum->level->sens != 0) { yyerror2("sensitivity %s occurs multiply in dominance " "definition", id); free(id); return -1; } datum->level->sens = ++order; /* no need to keep sensitivity name */ free(id); } if (order != policydbp->p_levels.nprim) { yyerror ("all sensitivities must be specified in dominance definition"); return -1; } return 0; } int define_category(void) { char *id; cat_datum_t *datum = 0, *aliasdatum = 0; int ret; uint32_t value; if (!mlspol) { yyerror("category definition in non-MLS configuration"); return -1; } if (pass == 2) { while ((id = queue_remove(id_queue))) free(id); return 0; } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no category name for category definition?"); return -1; } if (id_has_dot(id)) { yyerror2("category identifier %s may not contain periods", id); goto bad; } datum = (cat_datum_t *) malloc(sizeof(cat_datum_t)); if (!datum) { yyerror("out of memory"); goto bad; } cat_datum_init(datum); datum->isalias = FALSE; ret = declare_symbol(SYM_CATS, id, datum, &value, &value); switch (ret) { case -3:{ yyerror("Out of memory!"); goto bad; } case -2:{ yyerror2("duplicate declaration of category %s", id); goto bad; } case -1:{ yyerror2("could not declare category %s here", id); goto bad; } case 0:{ datum->s.value = value; break; } case 1:{ cat_datum_destroy(datum); free(datum); free(id); break; } default:{ assert(0); /* should never get here */ } } while ((id = queue_remove(id_queue))) { if (id_has_dot(id)) { yyerror2("category alias %s may not contain periods", id); free(id); return -1; } aliasdatum = (cat_datum_t *) malloc(sizeof(cat_datum_t)); if (!aliasdatum) { yyerror("out of memory"); free(id); return -1; } cat_datum_init(aliasdatum); aliasdatum->isalias = TRUE; aliasdatum->s.value = value; ret = declare_symbol(SYM_CATS, id, aliasdatum, NULL, &value); switch (ret) { case -3:{ yyerror("Out of memory!"); goto bad_alias; } case -2:{ yyerror2 ("duplicate declaration of category alias %s", id); goto bad_alias; } case -1:{ yyerror2 ("could not declare category alias %s here", id); goto bad_alias; } case 0:{ break; } case 1:{ cat_datum_destroy(aliasdatum); free(aliasdatum); free(id); break; } default:{ assert(0); /* should never get here */ } } } return 0; bad: if (id) free(id); if (datum) { cat_datum_destroy(datum); free(datum); } return -1; bad_alias: if (id) free(id); if (aliasdatum) { cat_datum_destroy(aliasdatum); free(aliasdatum); } return -1; } static int clone_level(hashtab_key_t key __attribute__ ((unused)), hashtab_datum_t datum, void *arg) { level_datum_t *levdatum = (level_datum_t *) datum; mls_level_t *level = (mls_level_t *) arg, *newlevel; if (levdatum->notdefined && levdatum->level == level) { if (!levdatum->isalias) { levdatum->notdefined = FALSE; return 0; } newlevel = (mls_level_t *) malloc(sizeof(mls_level_t)); if (!newlevel) return -1; if (mls_level_cpy(newlevel, level)) { free(newlevel); return -1; } levdatum->level = newlevel; levdatum->notdefined = FALSE; } return 0; } int define_level(void) { char *id; level_datum_t *levdatum; if (!mlspol) { yyerror("level definition in non-MLS configuration"); return -1; } if (pass == 2) { while ((id = queue_remove(id_queue))) free(id); return 0; } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no level name for level definition?"); return -1; } levdatum = (level_datum_t *) hashtab_search(policydbp->p_levels.table, (hashtab_key_t) id); if (!levdatum) { yyerror2("unknown sensitivity %s used in level definition", id); free(id); return -1; } if (ebitmap_length(&levdatum->level->cat)) { yyerror2("sensitivity %s used in multiple level definitions", id); free(id); return -1; } free(id); while ((id = queue_remove(id_queue))) { cat_datum_t *cdatum; uint32_t range_start, range_end, i; if (id_has_dot(id)) { char *id_start = id; char *id_end = strchr(id, '.'); *(id_end++) = '\0'; cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats. table, (hashtab_key_t) id_start); if (!cdatum) { yyerror2("unknown category %s", id_start); free(id); return -1; } range_start = cdatum->s.value - 1; cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats. table, (hashtab_key_t) id_end); if (!cdatum) { yyerror2("unknown category %s", id_end); free(id); return -1; } range_end = cdatum->s.value - 1; if (range_end < range_start) { yyerror2("category range %d-%d is invalid", range_start, range_end); free(id); return -1; } } else { cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats. table, (hashtab_key_t) id); if (!cdatum) { yyerror2("unknown category %s", id); free(id); return -1; } range_start = range_end = cdatum->s.value - 1; } for (i = range_start; i <= range_end; i++) { if (ebitmap_set_bit(&levdatum->level->cat, i, TRUE)) { yyerror("out of memory"); free(id); return -1; } } free(id); } if (hashtab_map (policydbp->p_levels.table, clone_level, levdatum->level)) { yyerror("out of memory"); return -1; } return 0; } int define_attrib(void) { if (pass == 2) { free(queue_remove(id_queue)); return 0; } if (declare_type(TRUE, TRUE) == NULL) { return -1; } return 0; } int expand_attrib(void) { char *id; ebitmap_t attrs; type_datum_t *attr; ebitmap_node_t *node; const char *name; uint32_t i; int rc = -1; int flags = 0; if (pass == 1) { for (i = 0; i < 2; i++) { while ((id = queue_remove(id_queue))) { free(id); } } return 0; } ebitmap_init(&attrs); while ((id = queue_remove(id_queue))) { if (!is_id_in_scope(SYM_TYPES, id)) { yyerror2("attribute %s is not within scope", id); goto exit; } attr = hashtab_search(policydbp->p_types.table, id); if (!attr) { yyerror2("attribute %s is not declared", id); goto exit; } if (attr->flavor != TYPE_ATTRIB) { yyerror2("%s is a type, not an attribute", id); goto exit; } if (ebitmap_set_bit(&attrs, attr->s.value - 1, TRUE)) { yyerror("Out of memory!"); goto exit; } free(id); } id = (char *) queue_remove(id_queue); if (!id) { yyerror("No option specified for attribute expansion."); goto exit; } if (!strcmp(id, "T")) { flags = TYPE_FLAGS_EXPAND_ATTR_TRUE; } else { flags = TYPE_FLAGS_EXPAND_ATTR_FALSE; } ebitmap_for_each_positive_bit(&attrs, node, i) { name = policydbp->sym_val_to_name[SYM_TYPES][i]; attr = hashtab_search(policydbp->p_types.table, name); attr->flags |= flags; if ((attr->flags & TYPE_FLAGS_EXPAND_ATTR_TRUE) && (attr->flags & TYPE_FLAGS_EXPAND_ATTR_FALSE)) { yywarn2("Expandattribute option of attribute %s was set to both true and false; " "Resolving to false.", name); attr->flags &= ~TYPE_FLAGS_EXPAND_ATTR_TRUE; } } rc = 0; exit: ebitmap_destroy(&attrs); free(id); return rc; } static int add_aliases_to_type(type_datum_t * type) { char *id; type_datum_t *aliasdatum = NULL; int ret; while ((id = queue_remove(id_queue))) { if (id_has_dot(id)) { yyerror2 ("type alias identifier %s may not contain periods", id); free(id); return -1; } aliasdatum = (type_datum_t *) malloc(sizeof(type_datum_t)); if (!aliasdatum) { free(id); yyerror("Out of memory!"); return -1; } memset(aliasdatum, 0, sizeof(type_datum_t)); aliasdatum->s.value = type->s.value; ret = declare_symbol(SYM_TYPES, id, aliasdatum, NULL, &aliasdatum->s.value); switch (ret) { case -3:{ yyerror("Out of memory!"); goto cleanup; } case -2:{ yyerror2("duplicate declaration of alias %s", id); goto cleanup; } case -1:{ yyerror2("could not declare alias %s here", id); goto cleanup; } case 0: break; case 1:{ /* ret == 1 means the alias was required and therefore already * has a value. Set it up as an alias with a different primary. */ type_datum_destroy(aliasdatum); free(aliasdatum); aliasdatum = hashtab_search(policydbp->symtab[SYM_TYPES].table, id); assert(aliasdatum); aliasdatum->primary = type->s.value; aliasdatum->flavor = TYPE_ALIAS; free(id); break; } default:{ assert(0); /* should never get here */ } } } return 0; cleanup: free(id); type_datum_destroy(aliasdatum); free(aliasdatum); return -1; } int define_typealias(void) { char *id; type_datum_t *t; if (pass == 2) { while ((id = queue_remove(id_queue))) free(id); return 0; } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no type name for typealias definition?"); return -1; } if (!is_id_in_scope(SYM_TYPES, id)) { yyerror2("type %s is not within scope", id); free(id); return -1; } t = hashtab_search(policydbp->p_types.table, id); if (!t || t->flavor == TYPE_ATTRIB) { yyerror2("unknown type %s, or it was already declared as an " "attribute", id); free(id); return -1; } free(id); return add_aliases_to_type(t); } int define_typeattribute(void) { char *id; type_datum_t *t, *attr; if (pass == 2) { while ((id = queue_remove(id_queue))) free(id); return 0; } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no type name for typeattribute definition?"); return -1; } if (!is_id_in_scope(SYM_TYPES, id)) { yyerror2("type %s is not within scope", id); free(id); return -1; } t = hashtab_search(policydbp->p_types.table, id); if (!t || t->flavor == TYPE_ATTRIB) { yyerror2("unknown type %s", id); free(id); return -1; } free(id); while ((id = queue_remove(id_queue))) { if (!is_id_in_scope(SYM_TYPES, id)) { yyerror2("attribute %s is not within scope", id); free(id); return -1; } attr = hashtab_search(policydbp->p_types.table, id); if (!attr) { /* treat it as a fatal error */ yyerror2("attribute %s is not declared", id); free(id); return -1; } if (attr->flavor != TYPE_ATTRIB) { yyerror2("%s is a type, not an attribute", id); free(id); return -1; } if ((attr = get_local_type(id, attr->s.value, 1)) == NULL) { yyerror("Out of memory!"); return -1; } if (ebitmap_set_bit(&attr->types, (t->s.value - 1), TRUE)) { yyerror("out of memory"); return -1; } } return 0; } static int define_typebounds_helper(const char *bounds_id, const char *type_id) { type_datum_t *bounds, *type; if (!is_id_in_scope(SYM_TYPES, bounds_id)) { yyerror2("type %s is not within scope", bounds_id); return -1; } bounds = hashtab_search(policydbp->p_types.table, bounds_id); if (!bounds || bounds->flavor == TYPE_ATTRIB) { yyerror2("type %s is not declared", bounds_id); return -1; } if (!is_id_in_scope(SYM_TYPES, type_id)) { yyerror2("type %s is not within scope", type_id); return -1; } type = hashtab_search(policydbp->p_types.table, type_id); if (!type || type->flavor == TYPE_ATTRIB) { yyerror2("type %s is not declared", type_id); return -1; } if (type->flavor == TYPE_TYPE && !type->primary) { type = policydbp->type_val_to_struct[type->s.value - 1]; } else if (type->flavor == TYPE_ALIAS) { type = policydbp->type_val_to_struct[type->primary - 1]; } if (!type->bounds) type->bounds = bounds->s.value; else if (type->bounds != bounds->s.value) { yyerror2("type %s has inconsistent bounds %s/%s", type_id, policydbp->p_type_val_to_name[type->bounds - 1], policydbp->p_type_val_to_name[bounds->s.value - 1]); return -1; } return 0; } int define_typebounds(void) { char *bounds, *id; if (pass == 1) { while ((id = queue_remove(id_queue))) free(id); return 0; } bounds = (char *) queue_remove(id_queue); if (!bounds) { yyerror("no type name for typebounds definition?"); return -1; } while ((id = queue_remove(id_queue))) { if (define_typebounds_helper(bounds, id)) { free(bounds); free(id); return -1; } free(id); } free(bounds); return 0; } int define_type(int alias) { char *id; type_datum_t *datum, *attr; if (pass == 2) { /* * If type name contains ".", we have to define boundary * relationship implicitly to keep compatibility with * old name based hierarchy. */ if ((id = queue_remove(id_queue))) { const char *delim; if ((delim = strrchr(id, '.'))) { int ret; char *bounds = strdup(id); if (!bounds) { yyerror("out of memory"); free(id); return -1; } bounds[(size_t)(delim - id)] = '\0'; ret = define_typebounds_helper(bounds, id); free(bounds); if (ret) { free(id); return -1; } } free(id); } if (alias) { while ((id = queue_remove(id_queue))) free(id); } while ((id = queue_remove(id_queue))) free(id); return 0; } if ((datum = declare_type(TRUE, FALSE)) == NULL) { return -1; } if (alias) { if (add_aliases_to_type(datum) == -1) { return -1; } } while ((id = queue_remove(id_queue))) { if (!is_id_in_scope(SYM_TYPES, id)) { yyerror2("attribute %s is not within scope", id); free(id); return -1; } attr = hashtab_search(policydbp->p_types.table, id); if (!attr) { /* treat it as a fatal error */ yyerror2("attribute %s is not declared", id); free(id); return -1; } if (attr->flavor != TYPE_ATTRIB) { yyerror2("%s is a type, not an attribute", id); free(id); return -1; } if ((attr = get_local_type(id, attr->s.value, 1)) == NULL) { yyerror("Out of memory!"); return -1; } if (ebitmap_set_bit(&attr->types, datum->s.value - 1, TRUE)) { yyerror("Out of memory"); return -1; } } return 0; } struct val_to_name { unsigned int val; char *name; }; /* Adds a type, given by its textual name, to a typeset. If *add is 0, then add the type to the negative set; otherwise if *add is 1 then add it to the positive side. */ static int set_types(type_set_t * set, char *id, int *add, char starallowed) { type_datum_t *t; if (strcmp(id, "*") == 0) { free(id); if (!starallowed) { yyerror("* not allowed in this type of rule"); return -1; } /* set TYPE_STAR flag */ set->flags = TYPE_STAR; *add = 1; return 0; } if (strcmp(id, "~") == 0) { free(id); if (!starallowed) { yyerror("~ not allowed in this type of rule"); return -1; } /* complement the set */ set->flags = TYPE_COMP; *add = 1; return 0; } if (strcmp(id, "-") == 0) { *add = 0; free(id); return 0; } if (!is_id_in_scope(SYM_TYPES, id)) { yyerror2("type %s is not within scope", id); free(id); return -1; } t = hashtab_search(policydbp->p_types.table, id); if (!t) { yyerror2("unknown type %s", id); free(id); return -1; } if (*add == 0) { if (ebitmap_set_bit(&set->negset, t->s.value - 1, TRUE)) goto oom; } else { if (ebitmap_set_bit(&set->types, t->s.value - 1, TRUE)) goto oom; } free(id); *add = 1; return 0; oom: yyerror("Out of memory"); free(id); return -1; } static int define_compute_type_helper(int which, avrule_t ** rule) { char *id; type_datum_t *datum; ebitmap_t tclasses; ebitmap_node_t *node; avrule_t *avrule; class_perm_node_t *perm; uint32_t i; int add = 1; avrule = malloc(sizeof(avrule_t)); if (!avrule) { yyerror("out of memory"); return -1; } avrule_init(avrule); avrule->specified = which; avrule->line = policydb_lineno; avrule->source_line = source_lineno; avrule->source_filename = strdup(source_file); if (!avrule->source_filename) { yyerror("out of memory"); return -1; } while ((id = queue_remove(id_queue))) { if (set_types(&avrule->stypes, id, &add, 0)) goto bad; } add = 1; while ((id = queue_remove(id_queue))) { if (strcmp(id, "self") == 0) { free(id); if (add == 0) { yyerror("-self is not supported"); goto bad; } avrule->flags |= RULE_SELF; continue; } if (set_types(&avrule->ttypes, id, &add, 0)) goto bad; } ebitmap_init(&tclasses); if (read_classes(&tclasses)) goto bad; id = (char *)queue_remove(id_queue); if (!id) { yyerror("no newtype?"); goto bad; } if (!is_id_in_scope(SYM_TYPES, id)) { yyerror2("type %s is not within scope", id); free(id); goto bad; } datum = (type_datum_t *) hashtab_search(policydbp->p_types.table, (hashtab_key_t) id); if (!datum || datum->flavor == TYPE_ATTRIB) { yyerror2("unknown type %s", id); free(id); goto bad; } free(id); ebitmap_for_each_positive_bit(&tclasses, node, i) { perm = malloc(sizeof(class_perm_node_t)); if (!perm) { yyerror("out of memory"); goto bad; } class_perm_node_init(perm); perm->tclass = i + 1; perm->data = datum->s.value; perm->next = avrule->perms; avrule->perms = perm; } ebitmap_destroy(&tclasses); *rule = avrule; return 0; bad: avrule_destroy(avrule); free(avrule); return -1; } int define_compute_type(int which) { char *id; avrule_t *avrule; if (pass == 1) { while ((id = queue_remove(id_queue))) free(id); while ((id = queue_remove(id_queue))) free(id); while ((id = queue_remove(id_queue))) free(id); id = queue_remove(id_queue); free(id); return 0; } if (define_compute_type_helper(which, &avrule)) return -1; append_avrule(avrule); return 0; } avrule_t *define_cond_compute_type(int which) { char *id; avrule_t *avrule; if (pass == 1) { while ((id = queue_remove(id_queue))) free(id); while ((id = queue_remove(id_queue))) free(id); while ((id = queue_remove(id_queue))) free(id); id = queue_remove(id_queue); free(id); return (avrule_t *) 1; } if (define_compute_type_helper(which, &avrule)) return COND_ERR; return avrule; } int define_bool_tunable(int is_tunable) { char *id, *bool_value; cond_bool_datum_t *datum; int ret; uint32_t value; if (pass == 2) { while ((id = queue_remove(id_queue))) free(id); return 0; } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no identifier for bool definition?"); return -1; } if (id_has_dot(id)) { yyerror2("boolean identifier %s may not contain periods", id); free(id); return -1; } datum = (cond_bool_datum_t *) malloc(sizeof(cond_bool_datum_t)); if (!datum) { yyerror("out of memory"); free(id); return -1; } memset(datum, 0, sizeof(cond_bool_datum_t)); if (is_tunable) datum->flags |= COND_BOOL_FLAGS_TUNABLE; ret = declare_symbol(SYM_BOOLS, id, datum, &value, &value); switch (ret) { case -3:{ yyerror("Out of memory!"); goto cleanup; } case -2:{ yyerror2("duplicate declaration of boolean %s", id); goto cleanup; } case -1:{ yyerror2("could not declare boolean %s here", id); goto cleanup; } case 0:{ break; } case 1:{ goto cleanup; } default:{ assert(0); /* should never get here */ } } datum->s.value = value; bool_value = (char *)queue_remove(id_queue); if (!bool_value) { yyerror("no default value for bool definition?"); return -1; } datum->state = (bool_value[0] == 'T') ? 1 : 0; free(bool_value); return 0; cleanup: cond_destroy_bool(id, datum, NULL); return ret == 1 ? 0 : -1; } avrule_t *define_cond_pol_list(avrule_t * avlist, avrule_t * sl) { avrule_t *last; if (pass == 1) { /* return something so we get through pass 1 */ return (avrule_t *) 1; } if (sl == NULL) { /* This is a require block, return previous list */ return avlist; } /* Prepend the new avlist to the pre-existing one. * An extended permission statement might consist of multiple av * rules. */ for (last = sl; last->next; last = last->next) ; last->next = avlist; return sl; } typedef struct av_xperm_range { uint16_t low; uint16_t high; } av_xperm_range_t; struct av_xperm_range_list { uint8_t omit; av_xperm_range_t range; struct av_xperm_range_list *next; }; static int avrule_sort_xperms(struct av_xperm_range_list **rangehead) { struct av_xperm_range_list *r, *r2, *sorted, *sortedhead = NULL; /* order list by range.low */ for (r = *rangehead; r != NULL; r = r->next) { sorted = malloc(sizeof(struct av_xperm_range_list)); if (sorted == NULL) goto error; memcpy(sorted, r, sizeof(struct av_xperm_range_list)); sorted->next = NULL; if (sortedhead == NULL) { sortedhead = sorted; continue; } for (r2 = sortedhead; r2 != NULL; r2 = r2->next) { if (sorted->range.low < r2->range.low) { /* range is the new head */ sorted->next = r2; sortedhead = sorted; break; } else if ((r2 ->next != NULL) && (r->range.low < r2->next->range.low)) { /* insert range between elements */ sorted->next = r2->next; r2->next = sorted; break; } else if (r2->next == NULL) { /* range is the new tail*/ r2->next = sorted; break; } } } r = *rangehead; while (r != NULL) { r2 = r; r = r->next; free(r2); } *rangehead = sortedhead; return 0; error: yyerror("out of memory"); return -1; } static void avrule_merge_xperms(struct av_xperm_range_list **rangehead) { struct av_xperm_range_list *r, *tmp; r = *rangehead; while (r != NULL && r->next != NULL) { /* merge */ if ((r->range.high + 1) >= r->next->range.low) { /* keep the higher of the two */ if (r->range.high < r->next->range.high) r->range.high = r->next->range.high; tmp = r->next; r->next = r->next->next; free(tmp); continue; } r = r->next; } } static int avrule_read_xperm_ranges(struct av_xperm_range_list **rangehead) { char *id; struct av_xperm_range_list *rnew, *r = NULL; uint8_t omit = 0; *rangehead = NULL; /* read in all the ioctl/netlink commands */ while ((id = queue_remove(id_queue))) { if (strcmp(id,"~") == 0) { /* these are values to be omitted */ free(id); omit = 1; } else if (strcmp(id,"-") == 0) { /* high value of range */ free(id); id = queue_remove(id_queue); r->range.high = (uint16_t) strtoul(id,NULL,0); if (r->range.high < r->range.low) { yyerror2("extended permission range %#x-%#x must be in ascending order.", r->range.low, r->range.high); return -1; } free(id); } else { /* read in new low value */ rnew = malloc(sizeof(struct av_xperm_range_list)); if (rnew == NULL) goto error; rnew->next = NULL; if (*rangehead == NULL) { *rangehead = rnew; r = *rangehead; } else { r->next = rnew; r = r->next; } rnew->range.low = (uint16_t) strtoul(id,NULL,0); rnew->range.high = rnew->range.low; free(id); } } r = *rangehead; if (r) { r->omit = omit; } return 0; error: yyerror("out of memory"); return -1; } /* flip to included ranges */ static int avrule_omit_xperms(struct av_xperm_range_list **rangehead) { struct av_xperm_range_list *rnew, *r, *newhead, *r2; rnew = calloc(1, sizeof(struct av_xperm_range_list)); if (!rnew) goto error; newhead = rnew; r = *rangehead; r2 = newhead; if (r->range.low == 0) { r2->range.low = r->range.high + 1; r = r->next; } else { r2->range.low = 0; } while (r) { r2->range.high = r->range.low - 1; rnew = calloc(1, sizeof(struct av_xperm_range_list)); if (!rnew) goto error; r2->next = rnew; r2 = r2->next; r2->range.low = r->range.high + 1; if (!r->next) r2->range.high = 0xffff; r = r->next; } r = *rangehead; while (r != NULL) { r2 = r; r = r->next; free(r2); } *rangehead = newhead; return 0; error: yyerror("out of memory"); return -1; } static int avrule_xperm_ranges(struct av_xperm_range_list **rangelist) { struct av_xperm_range_list *rangehead; uint8_t omit; /* read in ranges to include and omit */ if (avrule_read_xperm_ranges(&rangehead)) return -1; if (rangehead == NULL) { yyerror("error processing ioctl/netlink commands"); return -1; } omit = rangehead->omit; /* sort and merge the input ranges */ if (avrule_sort_xperms(&rangehead)) return -1; avrule_merge_xperms(&rangehead); /* flip ranges if these are omitted */ if (omit) { if (avrule_omit_xperms(&rangehead)) return -1; } *rangelist = rangehead; return 0; } static int define_te_avtab_xperms_helper(int which, avrule_t ** rule) { char *id; class_perm_node_t *perms, *tail = NULL, *cur_perms = NULL; class_datum_t *cladatum; perm_datum_t *perdatum = NULL; ebitmap_t tclasses; ebitmap_node_t *node; avrule_t *avrule; unsigned int i; int add = 1, ret = 0; avrule = (avrule_t *) malloc(sizeof(avrule_t)); if (!avrule) { yyerror("out of memory"); ret = -1; goto out; } avrule_init(avrule); avrule->specified = which; avrule->line = policydb_lineno; avrule->source_line = source_lineno; avrule->source_filename = strdup(source_file); avrule->xperms = NULL; if (!avrule->source_filename) { yyerror("out of memory"); return -1; } while ((id = queue_remove(id_queue))) { if (set_types (&avrule->stypes, id, &add, which == AVRULE_XPERMS_NEVERALLOW ? 1 : 0)) { ret = -1; goto out; } } add = 1; while ((id = queue_remove(id_queue))) { if (strcmp(id, "self") == 0) { free(id); if (add == 0 && which != AVRULE_XPERMS_NEVERALLOW) { yyerror("-self is only supported in neverallow and neverallowxperm rules"); ret = -1; goto out; } avrule->flags |= (add ? RULE_SELF : RULE_NOTSELF); if ((avrule->flags & RULE_SELF) && (avrule->flags & RULE_NOTSELF)) { yyerror("self and -self are mutual exclusive"); ret = -1; goto out; } continue; } if (set_types (&avrule->ttypes, id, &add, which == AVRULE_XPERMS_NEVERALLOW ? 1 : 0)) { ret = -1; goto out; } } if ((avrule->ttypes.flags & TYPE_COMP)) { if (avrule->flags & RULE_NOTSELF) { yyerror("-self is not supported in complements"); ret = -1; goto out; } if (avrule->flags & RULE_SELF) { avrule->flags &= ~RULE_SELF; avrule->flags |= RULE_NOTSELF; } } ebitmap_init(&tclasses); ret = read_classes(&tclasses); if (ret) goto out; perms = NULL; id = queue_head(id_queue); ebitmap_for_each_positive_bit(&tclasses, node, i) { cur_perms = (class_perm_node_t *) malloc(sizeof(class_perm_node_t)); if (!cur_perms) { yyerror("out of memory"); ret = -1; goto out; } class_perm_node_init(cur_perms); cur_perms->tclass = i + 1; if (!perms) perms = cur_perms; if (tail) tail->next = cur_perms; tail = cur_perms; cladatum = policydbp->class_val_to_struct[i]; perdatum = hashtab_search(cladatum->permissions.table, id); if (!perdatum) { if (cladatum->comdatum) { perdatum = hashtab_search(cladatum->comdatum-> permissions.table, id); } } if (!perdatum) { yyerror2("permission %s is not defined" " for class %s", id, policydbp->p_class_val_to_name[i]); continue; } else if (!is_perm_in_scope (id, policydbp->p_class_val_to_name[i])) { yyerror2("permission %s of class %s is" " not within scope", id, policydbp->p_class_val_to_name[i]); continue; } else { cur_perms->data |= UINT32_C(1) << (perdatum->s.value - 1); } } ebitmap_destroy(&tclasses); avrule->perms = perms; *rule = avrule; out: return ret; } /* index of the u32 containing the permission */ #define XPERM_IDX(x) ((x) >> 5) /* set bits 0 through x-1 within the u32 */ #define XPERM_SETBITS(x) ((UINT32_C(1) << ((x) & 0x1f)) - 1) /* low value for this u32 */ #define XPERM_LOW(x) ((x) << 5) /* high value for this u32 */ #define XPERM_HIGH(x) ((((x) + 1) << 5) - 1) static void avrule_xperm_setrangebits(uint16_t low, uint16_t high, av_extended_perms_t *xperms) { unsigned int i; uint16_t h = high + 1; /* for each u32 that this low-high range touches, set driver permissions */ for (i = XPERM_IDX(low); i <= XPERM_IDX(high); i++) { /* set all bits in u32 */ if ((low <= XPERM_LOW(i)) && (high >= XPERM_HIGH(i))) xperms->perms[i] |= ~0U; /* set low bits */ else if ((low <= XPERM_LOW(i)) && (high < XPERM_HIGH(i))) xperms->perms[i] |= XPERM_SETBITS(h); /* set high bits */ else if ((low > XPERM_LOW(i)) && (high >= XPERM_HIGH(i))) xperms->perms[i] |= ~0U - XPERM_SETBITS(low); /* set middle bits */ else if ((low > XPERM_LOW(i)) && (high <= XPERM_HIGH(i))) xperms->perms[i] |= XPERM_SETBITS(h) - XPERM_SETBITS(low); } } static int avrule_xperms_used(const av_extended_perms_t *xperms) { unsigned int i; for (i = 0; i < sizeof(xperms->perms)/sizeof(xperms->perms[0]); i++) { if (xperms->perms[i]) return 1; } return 0; } /* * using definitions found in kernel document ioctl-number.txt * The kernel components of an ioctl command are: * dir, size, driver, and function. Only the driver and function fields * are considered here */ #define IOC_DRIV(x) ((x) >> 8) #define IOC_FUNC(x) ((x) & 0xff) #define IOC_CMD(driver, func) (((driver) << 8) + (func)) static int avrule_xperm_partialdriver(struct av_xperm_range_list *rangelist, av_extended_perms_t *complete_driver, av_extended_perms_t **extended_perms) { struct av_xperm_range_list *r; av_extended_perms_t *xperms; uint8_t low, high; xperms = calloc(1, sizeof(av_extended_perms_t)); if (!xperms) { yyerror("out of memory"); return -1; } r = rangelist; while(r) { low = IOC_DRIV(r->range.low); high = IOC_DRIV(r->range.high); if (complete_driver) { if (!xperm_test(low, complete_driver->perms)) xperm_set(low, xperms->perms); if (!xperm_test(high, complete_driver->perms)) xperm_set(high, xperms->perms); } else { xperm_set(low, xperms->perms); xperm_set(high, xperms->perms); } r = r->next; } if (avrule_xperms_used(xperms)) { *extended_perms = xperms; } else { free(xperms); *extended_perms = NULL; } return 0; } static int avrule_ioctl_completedriver(struct av_xperm_range_list *rangelist, av_extended_perms_t **extended_perms) { struct av_xperm_range_list *r; av_extended_perms_t *xperms; uint16_t low, high; xperms = calloc(1, sizeof(av_extended_perms_t)); if (!xperms) { yyerror("out of memory"); return -1; } r = rangelist; while(r) { /* * Any driver code that has sequence 0x00 - 0xff is a complete code, * * if command number = 0xff, then round high up to next code, * else 0x00 - 0xfe keep current code * of this range. temporarily u32 for the + 1 * to account for possible rollover before right shift */ high = IOC_DRIV((uint32_t) (r->range.high + 1)); /* if 0x00 keep current driver code else 0x01 - 0xff round up to next code*/ low = IOC_DRIV(r->range.low); if (IOC_FUNC(r->range.low)) low++; if (high > low) avrule_xperm_setrangebits(low, high - 1, xperms); r = r->next; } if (avrule_xperms_used(xperms)) { xperms->driver = 0x00; xperms->specified = AVRULE_XPERMS_IOCTLDRIVER; *extended_perms = xperms; } else { free(xperms); *extended_perms = NULL; } return 0; } static int avrule_xperm_func(struct av_xperm_range_list *rangelist, av_extended_perms_t **extended_perms, unsigned int driver, uint8_t specified) { struct av_xperm_range_list *r; av_extended_perms_t *xperms; uint16_t low, high; *extended_perms = NULL; xperms = calloc(1, sizeof(av_extended_perms_t)); if (!xperms) { yyerror("out of memory"); return -1; } r = rangelist; /* for the passed in driver code, find the ranges that apply */ while (r) { low = r->range.low; high = r->range.high; if ((driver != IOC_DRIV(low)) && (driver != IOC_DRIV(high))) { r = r->next; continue; } if (driver == IOC_DRIV(low)) { if (high > IOC_CMD(driver, 0xff)) high = IOC_CMD(driver, 0xff); } else { if (low < IOC_CMD(driver, 0)) low = IOC_CMD(driver, 0); } low = IOC_FUNC(low); high = IOC_FUNC(high); avrule_xperm_setrangebits(low, high, xperms); xperms->driver = driver; xperms->specified = specified; r = r->next; } if (avrule_xperms_used(xperms)) { *extended_perms = xperms; } else { free(xperms); *extended_perms = NULL; } return 0; } static unsigned int xperms_for_each_bit(unsigned int *bit, av_extended_perms_t *xperms) { unsigned int i; for (i = *bit; i < sizeof(xperms->perms)*8; i++) { if (xperm_test(i,xperms->perms)) { xperm_clear(i, xperms->perms); *bit = i; return 1; } } return 0; } static int avrule_cpy(avrule_t *dest, const avrule_t *src) { class_perm_node_t *src_perms; class_perm_node_t *dest_perms, *dest_tail; dest_tail = NULL; avrule_init(dest); dest->specified = src->specified; dest->flags = src->flags; if (type_set_cpy(&dest->stypes, &src->stypes)) { yyerror("out of memory"); return -1; } if (type_set_cpy(&dest->ttypes, &src->ttypes)) { yyerror("out of memory"); return -1; } dest->line = src->line; dest->source_filename = strdup(source_file); if (!dest->source_filename) { yyerror("out of memory"); return -1; } dest->source_line = src->source_line; /* increment through the class perms and copy over */ src_perms = src->perms; while (src_perms) { dest_perms = (class_perm_node_t *) calloc(1, sizeof(class_perm_node_t)); if (!dest_perms) { yyerror("out of memory"); return -1; } class_perm_node_init(dest_perms); if (!dest->perms) dest->perms = dest_perms; else dest_tail->next = dest_perms; dest_perms->tclass = src_perms->tclass; dest_perms->data = src_perms->data; dest_perms->next = NULL; dest_tail = dest_perms; src_perms = src_perms->next; } return 0; } static int define_te_avtab_ioctl(const avrule_t *avrule_template, avrule_t **ret_avrules) { avrule_t *avrule, *ret = NULL, **last = &ret; struct av_xperm_range_list *rangelist, *r; av_extended_perms_t *complete_driver, *partial_driver, *xperms; unsigned int i; /* organize ioctl ranges */ if (avrule_xperm_ranges(&rangelist)) return -1; /* create rule for ioctl driver types that are entirely enabled */ if (avrule_ioctl_completedriver(rangelist, &complete_driver)) return -1; if (complete_driver) { avrule = (avrule_t *) calloc(1, sizeof(avrule_t)); if (!avrule) { yyerror("out of memory"); return -1; } if (avrule_cpy(avrule, avrule_template)) return -1; avrule->xperms = complete_driver; if (ret_avrules) { *last = avrule; last = &(avrule->next); } else { append_avrule(avrule); } } /* flag ioctl driver codes that are partially enabled */ if (avrule_xperm_partialdriver(rangelist, complete_driver, &partial_driver)) return -1; if (!partial_driver || !avrule_xperms_used(partial_driver)) goto done; /* * create rule for each partially used driver codes * "partially used" meaning that the code number e.g. socket 0x89 * has some permission bits set and others not set. */ i = 0; while (xperms_for_each_bit(&i, partial_driver)) { if (avrule_xperm_func(rangelist, &xperms, i, AVRULE_XPERMS_IOCTLFUNCTION)) return -1; if (xperms) { avrule = (avrule_t *) calloc(1, sizeof(avrule_t)); if (!avrule) { yyerror("out of memory"); return -1; } if (avrule_cpy(avrule, avrule_template)) return -1; avrule->xperms = xperms; if (ret_avrules) { *last = avrule; last = &(avrule->next); } else { append_avrule(avrule); } } } done: if (partial_driver) free(partial_driver); while (rangelist != NULL) { r = rangelist; rangelist = rangelist->next; free(r); } if (ret_avrules) *ret_avrules = ret; return 0; } static int define_te_avtab_netlink(const avrule_t *avrule_template, avrule_t **ret_avrules) { avrule_t *avrule, *ret = NULL, **last = &ret; struct av_xperm_range_list *rangelist, *r; av_extended_perms_t *partial_driver, *xperms; unsigned int i; /* organize ranges */ if (avrule_xperm_ranges(&rangelist)) return -1; /* flag driver codes that are partially enabled */ if (avrule_xperm_partialdriver(rangelist, NULL, &partial_driver)) return -1; if (!partial_driver || !avrule_xperms_used(partial_driver)) goto done; /* * create rule for each partially used driver codes * "partially used" meaning that the code number e.g. socket 0x89 * has some permission bits set and others not set. */ i = 0; while (xperms_for_each_bit(&i, partial_driver)) { if (avrule_xperm_func(rangelist, &xperms, i, AVRULE_XPERMS_NLMSG)) return -1; if (xperms) { avrule = (avrule_t *) calloc(1, sizeof(avrule_t)); if (!avrule) { yyerror("out of memory"); return -1; } if (avrule_cpy(avrule, avrule_template)) return -1; avrule->xperms = xperms; if (ret_avrules) { *last = avrule; last = &(avrule->next); } else { append_avrule(avrule); } } } done: if (partial_driver) free(partial_driver); while (rangelist != NULL) { r = rangelist; rangelist = rangelist->next; free(r); } if (ret_avrules) *ret_avrules = ret; return 0; } avrule_t *define_cond_te_avtab_extended_perms(int which) { char *id; unsigned int i; avrule_t *avrule_template, *rules = NULL; int rc = 0; if (policydbp->policy_type == POLICY_KERN && policydbp->policyvers < POLICYDB_VERSION_COND_XPERMS) { yyerror2("extended permissions in conditional policies are only supported since policy version %d, found policy version %d", POLICYDB_VERSION_COND_XPERMS, policydbp->policyvers); return COND_ERR; } if (policydbp->policy_type != POLICY_KERN && policydbp->policyvers < MOD_POLICYDB_VERSION_COND_XPERMS) { yyerror2("extended permissions in conditional policies are only supported since module policy version %d, found module policy version %d", MOD_POLICYDB_VERSION_COND_XPERMS, policydbp->policyvers); return COND_ERR; } if (pass == 1) { for (i = 0; i < 4; i++) { while ((id = queue_remove(id_queue))) free(id); } return (avrule_t *) 1; /* any non-NULL value */ } /* populate avrule template with source/target/tclass */ if (define_te_avtab_xperms_helper(which, &avrule_template)) return COND_ERR; id = queue_remove(id_queue); if (strcmp(id, "ioctl") == 0) { rc = define_te_avtab_ioctl(avrule_template, &rules); } else if (strcmp(id, "nlmsg") == 0) { rc = define_te_avtab_netlink(avrule_template, &rules); } else { yyerror2("only ioctl and nlmsg extended permissions are supported, found %s", id); rc = -1; } free(id); avrule_destroy(avrule_template); free(avrule_template); if (rc) { avrule_destroy(rules); return NULL; } return rules; } int define_te_avtab_extended_perms(int which) { char *id; unsigned int i; avrule_t *avrule_template; int rc = 0; if (pass == 1) { for (i = 0; i < 4; i++) { while ((id = queue_remove(id_queue))) free(id); } return 0; } /* populate avrule template with source/target/tclass */ if (define_te_avtab_xperms_helper(which, &avrule_template)) return -1; id = queue_remove(id_queue); if (strcmp(id,"ioctl") == 0) { rc = define_te_avtab_ioctl(avrule_template, NULL); } else if (strcmp(id,"nlmsg") == 0) { rc = define_te_avtab_netlink(avrule_template, NULL); } else { yyerror2("only ioctl and nlmsg extended permissions are supported, found %s", id); rc = -1; } free(id); avrule_destroy(avrule_template); free(avrule_template); return rc; } #define PERMISSION_MASK(nprim) ((nprim) == PERM_SYMTAB_SIZE ? (~UINT32_C(0)) : ((UINT32_C(1) << (nprim)) - 1)) static int define_te_avtab_helper(int which, avrule_t ** rule) { char *id; class_datum_t *cladatum; perm_datum_t *perdatum = NULL; class_perm_node_t *perms, *tail = NULL, *cur_perms = NULL; ebitmap_t tclasses; ebitmap_node_t *node; avrule_t *avrule; unsigned int i; int add = 1, ret = 0; int suppress = 0; ebitmap_init(&tclasses); avrule = (avrule_t *) malloc(sizeof(avrule_t)); if (!avrule) { yyerror("memory error"); ret = -1; goto out; } avrule_init(avrule); avrule->specified = which; avrule->line = policydb_lineno; avrule->source_line = source_lineno; avrule->source_filename = strdup(source_file); avrule->xperms = NULL; if (!avrule->source_filename) { yyerror("out of memory"); return -1; } while ((id = queue_remove(id_queue))) { if (set_types (&avrule->stypes, id, &add, which == AVRULE_NEVERALLOW ? 1 : 0)) { ret = -1; goto out; } } add = 1; while ((id = queue_remove(id_queue))) { if (strcmp(id, "self") == 0) { free(id); if (add == 0 && which != AVRULE_NEVERALLOW) { yyerror("-self is only supported in neverallow and neverallowxperm rules"); ret = -1; goto out; } avrule->flags |= (add ? RULE_SELF : RULE_NOTSELF); if ((avrule->flags & RULE_SELF) && (avrule->flags & RULE_NOTSELF)) { yyerror("self and -self are mutual exclusive"); ret = -1; goto out; } continue; } if (set_types (&avrule->ttypes, id, &add, which == AVRULE_NEVERALLOW ? 1 : 0)) { ret = -1; goto out; } } if ((avrule->ttypes.flags & TYPE_COMP)) { if (avrule->flags & RULE_NOTSELF) { yyerror("-self is not supported in complements"); ret = -1; goto out; } if (avrule->flags & RULE_SELF) { avrule->flags &= ~RULE_SELF; avrule->flags |= RULE_NOTSELF; } } ret = read_classes(&tclasses); if (ret) goto out; perms = NULL; ebitmap_for_each_positive_bit(&tclasses, node, i) { cur_perms = (class_perm_node_t *) malloc(sizeof(class_perm_node_t)); if (!cur_perms) { yyerror("out of memory"); ret = -1; goto out; } class_perm_node_init(cur_perms); cur_perms->tclass = i + 1; if (!perms) perms = cur_perms; if (tail) tail->next = cur_perms; tail = cur_perms; } while ((id = queue_remove(id_queue))) { cur_perms = perms; ebitmap_for_each_positive_bit(&tclasses, node, i) { cladatum = policydbp->class_val_to_struct[i]; if (strcmp(id, "*") == 0) { /* set all declared permissions in the class */ cur_perms->data = PERMISSION_MASK(cladatum->permissions.nprim); goto next; } if (strcmp(id, "~") == 0) { /* complement the set */ if (which == AVRULE_DONTAUDIT) yywarn("dontaudit rule with a ~?"); cur_perms->data = ~cur_perms->data & PERMISSION_MASK(cladatum->permissions.nprim); if (cur_perms->data == 0) { class_perm_node_t *tmp = cur_perms; yywarn("omitting avrule with no permission set"); if (perms == cur_perms) perms = cur_perms->next; cur_perms = cur_perms->next; free(tmp); continue; } goto next; } perdatum = hashtab_search(cladatum->permissions.table, id); if (!perdatum) { if (cladatum->comdatum) { perdatum = hashtab_search(cladatum->comdatum-> permissions.table, id); } } if (!perdatum) { if (!suppress) yyerror2("permission %s is not defined" " for class %s", id, policydbp->p_class_val_to_name[i]); continue; } else if (!is_perm_in_scope (id, policydbp->p_class_val_to_name[i])) { if (!suppress) { yyerror2("permission %s of class %s is" " not within scope", id, policydbp->p_class_val_to_name[i]); } continue; } else { cur_perms->data |= UINT32_C(1) << (perdatum->s.value - 1); } next: cur_perms = cur_perms->next; } free(id); } avrule->perms = perms; *rule = avrule; out: if (ret) { avrule_destroy(avrule); free(avrule); } ebitmap_destroy(&tclasses); return ret; } avrule_t *define_cond_te_avtab(int which) { char *id; avrule_t *avrule; int i; if (pass == 1) { for (i = 0; i < 4; i++) { while ((id = queue_remove(id_queue))) free(id); } return (avrule_t *) 1; /* any non-NULL value */ } if (define_te_avtab_helper(which, &avrule)) return COND_ERR; return avrule; } int define_te_avtab(int which) { char *id; avrule_t *avrule; int i; if (pass == 1) { for (i = 0; i < 4; i++) { while ((id = queue_remove(id_queue))) free(id); } return 0; } if (define_te_avtab_helper(which, &avrule)) return -1; /* append this avrule to the end of the current rules list */ append_avrule(avrule); return 0; } /* The role-types rule is no longer used to declare regular role or * role attribute, but solely aimed for declaring role-types associations. */ int define_role_types(void) { role_datum_t *role; char *id; int add = 1; if (pass == 1) { while ((id = queue_remove(id_queue))) free(id); return 0; } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no role name for role-types rule?"); return -1; } if (!is_id_in_scope(SYM_ROLES, id)) { yyerror2("role %s is not within scope", id); free(id); return -1; } role = hashtab_search(policydbp->p_roles.table, id); if (!role) { yyerror2("unknown role %s", id); free(id); return -1; } role = get_local_role(id, role->s.value, (role->flavor == ROLE_ATTRIB)); while ((id = queue_remove(id_queue))) { if (set_types(&role->types, id, &add, 0)) return -1; } return 0; } int define_attrib_role(void) { if (pass == 2) { free(queue_remove(id_queue)); return 0; } /* Declare a role attribute */ if (declare_role(TRUE) == NULL) return -1; return 0; } int define_role_attr(void) { char *id; role_datum_t *r, *attr; if (pass == 2) { while ((id = queue_remove(id_queue))) free(id); return 0; } /* Declare a regular role */ if ((r = declare_role(FALSE)) == NULL) return -1; while ((id = queue_remove(id_queue))) { if (!is_id_in_scope(SYM_ROLES, id)) { yyerror2("attribute %s is not within scope", id); free(id); return -1; } attr = hashtab_search(policydbp->p_roles.table, id); if (!attr) { /* treat it as a fatal error */ yyerror2("role attribute %s is not declared", id); free(id); return -1; } if (attr->flavor != ROLE_ATTRIB) { yyerror2("%s is a regular role, not an attribute", id); free(id); return -1; } if ((attr = get_local_role(id, attr->s.value, 1)) == NULL) { yyerror("Out of memory!"); return -1; } if (ebitmap_set_bit(&attr->roles, (r->s.value - 1), TRUE)) { yyerror("out of memory"); return -1; } } return 0; } int define_roleattribute(void) { char *id; role_datum_t *r, *attr; if (pass == 2) { while ((id = queue_remove(id_queue))) free(id); return 0; } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no role name for roleattribute definition?"); return -1; } if (!is_id_in_scope(SYM_ROLES, id)) { yyerror2("role %s is not within scope", id); free(id); return -1; } r = hashtab_search(policydbp->p_roles.table, id); /* We support adding one role attribute into another */ if (!r) { yyerror2("unknown role %s", id); free(id); return -1; } free(id); while ((id = queue_remove(id_queue))) { if (!is_id_in_scope(SYM_ROLES, id)) { yyerror2("attribute %s is not within scope", id); free(id); return -1; } attr = hashtab_search(policydbp->p_roles.table, id); if (!attr) { /* treat it as a fatal error */ yyerror2("role attribute %s is not declared", id); free(id); return -1; } if (attr->flavor != ROLE_ATTRIB) { yyerror2("%s is a regular role, not an attribute", id); free(id); return -1; } if ((attr = get_local_role(id, attr->s.value, 1)) == NULL) { yyerror("Out of memory!"); return -1; } if (ebitmap_set_bit(&attr->roles, (r->s.value - 1), TRUE)) { yyerror("out of memory"); return -1; } } return 0; } static int role_val_to_name_helper(hashtab_key_t key, hashtab_datum_t datum, void *p) { struct val_to_name *v = p; role_datum_t *roldatum; roldatum = (role_datum_t *) datum; if (v->val == roldatum->s.value) { v->name = key; return 1; } return 0; } static char *role_val_to_name(unsigned int val) { struct val_to_name v; int rc; v.val = val; rc = hashtab_map(policydbp->p_roles.table, role_val_to_name_helper, &v); if (rc) return v.name; return NULL; } static int set_roles(role_set_t * set, char *id) { role_datum_t *r; if (strcmp(id, "*") == 0) { free(id); yyerror("* is not allowed for role sets"); return -1; } if (strcmp(id, "~") == 0) { free(id); yyerror("~ is not allowed for role sets"); return -1; } if (!is_id_in_scope(SYM_ROLES, id)) { yyerror2("role %s is not within scope", id); free(id); return -1; } r = hashtab_search(policydbp->p_roles.table, id); if (!r) { yyerror2("unknown role %s", id); free(id); return -1; } if (ebitmap_set_bit(&set->roles, r->s.value - 1, TRUE)) { yyerror("out of memory"); free(id); return -1; } free(id); return 0; } int define_role_trans(int class_specified) { char *id; role_datum_t *role; role_set_t roles; type_set_t types; class_datum_t *cladatum; ebitmap_t e_types, e_roles, e_classes; ebitmap_node_t *tnode, *rnode, *cnode; struct role_trans *tr = NULL; struct role_trans_rule *rule = NULL; unsigned int i, j, k; int add = 1; if (pass == 1) { while ((id = queue_remove(id_queue))) free(id); while ((id = queue_remove(id_queue))) free(id); if (class_specified) while ((id = queue_remove(id_queue))) free(id); id = queue_remove(id_queue); free(id); return 0; } role_set_init(&roles); ebitmap_init(&e_roles); type_set_init(&types); ebitmap_init(&e_types); ebitmap_init(&e_classes); while ((id = queue_remove(id_queue))) { if (set_roles(&roles, id)) return -1; } add = 1; while ((id = queue_remove(id_queue))) { if (set_types(&types, id, &add, 0)) return -1; } if (class_specified) { if (read_classes(&e_classes)) return -1; } else { cladatum = hashtab_search(policydbp->p_classes.table, "process"); if (!cladatum) { yyerror2("could not find process class for " "legacy role_transition statement"); return -1; } if (ebitmap_set_bit(&e_classes, cladatum->s.value - 1, TRUE)) { yyerror("out of memory"); return -1; } } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no new role in transition definition?"); goto bad; } if (!is_id_in_scope(SYM_ROLES, id)) { yyerror2("role %s is not within scope", id); free(id); goto bad; } role = hashtab_search(policydbp->p_roles.table, id); if (!role) { yyerror2("unknown role %s used in transition definition", id); free(id); goto bad; } if (role->flavor != ROLE_ROLE) { yyerror2("the new role %s must be a regular role", id); free(id); goto bad; } free(id); /* This ebitmap business is just to ensure that there are not conflicting role_trans rules */ if (role_set_expand(&roles, &e_roles, policydbp, NULL, NULL)) goto bad; if (type_set_expand(&types, &e_types, policydbp, 1)) goto bad; ebitmap_for_each_positive_bit(&e_roles, rnode, i) { ebitmap_for_each_positive_bit(&e_types, tnode, j) { ebitmap_for_each_positive_bit(&e_classes, cnode, k) { for (tr = policydbp->role_tr; tr; tr = tr->next) { if (tr->role == (i + 1) && tr->type == (j + 1) && tr->tclass == (k + 1)) { yyerror2("duplicate role " "transition for " "(%s,%s,%s)", role_val_to_name(i+1), policydbp->p_type_val_to_name[j], policydbp->p_class_val_to_name[k]); goto bad; } } tr = malloc(sizeof(struct role_trans)); if (!tr) { yyerror("out of memory"); return -1; } memset(tr, 0, sizeof(struct role_trans)); tr->role = i + 1; tr->type = j + 1; tr->tclass = k + 1; tr->new_role = role->s.value; tr->next = policydbp->role_tr; policydbp->role_tr = tr; } } } /* Now add the real rule */ rule = malloc(sizeof(struct role_trans_rule)); if (!rule) { yyerror("out of memory"); return -1; } memset(rule, 0, sizeof(struct role_trans_rule)); rule->roles = roles; rule->types = types; rule->classes = e_classes; rule->new_role = role->s.value; append_role_trans(rule); ebitmap_destroy(&e_roles); ebitmap_destroy(&e_types); return 0; bad: return -1; } int define_role_allow(void) { char *id; struct role_allow_rule *ra = 0; if (pass == 1) { while ((id = queue_remove(id_queue))) free(id); while ((id = queue_remove(id_queue))) free(id); return 0; } ra = malloc(sizeof(role_allow_rule_t)); if (!ra) { yyerror("out of memory"); return -1; } role_allow_rule_init(ra); while ((id = queue_remove(id_queue))) { if (set_roles(&ra->roles, id)) { role_allow_rule_destroy(ra); free(ra); return -1; } } while ((id = queue_remove(id_queue))) { if (set_roles(&ra->new_roles, id)) { role_allow_rule_destroy(ra); free(ra); return -1; } } append_role_allow(ra); return 0; } avrule_t *define_cond_filename_trans(void) { yyerror("type transitions with a filename not allowed inside " "conditionals"); return COND_ERR; } int define_filename_trans(void) { char *id, *name = NULL; type_set_t stypes, ttypes; ebitmap_t e_stypes, e_ttypes; ebitmap_t e_tclasses; ebitmap_node_t *snode, *tnode, *cnode; filename_trans_rule_t *ftr; type_datum_t *typdatum; uint32_t otype; unsigned int c, s, t; int add, self, rc; if (pass == 1) { /* stype */ while ((id = queue_remove(id_queue))) free(id); /* ttype */ while ((id = queue_remove(id_queue))) free(id); /* tclass */ while ((id = queue_remove(id_queue))) free(id); /* otype */ id = queue_remove(id_queue); free(id); /* name */ id = queue_remove(id_queue); free(id); return 0; } type_set_init(&stypes); type_set_init(&ttypes); ebitmap_init(&e_stypes); ebitmap_init(&e_ttypes); ebitmap_init(&e_tclasses); add = 1; while ((id = queue_remove(id_queue))) { if (set_types(&stypes, id, &add, 0)) goto bad; } self = 0; add = 1; while ((id = queue_remove(id_queue))) { if (strcmp(id, "self") == 0) { free(id); if (add == 0) { yyerror("-self is not supported"); goto bad; } self = 1; continue; } if (set_types(&ttypes, id, &add, 0)) goto bad; } if (read_classes(&e_tclasses)) goto bad; id = (char *)queue_remove(id_queue); if (!id) { yyerror("no otype in transition definition?"); goto bad; } if (!is_id_in_scope(SYM_TYPES, id)) { yyerror2("type %s is not within scope", id); free(id); goto bad; } typdatum = hashtab_search(policydbp->p_types.table, id); if (!typdatum) { yyerror2("unknown type %s used in transition definition", id); free(id); goto bad; } free(id); otype = typdatum->s.value; name = queue_remove(id_queue); if (!name) { yyerror("no pathname specified in filename_trans definition?"); goto bad; } /* We expand the class set into separate rules. We expand the types * just to make sure there are not duplicates. They will get turned * into separate rules later */ if (type_set_expand(&stypes, &e_stypes, policydbp, 1)) goto bad; if (type_set_expand(&ttypes, &e_ttypes, policydbp, 1)) goto bad; ebitmap_for_each_positive_bit(&e_tclasses, cnode, c) { ebitmap_for_each_positive_bit(&e_stypes, snode, s) { ebitmap_for_each_positive_bit(&e_ttypes, tnode, t) { rc = policydb_filetrans_insert( policydbp, s+1, t+1, c+1, name, NULL, otype, NULL ); if (rc != SEPOL_OK) { if (rc == SEPOL_EEXIST) { yyerror2("duplicate filename transition for: filename_trans %s %s %s:%s", name, policydbp->p_type_val_to_name[s], policydbp->p_type_val_to_name[t], policydbp->p_class_val_to_name[c]); goto bad; } yyerror("out of memory"); goto bad; } } if (self) { rc = policydb_filetrans_insert( policydbp, s+1, s+1, c+1, name, NULL, otype, NULL ); if (rc != SEPOL_OK) { if (rc == SEPOL_EEXIST) { yyerror2("duplicate filename transition for: filename_trans %s %s %s:%s", name, policydbp->p_type_val_to_name[s], policydbp->p_type_val_to_name[s], policydbp->p_class_val_to_name[c]); goto bad; } yyerror("out of memory"); goto bad; } } } /* Now add the real rule since we didn't find any duplicates */ ftr = malloc(sizeof(*ftr)); if (!ftr) { yyerror("out of memory"); goto bad; } filename_trans_rule_init(ftr); append_filename_trans(ftr); ftr->name = strdup(name); if (type_set_cpy(&ftr->stypes, &stypes)) { yyerror("out of memory"); goto bad; } if (type_set_cpy(&ftr->ttypes, &ttypes)) { yyerror("out of memory"); goto bad; } ftr->tclass = c + 1; ftr->otype = otype; ftr->flags = self ? RULE_SELF : 0; } free(name); ebitmap_destroy(&e_stypes); ebitmap_destroy(&e_ttypes); ebitmap_destroy(&e_tclasses); type_set_destroy(&stypes); type_set_destroy(&ttypes); return 0; bad: free(name); ebitmap_destroy(&e_stypes); ebitmap_destroy(&e_ttypes); ebitmap_destroy(&e_tclasses); type_set_destroy(&stypes); type_set_destroy(&ttypes); return -1; } static constraint_expr_t *constraint_expr_clone(const constraint_expr_t * expr) { constraint_expr_t *h = NULL, *l = NULL, *newe; const constraint_expr_t *e; for (e = expr; e; e = e->next) { newe = malloc(sizeof(*newe)); if (!newe) goto oom; if (constraint_expr_init(newe) == -1) { free(newe); goto oom; } if (l) l->next = newe; else h = newe; l = newe; newe->expr_type = e->expr_type; newe->attr = e->attr; newe->op = e->op; if (newe->expr_type == CEXPR_NAMES) { if (newe->attr & CEXPR_TYPE) { if (type_set_cpy (newe->type_names, e->type_names)) goto oom; } else { if (ebitmap_cpy(&newe->names, &e->names)) goto oom; } } } return h; oom: constraint_expr_destroy(h); return NULL; } int define_constraint(constraint_expr_t * expr) { struct constraint_node *node; char *id; class_datum_t *cladatum; perm_datum_t *perdatum; ebitmap_t classmap; ebitmap_node_t *enode; constraint_expr_t *e; unsigned int i; int depth; unsigned char useexpr = 1; if (pass == 1) { while ((id = queue_remove(id_queue))) free(id); while ((id = queue_remove(id_queue))) free(id); return 0; } ebitmap_init(&classmap); depth = -1; for (e = expr; e; e = e->next) { switch (e->expr_type) { case CEXPR_NOT: if (depth < 0) { yyerror("illegal constraint expression"); goto bad; } break; case CEXPR_AND: case CEXPR_OR: if (depth < 1) { yyerror("illegal constraint expression"); goto bad; } depth--; break; case CEXPR_ATTR: case CEXPR_NAMES: if (e->attr & CEXPR_XTARGET) { yyerror("illegal constraint expression"); goto bad; /* only for validatetrans rules */ } if (depth == (CEXPR_MAXDEPTH - 1)) { yyerror("constraint expression is too deep"); goto bad; } depth++; break; default: yyerror("illegal constraint expression"); goto bad; } } if (depth != 0) { yyerror("illegal constraint expression"); goto bad; } while ((id = queue_remove(id_queue))) { if (!is_id_in_scope(SYM_CLASSES, id)) { yyerror2("class %s is not within scope", id); free(id); goto bad; } cladatum = (class_datum_t *) hashtab_search(policydbp->p_classes.table, (hashtab_key_t) id); if (!cladatum) { yyerror2("class %s is not defined", id); free(id); goto bad; } if (ebitmap_set_bit(&classmap, cladatum->s.value - 1, TRUE)) { yyerror("out of memory"); free(id); goto bad; } node = malloc(sizeof(struct constraint_node)); if (!node) { yyerror("out of memory"); free(node); goto bad; } memset(node, 0, sizeof(constraint_node_t)); if (useexpr) { node->expr = expr; useexpr = 0; } else { node->expr = constraint_expr_clone(expr); } if (!node->expr) { yyerror("out of memory"); free(node); goto bad; } node->permissions = 0; node->next = cladatum->constraints; cladatum->constraints = node; free(id); } while ((id = queue_remove(id_queue))) { ebitmap_for_each_positive_bit(&classmap, enode, i) { cladatum = policydbp->class_val_to_struct[i]; node = cladatum->constraints; if (strcmp(id, "*") == 0) { node->permissions = PERMISSION_MASK(cladatum->permissions.nprim); continue; } if (strcmp(id, "~") == 0) { node->permissions = ~node->permissions & PERMISSION_MASK(cladatum->permissions.nprim); if (node->permissions == 0) { yywarn("omitting constraint with no permission set"); cladatum->constraints = node->next; constraint_expr_destroy(node->expr); free(node); } continue; } perdatum = (perm_datum_t *) hashtab_search(cladatum-> permissions. table, (hashtab_key_t) id); if (!perdatum) { if (cladatum->comdatum) { perdatum = (perm_datum_t *) hashtab_search(cladatum-> comdatum-> permissions. table, (hashtab_key_t) id); } if (!perdatum) { yyerror2("permission %s is not" " defined for class %s", id, policydbp->p_class_val_to_name[i]); free(id); goto bad; } } node->permissions |= (UINT32_C(1) << (perdatum->s.value - 1)); } free(id); } ebitmap_destroy(&classmap); return 0; bad: ebitmap_destroy(&classmap); if (useexpr) constraint_expr_destroy(expr); return -1; } int define_validatetrans(constraint_expr_t * expr) { struct constraint_node *node; char *id; class_datum_t *cladatum; ebitmap_t classmap; constraint_expr_t *e; int depth; unsigned char useexpr = 1; if (pass == 1) { while ((id = queue_remove(id_queue))) free(id); return 0; } ebitmap_init(&classmap); depth = -1; for (e = expr; e; e = e->next) { switch (e->expr_type) { case CEXPR_NOT: if (depth < 0) { yyerror("illegal validatetrans expression"); goto bad; } break; case CEXPR_AND: case CEXPR_OR: if (depth < 1) { yyerror("illegal validatetrans expression"); goto bad; } depth--; break; case CEXPR_ATTR: case CEXPR_NAMES: if (depth == (CEXPR_MAXDEPTH - 1)) { yyerror("validatetrans expression is too deep"); goto bad; } depth++; break; default: yyerror("illegal validatetrans expression"); goto bad; } } if (depth != 0) { yyerror("illegal validatetrans expression"); goto bad; } while ((id = queue_remove(id_queue))) { if (!is_id_in_scope(SYM_CLASSES, id)) { yyerror2("class %s is not within scope", id); free(id); goto bad; } cladatum = (class_datum_t *) hashtab_search(policydbp->p_classes.table, (hashtab_key_t) id); if (!cladatum) { yyerror2("class %s is not defined", id); free(id); goto bad; } if (ebitmap_set_bit(&classmap, (cladatum->s.value - 1), TRUE)) { yyerror("out of memory"); free(id); goto bad; } node = malloc(sizeof(struct constraint_node)); if (!node) { yyerror("out of memory"); free(id); goto bad; } memset(node, 0, sizeof(constraint_node_t)); if (useexpr) { node->expr = expr; useexpr = 0; } else { node->expr = constraint_expr_clone(expr); } node->permissions = 0; node->next = cladatum->validatetrans; cladatum->validatetrans = node; free(id); } ebitmap_destroy(&classmap); return 0; bad: ebitmap_destroy(&classmap); if (useexpr) constraint_expr_destroy(expr); return -1; } uintptr_t define_cexpr(uint32_t expr_type, uintptr_t arg1, uintptr_t arg2) { struct constraint_expr *expr, *e1 = NULL, *e2; user_datum_t *user; role_datum_t *role; ebitmap_t negset; char *id; uint32_t val; int add = 1; if (pass == 1) { if (expr_type == CEXPR_NAMES) { while ((id = queue_remove(id_queue))) free(id); } return 1; /* any non-NULL value */ } if ((expr = malloc(sizeof(*expr))) == NULL || constraint_expr_init(expr) == -1) { yyerror("out of memory"); free(expr); return 0; } expr->expr_type = expr_type; switch (expr_type) { case CEXPR_NOT: e1 = NULL; e2 = (struct constraint_expr *)arg1; while (e2) { e1 = e2; e2 = e2->next; } if (!e1 || e1->next) { yyerror("illegal constraint expression"); constraint_expr_destroy(expr); return 0; } e1->next = expr; return arg1; case CEXPR_AND: case CEXPR_OR: e1 = NULL; e2 = (struct constraint_expr *)arg1; while (e2) { e1 = e2; e2 = e2->next; } if (!e1 || e1->next) { yyerror("illegal constraint expression"); constraint_expr_destroy(expr); return 0; } e1->next = (struct constraint_expr *)arg2; e1 = NULL; e2 = (struct constraint_expr *)arg2; while (e2) { e1 = e2; e2 = e2->next; } if (!e1 || e1->next) { yyerror("illegal constraint expression"); constraint_expr_destroy(expr); return 0; } e1->next = expr; return arg1; case CEXPR_ATTR: expr->attr = arg1; expr->op = arg2; return (uintptr_t) expr; case CEXPR_NAMES: add = 1; expr->attr = arg1; expr->op = arg2; ebitmap_init(&negset); while ((id = (char *)queue_remove(id_queue))) { if (expr->attr & CEXPR_USER) { if (!is_id_in_scope(SYM_USERS, id)) { yyerror2("user %s is not within scope", id); free(id); constraint_expr_destroy(expr); return 0; } user = (user_datum_t *) hashtab_search(policydbp-> p_users. table, (hashtab_key_t) id); if (!user) { yyerror2("unknown user %s", id); free(id); constraint_expr_destroy(expr); return 0; } val = user->s.value; } else if (expr->attr & CEXPR_ROLE) { if (!is_id_in_scope(SYM_ROLES, id)) { yyerror2("role %s is not within scope", id); constraint_expr_destroy(expr); free(id); return 0; } role = (role_datum_t *) hashtab_search(policydbp-> p_roles. table, (hashtab_key_t) id); if (!role) { yyerror2("unknown role %s", id); constraint_expr_destroy(expr); free(id); return 0; } val = role->s.value; } else if (expr->attr & CEXPR_TYPE) { if (set_types(expr->type_names, id, &add, 0)) { constraint_expr_destroy(expr); return 0; } continue; } else { yyerror("invalid constraint expression"); constraint_expr_destroy(expr); free(id); return 0; } if (ebitmap_set_bit(&expr->names, val - 1, TRUE)) { yyerror("out of memory"); ebitmap_destroy(&expr->names); free(id); constraint_expr_destroy(expr); return 0; } free(id); } ebitmap_destroy(&negset); return (uintptr_t) expr; default: break; } yyerror("invalid constraint expression"); constraint_expr_destroy(expr); return 0; } int define_conditional(cond_expr_t * expr, avrule_t * t_list, avrule_t * f_list) { cond_expr_t *e; int depth, booleans, tunables; cond_node_t cn, *cn_old; const cond_bool_datum_t *bool_var; /* expression cannot be NULL */ if (!expr) { yyerror("illegal conditional expression"); return -1; } if (!t_list) { if (!f_list) { /* empty is fine, destroy expression and return */ cond_expr_destroy(expr); return 0; } /* Invert */ t_list = f_list; f_list = NULL; expr = define_cond_expr(COND_NOT, expr, 0); if (!expr) { yyerror("unable to invert conditional expression"); return -1; } } /* verify expression */ depth = -1; booleans = 0; tunables = 0; for (e = expr; e; e = e->next) { switch (e->expr_type) { case COND_NOT: if (depth < 0) { yyerror ("illegal conditional expression; Bad NOT"); return -1; } break; case COND_AND: case COND_OR: case COND_XOR: case COND_EQ: case COND_NEQ: if (depth < 1) { yyerror ("illegal conditional expression; Bad binary op"); return -1; } depth--; break; case COND_BOOL: if (depth == (COND_EXPR_MAXDEPTH - 1)) { yyerror ("conditional expression is like totally too deep"); return -1; } depth++; bool_var = policydbp->bool_val_to_struct[e->boolean - 1]; if (bool_var->flags & COND_BOOL_FLAGS_TUNABLE) { tunables = 1; } else { booleans = 1; } break; default: yyerror("illegal conditional expression"); return -1; } } if (depth != 0) { yyerror("illegal conditional expression"); return -1; } if (booleans && tunables) { yyerror("illegal conditional expression; Contains boolean and tunable"); return -1; } /* use tmp conditional node to partially build new node */ memset(&cn, 0, sizeof(cn)); cn.expr = expr; cn.avtrue_list = t_list; cn.avfalse_list = f_list; /* normalize/precompute expression */ if (cond_normalize_expr(policydbp, &cn) < 0) { yyerror("problem normalizing conditional expression"); return -1; } /* get the existing conditional node, or create a new one */ cn_old = get_current_cond_list(&cn); if (!cn_old) { return -1; } append_cond_list(&cn); /* note that there is no check here for duplicate rules, nor * check that rule already exists in base -- that will be * handled during conditional expansion, in expand.c */ cn.avtrue_list = NULL; cn.avfalse_list = NULL; cond_node_destroy(&cn); return 0; } cond_expr_t *define_cond_expr(uint32_t expr_type, void *arg1, void *arg2) { struct cond_expr *expr, *e1 = NULL, *e2; cond_bool_datum_t *bool_var; char *id; /* expressions are handled in the second pass */ if (pass == 1) { if (expr_type == COND_BOOL) { while ((id = queue_remove(id_queue))) { free(id); } } return (cond_expr_t *) 1; /* any non-NULL value */ } /* create a new expression struct */ expr = malloc(sizeof(struct cond_expr)); if (!expr) { yyerror("out of memory"); return NULL; } memset(expr, 0, sizeof(cond_expr_t)); expr->expr_type = expr_type; /* create the type asked for */ switch (expr_type) { case COND_NOT: e1 = NULL; e2 = (struct cond_expr *)arg1; while (e2) { e1 = e2; e2 = e2->next; } if (!e1 || e1->next) { yyerror("illegal conditional NOT expression"); free(expr); return NULL; } e1->next = expr; return (struct cond_expr *)arg1; case COND_AND: case COND_OR: case COND_XOR: case COND_EQ: case COND_NEQ: e1 = NULL; e2 = (struct cond_expr *)arg1; while (e2) { e1 = e2; e2 = e2->next; } if (!e1 || e1->next) { yyerror ("illegal left side of conditional binary op expression"); free(expr); return NULL; } e1->next = (struct cond_expr *)arg2; e1 = NULL; e2 = (struct cond_expr *)arg2; while (e2) { e1 = e2; e2 = e2->next; } if (!e1 || e1->next) { yyerror ("illegal right side of conditional binary op expression"); free(expr); return NULL; } e1->next = expr; return (struct cond_expr *)arg1; case COND_BOOL: id = (char *)queue_remove(id_queue); if (!id) { yyerror("bad conditional; expected boolean id"); free(id); free(expr); return NULL; } if (!is_id_in_scope(SYM_BOOLS, id)) { yyerror2("boolean %s is not within scope", id); free(id); free(expr); return NULL; } bool_var = (cond_bool_datum_t *) hashtab_search(policydbp->p_bools. table, (hashtab_key_t) id); if (!bool_var) { yyerror2("unknown boolean %s in conditional expression", id); free(expr); free(id); return NULL; } expr->boolean = bool_var->s.value; free(id); return expr; default: yyerror("illegal conditional expression"); free(expr); return NULL; } } static int set_user_roles(role_set_t * set, char *id) { role_datum_t *r; if (strcmp(id, "*") == 0) { free(id); yyerror("* is not allowed in user declarations"); return -1; } if (strcmp(id, "~") == 0) { free(id); yyerror("~ is not allowed in user declarations"); return -1; } if (!is_id_in_scope(SYM_ROLES, id)) { yyerror2("role %s is not within scope", id); free(id); return -1; } r = hashtab_search(policydbp->p_roles.table, id); if (!r) { yyerror2("unknown role %s", id); free(id); return -1; } free(id); if (ebitmap_set_bit(&set->roles, r->s.value - 1, TRUE)) goto oom; return 0; oom: yyerror("out of memory"); return -1; } static int parse_categories(char *id, level_datum_t * levdatum, ebitmap_t * cats) { cat_datum_t *cdatum; uint32_t range_start, range_end, i; if (id_has_dot(id)) { char *id_start = id; char *id_end = strchr(id, '.'); *(id_end++) = '\0'; cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table, (hashtab_key_t) id_start); if (!cdatum) { yyerror2("unknown category %s", id_start); return -1; } range_start = cdatum->s.value - 1; cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table, (hashtab_key_t) id_end); if (!cdatum) { yyerror2("unknown category %s", id_end); return -1; } range_end = cdatum->s.value - 1; if (range_end < range_start) { yyerror2("category range %d-%d is invalid", range_start, range_end); return -1; } } else { cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table, (hashtab_key_t) id); if (!cdatum) { yyerror2("unknown category %s", id); return -1; } range_start = range_end = cdatum->s.value - 1; } for (i = range_start; i <= range_end; i++) { if (!ebitmap_get_bit(&levdatum->level->cat, i)) { uint32_t level_value = levdatum->level->sens - 1; policydb_index_others(NULL, policydbp, 0); yyerror2("category %s can not be associated " "with level %s", policydbp->p_cat_val_to_name[i], policydbp->p_sens_val_to_name[level_value]); return -1; } if (ebitmap_set_bit(cats, i, TRUE)) { yyerror("out of memory"); return -1; } } return 0; } static int mls_semantic_cats_merge(mls_semantic_cat_t ** dst, const mls_semantic_cat_t * src) { mls_semantic_cat_t *new; while (src) { new = (mls_semantic_cat_t *) malloc(sizeof(mls_semantic_cat_t)); if (!new) return -1; mls_semantic_cat_init(new); new->low = src->low; new->high = src->high; new->next = *dst; *dst = new; src = src->next; } return 0; } static int mls_add_or_check_level(mls_semantic_level_t *dst, const mls_semantic_level_t *src) { if (!dst->sens) { if (mls_semantic_level_cpy(dst, src) < 0) { yyerror("out of memory"); return -1; } } else { if (dst->sens != src->sens) { return -1; } /* Duplicate cats won't cause problems, but different cats will * result in an error during expansion */ if (mls_semantic_cats_merge(&dst->cat, src->cat) < 0) { yyerror("out of memory"); return -1; } } return 0; } static int parse_semantic_categories(char *id, level_datum_t * levdatum __attribute__ ((unused)), mls_semantic_cat_t ** cats) { cat_datum_t *cdatum; mls_semantic_cat_t *newcat; unsigned int range_start, range_end; if (id_has_dot(id)) { char *id_start = id; char *id_end = strchr(id, '.'); *(id_end++) = '\0'; cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table, (hashtab_key_t) id_start); if (!cdatum) { yyerror2("unknown category %s", id_start); return -1; } range_start = cdatum->s.value; cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table, (hashtab_key_t) id_end); if (!cdatum) { yyerror2("unknown category %s", id_end); return -1; } range_end = cdatum->s.value; } else { cdatum = (cat_datum_t *) hashtab_search(policydbp->p_cats.table, (hashtab_key_t) id); if (!cdatum) { yyerror2("unknown category %s", id); return -1; } range_start = range_end = cdatum->s.value; } newcat = (mls_semantic_cat_t *) malloc(sizeof(mls_semantic_cat_t)); if (!newcat) { yyerror("out of memory"); return -1; } mls_semantic_cat_init(newcat); newcat->next = *cats; newcat->low = range_start; newcat->high = range_end; *cats = newcat; return 0; } int define_user(void) { const char *username; char *id; user_datum_t *usrdatum, *usr_global; level_datum_t *levdatum; int l; if (pass == 1) { while ((id = queue_remove(id_queue))) free(id); if (mlspol) { while ((id = queue_remove(id_queue))) free(id); id = queue_remove(id_queue); free(id); for (l = 0; l < 2; l++) { while ((id = queue_remove(id_queue))) { free(id); } id = queue_remove(id_queue); if (!id) break; free(id); } } return 0; } username = queue_head(id_queue); if (!username) { yyerror("no user name"); return -1; } id = strdup(username); if ((usrdatum = declare_user()) == NULL) { free(id); return -1; } usr_global = hashtab_search(policydbp->p_users.table, (hashtab_key_t) id); free(id); while ((id = queue_remove(id_queue))) { if (set_user_roles(&usrdatum->roles, id)) return -1; } if (mlspol) { id = queue_remove(id_queue); if (!id) { yyerror("no default level specified for user"); return -1; } levdatum = (level_datum_t *) hashtab_search(policydbp->p_levels.table, (hashtab_key_t) id); if (!levdatum) { yyerror2("unknown sensitivity %s used in user" " level definition", id); free(id); return -1; } free(id); usrdatum->dfltlevel.sens = levdatum->level->sens; while ((id = queue_remove(id_queue))) { /* This will add to any already existing categories */ if (parse_semantic_categories(id, levdatum, &usrdatum->dfltlevel.cat)) { free(id); return -1; } free(id); } id = queue_remove(id_queue); for (l = 0; l < 2; l++) { levdatum = (level_datum_t *) hashtab_search(policydbp->p_levels.table, (hashtab_key_t) id); if (!levdatum) { yyerror2("unknown sensitivity %s used in user" " range definition", id); free(id); return -1; } free(id); usrdatum->range.level[l].sens = levdatum->level->sens; while ((id = queue_remove(id_queue))) { /* This will add to any already existing categories */ if (parse_semantic_categories(id, levdatum, &usrdatum->range.level[l].cat)) { free(id); return -1; } free(id); } id = queue_remove(id_queue); if (!id) break; } if (l == 0) { if (mls_semantic_level_cpy(&usrdatum->range.level[1], &usrdatum->range.level[0])) { yyerror("out of memory"); return -1; } } if (usr_global && usr_global != usrdatum) { if (mls_add_or_check_level(&usr_global->dfltlevel, &usrdatum->dfltlevel)) { yyerror("Problem with user default level"); return -1; } if (mls_add_or_check_level(&usr_global->range.level[0], &usrdatum->range.level[0])) { yyerror("Problem with user low level"); return -1; } if (mls_add_or_check_level(&usr_global->range.level[1], &usrdatum->range.level[1])) { yyerror("Problem with user high level"); return -1; } } } return 0; } static int parse_security_context(context_struct_t * c) { char *id; role_datum_t *role; type_datum_t *typdatum; user_datum_t *usrdatum; level_datum_t *levdatum; int l; if (pass == 1) { id = queue_remove(id_queue); free(id); /* user */ id = queue_remove(id_queue); free(id); /* role */ id = queue_remove(id_queue); free(id); /* type */ if (mlspol) { id = queue_remove(id_queue); free(id); for (l = 0; l < 2; l++) { while ((id = queue_remove(id_queue))) { free(id); } id = queue_remove(id_queue); if (!id) break; free(id); } } return 0; } /* check context c to make sure ok to dereference c later */ if (c == NULL) { yyerror("null context pointer!"); return -1; } context_init(c); /* extract the user */ id = queue_remove(id_queue); if (!id) { yyerror("no effective user?"); goto bad; } if (!is_id_in_scope(SYM_USERS, id)) { yyerror2("user %s is not within scope", id); free(id); goto bad; } usrdatum = (user_datum_t *) hashtab_search(policydbp->p_users.table, (hashtab_key_t) id); if (!usrdatum) { yyerror2("user %s is not defined", id); free(id); goto bad; } c->user = usrdatum->s.value; /* no need to keep the user name */ free(id); /* extract the role */ id = (char *)queue_remove(id_queue); if (!id) { yyerror("no role name for sid context definition?"); return -1; } if (!is_id_in_scope(SYM_ROLES, id)) { yyerror2("role %s is not within scope", id); free(id); return -1; } role = (role_datum_t *) hashtab_search(policydbp->p_roles.table, (hashtab_key_t) id); if (!role) { yyerror2("role %s is not defined", id); free(id); return -1; } c->role = role->s.value; /* no need to keep the role name */ free(id); /* extract the type */ id = (char *)queue_remove(id_queue); if (!id) { yyerror("no type name for sid context definition?"); return -1; } if (!is_id_in_scope(SYM_TYPES, id)) { yyerror2("type %s is not within scope", id); free(id); return -1; } typdatum = (type_datum_t *) hashtab_search(policydbp->p_types.table, (hashtab_key_t) id); if (!typdatum || typdatum->flavor == TYPE_ATTRIB) { yyerror2("type %s is not defined or is an attribute", id); free(id); return -1; } c->type = typdatum->s.value; /* no need to keep the type name */ free(id); if (mlspol) { /* extract the low sensitivity */ id = (char *)queue_remove(id_queue); if (!id) { yyerror("no sensitivity name for sid context" " definition?"); return -1; } for (l = 0; l < 2; l++) { levdatum = (level_datum_t *) hashtab_search(policydbp->p_levels.table, (hashtab_key_t) id); if (!levdatum) { yyerror2("Sensitivity %s is not defined", id); free(id); return -1; } free(id); c->range.level[l].sens = levdatum->level->sens; /* extract low category set */ while ((id = queue_remove(id_queue))) { if (parse_categories(id, levdatum, &c->range.level[l].cat)) { free(id); return -1; } free(id); } /* extract high sensitivity */ id = (char *)queue_remove(id_queue); if (!id) break; } if (l == 0) { c->range.level[1].sens = c->range.level[0].sens; if (ebitmap_cpy(&c->range.level[1].cat, &c->range.level[0].cat)) { yyerror("out of memory"); goto bad; } } } if (!policydb_context_isvalid(policydbp, c)) { yyerror("invalid security context"); goto bad; } return 0; bad: context_destroy(c); return -1; } int define_initial_sid_context(void) { char *id; ocontext_t *c, *head; if (pass == 1) { id = (char *)queue_remove(id_queue); free(id); parse_security_context(NULL); return 0; } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no sid name for SID context definition?"); return -1; } head = policydbp->ocontexts[OCON_ISID]; for (c = head; c; c = c->next) { if (!strcmp(id, c->u.name)) break; } if (!c) { yyerror2("SID %s is not defined", id); free(id); return -1; } if (c->context[0].user) { yyerror2("The context for SID %s is multiply defined", id); free(id); return -1; } /* no need to keep the sid name */ free(id); if (parse_security_context(&c->context[0])) return -1; return 0; } int define_fs_context(unsigned int major, unsigned int minor) { ocontext_t *newc, *c, *head; if (policydbp->target_platform != SEPOL_TARGET_SELINUX) { yyerror("fscon not supported for target"); return -1; } if (pass == 1) { parse_security_context(NULL); parse_security_context(NULL); return 0; } newc = (ocontext_t *) malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); return -1; } memset(newc, 0, sizeof(ocontext_t)); newc->u.name = (char *)malloc(6); if (!newc->u.name) { yyerror("out of memory"); free(newc); return -1; } sprintf(newc->u.name, "%02x:%02x", major, minor); if (parse_security_context(&newc->context[0])) { free(newc->u.name); free(newc); return -1; } if (parse_security_context(&newc->context[1])) { context_destroy(&newc->context[0]); free(newc->u.name); free(newc); return -1; } head = policydbp->ocontexts[OCON_FS]; for (c = head; c; c = c->next) { if (!strcmp(newc->u.name, c->u.name)) { yyerror2("duplicate entry for file system %s", newc->u.name); context_destroy(&newc->context[0]); context_destroy(&newc->context[1]); free(newc->u.name); free(newc); return -1; } } newc->next = head; policydbp->ocontexts[OCON_FS] = newc; return 0; } int define_pirq_context(unsigned int pirq) { ocontext_t *newc, *c, *l, *head; if (policydbp->target_platform != SEPOL_TARGET_XEN) { yyerror("pirqcon not supported for target"); return -1; } if (pass == 1) { parse_security_context(NULL); return 0; } newc = malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); return -1; } memset(newc, 0, sizeof(ocontext_t)); newc->u.pirq = pirq; if (parse_security_context(&newc->context[0])) { free(newc); return -1; } head = policydbp->ocontexts[OCON_XEN_PIRQ]; for (l = NULL, c = head; c; l = c, c = c->next) { unsigned int pirq2; pirq2 = c->u.pirq; if (pirq == pirq2) { yyerror2("duplicate pirqcon entry for %d ", pirq); goto bad; } } if (l) l->next = newc; else policydbp->ocontexts[OCON_XEN_PIRQ] = newc; return 0; bad: free(newc); return -1; } int define_iomem_context(uint64_t low, uint64_t high) { ocontext_t *newc, *c, *l, *head; if (policydbp->target_platform != SEPOL_TARGET_XEN) { yyerror("iomemcon not supported for target"); return -1; } if (pass == 1) { parse_security_context(NULL); return 0; } newc = malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); return -1; } memset(newc, 0, sizeof(ocontext_t)); newc->u.iomem.low_iomem = low; newc->u.iomem.high_iomem = high; if (low > high) { yyerror2("low memory 0x%"PRIx64" exceeds high memory 0x%"PRIx64"", low, high); free(newc); return -1; } if (parse_security_context(&newc->context[0])) { free(newc); return -1; } head = policydbp->ocontexts[OCON_XEN_IOMEM]; for (l = NULL, c = head; c; l = c, c = c->next) { uint64_t low2, high2; low2 = c->u.iomem.low_iomem; high2 = c->u.iomem.high_iomem; if (low <= high2 && low2 <= high) { yyerror2("iomemcon entry for 0x%"PRIx64"-0x%"PRIx64" overlaps with " "earlier entry 0x%"PRIx64"-0x%"PRIx64"", low, high, low2, high2); goto bad; } } if (l) l->next = newc; else policydbp->ocontexts[OCON_XEN_IOMEM] = newc; return 0; bad: free(newc); return -1; } int define_ioport_context(unsigned long low, unsigned long high) { ocontext_t *newc, *c, *l, *head; if (policydbp->target_platform != SEPOL_TARGET_XEN) { yyerror("ioportcon not supported for target"); return -1; } if (pass == 1) { parse_security_context(NULL); return 0; } newc = malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); return -1; } memset(newc, 0, sizeof(ocontext_t)); newc->u.ioport.low_ioport = low; newc->u.ioport.high_ioport = high; if (low > high) { yyerror2("low ioport 0x%lx exceeds high ioport 0x%lx", low, high); free(newc); return -1; } if (parse_security_context(&newc->context[0])) { free(newc); return -1; } head = policydbp->ocontexts[OCON_XEN_IOPORT]; for (l = NULL, c = head; c; l = c, c = c->next) { uint32_t low2, high2; low2 = c->u.ioport.low_ioport; high2 = c->u.ioport.high_ioport; if (low <= high2 && low2 <= high) { yyerror2("ioportcon entry for 0x%lx-0x%lx overlaps with" "earlier entry 0x%x-0x%x", low, high, low2, high2); goto bad; } } if (l) l->next = newc; else policydbp->ocontexts[OCON_XEN_IOPORT] = newc; return 0; bad: free(newc); return -1; } int define_pcidevice_context(unsigned long device) { ocontext_t *newc, *c, *l, *head; if (policydbp->target_platform != SEPOL_TARGET_XEN) { yyerror("pcidevicecon not supported for target"); return -1; } if (pass == 1) { parse_security_context(NULL); return 0; } newc = malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); return -1; } memset(newc, 0, sizeof(ocontext_t)); newc->u.device = device; if (parse_security_context(&newc->context[0])) { free(newc); return -1; } head = policydbp->ocontexts[OCON_XEN_PCIDEVICE]; for (l = NULL, c = head; c; l = c, c = c->next) { unsigned int device2; device2 = c->u.device; if (device == device2) { yyerror2("duplicate pcidevicecon entry for 0x%lx", device); goto bad; } } if (l) l->next = newc; else policydbp->ocontexts[OCON_XEN_PCIDEVICE] = newc; return 0; bad: free(newc); return -1; } int define_devicetree_context(void) { ocontext_t *newc, *c, *l, *head; if (policydbp->target_platform != SEPOL_TARGET_XEN) { yyerror("devicetreecon not supported for target"); return -1; } if (pass == 1) { free(queue_remove(id_queue)); parse_security_context(NULL); return 0; } newc = malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); return -1; } memset(newc, 0, sizeof(ocontext_t)); newc->u.name = (char *)queue_remove(id_queue); if (!newc->u.name) { free(newc); return -1; } if (parse_security_context(&newc->context[0])) { free(newc->u.name); free(newc); return -1; } head = policydbp->ocontexts[OCON_XEN_DEVICETREE]; for (l = NULL, c = head; c; l = c, c = c->next) { if (strcmp(newc->u.name, c->u.name) == 0) { yyerror2("duplicate devicetree entry for '%s'", newc->u.name); goto bad; } } if (l) l->next = newc; else policydbp->ocontexts[OCON_XEN_DEVICETREE] = newc; return 0; bad: free(newc->u.name); free(newc); return -1; } int define_port_context(unsigned int low, unsigned int high) { ocontext_t *newc, *c, *l, *head; unsigned int protocol; char *id; if (policydbp->target_platform != SEPOL_TARGET_SELINUX) { yyerror("portcon not supported for target"); return -1; } if (pass == 1) { id = (char *)queue_remove(id_queue); free(id); parse_security_context(NULL); return 0; } newc = malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); return -1; } memset(newc, 0, sizeof(ocontext_t)); id = (char *)queue_remove(id_queue); if (!id) { free(newc); return -1; } if ((strcmp(id, "tcp") == 0) || (strcmp(id, "TCP") == 0)) { protocol = IPPROTO_TCP; } else if ((strcmp(id, "udp") == 0) || (strcmp(id, "UDP") == 0)) { protocol = IPPROTO_UDP; } else if ((strcmp(id, "dccp") == 0) || (strcmp(id, "DCCP") == 0)) { protocol = IPPROTO_DCCP; } else if ((strcmp(id, "sctp") == 0) || (strcmp(id, "SCTP") == 0)) { protocol = IPPROTO_SCTP; } else { yyerror2("unrecognized protocol %s", id); goto bad; } newc->u.port.protocol = protocol; newc->u.port.low_port = low; newc->u.port.high_port = high; if (low > high) { yyerror2("low port %d exceeds high port %d", low, high); goto bad; } if (parse_security_context(&newc->context[0])) { goto bad; } /* Preserve the matching order specified in the configuration. */ head = policydbp->ocontexts[OCON_PORT]; for (l = NULL, c = head; c; l = c, c = c->next) { unsigned int prot2, low2, high2; prot2 = c->u.port.protocol; low2 = c->u.port.low_port; high2 = c->u.port.high_port; if (protocol != prot2) continue; if (low == low2 && high == high2) { yyerror2("duplicate portcon entry for %s %d-%d ", id, low, high); goto bad; } if (low2 <= low && high2 >= high) { yyerror2("portcon entry for %s %d-%d hidden by earlier " "entry for %d-%d", id, low, high, low2, high2); goto bad; } } if (l) l->next = newc; else policydbp->ocontexts[OCON_PORT] = newc; free(id); return 0; bad: free(id); free(newc); return -1; } int define_ibpkey_context(unsigned int low, unsigned int high) { ocontext_t *newc, *c, *l, *head; struct in6_addr subnet_prefix; char *id; int rc = 0; if (policydbp->target_platform != SEPOL_TARGET_SELINUX) { yyerror("ibpkeycon not supported for target"); return -1; } if (pass == 1) { id = (char *)queue_remove(id_queue); free(id); parse_security_context(NULL); return 0; } newc = malloc(sizeof(*newc)); if (!newc) { yyerror("out of memory"); return -1; } memset(newc, 0, sizeof(*newc)); id = queue_remove(id_queue); if (!id) { yyerror("failed to read the subnet prefix"); rc = -1; goto out; } rc = inet_pton(AF_INET6, id, &subnet_prefix); free(id); if (rc < 1) { yyerror("failed to parse the subnet prefix"); if (rc == 0) rc = -1; goto out; } if (subnet_prefix.s6_addr32[2] || subnet_prefix.s6_addr32[3]) { yyerror("subnet prefix should be 0's in the low order 64 bits."); rc = -1; goto out; } if (low > 0xffff || high > 0xffff) { yyerror("pkey value too large, pkeys are 16 bits."); rc = -1; goto out; } memcpy(&newc->u.ibpkey.subnet_prefix, &subnet_prefix.s6_addr[0], sizeof(newc->u.ibpkey.subnet_prefix)); newc->u.ibpkey.low_pkey = low; newc->u.ibpkey.high_pkey = high; if (low > high) { yyerror2("low pkey %d exceeds high pkey %d", low, high); rc = -1; goto out; } rc = parse_security_context(&newc->context[0]); if (rc) goto out; /* Preserve the matching order specified in the configuration. */ head = policydbp->ocontexts[OCON_IBPKEY]; for (l = NULL, c = head; c; l = c, c = c->next) { unsigned int low2, high2; low2 = c->u.ibpkey.low_pkey; high2 = c->u.ibpkey.high_pkey; if (low == low2 && high == high2 && c->u.ibpkey.subnet_prefix == newc->u.ibpkey.subnet_prefix) { yyerror2("duplicate ibpkeycon entry for %d-%d ", low, high); rc = -1; goto out; } if (low2 <= low && high2 >= high && c->u.ibpkey.subnet_prefix == newc->u.ibpkey.subnet_prefix) { yyerror2("ibpkeycon entry for %d-%d hidden by earlier entry for %d-%d", low, high, low2, high2); rc = -1; goto out; } } if (l) l->next = newc; else policydbp->ocontexts[OCON_IBPKEY] = newc; return 0; out: free(newc); return rc; } int define_ibendport_context(unsigned int port) { ocontext_t *newc, *c, *l, *head; char *id; int rc = 0; if (policydbp->target_platform != SEPOL_TARGET_SELINUX) { yyerror("ibendportcon not supported for target"); return -1; } if (pass == 1) { id = (char *)queue_remove(id_queue); free(id); parse_security_context(NULL); return 0; } if (port > 0xff || port == 0) { yyerror2("Invalid ibendport port number %d, should be 0 < port < 256", port); return -1; } newc = malloc(sizeof(*newc)); if (!newc) { yyerror("out of memory"); return -1; } memset(newc, 0, sizeof(*newc)); newc->u.ibendport.dev_name = queue_remove(id_queue); if (!newc->u.ibendport.dev_name) { yyerror("failed to read infiniband device name."); rc = -1; goto out; } if (strlen(newc->u.ibendport.dev_name) > IB_DEVICE_NAME_MAX - 1) { yyerror2("infiniband device name %s exceeds max length of 63.", newc->u.ibendport.dev_name); rc = -1; goto out; } newc->u.ibendport.port = port; if (parse_security_context(&newc->context[0])) { free(newc); return -1; } /* Preserve the matching order specified in the configuration. */ head = policydbp->ocontexts[OCON_IBENDPORT]; for (l = NULL, c = head; c; l = c, c = c->next) { unsigned int port2; port2 = c->u.ibendport.port; if (port == port2 && !strcmp(c->u.ibendport.dev_name, newc->u.ibendport.dev_name)) { yyerror2("duplicate ibendportcon entry for %s port %u", newc->u.ibendport.dev_name, port); rc = -1; goto out; } } if (l) l->next = newc; else policydbp->ocontexts[OCON_IBENDPORT] = newc; return 0; out: free(newc->u.ibendport.dev_name); free(newc); return rc; } int define_netif_context(void) { ocontext_t *newc, *c, *head; if (policydbp->target_platform != SEPOL_TARGET_SELINUX) { yyerror("netifcon not supported for target"); return -1; } if (pass == 1) { free(queue_remove(id_queue)); parse_security_context(NULL); parse_security_context(NULL); return 0; } newc = (ocontext_t *) malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); return -1; } memset(newc, 0, sizeof(ocontext_t)); newc->u.name = (char *)queue_remove(id_queue); if (!newc->u.name) { free(newc); return -1; } if (parse_security_context(&newc->context[0])) { free(newc->u.name); free(newc); return -1; } if (parse_security_context(&newc->context[1])) { context_destroy(&newc->context[0]); free(newc->u.name); free(newc); return -1; } head = policydbp->ocontexts[OCON_NETIF]; for (c = head; c; c = c->next) { if (!strcmp(newc->u.name, c->u.name)) { yyerror2("duplicate entry for network interface %s", newc->u.name); context_destroy(&newc->context[0]); context_destroy(&newc->context[1]); free(newc->u.name); free(newc); return -1; } } newc->next = head; policydbp->ocontexts[OCON_NETIF] = newc; return 0; } static int insert_ipv4_node(ocontext_t *newc) { ocontext_t *c, *l; char addr[INET_ADDRSTRLEN]; char mask[INET_ADDRSTRLEN]; /* Create order of most specific to least retaining the order specified in the configuration. */ for (l = NULL, c = policydbp->ocontexts[OCON_NODE]; c; l = c, c = c->next) { if (newc->u.node.mask == c->u.node.mask && newc->u.node.addr == c->u.node.addr) { yyerror2("duplicate entry for network node %s %s", inet_ntop(AF_INET, &newc->u.node.addr, addr, INET_ADDRSTRLEN) ?: "", inet_ntop(AF_INET, &newc->u.node.mask, mask, INET_ADDRSTRLEN) ?: ""); context_destroy(&newc->context[0]); free(newc); return -1; } if (newc->u.node.mask > c->u.node.mask) break; } newc->next = c; if (l) l->next = newc; else policydbp->ocontexts[OCON_NODE] = newc; return 0; } int define_ipv4_node_context(void) { char *id; int rc = 0; struct in_addr addr, mask; ocontext_t *newc; if (policydbp->target_platform != SEPOL_TARGET_SELINUX) { yyerror("nodecon not supported for target"); return -1; } if (pass == 1) { free(queue_remove(id_queue)); free(queue_remove(id_queue)); parse_security_context(NULL); return 0; } id = queue_remove(id_queue); if (!id) { yyerror("failed to read ipv4 address"); return -1; } rc = inet_pton(AF_INET, id, &addr); if (rc < 1) { yyerror2("failed to parse ipv4 address %s", id); free(id); return -1; } free(id); id = queue_remove(id_queue); if (!id) { yyerror("failed to read ipv4 address"); return -1; } rc = inet_pton(AF_INET, id, &mask); if (rc < 1) { yyerror2("failed to parse ipv4 mask %s", id); free(id); return -1; } free(id); if (mask.s_addr != 0 && ((~be32toh(mask.s_addr) + 1) & ~be32toh(mask.s_addr)) != 0) { yywarn("ipv4 mask is not contiguous"); } if ((~mask.s_addr & addr.s_addr) != 0) { yywarn("host bits in ipv4 address set"); } newc = malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); return -1; } memset(newc, 0, sizeof(ocontext_t)); newc->u.node.addr = addr.s_addr; newc->u.node.mask = mask.s_addr; if (parse_security_context(&newc->context[0])) { free(newc); return -1; } return insert_ipv4_node(newc); } int define_ipv4_cidr_node_context(void) { char *endptr, *id, *split; unsigned long mask_bits; uint32_t mask; struct in_addr addr; ocontext_t *newc; int rc; if (policydbp->target_platform != SEPOL_TARGET_SELINUX) { yyerror("nodecon not supported for target"); return -1; } if (pass == 1) { free(queue_remove(id_queue)); parse_security_context(NULL); return 0; } id = queue_remove(id_queue); if (!id) { yyerror("failed to read IPv4 address"); return -1; } split = strchr(id, '/'); if (!split) { yyerror2("invalid IPv4 CIDR notation: %s", id); free(id); return -1; } *split = '\0'; rc = inet_pton(AF_INET, id, &addr); if (rc < 1) { yyerror2("failed to parse IPv4 address %s", id); free(id); return -1; } errno = 0; mask_bits = strtoul(split + 1, &endptr, 10); if (errno || *endptr != '\0' || mask_bits > 32) { yyerror2("invalid mask in IPv4 CIDR notation: %s", split + 1); free(id); return -1; } free(id); if (mask_bits == 0) { yywarn("IPv4 CIDR mask of 0, matching all IPs"); mask = 0; } else { mask = ~((UINT32_C(1) << (32 - mask_bits)) - 1); mask = htobe32(mask); } if ((~mask & addr.s_addr) != 0) yywarn("host bits in IPv4 address set"); newc = calloc(1, sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); return -1; } newc->u.node.addr = addr.s_addr & mask; newc->u.node.mask = mask; if (parse_security_context(&newc->context[0])) { free(newc); return -1; } return insert_ipv4_node(newc); } static int ipv6_is_mask_contiguous(const struct in6_addr *mask) { int filled = 1; unsigned i; for (i = 0; i < 16; i++) { if ((((~mask->s6_addr[i] & 0xFF) + 1) & (~mask->s6_addr[i] & 0xFF)) != 0) { return 0; } if (!filled && mask->s6_addr[i] != 0) { return 0; } if (filled && mask->s6_addr[i] != 0xFF) { filled = 0; } } return 1; } static int ipv6_has_host_bits_set(const struct in6_addr *addr, const struct in6_addr *mask) { unsigned i; for (i = 0; i < 16; i++) { if ((addr->s6_addr[i] & ~mask->s6_addr[i]) != 0) { return 1; } } return 0; } static void ipv6_cidr_bits_to_mask(unsigned long cidr_bits, struct in6_addr *mask) { unsigned i; for (i = 0; i < 4; i++) { if (cidr_bits == 0) { mask->s6_addr32[i] = 0; } else if (cidr_bits >= 32) { mask->s6_addr32[i] = ~UINT32_C(0); } else { mask->s6_addr32[i] = htobe32(~((UINT32_C(1) << (32 - cidr_bits)) - 1)); } if (cidr_bits >= 32) cidr_bits -= 32; else cidr_bits = 0; } } static void ipv6_apply_mask(struct in6_addr *restrict addr, const struct in6_addr *restrict mask) { unsigned i; for (i = 0; i < 4; i++) addr->s6_addr32[i] &= mask->s6_addr32[i]; } static int insert_ipv6_node(ocontext_t *newc) { ocontext_t *c, *l; char addr[INET6_ADDRSTRLEN]; char mask[INET6_ADDRSTRLEN]; /* Create order of most specific to least retaining the order specified in the configuration. */ for (l = NULL, c = policydbp->ocontexts[OCON_NODE6]; c; l = c, c = c->next) { if (memcmp(&newc->u.node6.mask, &c->u.node6.mask, 16) == 0 && memcmp(&newc->u.node6.addr, &c->u.node6.addr, 16) == 0) { yyerror2("duplicate entry for network node %s %s", inet_ntop(AF_INET6, &newc->u.node6.addr, addr, INET6_ADDRSTRLEN) ?: "", inet_ntop(AF_INET6, &newc->u.node6.mask, mask, INET6_ADDRSTRLEN) ?: ""); context_destroy(&newc->context[0]); free(newc); return -1; } if (memcmp(&newc->u.node6.mask, &c->u.node6.mask, 16) > 0) break; } newc->next = c; if (l) l->next = newc; else policydbp->ocontexts[OCON_NODE6] = newc; return 0; } int define_ipv6_node_context(void) { char *id; int rc = 0; struct in6_addr addr, mask; ocontext_t *newc; if (policydbp->target_platform != SEPOL_TARGET_SELINUX) { yyerror("nodecon not supported for target"); return -1; } if (pass == 1) { free(queue_remove(id_queue)); free(queue_remove(id_queue)); parse_security_context(NULL); return 0; } id = queue_remove(id_queue); if (!id) { yyerror("failed to read ipv6 address"); return -1; } rc = inet_pton(AF_INET6, id, &addr); if (rc < 1) { yyerror2("failed to parse ipv6 address %s", id); free(id); return -1; } free(id); id = queue_remove(id_queue); if (!id) { yyerror("failed to read ipv6 address"); return -1; } rc = inet_pton(AF_INET6, id, &mask); if (rc < 1) { yyerror2("failed to parse ipv6 mask %s", id); free(id); return -1; } free(id); if (!ipv6_is_mask_contiguous(&mask)) { yywarn("ipv6 mask is not contiguous"); } if (ipv6_has_host_bits_set(&addr, &mask)) { yywarn("host bits in ipv6 address set"); } newc = malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); return -1; } memset(newc, 0, sizeof(ocontext_t)); memcpy(&newc->u.node6.addr[0], &addr.s6_addr[0], 16); memcpy(&newc->u.node6.mask[0], &mask.s6_addr[0], 16); if (parse_security_context(&newc->context[0])) { free(newc); return -1; } return insert_ipv6_node(newc); } int define_ipv6_cidr_node_context(void) { char *endptr, *id, *split; unsigned long mask_bits; int rc; struct in6_addr addr, mask; ocontext_t *newc; if (policydbp->target_platform != SEPOL_TARGET_SELINUX) { yyerror("nodecon not supported for target"); return -1; } if (pass == 1) { free(queue_remove(id_queue)); parse_security_context(NULL); return 0; } id = queue_remove(id_queue); if (!id) { yyerror("failed to read IPv6 address"); return -1; } split = strchr(id, '/'); if (!split) { yyerror2("invalid IPv6 CIDR notation: %s", id); free(id); return -1; } *split = '\0'; rc = inet_pton(AF_INET6, id, &addr); if (rc < 1) { yyerror2("failed to parse IPv6 address %s", id); free(id); return -1; } errno = 0; mask_bits = strtoul(split + 1, &endptr, 10); if (errno || *endptr != '\0' || mask_bits > 128) { yyerror2("invalid mask in IPv6 CIDR notation: %s", split + 1); free(id); return -1; } if (mask_bits == 0) { yywarn("IPv6 CIDR mask of 0, matching all IPs"); } ipv6_cidr_bits_to_mask(mask_bits, &mask); if (ipv6_has_host_bits_set(&addr, &mask)) { yywarn("host bits in ipv6 address set"); } free(id); newc = calloc(1, sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); return -1; } ipv6_apply_mask(&addr, &mask); memcpy(&newc->u.node6.addr[0], &addr.s6_addr[0], 16); memcpy(&newc->u.node6.mask[0], &mask.s6_addr[0], 16); if (parse_security_context(&newc->context[0])) { free(newc); return -1; } return insert_ipv6_node(newc); } int define_fs_use(int behavior) { ocontext_t *newc, *c, *head; if (policydbp->target_platform != SEPOL_TARGET_SELINUX) { yyerror("fsuse not supported for target"); return -1; } if (pass == 1) { free(queue_remove(id_queue)); parse_security_context(NULL); return 0; } newc = (ocontext_t *) malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); return -1; } memset(newc, 0, sizeof(ocontext_t)); newc->u.name = (char *)queue_remove(id_queue); if (!newc->u.name) { free(newc); return -1; } newc->v.behavior = behavior; if (parse_security_context(&newc->context[0])) { free(newc->u.name); free(newc); return -1; } head = policydbp->ocontexts[OCON_FSUSE]; for (c = head; c; c = c->next) { if (!strcmp(newc->u.name, c->u.name)) { yyerror2("duplicate fs_use entry for filesystem type %s", newc->u.name); context_destroy(&newc->context[0]); free(newc->u.name); free(newc); return -1; } } newc->next = head; policydbp->ocontexts[OCON_FSUSE] = newc; return 0; } static int define_genfs_context_helper(char *fstype, int has_type) { struct genfs *genfs_p, *genfs, *newgenfs; ocontext_t *newc, *c, *head, *p; class_datum_t *cladatum; char *type = NULL; const char *sclass; size_t len, len2; if (policydbp->target_platform != SEPOL_TARGET_SELINUX) { yyerror("genfs not supported for target"); return -1; } if (pass == 1) { free(fstype); free(queue_remove(id_queue)); if (has_type) free(queue_remove(id_queue)); parse_security_context(NULL); return 0; } for (genfs_p = NULL, genfs = policydbp->genfs; genfs; genfs_p = genfs, genfs = genfs->next) { if (strcmp(fstype, genfs->fstype) <= 0) break; } if (!genfs || strcmp(fstype, genfs->fstype)) { newgenfs = malloc(sizeof(struct genfs)); if (!newgenfs) { yyerror("out of memory"); return -1; } memset(newgenfs, 0, sizeof(struct genfs)); newgenfs->fstype = fstype; newgenfs->next = genfs; if (genfs_p) genfs_p->next = newgenfs; else policydbp->genfs = newgenfs; genfs = newgenfs; } else { free(fstype); fstype = NULL; } newc = (ocontext_t *) malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); return -1; } memset(newc, 0, sizeof(ocontext_t)); newc->u.name = (char *)queue_remove(id_queue); if (!newc->u.name) goto fail; if (has_type) { type = (char *)queue_remove(id_queue); if (!type) goto fail; if (type[1] != 0) { yyerror2("invalid type %s", type); goto fail; } switch (type[0]) { case 'b': sclass = "blk_file"; break; case 'c': sclass = "chr_file"; break; case 'd': sclass = "dir"; break; case 'p': sclass = "fifo_file"; break; case 'l': sclass = "lnk_file"; break; case 's': sclass = "sock_file"; break; case '-': sclass = "file"; break; default: yyerror2("invalid type %s", type); goto fail; } cladatum = hashtab_search(policydbp->p_classes.table, sclass); if (!cladatum) { yyerror2("could not find class %s for " "genfscon statement", sclass); goto fail; } newc->v.sclass = cladatum->s.value; } if (parse_security_context(&newc->context[0])) goto fail; head = genfs->head; for (p = NULL, c = head; c; p = c, c = c->next) { if (!strcmp(newc->u.name, c->u.name) && (!newc->v.sclass || !c->v.sclass || newc->v.sclass == c->v.sclass)) { yyerror2("duplicate entry for genfs entry (%s, %s)", genfs->fstype, newc->u.name); goto fail; } len = strlen(newc->u.name); len2 = strlen(c->u.name); if (len > len2) break; } newc->next = c; if (p) p->next = newc; else genfs->head = newc; free(type); return 0; fail: if (type) free(type); context_destroy(&newc->context[0]); if (fstype) free(fstype); if (newc->u.name) free(newc->u.name); free(newc); return -1; } int define_genfs_context(int has_type) { return define_genfs_context_helper(queue_remove(id_queue), has_type); } int define_range_trans(int class_specified) { char *id; level_datum_t *levdatum = 0; class_datum_t *cladatum; range_trans_rule_t *rule; int l, add = 1; if (!mlspol) { yyerror("range_transition rule in non-MLS configuration"); return -1; } if (pass == 1) { while ((id = queue_remove(id_queue))) free(id); while ((id = queue_remove(id_queue))) free(id); if (class_specified) while ((id = queue_remove(id_queue))) free(id); id = queue_remove(id_queue); free(id); for (l = 0; l < 2; l++) { while ((id = queue_remove(id_queue))) { free(id); } id = queue_remove(id_queue); if (!id) break; free(id); } return 0; } rule = malloc(sizeof(struct range_trans_rule)); if (!rule) { yyerror("out of memory"); return -1; } range_trans_rule_init(rule); while ((id = queue_remove(id_queue))) { if (set_types(&rule->stypes, id, &add, 0)) goto out; } add = 1; while ((id = queue_remove(id_queue))) { if (set_types(&rule->ttypes, id, &add, 0)) goto out; } if (class_specified) { if (read_classes(&rule->tclasses)) goto out; } else { cladatum = hashtab_search(policydbp->p_classes.table, "process"); if (!cladatum) { yyerror2("could not find process class for " "legacy range_transition statement"); goto out; } if (ebitmap_set_bit(&rule->tclasses, cladatum->s.value - 1, TRUE)) { yyerror("out of memory"); goto out; } } id = (char *)queue_remove(id_queue); if (!id) { yyerror("no range in range_transition definition?"); goto out; } for (l = 0; l < 2; l++) { levdatum = hashtab_search(policydbp->p_levels.table, id); if (!levdatum) { yyerror2("unknown level %s used in range_transition " "definition", id); free(id); goto out; } free(id); rule->trange.level[l].sens = levdatum->level->sens; while ((id = queue_remove(id_queue))) { if (parse_semantic_categories(id, levdatum, &rule->trange.level[l].cat)) { free(id); goto out; } free(id); } id = (char *)queue_remove(id_queue); if (!id) break; } if (l == 0) { if (mls_semantic_level_cpy(&rule->trange.level[1], &rule->trange.level[0])) { yyerror("out of memory"); goto out; } } append_range_trans(rule); return 0; out: range_trans_rule_destroy(rule); free(rule); return -1; } /* FLASK */ checkpolicy-3.8.1/policy_define.h000066400000000000000000000055321476211737200170210ustar00rootroot00000000000000/* Functions used to define policy grammar components. */ #ifndef _POLICY_DEFINE_H_ #define _POLICY_DEFINE_H_ /* * We need the following so we have a valid error return code in yacc * when we have a parse error for a conditional rule. We can't check * for NULL (ie 0) because that is a potentially valid return. */ #define COND_ERR ((avrule_t *)-1) #define TRUE 1 #define FALSE 0 avrule_t *define_cond_compute_type(int which); avrule_t *define_cond_pol_list(avrule_t *avlist, avrule_t *sl); avrule_t *define_cond_te_avtab(int which); avrule_t *define_cond_te_avtab_extended_perms(int which); avrule_t *define_cond_filename_trans(void); cond_expr_t *define_cond_expr(uint32_t expr_type, void *arg1, void* arg2); int define_attrib(void); int define_attrib_role(void); int define_av_perms(int inherits); int define_bool_tunable(int is_tunable); int define_category(void); int define_class(void); int define_default_user(int which); int define_default_role(int which); int define_default_type(int which); int define_default_range(int which); int define_common_perms(void); int define_compute_type(int which); int define_conditional(cond_expr_t *expr, avrule_t *t_list, avrule_t *f_list ); int define_constraint(constraint_expr_t *expr); int define_dominance(void); int define_fs_context(unsigned int major, unsigned int minor); int define_fs_use(int behavior); int define_genfs_context(int has_type); int define_initial_sid_context(void); int define_initial_sid(void); int define_ipv4_node_context(void); int define_ipv4_cidr_node_context(void); int define_ipv6_node_context(void); int define_ipv6_cidr_node_context(void); int define_level(void); int define_netif_context(void); int define_permissive(void); int define_polcap(void); int define_ibpkey_context(unsigned int low, unsigned int high); int define_ibendport_context(unsigned int port); int define_port_context(unsigned int low, unsigned int high); int define_pirq_context(unsigned int pirq); int define_iomem_context(uint64_t low, uint64_t high); int define_ioport_context(unsigned long low, unsigned long high); int define_pcidevice_context(unsigned long device); int define_devicetree_context(void); int define_range_trans(int class_specified); int define_role_allow(void); int define_role_trans(int class_specified); int define_role_types(void); int define_role_attr(void); int define_roleattribute(void); int define_filename_trans(void); int define_sens(void); int define_te_avtab(int which); int define_te_avtab_extended_perms(int which); int define_typealias(void); int define_typeattribute(void); int define_typebounds(void); int define_type(int alias); int define_user(void); int define_validatetrans(constraint_expr_t *expr); int expand_attrib(void); int insert_id(const char *id,int push); int insert_separator(int push); uintptr_t define_cexpr(uint32_t expr_type, uintptr_t arg1, uintptr_t arg2); #endif /* _POLICY_DEFINE_H_ */ checkpolicy-3.8.1/policy_parse.y000066400000000000000000001037601476211737200167240ustar00rootroot00000000000000 /* * Author : Stephen Smalley, */ /* * Updated: Trusted Computer Solutions, Inc. * * Support for enhanced MLS infrastructure. * * Updated: David Caplan, * * Added conditional policy language extensions * * Updated: Joshua Brindle * Karl MacMillan * Jason Tang * * Added support for binary policy modules * * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc. * Copyright (C) 2003 - 2008 Tresys Technology, LLC * Copyright (C) 2007 Red Hat Inc. * Copyright (C) 2017 Mellanox Technologies Inc. * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ /* FLASK */ %{ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "queue.h" #include "module_compiler.h" #include "policy_define.h" extern policydb_t *policydbp; extern unsigned int pass; extern char yytext[]; extern int yylex(void); extern int yywarn(const char *msg); extern int yyerror(const char *msg); typedef int (* require_func_t)(int pass); %} %union { unsigned int val; uint64_t val64; uintptr_t valptr; void *ptr; require_func_t require_func; } %type cond_expr cond_expr_prim cond_pol_list cond_else %type cond_allow_def cond_auditallow_def cond_auditdeny_def cond_dontaudit_def %type cond_xperm_allow_def cond_xperm_auditallow_def cond_xperm_dontaudit_def %type cond_transition_def cond_te_avtab_def cond_rule_def %type cexpr cexpr_prim op role_mls_op %type ipv4_addr_def number %type number64 %type require_decl_def %token PATH %token QPATH %token FILENAME %token COMMON %token CLASS %token CONSTRAIN %token VALIDATETRANS %token INHERITS %token SID %token ROLE %token ROLEATTRIBUTE %token ATTRIBUTE_ROLE %token ROLES %token TYPEALIAS %token TYPEATTRIBUTE %token TYPEBOUNDS %token TYPE %token TYPES %token ALIAS %token ATTRIBUTE %token EXPANDATTRIBUTE %token BOOL %token TUNABLE %token IF %token ELSE %token TYPE_TRANSITION %token TYPE_MEMBER %token TYPE_CHANGE %token ROLE_TRANSITION %token RANGE_TRANSITION %token SENSITIVITY %token DOMINANCE %token DOM DOMBY INCOMP %token CATEGORY %token LEVEL %token RANGE %token MLSCONSTRAIN %token MLSVALIDATETRANS %token USER %token NEVERALLOW %token ALLOW %token AUDITALLOW %token AUDITDENY %token DONTAUDIT %token ALLOWXPERM %token AUDITALLOWXPERM %token DONTAUDITXPERM %token NEVERALLOWXPERM %token SOURCE %token TARGET %token SAMEUSER %token FSCON PORTCON NETIFCON NODECON %token IBPKEYCON %token IBENDPORTCON %token PIRQCON IOMEMCON IOPORTCON PCIDEVICECON DEVICETREECON %token FSUSEXATTR FSUSETASK FSUSETRANS %token GENFSCON %token U1 U2 U3 R1 R2 R3 T1 T2 T3 L1 L2 H1 H2 %token NOT AND OR XOR %token CTRUE CFALSE %token IDENTIFIER %token NUMBER %token EQUALS %token NOTEQUAL %token IPV4_ADDR %token IPV4_CIDR %token IPV6_ADDR %token IPV6_CIDR %token MODULE VERSION_IDENTIFIER REQUIRE OPTIONAL %token POLICYCAP %token PERMISSIVE %token FILESYSTEM %token DEFAULT_USER DEFAULT_ROLE DEFAULT_TYPE DEFAULT_RANGE %token LOW_HIGH LOW HIGH GLBLUB %token INVALID_CHAR %left OR %left XOR %left AND %right NOT %left EQUALS NOTEQUAL %% policy : base_policy | module_policy ; base_policy : { if (define_policy(pass, 0) == -1) YYABORT; } classes initial_sids access_vectors { if (pass == 1) { if (policydb_index_classes(policydbp)) YYABORT; } else if (pass == 2) { if (policydb_index_others(NULL, policydbp, 0)) YYABORT; }} opt_default_rules opt_mls te_rbac users opt_constraints { if (pass == 1) { if (policydb_index_bools(policydbp)) YYABORT; } else if (pass == 2) { if (policydb_index_others(NULL, policydbp, 0)) YYABORT; }} initial_sid_contexts opt_fs_contexts opt_fs_uses opt_genfs_contexts net_contexts opt_dev_contexts opt_ibpkey_contexts opt_ibendport_contexts ; classes : class_def | classes class_def ; class_def : CLASS identifier {if (define_class()) YYABORT;} ; initial_sids : initial_sid_def | initial_sids initial_sid_def ; initial_sid_def : SID identifier {if (define_initial_sid()) YYABORT;} ; access_vectors : opt_common_perms av_perms ; opt_common_perms : common_perms | ; common_perms : common_perms_def | common_perms common_perms_def ; common_perms_def : COMMON identifier '{' identifier_list '}' {if (define_common_perms()) YYABORT;} ; av_perms : av_perms_def | av_perms av_perms_def ; av_perms_def : CLASS identifier '{' identifier_list '}' {if (define_av_perms(FALSE)) YYABORT;} | CLASS identifier INHERITS identifier {if (define_av_perms(TRUE)) YYABORT;} | CLASS identifier INHERITS identifier '{' identifier_list '}' {if (define_av_perms(TRUE)) YYABORT;} ; opt_default_rules : default_rules | ; default_rules : default_user_def | default_role_def | default_type_def | default_range_def | default_rules default_user_def | default_rules default_role_def | default_rules default_type_def | default_rules default_range_def ; default_user_def : DEFAULT_USER names SOURCE ';' {if (define_default_user(DEFAULT_SOURCE)) YYABORT; } | DEFAULT_USER names TARGET ';' {if (define_default_user(DEFAULT_TARGET)) YYABORT; } ; default_role_def : DEFAULT_ROLE names SOURCE ';' {if (define_default_role(DEFAULT_SOURCE)) YYABORT; } | DEFAULT_ROLE names TARGET ';' {if (define_default_role(DEFAULT_TARGET)) YYABORT; } ; default_type_def : DEFAULT_TYPE names SOURCE ';' {if (define_default_type(DEFAULT_SOURCE)) YYABORT;; } | DEFAULT_TYPE names TARGET ';' {if (define_default_type(DEFAULT_TARGET)) YYABORT; } ; default_range_def : DEFAULT_RANGE names SOURCE LOW ';' {if (define_default_range(DEFAULT_SOURCE_LOW)) YYABORT; } | DEFAULT_RANGE names SOURCE HIGH ';' {if (define_default_range(DEFAULT_SOURCE_HIGH)) YYABORT; } | DEFAULT_RANGE names SOURCE LOW_HIGH ';' {if (define_default_range(DEFAULT_SOURCE_LOW_HIGH)) YYABORT; } | DEFAULT_RANGE names TARGET LOW ';' {if (define_default_range(DEFAULT_TARGET_LOW)) YYABORT; } | DEFAULT_RANGE names TARGET HIGH ';' {if (define_default_range(DEFAULT_TARGET_HIGH)) YYABORT; } | DEFAULT_RANGE names TARGET LOW_HIGH ';' {if (define_default_range(DEFAULT_TARGET_LOW_HIGH)) YYABORT; } | DEFAULT_RANGE names GLBLUB';' {if (define_default_range(DEFAULT_GLBLUB)) YYABORT; } ; opt_mls : mls | ; mls : sensitivities dominance opt_categories levels mlspolicy ; sensitivities : sensitivity_def | sensitivities sensitivity_def ; sensitivity_def : SENSITIVITY identifier alias_def ';' {if (define_sens()) YYABORT;} | SENSITIVITY identifier ';' {if (define_sens()) YYABORT;} ; alias_def : ALIAS names ; dominance : DOMINANCE identifier {if (define_dominance()) YYABORT;} | DOMINANCE '{' identifier_list '}' {if (define_dominance()) YYABORT;} ; opt_categories : categories | ; categories : category_def | categories category_def ; category_def : CATEGORY identifier alias_def ';' {if (define_category()) YYABORT;} | CATEGORY identifier ';' {if (define_category()) YYABORT;} ; levels : level_def | levels level_def ; level_def : LEVEL identifier ':' id_comma_list ';' {if (define_level()) YYABORT;} | LEVEL identifier ';' {if (define_level()) YYABORT;} ; mlspolicy : mlspolicy_decl | mlspolicy mlspolicy_decl ; mlspolicy_decl : mlsconstraint_def | mlsvalidatetrans_def ; mlsconstraint_def : MLSCONSTRAIN names names cexpr ';' { if (define_constraint((constraint_expr_t*)$4)) YYABORT; } ; mlsvalidatetrans_def : MLSVALIDATETRANS names cexpr ';' { if (define_validatetrans((constraint_expr_t*)$3)) YYABORT; } ; te_rbac : te_rbac_decl | te_rbac te_rbac_decl ; te_rbac_decl : te_decl | rbac_decl | cond_stmt_def | optional_block | policycap_def | ';' ; rbac_decl : attribute_role_def | role_type_def | role_trans_def | role_allow_def | roleattribute_def | role_attr_def ; te_decl : attribute_def | expandattribute_def | type_def | typealias_def | typeattribute_def | typebounds_def | bool_def | tunable_def | transition_def | range_trans_def | te_avtab_def | permissive_def ; attribute_def : ATTRIBUTE identifier ';' { if (define_attrib()) YYABORT;} ; expandattribute_def : EXPANDATTRIBUTE names bool_val ';' { if (expand_attrib()) YYABORT;} ; type_def : TYPE identifier alias_def opt_attr_list ';' {if (define_type(1)) YYABORT;} | TYPE identifier opt_attr_list ';' {if (define_type(0)) YYABORT;} ; typealias_def : TYPEALIAS identifier alias_def ';' {if (define_typealias()) YYABORT;} ; typeattribute_def : TYPEATTRIBUTE identifier id_comma_list ';' {if (define_typeattribute()) YYABORT;} ; typebounds_def : TYPEBOUNDS identifier id_comma_list ';' {if (define_typebounds()) YYABORT;} ; opt_attr_list : ',' id_comma_list | ; bool_def : BOOL identifier bool_val ';' { if (define_bool_tunable(0)) YYABORT; } ; tunable_def : TUNABLE identifier bool_val ';' { if (define_bool_tunable(1)) YYABORT; } ; bool_val : CTRUE { if (insert_id("T",0)) YYABORT; } | CFALSE { if (insert_id("F",0)) YYABORT; } ; cond_stmt_def : IF cond_expr '{' cond_pol_list '}' cond_else { if (pass == 2) { if (define_conditional((cond_expr_t*)$2, (avrule_t*)$4, (avrule_t*)$6) < 0) YYABORT; }} ; cond_else : ELSE '{' cond_pol_list '}' { $$ = $3; } | /* empty */ { $$ = NULL; } ; cond_expr : '(' cond_expr ')' { $$ = $2;} | NOT cond_expr { $$ = define_cond_expr(COND_NOT, $2, 0); if ($$ == 0) YYABORT; } | cond_expr AND cond_expr { $$ = define_cond_expr(COND_AND, $1, $3); if ($$ == 0) YYABORT; } | cond_expr OR cond_expr { $$ = define_cond_expr(COND_OR, $1, $3); if ($$ == 0) YYABORT; } | cond_expr XOR cond_expr { $$ = define_cond_expr(COND_XOR, $1, $3); if ($$ == 0) YYABORT; } | cond_expr EQUALS cond_expr { $$ = define_cond_expr(COND_EQ, $1, $3); if ($$ == 0) YYABORT; } | cond_expr NOTEQUAL cond_expr { $$ = define_cond_expr(COND_NEQ, $1, $3); if ($$ == 0) YYABORT; } | cond_expr_prim { $$ = $1; } ; cond_expr_prim : identifier { $$ = define_cond_expr(COND_BOOL,0, 0); if ($$ == COND_ERR) YYABORT; } ; cond_pol_list : cond_pol_list cond_rule_def { $$ = define_cond_pol_list((avrule_t *)$1, (avrule_t *)$2); } | /* empty */ { $$ = NULL; } ; cond_rule_def : cond_transition_def { $$ = $1; } | cond_te_avtab_def { $$ = $1; } | require_block { $$ = NULL; } ; cond_transition_def : TYPE_TRANSITION names names ':' names identifier filename ';' { $$ = define_cond_filename_trans() ; if ($$ == COND_ERR) YYABORT;} | TYPE_TRANSITION names names ':' names identifier ';' { $$ = define_cond_compute_type(AVRULE_TRANSITION) ; if ($$ == COND_ERR) YYABORT;} | TYPE_MEMBER names names ':' names identifier ';' { $$ = define_cond_compute_type(AVRULE_MEMBER) ; if ($$ == COND_ERR) YYABORT;} | TYPE_CHANGE names names ':' names identifier ';' { $$ = define_cond_compute_type(AVRULE_CHANGE) ; if ($$ == COND_ERR) YYABORT;} ; cond_te_avtab_def : cond_allow_def { $$ = $1; } | cond_auditallow_def { $$ = $1; } | cond_auditdeny_def { $$ = $1; } | cond_dontaudit_def { $$ = $1; } | cond_xperm_allow_def { $$ = $1; } | cond_xperm_auditallow_def { $$ = $1; } | cond_xperm_dontaudit_def { $$ = $1; } ; cond_allow_def : ALLOW names names ':' names names ';' { $$ = define_cond_te_avtab(AVRULE_ALLOWED) ; if ($$ == COND_ERR) YYABORT; } ; cond_auditallow_def : AUDITALLOW names names ':' names names ';' { $$ = define_cond_te_avtab(AVRULE_AUDITALLOW) ; if ($$ == COND_ERR) YYABORT; } ; cond_auditdeny_def : AUDITDENY names names ':' names names ';' { $$ = define_cond_te_avtab(AVRULE_AUDITDENY) ; if ($$ == COND_ERR) YYABORT; } ; cond_dontaudit_def : DONTAUDIT names names ':' names names ';' { $$ = define_cond_te_avtab(AVRULE_DONTAUDIT); if ($$ == COND_ERR) YYABORT; } ; cond_xperm_allow_def : ALLOWXPERM names names ':' names identifier xperms ';' { $$ = define_cond_te_avtab_extended_perms(AVRULE_XPERMS_ALLOWED) ; if ($$ == COND_ERR) YYABORT; } ; cond_xperm_auditallow_def : AUDITALLOWXPERM names names ':' names identifier xperms ';' { $$ = define_cond_te_avtab_extended_perms(AVRULE_XPERMS_AUDITALLOW) ; if ($$ == COND_ERR) YYABORT; } ; cond_xperm_dontaudit_def : DONTAUDITXPERM names names ':' names identifier xperms ';' { $$ = define_cond_te_avtab_extended_perms(AVRULE_XPERMS_DONTAUDIT) ; if ($$ == COND_ERR) YYABORT; } ; transition_def : TYPE_TRANSITION names names ':' names identifier filename ';' {if (define_filename_trans()) YYABORT; } | TYPE_TRANSITION names names ':' names identifier ';' {if (define_compute_type(AVRULE_TRANSITION)) YYABORT;} | TYPE_MEMBER names names ':' names identifier ';' {if (define_compute_type(AVRULE_MEMBER)) YYABORT;} | TYPE_CHANGE names names ':' names identifier ';' {if (define_compute_type(AVRULE_CHANGE)) YYABORT;} ; range_trans_def : RANGE_TRANSITION names names mls_range_def ';' { if (define_range_trans(0)) YYABORT; } | RANGE_TRANSITION names names ':' names mls_range_def ';' { if (define_range_trans(1)) YYABORT; } ; te_avtab_def : allow_def | auditallow_def | auditdeny_def | dontaudit_def | neverallow_def | xperm_allow_def | xperm_auditallow_def | xperm_dontaudit_def | xperm_neverallow_def ; allow_def : ALLOW names names ':' names names ';' {if (define_te_avtab(AVRULE_ALLOWED)) YYABORT; } ; auditallow_def : AUDITALLOW names names ':' names names ';' {if (define_te_avtab(AVRULE_AUDITALLOW)) YYABORT; } ; auditdeny_def : AUDITDENY names names ':' names names ';' {if (define_te_avtab(AVRULE_AUDITDENY)) YYABORT; } ; dontaudit_def : DONTAUDIT names names ':' names names ';' {if (define_te_avtab(AVRULE_DONTAUDIT)) YYABORT; } ; neverallow_def : NEVERALLOW names names ':' names names ';' {if (define_te_avtab(AVRULE_NEVERALLOW)) YYABORT; } ; xperm_allow_def : ALLOWXPERM names names ':' names identifier xperms ';' {if (define_te_avtab_extended_perms(AVRULE_XPERMS_ALLOWED)) YYABORT; } ; xperm_auditallow_def : AUDITALLOWXPERM names names ':' names identifier xperms ';' {if (define_te_avtab_extended_perms(AVRULE_XPERMS_AUDITALLOW)) YYABORT; } ; xperm_dontaudit_def : DONTAUDITXPERM names names ':' names identifier xperms ';' {if (define_te_avtab_extended_perms(AVRULE_XPERMS_DONTAUDIT)) YYABORT; } ; xperm_neverallow_def : NEVERALLOWXPERM names names ':' names identifier xperms ';' {if (define_te_avtab_extended_perms(AVRULE_XPERMS_NEVERALLOW)) YYABORT; } ; attribute_role_def : ATTRIBUTE_ROLE identifier ';' {if (define_attrib_role()) YYABORT; } ; role_type_def : ROLE identifier TYPES names ';' {if (define_role_types()) YYABORT;} ; role_attr_def : ROLE identifier opt_attr_list ';' {if (define_role_attr()) YYABORT;} ; role_trans_def : ROLE_TRANSITION names names identifier ';' {if (define_role_trans(0)) YYABORT; } | ROLE_TRANSITION names names ':' names identifier ';' {if (define_role_trans(1)) YYABORT;} ; role_allow_def : ALLOW names names ';' {if (define_role_allow()) YYABORT; } ; roleattribute_def : ROLEATTRIBUTE identifier id_comma_list ';' {if (define_roleattribute()) YYABORT;} ; opt_constraints : constraints | ; constraints : constraint_decl | constraints constraint_decl ; constraint_decl : constraint_def | validatetrans_def ; constraint_def : CONSTRAIN names names cexpr ';' { if (define_constraint((constraint_expr_t*)$4)) YYABORT; } ; validatetrans_def : VALIDATETRANS names cexpr ';' { if (define_validatetrans((constraint_expr_t*)$3)) YYABORT; } ; cexpr : '(' cexpr ')' { $$ = $2; } | NOT cexpr { $$ = define_cexpr(CEXPR_NOT, $2, 0); if ($$ == 0) YYABORT; } | cexpr AND cexpr { $$ = define_cexpr(CEXPR_AND, $1, $3); if ($$ == 0) YYABORT; } | cexpr OR cexpr { $$ = define_cexpr(CEXPR_OR, $1, $3); if ($$ == 0) YYABORT; } | cexpr_prim { $$ = $1; } ; cexpr_prim : U1 op U2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_USER, $2); if ($$ == 0) YYABORT; } | R1 role_mls_op R2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_ROLE, $2); if ($$ == 0) YYABORT; } | T1 op T2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_TYPE, $2); if ($$ == 0) YYABORT; } | U1 op { if (insert_separator(1)) YYABORT; } names_push { $$ = define_cexpr(CEXPR_NAMES, CEXPR_USER, $2); if ($$ == 0) YYABORT; } | U2 op { if (insert_separator(1)) YYABORT; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_USER | CEXPR_TARGET), $2); if ($$ == 0) YYABORT; } | U3 op { if (insert_separator(1)) YYABORT; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_USER | CEXPR_XTARGET), $2); if ($$ == 0) YYABORT; } | R1 op { if (insert_separator(1)) YYABORT; } names_push { $$ = define_cexpr(CEXPR_NAMES, CEXPR_ROLE, $2); if ($$ == 0) YYABORT; } | R2 op { if (insert_separator(1)) YYABORT; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_ROLE | CEXPR_TARGET), $2); if ($$ == 0) YYABORT; } | R3 op { if (insert_separator(1)) YYABORT; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_ROLE | CEXPR_XTARGET), $2); if ($$ == 0) YYABORT; } | T1 op { if (insert_separator(1)) YYABORT; } names_push { $$ = define_cexpr(CEXPR_NAMES, CEXPR_TYPE, $2); if ($$ == 0) YYABORT; } | T2 op { if (insert_separator(1)) YYABORT; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_TYPE | CEXPR_TARGET), $2); if ($$ == 0) YYABORT; } | T3 op { if (insert_separator(1)) YYABORT; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_TYPE | CEXPR_XTARGET), $2); if ($$ == 0) YYABORT; } | SAMEUSER { $$ = define_cexpr(CEXPR_ATTR, CEXPR_USER, CEXPR_EQ); if ($$ == 0) YYABORT; } | SOURCE ROLE { if (insert_separator(1)) YYABORT; } names_push { $$ = define_cexpr(CEXPR_NAMES, CEXPR_ROLE, CEXPR_EQ); if ($$ == 0) YYABORT; } | TARGET ROLE { if (insert_separator(1)) YYABORT; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_ROLE | CEXPR_TARGET), CEXPR_EQ); if ($$ == 0) YYABORT; } | ROLE role_mls_op { $$ = define_cexpr(CEXPR_ATTR, CEXPR_ROLE, $2); if ($$ == 0) YYABORT; } | SOURCE TYPE { if (insert_separator(1)) YYABORT; } names_push { $$ = define_cexpr(CEXPR_NAMES, CEXPR_TYPE, CEXPR_EQ); if ($$ == 0) YYABORT; } | TARGET TYPE { if (insert_separator(1)) YYABORT; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_TYPE | CEXPR_TARGET), CEXPR_EQ); if ($$ == 0) YYABORT; } | L1 role_mls_op L2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_L1L2, $2); if ($$ == 0) YYABORT; } | L1 role_mls_op H2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_L1H2, $2); if ($$ == 0) YYABORT; } | H1 role_mls_op L2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_H1L2, $2); if ($$ == 0) YYABORT; } | H1 role_mls_op H2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_H1H2, $2); if ($$ == 0) YYABORT; } | L1 role_mls_op H1 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_L1H1, $2); if ($$ == 0) YYABORT; } | L2 role_mls_op H2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_L2H2, $2); if ($$ == 0) YYABORT; } ; op : EQUALS { $$ = CEXPR_EQ; } | NOTEQUAL { $$ = CEXPR_NEQ; } ; role_mls_op : op { $$ = $1; } | DOM { $$ = CEXPR_DOM; } | DOMBY { $$ = CEXPR_DOMBY; } | INCOMP { $$ = CEXPR_INCOMP; } ; users : user_def | users user_def ; user_def : USER identifier ROLES names opt_mls_user ';' {if (define_user()) YYABORT;} ; opt_mls_user : LEVEL mls_level_def RANGE mls_range_def | ; initial_sid_contexts : initial_sid_context_def | initial_sid_contexts initial_sid_context_def ; initial_sid_context_def : SID identifier security_context_def {if (define_initial_sid_context()) YYABORT;} ; opt_dev_contexts : dev_contexts | ; dev_contexts : dev_context_def | dev_contexts dev_context_def ; dev_context_def : pirq_context_def | iomem_context_def | ioport_context_def | pci_context_def | dtree_context_def ; pirq_context_def : PIRQCON number security_context_def {if (define_pirq_context($2)) YYABORT;} ; iomem_context_def : IOMEMCON number64 security_context_def {if (define_iomem_context($2,$2)) YYABORT;} | IOMEMCON number64 '-' number64 security_context_def {if (define_iomem_context($2,$4)) YYABORT;} ; ioport_context_def : IOPORTCON number security_context_def {if (define_ioport_context($2,$2)) YYABORT;} | IOPORTCON number '-' number security_context_def {if (define_ioport_context($2,$4)) YYABORT;} ; pci_context_def : PCIDEVICECON number security_context_def {if (define_pcidevice_context($2)) YYABORT;} ; dtree_context_def : DEVICETREECON path security_context_def {if (define_devicetree_context()) YYABORT;} ; opt_fs_contexts : fs_contexts | ; fs_contexts : fs_context_def | fs_contexts fs_context_def ; fs_context_def : FSCON number number security_context_def security_context_def {if (define_fs_context($2,$3)) YYABORT;} ; net_contexts : opt_port_contexts opt_netif_contexts opt_node_contexts ; opt_port_contexts : port_contexts | ; port_contexts : port_context_def | port_contexts port_context_def ; port_context_def : PORTCON identifier number security_context_def {if (define_port_context($3,$3)) YYABORT;} | PORTCON identifier number '-' number security_context_def {if (define_port_context($3,$5)) YYABORT;} ; opt_ibpkey_contexts : ibpkey_contexts | ; ibpkey_contexts : ibpkey_context_def | ibpkey_contexts ibpkey_context_def ; ibpkey_context_def : IBPKEYCON ipv6_addr number security_context_def {if (define_ibpkey_context($3,$3)) YYABORT;} | IBPKEYCON ipv6_addr number '-' number security_context_def {if (define_ibpkey_context($3,$5)) YYABORT;} ; opt_ibendport_contexts : ibendport_contexts | ; ibendport_contexts : ibendport_context_def | ibendport_contexts ibendport_context_def ; ibendport_context_def : IBENDPORTCON identifier number security_context_def {if (define_ibendport_context($3)) YYABORT;} ; opt_netif_contexts : netif_contexts | ; netif_contexts : netif_context_def | netif_contexts netif_context_def ; netif_context_def : NETIFCON identifier security_context_def security_context_def {if (define_netif_context()) YYABORT;} ; opt_node_contexts : node_contexts | ; node_contexts : node_context_def | node_contexts node_context_def ; node_context_def : NODECON ipv4_addr_def ipv4_addr_def security_context_def {if (define_ipv4_node_context()) YYABORT;} | NODECON ipv4_cidr_def security_context_def {if (define_ipv4_cidr_node_context()) YYABORT;} | NODECON ipv6_addr ipv6_addr security_context_def {if (define_ipv6_node_context()) YYABORT;} | NODECON ipv6_cidr security_context_def {if (define_ipv6_cidr_node_context()) YYABORT;} ; opt_fs_uses : fs_uses | ; fs_uses : fs_use_def | fs_uses fs_use_def ; fs_use_def : FSUSEXATTR filesystem security_context_def ';' {if (define_fs_use(SECURITY_FS_USE_XATTR)) YYABORT;} | FSUSETASK identifier security_context_def ';' {if (define_fs_use(SECURITY_FS_USE_TASK)) YYABORT;} | FSUSETRANS identifier security_context_def ';' {if (define_fs_use(SECURITY_FS_USE_TRANS)) YYABORT;} ; opt_genfs_contexts : genfs_contexts | ; genfs_contexts : genfs_context_def | genfs_contexts genfs_context_def ; genfs_context_def : GENFSCON filesystem path '-' identifier security_context_def {if (define_genfs_context(1)) YYABORT;} | GENFSCON filesystem path '-' '-' {insert_id("-", 0);} security_context_def {if (define_genfs_context(1)) YYABORT;} | GENFSCON filesystem path security_context_def {if (define_genfs_context(0)) YYABORT;} ; ipv4_addr_def : IPV4_ADDR { if (insert_id(yytext,0)) YYABORT; } ; ipv4_cidr_def : IPV4_CIDR { if (insert_id(yytext,0)) YYABORT; } ; xperms : xperm { if (insert_separator(0)) YYABORT; } | nested_xperm_set { if (insert_separator(0)) YYABORT; } | tilde xperm { if (insert_id("~", 0)) YYABORT; } | tilde nested_xperm_set { if (insert_id("~", 0)) YYABORT; if (insert_separator(0)) YYABORT; } ; nested_xperm_set : '{' nested_xperm_list '}' ; nested_xperm_list : nested_xperm_element | nested_xperm_list nested_xperm_element ; nested_xperm_element: xperm '-' { if (insert_id("-", 0)) YYABORT; } xperm | xperm | nested_xperm_set ; xperm : number { if (insert_id(yytext,0)) YYABORT; } ; security_context_def : identifier ':' identifier ':' identifier opt_mls_range_def ; opt_mls_range_def : ':' mls_range_def | ; mls_range_def : mls_level_def '-' mls_level_def {if (insert_separator(0)) YYABORT;} | mls_level_def {if (insert_separator(0)) YYABORT;} ; mls_level_def : identifier ':' id_comma_list {if (insert_separator(0)) YYABORT;} | identifier {if (insert_separator(0)) YYABORT;} ; id_comma_list : identifier | id_comma_list ',' identifier ; tilde : '~' ; asterisk : '*' ; names : identifier { if (insert_separator(0)) YYABORT; } | nested_id_set { if (insert_separator(0)) YYABORT; } | asterisk { if (insert_id("*", 0)) YYABORT; if (insert_separator(0)) YYABORT; } | tilde identifier { if (insert_id("~", 0)) YYABORT; if (insert_separator(0)) YYABORT; } | tilde nested_id_set { if (insert_id("~", 0)) YYABORT; if (insert_separator(0)) YYABORT; } | identifier '-' { if (insert_id("-", 0)) YYABORT; } identifier { if (insert_separator(0)) YYABORT; } ; tilde_push : tilde { if (insert_id("~", 1)) YYABORT; } ; asterisk_push : asterisk { if (insert_id("*", 1)) YYABORT; } ; names_push : identifier_push | '{' identifier_list_push '}' | asterisk_push | tilde_push identifier_push | tilde_push '{' identifier_list_push '}' ; identifier_list_push : identifier_push | identifier_list_push identifier_push ; identifier_push : IDENTIFIER { if (insert_id(yytext, 1)) YYABORT; } ; identifier_list : identifier | identifier_list identifier ; nested_id_set : '{' nested_id_list '}' ; nested_id_list : nested_id_element | nested_id_list nested_id_element ; nested_id_element : identifier | '-' { if (insert_id("-", 0)) YYABORT; } identifier | nested_id_set ; identifier : IDENTIFIER { if (insert_id(yytext,0)) YYABORT; } ; filesystem : FILESYSTEM { if (insert_id(yytext,0)) YYABORT; } | IDENTIFIER { if (insert_id(yytext,0)) YYABORT; } ; path : PATH { if (insert_id(yytext,0)) YYABORT; } | QPATH { yytext[strlen(yytext) - 1] = '\0'; if (insert_id(yytext + 1,0)) YYABORT; } ; filename : FILENAME { yytext[strlen(yytext) - 1] = '\0'; if (insert_id(yytext + 1,0)) YYABORT; } ; number : NUMBER { unsigned long x; errno = 0; x = strtoul(yytext, NULL, 0); if (errno) YYABORT; #if ULONG_MAX > UINT_MAX if (x > UINT_MAX) YYABORT; #endif $$ = (unsigned int) x; } ; number64 : NUMBER { unsigned long long x; errno = 0; x = strtoull(yytext, NULL, 0); if (errno) YYABORT; $$ = (uint64_t) x; } ; ipv6_addr : IPV6_ADDR { if (insert_id(yytext,0)) YYABORT; } ; ipv6_cidr : IPV6_CIDR { if (insert_id(yytext,0)) YYABORT; } ; policycap_def : POLICYCAP identifier ';' {if (define_polcap()) YYABORT;} ; permissive_def : PERMISSIVE identifier ';' {if (define_permissive()) YYABORT;} /*********** module grammar below ***********/ module_policy : module_def avrules_block { if (end_avrule_block(pass) == -1) YYABORT; if (policydb_index_others(NULL, policydbp, 0)) YYABORT; } ; module_def : MODULE identifier version_identifier ';' { if (define_policy(pass, 1) == -1) YYABORT; } ; version_identifier : VERSION_IDENTIFIER { if (insert_id(yytext,0)) YYABORT; } | number { if (insert_id(yytext,0)) YYABORT; } | ipv4_addr_def /* version can look like ipv4 address */ ; avrules_block : avrule_decls avrule_user_defs ; avrule_decls : avrule_decls avrule_decl | avrule_decl ; avrule_decl : rbac_decl | te_decl | cond_stmt_def | require_block | optional_block | ';' ; require_block : REQUIRE '{' require_list '}' ; require_list : require_list require_decl | require_decl ; require_decl : require_class ';' | require_decl_def require_id_list ';' ; require_class : CLASS identifier names { if (require_class(pass)) YYABORT; } ; require_decl_def : ROLE { $$ = require_role; } | TYPE { $$ = require_type; } | ATTRIBUTE { $$ = require_attribute; } | ATTRIBUTE_ROLE { $$ = require_attribute_role; } | USER { $$ = require_user; } | BOOL { $$ = require_bool; } | TUNABLE { $$ = require_tunable; } | SENSITIVITY { $$ = require_sens; } | CATEGORY { $$ = require_cat; } ; require_id_list : identifier { if ($0 (pass)) YYABORT; } | require_id_list ',' identifier { if ($0 (pass)) YYABORT; } ; optional_block : optional_decl '{' avrules_block '}' { if (end_avrule_block(pass) == -1) YYABORT; } optional_else { if (end_optional(pass) == -1) YYABORT; } ; optional_else : else_decl '{' avrules_block '}' { if (end_avrule_block(pass) == -1) YYABORT; } | /* empty */ ; optional_decl : OPTIONAL { if (begin_optional(pass) == -1) YYABORT; } ; else_decl : ELSE { if (begin_optional_else(pass) == -1) YYABORT; } ; avrule_user_defs : user_def avrule_user_defs | /* empty */ ; checkpolicy-3.8.1/policy_scan.l000066400000000000000000000222351476211737200165160ustar00rootroot00000000000000 /* * Author : Stephen Smalley, */ /* Updated: David Caplan, * * Added conditional policy language extensions * * Jason Tang * * Added support for binary policy modules * * Copyright (C) 2003-5 Tresys Technology, LLC * Copyright (C) 2017 Mellanox Technologies Inc. * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ /* FLASK */ %{ #include #include #include #include #include typedef int (* require_func_t)(void); #ifdef ANDROID #include "policy_parse.h" #else #include "y.tab.h" #endif static char linebuf[2][255]; static unsigned int lno = 0; int werror = 0; int yyerror(const char *msg); int yywarn(const char *msg); #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* * Version that does not exit, like yy_fatal_error(), * since fuzz targets must not call exit(). */ #include extern jmp_buf fuzzing_pre_parse_stack_state; void yyfatal(const char *msg) { yyerror(msg); longjmp(fuzzing_pre_parse_stack_state, 1); } #define YY_FATAL_ERROR(msg) yyfatal(msg) #endif void set_source_file(const char *name); char source_file[PATH_MAX]; unsigned long source_lineno = 1; unsigned long policydb_lineno = 1; unsigned int policydb_errors = 0; %} %option noinput nounput noyywrap %array letter [A-Za-z] digit [0-9] alnum [a-zA-Z0-9] hexval [0-9A-Fa-f] %% \n.* { #if defined(__GNUC__) && __GNUC__ >= 8 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstringop-truncation" #endif strncpy(linebuf[lno], yytext+1, 255); #if defined(__GNUC__) && __GNUC__ >= 8 #pragma GCC diagnostic pop #endif linebuf[lno][254] = 0; lno = 1 - lno; policydb_lineno++; if (source_lineno == ULONG_MAX) yywarn("source line number overflow"); else source_lineno++; yyless(1); } COMMON | common { return(COMMON); } CLASS | class { return(CLASS); } CONSTRAIN | constrain { return(CONSTRAIN); } VALIDATETRANS | validatetrans { return(VALIDATETRANS); } INHERITS | inherits { return(INHERITS); } SID | sid { return(SID); } ROLE | role { return(ROLE); } ROLES | roles { return(ROLES); } ROLEATTRIBUTE | roleattribute { return(ROLEATTRIBUTE);} ATTRIBUTE_ROLE | attribute_role { return(ATTRIBUTE_ROLE);} TYPES | types { return(TYPES); } TYPEALIAS | typealias { return(TYPEALIAS); } TYPEATTRIBUTE | typeattribute { return(TYPEATTRIBUTE); } TYPEBOUNDS | typebounds { return(TYPEBOUNDS); } TYPE | type { return(TYPE); } BOOL | bool { return(BOOL); } TUNABLE | tunable { return(TUNABLE); } IF | if { return(IF); } ELSE | else { return(ELSE); } ALIAS | alias { return(ALIAS); } ATTRIBUTE | attribute { return(ATTRIBUTE); } EXPANDATTRIBUTE | expandattribute { return(EXPANDATTRIBUTE); } TYPE_TRANSITION | type_transition { return(TYPE_TRANSITION); } TYPE_MEMBER | type_member { return(TYPE_MEMBER); } TYPE_CHANGE | type_change { return(TYPE_CHANGE); } ROLE_TRANSITION | role_transition { return(ROLE_TRANSITION); } RANGE_TRANSITION | range_transition { return(RANGE_TRANSITION); } SENSITIVITY | sensitivity { return(SENSITIVITY); } DOMINANCE | dominance { return(DOMINANCE); } CATEGORY | category { return(CATEGORY); } LEVEL | level { return(LEVEL); } RANGE | range { return(RANGE); } MLSCONSTRAIN | mlsconstrain { return(MLSCONSTRAIN); } MLSVALIDATETRANS | mlsvalidatetrans { return(MLSVALIDATETRANS); } USER | user { return(USER); } NEVERALLOW | neverallow { return(NEVERALLOW); } ALLOW | allow { return(ALLOW); } AUDITALLOW | auditallow { return(AUDITALLOW); } AUDITDENY | auditdeny { return(AUDITDENY); } DONTAUDIT | dontaudit { return(DONTAUDIT); } ALLOWXPERM | allowxperm { return(ALLOWXPERM); } AUDITALLOWXPERM | auditallowxperm { return(AUDITALLOWXPERM); } DONTAUDITXPERM | dontauditxperm { return(DONTAUDITXPERM); } NEVERALLOWXPERM | neverallowxperm { return(NEVERALLOWXPERM); } SOURCE | source { return(SOURCE); } TARGET | target { return(TARGET); } SAMEUSER | sameuser { return(SAMEUSER);} module|MODULE { return(MODULE); } require|REQUIRE { return(REQUIRE); } optional|OPTIONAL { return(OPTIONAL); } OR | or { return(OR);} AND | and { return(AND);} NOT | not { return(NOT);} xor | XOR { return(XOR); } eq | EQ { return(EQUALS);} true | TRUE { return(CTRUE); } false | FALSE { return(CFALSE); } dom | DOM { return(DOM);} domby | DOMBY { return(DOMBY);} INCOMP | incomp { return(INCOMP);} fscon | FSCON { return(FSCON);} ibpkeycon | IBPKEYCON { return(IBPKEYCON);} ibendportcon | IBENDPORTCON { return(IBENDPORTCON);} portcon | PORTCON { return(PORTCON);} netifcon | NETIFCON { return(NETIFCON);} nodecon | NODECON { return(NODECON);} pirqcon | PIRQCON { return(PIRQCON);} iomemcon | IOMEMCON { return(IOMEMCON);} ioportcon | IOPORTCON { return(IOPORTCON);} pcidevicecon | PCIDEVICECON { return(PCIDEVICECON);} devicetreecon | DEVICETREECON { return(DEVICETREECON);} fs_use_xattr | FS_USE_XATTR { return(FSUSEXATTR);} fs_use_task | FS_USE_TASK { return(FSUSETASK);} fs_use_trans | FS_USE_TRANS { return(FSUSETRANS);} genfscon | GENFSCON { return(GENFSCON);} r1 | R1 { return(R1); } r2 | R2 { return(R2); } r3 | R3 { return(R3); } u1 | U1 { return(U1); } u2 | U2 { return(U2); } u3 | U3 { return(U3); } t1 | T1 { return(T1); } t2 | T2 { return(T2); } t3 | T3 { return(T3); } l1 | L1 { return(L1); } l2 | L2 { return(L2); } h1 | H1 { return(H1); } h2 | H2 { return(H2); } policycap | POLICYCAP { return(POLICYCAP); } permissive | PERMISSIVE { return(PERMISSIVE); } default_user | DEFAULT_USER { return(DEFAULT_USER); } default_role | DEFAULT_ROLE { return(DEFAULT_ROLE); } default_type | DEFAULT_TYPE { return(DEFAULT_TYPE); } default_range | DEFAULT_RANGE { return(DEFAULT_RANGE); } low-high | LOW-HIGH { return(LOW_HIGH); } high | HIGH { return(HIGH); } low | LOW { return(LOW); } glblub | GLBLUB { return(GLBLUB); } "/"[^ \n\r\t\f]* { return(PATH); } \""/"[^\"\n]*\" { return(QPATH); } \"[^"/"\"\n]+\" { return(FILENAME); } {letter}({alnum}|[_\-])*([\.]?({alnum}|[_\-]))* { return(IDENTIFIER); } {digit}+|0x{hexval}+ { return(NUMBER); } {alnum}*{letter}{alnum}* { return(FILESYSTEM); } {digit}{1,3}(\.{digit}{1,3}){3}"/"{digit}{1,2} { return(IPV4_CIDR); } {digit}{1,3}(\.{digit}{1,3}){3} { return(IPV4_ADDR); } {hexval}{0,4}":"{hexval}{0,4}":"({hexval}|[:.])* { return(IPV6_ADDR); } {hexval}{0,4}":"{hexval}{0,4}":"({hexval}|[:.])*"/"{digit}{1,3} { return(IPV6_CIDR); } {digit}+(\.({alnum}|[_.])*)? { return(VERSION_IDENTIFIER); } #line[ ]1[ ]\"[^\n]*\" { set_source_file(yytext+9); } #line[ ]{digit}+ { errno = 0; source_lineno = strtoul(yytext+6, NULL, 10) - 1; if (errno) { yywarn("source line number too big"); } } #[^\n]* { /* delete comments */ } [ \t\f]+ { /* delete whitespace */ } "==" { return(EQUALS); } "!=" { return (NOTEQUAL); } "&&" { return (AND); } "||" { return (OR); } "!" { return (NOT); } "^" { return (XOR); } "," | ":" | ";" | "(" | ")" | "{" | "}" | "[" | "-" | "." | "]" | "~" | "*" { return(yytext[0]); } . { yyerror("unrecognized character"); /* Available since bison 3.6, avoids duplicate error message */ #ifdef YYerror return YYerror; #else return INVALID_CHAR; #endif } %% int yyerror(const char *msg) { #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION const char *token; char buf[8]; if (isprint((unsigned char)yytext[0])) { token = yytext; } else { snprintf(buf, sizeof(buf), "%#x", yytext[0]); token = buf; } if (source_file[0]) fprintf(stderr, "%s:%lu:", source_file, source_lineno); else fprintf(stderr, "(unknown source)::"); fprintf(stderr, "ERROR '%s' at token '%s' on line %lu:\n%s\n%s\n", msg, token, policydb_lineno, linebuf[0], linebuf[1]); #else (void)msg; #endif policydb_errors++; return -1; } int yywarn(const char *msg) { if (werror) return yyerror(msg); #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION if (source_file[0]) fprintf(stderr, "%s:%lu:", source_file, source_lineno); else fprintf(stderr, "(unknown source)::"); fprintf(stderr, "WARNING '%s' at token '%s' on line %lu:\n%s\n%s\n", msg, yytext, policydb_lineno, linebuf[0], linebuf[1]); #endif return 0; } void set_source_file(const char *name) { source_lineno = 1; strncpy(source_file, name, sizeof(source_file)-1); source_file[sizeof(source_file)-1] = '\0'; if (strlen(source_file) && source_file[strlen(source_file)-1] == '"') source_file[strlen(source_file)-1] = '\0'; } checkpolicy-3.8.1/queue.c000066400000000000000000000053341476211737200153270ustar00rootroot00000000000000 /* Author : Stephen Smalley, */ /* FLASK */ /* * Implementation of the double-ended queue type. */ #include #include "queue.h" queue_t queue_create(void) { queue_t q; q = (queue_t) malloc(sizeof(struct queue_info)); if (q == NULL) return NULL; q->head = q->tail = NULL; return q; } int queue_insert(queue_t q, queue_element_t e) { queue_node_ptr_t newnode; if (!q) return -1; newnode = (queue_node_ptr_t) malloc(sizeof(struct queue_node)); if (newnode == NULL) return -1; newnode->element = e; newnode->next = NULL; if (q->head == NULL) { q->head = q->tail = newnode; } else { q->tail->next = newnode; q->tail = newnode; } return 0; } int queue_push(queue_t q, queue_element_t e) { queue_node_ptr_t newnode; if (!q) return -1; newnode = (queue_node_ptr_t) malloc(sizeof(struct queue_node)); if (newnode == NULL) return -1; newnode->element = e; newnode->next = NULL; if (q->head == NULL) { q->head = q->tail = newnode; } else { newnode->next = q->head; q->head = newnode; } return 0; } queue_element_t queue_remove(queue_t q) { queue_node_ptr_t node; queue_element_t e; if (!q) return NULL; if (q->head == NULL) return NULL; node = q->head; q->head = q->head->next; if (q->head == NULL) q->tail = NULL; e = node->element; free(node); return e; } queue_element_t queue_head(queue_t q) { if (!q) return NULL; if (q->head == NULL) return NULL; return q->head->element; } void queue_clear(queue_t q) { queue_node_ptr_t p, temp; if (!q) return; p = q->head; while (p != NULL) { free(p->element); temp = p; p = p->next; free(temp); } q->head = q->tail = NULL; } void queue_destroy(queue_t q) { queue_node_ptr_t p, temp; if (!q) return; p = q->head; while (p != NULL) { free(p->element); temp = p; p = p->next; free(temp); } free(q); } int queue_map(queue_t q, int (*f) (queue_element_t, void *), void *vp) { queue_node_ptr_t p; int ret; if (!q) return 0; p = q->head; while (p != NULL) { ret = f(p->element, vp); if (ret) return ret; p = p->next; } return 0; } void queue_map_remove_on_error(queue_t q, int (*f) (queue_element_t, void *), void (*g) (queue_element_t, void *), void *vp) { queue_node_ptr_t p, last, temp; int ret; if (!q) return; last = NULL; p = q->head; while (p != NULL) { ret = f(p->element, vp); if (ret) { if (last) { last->next = p->next; if (last->next == NULL) q->tail = last; } else { q->head = p->next; if (q->head == NULL) q->tail = NULL; } temp = p; p = p->next; g(temp->element, vp); free(temp); } else { last = p; p = p->next; } } return; } /* FLASK */ checkpolicy-3.8.1/queue.h000066400000000000000000000030311476211737200153240ustar00rootroot00000000000000 /* Author : Stephen Smalley, */ /* FLASK */ /* * A double-ended queue is a singly linked list of * elements of arbitrary type that may be accessed * at either end. */ #ifndef _QUEUE_H_ #define _QUEUE_H_ typedef void *queue_element_t; typedef struct queue_node *queue_node_ptr_t; typedef struct queue_node { queue_element_t element; queue_node_ptr_t next; } queue_node_t; typedef struct queue_info { queue_node_ptr_t head; queue_node_ptr_t tail; } queue_info_t; typedef queue_info_t *queue_t; queue_t queue_create(void); int queue_insert(queue_t, queue_element_t); int queue_push(queue_t, queue_element_t); queue_element_t queue_remove(queue_t); queue_element_t queue_head(queue_t); void queue_clear(queue_t); void queue_destroy(queue_t); /* Applies the specified function f to each element in the specified queue. In addition to passing the element to f, queue_map passes the specified void* pointer to f on each invocation. If f returns a non-zero status, then queue_map will cease iterating through the hash table and will propagate the error return to its caller. */ int queue_map(queue_t, int (*f) (queue_element_t, void *), void *); /* Same as queue_map, except that if f returns a non-zero status, then the element will be removed from the queue and the g function will be applied to the element. */ void queue_map_remove_on_error(queue_t, int (*f) (queue_element_t, void *), void (*g) (queue_element_t, void *), void *); #endif /* FLASK */ checkpolicy-3.8.1/test/000077500000000000000000000000001476211737200150115ustar00rootroot00000000000000checkpolicy-3.8.1/test/.gitignore000066400000000000000000000000161476211737200167760ustar00rootroot00000000000000dismod dispol checkpolicy-3.8.1/test/Makefile000066400000000000000000000011351476211737200164510ustar00rootroot00000000000000# # Makefile for building the dispol program # CFLAGS ?= -g -Wall -W -Werror -O2 # If no specific libsepol.a is specified, fall back on LDFLAGS search path # Otherwise, as $(LIBSEPOLA) already appears in the dependencies, there # is no need to define a value for LDLIBS_LIBSEPOLA ifeq ($(LIBSEPOLA),) LDLIBS_LIBSEPOLA := -l:libsepol.a endif all: dispol dismod dispol: dispol.o $(LIBSEPOLA) $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS_LIBSEPOLA) dismod: dismod.o $(LIBSEPOLA) $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS_LIBSEPOLA) clean: -rm -f dispol dismod *.o checkpolicy-3.8.1/test/dismod.c000066400000000000000000000660411476211737200164430ustar00rootroot00000000000000/* Authors: Frank Mayer and Karl MacMillan * * Copyright (C) 2003,2004,2005 Tresys Technology, LLC * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ /* * dismod.c * * Test program to the contents of a binary policy in text * form. * * dismod binary_mod_file */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __BYTE_ORDER == __LITTLE_ENDIAN #define le32_to_cpu(x) (x) #else #define le32_to_cpu(x) bswap_32(x) #endif #define DISPLAY_AVBLOCK_COND_AVTAB 0 #define DISPLAY_AVBLOCK_UNCOND_AVTAB 1 #define DISPLAY_AVBLOCK_ROLE_TYPE_NODE 2 /* unused? */ #define DISPLAY_AVBLOCK_ROLE_TRANS 3 #define DISPLAY_AVBLOCK_ROLE_ALLOW 4 #define DISPLAY_AVBLOCK_REQUIRES 5 #define DISPLAY_AVBLOCK_DECLARES 6 #define DISPLAY_AVBLOCK_FILENAME_TRANS 7 static policydb_t policydb; static const char *const symbol_labels[9] = { "commons", "classes", "roles ", "types ", "users ", "bools ", "levels ", "cats ", "attribs" }; static struct command { enum { EOL = 0, HEADER = 1, CMD = 1 << 1, NOOPT = 1 << 2, } meta; char cmd; const char *desc; } commands[] = { {HEADER, 0, "\nSelect a command:"}, {CMD, '1', "display unconditional AVTAB" }, {CMD, '2', "display conditional AVTAB" }, {CMD, '3', "display users" }, {CMD, '4', "display bools" }, {CMD, '5', "display roles" }, {CMD, '6', "display types, attributes, and aliases" }, {CMD, '7', "display role transitions" }, {CMD, '8', "display role allows" }, {CMD, '9', "Display policycon" }, {CMD, '0', "Display initial SIDs" }, {HEADER, 0, ""}, {CMD, 'a', "Display avrule requirements"}, {CMD, 'b', "Display avrule declarations"}, {CMD, 'c', "Display policy capabilities"}, {CMD|NOOPT, 'l', "Link in a module"}, {CMD, 'u', "Display the unknown handling setting"}, {CMD, 'F', "Display filename_trans rules"}, {CMD, 'v', "display the version of policy and/or module"}, {HEADER, 0, ""}, {CMD|NOOPT, 'f', "set output file"}, {CMD|NOOPT, 'm', "display menu"}, {CMD|NOOPT, 'q', "quit"}, {EOL, 0, "" }, }; static __attribute__((__noreturn__)) void usage(const char *progname) { puts("Usage:"); printf(" %s [OPTIONS] binary_pol_file\n\n", progname); puts("Options:"); puts(" -h, --help print this help message"); puts(" -a, --actions ACTIONS run non-interactively"); puts(""); puts("Actions:"); for (unsigned int i = 0; commands[i].meta != EOL; i++) { if (commands[i].meta == HEADER || commands[i].meta & NOOPT) continue; printf(" %c %s\n", commands[i].cmd, commands[i].desc); } puts(""); exit(1); } static void render_access_mask(uint32_t mask, uint32_t class, policydb_t * p, FILE * fp) { char *perm = sepol_av_to_string(p, class, mask); fprintf(fp, "{"); fprintf(fp, "%s ", perm ?: ""); fprintf(fp, "}"); free(perm); } static void render_access_bitmap(ebitmap_t * map, uint32_t class, policydb_t * p, FILE * fp) { unsigned int i; char *perm; fprintf(fp, " {"); for (i = ebitmap_startbit(map); i < ebitmap_length(map); i++) { if (ebitmap_get_bit(map, i)) { perm = sepol_av_to_string(p, class, UINT32_C(1) << i); fprintf(fp, "%s", perm ?: ""); free(perm); } } fprintf(fp, " }"); } static void display_id(policydb_t * p, FILE * fp, uint32_t symbol_type, uint32_t symbol_value, const char *prefix) { char *id = p->sym_val_to_name[symbol_type][symbol_value]; scope_datum_t *scope = (scope_datum_t *) hashtab_search(p->scope[symbol_type].table, id); assert(scope != NULL); if (scope->scope == SCOPE_REQ) { fprintf(fp, " [%s%s]", prefix, id); } else { fprintf(fp, " %s%s", prefix, id); } } static int display_type_set(type_set_t * set, uint32_t flags, policydb_t * policy, FILE * fp) { unsigned int i, num_types; if (set->flags & TYPE_STAR) { fprintf(fp, " *"); return 0; } else if (set->flags & TYPE_COMP) { fprintf(fp, " ~"); } else { fprintf(fp, " "); } num_types = 0; if (flags & (RULE_SELF | RULE_NOTSELF)) { num_types++; } for (i = ebitmap_startbit(&set->types); i < ebitmap_length(&set->types); i++) { if (!ebitmap_get_bit(&set->types, i)) continue; num_types++; if (num_types > 1) break; } if (num_types <= 1) { for (i = ebitmap_startbit(&set->negset); i < ebitmap_length(&set->negset); i++) { if (!ebitmap_get_bit(&set->negset, i)) continue; num_types++; if (num_types > 1) break; } } if (num_types > 1) fprintf(fp, "{"); for (i = ebitmap_startbit(&set->types); i < ebitmap_length(&set->types); i++) { if (!ebitmap_get_bit(&set->types, i)) continue; display_id(policy, fp, SYM_TYPES, i, ""); } for (i = ebitmap_startbit(&set->negset); i < ebitmap_length(&set->negset); i++) { if (!ebitmap_get_bit(&set->negset, i)) continue; display_id(policy, fp, SYM_TYPES, i, "-"); } if (flags & RULE_SELF) { fprintf(fp, " self"); } if (flags & RULE_NOTSELF) { if (set->flags & TYPE_COMP) fprintf(fp, " self"); else fprintf(fp, " -self"); } if (num_types > 1) fprintf(fp, " }"); return 0; } static int display_mod_role_set(role_set_t * roles, policydb_t * p, FILE * fp) { unsigned int i, num = 0; if (roles->flags & ROLE_STAR) { fprintf(fp, " * "); return 0; } else if (roles->flags & ROLE_COMP) { fprintf(fp, " ~"); } for (i = ebitmap_startbit(&roles->roles); i < ebitmap_length(&roles->roles); i++) { if (!ebitmap_get_bit(&roles->roles, i)) continue; num++; if (num > 1) { fprintf(fp, "{"); break; } } for (i = ebitmap_startbit(&roles->roles); i < ebitmap_length(&roles->roles); i++) { if (ebitmap_get_bit(&roles->roles, i)) display_id(p, fp, SYM_ROLES, i, ""); } if (num > 1) fprintf(fp, " }"); return 0; } static int display_avrule(avrule_t * avrule, policydb_t * policy, FILE * fp) { class_perm_node_t *cur; int num_classes; if (avrule == NULL) { fprintf(fp, " \n"); return 0; } if (avrule->specified & AVRULE_AV) { if (avrule->specified & AVRULE_ALLOWED) { fprintf(fp, " allow"); } if (avrule->specified & AVRULE_AUDITALLOW) { fprintf(fp, " auditallow "); } if (avrule->specified & AVRULE_DONTAUDIT) { fprintf(fp, " dontaudit"); } if (avrule->specified & AVRULE_NEVERALLOW) { fprintf(fp, " neverallow"); } } else if (avrule->specified & AVRULE_TYPE) { if (avrule->specified & AVRULE_TRANSITION) { fprintf(fp, " type_transition"); } if (avrule->specified & AVRULE_MEMBER) { fprintf(fp, " type_member"); } if (avrule->specified & AVRULE_CHANGE) { fprintf(fp, " type_change"); } } else if (avrule->specified & AVRULE_XPERMS) { if (avrule->specified & AVRULE_XPERMS_ALLOWED) fprintf(fp, " allowxperm"); else if (avrule->specified & AVRULE_XPERMS_AUDITALLOW) fprintf(fp, " auditallowxperm"); else if (avrule->specified & AVRULE_XPERMS_DONTAUDIT) fprintf(fp, " dontauditxperm"); else if (avrule->specified & AVRULE_XPERMS_NEVERALLOW) fprintf(fp, " neverallowxperm"); } else { fprintf(fp, " ERROR: no valid rule type specified\n"); return -1; } if (display_type_set(&avrule->stypes, 0, policy, fp)) return -1; if (display_type_set(&avrule->ttypes, avrule->flags, policy, fp)) return -1; fprintf(fp, " :"); cur = avrule->perms; num_classes = 0; while (cur) { num_classes++; if (num_classes > 1) break; cur = cur->next; } if (num_classes > 1) fprintf(fp, " {"); cur = avrule->perms; while (cur) { display_id(policy, fp, SYM_CLASSES, cur->tclass - 1, ""); cur = cur->next; } if (num_classes > 1) fprintf(fp, " }"); fprintf(fp, " "); if (avrule->specified & (AVRULE_AV | AVRULE_NEVERALLOW)) { render_access_mask(avrule->perms->data, avrule->perms->tclass, policy, fp); } else if (avrule->specified & AVRULE_TYPE) { display_id(policy, fp, SYM_TYPES, avrule->perms->data - 1, ""); } else if (avrule->specified & AVRULE_XPERMS) { avtab_extended_perms_t xperms; char *perms; int i; if (avrule->xperms->specified == AVRULE_XPERMS_IOCTLFUNCTION) xperms.specified = AVTAB_XPERMS_IOCTLFUNCTION; else if (avrule->xperms->specified == AVRULE_XPERMS_IOCTLDRIVER) xperms.specified = AVTAB_XPERMS_IOCTLDRIVER; else if (avrule->xperms->specified == AVRULE_XPERMS_NLMSG) xperms.specified = AVTAB_XPERMS_NLMSG; else { fprintf(fp, " ERROR: no valid xperms specified\n"); return -1; } xperms.driver = avrule->xperms->driver; for (i = 0; i < EXTENDED_PERMS_LEN; i++) xperms.perms[i] = avrule->xperms->perms[i]; perms = sepol_extended_perms_to_string(&xperms); if (!perms) { fprintf(fp, " ERROR: failed to format xperms\n"); return -1; } fprintf(fp, "%s", perms); free(perms); } fprintf(fp, ";\n"); return 0; } static int display_type_callback(hashtab_key_t key, hashtab_datum_t datum, void *data) { type_datum_t *type; FILE *fp; unsigned int i, first_attrib = 1; type = (type_datum_t *) datum; fp = (FILE *) data; if (type->primary) { display_id(&policydb, fp, SYM_TYPES, type->s.value - 1, ""); fprintf(fp, " [%d]: ", type->s.value); } else { /* as that aliases have no value of their own and that * they can never be required by a module, use this * alternative way of displaying a name */ fprintf(fp, " %s [%d]: ", (char *)key, type->s.value); } if (type->flavor == TYPE_ATTRIB) { fprintf(fp, "attribute for types"); for (i = ebitmap_startbit(&type->types); i < ebitmap_length(&type->types); i++) { if (!ebitmap_get_bit(&type->types, i)) continue; if (first_attrib) { first_attrib = 0; } else { fprintf(fp, ","); } display_id(&policydb, fp, SYM_TYPES, i, ""); } } else if (type->primary) { fprintf(fp, "type"); } else { fprintf(fp, "alias for type"); display_id(&policydb, fp, SYM_TYPES, type->s.value - 1, ""); } fprintf(fp, " flags:%x\n", type->flags); return 0; } static int display_types(policydb_t * p, FILE * fp) { if (hashtab_map(p->p_types.table, display_type_callback, fp)) return -1; return 0; } static int display_users(policydb_t * p, FILE * fp) { unsigned int i, j; ebitmap_t *bitmap; for (i = 0; i < p->p_users.nprim; i++) { display_id(p, fp, SYM_USERS, i, ""); fprintf(fp, ":"); bitmap = &(p->user_val_to_struct[i]->roles.roles); for (j = ebitmap_startbit(bitmap); j < ebitmap_length(bitmap); j++) { if (ebitmap_get_bit(bitmap, j)) { display_id(p, fp, SYM_ROLES, j, ""); } } fprintf(fp, "\n"); } return 0; } static int display_bools(policydb_t * p, FILE * fp) { unsigned int i; for (i = 0; i < p->p_bools.nprim; i++) { display_id(p, fp, SYM_BOOLS, i, ""); fprintf(fp, " : %d\n", p->bool_val_to_struct[i]->state); } return 0; } static void display_expr(policydb_t * p, cond_expr_t * exp, FILE * fp) { cond_expr_t *cur; for (cur = exp; cur != NULL; cur = cur->next) { switch (cur->expr_type) { case COND_BOOL: fprintf(fp, "%s ", p->p_bool_val_to_name[cur->boolean - 1]); break; case COND_NOT: fprintf(fp, "! "); break; case COND_OR: fprintf(fp, "|| "); break; case COND_AND: fprintf(fp, "&& "); break; case COND_XOR: fprintf(fp, "^ "); break; case COND_EQ: fprintf(fp, "== "); break; case COND_NEQ: fprintf(fp, "!= "); break; default: fprintf(fp, "error!"); break; } } } static void display_policycon(FILE * fp) { /* There was an attempt to implement this at one time. Look through * git history to find it. */ fprintf(fp, "Sorry, not implemented\n"); } static void display_initial_sids(policydb_t * p, FILE * fp) { ocontext_t *cur; char *user, *role, *type; fprintf(fp, "Initial SIDs:\n"); for (cur = p->ocontexts[OCON_ISID]; cur != NULL; cur = cur->next) { user = p->p_user_val_to_name[cur->context[0].user - 1]; role = p->p_role_val_to_name[cur->context[0].role - 1]; type = p->p_type_val_to_name[cur->context[0].type - 1]; fprintf(fp, "\tsid %d, context %s:%s:%s\n", cur->sid[0], user, role, type); } #if 0 fprintf(fp, "Policy Initial SIDs:\n"); for (cur = p->ocontexts[OCON_POLICYISID]; cur != NULL; cur = cur->next) { user = p->p_user_val_to_name[cur->context[0].user - 1]; role = p->p_role_val_to_name[cur->context[0].role - 1]; type = p->p_type_val_to_name[cur->context[0].type - 1]; fprintf(fp, "\t%s: sid %d, context %s:%s:%s\n", cur->u.name, cur->sid[0], user, role, type); } #endif } static void display_class_set(ebitmap_t *classes, policydb_t *p, FILE *fp) { unsigned int i, num = 0; for (i = ebitmap_startbit(classes); i < ebitmap_length(classes); i++) { if (!ebitmap_get_bit(classes, i)) continue; num++; if (num > 1) { fprintf(fp, "{"); break; } } for (i = ebitmap_startbit(classes); i < ebitmap_length(classes); i++) { if (ebitmap_get_bit(classes, i)) display_id(p, fp, SYM_CLASSES, i, ""); } if (num > 1) fprintf(fp, " }"); } static void display_role_trans(role_trans_rule_t * tr, policydb_t * p, FILE * fp) { for (; tr; tr = tr->next) { fprintf(fp, "role transition "); display_mod_role_set(&tr->roles, p, fp); display_type_set(&tr->types, 0, p, fp); fprintf(fp, " :"); display_class_set(&tr->classes, p, fp); display_id(p, fp, SYM_ROLES, tr->new_role - 1, ""); fprintf(fp, "\n"); } } static void display_role_allow(role_allow_rule_t * ra, policydb_t * p, FILE * fp) { for (; ra; ra = ra->next) { fprintf(fp, "role allow "); display_mod_role_set(&ra->roles, p, fp); display_mod_role_set(&ra->new_roles, p, fp); fprintf(fp, "\n"); } } static void display_filename_trans(filename_trans_rule_t * tr, policydb_t * p, FILE * fp) { fprintf(fp, "filename transition"); for (; tr; tr = tr->next) { display_type_set(&tr->stypes, 0, p, fp); display_type_set(&tr->ttypes, 0, p, fp); display_id(p, fp, SYM_CLASSES, tr->tclass - 1, ":"); display_id(p, fp, SYM_TYPES, tr->otype - 1, ""); fprintf(fp, " %s\n", tr->name); } } static int role_display_callback(hashtab_key_t key __attribute__((unused)), hashtab_datum_t datum, void *data) { role_datum_t *role; FILE *fp; role = (role_datum_t *) datum; fp = (FILE *) data; fprintf(fp, "role:"); display_id(&policydb, fp, SYM_ROLES, role->s.value - 1, ""); fprintf(fp, " types: "); display_type_set(&role->types, 0, &policydb, fp); fprintf(fp, "\n"); return 0; } static int display_scope_index(scope_index_t * indices, policydb_t * p, FILE * out_fp) { unsigned int i; for (i = 0; i < SYM_NUM; i++) { unsigned int any_found = 0, j; fprintf(out_fp, "%s:", symbol_labels[i]); for (j = ebitmap_startbit(&indices->scope[i]); j < ebitmap_length(&indices->scope[i]); j++) { if (ebitmap_get_bit(&indices->scope[i], j)) { any_found = 1; fprintf(out_fp, " %s", p->sym_val_to_name[i][j]); if (i == SYM_CLASSES) { if (j < indices->class_perms_len) { render_access_bitmap(indices-> class_perms_map + j, j + 1, p, out_fp); } else { fprintf(out_fp, " "); } } } } if (!any_found) { fprintf(out_fp, " "); } fprintf(out_fp, "\n"); } return 0; } #if 0 int display_cond_expressions(policydb_t * p, FILE * fp) { cond_node_t *cur; cond_av_list_t *av_cur; for (cur = p->cond_list; cur != NULL; cur = cur->next) { fprintf(fp, "expression: "); display_expr(p, cur->expr, fp); fprintf(fp, "current state: %d\n", cur->cur_state); fprintf(fp, "True list:\n"); for (av_cur = cur->true_list; av_cur != NULL; av_cur = av_cur->next) { fprintf(fp, "\t"); render_av_rule(&av_cur->node->key, &av_cur->node->datum, RENDER_CONDITIONAL, p, fp); } fprintf(fp, "False list:\n"); for (av_cur = cur->false_list; av_cur != NULL; av_cur = av_cur->next) { fprintf(fp, "\t"); render_av_rule(&av_cur->node->key, &av_cur->node->datum, RENDER_CONDITIONAL, p, fp); } } return 0; } int change_bool(char *name, int state, policydb_t * p, FILE * fp) { cond_bool_datum_t *boolean; boolean = hashtab_search(p->p_bools.table, name); if (boolean == NULL) { fprintf(fp, "Could not find bool %s\n", name); return -1; } boolean->state = state; evaluate_conds(p); return 0; } #endif static int display_avdecl(avrule_decl_t * decl, int field, policydb_t * policy, FILE * out_fp) { fprintf(out_fp, "decl %u:%s\n", decl->decl_id, (decl->enabled ? " [enabled]" : "")); switch (field) { case DISPLAY_AVBLOCK_COND_AVTAB:{ cond_list_t *cond = decl->cond_list; avrule_t *avrule; while (cond) { fprintf(out_fp, "expression: "); display_expr(&policydb, cond->expr, out_fp); fprintf(out_fp, "current state: %d\n", cond->cur_state); fprintf(out_fp, "True list:\n"); avrule = cond->avtrue_list; while (avrule) { display_avrule(avrule, &policydb, out_fp); avrule = avrule->next; } fprintf(out_fp, "False list:\n"); avrule = cond->avfalse_list; while (avrule) { display_avrule(avrule, &policydb, out_fp); avrule = avrule->next; } cond = cond->next; } break; } case DISPLAY_AVBLOCK_UNCOND_AVTAB:{ avrule_t *avrule = decl->avrules; if (avrule == NULL) { fprintf(out_fp, " \n"); } while (avrule != NULL) { if (display_avrule(avrule, policy, out_fp)) return -1; avrule = avrule->next; } break; } case DISPLAY_AVBLOCK_ROLE_TYPE_NODE:{ /* role_type_node */ break; } case DISPLAY_AVBLOCK_ROLE_TRANS:{ display_role_trans(decl->role_tr_rules, policy, out_fp); break; } case DISPLAY_AVBLOCK_ROLE_ALLOW:{ display_role_allow(decl->role_allow_rules, policy, out_fp); break; } case DISPLAY_AVBLOCK_REQUIRES:{ if (display_scope_index (&decl->required, policy, out_fp)) { return -1; } break; } case DISPLAY_AVBLOCK_DECLARES:{ if (display_scope_index (&decl->declared, policy, out_fp)) { return -1; } break; } case DISPLAY_AVBLOCK_FILENAME_TRANS: display_filename_trans(decl->filename_trans_rules, policy, out_fp); break; default:{ assert(0); } } return 0; /* should never get here */ } static int display_avblock(int field, policydb_t * policy, FILE * out_fp) { avrule_block_t *block = policydb.global; while (block != NULL) { avrule_decl_t *decl = block->branch_list; fprintf(out_fp, "--- begin avrule block ---\n"); while (decl != NULL) { if (display_avdecl(decl, field, policy, out_fp)) { return -1; } decl = decl->next; } block = block->next; } return 0; } static int display_handle_unknown(policydb_t * p, FILE * out_fp) { if (p->handle_unknown == ALLOW_UNKNOWN) fprintf(out_fp, "Allow unknown classes and perms\n"); else if (p->handle_unknown == DENY_UNKNOWN) fprintf(out_fp, "Deny unknown classes and perms\n"); else if (p->handle_unknown == REJECT_UNKNOWN) fprintf(out_fp, "Reject unknown classes and perms\n"); return 0; } static int read_policy(char *filename, policydb_t * policy, int verbose) { FILE *in_fp; struct policy_file f; int retval; uint32_t buf[1]; if ((in_fp = fopen(filename, "rb")) == NULL) { fprintf(stderr, "Can't open '%s': %s\n", filename, strerror(errno)); exit(1); } policy_file_init(&f); f.type = PF_USE_STDIO; f.fp = in_fp; /* peek at the first byte. if they are indicative of a package use the package reader, otherwise use the normal policy reader */ if (fread(buf, sizeof(uint32_t), 1, in_fp) != 1) { fprintf(stderr, "Could not read from policy.\n"); exit(1); } rewind(in_fp); if (le32_to_cpu(buf[0]) == SEPOL_MODULE_PACKAGE_MAGIC) { sepol_module_package_t *package; if (sepol_module_package_create(&package)) { fprintf(stderr, "%s: Out of memory!\n", __FUNCTION__); exit(1); } sepol_policydb_free(package->policy); package->policy = (sepol_policydb_t *) policy; package->file_contexts = NULL; retval = sepol_module_package_read(package, (sepol_policy_file_t *) & f, verbose); package->policy = NULL; sepol_module_package_free(package); } else { retval = policydb_read(policy, &f, verbose); } fclose(in_fp); return retval; } static void link_module(policydb_t * base, FILE * out_fp, int verbose) { char module_name[80] = { 0 }; int ret; policydb_t module, *mods = &module; if (base->policy_type != POLICY_BASE) { printf("Can only link if initial file was a base policy.\n"); return; } printf("\nModule filename: "); if (fgets(module_name, sizeof(module_name), stdin) == NULL) { fprintf(stderr, "fgets failed at line %d: %s\n", __LINE__, strerror(errno)); exit(1); } module_name[strlen(module_name) - 1] = '\0'; /* remove LF */ if (module_name[0] == '\0') { return; } if (policydb_init(mods)) { fprintf(stderr, "Out of memory!\n"); exit(1); } /* read the binary policy */ if (verbose) fprintf(out_fp, "Reading module...\n"); policydb_set_target_platform(mods, base->target_platform); if (read_policy(module_name, mods, verbose)) { fprintf(stderr, "%s: error(s) encountered while loading policy\n", module_name); exit(1); } if (module.policy_type != POLICY_MOD) { fprintf(stderr, "This file is not a loadable policy module.\n"); exit(1); } if (policydb_index_classes(&module) || policydb_index_others(NULL, &module, 0)) { fprintf(stderr, "Could not index module.\n"); exit(1); } ret = link_modules(NULL, base, &mods, 1, 0); if (ret != 0) { printf("Link failed (error %d)\n", ret); printf("(You will probably need to restart dismod.)\n"); } policydb_destroy(&module); return; } static void display_policycaps(policydb_t * p, FILE * fp) { ebitmap_node_t *node; const char *capname; char buf[64]; unsigned int i; fprintf(fp, "policy capabilities:\n"); ebitmap_for_each_positive_bit(&p->policycaps, node, i) { capname = sepol_polcap_getname(i); if (capname == NULL) { snprintf(buf, sizeof(buf), "unknown (%u)", i); capname = buf; } fprintf(fp, "\t%s\n", capname); } } static int menu(void) { unsigned int i; for (i = 0; commands[i].meta != EOL; i++) { if (commands[i].meta == HEADER) printf("%s\n", commands[i].desc); else if (commands[i].meta & CMD) printf("%c) %s\n", commands[i].cmd, commands[i].desc); } return 0; } static void print_version_info(policydb_t * p, FILE * fp) { if (p->policy_type == POLICY_BASE) { fprintf(fp, "Binary base policy file loaded.\n"); } else { fprintf(fp, "Binary policy module file loaded.\n"); fprintf(fp, "Module name: %s\n", p->name); fprintf(fp, "Module version: %s\n", p->version); } fprintf(fp, "Policy version: %d\n\n", p->policyvers); } int main(int argc, char **argv) { char *ops = NULL; char *mod; FILE *out_fp = stdout; char ans[81], OutfileName[121]; if (argc < 2 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) usage(argv[0]); mod = argv[1]; if (strcmp (mod, "--actions") == 0 || strcmp (mod, "-a") == 0) { if (argc != 4) { fprintf(stderr, "%s: unexpected number of arguments\n", argv[0]); usage(argv[0]); } ops = argv[2]; mod = argv[3]; } else if (mod[0] == '-') { fprintf(stderr, "%s: unknown option: %s\n", argv[0], mod); usage(argv[0]); } /* read the binary policy */ if (!ops) fprintf(out_fp, "Reading policy...\n"); if (policydb_init(&policydb)) { fprintf(stderr, "%s: Out of memory!\n", __FUNCTION__); exit(1); } if (read_policy(mod, &policydb, ops? 0: 1)) { fprintf(stderr, "%s: error(s) encountered while loading policy\n", argv[0]); exit(1); } if (policydb.policy_type != POLICY_BASE && policydb.policy_type != POLICY_MOD) { fprintf(stderr, "This file is neither a base nor loadable policy module.\n"); exit(1); } if (policydb_index_classes(&policydb)) { fprintf(stderr, "Error indexing classes\n"); exit(1); } if (policydb_index_others(NULL, &policydb, ops? 0: 1)) { fprintf(stderr, "Error indexing others\n"); exit(1); } if (!ops) { print_version_info(&policydb, stdout); menu(); } for (;;) { if (ops) { puts(""); ans[0] = *ops? *ops++: 'q'; ans[1] = '\0'; } else { printf("\nCommand (\'m\' for menu): "); if (fgets(ans, sizeof(ans), stdin) == NULL) { if (feof(stdin)) break; fprintf(stderr, "fgets failed at line %d: %s\n", __LINE__, strerror(errno)); continue; } } switch (ans[0]) { case '1': fprintf(out_fp, "unconditional avtab:\n"); display_avblock(DISPLAY_AVBLOCK_UNCOND_AVTAB, &policydb, out_fp); break; case '2': fprintf(out_fp, "conditional avtab:\n"); display_avblock(DISPLAY_AVBLOCK_COND_AVTAB, &policydb, out_fp); break; case '3': display_users(&policydb, out_fp); break; case '4': display_bools(&policydb, out_fp); break; case '5': if (hashtab_map (policydb.p_roles.table, role_display_callback, out_fp)) exit(1); break; case '6': if (display_types(&policydb, out_fp)) { fprintf(stderr, "Error displaying types\n"); exit(1); } break; case '7': fprintf(out_fp, "role transitions:\n"); display_avblock(DISPLAY_AVBLOCK_ROLE_TRANS, &policydb, out_fp); break; case '8': fprintf(out_fp, "role allows:\n"); display_avblock(DISPLAY_AVBLOCK_ROLE_ALLOW, &policydb, out_fp); break; case '9': display_policycon(out_fp); break; case '0': display_initial_sids(&policydb, out_fp); break; case 'a': fprintf(out_fp, "avrule block requirements:\n"); display_avblock(DISPLAY_AVBLOCK_REQUIRES, &policydb, out_fp); break; case 'b': fprintf(out_fp, "avrule block declarations:\n"); display_avblock(DISPLAY_AVBLOCK_DECLARES, &policydb, out_fp); break; case 'c': display_policycaps(&policydb, out_fp); break; case 'u': case 'U': display_handle_unknown(&policydb, out_fp); break; case 'f': printf ("\nFilename for output ( for screen output): "); if (fgets(OutfileName, sizeof(OutfileName), stdin) == NULL) { fprintf(stderr, "fgets failed at line %d: %s\n", __LINE__, strerror(errno)); break; } OutfileName[strlen(OutfileName) - 1] = '\0'; /* fix_string (remove LF) */ if (strlen(OutfileName) == 0) out_fp = stdout; else if ((out_fp = fopen(OutfileName, "w")) == NULL) { fprintf(stderr, "Cannot open output file %s\n", OutfileName); out_fp = stdout; } if (out_fp != stdout) printf("\nOutput to file: %s\n", OutfileName); break; case 'F': fprintf(out_fp, "filename_trans rules:\n"); display_avblock(DISPLAY_AVBLOCK_FILENAME_TRANS, &policydb, out_fp); break; case 'l': link_module(&policydb, out_fp, ops? 0: 1); break; case 'v': print_version_info(&policydb, out_fp); break; case 'q': policydb_destroy(&policydb); exit(0); break; case 'm': menu(); break; default: printf("\nInvalid choice\n"); menu(); break; } } exit(EXIT_SUCCESS); } checkpolicy-3.8.1/test/dispol.c000066400000000000000000000432611476211737200164550ustar00rootroot00000000000000/* Authors: Frank Mayer and Karl MacMillan * * Copyright (C) 2003 Tresys Technology, LLC * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. */ /* * displaypol.c * * Test program to the contents of a binary policy in text * form. This program currently only displays the * avtab (including conditional avtab) rules. * * displaypol binary_pol_file */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const struct command { enum { EOL = 0, HEADER = 1, CMD = 1 << 1, NOOPT = 1 << 2, } meta; char cmd; const char *desc; } commands[] = { {HEADER, 0, "\nSelect a command:"}, {CMD, '1', "display unconditional AVTAB" }, {CMD, '2', "display conditional AVTAB (entirely)"}, {CMD, '3', "display conditional AVTAB (only ENABLED rules)"}, {CMD, '4', "display conditional AVTAB (only DISABLED rules)"}, {CMD, '5', "display booleans"}, {CMD, '6', "display conditional expressions"}, {CMD|NOOPT, '7', "change a boolean value"}, {CMD, '8', "display role transitions"}, {HEADER, 0, ""}, {CMD, 'c', "display policy capabilities"}, {CMD, 'C', "display classes"}, {CMD, 'u', "display users"}, {CMD, 'r', "display roles"}, {CMD, 't', "display types"}, {CMD, 'a', "display type attributes"}, {CMD, 'p', "display the list of permissive types"}, {CMD, 'U', "display unknown handling setting"}, {CMD, 'F', "display filename_trans rules"}, {HEADER, 0, ""}, {CMD|NOOPT, 'f', "set output file"}, {CMD|NOOPT, 'm', "display menu"}, {CMD|NOOPT, 'q', "quit"}, {EOL, 0, "" }, }; static __attribute__((__noreturn__)) void usage(const char *progname) { puts("Usage:"); printf(" %s [OPTIONS] binary_pol_file\n\n", progname); puts("Options:"); puts(" -h, --help print this help message"); puts(" -a, --actions ACTIONS run non-interactively"); puts(""); puts("Actions:"); for (unsigned int i = 0; commands[i].meta != EOL; i++) { if (commands[i].meta == HEADER || commands[i].meta & NOOPT) continue; printf(" %c %s\n", commands[i].cmd, commands[i].desc); } puts(""); exit(1); } static int render_access_mask(uint32_t mask, avtab_key_t * key, policydb_t * p, FILE * fp) { char *perm = sepol_av_to_string(p, key->target_class, mask); fprintf(fp, "{"); fprintf(fp, "%s ", perm ?: ""); fprintf(fp, "}"); free(perm); return 0; } static int render_type(uint32_t type, policydb_t * p, FILE * fp) { fprintf(fp, "%s", p->p_type_val_to_name[type - 1]); return 0; } static int render_key(avtab_key_t * key, policydb_t * p, FILE * fp) { char *stype, *ttype, *tclass; stype = p->p_type_val_to_name[key->source_type - 1]; ttype = p->p_type_val_to_name[key->target_type - 1]; tclass = p->p_class_val_to_name[key->target_class - 1]; if (stype && ttype) fprintf(fp, "%s %s : %s ", stype, ttype, tclass); else if (stype) fprintf(fp, "%s %u : %s ", stype, key->target_type, tclass); else if (ttype) fprintf(fp, "%u %s : %s ", key->source_type, ttype, tclass); else fprintf(fp, "%u %u : %s ", key->source_type, key->target_type, tclass); return 0; } /* 'what' values for this function */ #define RENDER_UNCONDITIONAL 0x0001 /* render all regardless of enabled state */ #define RENDER_ENABLED 0x0002 #define RENDER_DISABLED 0x0004 #define RENDER_CONDITIONAL (RENDER_ENABLED|RENDER_DISABLED) static int render_av_rule(avtab_key_t * key, avtab_datum_t * datum, uint32_t what, policydb_t * p, FILE * fp) { if (!(what & RENDER_UNCONDITIONAL)) { if (what != RENDER_CONDITIONAL && (((what & RENDER_ENABLED) && !(key-> specified & AVTAB_ENABLED)) || ((what & RENDER_DISABLED) && (key-> specified & AVTAB_ENABLED)))) { return 0; /* doesn't match selection criteria */ } } if (!(what & RENDER_UNCONDITIONAL)) { if (key->specified & AVTAB_ENABLED) fprintf(fp, "[enabled] "); else if (!(key->specified & AVTAB_ENABLED)) fprintf(fp, "[disabled] "); } if (key->specified & AVTAB_AV) { if (key->specified & AVTAB_ALLOWED) { fprintf(fp, "allow "); render_key(key, p, fp); render_access_mask(datum->data, key, p, fp); fprintf(fp, ";\n"); } if (key->specified & AVTAB_AUDITALLOW) { fprintf(fp, "auditallow "); render_key(key, p, fp); render_access_mask(datum->data, key, p, fp); fprintf(fp, ";\n"); } if (key->specified & AVTAB_AUDITDENY) { fprintf(fp, "dontaudit "); render_key(key, p, fp); /* We inverse the mask for dontaudit since the mask is internally stored * as a auditdeny mask */ render_access_mask(~datum->data, key, p, fp); fprintf(fp, ";\n"); } } else if (key->specified & AVTAB_TYPE) { if (key->specified & AVTAB_TRANSITION) { fprintf(fp, "type_transition "); render_key(key, p, fp); render_type(datum->data, p, fp); fprintf(fp, ";\n"); } if (key->specified & AVTAB_MEMBER) { fprintf(fp, "type_member "); render_key(key, p, fp); render_type(datum->data, p, fp); fprintf(fp, ";\n"); } if (key->specified & AVTAB_CHANGE) { fprintf(fp, "type_change "); render_key(key, p, fp); render_type(datum->data, p, fp); fprintf(fp, ";\n"); } } else if (key->specified & AVTAB_XPERMS) { char *perms; if (key->specified & AVTAB_XPERMS_ALLOWED) fprintf(fp, "allowxperm "); else if (key->specified & AVTAB_XPERMS_AUDITALLOW) fprintf(fp, "auditallowxperm "); else if (key->specified & AVTAB_XPERMS_DONTAUDIT) fprintf(fp, "dontauditxperm "); render_key(key, p, fp); perms = sepol_extended_perms_to_string(datum->xperms); if (!perms) { fprintf(fp, " ERROR: failed to format xperms\n"); return -1; } fprintf(fp, "%s;\n", perms); free(perms); } else { fprintf(fp, " ERROR: no valid rule type specified\n"); return -1; } return 0; } static int display_avtab(avtab_t * a, uint32_t what, policydb_t * p, FILE * fp) { unsigned int i; avtab_ptr_t cur; /* hmm...should have used avtab_map. */ for (i = 0; i < a->nslot; i++) { for (cur = a->htable[i]; cur; cur = cur->next) { render_av_rule(&cur->key, &cur->datum, what, p, fp); } } fprintf(fp, "\n"); return 0; } static void display_expr(policydb_t * p, cond_expr_t * exp, FILE * fp) { cond_expr_t *cur; for (cur = exp; cur != NULL; cur = cur->next) { switch (cur->expr_type) { case COND_BOOL: fprintf(fp, "%s ", p->p_bool_val_to_name[cur->boolean - 1]); break; case COND_NOT: fprintf(fp, "! "); break; case COND_OR: fprintf(fp, "|| "); break; case COND_AND: fprintf(fp, "&& "); break; case COND_XOR: fprintf(fp, "^ "); break; case COND_EQ: fprintf(fp, "== "); break; case COND_NEQ: fprintf(fp, "!= "); break; default: fprintf(fp, "error!"); break; } } } static int display_cond_expressions(policydb_t * p, FILE * fp) { cond_node_t *cur; cond_av_list_t *av_cur; for (cur = p->cond_list; cur != NULL; cur = cur->next) { fprintf(fp, "expression: "); display_expr(p, cur->expr, fp); fprintf(fp, "current state: %d\n", cur->cur_state); fprintf(fp, "True list:\n"); for (av_cur = cur->true_list; av_cur != NULL; av_cur = av_cur->next) { fprintf(fp, "\t"); render_av_rule(&av_cur->node->key, &av_cur->node->datum, RENDER_CONDITIONAL, p, fp); } fprintf(fp, "False list:\n"); for (av_cur = cur->false_list; av_cur != NULL; av_cur = av_cur->next) { fprintf(fp, "\t"); render_av_rule(&av_cur->node->key, &av_cur->node->datum, RENDER_CONDITIONAL, p, fp); } } return 0; } static int display_handle_unknown(policydb_t * p, FILE * out_fp) { if (p->handle_unknown == ALLOW_UNKNOWN) fprintf(out_fp, "Allow unknown classes and permissions\n"); else if (p->handle_unknown == DENY_UNKNOWN) fprintf(out_fp, "Deny unknown classes and permissions\n"); else if (p->handle_unknown == REJECT_UNKNOWN) fprintf(out_fp, "Reject unknown classes and permissions\n"); else fprintf(out_fp, "\n"); return 0; } static int change_bool(char *name, int state, policydb_t * p, FILE * fp) { cond_bool_datum_t *boolean; boolean = hashtab_search(p->p_bools.table, name); if (boolean == NULL) { fprintf(fp, "Could not find bool %s\n", name); return -1; } boolean->state = state; evaluate_conds(p); return 0; } static int display_booleans(policydb_t * p, FILE *fp) { uint32_t i; fprintf(fp, "booleans (#%u):\n", p->p_bools.table->nel); for (i = 0; i < p->p_bools.nprim; i++) { fprintf(fp, "\t%s : %d\n", p->p_bool_val_to_name[i], p->bool_val_to_struct[i]->state); } return 0; } static void display_policycaps(policydb_t * p, FILE * fp) { ebitmap_node_t *node; const char *capname; char buf[64]; unsigned int i; fprintf(fp, "policy capabilities:\n"); ebitmap_for_each_positive_bit(&p->policycaps, node, i) { capname = sepol_polcap_getname(i); if (capname == NULL) { snprintf(buf, sizeof(buf), "unknown (%u)", i); capname = buf; } fprintf(fp, "\t%s\n", capname); } } static int display_classes(policydb_t * p, FILE *fp) { uint32_t i; fprintf(fp, "classes (#%u):\n", p->p_classes.table->nel); for (i = 0; i < p->p_classes.nprim; i++) { if (!p->p_class_val_to_name[i]) continue; fprintf(fp, "\t%s\n", p->p_class_val_to_name[i]); } return 0; } static void display_id(policydb_t *p, FILE *fp, uint32_t symbol_type, uint32_t symbol_value, const char *prefix) { const char *id = p->sym_val_to_name[symbol_type][symbol_value]; fprintf(fp, " %s%s", prefix, id); } static void display_permissive(policydb_t *p, FILE *fp) { ebitmap_node_t *node; unsigned int i; fprintf(fp, "permissive sids (#%u):\n", ebitmap_cardinality(&p->permissive_map)); ebitmap_for_each_positive_bit(&p->permissive_map, node, i) { fprintf(fp, "\t"); display_id(p, fp, SYM_TYPES, i - 1, ""); fprintf(fp, "\n"); } } static int display_users(policydb_t * p, FILE *fp) { uint32_t i; fprintf(fp, "users (#%u):\n", p->p_users.table->nel); for (i = 0; i < p->p_users.nprim; i++) { if (!p->p_user_val_to_name[i]) continue; fprintf(fp, "\t%s\n", p->p_user_val_to_name[i]); } return 0; } static int display_roles(policydb_t * p, FILE *fp) { uint32_t i; fprintf(fp, "roles (#%u):\n", p->p_roles.table->nel); for (i = 0; i < p->p_roles.nprim; i++) { if (!p->p_role_val_to_name[i]) continue; fprintf(fp, "\t%s\n", p->p_role_val_to_name[i]); } return 0; } static int display_types(policydb_t * p, FILE *fp) { uint32_t i; fprintf(fp, "types (out of #%u):\n", p->p_types.table->nel); for (i = 0; i < p->p_types.nprim; i++) { if (!p->p_type_val_to_name[i]) continue; if (p->type_val_to_struct[i]->flavor == TYPE_ATTRIB) continue; fprintf(fp, "\t%s\n", p->p_type_val_to_name[i]); } return 0; } static int display_attributes(policydb_t * p, FILE *fp) { uint32_t i; fprintf(fp, "attributes (out of #%u):\n", p->p_types.table->nel); for (i = 0; i < p->p_types.nprim; i++) { if (!p->p_type_val_to_name[i]) continue; if (p->type_val_to_struct[i]->flavor != TYPE_ATTRIB) continue; fprintf(fp, "\t%s\n", p->p_type_val_to_name[i]); } return 0; } static void display_role_trans(policydb_t *p, FILE *fp) { role_trans_t *rt; fprintf(fp, "role_trans rules:\n"); for (rt = p->role_tr; rt; rt = rt->next) { display_id(p, fp, SYM_ROLES, rt->role - 1, ""); display_id(p, fp, SYM_TYPES, rt->type - 1, ""); display_id(p, fp, SYM_CLASSES, rt->tclass - 1, ":"); display_id(p, fp, SYM_ROLES, rt->new_role - 1, ""); fprintf(fp, "\n"); } } struct filenametr_display_args { policydb_t *p; FILE *fp; }; static int filenametr_display(hashtab_key_t key, hashtab_datum_t datum, void *ptr) { struct filename_trans_key *ft = (struct filename_trans_key *)key; struct filename_trans_datum *ftdatum = datum; struct filenametr_display_args *args = ptr; policydb_t *p = args->p; FILE *fp = args->fp; ebitmap_node_t *node; uint32_t bit; do { ebitmap_for_each_positive_bit(&ftdatum->stypes, node, bit) { display_id(p, fp, SYM_TYPES, bit, ""); display_id(p, fp, SYM_TYPES, ft->ttype - 1, ""); display_id(p, fp, SYM_CLASSES, ft->tclass - 1, ":"); display_id(p, fp, SYM_TYPES, ftdatum->otype - 1, ""); fprintf(fp, " %s\n", ft->name); } ftdatum = ftdatum->next; } while (ftdatum); return 0; } static void display_filename_trans(policydb_t *p, FILE *fp) { struct filenametr_display_args args; fprintf(fp, "filename_trans rules:\n"); args.p = p; args.fp = fp; hashtab_map(p->filename_trans, filenametr_display, &args); } static int menu(void) { unsigned int i; for (i = 0; commands[i].meta != EOL; i++) { if (commands[i].meta == HEADER) printf("%s\n", commands[i].desc); else if (commands[i].meta & CMD) printf("%c) %s\n", commands[i].cmd, commands[i].desc); } return 0; } int main(int argc, char **argv) { char *ops = NULL; char *bpol; FILE *out_fp = stdout; char ans[81], OutfileName[121]; int fd, ret; struct stat sb; void *map; char *name; int state; struct policy_file pf; policydb_t policydb; if (argc < 2 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) usage(argv[0]); bpol = argv[1]; if (strcmp (bpol, "--actions") == 0 || strcmp (bpol, "-a") == 0) { if (argc != 4) { fprintf(stderr, "%s: unexpected number of arguments\n", argv[0]); usage(argv[0]); } ops = argv[2]; bpol = argv[3]; } else if (bpol[0] == '-') { fprintf(stderr, "%s: unknown option: %s\n", argv[0], bpol); usage(argv[0]); } fd = open(bpol, O_RDONLY); if (fd < 0) { fprintf(stderr, "Can't open '%s': %s\n", bpol, strerror(errno)); exit(1); } if (fstat(fd, &sb) < 0) { fprintf(stderr, "Can't stat '%s': %s\n", bpol, strerror(errno)); exit(1); } map = mmap(NULL, sb.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (map == MAP_FAILED) { fprintf(stderr, "Can't map '%s': %s\n", bpol, strerror(errno)); exit(1); } /* read the binary policy */ if (!ops) fprintf(out_fp, "Reading policy...\n"); policy_file_init(&pf); pf.type = PF_USE_MEMORY; pf.data = map; pf.len = sb.st_size; if (policydb_init(&policydb)) { fprintf(stderr, "%s: Out of memory!\n", argv[0]); exit(1); } ret = policydb_read(&policydb, &pf, ops? 0: 1); if (ret) { fprintf(stderr, "%s: error(s) encountered while parsing configuration\n", argv[0]); exit(1); } if (!ops) fprintf(stdout, "binary policy file loaded\n\n"); close(fd); if (!ops) menu(); for (;;) { if (ops) { puts(""); ans[0] = *ops? *ops++: 'q'; ans[1] = '\0'; } else { printf("\nCommand (\'m\' for menu): "); if (fgets(ans, sizeof(ans), stdin) == NULL) { if (feof(stdin)) break; fprintf(stderr, "fgets failed at line %d: %s\n", __LINE__, strerror(errno)); continue; } } switch (ans[0]) { case '1': display_avtab(&policydb.te_avtab, RENDER_UNCONDITIONAL, &policydb, out_fp); break; case '2': display_avtab(&policydb.te_cond_avtab, RENDER_CONDITIONAL, &policydb, out_fp); break; case '3': display_avtab(&policydb.te_cond_avtab, RENDER_ENABLED, &policydb, out_fp); break; case '4': display_avtab(&policydb.te_cond_avtab, RENDER_DISABLED, &policydb, out_fp); break; case '5': display_booleans(&policydb, out_fp); break; case '6': display_cond_expressions(&policydb, out_fp); break; case '7': printf("name? "); if (fgets(ans, sizeof(ans), stdin) == NULL) { fprintf(stderr, "fgets failed at line %d: %s\n", __LINE__, strerror(errno)); break; } ans[strlen(ans) - 1] = 0; name = strdup(ans); if (name == NULL) { fprintf(stderr, "couldn't strdup string.\n"); break; } printf("state? "); if (fgets(ans, sizeof(ans), stdin) == NULL) { fprintf(stderr, "fgets failed at line %d: %s\n", __LINE__, strerror(errno)); break; } ans[strlen(ans) - 1] = 0; if (atoi(ans)) state = 1; else state = 0; change_bool(name, state, &policydb, out_fp); free(name); break; case '8': display_role_trans(&policydb, out_fp); break; case 'a': display_attributes(&policydb, out_fp); break; case 'c': display_policycaps(&policydb, out_fp); break; case 'C': display_classes(&policydb, out_fp); break; case 'p': display_permissive(&policydb, out_fp); break; case 'r': display_roles(&policydb, out_fp); break; case 't': display_types(&policydb, out_fp); break; case 'u': display_users(&policydb, out_fp); break; case 'U': display_handle_unknown(&policydb, out_fp); break; case 'f': printf ("\nFilename for output ( for screen output): "); if (fgets(OutfileName, sizeof(OutfileName), stdin) == NULL) { fprintf(stderr, "fgets failed at line %d: %s\n", __LINE__, strerror(errno)); break; } OutfileName[strlen(OutfileName) - 1] = '\0'; /* fix_string (remove LF) */ if (strlen(OutfileName) == 0) out_fp = stdout; else if ((out_fp = fopen(OutfileName, "w")) == NULL) { fprintf(stderr, "Cannot open output file %s\n", OutfileName); out_fp = stdout; } if (out_fp != stdout) printf("\nOutput to file: %s\n", OutfileName); break; case 'F': display_filename_trans(&policydb, out_fp); break; case 'q': policydb_destroy(&policydb); exit(0); break; case 'm': menu(); break; default: printf("\nInvalid choice\n"); menu(); break; } } } /* FLASK */ checkpolicy-3.8.1/tests/000077500000000000000000000000001476211737200151745ustar00rootroot00000000000000checkpolicy-3.8.1/tests/policy_allonce.conf000066400000000000000000000062521476211737200210440ustar00rootroot00000000000000# handle_unknown deny class CLASS1 class CLASS2 class CLASS3 class CLASS4 class dir class file class process sid kernel common COMMON1 { CPERM1 } class CLASS1 { PERM1 ioctl } class CLASS2 inherits COMMON1 class CLASS3 inherits COMMON1 { PERM1 } class CLASS4 { nlmsg } default_user { CLASS1 } source; default_role { CLASS2 } target; default_type { CLASS3 } source; policycap open_perms; attribute ATTR1; attribute ATTR2; expandattribute ATTR1 true; expandattribute ATTR2 false; type TYPE1; type TYPE2, ATTR1; type TYPE3 alias { TYPEALIAS3A TYPEALIAS3B }; type TYPE4 alias TYPEALIAS4, ATTR2; typealias TYPE1 alias TYPEALIAS1; typeattribute TYPE1 ATTR1; typebounds TYPE4 TYPE3; bool BOOL1 true; bool BOOL2 false; tunable TUNABLE1 false; tunable TUNABLE2 true; type_transition TYPE1 TYPE2 : CLASS1 TYPE3; type_transition { TYPE1 TYPE2 } { TYPE3 TYPE4 } : CLASS1 TYPE1 "FILENAME"; type_member TYPE1 TYPE2 : CLASS1 TYPE2; type_change TYPE1 TYPE2 : CLASS1 TYPE3; allow TYPE1 self : CLASS1 { PERM1 }; auditallow { TYPE1 TYPE2 } TYPE3 : CLASS1 { PERM1 }; dontaudit TYPE1 { TYPE2 TYPE3 } : CLASS3 { PERM1 CPERM1 }; neverallow TYPE1 TYPE2 : { CLASS2 CLASS3 } { CPERM1 }; allowxperm TYPE1 TYPE2 : CLASS1 ioctl { 0x456-0x5678 }; allowxperm TYPE2 TYPE1 : CLASS4 nlmsg { 0x1 0x12 }; auditallowxperm TYPE1 TYPE2 : CLASS1 ioctl 0x2; dontauditxperm TYPE1 TYPE2 : CLASS1 ioctl 0x3; neverallowxperm TYPE1 TYPE2 : CLASS1 ioctl 0x4; permissive TYPE1; attribute_role ROLE_ATTR1; role ROLE1; role ROLE3; role ROLE2, ROLE_ATTR1; role_transition ROLE1 TYPE1 ROLE2; role_transition ROLE1 TYPE1 : CLASS1 ROLE2; allow ROLE1 ROLE2; roleattribute ROLE3 ROLE_ATTR1; role ROLE1 types { TYPE1 }; if ! BOOL1 { allow TYPE1 self: CLASS1 *; dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x6789 - 0x9876 }; } if BOOL2 { allowxperm TYPE2 TYPE1:CLASS4 nlmsg { 0x1 0x2 }; } if TUNABLE1 xor TUNABLE2 { allow TYPE1 self: CLASS2 *; } else { allow TYPE1 self: CLASS3 *; } optional { require { class CLASS2 { CPERM1 }; } allow TYPE1 self: CLASS2 *; } user USER1 roles ROLE1; constrain CLASS1 { PERM1 } (u1 == u2 or (r1 == r2 and t1 == t2)); # sameuser will be turned into (u1 == u2) validatetrans CLASS2 sameuser and t3 == ATTR1; sid kernel USER1:ROLE1:TYPE1 # fscon statements are not dumped fscon 2 3 USER1:ROLE1:TYPE1 USER1:ROLE1:TYPE1 fs_use_xattr btrfs USER1:ROLE1:TYPE1; fs_use_trans devpts USER1:ROLE1:TYPE1; fs_use_task pipefs USER1:ROLE1:TYPE1; # paths will be turned into quoted strings genfscon proc / -d USER1:ROLE1:TYPE1 genfscon proc "/file1" -- USER1:ROLE1:TYPE1 genfscon proc "/path/to/file" USER1:ROLE1:TYPE1 portcon tcp 80 USER1:ROLE1:TYPE1 portcon udp 100-200 USER1:ROLE1:TYPE1 netifcon lo USER1:ROLE1:TYPE1 USER1:ROLE1:TYPE1 nodecon 127.0.0.1 255.255.255.255 USER1:ROLE1:TYPE1 nodecon 192.168.42.0 255.255.0.0 USER1:ROLE1:TYPE1 nodecon 127.0.0.1/24 USER1:ROLE1:TYPE1 nodecon 192.168.41.0/16 USER1:ROLE1:TYPE1 nodecon ::ffff:127.0.0.1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff USER1:ROLE1:TYPE1 nodecon ff80::/16 USER1:ROLE1:TYPE1 nodecon ff00::1/8 USER1:ROLE1:TYPE1 # hex numbers will be turned in decimal ones ibpkeycon fe80:: 0xFFFF USER1:ROLE1:TYPE1 ibpkeycon fe80:: 0-0x10 USER1:ROLE1:TYPE1 ibendportcon mlx4_0 2 USER1:ROLE1:TYPE1 ibendportcon mlx5_0 1 USER1:ROLE1:TYPE1 checkpolicy-3.8.1/tests/policy_allonce.expected.conf000066400000000000000000000061201476211737200226360ustar00rootroot00000000000000# handle_unknown deny class CLASS1 class CLASS2 class CLASS3 class CLASS4 class dir class file class process sid kernel common COMMON1 { CPERM1 } class CLASS1 { PERM1 ioctl } class CLASS2 inherits COMMON1 class CLASS3 inherits COMMON1 { PERM1 } class CLASS4 { nlmsg } default_user { CLASS1 } source; default_role { CLASS2 } target; default_type { CLASS3 } source; policycap open_perms; attribute ATTR1; attribute ATTR2; bool BOOL1 true; bool BOOL2 false; type TYPE1; type TYPE2; type TYPE3; type TYPE4; typealias TYPE1 alias TYPEALIAS1; typealias TYPE3 alias TYPEALIAS3A; typealias TYPE3 alias TYPEALIAS3B; typealias TYPE4 alias TYPEALIAS4; typebounds TYPE4 TYPE3; typeattribute TYPE4 ATTR2; permissive TYPE1; allow TYPE1 self:CLASS1 { PERM1 }; allow TYPE1 self:CLASS2 { CPERM1 }; auditallow TYPE1 TYPE3:CLASS1 { PERM1 }; auditallow TYPE2 TYPE3:CLASS1 { PERM1 }; dontaudit TYPE1 TYPE2:CLASS3 { CPERM1 PERM1 }; dontaudit TYPE1 TYPE3:CLASS3 { CPERM1 PERM1 }; allowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x456-0x4ff }; allowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x500-0x55ff }; allowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x5600-0x5678 }; allowxperm TYPE2 TYPE1:CLASS4 nlmsg { 0x1 0x12 }; auditallowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x2 }; dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x3 }; type_transition TYPE1 TYPE2:CLASS1 TYPE3; type_member TYPE1 TYPE2:CLASS1 TYPE2; type_change TYPE1 TYPE2:CLASS1 TYPE3; type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME"; type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME"; type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME"; type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; if (BOOL1) { } else { allow TYPE1 self:CLASS1 { PERM1 ioctl }; dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x6789-0x67ff }; dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x6800-0x97ff }; dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x9800-0x9876 }; } if (BOOL2) { allowxperm TYPE2 TYPE1:CLASS4 nlmsg { 0x1-0x2 }; } role ROLE1; role ROLE2; role ROLE3; role ROLE1 types { TYPE1 }; role_transition ROLE1 TYPE1:CLASS1 ROLE2; role_transition ROLE1 TYPE1:process ROLE2; allow ROLE1 ROLE2; user USER1 roles ROLE1; constrain CLASS1 { PERM1 } (u1 == u2 or (r1 == r2 and t1 == t2)); validatetrans CLASS2 (u1 == u2 and t3 == ATTR1); sid kernel USER1:ROLE1:TYPE1 fs_use_xattr btrfs USER1:ROLE1:TYPE1; fs_use_trans devpts USER1:ROLE1:TYPE1; fs_use_task pipefs USER1:ROLE1:TYPE1; genfscon proc "/" -d USER1:ROLE1:TYPE1 genfscon proc "/file1" -- USER1:ROLE1:TYPE1 genfscon proc "/path/to/file" USER1:ROLE1:TYPE1 portcon tcp 80 USER1:ROLE1:TYPE1 portcon udp 100-200 USER1:ROLE1:TYPE1 netifcon lo USER1:ROLE1:TYPE1 USER1:ROLE1:TYPE1 nodecon 127.0.0.1 255.255.255.255 USER1:ROLE1:TYPE1 nodecon 127.0.0.0 255.255.255.0 USER1:ROLE1:TYPE1 nodecon 192.168.0.0 255.255.0.0 USER1:ROLE1:TYPE1 nodecon 192.168.42.0 255.255.0.0 USER1:ROLE1:TYPE1 nodecon ::ffff:127.0.0.1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff USER1:ROLE1:TYPE1 nodecon ff80:: ffff:: USER1:ROLE1:TYPE1 nodecon ff00:: ff00:: USER1:ROLE1:TYPE1 ibpkeycon fe80:: 65535 USER1:ROLE1:TYPE1 ibpkeycon fe80:: 0-16 USER1:ROLE1:TYPE1 ibendportcon mlx4_0 2 USER1:ROLE1:TYPE1 ibendportcon mlx5_0 1 USER1:ROLE1:TYPE1 checkpolicy-3.8.1/tests/policy_allonce.expected_opt.conf000066400000000000000000000061061476211737200235240ustar00rootroot00000000000000# handle_unknown deny class CLASS1 class CLASS2 class CLASS3 class CLASS4 class dir class file class process sid kernel common COMMON1 { CPERM1 } class CLASS1 { PERM1 ioctl } class CLASS2 inherits COMMON1 class CLASS3 inherits COMMON1 { PERM1 } class CLASS4 { nlmsg } default_user { CLASS1 } source; default_role { CLASS2 } target; default_type { CLASS3 } source; policycap open_perms; attribute ATTR1; attribute ATTR2; bool BOOL1 true; bool BOOL2 false; type TYPE1; type TYPE2; type TYPE3; type TYPE4; typealias TYPE1 alias TYPEALIAS1; typealias TYPE3 alias TYPEALIAS3A; typealias TYPE3 alias TYPEALIAS3B; typealias TYPE4 alias TYPEALIAS4; typebounds TYPE4 TYPE3; typeattribute TYPE4 ATTR2; permissive TYPE1; allow TYPE1 self:CLASS1 { PERM1 }; allow TYPE1 self:CLASS2 { CPERM1 }; auditallow TYPE1 TYPE3:CLASS1 { PERM1 }; auditallow TYPE2 TYPE3:CLASS1 { PERM1 }; dontaudit TYPE1 TYPE2:CLASS3 { CPERM1 PERM1 }; dontaudit TYPE1 TYPE3:CLASS3 { CPERM1 PERM1 }; allowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x456-0x4ff }; allowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x500-0x55ff }; allowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x5600-0x5678 }; allowxperm TYPE2 TYPE1:CLASS4 nlmsg { 0x1 0x12 }; auditallowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x2 }; dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x3 }; type_transition TYPE1 TYPE2:CLASS1 TYPE3; type_member TYPE1 TYPE2:CLASS1 TYPE2; type_change TYPE1 TYPE2:CLASS1 TYPE3; type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME"; type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME"; type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME"; type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; if (BOOL1) { } else { allow TYPE1 self:CLASS1 { ioctl }; dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x6789-0x67ff }; dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x6800-0x97ff }; dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x9800-0x9876 }; } if (BOOL2) { allowxperm TYPE2 TYPE1:CLASS4 nlmsg { 0x2 }; } role ROLE1; role ROLE2; role ROLE3; role ROLE1 types { TYPE1 }; role_transition ROLE1 TYPE1:CLASS1 ROLE2; role_transition ROLE1 TYPE1:process ROLE2; allow ROLE1 ROLE2; user USER1 roles ROLE1; constrain CLASS1 { PERM1 } (u1 == u2 or (r1 == r2 and t1 == t2)); validatetrans CLASS2 (u1 == u2 and t3 == ATTR1); sid kernel USER1:ROLE1:TYPE1 fs_use_xattr btrfs USER1:ROLE1:TYPE1; fs_use_trans devpts USER1:ROLE1:TYPE1; fs_use_task pipefs USER1:ROLE1:TYPE1; genfscon proc "/" -d USER1:ROLE1:TYPE1 genfscon proc "/file1" -- USER1:ROLE1:TYPE1 genfscon proc "/path/to/file" USER1:ROLE1:TYPE1 portcon tcp 80 USER1:ROLE1:TYPE1 portcon udp 100-200 USER1:ROLE1:TYPE1 netifcon lo USER1:ROLE1:TYPE1 USER1:ROLE1:TYPE1 nodecon 127.0.0.1 255.255.255.255 USER1:ROLE1:TYPE1 nodecon 127.0.0.0 255.255.255.0 USER1:ROLE1:TYPE1 nodecon 192.168.0.0 255.255.0.0 USER1:ROLE1:TYPE1 nodecon 192.168.42.0 255.255.0.0 USER1:ROLE1:TYPE1 nodecon ::ffff:127.0.0.1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff USER1:ROLE1:TYPE1 nodecon ff80:: ffff:: USER1:ROLE1:TYPE1 nodecon ff00:: ff00:: USER1:ROLE1:TYPE1 ibpkeycon fe80:: 65535 USER1:ROLE1:TYPE1 ibpkeycon fe80:: 0-16 USER1:ROLE1:TYPE1 ibendportcon mlx4_0 2 USER1:ROLE1:TYPE1 ibendportcon mlx5_0 1 USER1:ROLE1:TYPE1 checkpolicy-3.8.1/tests/policy_allonce_mls.conf000066400000000000000000000062441476211737200217200ustar00rootroot00000000000000# handle_unknown deny class CLASS1 class CLASS2 class CLASS3 class dir class file class process sid kernel common COMMON1 { CPERM1 } class CLASS1 { PERM1 ioctl } class CLASS2 inherits COMMON1 class CLASS3 inherits COMMON1 { PERM1 } default_user { CLASS1 } source; default_role { CLASS2 } target; default_type { CLASS3 } source; sensitivity s0; sensitivity s1; sensitivity s2 alias SENSALIAS; dominance { s0 s1 SENSALIAS } category c0; category c1 alias CATALIAS; level s0:c0; level s1:c0,c1; level s2; mlsconstrain CLASS1 { PERM1 } l1 == l2; mlsvalidatetrans CLASS1 r1 domby r2 and l1 incomp h2; policycap open_perms; attribute ATTR1; attribute ATTR2; expandattribute ATTR1 true; expandattribute ATTR2 false; type TYPE1; type TYPE2, ATTR1; type TYPE3 alias { TYPEALIAS3A TYPEALIAS3B }; type TYPE4 alias TYPEALIAS4, ATTR2; typealias TYPE1 alias TYPEALIAS1; typeattribute TYPE1 ATTR1; typebounds TYPE4 TYPE3; bool BOOL1 true; tunable TUNABLE1 false; tunable TUNABLE2 true; type_transition TYPE1 TYPE2 : CLASS1 TYPE3; type_transition { TYPE1 TYPE2 } { TYPE3 TYPE4 } : CLASS1 TYPE1 "FILENAME"; type_member TYPE1 TYPE2 : CLASS1 TYPE2; type_change TYPE1 TYPE2 : CLASS1 TYPE3; range_transition TYPE1 TYPE2 : CLASS1 s1:c0.c1; allow TYPE1 self : CLASS1 { PERM1 }; auditallow { TYPE1 TYPE2 } TYPE3 : CLASS1 { PERM1 }; dontaudit TYPE1 { TYPE2 TYPE3 } : CLASS3 { PERM1 CPERM1 }; neverallow TYPE1 TYPE2 : { CLASS2 CLASS3 } { CPERM1 }; allowxperm TYPE1 TYPE2 : CLASS1 ioctl 0x1; auditallowxperm TYPE1 TYPE2 : CLASS1 ioctl 0x2; dontauditxperm TYPE1 TYPE2 : CLASS1 ioctl 0x3; neverallowxperm TYPE1 TYPE2 : CLASS1 ioctl 0x4; permissive TYPE1; attribute_role ROLE_ATTR1; role ROLE1; role ROLE3; role ROLE2, ROLE_ATTR1; role_transition ROLE1 TYPE1 ROLE2; role_transition ROLE1 TYPE1 : CLASS1 ROLE2; allow ROLE1 ROLE2; roleattribute ROLE3 ROLE_ATTR1; role ROLE1 types { TYPE1 }; if ! BOOL1 { allow TYPE1 self: CLASS1 *; } if TUNABLE1 xor TUNABLE2 { allow TYPE1 self: CLASS2 *; } else { allow TYPE1 self: CLASS3 *; } optional { require { class CLASS2 { CPERM1 }; } allow TYPE1 self: CLASS2 *; } user USER1 roles ROLE1 level s0 range s0 - s1:c0.c1; constrain CLASS1 { PERM1 } (u1 == u2 or (r1 == r2 and t1 == t2)); # sameuser will be turned into (u1 == u2) validatetrans CLASS2 sameuser and t3 == ATTR1; sid kernel USER1:ROLE1:TYPE1:s0 - s1:c0.c1 # fscon statements are not dumped fscon 2 3 USER1:ROLE1:TYPE1:s0 USER1:ROLE1:TYPE1:s0 fs_use_xattr btrfs USER1:ROLE1:TYPE1:s0 - s1:c0.CATALIAS; fs_use_trans devpts USER1:ROLE1:TYPE1:s0 - s0; fs_use_task pipefs USER1:ROLE1:TYPE1:s0 - s1; # paths will be turned into quoted strings genfscon proc / -d USER1:ROLE1:TYPE1:s0 genfscon proc "/file1" -- USER1:ROLE1:TYPE1:s0 genfscon proc "/path/to/file" USER1:ROLE1:TYPE1:s0 portcon tcp 80 USER1:ROLE1:TYPE1:s0 portcon udp 100-200 USER1:ROLE1:TYPE1:s0 netifcon lo USER1:ROLE1:TYPE1:s0 USER1:ROLE1:TYPE1:s0 nodecon 127.0.0.1 255.255.255.255 USER1:ROLE1:TYPE1:s0 nodecon ::ffff:127.0.0.1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff USER1:ROLE1:TYPE1:s0 # hex numbers will be turned in decimal ones ibpkeycon fe80:: 0xFFFF USER1:ROLE1:TYPE1:s0 ibpkeycon fe80:: 0-0x10 USER1:ROLE1:TYPE1:s0 ibendportcon mlx4_0 2 USER1:ROLE1:TYPE1:s0 ibendportcon mlx5_0 1 USER1:ROLE1:TYPE1:s0 checkpolicy-3.8.1/tests/policy_allonce_mls.expected.conf000066400000000000000000000055741476211737200235250ustar00rootroot00000000000000# handle_unknown deny class CLASS1 class CLASS2 class CLASS3 class dir class file class process sid kernel common COMMON1 { CPERM1 } class CLASS1 { PERM1 ioctl } class CLASS2 inherits COMMON1 class CLASS3 inherits COMMON1 { PERM1 } default_user { CLASS1 } source; default_role { CLASS2 } target; default_type { CLASS3 } source; sensitivity s0; sensitivity s1; sensitivity s2 alias SENSALIAS; dominance { s0 s1 s2 } category c0; category c1 alias CATALIAS; level s0:c0; level s1:c0,c1; level s2; mlsconstrain CLASS1 { PERM1 } l1 == l2; mlsvalidatetrans CLASS1 (r1 domby r2 and l1 incomp h2); policycap open_perms; attribute ATTR1; attribute ATTR2; bool BOOL1 true; type TYPE1; type TYPE2; type TYPE3; type TYPE4; typealias TYPE1 alias TYPEALIAS1; typealias TYPE3 alias TYPEALIAS3A; typealias TYPE3 alias TYPEALIAS3B; typealias TYPE4 alias TYPEALIAS4; typebounds TYPE4 TYPE3; typeattribute TYPE4 ATTR2; permissive TYPE1; allow TYPE1 self:CLASS1 { PERM1 }; allow TYPE1 self:CLASS2 { CPERM1 }; auditallow TYPE1 TYPE3:CLASS1 { PERM1 }; auditallow TYPE2 TYPE3:CLASS1 { PERM1 }; dontaudit TYPE1 TYPE2:CLASS3 { CPERM1 PERM1 }; dontaudit TYPE1 TYPE3:CLASS3 { CPERM1 PERM1 }; allowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x1 }; auditallowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x2 }; dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x3 }; type_transition TYPE1 TYPE2:CLASS1 TYPE3; type_member TYPE1 TYPE2:CLASS1 TYPE2; type_change TYPE1 TYPE2:CLASS1 TYPE3; type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME"; type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME"; type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME"; type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; range_transition TYPE1 TYPE2:CLASS1 s1:c0,c1 - s1:c0,c1; if (BOOL1) { } else { allow TYPE1 self:CLASS1 { PERM1 ioctl }; } role ROLE1; role ROLE2; role ROLE3; role ROLE1 types { TYPE1 }; role_transition ROLE1 TYPE1:CLASS1 ROLE2; role_transition ROLE1 TYPE1:process ROLE2; allow ROLE1 ROLE2; user USER1 roles ROLE1 level s0 range s0 - s1:c0,c1; constrain CLASS1 { PERM1 } (u1 == u2 or (r1 == r2 and t1 == t2)); validatetrans CLASS2 (u1 == u2 and t3 == ATTR1); sid kernel USER1:ROLE1:TYPE1:s0 - s1:c0,c1 fs_use_xattr btrfs USER1:ROLE1:TYPE1:s0 - s1:c0,c1; fs_use_trans devpts USER1:ROLE1:TYPE1:s0 - s0; fs_use_task pipefs USER1:ROLE1:TYPE1:s0 - s1; genfscon proc "/" -d USER1:ROLE1:TYPE1:s0 - s0 genfscon proc "/file1" -- USER1:ROLE1:TYPE1:s0 - s0 genfscon proc "/path/to/file" USER1:ROLE1:TYPE1:s0 - s0 portcon tcp 80 USER1:ROLE1:TYPE1:s0 - s0 portcon udp 100-200 USER1:ROLE1:TYPE1:s0 - s0 netifcon lo USER1:ROLE1:TYPE1:s0 - s0 USER1:ROLE1:TYPE1:s0 - s0 nodecon 127.0.0.1 255.255.255.255 USER1:ROLE1:TYPE1:s0 - s0 nodecon ::ffff:127.0.0.1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff USER1:ROLE1:TYPE1:s0 - s0 ibpkeycon fe80:: 65535 USER1:ROLE1:TYPE1:s0 - s0 ibpkeycon fe80:: 0-16 USER1:ROLE1:TYPE1:s0 - s0 ibendportcon mlx4_0 2 USER1:ROLE1:TYPE1:s0 - s0 ibendportcon mlx5_0 1 USER1:ROLE1:TYPE1:s0 - s0 checkpolicy-3.8.1/tests/policy_allonce_mls.expected_opt.conf000066400000000000000000000055661476211737200244100ustar00rootroot00000000000000# handle_unknown deny class CLASS1 class CLASS2 class CLASS3 class dir class file class process sid kernel common COMMON1 { CPERM1 } class CLASS1 { PERM1 ioctl } class CLASS2 inherits COMMON1 class CLASS3 inherits COMMON1 { PERM1 } default_user { CLASS1 } source; default_role { CLASS2 } target; default_type { CLASS3 } source; sensitivity s0; sensitivity s1; sensitivity s2 alias SENSALIAS; dominance { s0 s1 s2 } category c0; category c1 alias CATALIAS; level s0:c0; level s1:c0,c1; level s2; mlsconstrain CLASS1 { PERM1 } l1 == l2; mlsvalidatetrans CLASS1 (r1 domby r2 and l1 incomp h2); policycap open_perms; attribute ATTR1; attribute ATTR2; bool BOOL1 true; type TYPE1; type TYPE2; type TYPE3; type TYPE4; typealias TYPE1 alias TYPEALIAS1; typealias TYPE3 alias TYPEALIAS3A; typealias TYPE3 alias TYPEALIAS3B; typealias TYPE4 alias TYPEALIAS4; typebounds TYPE4 TYPE3; typeattribute TYPE4 ATTR2; permissive TYPE1; allow TYPE1 self:CLASS1 { PERM1 }; allow TYPE1 self:CLASS2 { CPERM1 }; auditallow TYPE1 TYPE3:CLASS1 { PERM1 }; auditallow TYPE2 TYPE3:CLASS1 { PERM1 }; dontaudit TYPE1 TYPE2:CLASS3 { CPERM1 PERM1 }; dontaudit TYPE1 TYPE3:CLASS3 { CPERM1 PERM1 }; allowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x1 }; auditallowxperm TYPE1 TYPE2:CLASS1 ioctl { 0x2 }; dontauditxperm TYPE1 TYPE2:CLASS1 ioctl { 0x3 }; type_transition TYPE1 TYPE2:CLASS1 TYPE3; type_member TYPE1 TYPE2:CLASS1 TYPE2; type_change TYPE1 TYPE2:CLASS1 TYPE3; type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME"; type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME"; type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME"; type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; range_transition TYPE1 TYPE2:CLASS1 s1:c0,c1 - s1:c0,c1; if (BOOL1) { } else { allow TYPE1 self:CLASS1 { ioctl }; } role ROLE1; role ROLE2; role ROLE3; role ROLE1 types { TYPE1 }; role_transition ROLE1 TYPE1:CLASS1 ROLE2; role_transition ROLE1 TYPE1:process ROLE2; allow ROLE1 ROLE2; user USER1 roles ROLE1 level s0 range s0 - s1:c0,c1; constrain CLASS1 { PERM1 } (u1 == u2 or (r1 == r2 and t1 == t2)); validatetrans CLASS2 (u1 == u2 and t3 == ATTR1); sid kernel USER1:ROLE1:TYPE1:s0 - s1:c0,c1 fs_use_xattr btrfs USER1:ROLE1:TYPE1:s0 - s1:c0,c1; fs_use_trans devpts USER1:ROLE1:TYPE1:s0 - s0; fs_use_task pipefs USER1:ROLE1:TYPE1:s0 - s1; genfscon proc "/" -d USER1:ROLE1:TYPE1:s0 - s0 genfscon proc "/file1" -- USER1:ROLE1:TYPE1:s0 - s0 genfscon proc "/path/to/file" USER1:ROLE1:TYPE1:s0 - s0 portcon tcp 80 USER1:ROLE1:TYPE1:s0 - s0 portcon udp 100-200 USER1:ROLE1:TYPE1:s0 - s0 netifcon lo USER1:ROLE1:TYPE1:s0 - s0 USER1:ROLE1:TYPE1:s0 - s0 nodecon 127.0.0.1 255.255.255.255 USER1:ROLE1:TYPE1:s0 - s0 nodecon ::ffff:127.0.0.1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff USER1:ROLE1:TYPE1:s0 - s0 ibpkeycon fe80:: 65535 USER1:ROLE1:TYPE1:s0 - s0 ibpkeycon fe80:: 0-16 USER1:ROLE1:TYPE1:s0 - s0 ibendportcon mlx4_0 2 USER1:ROLE1:TYPE1:s0 - s0 ibendportcon mlx5_0 1 USER1:ROLE1:TYPE1:s0 - s0 checkpolicy-3.8.1/tests/policy_allonce_xen.conf000066400000000000000000000036651476211737200217230ustar00rootroot00000000000000# handle_unknown deny class CLASS1 class CLASS2 class CLASS3 class dir class file class process sid kernel common COMMON1 { CPERM1 } class CLASS1 { PERM1 } class CLASS2 inherits COMMON1 class CLASS3 inherits COMMON1 { PERM1 } default_user { CLASS1 } source; default_role { CLASS2 } target; default_type { CLASS3 } source; attribute ATTR1; attribute ATTR2; expandattribute ATTR1 true; expandattribute ATTR2 false; type TYPE1; type TYPE2, ATTR1; type TYPE3 alias { TYPEALIAS3A TYPEALIAS3B }; type TYPE4 alias TYPEALIAS4, ATTR2; typealias TYPE1 alias TYPEALIAS1; typeattribute TYPE1 ATTR1; typebounds TYPE4 TYPE3; bool BOOL1 true; tunable TUNABLE1 false; tunable TUNABLE2 true; type_transition TYPE1 TYPE2 : CLASS1 TYPE3; type_transition { TYPE1 TYPE2 } { TYPE3 TYPE4 } : CLASS1 TYPE1 "FILENAME"; type_member TYPE1 TYPE2 : CLASS1 TYPE2; type_change TYPE1 TYPE2 : CLASS1 TYPE3; allow TYPE1 self : CLASS1 { PERM1 }; auditallow { TYPE1 TYPE2 } TYPE3 : CLASS1 { PERM1 }; dontaudit TYPE1 { TYPE2 TYPE3 } : CLASS3 { PERM1 CPERM1 }; neverallow TYPE1 TYPE2 : { CLASS2 CLASS3 } { CPERM1 }; permissive TYPE1; attribute_role ROLE_ATTR1; role ROLE1; role ROLE3; role ROLE2, ROLE_ATTR1; role_transition ROLE1 TYPE1 ROLE2; role_transition ROLE1 TYPE1 : CLASS1 ROLE2; allow ROLE1 ROLE2; roleattribute ROLE3 ROLE_ATTR1; role ROLE1 types { TYPE1 }; if ! BOOL1 { allow TYPE1 self: CLASS1 *; } if TUNABLE1 xor TUNABLE2 { allow TYPE1 self: CLASS2 *; } else { allow TYPE1 self: CLASS3 *; } optional { require { class CLASS2 { CPERM1 }; } allow TYPE1 self: CLASS2 *; } policycap open_perms; user USER1 roles ROLE1; constrain CLASS1 { PERM1 } (u1 == u2 or (r1 == r2 and t1 == t2)); validatetrans CLASS2 sameuser and t3 == ATTR1; sid kernel USER1:ROLE1:TYPE1 pirqcon 13 USER1:ROLE1:TYPE1 iomemcon 13 USER1:ROLE1:TYPE1 iomemcon 23-31 USER1:ROLE1:TYPE1 ioportcon 13 USER1:ROLE1:TYPE1 ioportcon 23-31 USER1:ROLE1:TYPE1 pcidevicecon 13 USER1:ROLE1:TYPE1 devicetreecon "/path/to/device" USER1:ROLE1:TYPE1 checkpolicy-3.8.1/tests/policy_allonce_xen.expected.conf000066400000000000000000000035201476211737200235110ustar00rootroot00000000000000# handle_unknown deny class CLASS1 class CLASS2 class CLASS3 class dir class file class process sid xen common COMMON1 { CPERM1 } class CLASS1 { PERM1 } class CLASS2 inherits COMMON1 class CLASS3 inherits COMMON1 { PERM1 } default_user { CLASS1 } source; default_role { CLASS2 } target; default_type { CLASS3 } source; policycap open_perms; attribute ATTR1; attribute ATTR2; bool BOOL1 true; type TYPE1; type TYPE2; type TYPE3; type TYPE4; typealias TYPE1 alias TYPEALIAS1; typealias TYPE3 alias TYPEALIAS3A; typealias TYPE3 alias TYPEALIAS3B; typealias TYPE4 alias TYPEALIAS4; typebounds TYPE4 TYPE3; typeattribute TYPE4 ATTR2; permissive TYPE1; allow TYPE1 self:CLASS1 { PERM1 }; allow TYPE1 self:CLASS2 { CPERM1 }; auditallow TYPE1 TYPE3:CLASS1 { PERM1 }; auditallow TYPE2 TYPE3:CLASS1 { PERM1 }; dontaudit TYPE1 TYPE2:CLASS3 { CPERM1 PERM1 }; dontaudit TYPE1 TYPE3:CLASS3 { CPERM1 PERM1 }; type_transition TYPE1 TYPE2:CLASS1 TYPE3; type_member TYPE1 TYPE2:CLASS1 TYPE2; type_change TYPE1 TYPE2:CLASS1 TYPE3; type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME"; type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME"; type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME"; type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; if (BOOL1) { } else { allow TYPE1 self:CLASS1 { PERM1 }; } role ROLE1; role ROLE2; role ROLE3; role ROLE1 types { TYPE1 }; role_transition ROLE1 TYPE1:CLASS1 ROLE2; role_transition ROLE1 TYPE1:process ROLE2; allow ROLE1 ROLE2; user USER1 roles ROLE1; constrain CLASS1 { PERM1 } (u1 == u2 or (r1 == r2 and t1 == t2)); validatetrans CLASS2 (u1 == u2 and t3 == ATTR1); sid xen USER1:ROLE1:TYPE1 pirqcon 13 USER1:ROLE1:TYPE1 iomemcon 0xd USER1:ROLE1:TYPE1 iomemcon 0x17-0x1f USER1:ROLE1:TYPE1 ioportcon 0xd USER1:ROLE1:TYPE1 ioportcon 0x17-0x1f USER1:ROLE1:TYPE1 pcidevicecon 0xd USER1:ROLE1:TYPE1 devicetreecon "/path/to/device" USER1:ROLE1:TYPE1 checkpolicy-3.8.1/tests/policy_allonce_xen.expected_opt.conf000066400000000000000000000034211476211737200243730ustar00rootroot00000000000000# handle_unknown deny class CLASS1 class CLASS2 class CLASS3 class dir class file class process sid xen common COMMON1 { CPERM1 } class CLASS1 { PERM1 } class CLASS2 inherits COMMON1 class CLASS3 inherits COMMON1 { PERM1 } default_user { CLASS1 } source; default_role { CLASS2 } target; default_type { CLASS3 } source; policycap open_perms; attribute ATTR1; attribute ATTR2; bool BOOL1 true; type TYPE1; type TYPE2; type TYPE3; type TYPE4; typealias TYPE1 alias TYPEALIAS1; typealias TYPE3 alias TYPEALIAS3A; typealias TYPE3 alias TYPEALIAS3B; typealias TYPE4 alias TYPEALIAS4; typebounds TYPE4 TYPE3; typeattribute TYPE4 ATTR2; permissive TYPE1; allow TYPE1 self:CLASS1 { PERM1 }; allow TYPE1 self:CLASS2 { CPERM1 }; auditallow TYPE1 TYPE3:CLASS1 { PERM1 }; auditallow TYPE2 TYPE3:CLASS1 { PERM1 }; dontaudit TYPE1 TYPE2:CLASS3 { CPERM1 PERM1 }; dontaudit TYPE1 TYPE3:CLASS3 { CPERM1 PERM1 }; type_transition TYPE1 TYPE2:CLASS1 TYPE3; type_member TYPE1 TYPE2:CLASS1 TYPE2; type_change TYPE1 TYPE2:CLASS1 TYPE3; type_transition TYPE1 TYPE3:CLASS1 TYPE1 "FILENAME"; type_transition TYPE1 TYPE4:CLASS1 TYPE1 "FILENAME"; type_transition TYPE2 TYPE3:CLASS1 TYPE1 "FILENAME"; type_transition TYPE2 TYPE4:CLASS1 TYPE1 "FILENAME"; role ROLE1; role ROLE2; role ROLE3; role ROLE1 types { TYPE1 }; role_transition ROLE1 TYPE1:CLASS1 ROLE2; role_transition ROLE1 TYPE1:process ROLE2; allow ROLE1 ROLE2; user USER1 roles ROLE1; constrain CLASS1 { PERM1 } (u1 == u2 or (r1 == r2 and t1 == t2)); validatetrans CLASS2 (u1 == u2 and t3 == ATTR1); sid xen USER1:ROLE1:TYPE1 pirqcon 13 USER1:ROLE1:TYPE1 iomemcon 0xd USER1:ROLE1:TYPE1 iomemcon 0x17-0x1f USER1:ROLE1:TYPE1 ioportcon 0xd USER1:ROLE1:TYPE1 ioportcon 0x17-0x1f USER1:ROLE1:TYPE1 pcidevicecon 0xd USER1:ROLE1:TYPE1 devicetreecon "/path/to/device" USER1:ROLE1:TYPE1 checkpolicy-3.8.1/tests/policy_minimal.conf000066400000000000000000000003211476211737200210440ustar00rootroot00000000000000# handle_unknown deny class CLASS1 sid kernel class CLASS1 { PERM1 } type TYPE1; allow TYPE1 self:CLASS1 { PERM1 }; role ROLE1; role ROLE1 types { TYPE1 }; user USER1 roles ROLE1; sid kernel USER1:ROLE1:TYPE1 checkpolicy-3.8.1/tests/policy_minimal_mls.conf000066400000000000000000000005261476211737200217260ustar00rootroot00000000000000# handle_unknown deny class CLASS1 sid kernel class CLASS1 { PERM1 } sensitivity s0; dominance { s0 } category c0; level s0:c0; mlsconstrain CLASS1 { PERM1 } l1 == l2; type TYPE1; allow TYPE1 self:CLASS1 { PERM1 }; role ROLE1; role ROLE1 types { TYPE1 }; user USER1 roles ROLE1 level s0 range s0 - s0:c0; sid kernel USER1:ROLE1:TYPE1:s0 - s0 checkpolicy-3.8.1/tests/test_roundtrip.sh000077500000000000000000000030011476211737200206120ustar00rootroot00000000000000#!/bin/sh set -eu BASEDIR=$(dirname "$0") CHECKPOLICY="${BASEDIR}/../checkpolicy" check_policy() { POLICY=$1 EXPECTED=$2 OPTS=$3 echo "==== Testing ${1}" ${CHECKPOLICY} ${OPTS} "${BASEDIR}/${POLICY}" -o "${BASEDIR}/testpol.bin" ${CHECKPOLICY} ${OPTS} -b -F "${BASEDIR}/testpol.bin" -o "${BASEDIR}/testpol.conf" diff -u "${BASEDIR}/${EXPECTED}" "${BASEDIR}/testpol.conf" ${CHECKPOLICY} ${OPTS} "${BASEDIR}/${EXPECTED}" -o "${BASEDIR}/testpol.bin" ${CHECKPOLICY} ${OPTS} -b -F "${BASEDIR}/testpol.bin" -o "${BASEDIR}/testpol.conf" diff -u "${BASEDIR}/${EXPECTED}" "${BASEDIR}/testpol.conf" echo "==== ${1} success" echo "" } check_policy policy_minimal.conf policy_minimal.conf '-E' check_policy policy_minimal.conf policy_minimal.conf '-E -S -O' check_policy policy_minimal_mls.conf policy_minimal_mls.conf '-M -E' check_policy policy_minimal_mls.conf policy_minimal_mls.conf '-M -E -S -O' check_policy policy_allonce.conf policy_allonce.expected.conf '' check_policy policy_allonce.conf policy_allonce.expected_opt.conf '-S -O' check_policy policy_allonce_mls.conf policy_allonce_mls.expected.conf '-M' check_policy policy_allonce_mls.conf policy_allonce_mls.expected_opt.conf '-M -S -O' check_policy policy_allonce_xen.conf policy_allonce_xen.expected.conf '--target xen -c 30 -E' check_policy policy_allonce_xen.conf policy_allonce_xen.expected_opt.conf '--target xen -c 30 -E -S -O'