pax_global_header00006660000000000000000000000064141342746730014525gustar00rootroot0000000000000052 comment=7f600c40bc18d8180993edcd54daf45124736776 checkpolicy-3.3/000077500000000000000000000000001413427467300136675ustar00rootroot00000000000000checkpolicy-3.3/.gitignore000066400000000000000000000000611413427467300156540ustar00rootroot00000000000000checkmodule checkpolicy lex.yy.c y.tab.c y.tab.h checkpolicy-3.3/COPYING000066400000000000000000000431311413427467300147240ustar00rootroot00000000000000 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.3/Makefile000066400000000000000000000037161413427467300153360ustar00rootroot00000000000000# # Makefile for building the checkpolicy program # LINGUAS ?= ru 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) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LDLIBS_LIBSEPOLA) checkmodule: $(CHECKMODOBJS) $(LIBSEPOLA) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LDLIBS_LIBSEPOLA) %.o: %.c $(CC) $(CFLAGS) -o $@ -c $< y.tab.o: y.tab.c $(CC) $(filter-out -Werror, $(CFLAGS)) -o $@ -c $< lex.yy.o: lex.yy.c $(CC) $(filter-out -Werror, $(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 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 $(MAKE) -C test clean indent: ../scripts/Lindent $(filter-out $(GENERATED),$(wildcard *.[ch])) checkpolicy-3.3/VERSION000066400000000000000000000000041413427467300147310ustar00rootroot000000000000003.3 checkpolicy-3.3/checkmodule.8000066400000000000000000000044231413427467300162460ustar00rootroot00000000000000.TH CHECKMODULE 8 .SH NAME checkmodule \- SELinux policy module compiler .SH SYNOPSIS .B checkmodule .I "[\-h] [\-b] [\-c policy_version] [\-C] [\-E] [\-m] [\-M] [\-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 \-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 . The program was written by Stephen Smalley . checkpolicy-3.3/checkmodule.c000066400000000000000000000217131413427467300163220ustar00rootroot00000000000000/* * 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 "checkpolicy.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"; unsigned int policy_type = POLICY_BASE; unsigned int policyvers = MOD_POLICYDB_VERSION_MAX; 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) { 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] [-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(" -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; 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'}, {"cil", no_argument, NULL, 'C'}, {"werror", no_argument, NULL, 'E'}, {NULL, 0, NULL, 0} }; while ((ch = getopt_long(argc, argv, "ho:bVEU:mMCc:", 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 '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; 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, 0, 1)) { 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) != 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.3/checkpolicy.8000066400000000000000000000043371413427467300162640ustar00rootroot00000000000000.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] [\-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 \-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 "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.3/checkpolicy.c000066400000000000000000000744111413427467300163370ustar00rootroot00000000000000 /* * 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 "checkpolicy.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"; unsigned int policyvers = 0; static __attribute__((__noreturn__)) void usage(const char *progname) { printf ("usage: %s [-b[F]] [-C] [-d] [-U handle_unknown (allow,deny,reject)] [-M] " "[-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->bool - 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 *bool; bool = hashtab_search(policydbp->p_bools.table, name); if (bool == NULL) { printf("Could not find bool %s\n", name); return -1; } bool->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->defined) { 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; struct val_to_name v; int ret, ch, fd, target = SEPOL_TARGET_SELINUX; 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'}, {"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:MCFSVc: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 '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; 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, 0, 1)) { fprintf(stderr, "Error while expanding policy\n"); exit(1); } policydb_destroy(policydbp); policydbp = &policydb; } policydbp->policyvers = policyvers ? policyvers : POLICYDB_VERSION_MAX; } 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 = malloc((strlen(ans) + 1) * sizeof(char)); if (name == NULL) { fprintf(stderr, "couldn't malloc string.\n"); break; } strcpy(name, ans); 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 = malloc((strlen(ans) + 1) * sizeof(char)); if (!name) { fprintf(stderr, "couldn't malloc string.\n"); break; } strcpy(name, ans); 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.3/checkpolicy.h000066400000000000000000000001311413427467300163300ustar00rootroot00000000000000#ifndef _CHECKPOLICY_H_ #define _CHECKPOLICY_H_ extern unsigned int policyvers; #endif checkpolicy-3.3/module_compiler.c000066400000000000000000001044551413427467300172230ustar00rootroot00000000000000/* 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" union stack_item_u { avrule_block_t *avrule; cond_list_t *cond_list; }; typedef struct scope_stack { union stack_item_u u; int type; /* for above union: 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, *child; } 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: yyerror("Unknown error"); } } 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.\n"); 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; } 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.\n"); 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, 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); 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]); 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); } datum->s.value = value; if (ret == 0) { *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; } *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); 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]); 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); } datum->s.value = value; if (ret == 0) { *user = datum; *key = strdup(id); if (*key == NULL) { yyerror("Out of memory!"); return -1; } } else if (ret == 1) { *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; } 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:{ s->u.avrule = va_arg(ap, avrule_block_t *); s->decl = va_arg(ap, avrule_decl_t *); break; } case 2:{ s->u.cond_list = va_arg(ap, cond_list_t *); break; } default: /* invalid stack type given */ assert(0); } va_end(ap); s->parent = stack_top; s->child = NULL; 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; if (parent != NULL) { parent->child = NULL; } free(stack_top); stack_top = parent; } checkpolicy-3.3/module_compiler.h000066400000000000000000000106361413427467300172250ustar00rootroot00000000000000/* 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, 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); #endif checkpolicy-3.3/parse_util.c000066400000000000000000000040431413427467300162030ustar00rootroot00000000000000/* * 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); extern int yyparse(void); extern void yyrestart(FILE *); extern queue_t id_queue; extern unsigned int policydb_errors; extern policydb_t *policydbp; extern int mlspol; extern void set_source_file(const char *name); int read_source_policy(policydb_t * p, const char *file, const char *progname) { yyin = fopen(file, "r"); if (!yyin) { fprintf(stderr, "%s: unable to open %s: %s\n", progname, file, strerror(errno)); return -1; } set_source_file(file); if ((id_queue = queue_create()) == NULL) { fprintf(stderr, "%s: out of memory!\n", progname); return -1; } policydbp = p; mlspol = p->mls; init_parser(1); if (yyparse() || policydb_errors) { fprintf(stderr, "%s: error(s) encountered while parsing configuration\n", progname); return -1; } rewind(yyin); init_parser(2); set_source_file(file); yyrestart(yyin); if (yyparse() || policydb_errors) { fprintf(stderr, "%s: error(s) encountered while parsing configuration\n", progname); return -1; } queue_destroy(id_queue); fclose(yyin); return 0; } checkpolicy-3.3/parse_util.h000066400000000000000000000025251413427467300162130ustar00rootroot00000000000000/* * 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.3/policy_define.c000066400000000000000000003501501413427467300166500ustar00rootroot00000000000000/* * 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 #include "queue.h" #include "checkpolicy.h" #include "module_compiler.h" #include "policy_define.h" extern void init_parser(int pass_number); __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 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) { policydb_lineno = 1; source_lineno = 1; policydb_errors = 0; pass = pass_number; } 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); } 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 = (char *)malloc(strlen(id) + 1); if (!newid) { yyerror("out of memory"); return -1; } strcpy(newid, id); 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 = 0; class_datum_t *datum = 0; int ret; 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 bad; } 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 bad; } case -2:{ yyerror2("duplicate declaration of class %s", id); goto bad; } case -1:{ yyerror("could not declare class here"); goto bad; } case 0: case 1:{ break; } default:{ assert(0); /* should never get here */ } } datum->s.value = value; return 0; bad: if (id) free(id); if (datum) free(datum); return -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\n", 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); 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); return -1; } cladatum = hashtab_search(policydbp->p_classes.table, id); if (!cladatum) { yyerror2("unknown class %s", id); return -1; } if (cladatum->default_user && cladatum->default_user != which) { yyerror2("conflicting default user information for class %s", 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); return -1; } cladatum = hashtab_search(policydbp->p_classes.table, id); if (!cladatum) { yyerror2("unknown class %s", id); return -1; } if (cladatum->default_role && cladatum->default_role != which) { yyerror2("conflicting default role information for class %s", 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); return -1; } cladatum = hashtab_search(policydbp->p_classes.table, id); if (!cladatum) { yyerror2("unknown class %s", id); return -1; } if (cladatum->default_type && cladatum->default_type != which) { yyerror2("conflicting default type information for class %s", 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); return -1; } cladatum = hashtab_search(policydbp->p_classes.table, id); if (!cladatum) { yyerror2("unknown class %s", id); return -1; } if (cladatum->default_range && cladatum->default_range != which) { yyerror2("conflicting default range information for class %s", 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\n", 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)) { yyerror ("too many permissions to fit in an access vector"); 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; } free(id); if (cladatum->comdatum || cladatum->permissions.nprim) { yyerror("duplicate access vector definition"); return -1; } 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)) { yyerror ("too many permissions to fit in an access vector"); 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)) { yyerror("sensitivity identifiers may not contain periods"); 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; ret = declare_symbol(SYM_LEVELS, id, datum, &value, &value); switch (ret) { case -3:{ yyerror("Out of memory!"); goto bad; } case -2:{ yyerror("duplicate declaration of sensitivity level"); goto bad; } case -1:{ yyerror("could not declare sensitivity level here"); goto bad; } case 0: case 1:{ break; } default:{ assert(0); /* should never get here */ } } while ((id = queue_remove(id_queue))) { if (id_has_dot(id)) { yyerror("sensitivity aliases may not contain periods"); goto bad_alias; } aliasdatum = (level_datum_t *) malloc(sizeof(level_datum_t)); if (!aliasdatum) { yyerror("out of memory"); goto bad_alias; } level_datum_init(aliasdatum); aliasdatum->isalias = TRUE; aliasdatum->level = level; ret = declare_symbol(SYM_LEVELS, id, aliasdatum, NULL, &value); switch (ret) { case -3:{ yyerror("Out of memory!"); goto bad_alias; } case -2:{ yyerror ("duplicate declaration of sensitivity alias"); goto bad_alias; } case -1:{ yyerror ("could not declare sensitivity alias here"); goto bad_alias; } case 0: case 1:{ 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)) { yyerror("category identifiers may not contain periods"); 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:{ yyerror("duplicate declaration of category"); goto bad; } case -1:{ yyerror("could not declare category here"); goto bad; } case 0: case 1:{ break; } default:{ assert(0); /* should never get here */ } } datum->s.value = value; while ((id = queue_remove(id_queue))) { if (id_has_dot(id)) { yyerror("category aliases may not contain periods"); goto bad_alias; } aliasdatum = (cat_datum_t *) malloc(sizeof(cat_datum_t)); if (!aliasdatum) { yyerror("out of memory"); goto bad_alias; } cat_datum_init(aliasdatum); aliasdatum->isalias = TRUE; aliasdatum->s.value = datum->s.value; ret = declare_symbol(SYM_CATS, id, aliasdatum, NULL, &datum->s.value); switch (ret) { case -3:{ yyerror("Out of memory!"); goto bad_alias; } case -2:{ yyerror ("duplicate declaration of category aliases"); goto bad_alias; } case -1:{ yyerror ("could not declare category aliases here"); goto bad_alias; } case 0: case 1:{ 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->level == level) { levdatum->defined = 1; if (!levdatum->isalias) 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; } 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); levdatum->defined = 1; while ((id = queue_remove(id_queue))) { cat_datum_t *cdatum; int 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 is invalid"); 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; 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) { attr = hashtab_search(policydbp->p_types.table, policydbp->sym_val_to_name[SYM_TYPES][i]); attr->flags |= flags; if ((attr->flags & TYPE_FLAGS_EXPAND_ATTR_TRUE) && (attr->flags & TYPE_FLAGS_EXPAND_ATTR_FALSE)) { yywarn("Expandattribute option was set to both true and false. " "Resolving to false."); 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)) { free(id); yyerror ("type alias identifiers may not contain periods"); 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:{ yyerror("could not declare alias here"); 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; 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(char *bounds_id, 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("hoge unknown type %s", 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 master {%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)) 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))) { char *bounds, *delim; if ((delim = strrchr(id, '.')) && (bounds = strdup(id))) { bounds[(size_t)(delim - id)] = '\0'; if (define_typebounds_helper(bounds, id)) return -1; free(bounds); } 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 (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)) { free(id); yyerror("boolean identifiers may not contain periods"); 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:{ yyerror("could not declare boolean here"); goto cleanup; } case 0: case 1:{ break; } 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 -1; } avrule_t *define_cond_pol_list(avrule_t * avlist, avrule_t * sl) { 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 */ sl->next = avlist; return sl; } typedef struct av_ioctl_range { uint16_t low; uint16_t high; } av_ioctl_range_t; struct av_ioctl_range_list { uint8_t omit; av_ioctl_range_t range; struct av_ioctl_range_list *next; }; static int avrule_sort_ioctls(struct av_ioctl_range_list **rangehead) { struct av_ioctl_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_ioctl_range_list)); if (sorted == NULL) goto error; memcpy(sorted, r, sizeof(struct av_ioctl_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 int avrule_merge_ioctls(struct av_ioctl_range_list **rangehead) { struct av_ioctl_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; } return 0; } static int avrule_read_ioctls(struct av_ioctl_range_list **rangehead) { char *id; struct av_ioctl_range_list *rnew, *r = NULL; uint8_t omit = 0; *rangehead = NULL; /* read in all the ioctl 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) { yyerror("Ioctl ranges must be in ascending order."); return -1; } free(id); } else { /* read in new low value */ rnew = malloc(sizeof(struct av_ioctl_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_ioctls(struct av_ioctl_range_list **rangehead) { struct av_ioctl_range_list *rnew, *r, *newhead, *r2; rnew = calloc(1, sizeof(struct av_ioctl_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_ioctl_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_ioctl_ranges(struct av_ioctl_range_list **rangelist) { struct av_ioctl_range_list *rangehead; uint8_t omit; /* read in ranges to include and omit */ if (avrule_read_ioctls(&rangehead)) return -1; if (rangehead == NULL) { yyerror("error processing ioctl commands"); return -1; } omit = rangehead->omit; /* sort and merge the input ioctls */ if (avrule_sort_ioctls(&rangehead)) return -1; if (avrule_merge_ioctls(&rangehead)) return -1; /* flip ranges if these are omitted */ if (omit) { if (avrule_omit_ioctls(&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) { yyerror("-self is not supported"); ret = -1; goto out; } avrule->flags |= RULE_SELF; continue; } if (set_types (&avrule->ttypes, id, &add, which == AVRULE_XPERMS_NEVERALLOW ? 1 : 0)) { ret = -1; goto out; } } 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_ioctl_partialdriver(struct av_ioctl_range_list *rangelist, av_extended_perms_t *complete_driver, av_extended_perms_t **extended_perms) { struct av_ioctl_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_ioctl_range_list *rangelist, av_extended_perms_t **extended_perms) { struct av_ioctl_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_ioctl_func(struct av_ioctl_range_list *rangelist, av_extended_perms_t **extended_perms, unsigned int driver) { struct av_ioctl_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 = AVRULE_XPERMS_IOCTLFUNCTION; 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)); class_perm_node_init(dest_perms); if (!dest_perms) { yyerror("out of memory"); return -1; } 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 *avrule; struct av_ioctl_range_list *rangelist, *r; av_extended_perms_t *complete_driver, *partial_driver, *xperms; unsigned int i; /* organize ioctl ranges */ if (avrule_ioctl_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; append_avrule(avrule); } /* flag ioctl driver codes that are partially enabled */ if (avrule_ioctl_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_ioctl_func(rangelist, &xperms, i)) 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; append_avrule(avrule); } } done: if (partial_driver) free(partial_driver); while (rangelist != NULL) { r = rangelist; rangelist = rangelist->next; free(r); } return 0; } 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); } else { yyerror("only ioctl extended permissions are supported"); rc = -1; } free(id); avrule_destroy(avrule_template); free(avrule_template); return rc; } 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; 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) { yyerror("-self is not supported"); ret = -1; goto out; } avrule->flags |= RULE_SELF; continue; } if (set_types (&avrule->ttypes, id, &add, which == AVRULE_NEVERALLOW ? 1 : 0)) { ret = -1; goto out; } } ebitmap_init(&tclasses); 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 permissions in the class */ cur_perms->data = ~0U; goto next; } if (strcmp(id, "~") == 0) { /* complement the set */ if (which == AVRULE_DONTAUDIT) yywarn("dontaudit rule with a ~?"); cur_perms->data = ~cur_perms->data; 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); } ebitmap_destroy(&tclasses); avrule->perms = perms; *rule = avrule; out: if (ret) { avrule_destroy(avrule); free(avrule); } 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; } role_datum_t *merge_roles_dom(role_datum_t * r1, role_datum_t * r2) { role_datum_t *new; if (pass == 1) { return (role_datum_t *) 1; /* any non-NULL value */ } new = malloc(sizeof(role_datum_t)); if (!new) { yyerror("out of memory"); return NULL; } memset(new, 0, sizeof(role_datum_t)); new->s.value = 0; /* temporary role */ if (ebitmap_or(&new->dominates, &r1->dominates, &r2->dominates)) { yyerror("out of memory"); free(new); return NULL; } if (ebitmap_or(&new->types.types, &r1->types.types, &r2->types.types)) { yyerror("out of memory"); free(new); return NULL; } if (!r1->s.value) { /* free intermediate result */ type_set_destroy(&r1->types); ebitmap_destroy(&r1->dominates); free(r1); } if (!r2->s.value) { /* free intermediate result */ yyerror("right hand role is temporary?"); type_set_destroy(&r2->types); ebitmap_destroy(&r2->dominates); free(r2); } return new; } /* This function eliminates the ordering dependency of role dominance rule */ static int dominate_role_recheck(hashtab_key_t key __attribute__ ((unused)), hashtab_datum_t datum, void *arg) { role_datum_t *rdp = (role_datum_t *) arg; role_datum_t *rdatum = (role_datum_t *) datum; ebitmap_node_t *node; uint32_t i; /* Don't bother to process against self role */ if (rdatum->s.value == rdp->s.value) return 0; /* If a dominating role found */ if (ebitmap_get_bit(&(rdatum->dominates), rdp->s.value - 1)) { ebitmap_t types; ebitmap_init(&types); if (type_set_expand(&rdp->types, &types, policydbp, 1)) { ebitmap_destroy(&types); return -1; } /* raise types and dominates from dominated role */ ebitmap_for_each_positive_bit(&rdp->dominates, node, i) { if (ebitmap_set_bit(&rdatum->dominates, i, TRUE)) goto oom; } ebitmap_for_each_positive_bit(&types, node, i) { if (ebitmap_set_bit(&rdatum->types.types, i, TRUE)) goto oom; } ebitmap_destroy(&types); } /* go through all the roles */ return 0; oom: yyerror("Out of memory"); return -1; } role_datum_t *define_role_dom(role_datum_t * r) { role_datum_t *role; char *role_id; ebitmap_node_t *node; unsigned int i; int ret; if (pass == 1) { role_id = queue_remove(id_queue); free(role_id); return (role_datum_t *) 1; /* any non-NULL value */ } yywarn("Role dominance has been deprecated"); role_id = queue_remove(id_queue); if (!is_id_in_scope(SYM_ROLES, role_id)) { yyerror2("role %s is not within scope", role_id); free(role_id); return NULL; } role = (role_datum_t *) hashtab_search(policydbp->p_roles.table, role_id); if (!role) { role = (role_datum_t *) malloc(sizeof(role_datum_t)); if (!role) { yyerror("out of memory"); free(role_id); return NULL; } memset(role, 0, sizeof(role_datum_t)); ret = declare_symbol(SYM_ROLES, (hashtab_key_t) role_id, (hashtab_datum_t) role, &role->s.value, &role->s.value); switch (ret) { case -3:{ yyerror("Out of memory!"); goto cleanup; } case -2:{ yyerror2("duplicate declaration of role %s", role_id); goto cleanup; } case -1:{ yyerror("could not declare role here"); goto cleanup; } case 0: case 1:{ break; } default:{ assert(0); /* should never get here */ } } if (ebitmap_set_bit(&role->dominates, role->s.value - 1, TRUE)) { yyerror("Out of memory!"); goto cleanup; } } if (r) { ebitmap_t types; ebitmap_init(&types); ebitmap_for_each_positive_bit(&r->dominates, node, i) { if (ebitmap_set_bit(&role->dominates, i, TRUE)) goto oom; } if (type_set_expand(&r->types, &types, policydbp, 1)) { ebitmap_destroy(&types); return NULL; } ebitmap_for_each_positive_bit(&types, node, i) { if (ebitmap_set_bit(&role->types.types, i, TRUE)) goto oom; } ebitmap_destroy(&types); if (!r->s.value) { /* free intermediate result */ type_set_destroy(&r->types); ebitmap_destroy(&r->dominates); free(r); } /* * Now go through all the roles and escalate this role's * dominates and types if a role dominates this role. */ hashtab_map(policydbp->p_roles.table, dominate_role_recheck, role); } return role; cleanup: free(role_id); role_datum_destroy(role); free(role); return NULL; oom: yyerror("Out of memory"); goto cleanup; } 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)) { free(ra); return -1; } } while ((id = queue_remove(id_queue))) { if (set_roles(&ra->new_roles, id)) { 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\n"); 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, 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; } add =1; while ((id = queue_remove(id_queue))) { 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; } } } /* 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; } 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; } depth = -1; for (e = expr; e; e = e->next) { switch (e->expr_type) { case CEXPR_NOT: if (depth < 0) { yyerror("illegal constraint expression"); return -1; } break; case CEXPR_AND: case CEXPR_OR: if (depth < 1) { yyerror("illegal constraint expression"); return -1; } depth--; break; case CEXPR_ATTR: case CEXPR_NAMES: if (e->attr & CEXPR_XTARGET) { yyerror("illegal constraint expression"); return -1; /* only for validatetrans rules */ } if (depth == (CEXPR_MAXDEPTH - 1)) { yyerror("constraint expression is too deep"); return -1; } depth++; break; default: yyerror("illegal constraint expression"); return -1; } } if (depth != 0) { yyerror("illegal constraint expression"); return -1; } ebitmap_init(&classmap); 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 = (class_datum_t *) hashtab_search(policydbp->p_classes.table, (hashtab_key_t) id); if (!cladatum) { yyerror2("class %s is not defined", id); ebitmap_destroy(&classmap); free(id); return -1; } if (ebitmap_set_bit(&classmap, cladatum->s.value - 1, TRUE)) { yyerror("out of memory"); ebitmap_destroy(&classmap); free(id); return -1; } node = malloc(sizeof(struct constraint_node)); if (!node) { yyerror("out of memory"); free(node); return -1; } 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); return -1; } 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; 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", id); free(id); ebitmap_destroy(&classmap); return -1; } } node->permissions |= (UINT32_C(1) << (perdatum->s.value - 1)); } free(id); } ebitmap_destroy(&classmap); return 0; } 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; } depth = -1; for (e = expr; e; e = e->next) { switch (e->expr_type) { case CEXPR_NOT: if (depth < 0) { yyerror("illegal validatetrans expression"); return -1; } break; case CEXPR_AND: case CEXPR_OR: if (depth < 1) { yyerror("illegal validatetrans expression"); return -1; } depth--; break; case CEXPR_ATTR: case CEXPR_NAMES: if (depth == (CEXPR_MAXDEPTH - 1)) { yyerror("validatetrans expression is too deep"); return -1; } depth++; break; default: yyerror("illegal validatetrans expression"); return -1; } } if (depth != 0) { yyerror("illegal validatetrans expression"); return -1; } ebitmap_init(&classmap); 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 = (class_datum_t *) hashtab_search(policydbp->p_classes.table, (hashtab_key_t) id); if (!cladatum) { yyerror2("class %s is not defined", id); ebitmap_destroy(&classmap); free(id); return -1; } if (ebitmap_set_bit(&classmap, (cladatum->s.value - 1), TRUE)) { yyerror("out of memory"); ebitmap_destroy(&classmap); free(id); return -1; } node = malloc(sizeof(struct constraint_node)); if (!node) { yyerror("out of memory"); return -1; } 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; } 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); 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); 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); 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); 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); return 0; } if (ebitmap_set_bit(&expr->names, val - 1, TRUE)) { yyerror("out of memory"); ebitmap_destroy(&expr->names); 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, avrule_t * f) { cond_expr_t *e; int depth; cond_node_t cn, *cn_old; /* expression cannot be NULL */ if (!expr) { yyerror("illegal conditional expression"); return -1; } if (!t) { if (!f) { /* empty is fine, destroy expression and return */ cond_expr_destroy(expr); return 0; } /* Invert */ t = f; f = 0; expr = define_cond_expr(COND_NOT, expr, 0); if (!expr) { yyerror("unable to invert"); return -1; } } /* verify expression */ depth = -1; 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++; break; default: yyerror("illegal conditional expression"); return -1; } } if (depth != 0) { yyerror("illegal conditional expression"); return -1; } /* use tmp conditional node to partially build new node */ memset(&cn, 0, sizeof(cn)); cn.expr = expr; cn.avtrue_list = t; cn.avfalse_list = f; /* 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->bool = 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; int 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 is invalid"); 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 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) { char *id; user_datum_t *usrdatum; 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; } if ((usrdatum = declare_user()) == NULL) { return -1; } while ((id = queue_remove(id_queue))) { if (set_user_roles(&usrdatum->roles, id)) continue; } 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))) { 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))) { 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; } } } 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_head(id_queue); if (!id) { yyerror("no sensitivity name for sid context" " definition?"); return -1; } id = (char *)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("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; char *id; if (policydbp->target_platform != SEPOL_TARGET_XEN) { yyerror("pirqcon 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)); 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; char *id; if (policydbp->target_platform != SEPOL_TARGET_XEN) { yyerror("iomemcon 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)); 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; char *id; if (policydbp->target_platform != SEPOL_TARGET_XEN) { yyerror("ioportcon 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)); 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; char *id; if (policydbp->target_platform != SEPOL_TARGET_XEN) { yyerror("pcidevicecon 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)); 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() { 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_addr[2] || subnet_prefix.s6_addr[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) { yyerror("Invalid ibendport port number, should be 0 < port < 256"); 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) { yyerror("infiniband device name exceeds max length of 63."); 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; } int define_ipv4_node_context() { char *id; int rc = 0; struct in_addr addr, mask; ocontext_t *newc, *c, *l, *head; 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); goto out; } id = queue_remove(id_queue); if (!id) { yyerror("failed to read ipv4 address"); rc = -1; goto out; } rc = inet_pton(AF_INET, id, &addr); free(id); if (rc < 1) { yyerror("failed to parse ipv4 address"); if (rc == 0) rc = -1; goto out; } id = queue_remove(id_queue); if (!id) { yyerror("failed to read ipv4 address"); rc = -1; goto out; } rc = inet_pton(AF_INET, id, &mask); free(id); if (rc < 1) { yyerror("failed to parse ipv4 mask"); if (rc == 0) rc = -1; goto out; } newc = malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); rc = -1; goto out; } 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; } /* Create order of most specific to least retaining the order specified in the configuration. */ head = policydbp->ocontexts[OCON_NODE]; for (l = NULL, c = head; c; l = c, c = c->next) { if (newc->u.node.mask > c->u.node.mask) break; } newc->next = c; if (l) l->next = newc; else policydbp->ocontexts[OCON_NODE] = newc; rc = 0; out: return rc; } int define_ipv6_node_context(void) { char *id; int rc = 0; struct in6_addr addr, mask; ocontext_t *newc, *c, *l, *head; 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); goto out; } id = queue_remove(id_queue); if (!id) { yyerror("failed to read ipv6 address"); rc = -1; goto out; } rc = inet_pton(AF_INET6, id, &addr); free(id); if (rc < 1) { yyerror("failed to parse ipv6 address"); if (rc == 0) rc = -1; goto out; } id = queue_remove(id_queue); if (!id) { yyerror("failed to read ipv6 address"); rc = -1; goto out; } rc = inet_pton(AF_INET6, id, &mask); free(id); if (rc < 1) { yyerror("failed to parse ipv6 mask"); if (rc == 0) rc = -1; goto out; } newc = malloc(sizeof(ocontext_t)); if (!newc) { yyerror("out of memory"); rc = -1; goto out; } 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); rc = -1; goto out; } /* Create order of most specific to least retaining the order specified in the configuration. */ head = policydbp->ocontexts[OCON_NODE6]; for (l = NULL, c = head; c; l = c, c = c->next) { 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; rc = 0; out: return rc; } 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; int 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.3/policy_define.h000066400000000000000000000055021413427467300166530ustar00rootroot00000000000000/* 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 *stmt); avrule_t *define_cond_te_avtab(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_ipv6_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); role_datum_t *define_role_dom(role_datum_t *r); role_datum_t *merge_roles_dom(role_datum_t *r1,role_datum_t *r2); uintptr_t define_cexpr(uint32_t expr_type, uintptr_t arg1, uintptr_t arg2); #endif /* _POLICY_DEFINE_H_ */ checkpolicy-3.3/policy_parse.y000066400000000000000000001033541413427467300165600ustar00rootroot00000000000000 /* * 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 "checkpolicy.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_transition_def cond_te_avtab_def cond_rule_def %type role_def roles %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 CLONE %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 IPV6_ADDR %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 %left OR %left XOR %left AND %right NOT %left EQUALS NOTEQUAL %% policy : base_policy | module_policy ; base_policy : { if (define_policy(pass, 0) == -1) return -1; } classes initial_sids access_vectors { if (pass == 1) { if (policydb_index_classes(policydbp)) return -1; } else if (pass == 2) { if (policydb_index_others(NULL, policydbp, 0)) return -1; }} opt_default_rules opt_mls te_rbac users opt_constraints { if (pass == 1) { if (policydb_index_bools(policydbp)) return -1;} else if (pass == 2) { if (policydb_index_others(NULL, policydbp, 0)) return -1;}} 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()) return -1;} ; initial_sids : initial_sid_def | initial_sids initial_sid_def ; initial_sid_def : SID identifier {if (define_initial_sid()) return -1;} ; 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()) return -1;} ; av_perms : av_perms_def | av_perms av_perms_def ; av_perms_def : CLASS identifier '{' identifier_list '}' {if (define_av_perms(FALSE)) return -1;} | CLASS identifier INHERITS identifier {if (define_av_perms(TRUE)) return -1;} | CLASS identifier INHERITS identifier '{' identifier_list '}' {if (define_av_perms(TRUE)) return -1;} ; 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)) return -1; } | DEFAULT_USER names TARGET ';' {if (define_default_user(DEFAULT_TARGET)) return -1; } ; default_role_def : DEFAULT_ROLE names SOURCE ';' {if (define_default_role(DEFAULT_SOURCE)) return -1; } | DEFAULT_ROLE names TARGET ';' {if (define_default_role(DEFAULT_TARGET)) return -1; } ; default_type_def : DEFAULT_TYPE names SOURCE ';' {if (define_default_type(DEFAULT_SOURCE)) return -1; } | DEFAULT_TYPE names TARGET ';' {if (define_default_type(DEFAULT_TARGET)) return -1; } ; default_range_def : DEFAULT_RANGE names SOURCE LOW ';' {if (define_default_range(DEFAULT_SOURCE_LOW)) return -1; } | DEFAULT_RANGE names SOURCE HIGH ';' {if (define_default_range(DEFAULT_SOURCE_HIGH)) return -1; } | DEFAULT_RANGE names SOURCE LOW_HIGH ';' {if (define_default_range(DEFAULT_SOURCE_LOW_HIGH)) return -1; } | DEFAULT_RANGE names TARGET LOW ';' {if (define_default_range(DEFAULT_TARGET_LOW)) return -1; } | DEFAULT_RANGE names TARGET HIGH ';' {if (define_default_range(DEFAULT_TARGET_HIGH)) return -1; } | DEFAULT_RANGE names TARGET LOW_HIGH ';' {if (define_default_range(DEFAULT_TARGET_LOW_HIGH)) return -1; } | DEFAULT_RANGE names GLBLUB';' {if (define_default_range(DEFAULT_GLBLUB)) return -1; } ; 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()) return -1;} | SENSITIVITY identifier ';' {if (define_sens()) return -1;} ; alias_def : ALIAS names ; dominance : DOMINANCE identifier {if (define_dominance()) return -1;} | DOMINANCE '{' identifier_list '}' {if (define_dominance()) return -1;} ; opt_categories : categories | ; categories : category_def | categories category_def ; category_def : CATEGORY identifier alias_def ';' {if (define_category()) return -1;} | CATEGORY identifier ';' {if (define_category()) return -1;} ; levels : level_def | levels level_def ; level_def : LEVEL identifier ':' id_comma_list ';' {if (define_level()) return -1;} | LEVEL identifier ';' {if (define_level()) return -1;} ; 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)) return -1; } ; mlsvalidatetrans_def : MLSVALIDATETRANS names cexpr ';' { if (define_validatetrans((constraint_expr_t*)$3)) return -1; } ; 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_dominance | 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()) return -1;} ; expandattribute_def : EXPANDATTRIBUTE names bool_val ';' { if (expand_attrib()) return -1;} ; type_def : TYPE identifier alias_def opt_attr_list ';' {if (define_type(1)) return -1;} | TYPE identifier opt_attr_list ';' {if (define_type(0)) return -1;} ; typealias_def : TYPEALIAS identifier alias_def ';' {if (define_typealias()) return -1;} ; typeattribute_def : TYPEATTRIBUTE identifier id_comma_list ';' {if (define_typeattribute()) return -1;} ; typebounds_def : TYPEBOUNDS identifier id_comma_list ';' {if (define_typebounds()) return -1;} ; opt_attr_list : ',' id_comma_list | ; bool_def : BOOL identifier bool_val ';' { if (define_bool_tunable(0)) return -1; } ; tunable_def : TUNABLE identifier bool_val ';' { if (define_bool_tunable(1)) return -1; } ; bool_val : CTRUE { if (insert_id("T",0)) return -1; } | CFALSE { if (insert_id("F",0)) return -1; } ; 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) return -1; }} ; 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) return -1; } | cond_expr AND cond_expr { $$ = define_cond_expr(COND_AND, $1, $3); if ($$ == 0) return -1; } | cond_expr OR cond_expr { $$ = define_cond_expr(COND_OR, $1, $3); if ($$ == 0) return -1; } | cond_expr XOR cond_expr { $$ = define_cond_expr(COND_XOR, $1, $3); if ($$ == 0) return -1; } | cond_expr EQUALS cond_expr { $$ = define_cond_expr(COND_EQ, $1, $3); if ($$ == 0) return -1; } | cond_expr NOTEQUAL cond_expr { $$ = define_cond_expr(COND_NEQ, $1, $3); if ($$ == 0) return -1; } | cond_expr_prim { $$ = $1; } ; cond_expr_prim : identifier { $$ = define_cond_expr(COND_BOOL,0, 0); if ($$ == COND_ERR) return -1; } ; 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) return -1;} | TYPE_TRANSITION names names ':' names identifier ';' { $$ = define_cond_compute_type(AVRULE_TRANSITION) ; if ($$ == COND_ERR) return -1;} | TYPE_MEMBER names names ':' names identifier ';' { $$ = define_cond_compute_type(AVRULE_MEMBER) ; if ($$ == COND_ERR) return -1;} | TYPE_CHANGE names names ':' names identifier ';' { $$ = define_cond_compute_type(AVRULE_CHANGE) ; if ($$ == COND_ERR) return -1;} ; cond_te_avtab_def : cond_allow_def { $$ = $1; } | cond_auditallow_def { $$ = $1; } | cond_auditdeny_def { $$ = $1; } | cond_dontaudit_def { $$ = $1; } ; cond_allow_def : ALLOW names names ':' names names ';' { $$ = define_cond_te_avtab(AVRULE_ALLOWED) ; if ($$ == COND_ERR) return -1; } ; cond_auditallow_def : AUDITALLOW names names ':' names names ';' { $$ = define_cond_te_avtab(AVRULE_AUDITALLOW) ; if ($$ == COND_ERR) return -1; } ; cond_auditdeny_def : AUDITDENY names names ':' names names ';' { $$ = define_cond_te_avtab(AVRULE_AUDITDENY) ; if ($$ == COND_ERR) return -1; } ; cond_dontaudit_def : DONTAUDIT names names ':' names names ';' { $$ = define_cond_te_avtab(AVRULE_DONTAUDIT); if ($$ == COND_ERR) return -1; } ; ; transition_def : TYPE_TRANSITION names names ':' names identifier filename ';' {if (define_filename_trans()) return -1; } | TYPE_TRANSITION names names ':' names identifier ';' {if (define_compute_type(AVRULE_TRANSITION)) return -1;} | TYPE_MEMBER names names ':' names identifier ';' {if (define_compute_type(AVRULE_MEMBER)) return -1;} | TYPE_CHANGE names names ':' names identifier ';' {if (define_compute_type(AVRULE_CHANGE)) return -1;} ; range_trans_def : RANGE_TRANSITION names names mls_range_def ';' { if (define_range_trans(0)) return -1; } | RANGE_TRANSITION names names ':' names mls_range_def ';' { if (define_range_trans(1)) return -1; } ; 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)) return -1; } ; auditallow_def : AUDITALLOW names names ':' names names ';' {if (define_te_avtab(AVRULE_AUDITALLOW)) return -1; } ; auditdeny_def : AUDITDENY names names ':' names names ';' {if (define_te_avtab(AVRULE_AUDITDENY)) return -1; } ; dontaudit_def : DONTAUDIT names names ':' names names ';' {if (define_te_avtab(AVRULE_DONTAUDIT)) return -1; } ; neverallow_def : NEVERALLOW names names ':' names names ';' {if (define_te_avtab(AVRULE_NEVERALLOW)) return -1; } ; xperm_allow_def : ALLOWXPERM names names ':' names identifier xperms ';' {if (define_te_avtab_extended_perms(AVRULE_XPERMS_ALLOWED)) return -1; } ; xperm_auditallow_def : AUDITALLOWXPERM names names ':' names identifier xperms ';' {if (define_te_avtab_extended_perms(AVRULE_XPERMS_AUDITALLOW)) return -1; } ; xperm_dontaudit_def : DONTAUDITXPERM names names ':' names identifier xperms ';' {if (define_te_avtab_extended_perms(AVRULE_XPERMS_DONTAUDIT)) return -1; } ; xperm_neverallow_def : NEVERALLOWXPERM names names ':' names identifier xperms ';' {if (define_te_avtab_extended_perms(AVRULE_XPERMS_NEVERALLOW)) return -1; } ; attribute_role_def : ATTRIBUTE_ROLE identifier ';' {if (define_attrib_role()) return -1; } ; role_type_def : ROLE identifier TYPES names ';' {if (define_role_types()) return -1;} ; role_attr_def : ROLE identifier opt_attr_list ';' {if (define_role_attr()) return -1;} ; role_dominance : DOMINANCE '{' roles '}' ; role_trans_def : ROLE_TRANSITION names names identifier ';' {if (define_role_trans(0)) return -1; } | ROLE_TRANSITION names names ':' names identifier ';' {if (define_role_trans(1)) return -1;} ; role_allow_def : ALLOW names names ';' {if (define_role_allow()) return -1; } ; roles : role_def { $$ = $1; } | roles role_def { $$ = merge_roles_dom((role_datum_t*)$1, (role_datum_t*)$2); if ($$ == 0) return -1;} ; role_def : ROLE identifier_push ';' {$$ = define_role_dom(NULL); if ($$ == 0) return -1;} | ROLE identifier_push '{' roles '}' {$$ = define_role_dom((role_datum_t*)$4); if ($$ == 0) return -1;} ; roleattribute_def : ROLEATTRIBUTE identifier id_comma_list ';' {if (define_roleattribute()) return -1;} ; 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)) return -1; } ; validatetrans_def : VALIDATETRANS names cexpr ';' { if (define_validatetrans((constraint_expr_t*)$3)) return -1; } ; cexpr : '(' cexpr ')' { $$ = $2; } | NOT cexpr { $$ = define_cexpr(CEXPR_NOT, $2, 0); if ($$ == 0) return -1; } | cexpr AND cexpr { $$ = define_cexpr(CEXPR_AND, $1, $3); if ($$ == 0) return -1; } | cexpr OR cexpr { $$ = define_cexpr(CEXPR_OR, $1, $3); if ($$ == 0) return -1; } | cexpr_prim { $$ = $1; } ; cexpr_prim : U1 op U2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_USER, $2); if ($$ == 0) return -1; } | R1 role_mls_op R2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_ROLE, $2); if ($$ == 0) return -1; } | T1 op T2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_TYPE, $2); if ($$ == 0) return -1; } | U1 op { if (insert_separator(1)) return -1; } names_push { $$ = define_cexpr(CEXPR_NAMES, CEXPR_USER, $2); if ($$ == 0) return -1; } | U2 op { if (insert_separator(1)) return -1; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_USER | CEXPR_TARGET), $2); if ($$ == 0) return -1; } | U3 op { if (insert_separator(1)) return -1; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_USER | CEXPR_XTARGET), $2); if ($$ == 0) return -1; } | R1 op { if (insert_separator(1)) return -1; } names_push { $$ = define_cexpr(CEXPR_NAMES, CEXPR_ROLE, $2); if ($$ == 0) return -1; } | R2 op { if (insert_separator(1)) return -1; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_ROLE | CEXPR_TARGET), $2); if ($$ == 0) return -1; } | R3 op { if (insert_separator(1)) return -1; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_ROLE | CEXPR_XTARGET), $2); if ($$ == 0) return -1; } | T1 op { if (insert_separator(1)) return -1; } names_push { $$ = define_cexpr(CEXPR_NAMES, CEXPR_TYPE, $2); if ($$ == 0) return -1; } | T2 op { if (insert_separator(1)) return -1; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_TYPE | CEXPR_TARGET), $2); if ($$ == 0) return -1; } | T3 op { if (insert_separator(1)) return -1; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_TYPE | CEXPR_XTARGET), $2); if ($$ == 0) return -1; } | SAMEUSER { $$ = define_cexpr(CEXPR_ATTR, CEXPR_USER, CEXPR_EQ); if ($$ == 0) return -1; } | SOURCE ROLE { if (insert_separator(1)) return -1; } names_push { $$ = define_cexpr(CEXPR_NAMES, CEXPR_ROLE, CEXPR_EQ); if ($$ == 0) return -1; } | TARGET ROLE { if (insert_separator(1)) return -1; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_ROLE | CEXPR_TARGET), CEXPR_EQ); if ($$ == 0) return -1; } | ROLE role_mls_op { $$ = define_cexpr(CEXPR_ATTR, CEXPR_ROLE, $2); if ($$ == 0) return -1; } | SOURCE TYPE { if (insert_separator(1)) return -1; } names_push { $$ = define_cexpr(CEXPR_NAMES, CEXPR_TYPE, CEXPR_EQ); if ($$ == 0) return -1; } | TARGET TYPE { if (insert_separator(1)) return -1; } names_push { $$ = define_cexpr(CEXPR_NAMES, (CEXPR_TYPE | CEXPR_TARGET), CEXPR_EQ); if ($$ == 0) return -1; } | L1 role_mls_op L2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_L1L2, $2); if ($$ == 0) return -1; } | L1 role_mls_op H2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_L1H2, $2); if ($$ == 0) return -1; } | H1 role_mls_op L2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_H1L2, $2); if ($$ == 0) return -1; } | H1 role_mls_op H2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_H1H2, $2); if ($$ == 0) return -1; } | L1 role_mls_op H1 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_L1H1, $2); if ($$ == 0) return -1; } | L2 role_mls_op H2 { $$ = define_cexpr(CEXPR_ATTR, CEXPR_L2H2, $2); if ($$ == 0) return -1; } ; 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()) return -1;} ; 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()) return -1;} ; 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)) return -1;} ; iomem_context_def : IOMEMCON number64 security_context_def {if (define_iomem_context($2,$2)) return -1;} | IOMEMCON number64 '-' number64 security_context_def {if (define_iomem_context($2,$4)) return -1;} ; ioport_context_def : IOPORTCON number security_context_def {if (define_ioport_context($2,$2)) return -1;} | IOPORTCON number '-' number security_context_def {if (define_ioport_context($2,$4)) return -1;} ; pci_context_def : PCIDEVICECON number security_context_def {if (define_pcidevice_context($2)) return -1;} ; dtree_context_def : DEVICETREECON path security_context_def {if (define_devicetree_context()) return -1;} ; 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)) return -1;} ; 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)) return -1;} | PORTCON identifier number '-' number security_context_def {if (define_port_context($3,$5)) return -1;} ; 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)) return -1;} | IBPKEYCON ipv6_addr number '-' number security_context_def {if (define_ibpkey_context($3,$5)) return -1;} ; 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)) return -1;} ; 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()) return -1;} ; 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()) return -1;} | NODECON ipv6_addr ipv6_addr security_context_def {if (define_ipv6_node_context()) return -1;} ; 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)) return -1;} | FSUSETASK identifier security_context_def ';' {if (define_fs_use(SECURITY_FS_USE_TASK)) return -1;} | FSUSETRANS identifier security_context_def ';' {if (define_fs_use(SECURITY_FS_USE_TRANS)) return -1;} ; 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)) return -1;} | GENFSCON filesystem path '-' '-' {insert_id("-", 0);} security_context_def {if (define_genfs_context(1)) return -1;} | GENFSCON filesystem path security_context_def {if (define_genfs_context(0)) return -1;} ; ipv4_addr_def : IPV4_ADDR { if (insert_id(yytext,0)) return -1; } ; xperms : xperm { if (insert_separator(0)) return -1; } | nested_xperm_set { if (insert_separator(0)) return -1; } | tilde xperm { if (insert_id("~", 0)) return -1; } | tilde nested_xperm_set { if (insert_id("~", 0)) return -1; if (insert_separator(0)) return -1; } ; 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)) return -1; } xperm | xperm | nested_xperm_set ; xperm : number { if (insert_id(yytext,0)) return -1; } ; 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)) return -1;} | mls_level_def {if (insert_separator(0)) return -1;} ; mls_level_def : identifier ':' id_comma_list {if (insert_separator(0)) return -1;} | identifier {if (insert_separator(0)) return -1;} ; id_comma_list : identifier | id_comma_list ',' identifier ; tilde : '~' ; asterisk : '*' ; names : identifier { if (insert_separator(0)) return -1; } | nested_id_set { if (insert_separator(0)) return -1; } | asterisk { if (insert_id("*", 0)) return -1; if (insert_separator(0)) return -1; } | tilde identifier { if (insert_id("~", 0)) return -1; if (insert_separator(0)) return -1; } | tilde nested_id_set { if (insert_id("~", 0)) return -1; if (insert_separator(0)) return -1; } | identifier '-' { if (insert_id("-", 0)) return -1; } identifier { if (insert_separator(0)) return -1; } ; tilde_push : tilde { if (insert_id("~", 1)) return -1; } ; asterisk_push : asterisk { if (insert_id("*", 1)) return -1; } ; 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)) return -1; } ; 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)) return -1; } identifier | nested_id_set ; identifier : IDENTIFIER { if (insert_id(yytext,0)) return -1; } ; filesystem : FILESYSTEM { if (insert_id(yytext,0)) return -1; } | IDENTIFIER { if (insert_id(yytext,0)) return -1; } ; path : PATH { if (insert_id(yytext,0)) return -1; } | QPATH { yytext[strlen(yytext) - 1] = '\0'; if (insert_id(yytext + 1,0)) return -1; } ; filename : FILENAME { yytext[strlen(yytext) - 1] = '\0'; if (insert_id(yytext + 1,0)) return -1; } ; number : NUMBER { unsigned long x; errno = 0; x = strtoul(yytext, NULL, 0); if (errno) return -1; #if ULONG_MAX > UINT_MAX if (x > UINT_MAX) return -1; #endif $$ = (unsigned int) x; } ; number64 : NUMBER { unsigned long long x; errno = 0; x = strtoull(yytext, NULL, 0); if (errno) return -1; $$ = (uint64_t) x; } ; ipv6_addr : IPV6_ADDR { if (insert_id(yytext,0)) return -1; } ; policycap_def : POLICYCAP identifier ';' {if (define_polcap()) return -1;} ; permissive_def : PERMISSIVE identifier ';' {if (define_permissive()) return -1;} /*********** module grammar below ***********/ module_policy : module_def avrules_block { if (end_avrule_block(pass) == -1) return -1; if (policydb_index_others(NULL, policydbp, 0)) return -1; } ; module_def : MODULE identifier version_identifier ';' { if (define_policy(pass, 1) == -1) return -1; } ; version_identifier : VERSION_IDENTIFIER { if (insert_id(yytext,0)) return -1; } | number { if (insert_id(yytext,0)) return -1; } | 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)) return -1; } ; 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)) return -1; } | require_id_list ',' identifier { if ($0 (pass)) return -1; } ; optional_block : optional_decl '{' avrules_block '}' { if (end_avrule_block(pass) == -1) return -1; } optional_else { if (end_optional(pass) == -1) return -1; } ; optional_else : else_decl '{' avrules_block '}' { if (end_avrule_block(pass) == -1) return -1; } | /* empty */ ; optional_decl : OPTIONAL { if (begin_optional(pass) == -1) return -1; } ; else_decl : ELSE { if (begin_optional_else(pass) == -1) return -1; } ; avrule_user_defs : user_def avrule_user_defs | /* empty */ ; checkpolicy-3.3/policy_scan.l000066400000000000000000000200201413427467300163410ustar00rootroot00000000000000 /* * 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 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); 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.* { strncpy(linebuf[lno], yytext+1, 255); linebuf[lno][254] = 0; lno = 1 - lno; policydb_lineno++; if (source_lineno == ULONG_MAX) yywarn("source line number overflow"); else source_lineno++; yyless(1); } CLONE | clone { return(CLONE); } 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} { return(IPV4_ADDR); } {hexval}{0,4}":"{hexval}{0,4}":"({hexval}|[:.])* { return(IPV6_ADDR); } {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");} %% int yyerror(const char *msg) { if (source_file[0]) fprintf(stderr, "%s:%ld:", source_file, source_lineno); else fprintf(stderr, "(unknown source)::"); fprintf(stderr, "ERROR '%s' at token '%s' on line %ld:\n%s\n%s\n", msg, yytext, policydb_lineno, linebuf[0], linebuf[1]); policydb_errors++; return -1; } int yywarn(const char *msg) { if (werror) return yyerror(msg); if (source_file[0]) fprintf(stderr, "%s:%ld:", source_file, source_lineno); else fprintf(stderr, "(unknown source)::"); fprintf(stderr, "WARNING '%s' at token '%s' on line %ld:\n%s\n%s\n", msg, yytext, policydb_lineno, linebuf[0], linebuf[1]); 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.3/queue.c000066400000000000000000000047771413427467300151760ustar00rootroot00000000000000 /* 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_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.3/queue.h000066400000000000000000000027611413427467300151720ustar00rootroot00000000000000 /* 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_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.3/ru/000077500000000000000000000000001413427467300143155ustar00rootroot00000000000000checkpolicy-3.3/ru/checkmodule.8000066400000000000000000000077631413427467300167060ustar00rootroot00000000000000.TH CHECKMODULE 8 .SH ИМЯ checkmodule \- компилятор модулей политики SELinux .SH ОБЗОР .B checkmodule .I "[\-h] [\-b] [\-C] [\-m] [\-M] [\-U handle_unknown ] [\-V] [\-o output_file] [input_file]" .SH "ОПИСАНИЕ" Эта страница руководства содержит описание команды .BR checkmodule. .PP .B checkmodule - это программа, которая проверяет и компилирует модуль политики безопасности SELinux в двоичное представление. Она может создавать либо базовый модуль политики (по умолчанию), либо небазовый модуль политики (параметр \-m); обычно требуется создать небазовый модуль политики для последующего добавления в существующее хранилище модулей, в котором уже есть базовый модуль, предоставленный базовой политикой. Используйте semodule_package, чтобы объединить этот модуль с соответствующим необязательным файлом контекстов файлов и таким образом создать пакет политики, а затем используйте semodule для установки пакета модуля в хранилище модулей и загрузите получившуюся политику. .SH ПАРАМЕТРЫ .TP .B \-b,\-\-binary Прочитать существующий файл двоичного модуля политики вместо исходного файла модуля политики. Этот параметр полезен при разработке/отладке. .TP .B \-C,\-\-cil Записать файл политики CIL, а не двоичный файл политики. .TP .B \-h,\-\-help Вывести справку. .TP .B \-m Создать небазовый модуль политики. .TP .B \-M,\-\-mls Включить поддержку MLS/MCS при проверке и компиляции модуля политики. .TP .B \-V,\-\-version Показать версии политики, созданные этой программой. Обратите внимание, что в настоящее время не поддерживается сборка более ранних версий. .TP .B \-o,\-\-output filename Записать файл двоичного модуля политики с указанным именем файла. Без указания имени файла checkmodule только проверит синтаксис исходного файла модуля и не будет создавать двоичный модуль. .TP .B \-U,\-\-handle-unknown Указать, как ядро должно обрабатывать неизвестные классы или разрешения (запрещать, разрешать или отклонять). .SH ПРИМЕР .nf # Собрать небазовый модуль политики с включённой системой безопасности MLS/MCS. $ checkmodule \-M \-m httpd.te \-o httpd.mod .fi .SH "СМОТРИТЕ ТАКЖЕ" .B semodule(8), semodule_package(8) Документация SELinux Reference Policy по адресу https://github.com/SELinuxProject/refpolicy/wiki .SH АВТОРЫ Эта страница руководства была скопирована со страницы руководства checkpolicy, написанной Árpád Magosányi , и отредактирована Dan Walsh . Программа была написана Stephen Smalley . Перевод на русский язык выполнила Олеся Герасименко . checkpolicy-3.3/ru/checkpolicy.8000066400000000000000000000064741413427467300167160ustar00rootroot00000000000000.TH CHECKPOLICY 8 .SH ИМЯ checkpolicy \- компилятор политики SELinux .SH ОБЗОР .B checkpolicy .I "[\-b[F]] [\-C] [\-d] [\-U handle_unknown (allow,deny,reject)] [\-M] [\-c policyvers] [\-o output_file] [\-S] [\-t target_platform (selinux,xen)] [\-V] [input_file]" .br .SH "ОПИСАНИЕ" Эта страница руководства содержит описание команды .BR checkpolicy. .PP .B checkpolicy - это программа, которая проверяет и компилирует конфигурацию политики безопасности SELinux в двоичное представление, которое можно загрузить в ядро. Если имя входного файла не указано, checkpolicy попытается выполнить чтение из policy.conf или policy (если указан флаг \-b). .SH ПАРАМЕТРЫ .TP .B \-b,\-\-binary Прочитать существующий двоичный файл политики, а не исходный файл policy.conf. .TP .B \-F,\-\-conf Записать файл policy.conf, а не двоичный файл политики. Этот параметр можно использовать только при работе с двоичным файлом политики. .TP .B \-C,\-\-cil Записать файл политики CIL, а не двоичный файл политики. .TP .B \-d,\-\-debug Войти в режим отладки после загрузки политики. .TP .B \-U,\-\-handle-unknown Указать, как ядро должно обрабатывать неизвестные классы или разрешения (запрещать, разрешать или отклонять). .TP .B \-M,\-\-mls Включить политику MLS при проверке и компиляции политики. .TP .B \-c policyvers Указать версию политики (по умолчанию используется последняя). .TP .B \-o,\-\-output filename Записать двоичный файл политики с указанным именем файла. .TP .B \-S,\-\-sort Сортировать прочие контексты (ocontexts) перед записью двоичной политики. Этот параметр позволяет задать соответствие вывода checkpolicy двоичным политикам, созданным с помощью semanage и secilc. .TP .B \-t,\-\-target Указать целевую платформу (selinux или xen). .TP .B \-V,\-\-version Показать сведения о версии. .TP .B \-h,\-\-help Показать сведения об использовании. .SH "СМОТРИТЕ ТАКЖЕ" Документация SELinux Reference Policy по адресу https://github.com/SELinuxProject/refpolicy/wiki .SH АВТОРЫ Эта страница руководства была написана Árpád Magosányi , и отредактирована Stephen Smalley . Программа была написана Stephen Smalley . Перевод на русский язык выполнила Олеся Герасименко . checkpolicy-3.3/test/000077500000000000000000000000001413427467300146465ustar00rootroot00000000000000checkpolicy-3.3/test/.gitignore000066400000000000000000000000161413427467300166330ustar00rootroot00000000000000dismod dispol checkpolicy-3.3/test/Makefile000066400000000000000000000011051413427467300163030ustar00rootroot00000000000000# # 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) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS_LIBSEPOLA) dismod: dismod.o $(LIBSEPOLA) $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LDLIBS_LIBSEPOLA) clean: -rm -f dispol dismod *.o checkpolicy-3.3/test/dismod.c000066400000000000000000000614031413427467300162750ustar00rootroot00000000000000 /* 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; extern unsigned int ss_initialized; int policyvers = MOD_POLICYDB_VERSION_BASE; static const char *symbol_labels[9] = { "commons", "classes", "roles ", "types ", "users ", "bools ", "levels ", "cats ", "attribs" }; static __attribute__((__noreturn__)) void usage(const char *progname) { printf("usage: %s binary_pol_file\n\n", progname); exit(1); } static void render_access_mask(uint32_t mask, uint32_t class, policydb_t * p, FILE * fp) { char *perm; fprintf(fp, "{"); perm = sepol_av_to_string(p, class, mask); if (perm) fprintf(fp, "%s ", perm); fprintf(fp, "}"); } 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); if (perm) fprintf(fp, " %s", 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, " ~"); } num_types = 0; if (flags & RULE_SELF) { 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 (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"); } } 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_NEVERALLOW) { fprintf(fp, " neverallow"); } 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 { 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; 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 { 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]; fprintf(fp, "%s", sepol_extended_perms_to_string(&xperms)); } 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->bool - 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 *bool; bool = hashtab_search(p->p_bools.table, name); if (bool == NULL) { fprintf(fp, "Could not find bool %s\n", name); return -1; } bool->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) { 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, 1); package->policy = NULL; sepol_module_package_free(package); } else { if (policydb_init(policy)) { fprintf(stderr, "%s: Out of memory!\n", __FUNCTION__); exit(1); } retval = policydb_read(policy, &f, 1); } fclose(in_fp); return retval; } static void link_module(policydb_t * base, FILE * out_fp) { 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; } /* read the binary policy */ fprintf(out_fp, "Reading module...\n"); if (read_policy(module_name, mods)) { 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) { printf("\nSelect a command:\n"); printf("1) display unconditional AVTAB\n"); printf("2) display conditional AVTAB\n"); printf("3) display users\n"); printf("4) display bools\n"); printf("5) display roles\n"); printf("6) display types, attributes, and aliases\n"); printf("7) display role transitions\n"); printf("8) display role allows\n"); printf("9) Display policycon\n"); printf("0) Display initial SIDs\n"); printf("\n"); printf("a) Display avrule requirements\n"); printf("b) Display avrule declarations\n"); printf("c) Display policy capabilities\n"); printf("l) Link in a module\n"); printf("u) Display the unknown handling setting\n"); printf("F) Display filename_trans rules\n"); printf("\n"); printf("f) set output file\n"); printf("m) display menu\n"); printf("q) quit\n"); return 0; } int main(int argc, char **argv) { FILE *out_fp = stdout; char ans[81], OutfileName[121]; if (argc != 2) usage(argv[0]); /* read the binary policy */ fprintf(out_fp, "Reading policy...\n"); if (policydb_init(&policydb)) { fprintf(stderr, "%s: Out of memory!\n", __FUNCTION__); exit(1); } if (read_policy(argv[1], &policydb)) { 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, 1)) { fprintf(stderr, "Error indexing others\n"); exit(1); } if (policydb.policy_type == POLICY_BASE) { printf("Binary base policy file loaded.\n"); } else { printf("Binary policy module file loaded.\n"); printf("Module name: %s\n", policydb.name); printf("Module version: %s\n", policydb.version); } printf("Policy version: %d\n\n", policydb.policyvers); menu(); for (;;) { printf("\nCommand (\'m\' for menu): "); if (fgets(ans, sizeof(ans), stdin) == NULL) { 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); 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.3/test/dispol.c000066400000000000000000000337341413427467300163160ustar00rootroot00000000000000 /* 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 policydb_t policydb; static __attribute__((__noreturn__)) void usage(const char *progname) { printf("usage: %s binary_pol_file\n\n", progname); exit(1); } static int render_access_mask(uint32_t mask, avtab_key_t * key, policydb_t * p, FILE * fp) { char *perm; fprintf(fp, "{"); perm = sepol_av_to_string(p, key->target_class, mask); if (perm) fprintf(fp, "%s ", perm); fprintf(fp, "}"); 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) { 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); fprintf(fp, "%s;\n", sepol_extended_perms_to_string(datum->xperms)); } 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 int display_bools(policydb_t * p, FILE * fp) { unsigned int i; for (i = 0; i < p->p_bools.nprim; i++) { fprintf(fp, "%s : %d\n", p->p_bool_val_to_name[i], 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->bool - 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"); return 0; } static int change_bool(char *name, int state, policydb_t * p, FILE * fp) { cond_bool_datum_t *bool; bool = hashtab_search(p->p_bools.table, name); if (bool == NULL) { fprintf(fp, "Could not find bool %s\n", name); return -1; } bool->state = state; evaluate_conds(p); 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 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:\n"); 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 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) { printf("\nSelect a command:\n"); printf("1) display unconditional AVTAB\n"); printf("2) display conditional AVTAB (entirely)\n"); printf("3) display conditional AVTAB (only ENABLED rules)\n"); printf("4) display conditional AVTAB (only DISABLED rules)\n"); printf("5) display conditional bools\n"); printf("6) display conditional expressions\n"); printf("7) change a boolean value\n"); printf("8) display role transitions\n"); printf("\n"); printf("c) display policy capabilities\n"); printf("p) display the list of permissive types\n"); printf("u) display unknown handling setting\n"); printf("F) display filename_trans rules\n"); printf("\n"); printf("f) set output file\n"); printf("m) display menu\n"); printf("q) quit\n"); return 0; } int main(int argc, char **argv) { 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; if (argc != 2) usage(argv[0]); fd = open(argv[1], O_RDONLY); if (fd < 0) { fprintf(stderr, "Can't open '%s': %s\n", argv[1], strerror(errno)); exit(1); } if (fstat(fd, &sb) < 0) { fprintf(stderr, "Can't stat '%s': %s\n", argv[1], 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", argv[1], strerror(errno)); exit(1); } /* read the binary policy */ 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, 1); if (ret) { fprintf(stderr, "%s: error(s) encountered while parsing configuration\n", argv[0]); exit(1); } fprintf(stdout, "binary policy file loaded\n\n"); close(fd); menu(); for (;;) { printf("\nCommand (\'m\' for menu): "); if (fgets(ans, sizeof(ans), stdin) == NULL) { 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_bools(&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 = malloc((strlen(ans) + 1) * sizeof(char)); if (name == NULL) { fprintf(stderr, "couldn't malloc string.\n"); break; } strcpy(name, ans); 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 'c': display_policycaps(&policydb, out_fp); break; case 'p': display_permissive(&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': 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 */