aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.bp6
-rw-r--r--CleanSpec.mk4
-rw-r--r--MODULE_LICENSE_GPL0
-rw-r--r--NOTICE2060
-rw-r--r--OWNERS3
-rw-r--r--README.android41
-rw-r--r--checkpolicy/Android.bp27
-rw-r--r--libselinux/Android.bp221
-rw-r--r--libselinux/MODULE_LICENSE_PUBLIC_DOMAIN0
-rw-r--r--libselinux/exported.map51
-rw-r--r--libselinux/exported_vendor.map36
-rw-r--r--libselinux/include/selinux/android.h68
-rw-r--r--libselinux/include/selinux/label.h4
-rw-r--r--libselinux/src/android/android.c198
-rw-r--r--libselinux/src/android/android_common.h44
-rw-r--r--libselinux/src/android/android_host.c8
-rw-r--r--libselinux/src/android/android_platform.c1859
-rw-r--r--libselinux/src/android/android_vendor.c14
-rw-r--r--libselinux/src/label.c38
-rw-r--r--libselinux/src/label_backends_android.c161
-rw-r--r--libselinux/src/label_file.c241
-rw-r--r--libselinux/src/label_file.h10
-rw-r--r--libselinux/src/label_internal.h8
-rw-r--r--libsepol/Android.bp107
-rw-r--r--libsepol/cil/include/cil/android.h34
-rw-r--r--libsepol/cil/include/cil/cil_write_ast.h7
-rw-r--r--libsepol/cil/src/android.c932
-rw-r--r--libsepol/cil/src/cil_write_ast.c1509
-rwxr-xr-xprebuilts/bin/audit2allow23
-rwxr-xr-xprebuilts/bin/audit2why23
-rwxr-xr-xprebuilts/bin/sediff15
-rwxr-xr-xprebuilts/bin/sediff.py1366
-rwxr-xr-xprebuilts/bin/seinfo16
-rwxr-xr-xprebuilts/bin/seinfo.py366
-rwxr-xr-xprebuilts/bin/sesearch9
-rwxr-xr-xprebuilts/bin/sesearch.py248
-rwxr-xr-x[-rw-r--r--]python/audit2allow/audit2allow0
-rw-r--r--secilc/Android.bp27
38 files changed, 9649 insertions, 135 deletions
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 00000000..22e65681
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,6 @@
+subdirs = [
+ "checkpolicy",
+ "libselinux",
+ "libsepol",
+ "secilc",
+]
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 1ac5a628..a67955b0 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -1,2 +1,6 @@
# This empty CleanSpec.mk file will prevent the build system
# from descending into subdirs.
+#
+$(call add-clean-step, rm -rf $(OUT_DIR)/host/linux-x86/bin/audit2allow)
+$(call add-clean-step, rm -rf $(OUT_DIR)/host/linux-x86/bin/audit2why)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/vendor/lib/libselinux.so)
diff --git a/MODULE_LICENSE_GPL b/MODULE_LICENSE_GPL
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/MODULE_LICENSE_GPL
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 00000000..0b4d3106
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,2060 @@
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 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.
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, 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 library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete 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 distribute a copy of this License along with the
+Library.
+
+ 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 Library or any portion
+of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+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 Library, 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 Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you 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.
+
+ If distribution of 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 satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be 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.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+ 9. 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 Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+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 with
+this License.
+
+ 11. 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 Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library 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 Library.
+
+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.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library 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.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+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 Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+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
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. 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 LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. 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.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+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 and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, 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 library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete 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 distribute a copy of this License along with the
+Library.
+
+ 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 Library or any portion
+of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+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 Library, 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 Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you 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.
+
+ If distribution of 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 satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be 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.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+ 9. 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 Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+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 with
+this License.
+
+ 11. 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 Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library 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 Library.
+
+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.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library 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.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+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 Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+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
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. 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 LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. 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.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 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.
+All files are licensed under the FreeBSD license, excepet for thid party
+components, which are subject to their respective licenses as specified in
+their source files.
+
+ FreeBSD License
+
+Copyright 2011 Tresys Technology, LLC. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY TRESYS TECHNOLOGY, LLC ``AS IS'' AND ANY EXPRESS
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL TRESYS TECHNOLOGY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those
+of the authors and should not be interpreted as representing official policies,
+either expressed or implied, of Tresys Technology, LLC.
+ 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 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.
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 00000000..215ed6a6
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+jeffv@google.com
+jgalenson@google.com
+nnk@google.com
diff --git a/README.android b/README.android
new file mode 100644
index 00000000..df8a60fe
--- /dev/null
+++ b/README.android
@@ -0,0 +1,41 @@
+This fork of Android differs in the following ways:
+ - README.android
+ - All Android.mk and Android.bp files
+ - ALL MODULE_LICENSE_* files
+ - libselinux/include/selinux/android.h
+ - libselinux/src/android/android.c
+
+All other changes should be upstreamed to selinux as
+Android no longer carries changes outside of those files.
+
+The upstream project can be found at:
+https://github.com/SELinuxProject/selinux
+
+Thus, since all changes are in separate files, updates merged from
+upstream should occur with no merge conflicts.
+
+This fork differs from upstream libselinux in at least the following ways:
+
+* The Android fork omits compiling many of the src files and specifies
+ custom build configurations. The exact details, are encoded in the
+ Android.bp and Android.mk files.
+
+* The SELinux policy files are all located in / rather than under
+ /etc/selinux since /etc is not available in Android until /system
+ is mounted and use fixed paths, not dependent on /etc/selinux/config.
+
+* The kernel policy file (sepolicy in Android, policy.N in Linux) does
+ not include a version suffix since Android does not need to support
+ booting multiple kernels.
+
+* The policy loading logic does not support automatic downgrading of
+ the kernel policy file to a version known to the kernel, since this
+ requires libsepol on the device and is only needed to support mixing
+ and matching kernels and userspace easily.
+
+* restorecon functionality, including recursive restorecon, has been
+ been upstreamed as selinux_restorecon(), but there are residual
+ differences between it and selinux_android_restorecon().
+
+* Support for seapp_contexts, a new Android-specific SELinux
+ configuration file has been added within android.c.
diff --git a/checkpolicy/Android.bp b/checkpolicy/Android.bp
new file mode 100644
index 00000000..bfd91b7c
--- /dev/null
+++ b/checkpolicy/Android.bp
@@ -0,0 +1,27 @@
+common_CFLAGS = [
+ "-Wall",
+ "-Werror",
+ "-Wshadow",
+]
+
+cc_binary_host {
+ name: "checkpolicy",
+ cflags: common_CFLAGS,
+ srcs: [
+ "policy_parse.y",
+ "policy_scan.l",
+ "queue.c",
+ "module_compiler.c",
+ "parse_util.c",
+ "policy_define.c",
+ "checkpolicy.c",
+ ],
+ static_libs: ["libsepol"],
+}
+
+cc_binary_host {
+ name: "dispol",
+ cflags: common_CFLAGS,
+ srcs: ["test/dispol.c"],
+ static_libs: ["libsepol"],
+}
diff --git a/libselinux/Android.bp b/libselinux/Android.bp
new file mode 100644
index 00000000..6833cba8
--- /dev/null
+++ b/libselinux/Android.bp
@@ -0,0 +1,221 @@
+common_CFLAGS = [
+ // Persistently stored patterns (pcre2) are architecture dependent.
+ // In particular paterns built on amd64 can not run on devices with armv7
+ // (32bit). Therefore, this feature stays off for now.
+ "-DNO_PERSISTENTLY_STORED_PATTERNS",
+ "-DDISABLE_SETRANS",
+ "-DDISABLE_BOOL",
+ "-D_GNU_SOURCE",
+ "-DNO_MEDIA_BACKEND",
+ "-DNO_X_BACKEND",
+ "-DNO_DB_BACKEND",
+ "-Wall",
+ "-Werror",
+ "-Wno-error=missing-noreturn",
+ "-Wno-error=unused-function",
+ "-Wno-error=unused-variable",
+]
+
+cc_defaults {
+ name: "libselinux_defaults",
+
+ cflags: common_CFLAGS,
+
+ srcs: [
+ "src/booleans.c",
+ "src/callbacks.c",
+ "src/freecon.c",
+ "src/label_backends_android.c",
+ "src/label.c",
+ "src/label_support.c",
+ "src/matchpathcon.c",
+ "src/setrans_client.c",
+ "src/sha1.c",
+ ],
+
+ target: {
+ host: {
+ cflags: [
+ "-DBUILD_HOST",
+ ],
+ },
+
+ android: {
+ srcs: [
+ "src/android/android.c",
+ "src/avc.c",
+ "src/avc_internal.c",
+ "src/avc_sidtab.c",
+ "src/canonicalize_context.c",
+ "src/checkAccess.c",
+ "src/check_context.c",
+ "src/compute_av.c",
+ "src/compute_create.c",
+ "src/compute_member.c",
+ "src/context.c",
+ "src/deny_unknown.c",
+ "src/disable.c",
+ "src/enabled.c",
+ "src/fgetfilecon.c",
+ "src/fsetfilecon.c",
+ "src/getenforce.c",
+ "src/getfilecon.c",
+ "src/get_initial_context.c",
+ "src/getpeercon.c",
+ "src/init.c",
+ "src/lgetfilecon.c",
+ "src/load_policy.c",
+ "src/lsetfilecon.c",
+ "src/mapping.c",
+ "src/policyvers.c",
+ "src/procattr.c",
+ "src/reject_unknown.c",
+ "src/sestatus.c",
+ "src/setenforce.c",
+ "src/setfilecon.c",
+ "src/stringrep.c",
+ ],
+
+ shared_libs: [
+ "libcrypto",
+ "liblog",
+ ],
+
+ local_include_dirs: [ "src" ],
+
+ // 1003 corresponds to auditd, from system/core/logd/event.logtags
+ cflags: [
+ "-DAUDITD_LOG_TAG=1003",
+ ],
+ }
+ },
+
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
+
+cc_library {
+ name: "libselinux",
+ defaults: ["libselinux_defaults"],
+
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
+ double_loadable: true,
+ recovery_available: true,
+
+ host_supported: true,
+ cflags: ["-DUSE_PCRE2"],
+
+ srcs: [
+ "src/label_file.c",
+ "src/regex.c",
+ ],
+
+ target: {
+ linux_glibc: {
+ srcs: [
+ "src/android/android_host.c",
+ "src/avc.c",
+ "src/avc_internal.c",
+ "src/avc_sidtab.c",
+ "src/compute_av.c",
+ "src/compute_create.c",
+ "src/compute_member.c",
+ "src/context.c",
+ "src/deny_unknown.c",
+ "src/enabled.c",
+ "src/fgetfilecon.c",
+ "src/getenforce.c",
+ "src/getfilecon.c",
+ "src/get_initial_context.c",
+ "src/init.c",
+ "src/lgetfilecon.c",
+ "src/load_policy.c",
+ "src/lsetfilecon.c",
+ "src/mapping.c",
+ "src/procattr.c",
+ "src/reject_unknown.c",
+ "src/setenforce.c",
+ "src/setexecfilecon.c",
+ "src/setfilecon.c",
+ "src/stringrep.c",
+ ],
+ },
+ linux_bionic: {
+ enabled: true,
+ srcs: [
+ "src/android/android_host.c",
+ "src/avc.c",
+ "src/avc_internal.c",
+ "src/avc_sidtab.c",
+ "src/compute_av.c",
+ "src/compute_create.c",
+ "src/compute_member.c",
+ "src/context.c",
+ "src/deny_unknown.c",
+ "src/enabled.c",
+ "src/getenforce.c",
+ "src/getfilecon.c",
+ "src/get_initial_context.c",
+ "src/init.c",
+ "src/load_policy.c",
+ "src/mapping.c",
+ "src/procattr.c",
+ "src/reject_unknown.c",
+ "src/setexecfilecon.c",
+ "src/stringrep.c",
+ ],
+ },
+
+ android: {
+ srcs: [
+ "src/android/android_platform.c",
+ ],
+
+ static: {
+ whole_static_libs: ["libpackagelistparser"],
+ },
+
+ shared: {
+ shared_libs: ["libpackagelistparser"],
+ },
+
+ version_script: "exported.map",
+ },
+
+ vendor: {
+ exclude_srcs: [
+ "src/android/android_platform.c",
+ ],
+ srcs: [
+ "src/android/android_vendor.c",
+ ],
+ cflags: ["-DNO_FILE_BACKEND"],
+ exclude_shared_libs: ["libpackagelistparser"],
+ exclude_static_libs: ["libpackagelistparser"],
+ version_script: "exported_vendor.map",
+ },
+ },
+
+ static: {
+ whole_static_libs: ["libpcre2"],
+ },
+ shared: {
+ shared_libs: ["libpcre2"],
+ },
+}
+
+cc_binary_host {
+ name: "sefcontext_compile",
+ defaults: ["libselinux_defaults"],
+ cflags: ["-DUSE_PCRE2"],
+ srcs: ["utils/sefcontext_compile.c"],
+
+ static_libs: [
+ "libselinux",
+ "libsepol",
+ ],
+ whole_static_libs: ["libpcre2"],
+}
diff --git a/libselinux/MODULE_LICENSE_PUBLIC_DOMAIN b/libselinux/MODULE_LICENSE_PUBLIC_DOMAIN
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/libselinux/MODULE_LICENSE_PUBLIC_DOMAIN
diff --git a/libselinux/exported.map b/libselinux/exported.map
new file mode 100644
index 00000000..673025c9
--- /dev/null
+++ b/libselinux/exported.map
@@ -0,0 +1,51 @@
+{
+ global:
+ fgetfilecon;
+ fgetfilecon_raw;
+ freecon;
+ fsetfilecon;
+ getcon;
+ getfilecon;
+ getpeercon;
+ getpidcon;
+ is_selinux_enabled;
+ lgetfilecon;
+ lsetfilecon;
+ security_compute_create;
+ security_get_initial_context;
+ security_getenforce;
+ security_load_policy;
+ security_policyvers;
+ security_setenforce;
+ selabel_close;
+ selabel_lookup;
+ selabel_lookup_best_match;
+ selabel_open;
+ selinux_android_file_context_handle;
+ selinux_android_hw_service_context_handle;
+ selinux_android_load_policy;
+ selinux_android_load_policy_from_fd;
+ selinux_android_prop_context_handle;
+ selinux_android_restorecon;
+ selinux_android_restorecon_pkgdir;
+ selinux_android_service_context_handle;
+ selinux_android_set_sehandle;
+ selinux_android_setcon;
+ selinux_android_setcontext;
+ selinux_android_vendor_service_context_handle;
+ selinux_check_access;
+ selinux_log_callback;
+ selinux_set_callback;
+ selinux_status_open;
+ selinux_status_updated;
+ selinux_vendor_log_callback;
+ set_selinuxmnt;
+ setcon;
+ setexeccon;
+ setfilecon;
+ setfscreatecon;
+ setsockcreatecon;
+ setsockcreatecon_raw;
+ string_to_security_class;
+ local: *;
+};
diff --git a/libselinux/exported_vendor.map b/libselinux/exported_vendor.map
new file mode 100644
index 00000000..fd604fd8
--- /dev/null
+++ b/libselinux/exported_vendor.map
@@ -0,0 +1,36 @@
+{
+ global:
+ fgetfilecon;
+ fsetfilecon;
+ freecon;
+ getcon;
+ getfilecon;
+ getpeercon;
+ getpidcon;
+ is_selinux_enabled;
+ lgetfilecon;
+ lsetfilecon;
+ selabel_close;
+ selabel_lookup;
+ selabel_open;
+ selinux_android_prop_context_handle;
+ selinux_android_restorecon;
+ selinux_android_service_context_handle;
+ selinux_android_hw_service_context_handle;
+ selinux_android_vendor_service_context_handle;
+ selinux_check_access;
+ security_getenforce;
+ security_setenforce;
+ security_load_policy;
+ security_policyvers;
+ selinux_log_callback;
+ selinux_set_callback;
+ selinux_status_open;
+ selinux_status_updated;
+ selinux_vendor_log_callback;
+ setcon;
+ setexeccon;
+ setfilecon;
+ setfscreatecon;
+ local: *;
+};
diff --git a/libselinux/include/selinux/android.h b/libselinux/include/selinux/android.h
new file mode 100644
index 00000000..c469262f
--- /dev/null
+++ b/libselinux/include/selinux/android.h
@@ -0,0 +1,68 @@
+#ifndef _SELINUX_ANDROID_H_
+#define _SELINUX_ANDROID_H_
+
+#include <stdbool.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <selinux/label.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct selabel_handle* selinux_android_file_context_handle(void);
+
+extern struct selabel_handle* selinux_android_prop_context_handle(void);
+
+extern struct selabel_handle* selinux_android_service_context_handle(void);
+
+extern struct selabel_handle* selinux_android_hw_service_context_handle(void);
+
+extern struct selabel_handle* selinux_android_vendor_service_context_handle(void);
+
+extern void selinux_android_set_sehandle(const struct selabel_handle *hndl);
+
+extern int selinux_android_load_policy(void);
+
+extern int selinux_android_load_policy_from_fd(int fd, const char *description);
+
+extern int selinux_android_setcon(const char *con);
+
+extern int selinux_android_setcontext(uid_t uid,
+ bool isSystemServer,
+ const char *seinfo,
+ const char *name);
+
+extern int selinux_android_setfilecon(const char *pkgdir,
+ const char *pkgname,
+ const char *seinfo,
+ uid_t uid);
+
+extern int selinux_log_callback(int type, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+
+// API to support legacy usecase where full-treble legacy VNDK vendor needs to use this callback.
+extern int selinux_vendor_log_callback(int type, const char *fmt, ...)
+ __attribute__ ((format(printf, 2, 3)));
+
+#define SELINUX_ANDROID_RESTORECON_NOCHANGE 1
+#define SELINUX_ANDROID_RESTORECON_VERBOSE 2
+#define SELINUX_ANDROID_RESTORECON_RECURSE 4
+#define SELINUX_ANDROID_RESTORECON_FORCE 8
+#define SELINUX_ANDROID_RESTORECON_DATADATA 16
+#define SELINUX_ANDROID_RESTORECON_SKIPCE 32
+#define SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS 64
+extern int selinux_android_restorecon(const char *file, unsigned int flags);
+
+extern int selinux_android_restorecon_pkgdir(const char *pkgdir,
+ const char *seinfo,
+ uid_t uid,
+ unsigned int flags);
+
+extern int selinux_android_seapp_context_reload(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/libselinux/include/selinux/label.h b/libselinux/include/selinux/label.h
index 277287ed..e537aa11 100644
--- a/libselinux/include/selinux/label.h
+++ b/libselinux/include/selinux/label.h
@@ -7,6 +7,7 @@
#define _SELABEL_H_
#include <stdbool.h>
+#include <stdint.h>
#include <sys/types.h>
#include <selinux/selinux.h>
@@ -105,6 +106,9 @@ int selabel_lookup_raw(struct selabel_handle *handle, char **con,
bool selabel_partial_match(struct selabel_handle *handle, const char *key);
+bool selabel_hash_all_partial_matches(struct selabel_handle *rec,
+ const char *key, uint8_t* digest);
+
int selabel_lookup_best_match(struct selabel_handle *rec, char **con,
const char *key, const char **aliases, int type);
int selabel_lookup_best_match_raw(struct selabel_handle *rec, char **con,
diff --git a/libselinux/src/android/android.c b/libselinux/src/android/android.c
new file mode 100644
index 00000000..e15dabed
--- /dev/null
+++ b/libselinux/src/android/android.c
@@ -0,0 +1,198 @@
+#include "android_common.h"
+
+// For 'system', 'product' (optional), 'vendor' (mandatory) and/or 'odm' (optional).
+#define MAX_FILE_CONTEXT_SIZE 4
+
+#ifdef __ANDROID_VNDK__
+#ifndef LOG_EVENT_STRING
+#define LOG_EVENT_STRING(...)
+#endif // LOG_EVENT_STRING
+#endif // __ANDROID_VNDK__
+
+static const struct selinux_opt seopts_service_plat[] = {
+ { SELABEL_OPT_PATH, "/system/etc/selinux/plat_service_contexts" },
+ { SELABEL_OPT_PATH, "/plat_service_contexts" }
+};
+static const struct selinux_opt seopts_service_product[] = {
+ { SELABEL_OPT_PATH, "/product/etc/selinux/product_service_contexts" },
+ { SELABEL_OPT_PATH, "/product_service_contexts" }
+};
+static const struct selinux_opt seopts_service_vendor[] = {
+ { SELABEL_OPT_PATH, "/vendor/etc/selinux/vendor_service_contexts" },
+ { SELABEL_OPT_PATH, "/vendor_service_contexts" },
+ // TODO: remove nonplat* when no need to retain backward compatibility.
+ { SELABEL_OPT_PATH, "/vendor/etc/selinux/nonplat_service_contexts" },
+ { SELABEL_OPT_PATH, "/nonplat_service_contexts" }
+};
+
+static const struct selinux_opt seopts_hwservice_plat[] = {
+ { SELABEL_OPT_PATH, "/system/etc/selinux/plat_hwservice_contexts" },
+ { SELABEL_OPT_PATH, "/plat_hwservice_contexts" }
+};
+static const struct selinux_opt seopts_hwservice_product[] = {
+ { SELABEL_OPT_PATH, "/product/etc/selinux/product_hwservice_contexts" },
+ { SELABEL_OPT_PATH, "/product_hwservice_contexts" }
+};
+static const struct selinux_opt seopts_hwservice_vendor[] = {
+ { SELABEL_OPT_PATH, "/vendor/etc/selinux/vendor_hwservice_contexts" },
+ { SELABEL_OPT_PATH, "/vendor_hwservice_contexts" },
+ // TODO: remove nonplat* when no need to retain backward compatibility.
+ { SELABEL_OPT_PATH, "/vendor/etc/selinux/nonplat_hwservice_contexts" },
+ { SELABEL_OPT_PATH, "/nonplat_hwservice_contexts" }
+};
+static const struct selinux_opt seopts_hwservice_odm[] = {
+ { SELABEL_OPT_PATH, "/odm/etc/selinux/odm_hwservice_contexts" },
+ { SELABEL_OPT_PATH, "/odm_hwservice_contexts" }
+};
+
+static const struct selinux_opt seopts_vndservice =
+ { SELABEL_OPT_PATH, "/vendor/etc/selinux/vndservice_contexts" };
+
+static const struct selinux_opt seopts_vndservice_rootfs =
+ { SELABEL_OPT_PATH, "/vndservice_contexts" };
+
+struct selabel_handle* selinux_android_service_open_context_handle(const struct selinux_opt* seopts_service,
+ unsigned nopts)
+{
+ struct selabel_handle* sehandle;
+
+ sehandle = selabel_open(SELABEL_CTX_ANDROID_SERVICE,
+ seopts_service, nopts);
+
+ if (!sehandle) {
+ selinux_log(SELINUX_ERROR, "%s: Error getting service context handle (%s)\n",
+ __FUNCTION__, strerror(errno));
+ return NULL;
+ }
+ selinux_log(SELINUX_INFO, "SELinux: Loaded service_contexts from:\n");
+ for (unsigned i = 0; i < nopts; i++) {
+ selinux_log(SELINUX_INFO, " %s\n", seopts_service[i].value);
+ }
+ return sehandle;
+}
+
+struct selabel_handle* selinux_android_service_context_handle(void)
+{
+ struct selinux_opt seopts_service[MAX_FILE_CONTEXT_SIZE];
+ int size = 0;
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(seopts_service_plat); i++) {
+ if (access(seopts_service_plat[i].value, R_OK) != -1) {
+ seopts_service[size++] = seopts_service_plat[i];
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(seopts_service_product); i++) {
+ if (access(seopts_service_product[i].value, R_OK) != -1) {
+ seopts_service[size++] = seopts_service_product[i];
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(seopts_service_vendor); i++) {
+ if (access(seopts_service_vendor[i].value, R_OK) != -1) {
+ seopts_service[size++] = seopts_service_vendor[i];
+ break;
+ }
+ }
+
+ return selinux_android_service_open_context_handle(seopts_service, size);
+}
+
+struct selabel_handle* selinux_android_hw_service_context_handle(void)
+{
+ struct selinux_opt seopts_service[MAX_FILE_CONTEXT_SIZE];
+ int size = 0;
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(seopts_hwservice_plat); i++) {
+ if (access(seopts_hwservice_plat[i].value, R_OK) != -1) {
+ seopts_service[size++] = seopts_hwservice_plat[i];
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(seopts_hwservice_product); i++) {
+ if (access(seopts_hwservice_product[i].value, R_OK) != -1) {
+ seopts_service[size++] = seopts_hwservice_product[i];
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(seopts_hwservice_vendor); i++) {
+ if (access(seopts_hwservice_vendor[i].value, R_OK) != -1) {
+ seopts_service[size++] = seopts_hwservice_vendor[i];
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(seopts_hwservice_odm); i++) {
+ if (access(seopts_hwservice_odm[i].value, R_OK) != -1) {
+ seopts_service[size++] = seopts_hwservice_odm[i];
+ break;
+ }
+ }
+ return selinux_android_service_open_context_handle(seopts_service, size);
+}
+
+struct selabel_handle* selinux_android_vendor_service_context_handle(void)
+{
+ const struct selinux_opt* seopts_service;
+ if (access(seopts_vndservice.value, R_OK) != -1) {
+ seopts_service = &seopts_vndservice;
+ } else {
+ seopts_service = &seopts_vndservice_rootfs;
+ }
+
+ return selinux_android_service_open_context_handle(seopts_service, 1);
+}
+
+int selinux_log_callback(int type, const char *fmt, ...)
+{
+ va_list ap;
+ int priority;
+ char *strp;
+
+ switch(type) {
+ case SELINUX_WARNING:
+ priority = ANDROID_LOG_WARN;
+ break;
+ case SELINUX_INFO:
+ priority = ANDROID_LOG_INFO;
+ break;
+ default:
+ priority = ANDROID_LOG_ERROR;
+ break;
+ }
+
+ va_start(ap, fmt);
+ if (vasprintf(&strp, fmt, ap) != -1) {
+ LOG_PRI(priority, "SELinux", "%s", strp);
+ LOG_EVENT_STRING(AUDITD_LOG_TAG, strp);
+ free(strp);
+ }
+ va_end(ap);
+ return 0;
+}
+
+int selinux_vendor_log_callback(int type, const char *fmt, ...)
+{
+ va_list ap;
+ int priority;
+ char *strp;
+
+ switch(type) {
+ case SELINUX_WARNING:
+ priority = ANDROID_LOG_WARN;
+ break;
+ case SELINUX_INFO:
+ priority = ANDROID_LOG_INFO;
+ break;
+ default:
+ priority = ANDROID_LOG_ERROR;
+ break;
+ }
+
+ va_start(ap, fmt);
+ if (vasprintf(&strp, fmt, ap) != -1) {
+ LOG_PRI(priority, "SELinux", "%s", strp);
+ free(strp);
+ }
+ va_end(ap);
+ return 0;
+}
diff --git a/libselinux/src/android/android_common.h b/libselinux/src/android/android_common.h
new file mode 100644
index 00000000..637d4e8b
--- /dev/null
+++ b/libselinux/src/android/android_common.h
@@ -0,0 +1,44 @@
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <errno.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/xattr.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <selinux/selinux.h>
+#include <selinux/context.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/avc.h>
+#include <openssl/sha.h>
+#ifndef __ANDROID_VNDK__
+#include <private/android_filesystem_config.h>
+#endif
+#include <log/log.h>
+#include "policy.h"
+#include "callbacks.h"
+#include "selinux_internal.h"
+#include "label_internal.h"
+#include <fnmatch.h>
+#include <limits.h>
+#include <sys/vfs.h>
+#include <linux/magic.h>
+#include <libgen.h>
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define FC_DIGEST_SIZE SHA_DIGEST_LENGTH
diff --git a/libselinux/src/android/android_host.c b/libselinux/src/android/android_host.c
new file mode 100644
index 00000000..8b55aa78
--- /dev/null
+++ b/libselinux/src/android/android_host.c
@@ -0,0 +1,8 @@
+#include <stdlib.h>
+
+// HACK: placeholder for a library the python bindings expect.
+// Delete after b/33170640 is fixed.
+const char *selinux_openssh_contexts_path(void)
+{
+ abort();
+}
diff --git a/libselinux/src/android/android_platform.c b/libselinux/src/android/android_platform.c
new file mode 100644
index 00000000..7b39b793
--- /dev/null
+++ b/libselinux/src/android/android_platform.c
@@ -0,0 +1,1859 @@
+#include "android_common.h"
+#include <packagelistparser/packagelistparser.h>
+
+// For 'system', 'product' (optional), 'vendor' (mandatory) and/or 'odm' (optional).
+#define MAX_FILE_CONTEXT_SIZE 4
+
+static const char *const sepolicy_file = "/sepolicy";
+
+static const struct selinux_opt seopts_file_plat[] = {
+ { SELABEL_OPT_PATH, "/system/etc/selinux/plat_file_contexts" },
+ { SELABEL_OPT_PATH, "/plat_file_contexts" }
+};
+static const struct selinux_opt seopts_file_product[] = {
+ { SELABEL_OPT_PATH, "/product/etc/selinux/product_file_contexts" },
+ { SELABEL_OPT_PATH, "/product_file_contexts" }
+};
+static const struct selinux_opt seopts_file_vendor[] = {
+ { SELABEL_OPT_PATH, "/vendor/etc/selinux/vendor_file_contexts" },
+ { SELABEL_OPT_PATH, "/vendor_file_contexts" },
+ // TODO: remove nonplat* when no need to retain backward compatibility.
+ { SELABEL_OPT_PATH, "/vendor/etc/selinux/nonplat_file_contexts" },
+ { SELABEL_OPT_PATH, "/nonplat_file_contexts" }
+};
+static const struct selinux_opt seopts_file_odm[] = {
+ { SELABEL_OPT_PATH, "/odm/etc/selinux/odm_file_contexts" },
+ { SELABEL_OPT_PATH, "/odm_file_contexts" }
+};
+
+static const struct selinux_opt seopts_prop_plat[] = {
+ { SELABEL_OPT_PATH, "/system/etc/selinux/plat_property_contexts" },
+ { SELABEL_OPT_PATH, "/plat_property_contexts" }
+};
+static const struct selinux_opt seopts_prop_product[] = {
+ { SELABEL_OPT_PATH, "/product/etc/selinux/product_property_contexts" },
+ { SELABEL_OPT_PATH, "/product_property_contexts" }
+};
+static const struct selinux_opt seopts_prop_vendor[] = {
+ { SELABEL_OPT_PATH, "/vendor/etc/selinux/vendor_property_contexts" },
+ { SELABEL_OPT_PATH, "/vendor_property_contexts" },
+ // TODO: remove nonplat* when no need to retain backward compatibility.
+ { SELABEL_OPT_PATH, "/vendor/etc/selinux/nonplat_property_contexts" },
+ { SELABEL_OPT_PATH, "/nonplat_property_contexts" }
+};
+static const struct selinux_opt seopts_prop_odm[] = {
+ { SELABEL_OPT_PATH, "/odm/etc/selinux/odm_property_contexts" },
+ { SELABEL_OPT_PATH, "/odm_property_contexts" }
+};
+
+/*
+ * XXX Where should this configuration file be located?
+ * Needs to be accessible by zygote and installd when
+ * setting credentials for app processes and setting permissions
+ * on app data directories.
+ */
+static char const * const seapp_contexts_plat[] = {
+ "/system/etc/selinux/plat_seapp_contexts",
+ "/plat_seapp_contexts"
+};
+static char const * const seapp_contexts_product[] = {
+ "/product/etc/selinux/product_seapp_contexts",
+ "/product_seapp_contexts"
+};
+static char const * const seapp_contexts_vendor[] = {
+ "/vendor/etc/selinux/vendor_seapp_contexts",
+ "/vendor_seapp_contexts",
+ // TODO: remove nonplat* when no need to retain backward compatibility.
+ "/vendor/etc/selinux/nonplat_seapp_contexts",
+ "/nonplat_seapp_contexts"
+};
+static char const * const seapp_contexts_odm[] = {
+ "/odm/etc/selinux/odm_seapp_contexts",
+ "/odm_seapp_contexts"
+};
+
+uint8_t fc_digest[FC_DIGEST_SIZE];
+
+static bool compute_file_contexts_hash(uint8_t c_digest[], const struct selinux_opt *opts, unsigned nopts)
+{
+ int fd = -1;
+ void *map = MAP_FAILED;
+ bool ret = false;
+ uint8_t *fc_data = NULL;
+ size_t total_size = 0;
+ struct stat sb;
+ size_t i;
+
+ for (i = 0; i < nopts; i++) {
+ fd = open(opts[i].value, O_CLOEXEC | O_RDONLY);
+ if (fd < 0) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could not open %s: %s\n",
+ opts[i].value, strerror(errno));
+ goto cleanup;
+ }
+
+ if (fstat(fd, &sb) < 0) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could not stat %s: %s\n",
+ opts[i].value, strerror(errno));
+ goto cleanup;
+ }
+
+ if (sb.st_size == 0) {
+ selinux_log(SELINUX_WARNING, "SELinux: Skipping %s: empty file\n",
+ opts[i].value);
+ goto nextfile;
+ }
+
+ map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could not map %s: %s\n",
+ opts[i].value, strerror(errno));
+ goto cleanup;
+ }
+
+ fc_data = realloc(fc_data, total_size + sb.st_size);
+ if (!fc_data) {
+ selinux_log(SELINUX_ERROR, "SELinux: Count not re-alloc for %s: %s\n",
+ opts[i].value, strerror(errno));
+ goto cleanup;
+ }
+
+ memcpy(fc_data + total_size, map, sb.st_size);
+ total_size += sb.st_size;
+
+ /* reset everything for next file */
+ munmap(map, sb.st_size);
+nextfile:
+ close(fd);
+ map = MAP_FAILED;
+ fd = -1;
+ }
+
+ SHA1(fc_data, total_size, c_digest);
+ ret = true;
+
+cleanup:
+ if (map != MAP_FAILED)
+ munmap(map, sb.st_size);
+ if (fd >= 0)
+ close(fd);
+ free(fc_data);
+
+ return ret;
+}
+
+static struct selabel_handle* selinux_android_file_context(const struct selinux_opt *opts,
+ unsigned nopts)
+{
+ struct selabel_handle *sehandle;
+ struct selinux_opt fc_opts[nopts + 1];
+
+ memcpy(fc_opts, opts, nopts*sizeof(struct selinux_opt));
+ fc_opts[nopts].type = SELABEL_OPT_BASEONLY;
+ fc_opts[nopts].value = (char *)1;
+
+ sehandle = selabel_open(SELABEL_CTX_FILE, fc_opts, ARRAY_SIZE(fc_opts));
+ if (!sehandle) {
+ selinux_log(SELINUX_ERROR, "%s: Error getting file context handle (%s)\n",
+ __FUNCTION__, strerror(errno));
+ return NULL;
+ }
+ if (!compute_file_contexts_hash(fc_digest, opts, nopts)) {
+ selabel_close(sehandle);
+ return NULL;
+ }
+
+ selinux_log(SELINUX_INFO, "SELinux: Loaded file_contexts\n");
+
+ return sehandle;
+}
+
+struct selabel_handle* selinux_android_file_context_handle(void)
+{
+ struct selinux_opt seopts_file[MAX_FILE_CONTEXT_SIZE];
+ int size = 0;
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(seopts_file_plat); i++) {
+ if (access(seopts_file_plat[i].value, R_OK) != -1) {
+ seopts_file[size++] = seopts_file_plat[i];
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(seopts_file_product); i++) {
+ if (access(seopts_file_product[i].value, R_OK) != -1) {
+ seopts_file[size++] = seopts_file_product[i];
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(seopts_file_vendor); i++) {
+ if (access(seopts_file_vendor[i].value, R_OK) != -1) {
+ seopts_file[size++] = seopts_file_vendor[i];
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(seopts_file_odm); i++) {
+ if (access(seopts_file_odm[i].value, R_OK) != -1) {
+ seopts_file[size++] = seopts_file_odm[i];
+ break;
+ }
+ }
+ return selinux_android_file_context(seopts_file, size);
+}
+
+struct selabel_handle* selinux_android_prop_context_handle(void)
+{
+ struct selabel_handle* sehandle;
+ struct selinux_opt seopts_prop[MAX_FILE_CONTEXT_SIZE];
+ int size = 0;
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(seopts_prop_plat); i++) {
+ if (access(seopts_prop_plat[i].value, R_OK) != -1) {
+ seopts_prop[size++] = seopts_prop_plat[i];
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(seopts_prop_product); i++) {
+ if (access(seopts_prop_product[i].value, R_OK) != -1) {
+ seopts_prop[size++] = seopts_prop_product[i];
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(seopts_prop_vendor); i++) {
+ if (access(seopts_prop_vendor[i].value, R_OK) != -1) {
+ seopts_prop[size++] = seopts_prop_vendor[i];
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(seopts_prop_odm); i++) {
+ if (access(seopts_prop_odm[i].value, R_OK) != -1) {
+ seopts_prop[size++] = seopts_prop_odm[i];
+ break;
+ }
+ }
+
+ sehandle = selabel_open(SELABEL_CTX_ANDROID_PROP, seopts_prop, size);
+ if (!sehandle) {
+ selinux_log(SELINUX_ERROR, "%s: Error getting property context handle (%s)\n",
+ __FUNCTION__, strerror(errno));
+ return NULL;
+ }
+ selinux_log(SELINUX_INFO, "SELinux: Loaded property_contexts from %s & %s.\n",
+ seopts_prop[0].value, seopts_prop[1].value);
+
+ return sehandle;
+}
+
+enum levelFrom {
+ LEVELFROM_NONE,
+ LEVELFROM_APP,
+ LEVELFROM_USER,
+ LEVELFROM_ALL
+};
+
+#if DEBUG
+static char const * const levelFromName[] = {
+ "none",
+ "app",
+ "user",
+ "all"
+};
+#endif
+
+struct prefix_str {
+ size_t len;
+ char *str;
+ char is_prefix;
+};
+
+static void free_prefix_str(struct prefix_str *p)
+{
+ if (!p)
+ return;
+ free(p->str);
+}
+
+struct seapp_context {
+ /* input selectors */
+ bool isSystemServer;
+ bool isEphemeralAppSet;
+ bool isEphemeralApp;
+ bool isV2AppSet;
+ bool isV2App;
+ bool isOwnerSet;
+ bool isOwner;
+ struct prefix_str user;
+ char *seinfo;
+ struct prefix_str name;
+ struct prefix_str path;
+ bool isPrivAppSet;
+ bool isPrivApp;
+ int32_t minTargetSdkVersion;
+ bool fromRunAs;
+ /* outputs */
+ char *domain;
+ char *type;
+ char *level;
+ enum levelFrom levelFrom;
+};
+
+static void free_seapp_context(struct seapp_context *s)
+{
+ if (!s)
+ return;
+
+ free_prefix_str(&s->user);
+ free(s->seinfo);
+ free_prefix_str(&s->name);
+ free_prefix_str(&s->path);
+ free(s->domain);
+ free(s->type);
+ free(s->level);
+}
+
+static bool seapp_contexts_dup = false;
+
+static int seapp_context_cmp(const void *A, const void *B)
+{
+ const struct seapp_context *const *sp1 = (const struct seapp_context *const *) A;
+ const struct seapp_context *const *sp2 = (const struct seapp_context *const *) B;
+ const struct seapp_context *s1 = *sp1, *s2 = *sp2;
+ bool dup;
+
+ /* Give precedence to isSystemServer=true. */
+ if (s1->isSystemServer != s2->isSystemServer)
+ return (s1->isSystemServer ? -1 : 1);
+
+ /* Give precedence to a specified isEphemeral= over an
+ * unspecified isEphemeral=. */
+ if (s1->isEphemeralAppSet != s2->isEphemeralAppSet)
+ return (s1->isEphemeralAppSet ? -1 : 1);
+
+ /* Give precedence to a specified isV2= over an
+ * unspecified isV2=. */
+ if (s1->isV2AppSet != s2->isV2AppSet)
+ return (s1->isV2AppSet ? -1 : 1);
+
+
+ /* Give precedence to a specified isOwner= over an unspecified isOwner=. */
+ if (s1->isOwnerSet != s2->isOwnerSet)
+ return (s1->isOwnerSet ? -1 : 1);
+
+ /* Give precedence to a specified user= over an unspecified user=. */
+ if (s1->user.str && !s2->user.str)
+ return -1;
+ if (!s1->user.str && s2->user.str)
+ return 1;
+
+ if (s1->user.str) {
+ /* Give precedence to a fixed user= string over a prefix. */
+ if (s1->user.is_prefix != s2->user.is_prefix)
+ return (s2->user.is_prefix ? -1 : 1);
+
+ /* Give precedence to a longer prefix over a shorter prefix. */
+ if (s1->user.is_prefix && s1->user.len != s2->user.len)
+ return (s1->user.len > s2->user.len) ? -1 : 1;
+ }
+
+ /* Give precedence to a specified seinfo= over an unspecified seinfo=. */
+ if (s1->seinfo && !s2->seinfo)
+ return -1;
+ if (!s1->seinfo && s2->seinfo)
+ return 1;
+
+ /* Give precedence to a specified name= over an unspecified name=. */
+ if (s1->name.str && !s2->name.str)
+ return -1;
+ if (!s1->name.str && s2->name.str)
+ return 1;
+
+ if (s1->name.str) {
+ /* Give precedence to a fixed name= string over a prefix. */
+ if (s1->name.is_prefix != s2->name.is_prefix)
+ return (s2->name.is_prefix ? -1 : 1);
+
+ /* Give precedence to a longer prefix over a shorter prefix. */
+ if (s1->name.is_prefix && s1->name.len != s2->name.len)
+ return (s1->name.len > s2->name.len) ? -1 : 1;
+ }
+
+ /* Give precedence to a specified path= over an unspecified path=. */
+ if (s1->path.str && !s2->path.str)
+ return -1;
+ if (!s1->path.str && s2->path.str)
+ return 1;
+
+ if (s1->path.str) {
+ /* Give precedence to a fixed path= string over a prefix. */
+ if (s1->path.is_prefix != s2->path.is_prefix)
+ return (s2->path.is_prefix ? -1 : 1);
+
+ /* Give precedence to a longer prefix over a shorter prefix. */
+ if (s1->path.is_prefix && s1->path.len != s2->path.len)
+ return (s1->path.len > s2->path.len) ? -1 : 1;
+ }
+
+ /* Give precedence to a specified isPrivApp= over an unspecified isPrivApp=. */
+ if (s1->isPrivAppSet != s2->isPrivAppSet)
+ return (s1->isPrivAppSet ? -1 : 1);
+
+ /* Give precedence to a higher minTargetSdkVersion= over a lower minTargetSdkVersion=.
+ * If unspecified, minTargetSdkVersion has a default value of 0.
+ */
+ if (s1->minTargetSdkVersion > s2->minTargetSdkVersion)
+ return -1;
+ else if (s1->minTargetSdkVersion < s2->minTargetSdkVersion)
+ return 1;
+
+ /* Give precedence to fromRunAs=true. */
+ if (s1->fromRunAs != s2->fromRunAs)
+ return (s1->fromRunAs ? -1 : 1);
+
+ /*
+ * Check for a duplicated entry on the input selectors.
+ * We already compared isSystemServer, isOwnerSet, and isOwner above.
+ * We also have already checked that both entries specify the same
+ * string fields, so if s1 has a non-NULL string, then so does s2.
+ */
+ dup = (!s1->user.str || !strcmp(s1->user.str, s2->user.str)) &&
+ (!s1->seinfo || !strcmp(s1->seinfo, s2->seinfo)) &&
+ (!s1->name.str || !strcmp(s1->name.str, s2->name.str)) &&
+ (!s1->path.str || !strcmp(s1->path.str, s2->path.str)) &&
+ (s1->isPrivAppSet && s1->isPrivApp == s2->isPrivApp) &&
+ (s1->isOwnerSet && s1->isOwner == s2->isOwner) &&
+ (s1->isSystemServer && s1->isSystemServer == s2->isSystemServer) &&
+ (s1->isV2AppSet && s1->isV2App == s2->isV2App) &&
+ (s1->isEphemeralAppSet && s1->isEphemeralApp == s2->isEphemeralApp);
+
+ if (dup) {
+ seapp_contexts_dup = true;
+ selinux_log(SELINUX_ERROR, "seapp_contexts: Duplicated entry\n");
+ if (s1->user.str)
+ selinux_log(SELINUX_ERROR, " user=%s\n", s1->user.str);
+ if (s1->seinfo)
+ selinux_log(SELINUX_ERROR, " seinfo=%s\n", s1->seinfo);
+ if (s1->name.str)
+ selinux_log(SELINUX_ERROR, " name=%s\n", s1->name.str);
+ if (s1->path.str)
+ selinux_log(SELINUX_ERROR, " path=%s\n", s1->path.str);
+ }
+
+ /* Anything else has equal precedence. */
+ return 0;
+}
+
+static struct seapp_context **seapp_contexts = NULL;
+static int nspec = 0;
+
+static void free_seapp_contexts(void)
+{
+ int n;
+
+ if (!seapp_contexts)
+ return;
+
+ for (n = 0; n < nspec; n++)
+ free_seapp_context(seapp_contexts[n]);
+
+ free(seapp_contexts);
+ seapp_contexts = NULL;
+ nspec = 0;
+}
+
+static int32_t get_minTargetSdkVersion(const char *value)
+{
+ char *endptr;
+ long minTargetSdkVersion;
+ minTargetSdkVersion = strtol(value, &endptr, 10);
+ if (('\0' != *endptr) || (minTargetSdkVersion < 0) || (minTargetSdkVersion > INT32_MAX)) {
+ return -1; /* error parsing minTargetSdkVersion */
+ } else {
+ return (int32_t) minTargetSdkVersion;
+ }
+}
+
+int selinux_android_seapp_context_reload(void)
+{
+ FILE *fp = NULL;
+ char line_buf[BUFSIZ];
+ char *token;
+ unsigned lineno;
+ struct seapp_context *cur;
+ char *p, *name = NULL, *value = NULL, *saveptr;
+ size_t i, len, files_len = 0;
+ int ret;
+ const char* seapp_contexts_files[MAX_FILE_CONTEXT_SIZE];
+ for (i = 0; i < ARRAY_SIZE(seapp_contexts_plat); i++) {
+ if (access(seapp_contexts_plat[i], R_OK) != -1) {
+ seapp_contexts_files[files_len++] = seapp_contexts_plat[i];
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(seapp_contexts_product); i++) {
+ if (access(seapp_contexts_product[i], R_OK) != -1) {
+ seapp_contexts_files[files_len++] = seapp_contexts_product[i];
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(seapp_contexts_vendor); i++) {
+ if (access(seapp_contexts_vendor[i], R_OK) != -1) {
+ seapp_contexts_files[files_len++] = seapp_contexts_vendor[i];
+ break;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(seapp_contexts_odm); i++) {
+ if (access(seapp_contexts_odm[i], R_OK) != -1) {
+ seapp_contexts_files[files_len++] = seapp_contexts_odm[i];
+ break;
+ }
+ }
+
+ free_seapp_contexts();
+
+ nspec = 0;
+ for (i = 0; i < files_len; i++) {
+ fp = fopen(seapp_contexts_files[i], "re");
+ if (!fp) {
+ selinux_log(SELINUX_ERROR, "%s: could not open seapp_contexts file: %s",
+ __FUNCTION__, seapp_contexts_files[i]);
+ return -1;
+ }
+ while (fgets(line_buf, sizeof line_buf - 1, fp)) {
+ p = line_buf;
+ while (isspace(*p))
+ p++;
+ if (*p == '#' || *p == 0)
+ continue;
+ nspec++;
+ }
+ fclose(fp);
+ }
+
+ seapp_contexts = (struct seapp_context **) calloc(nspec, sizeof(struct seapp_context *));
+ if (!seapp_contexts)
+ goto oom;
+
+ nspec = 0;
+ for (i = 0; i < files_len; i++) {
+ lineno = 1;
+ fp = fopen(seapp_contexts_files[i], "re");
+ if (!fp) {
+ selinux_log(SELINUX_ERROR, "%s: could not open seapp_contexts file: %s",
+ __FUNCTION__, seapp_contexts_files[i]);
+ free_seapp_contexts();
+ return -1;
+ }
+ while (fgets(line_buf, sizeof line_buf - 1, fp)) {
+ len = strlen(line_buf);
+ if (len == 0) {
+ // line contains a NUL byte as its first entry
+ goto err;
+ }
+ if (line_buf[len - 1] == '\n')
+ line_buf[len - 1] = 0;
+ p = line_buf;
+ while (isspace(*p))
+ p++;
+ if (*p == '#' || *p == 0)
+ continue;
+
+ cur = (struct seapp_context *) calloc(1, sizeof(struct seapp_context));
+ if (!cur)
+ goto oom;
+
+ token = strtok_r(p, " \t", &saveptr);
+ if (!token) {
+ free_seapp_context(cur);
+ goto err;
+ }
+
+ while (1) {
+ name = token;
+ value = strchr(name, '=');
+ if (!value) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ *value++ = 0;
+
+ if (!strcasecmp(name, "isSystemServer")) {
+ if (!strcasecmp(value, "true"))
+ cur->isSystemServer = true;
+ else if (!strcasecmp(value, "false"))
+ cur->isSystemServer = false;
+ else {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else if (!strcasecmp(name, "isEphemeralApp")) {
+ cur->isEphemeralAppSet = true;
+ if (!strcasecmp(value, "true"))
+ cur->isEphemeralApp = true;
+ else if (!strcasecmp(value, "false"))
+ cur->isEphemeralApp = false;
+ else {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else if (!strcasecmp(name, "isV2App")) {
+ cur->isV2AppSet = true;
+ if (!strcasecmp(value, "true"))
+ cur->isV2App = true;
+ else if (!strcasecmp(value, "false"))
+ cur->isV2App = false;
+ else {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else if (!strcasecmp(name, "isOwner")) {
+ cur->isOwnerSet = true;
+ if (!strcasecmp(value, "true"))
+ cur->isOwner = true;
+ else if (!strcasecmp(value, "false"))
+ cur->isOwner = false;
+ else {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else if (!strcasecmp(name, "user")) {
+ if (cur->user.str) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ cur->user.str = strdup(value);
+ if (!cur->user.str) {
+ free_seapp_context(cur);
+ goto oom;
+ }
+ cur->user.len = strlen(cur->user.str);
+ if (cur->user.str[cur->user.len-1] == '*')
+ cur->user.is_prefix = 1;
+ } else if (!strcasecmp(name, "seinfo")) {
+ if (cur->seinfo) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ cur->seinfo = strdup(value);
+ if (!cur->seinfo) {
+ free_seapp_context(cur);
+ goto oom;
+ }
+ if (strstr(value, ":")) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else if (!strcasecmp(name, "name")) {
+ if (cur->name.str) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ cur->name.str = strdup(value);
+ if (!cur->name.str) {
+ free_seapp_context(cur);
+ goto oom;
+ }
+ cur->name.len = strlen(cur->name.str);
+ if (cur->name.str[cur->name.len-1] == '*')
+ cur->name.is_prefix = 1;
+ } else if (!strcasecmp(name, "domain")) {
+ if (cur->domain) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ cur->domain = strdup(value);
+ if (!cur->domain) {
+ free_seapp_context(cur);
+ goto oom;
+ }
+ } else if (!strcasecmp(name, "type")) {
+ if (cur->type) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ cur->type = strdup(value);
+ if (!cur->type) {
+ free_seapp_context(cur);
+ goto oom;
+ }
+ } else if (!strcasecmp(name, "levelFromUid")) {
+ if (cur->levelFrom) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ if (!strcasecmp(value, "true"))
+ cur->levelFrom = LEVELFROM_APP;
+ else if (!strcasecmp(value, "false"))
+ cur->levelFrom = LEVELFROM_NONE;
+ else {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else if (!strcasecmp(name, "levelFrom")) {
+ if (cur->levelFrom) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ if (!strcasecmp(value, "none"))
+ cur->levelFrom = LEVELFROM_NONE;
+ else if (!strcasecmp(value, "app"))
+ cur->levelFrom = LEVELFROM_APP;
+ else if (!strcasecmp(value, "user"))
+ cur->levelFrom = LEVELFROM_USER;
+ else if (!strcasecmp(value, "all"))
+ cur->levelFrom = LEVELFROM_ALL;
+ else {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else if (!strcasecmp(name, "level")) {
+ if (cur->level) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ cur->level = strdup(value);
+ if (!cur->level) {
+ free_seapp_context(cur);
+ goto oom;
+ }
+ } else if (!strcasecmp(name, "path")) {
+ if (cur->path.str) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ cur->path.str = strdup(value);
+ if (!cur->path.str) {
+ free_seapp_context(cur);
+ goto oom;
+ }
+ cur->path.len = strlen(cur->path.str);
+ if (cur->path.str[cur->path.len-1] == '*')
+ cur->path.is_prefix = 1;
+ } else if (!strcasecmp(name, "isPrivApp")) {
+ cur->isPrivAppSet = true;
+ if (!strcasecmp(value, "true"))
+ cur->isPrivApp = true;
+ else if (!strcasecmp(value, "false"))
+ cur->isPrivApp = false;
+ else {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else if (!strcasecmp(name, "minTargetSdkVersion")) {
+ cur->minTargetSdkVersion = get_minTargetSdkVersion(value);
+ if (cur->minTargetSdkVersion < 0) {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else if (!strcasecmp(name, "fromRunAs")) {
+ if (!strcasecmp(value, "true"))
+ cur->fromRunAs = true;
+ else if (!strcasecmp(value, "false"))
+ cur->fromRunAs = false;
+ else {
+ free_seapp_context(cur);
+ goto err;
+ }
+ } else {
+ free_seapp_context(cur);
+ goto err;
+ }
+
+ token = strtok_r(NULL, " \t", &saveptr);
+ if (!token)
+ break;
+ }
+
+ if (cur->name.str &&
+ (!cur->seinfo || !strcmp(cur->seinfo, "default"))) {
+ selinux_log(SELINUX_ERROR, "%s: No specific seinfo value specified with name=\"%s\", on line %u: insecure configuration!\n",
+ seapp_contexts_files[i], cur->name.str, lineno);
+ free_seapp_context(cur);
+ goto err;
+ }
+
+ seapp_contexts[nspec] = cur;
+ nspec++;
+ lineno++;
+ }
+ fclose(fp);
+ fp = NULL;
+ }
+
+ qsort(seapp_contexts, nspec, sizeof(struct seapp_context *),
+ seapp_context_cmp);
+
+ if (seapp_contexts_dup)
+ goto err_no_log;
+
+#if DEBUG
+ {
+ int i;
+ for (i = 0; i < nspec; i++) {
+ cur = seapp_contexts[i];
+ selinux_log(SELINUX_INFO, "%s: isSystemServer=%s isEphemeralApp=%s isV2App=%s isOwner=%s user=%s seinfo=%s "
+ "name=%s path=%s isPrivApp=%s minTargetSdkVersion=%d fromRunAs=%s -> domain=%s type=%s level=%s levelFrom=%s",
+ __FUNCTION__,
+ cur->isSystemServer ? "true" : "false",
+ cur->isEphemeralAppSet ? (cur->isEphemeralApp ? "true" : "false") : "null",
+ cur->isV2AppSet ? (cur->isV2App ? "true" : "false") : "null",
+ cur->isOwnerSet ? (cur->isOwner ? "true" : "false") : "null",
+ cur->user.str,
+ cur->seinfo, cur->name.str, cur->path.str,
+ cur->isPrivAppSet ? (cur->isPrivApp ? "true" : "false") : "null",
+ cur->minTargetSdkVersion,
+ cur->fromRunAs ? "true" : "false",
+ cur->domain, cur->type, cur->level,
+ levelFromName[cur->levelFrom]);
+ }
+ }
+#endif
+
+ ret = 0;
+
+out:
+ if (fp) {
+ fclose(fp);
+ }
+ return ret;
+
+err:
+ selinux_log(SELINUX_ERROR, "%s: Invalid entry on line %u\n",
+ seapp_contexts_files[i], lineno);
+err_no_log:
+ free_seapp_contexts();
+ ret = -1;
+ goto out;
+oom:
+ selinux_log(SELINUX_ERROR,
+ "%s: Out of memory\n", __FUNCTION__);
+ free_seapp_contexts();
+ ret = -1;
+ goto out;
+}
+
+
+static void seapp_context_init(void)
+{
+ selinux_android_seapp_context_reload();
+}
+
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+/*
+ * Max id that can be mapped to category set uniquely
+ * using the current scheme.
+ */
+#define CAT_MAPPING_MAX_ID (0x1<<16)
+
+enum seapp_kind {
+ SEAPP_TYPE,
+ SEAPP_DOMAIN
+};
+
+#define PRIVILEGED_APP_STR ":privapp"
+#define EPHEMERAL_APP_STR ":ephemeralapp"
+#define V2_APP_STR ":v2"
+#define TARGETSDKVERSION_STR ":targetSdkVersion="
+#define FROM_RUNAS_STR ":fromRunAs"
+static int32_t get_app_targetSdkVersion(const char *seinfo)
+{
+ char *substr = strstr(seinfo, TARGETSDKVERSION_STR);
+ long targetSdkVersion;
+ char *endptr;
+ if (substr != NULL) {
+ substr = substr + strlen(TARGETSDKVERSION_STR);
+ if (substr != NULL) {
+ targetSdkVersion = strtol(substr, &endptr, 10);
+ if (('\0' != *endptr && ':' != *endptr)
+ || (targetSdkVersion < 0) || (targetSdkVersion > INT32_MAX)) {
+ return -1; /* malformed targetSdkVersion value in seinfo */
+ } else {
+ return (int32_t) targetSdkVersion;
+ }
+ }
+ }
+ return 0; /* default to 0 when targetSdkVersion= is not present in seinfo */
+}
+
+static int seinfo_parse(char *dest, const char *src, size_t size)
+{
+ size_t len;
+ char *p;
+
+ if ((p = strchr(src, ':')) != NULL)
+ len = p - src;
+ else
+ len = strlen(src);
+
+ if (len > size - 1)
+ return -1;
+
+ strncpy(dest, src, len);
+ dest[len] = '\0';
+
+ return 0;
+}
+
+static int seapp_context_lookup(enum seapp_kind kind,
+ uid_t uid,
+ bool isSystemServer,
+ const char *seinfo,
+ const char *pkgname,
+ const char *path,
+ context_t ctx)
+{
+ struct passwd *pwd;
+ bool isOwner;
+ const char *username = NULL;
+ struct seapp_context *cur = NULL;
+ int i;
+ uid_t userid;
+ uid_t appid;
+ bool isPrivApp = false;
+ bool isEphemeralApp = false;
+ int32_t targetSdkVersion = 0;
+ bool isV2App = false;
+ bool fromRunAs = false;
+ char parsedseinfo[BUFSIZ];
+
+ __selinux_once(once, seapp_context_init);
+
+ if (seinfo) {
+ if (seinfo_parse(parsedseinfo, seinfo, BUFSIZ))
+ goto err;
+ isPrivApp = strstr(seinfo, PRIVILEGED_APP_STR) ? true : false;
+ isEphemeralApp = strstr(seinfo, EPHEMERAL_APP_STR) ? true : false;
+ isV2App = strstr(seinfo, V2_APP_STR) ? true : false;
+ fromRunAs = strstr(seinfo, FROM_RUNAS_STR) ? true : false;
+ targetSdkVersion = get_app_targetSdkVersion(seinfo);
+ if (targetSdkVersion < 0) {
+ selinux_log(SELINUX_ERROR,
+ "%s: Invalid targetSdkVersion passed for app with uid %d, seinfo %s, name %s\n",
+ __FUNCTION__, uid, seinfo, pkgname);
+ goto err;
+ }
+ seinfo = parsedseinfo;
+ }
+
+ userid = uid / AID_USER;
+ isOwner = (userid == 0);
+ appid = uid % AID_USER;
+ if (appid < AID_APP) {
+ /*
+ * This code is Android specific, bionic guarantees that
+ * calls to non-reentrant getpwuid() are thread safe.
+ */
+#ifndef __BIONIC__
+#warning "This code assumes that getpwuid is thread safe, only true with Bionic!"
+#endif
+ pwd = getpwuid(appid);
+ if (!pwd)
+ goto err;
+
+ username = pwd->pw_name;
+
+ } else if (appid < AID_ISOLATED_START) {
+ username = "_app";
+ appid -= AID_APP;
+ } else {
+ username = "_isolated";
+ appid -= AID_ISOLATED_START;
+ }
+
+ if (appid >= CAT_MAPPING_MAX_ID || userid >= CAT_MAPPING_MAX_ID)
+ goto err;
+
+ for (i = 0; i < nspec; i++) {
+ cur = seapp_contexts[i];
+
+ if (cur->isSystemServer != isSystemServer)
+ continue;
+
+ if (cur->isEphemeralAppSet && cur->isEphemeralApp != isEphemeralApp)
+ continue;
+
+ if (cur->isV2AppSet && cur->isV2App != isV2App)
+ continue;
+
+ if (cur->isOwnerSet && cur->isOwner != isOwner)
+ continue;
+
+ if (cur->user.str) {
+ if (cur->user.is_prefix) {
+ if (strncasecmp(username, cur->user.str, cur->user.len-1))
+ continue;
+ } else {
+ if (strcasecmp(username, cur->user.str))
+ continue;
+ }
+ }
+
+ if (cur->seinfo) {
+ if (!seinfo || strcasecmp(seinfo, cur->seinfo))
+ continue;
+ }
+
+ if (cur->name.str) {
+ if(!pkgname)
+ continue;
+
+ if (cur->name.is_prefix) {
+ if (strncasecmp(pkgname, cur->name.str, cur->name.len-1))
+ continue;
+ } else {
+ if (strcasecmp(pkgname, cur->name.str))
+ continue;
+ }
+ }
+
+ if (cur->isPrivAppSet && cur->isPrivApp != isPrivApp)
+ continue;
+
+ if (cur->minTargetSdkVersion > targetSdkVersion)
+ continue;
+
+ if (cur->fromRunAs != fromRunAs)
+ continue;
+
+ if (cur->path.str) {
+ if (!path)
+ continue;
+
+ if (cur->path.is_prefix) {
+ if (strncmp(path, cur->path.str, cur->path.len-1))
+ continue;
+ } else {
+ if (strcmp(path, cur->path.str))
+ continue;
+ }
+ }
+
+ if (kind == SEAPP_TYPE && !cur->type)
+ continue;
+ else if (kind == SEAPP_DOMAIN && !cur->domain)
+ continue;
+
+ if (kind == SEAPP_TYPE) {
+ if (context_type_set(ctx, cur->type))
+ goto oom;
+ } else if (kind == SEAPP_DOMAIN) {
+ if (context_type_set(ctx, cur->domain))
+ goto oom;
+ }
+
+ if (cur->levelFrom != LEVELFROM_NONE) {
+ char level[255];
+ switch (cur->levelFrom) {
+ case LEVELFROM_APP:
+ snprintf(level, sizeof level, "s0:c%u,c%u",
+ appid & 0xff,
+ 256 + (appid>>8 & 0xff));
+ break;
+ case LEVELFROM_USER:
+ snprintf(level, sizeof level, "s0:c%u,c%u",
+ 512 + (userid & 0xff),
+ 768 + (userid>>8 & 0xff));
+ break;
+ case LEVELFROM_ALL:
+ snprintf(level, sizeof level, "s0:c%u,c%u,c%u,c%u",
+ appid & 0xff,
+ 256 + (appid>>8 & 0xff),
+ 512 + (userid & 0xff),
+ 768 + (userid>>8 & 0xff));
+ break;
+ default:
+ goto err;
+ }
+ if (context_range_set(ctx, level))
+ goto oom;
+ } else if (cur->level) {
+ if (context_range_set(ctx, cur->level))
+ goto oom;
+ }
+
+ break;
+ }
+
+ if (kind == SEAPP_DOMAIN && i == nspec) {
+ /*
+ * No match.
+ * Fail to prevent staying in the zygote's context.
+ */
+ selinux_log(SELINUX_ERROR,
+ "%s: No match for app with uid %d, seinfo %s, name %s\n",
+ __FUNCTION__, uid, seinfo, pkgname);
+
+ if (security_getenforce() == 1)
+ goto err;
+ }
+
+ return 0;
+err:
+ return -1;
+oom:
+ return -2;
+}
+
+int selinux_android_setfilecon(const char *pkgdir,
+ const char *pkgname,
+ const char *seinfo,
+ uid_t uid)
+{
+ char *orig_ctx_str = NULL;
+ char *ctx_str = NULL;
+ context_t ctx = NULL;
+ int rc = -1;
+
+ if (is_selinux_enabled() <= 0)
+ return 0;
+
+ rc = getfilecon(pkgdir, &ctx_str);
+ if (rc < 0)
+ goto err;
+
+ ctx = context_new(ctx_str);
+ orig_ctx_str = ctx_str;
+ if (!ctx)
+ goto oom;
+
+ rc = seapp_context_lookup(SEAPP_TYPE, uid, 0, seinfo, pkgname, NULL, ctx);
+ if (rc == -1)
+ goto err;
+ else if (rc == -2)
+ goto oom;
+
+ ctx_str = context_str(ctx);
+ if (!ctx_str)
+ goto oom;
+
+ rc = security_check_context(ctx_str);
+ if (rc < 0)
+ goto err;
+
+ if (strcmp(ctx_str, orig_ctx_str)) {
+ rc = setfilecon(pkgdir, ctx_str);
+ if (rc < 0)
+ goto err;
+ }
+
+ rc = 0;
+out:
+ freecon(orig_ctx_str);
+ context_free(ctx);
+ return rc;
+err:
+ selinux_log(SELINUX_ERROR, "%s: Error setting context for pkgdir %s, uid %d: %s\n",
+ __FUNCTION__, pkgdir, uid, strerror(errno));
+ rc = -1;
+ goto out;
+oom:
+ selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __FUNCTION__);
+ rc = -1;
+ goto out;
+}
+
+int selinux_android_setcon(const char *con)
+{
+ int ret = setcon(con);
+ if (ret)
+ return ret;
+ /*
+ System properties must be reinitialized after setcon() otherwise the
+ previous property files will be leaked since mmap()'ed regions are not
+ closed as a result of setcon().
+ */
+ return __system_properties_init();
+}
+
+int selinux_android_setcontext(uid_t uid,
+ bool isSystemServer,
+ const char *seinfo,
+ const char *pkgname)
+{
+ char *orig_ctx_str = NULL, *ctx_str;
+ context_t ctx = NULL;
+ int rc = -1;
+
+ if (is_selinux_enabled() <= 0)
+ return 0;
+
+ rc = getcon(&ctx_str);
+ if (rc)
+ goto err;
+
+ ctx = context_new(ctx_str);
+ orig_ctx_str = ctx_str;
+ if (!ctx)
+ goto oom;
+
+ rc = seapp_context_lookup(SEAPP_DOMAIN, uid, isSystemServer, seinfo, pkgname, NULL, ctx);
+ if (rc == -1)
+ goto err;
+ else if (rc == -2)
+ goto oom;
+
+ ctx_str = context_str(ctx);
+ if (!ctx_str)
+ goto oom;
+
+ rc = security_check_context(ctx_str);
+ if (rc < 0)
+ goto err;
+
+ if (strcmp(ctx_str, orig_ctx_str)) {
+ rc = selinux_android_setcon(ctx_str);
+ if (rc < 0)
+ goto err;
+ }
+
+ rc = 0;
+out:
+ freecon(orig_ctx_str);
+ context_free(ctx);
+ avc_netlink_close();
+ return rc;
+err:
+ if (isSystemServer)
+ selinux_log(SELINUX_ERROR,
+ "%s: Error setting context for system server: %s\n",
+ __FUNCTION__, strerror(errno));
+ else
+ selinux_log(SELINUX_ERROR,
+ "%s: Error setting context for app with uid %d, seinfo %s: %s\n",
+ __FUNCTION__, uid, seinfo, strerror(errno));
+
+ rc = -1;
+ goto out;
+oom:
+ selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __FUNCTION__);
+ rc = -1;
+ goto out;
+}
+
+static struct selabel_handle *fc_sehandle = NULL;
+
+static void file_context_init(void)
+{
+ if (!fc_sehandle)
+ fc_sehandle = selinux_android_file_context_handle();
+}
+
+static pthread_once_t fc_once = PTHREAD_ONCE_INIT;
+
+#define PKGTAB_SIZE 256
+static struct pkg_info *pkgTab[PKGTAB_SIZE];
+
+static unsigned int pkghash(const char *pkgname)
+{
+ unsigned int h = 7;
+ for (; *pkgname; pkgname++) {
+ h = h * 31 + *pkgname;
+ }
+ return h & (PKGTAB_SIZE - 1);
+}
+
+static bool pkg_parse_callback(pkg_info *info, void *userdata) {
+
+ (void) userdata;
+
+ unsigned int hash = pkghash(info->name);
+ if (pkgTab[hash])
+ info->private_data = pkgTab[hash];
+ pkgTab[hash] = info;
+ return true;
+}
+
+static void package_info_init(void)
+{
+
+ bool rc = packagelist_parse(pkg_parse_callback, NULL);
+ if (!rc) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could NOT parse package list\n");
+ return;
+ }
+
+#if DEBUG
+ {
+ unsigned int hash, buckets, entries, chainlen, longestchain;
+ struct pkg_info *info = NULL;
+
+ buckets = entries = longestchain = 0;
+ for (hash = 0; hash < PKGTAB_SIZE; hash++) {
+ if (pkgTab[hash]) {
+ buckets++;
+ chainlen = 0;
+ for (info = pkgTab[hash]; info; info = (pkg_info *)info->private_data) {
+ chainlen++;
+ selinux_log(SELINUX_INFO, "%s: name=%s uid=%u debuggable=%s dataDir=%s seinfo=%s\n",
+ __FUNCTION__,
+ info->name, info->uid, info->debuggable ? "true" : "false", info->data_dir, info->seinfo);
+ }
+ entries += chainlen;
+ if (longestchain < chainlen)
+ longestchain = chainlen;
+ }
+ }
+ selinux_log(SELINUX_INFO, "SELinux: %d pkg entries and %d/%d buckets used, longest chain %d\n", entries, buckets, PKGTAB_SIZE, longestchain);
+ }
+#endif
+
+}
+
+static pthread_once_t pkg_once = PTHREAD_ONCE_INIT;
+
+struct pkg_info *package_info_lookup(const char *name)
+{
+ struct pkg_info *info;
+ unsigned int hash;
+
+ __selinux_once(pkg_once, package_info_init);
+
+ hash = pkghash(name);
+ for (info = pkgTab[hash]; info; info = (pkg_info *)info->private_data) {
+ if (!strcmp(name, info->name))
+ return info;
+ }
+ return NULL;
+}
+
+/* The contents of these paths are encrypted on FBE devices until user
+ * credentials are presented (filenames inside are mangled), so we need
+ * to delay restorecon of those until vold explicitly requests it. */
+// NOTE: these paths need to be kept in sync with vold
+#define DATA_SYSTEM_CE_PREFIX "/data/system_ce/"
+#define DATA_MISC_CE_PREFIX "/data/misc_ce/"
+
+/* The path prefixes of package data directories. */
+#define DATA_DATA_PATH "/data/data"
+#define DATA_USER_PATH "/data/user"
+#define DATA_USER_DE_PATH "/data/user_de"
+#define EXPAND_USER_PATH "/mnt/expand/\?\?\?\?\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?\?\?\?\?\?\?\?\?/user"
+#define EXPAND_USER_DE_PATH "/mnt/expand/\?\?\?\?\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?-\?\?\?\?\?\?\?\?\?\?\?\?/user_de"
+#define DATA_DATA_PREFIX DATA_DATA_PATH "/"
+#define DATA_USER_PREFIX DATA_USER_PATH "/"
+#define DATA_USER_DE_PREFIX DATA_USER_DE_PATH "/"
+
+static int pkgdir_selabel_lookup(const char *pathname,
+ const char *seinfo,
+ uid_t uid,
+ char **secontextp)
+{
+ char *pkgname = NULL, *end = NULL;
+ struct pkg_info *info = NULL;
+ char *secontext = *secontextp;
+ context_t ctx = NULL;
+ int rc = 0;
+
+ /* Skip directory prefix before package name. */
+ if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1)) {
+ pathname += sizeof(DATA_DATA_PREFIX) - 1;
+ } else if (!strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1)) {
+ pathname += sizeof(DATA_USER_PREFIX) - 1;
+ while (isdigit(*pathname))
+ pathname++;
+ if (*pathname == '/')
+ pathname++;
+ else
+ return 0;
+ } else if (!strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1)) {
+ pathname += sizeof(DATA_USER_DE_PREFIX) - 1;
+ while (isdigit(*pathname))
+ pathname++;
+ if (*pathname == '/')
+ pathname++;
+ else
+ return 0;
+ } else if (!fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
+ pathname += sizeof(EXPAND_USER_PATH);
+ while (isdigit(*pathname))
+ pathname++;
+ if (*pathname == '/')
+ pathname++;
+ else
+ return 0;
+ } else if (!fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
+ pathname += sizeof(EXPAND_USER_DE_PATH);
+ while (isdigit(*pathname))
+ pathname++;
+ if (*pathname == '/')
+ pathname++;
+ else
+ return 0;
+ } else
+ return 0;
+
+ if (!(*pathname))
+ return 0;
+
+ pkgname = strdup(pathname);
+ if (!pkgname)
+ return -1;
+
+ for (end = pkgname; *end && *end != '/'; end++)
+ ;
+ pathname = end;
+ if (*end)
+ pathname++;
+ *end = '\0';
+
+ if (!seinfo) {
+ info = package_info_lookup(pkgname);
+ if (!info) {
+ selinux_log(SELINUX_WARNING, "SELinux: Could not look up information for package %s, cannot restorecon %s.\n",
+ pkgname, pathname);
+ free(pkgname);
+ return -1;
+ }
+ }
+
+ ctx = context_new(secontext);
+ if (!ctx)
+ goto err;
+
+ rc = seapp_context_lookup(SEAPP_TYPE, info ? info->uid : uid, 0,
+ info ? info->seinfo : seinfo, info ? info->name : pkgname, pathname, ctx);
+ if (rc < 0)
+ goto err;
+
+ secontext = context_str(ctx);
+ if (!secontext)
+ goto err;
+
+ if (!strcmp(secontext, *secontextp))
+ goto out;
+
+ rc = security_check_context(secontext);
+ if (rc < 0)
+ goto err;
+
+ freecon(*secontextp);
+ *secontextp = strdup(secontext);
+ if (!(*secontextp))
+ goto err;
+
+ rc = 0;
+
+out:
+ free(pkgname);
+ context_free(ctx);
+ return rc;
+err:
+ selinux_log(SELINUX_ERROR, "%s: Error looking up context for path %s, pkgname %s, seinfo %s, uid %u: %s\n",
+ __FUNCTION__, pathname, pkgname, info->seinfo, info->uid, strerror(errno));
+ rc = -1;
+ goto out;
+}
+
+#define RESTORECON_PARTIAL_MATCH_DIGEST "security.sehash"
+
+static int restorecon_sb(const char *pathname, const struct stat *sb,
+ bool nochange, bool verbose,
+ const char *seinfo, uid_t uid)
+{
+ char *secontext = NULL;
+ char *oldsecontext = NULL;
+ int rc = 0;
+
+ if (selabel_lookup(fc_sehandle, &secontext, pathname, sb->st_mode) < 0)
+ return 0; /* no match, but not an error */
+
+ if (lgetfilecon(pathname, &oldsecontext) < 0)
+ goto err;
+
+ /*
+ * For subdirectories of /data/data or /data/user, we ignore selabel_lookup()
+ * and use pkgdir_selabel_lookup() instead. Files within those directories
+ * have different labeling rules, based off of /seapp_contexts, and
+ * installd is responsible for managing these labels instead of init.
+ */
+ if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) ||
+ !strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
+ !strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||
+ !fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME) ||
+ !fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME)) {
+ if (pkgdir_selabel_lookup(pathname, seinfo, uid, &secontext) < 0)
+ goto err;
+ }
+
+ if (strcmp(oldsecontext, secontext) != 0) {
+ if (verbose)
+ selinux_log(SELINUX_INFO,
+ "SELinux: Relabeling %s from %s to %s.\n", pathname, oldsecontext, secontext);
+ if (!nochange) {
+ if (lsetfilecon(pathname, secontext) < 0)
+ goto err;
+ }
+ }
+
+ rc = 0;
+
+out:
+ freecon(oldsecontext);
+ freecon(secontext);
+ return rc;
+
+err:
+ selinux_log(SELINUX_ERROR,
+ "SELinux: Could not set context for %s: %s\n",
+ pathname, strerror(errno));
+ rc = -1;
+ goto out;
+}
+
+#define SYS_PATH "/sys"
+#define SYS_PREFIX SYS_PATH "/"
+
+struct dir_hash_node {
+ char* path;
+ uint8_t digest[SHA1_HASH_SIZE];
+ struct dir_hash_node *next;
+};
+
+// Returns true if the digest of all partial matched contexts is the same as the one
+// saved by setxattr. Otherwise returns false and constructs a dir_hash_node with the
+// newly calculated digest.
+static bool check_context_match_for_dir(const char *pathname, struct dir_hash_node **new_node,
+ bool force, int error) {
+ uint8_t read_digest[SHA1_HASH_SIZE];
+ ssize_t read_size = getxattr(pathname, RESTORECON_PARTIAL_MATCH_DIGEST,
+ read_digest, SHA1_HASH_SIZE);
+ uint8_t calculated_digest[SHA1_HASH_SIZE];
+ bool status = selabel_hash_all_partial_matches(fc_sehandle, pathname,
+ calculated_digest);
+
+ if (!new_node) {
+ return false;
+ }
+ *new_node = NULL;
+ if (!force && status && read_size == SHA1_HASH_SIZE &&
+ memcmp(read_digest, calculated_digest, SHA1_HASH_SIZE) == 0) {
+ return true;
+ }
+
+ // Save the digest of all matched contexts for the current directory.
+ if (!error && status) {
+ *new_node = calloc(1, sizeof(struct dir_hash_node));
+ if (*new_node == NULL) {
+ selinux_log(SELINUX_ERROR,
+ "SELinux: %s: Out of memory\n", __func__);
+ return false;
+ }
+
+ (*new_node)->path = strdup(pathname);
+ if ((*new_node)->path == NULL) {
+ selinux_log(SELINUX_ERROR,
+ "SELinux: %s: Out of memory\n", __func__);
+ free(*new_node);
+ *new_node = NULL;
+ return false;
+ }
+ memcpy((*new_node)->digest, calculated_digest, SHA1_HASH_SIZE);
+ (*new_node)->next = NULL;
+ }
+
+ return false;
+}
+
+static int selinux_android_restorecon_common(const char* pathname_orig,
+ const char *seinfo,
+ uid_t uid,
+ unsigned int flags)
+{
+ bool nochange = (flags & SELINUX_ANDROID_RESTORECON_NOCHANGE) ? true : false;
+ bool verbose = (flags & SELINUX_ANDROID_RESTORECON_VERBOSE) ? true : false;
+ bool recurse = (flags & SELINUX_ANDROID_RESTORECON_RECURSE) ? true : false;
+ bool force = (flags & SELINUX_ANDROID_RESTORECON_FORCE) ? true : false;
+ bool datadata = (flags & SELINUX_ANDROID_RESTORECON_DATADATA) ? true : false;
+ bool skipce = (flags & SELINUX_ANDROID_RESTORECON_SKIPCE) ? true : false;
+ bool cross_filesystems = (flags & SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS) ? true : false;
+ bool issys;
+ bool setrestoreconlast = true;
+ struct stat sb;
+ struct statfs sfsb;
+ FTS *fts;
+ FTSENT *ftsent;
+ char *pathname = NULL, *pathdnamer = NULL, *pathdname, *pathbname;
+ char * paths[2] = { NULL , NULL };
+ int ftsflags = FTS_NOCHDIR | FTS_PHYSICAL;
+ int error, sverrno;
+ struct dir_hash_node *current = NULL;
+ struct dir_hash_node *head = NULL;
+
+ if (!cross_filesystems) {
+ ftsflags |= FTS_XDEV;
+ }
+
+ if (is_selinux_enabled() <= 0)
+ return 0;
+
+ __selinux_once(fc_once, file_context_init);
+
+ if (!fc_sehandle)
+ return 0;
+
+ /*
+ * Convert passed-in pathname to canonical pathname by resolving realpath of
+ * containing dir, then appending last component name.
+ */
+ pathbname = basename(pathname_orig);
+ if (!strcmp(pathbname, "/") || !strcmp(pathbname, ".") || !strcmp(pathbname, "..")) {
+ pathname = realpath(pathname_orig, NULL);
+ if (!pathname)
+ goto realpatherr;
+ } else {
+ pathdname = dirname(pathname_orig);
+ pathdnamer = realpath(pathdname, NULL);
+ if (!pathdnamer)
+ goto realpatherr;
+ if (!strcmp(pathdnamer, "/"))
+ error = asprintf(&pathname, "/%s", pathbname);
+ else
+ error = asprintf(&pathname, "%s/%s", pathdnamer, pathbname);
+ if (error < 0)
+ goto oom;
+ }
+
+ paths[0] = pathname;
+ issys = (!strcmp(pathname, SYS_PATH)
+ || !strncmp(pathname, SYS_PREFIX, sizeof(SYS_PREFIX)-1)) ? true : false;
+
+ if (!recurse) {
+ if (lstat(pathname, &sb) < 0) {
+ error = -1;
+ goto cleanup;
+ }
+
+ error = restorecon_sb(pathname, &sb, nochange, verbose, seinfo, uid);
+ goto cleanup;
+ }
+
+ /*
+ * Ignore saved partial match digest on /data/data or /data/user
+ * since their labeling is based on seapp_contexts and seinfo
+ * assignments rather than file_contexts and is managed by
+ * installd rather than init.
+ */
+ if (!strncmp(pathname, DATA_DATA_PREFIX, sizeof(DATA_DATA_PREFIX)-1) ||
+ !strncmp(pathname, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
+ !strncmp(pathname, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||
+ !fnmatch(EXPAND_USER_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME) ||
+ !fnmatch(EXPAND_USER_DE_PATH, pathname, FNM_LEADING_DIR|FNM_PATHNAME))
+ setrestoreconlast = false;
+
+ /* Also ignore on /sys since it is regenerated on each boot regardless. */
+ if (issys)
+ setrestoreconlast = false;
+
+ /* Ignore files on in-memory filesystems */
+ if (statfs(pathname, &sfsb) == 0) {
+ if (sfsb.f_type == RAMFS_MAGIC || sfsb.f_type == TMPFS_MAGIC)
+ setrestoreconlast = false;
+ }
+
+ fts = fts_open(paths, ftsflags, NULL);
+ if (!fts) {
+ error = -1;
+ goto cleanup;
+ }
+
+ error = 0;
+ while ((ftsent = fts_read(fts)) != NULL) {
+ switch (ftsent->fts_info) {
+ case FTS_DC:
+ selinux_log(SELINUX_ERROR,
+ "SELinux: Directory cycle on %s.\n", ftsent->fts_path);
+ errno = ELOOP;
+ error = -1;
+ goto out;
+ case FTS_DP:
+ continue;
+ case FTS_DNR:
+ selinux_log(SELINUX_ERROR,
+ "SELinux: Could not read %s: %s.\n", ftsent->fts_path, strerror(errno));
+ fts_set(fts, ftsent, FTS_SKIP);
+ continue;
+ case FTS_NS:
+ selinux_log(SELINUX_ERROR,
+ "SELinux: Could not stat %s: %s.\n", ftsent->fts_path, strerror(errno));
+ fts_set(fts, ftsent, FTS_SKIP);
+ continue;
+ case FTS_ERR:
+ selinux_log(SELINUX_ERROR,
+ "SELinux: Error on %s: %s.\n", ftsent->fts_path, strerror(errno));
+ fts_set(fts, ftsent, FTS_SKIP);
+ continue;
+ case FTS_D:
+ if (issys && !selabel_partial_match(fc_sehandle, ftsent->fts_path)) {
+ fts_set(fts, ftsent, FTS_SKIP);
+ continue;
+ }
+
+ if (setrestoreconlast) {
+ struct dir_hash_node* new_node = NULL;
+ if (check_context_match_for_dir(ftsent->fts_path, &new_node, force, error)) {
+ selinux_log(SELINUX_INFO,
+ "SELinux: Skipping restorecon on directory(%s)\n",
+ ftsent->fts_path);
+ fts_set(fts, ftsent, FTS_SKIP);
+ continue;
+ }
+ if (new_node) {
+ if (!current) {
+ current = new_node;
+ head = current;
+ } else {
+ current->next = new_node;
+ current = current->next;
+ }
+ }
+ }
+
+ if (skipce &&
+ (!strncmp(ftsent->fts_path, DATA_SYSTEM_CE_PREFIX, sizeof(DATA_SYSTEM_CE_PREFIX)-1) ||
+ !strncmp(ftsent->fts_path, DATA_MISC_CE_PREFIX, sizeof(DATA_MISC_CE_PREFIX)-1))) {
+ // Don't label anything below this directory.
+ fts_set(fts, ftsent, FTS_SKIP);
+ // but fall through and make sure we label the directory itself
+ }
+
+ if (!datadata &&
+ (!strcmp(ftsent->fts_path, DATA_DATA_PATH) ||
+ !strncmp(ftsent->fts_path, DATA_USER_PREFIX, sizeof(DATA_USER_PREFIX)-1) ||
+ !strncmp(ftsent->fts_path, DATA_USER_DE_PREFIX, sizeof(DATA_USER_DE_PREFIX)-1) ||
+ !fnmatch(EXPAND_USER_PATH, ftsent->fts_path, FNM_LEADING_DIR|FNM_PATHNAME) ||
+ !fnmatch(EXPAND_USER_DE_PATH, ftsent->fts_path, FNM_LEADING_DIR|FNM_PATHNAME))) {
+ // Don't label anything below this directory.
+ fts_set(fts, ftsent, FTS_SKIP);
+ // but fall through and make sure we label the directory itself
+ }
+ /* fall through */
+ default:
+ error |= restorecon_sb(ftsent->fts_path, ftsent->fts_statp, nochange, verbose, seinfo, uid);
+ break;
+ }
+ }
+
+ // Labeling successful. Write the partial match digests for subdirectories.
+ // TODO: Write the digest upon FTS_DP if no error occurs in its descents.
+ if (setrestoreconlast && !nochange && !error) {
+ current = head;
+ while (current != NULL) {
+ if (setxattr(current->path, RESTORECON_PARTIAL_MATCH_DIGEST, current->digest,
+ SHA1_HASH_SIZE, 0) < 0) {
+ selinux_log(SELINUX_ERROR,
+ "SELinux: setxattr failed: %s: %s\n",
+ current->path,
+ strerror(errno));
+ }
+ current = current->next;
+ }
+ }
+
+out:
+ sverrno = errno;
+ (void) fts_close(fts);
+ errno = sverrno;
+cleanup:
+ free(pathdnamer);
+ free(pathname);
+ current = head;
+ while (current != NULL) {
+ struct dir_hash_node *next = current->next;
+ free(current->path);
+ free(current);
+ current = next;
+ }
+ return error;
+oom:
+ sverrno = errno;
+ selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __FUNCTION__);
+ errno = sverrno;
+ error = -1;
+ goto cleanup;
+realpatherr:
+ sverrno = errno;
+ selinux_log(SELINUX_ERROR, "SELinux: Could not get canonical path for %s restorecon: %s.\n",
+ pathname_orig, strerror(errno));
+ errno = sverrno;
+ error = -1;
+ goto cleanup;
+}
+
+int selinux_android_restorecon(const char *file, unsigned int flags)
+{
+ return selinux_android_restorecon_common(file, NULL, -1, flags);
+}
+
+int selinux_android_restorecon_pkgdir(const char *pkgdir,
+ const char *seinfo,
+ uid_t uid,
+ unsigned int flags)
+{
+ return selinux_android_restorecon_common(pkgdir, seinfo, uid, flags | SELINUX_ANDROID_RESTORECON_DATADATA);
+}
+
+
+void selinux_android_set_sehandle(const struct selabel_handle *hndl)
+{
+ fc_sehandle = (struct selabel_handle *) hndl;
+}
+
+int selinux_android_load_policy()
+{
+ int fd = -1;
+
+ fd = open(sepolicy_file, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd < 0) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could not open %s: %s\n",
+ sepolicy_file, strerror(errno));
+ return -1;
+ }
+ int ret = selinux_android_load_policy_from_fd(fd, sepolicy_file);
+ close(fd);
+ return ret;
+}
+
+int selinux_android_load_policy_from_fd(int fd, const char *description)
+{
+ int rc;
+ struct stat sb;
+ void *map = NULL;
+ static int load_successful = 0;
+
+ /*
+ * Since updating policy at runtime has been abolished
+ * we just check whether a policy has been loaded before
+ * and return if this is the case.
+ * There is no point in reloading policy.
+ */
+ if (load_successful){
+ selinux_log(SELINUX_WARNING, "SELinux: Attempted reload of SELinux policy!/n");
+ return 0;
+ }
+
+ set_selinuxmnt(SELINUXMNT);
+ if (fstat(fd, &sb) < 0) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could not stat %s: %s\n",
+ description, strerror(errno));
+ return -1;
+ }
+ map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (map == MAP_FAILED) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could not map %s: %s\n",
+ description, strerror(errno));
+ return -1;
+ }
+
+ rc = security_load_policy(map, sb.st_size);
+ if (rc < 0) {
+ selinux_log(SELINUX_ERROR, "SELinux: Could not load policy: %s\n",
+ strerror(errno));
+ munmap(map, sb.st_size);
+ return -1;
+ }
+
+ munmap(map, sb.st_size);
+ selinux_log(SELINUX_INFO, "SELinux: Loaded policy from %s\n", description);
+ load_successful = 1;
+ return 0;
+}
+
diff --git a/libselinux/src/android/android_vendor.c b/libselinux/src/android/android_vendor.c
new file mode 100644
index 00000000..82fea4dd
--- /dev/null
+++ b/libselinux/src/android/android_vendor.c
@@ -0,0 +1,14 @@
+#include "android_common.h"
+
+int selinux_android_restorecon(const char *file __attribute__((unused)),
+ unsigned int flags __attribute__((unused)))
+{
+ selinux_log(SELINUX_ERROR, "%s: not implemented for vendor variant of libselinux\n", __FUNCTION__);
+ return -1;
+}
+
+struct selabel_handle* selinux_android_prop_context_handle(void)
+{
+ selinux_log(SELINUX_ERROR, "%s: not implemented for vendor variant of libselinux\n", __FUNCTION__);
+ return NULL;
+}
diff --git a/libselinux/src/label.c b/libselinux/src/label.c
index 8d586bda..e232eb1b 100644
--- a/libselinux/src/label.c
+++ b/libselinux/src/label.c
@@ -17,6 +17,12 @@
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#ifdef NO_FILE_BACKEND
+#define CONFIG_FILE_BACKEND(fnptr) NULL
+#else
+#define CONFIG_FILE_BACKEND(fnptr) &fnptr
+#endif
+
#ifdef NO_MEDIA_BACKEND
#define CONFIG_MEDIA_BACKEND(fnptr) NULL
#else
@@ -46,7 +52,7 @@ typedef int (*selabel_initfunc)(struct selabel_handle *rec,
unsigned nopts);
static selabel_initfunc initfuncs[] = {
- &selabel_file_init,
+ CONFIG_FILE_BACKEND(selabel_file_init),
CONFIG_MEDIA_BACKEND(selabel_media_init),
CONFIG_X_BACKEND(selabel_x_init),
CONFIG_DB_BACKEND(selabel_db_init),
@@ -143,7 +149,11 @@ static int selabel_fini(struct selabel_handle *rec,
struct selabel_lookup_rec *lr,
int translating)
{
- if (compat_validate(rec, lr, rec->spec_file, lr->lineno))
+ char *path = NULL;
+
+ if (rec->spec_files)
+ path = rec->spec_files[0];
+ if (compat_validate(rec, lr, path, lr->lineno))
return -1;
if (translating && !lr->ctx_trans &&
@@ -226,11 +236,9 @@ struct selabel_handle *selabel_open(unsigned int backend,
rec->digest = selabel_is_digest_set(opts, nopts, rec->digest);
if ((*initfuncs[backend])(rec, opts, nopts)) {
- free(rec->spec_file);
- free(rec);
+ selabel_close(rec);
rec = NULL;
}
-
out:
return rec;
}
@@ -274,6 +282,15 @@ bool selabel_partial_match(struct selabel_handle *rec, const char *key)
return rec->func_partial_match(rec, key);
}
+bool selabel_hash_all_partial_matches(struct selabel_handle *rec,
+ const char *key, uint8_t *digest) {
+ if (!rec->func_hash_all_partial_matches) {
+ return false;
+ }
+
+ return rec->func_hash_all_partial_matches(rec, key, digest);
+}
+
int selabel_lookup_best_match(struct selabel_handle *rec, char **con,
const char *key, const char **aliases, int type)
{
@@ -337,10 +354,17 @@ int selabel_digest(struct selabel_handle *rec,
void selabel_close(struct selabel_handle *rec)
{
+ size_t i;
+
+ if (rec->spec_files) {
+ for (i = 0; i < rec->spec_files_len; i++)
+ free(rec->spec_files[i]);
+ free(rec->spec_files);
+ }
if (rec->digest)
selabel_digest_fini(rec->digest);
- rec->func_close(rec);
- free(rec->spec_file);
+ if (rec->func_close)
+ rec->func_close(rec);
free(rec);
}
diff --git a/libselinux/src/label_backends_android.c b/libselinux/src/label_backends_android.c
index cb8aae26..eaca5947 100644
--- a/libselinux/src/label_backends_android.c
+++ b/libselinux/src/label_backends_android.c
@@ -45,9 +45,11 @@ static int cmp(const void *A, const void *B)
}
/*
- * Warn about duplicate specifications.
+ * Warn about duplicate specifications. Return error on different specifications.
+ * TODO: Remove duplicate specifications. Move duplicate check to after sort
+ * to improve performance.
*/
-static int nodups_specs(struct saved_data *data, const char *path)
+static int nodups_specs(struct saved_data *data)
{
int rc = 0;
unsigned int ii, jj;
@@ -58,21 +60,21 @@ static int nodups_specs(struct saved_data *data, const char *path)
for (jj = ii + 1; jj < data->nspec; jj++) {
if (!strcmp(spec_arr[jj].property_key,
curr_spec->property_key)) {
- rc = -1;
- errno = EINVAL;
if (strcmp(spec_arr[jj].lr.ctx_raw,
curr_spec->lr.ctx_raw)) {
+ rc = -1;
+ errno = EINVAL;
selinux_log
(SELINUX_ERROR,
- "%s: Multiple different specifications for %s (%s and %s).\n",
- path, curr_spec->property_key,
+ "Multiple different specifications for %s (%s and %s).\n",
+ curr_spec->property_key,
spec_arr[jj].lr.ctx_raw,
curr_spec->lr.ctx_raw);
} else {
selinux_log
- (SELINUX_ERROR,
- "%s: Multiple same specifications for %s.\n",
- path, curr_spec->property_key);
+ (SELINUX_WARNING,
+ "Multiple same specifications for %s.\n",
+ curr_spec->property_key);
}
}
}
@@ -130,33 +132,23 @@ static int process_line(struct selabel_handle *rec,
return -1;
}
}
+
+ data->nspec = ++nspec;
}
- data->nspec = ++nspec;
return 0;
}
-static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
- unsigned n)
+static int process_file(struct selabel_handle *rec, const char *path)
{
struct saved_data *data = (struct saved_data *)rec->data;
- const char *path = NULL;
- FILE *fp;
char line_buf[BUFSIZ];
unsigned int lineno, maxnspec, pass;
- int status = -1;
struct stat sb;
-
- /* Process arguments */
- while (n--)
- switch (opts[n].type) {
- case SELABEL_OPT_PATH:
- path = opts[n].value;
- break;
- }
-
- if (!path)
- return -1;
+ FILE *fp;
+ int status = -1;
+ unsigned int nspec;
+ spec_t *spec_arr;
/* Open the specification file. */
if ((fp = fopen(path, "re")) == NULL)
@@ -164,60 +156,116 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
if (fstat(fileno(fp), &sb) < 0)
goto finish;
+
errno = EINVAL;
+
if (!S_ISREG(sb.st_mode))
goto finish;
/*
- * Two passes of the specification file. First is to get the size.
- * After the first pass, the spec array is malloced to the appropriate
- * size. Second pass is to populate the spec array and check for
- * dups.
+ * Two passes per specification file. First is to get the size.
+ * After the first pass, the spec array is malloced / realloced to
+ * the appropriate size. Second pass is to populate the spec array.
*/
maxnspec = UINT_MAX / sizeof(spec_t);
for (pass = 0; pass < 2; pass++) {
- data->nspec = 0;
+ nspec = 0;
lineno = 0;
- while (fgets(line_buf, sizeof(line_buf) - 1, fp)
- && data->nspec < maxnspec) {
- if (process_line(rec, path, line_buf, pass, ++lineno)
- != 0)
- goto finish;
- }
-
- if (pass == 1) {
- status = nodups_specs(data, path);
-
- if (status)
+ while (fgets(line_buf, sizeof(line_buf) - 1, fp) &&
+ nspec < maxnspec) {
+ if (process_line(rec, path, line_buf, pass, ++lineno))
goto finish;
+ nspec++;
}
if (pass == 0) {
- if (data->nspec == 0) {
+ if (nspec == 0) {
status = 0;
goto finish;
}
- if (NULL == (data->spec_arr =
- calloc(data->nspec, sizeof(spec_t))))
+ /* grow spec array if required */
+ spec_arr = realloc(data->spec_arr,
+ (data->nspec + nspec) * sizeof(spec_t));
+ if (spec_arr == NULL)
goto finish;
- maxnspec = data->nspec;
+ memset(&spec_arr[data->nspec], 0, nspec * sizeof(spec_t));
+ data->spec_arr = spec_arr;
+ maxnspec = nspec;
rewind(fp);
}
}
- qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
-
status = digest_add_specfile(rec->digest, fp, NULL, sb.st_size, path);
+
+finish:
+ fclose(fp);
+ return status;
+}
+
+static void closef(struct selabel_handle *rec);
+
+static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
+ unsigned n)
+{
+ struct saved_data *data = (struct saved_data *)rec->data;
+ char **paths = NULL;
+ size_t num_paths = 0;
+ int status = -1;
+ size_t i;
+
+ /* Process arguments */
+ i = n;
+ while (i--) {
+ switch (opts[i].type) {
+ case SELABEL_OPT_PATH:
+ num_paths++;
+ break;
+ }
+ }
+
+ if (!num_paths)
+ return -1;
+
+ paths = calloc(num_paths, sizeof(*paths));
+ if (!paths)
+ return -1;
+
+ rec->spec_files = paths;
+ rec->spec_files_len = num_paths;
+
+ i = n;
+ while (i--) {
+ switch(opts[i].type) {
+ case SELABEL_OPT_PATH:
+ *paths = strdup(opts[i].value);
+ if (*paths == NULL)
+ goto finish;
+ paths++;
+ }
+ }
+
+ for (i = 0; i < num_paths; i++) {
+ status = process_file(rec, rec->spec_files[i]);
+ if (status)
+ goto finish;
+ }
+
+ /* warn about duplicates after all files have been processed. */
+ status = nodups_specs(data);
if (status)
goto finish;
+ qsort(data->spec_arr, data->nspec, sizeof(struct spec), cmp);
+
digest_gen_hash(rec->digest);
finish:
- fclose(fp);
+ if (status)
+ closef(rec);
+
return status;
}
@@ -230,15 +278,16 @@ static void closef(struct selabel_handle *rec)
struct spec *spec;
unsigned int i;
- for (i = 0; i < data->nspec; i++) {
- spec = &data->spec_arr[i];
- free(spec->property_key);
- free(spec->lr.ctx_raw);
- free(spec->lr.ctx_trans);
- }
+ if (data->spec_arr) {
+ for (i = 0; i < data->nspec; i++) {
+ spec = &data->spec_arr[i];
+ free(spec->property_key);
+ free(spec->lr.ctx_raw);
+ free(spec->lr.ctx_trans);
+ }
- if (data->spec_arr)
free(data->spec_arr);
+ }
free(data);
}
diff --git a/libselinux/src/label_file.c b/libselinux/src/label_file.c
index b81fd552..fd3e0130 100644
--- a/libselinux/src/label_file.c
+++ b/libselinux/src/label_file.c
@@ -39,18 +39,17 @@ static int get_stem_from_file_name(const char *const buf)
/* find the stem of a file name, returns the index into stem_arr (or -1 if
* there is no match - IE for a file in the root directory or a regex that is
- * too complex for us). Makes buf point to the text AFTER the stem. */
-static int find_stem_from_file(struct saved_data *data, const char **buf)
+ * too complex for us). */
+static int find_stem_from_file(struct saved_data *data, const char *key)
{
int i;
- int stem_len = get_stem_from_file_name(*buf);
+ int stem_len = get_stem_from_file_name(key);
if (!stem_len)
return -1;
for (i = 0; i < data->num_stems; i++) {
if (stem_len == data->stem_arr[i].len
- && !strncmp(*buf, data->stem_arr[i].buf, stem_len)) {
- *buf += stem_len;
+ && !strncmp(key, data->stem_arr[i].buf, stem_len)) {
return i;
}
}
@@ -600,6 +599,7 @@ static char *selabel_sub(struct selabel_sub *ptr, const char *src)
return NULL;
}
+#if !defined(BUILD_HOST) && !defined(ANDROID)
static int selabel_subs_init(const char *path, struct selabel_digest *digest,
struct selabel_sub **out_subs)
{
@@ -683,6 +683,7 @@ err:
}
goto out;
}
+#endif
static char *selabel_sub_key(struct saved_data *data, const char *key)
{
@@ -711,28 +712,61 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
unsigned n)
{
struct saved_data *data = (struct saved_data *)rec->data;
- const char *path = NULL;
+ size_t num_paths = 0;
+ char **path = NULL;
const char *prefix = NULL;
- int status = -1, baseonly = 0;
+ int status = -1;
+ size_t i;
+ bool baseonly = false;
+ bool path_provided;
/* Process arguments */
- while (n--)
- switch(opts[n].type) {
+ i = n;
+ while (i--)
+ switch(opts[i].type) {
case SELABEL_OPT_PATH:
- path = opts[n].value;
+ num_paths++;
break;
case SELABEL_OPT_SUBSET:
- prefix = opts[n].value;
+ prefix = opts[i].value;
break;
case SELABEL_OPT_BASEONLY:
- baseonly = !!opts[n].value;
+ baseonly = !!opts[i].value;
break;
}
+ if (!num_paths) {
+ num_paths = 1;
+ path_provided = false;
+ } else {
+ path_provided = true;
+ }
+
+ path = calloc(num_paths, sizeof(*path));
+ if (path == NULL) {
+ goto finish;
+ }
+ rec->spec_files = path;
+ rec->spec_files_len = num_paths;
+
+ if (path_provided) {
+ for (i = 0; i < n; i++) {
+ switch(opts[i].type) {
+ case SELABEL_OPT_PATH:
+ *path = strdup(opts[i].value);
+ if (*path == NULL)
+ goto finish;
+ path++;
+ break;
+ default:
+ break;
+ }
+ }
+ }
#if !defined(BUILD_HOST) && !defined(ANDROID)
char subs_file[PATH_MAX + 1];
/* Process local and distribution substitution files */
- if (!path) {
+ if (!path_provided) {
status = selabel_subs_init(
selinux_file_context_subs_dist_path(),
rec->digest, &data->dist_subs);
@@ -742,43 +776,52 @@ static int init(struct selabel_handle *rec, const struct selinux_opt *opts,
rec->digest, &data->subs);
if (status)
goto finish;
- path = selinux_file_context_path();
+ rec->spec_files[0] = strdup(selinux_file_context_path());
+ if (rec->spec_files[0] == NULL)
+ goto finish;
} else {
- snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", path);
- status = selabel_subs_init(subs_file, rec->digest,
+ for (i = 0; i < num_paths; i++) {
+ snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", rec->spec_files[i]);
+ status = selabel_subs_init(subs_file, rec->digest,
&data->dist_subs);
- if (status)
- goto finish;
- snprintf(subs_file, sizeof(subs_file), "%s.subs", path);
- status = selabel_subs_init(subs_file, rec->digest,
+ if (status)
+ goto finish;
+ snprintf(subs_file, sizeof(subs_file), "%s.subs", rec->spec_files[i]);
+ status = selabel_subs_init(subs_file, rec->digest,
&data->subs);
- if (status)
- goto finish;
+ if (status)
+ goto finish;
+ }
+ }
+#else
+ if (!path_provided) {
+ selinux_log(SELINUX_ERROR, "No path given to file labeling backend\n");
+ goto finish;
}
-
#endif
- rec->spec_file = strdup(path);
/*
- * The do detailed validation of the input and fill the spec array
+ * Do detailed validation of the input and fill the spec array
*/
- status = process_file(path, NULL, rec, prefix, rec->digest);
- if (status)
- goto finish;
-
- if (rec->validating) {
- status = nodups_specs(data, path);
+ for (i = 0; i < num_paths; i++) {
+ status = process_file(rec->spec_files[i], NULL, rec, prefix, rec->digest);
if (status)
goto finish;
+
+ if (rec->validating) {
+ status = nodups_specs(data, rec->spec_files[i]);
+ if (status)
+ goto finish;
+ }
}
if (!baseonly) {
- status = process_file(path, "homedirs", rec, prefix,
+ status = process_file(rec->spec_files[0], "homedirs", rec, prefix,
rec->digest);
if (status && errno != ENOENT)
goto finish;
- status = process_file(path, "local", rec, prefix,
+ status = process_file(rec->spec_files[0], "local", rec, prefix,
rec->digest);
if (status && errno != ENOENT)
goto finish;
@@ -806,6 +849,12 @@ static void closef(struct selabel_handle *rec)
struct stem *stem;
unsigned int i;
+ if (!data)
+ return;
+
+ /* make sure successive ->func_close() calls are harmless */
+ rec->data = NULL;
+
selabel_subs_fini(data->subs);
selabel_subs_fini(data->dist_subs);
@@ -843,22 +892,36 @@ static void closef(struct selabel_handle *rec)
free(data);
}
-static struct spec *lookup_common(struct selabel_handle *rec,
- const char *key,
- int type,
- bool partial)
+// Finds all the matches of |key| in the given context. Returns the result in
+// the allocated array and updates the match count. If match_count is NULL,
+// stops early once the 1st match is found.
+static const struct spec **lookup_all(struct selabel_handle *rec,
+ const char *key,
+ int type,
+ bool partial,
+ size_t *match_count)
{
struct saved_data *data = (struct saved_data *)rec->data;
struct spec *spec_arr = data->spec_arr;
int i, rc, file_stem;
mode_t mode = (mode_t)type;
- const char *buf;
- struct spec *ret = NULL;
char *clean_key = NULL;
const char *prev_slash, *next_slash;
unsigned int sofar = 0;
char *sub = NULL;
+ const struct spec **result = NULL;
+ if (match_count) {
+ *match_count = 0;
+ result = calloc(data->nspec, sizeof(struct spec*));
+ } else {
+ result = calloc(1, sizeof(struct spec*));
+ }
+ if (!result) {
+ selinux_log(SELINUX_ERROR, "%s: Out of memory\n", __func__);
+ goto finish;
+ }
+
if (!data->nspec) {
errno = ENOENT;
goto finish;
@@ -884,8 +947,7 @@ static struct spec *lookup_common(struct selabel_handle *rec,
if (sub)
key = sub;
- buf = key;
- file_stem = find_stem_from_file(data, &buf);
+ file_stem = find_stem_from_file(data, key);
mode &= S_IFMT;
/*
@@ -898,19 +960,34 @@ static struct spec *lookup_common(struct selabel_handle *rec,
* stem as the file AND if the spec in question has no mode
* specified or if the mode matches the file mode then we do
* a regex check */
- if ((spec->stem_id == -1 || spec->stem_id == file_stem) &&
- (!mode || !spec->mode || mode == spec->mode)) {
- if (compile_regex(data, spec, NULL) < 0)
+ bool stem_matches = spec->stem_id == -1 || spec->stem_id == file_stem;
+ // Don't check the stem if we want to find partial matches.
+ // Otherwise the case "/abc/efg/(/.*)?" will be considered
+ //a miss for "/abc".
+ if ((partial || stem_matches) &&
+ (!mode || !spec->mode || mode == spec->mode)) {
+ if (compile_regex(spec, NULL) < 0)
goto finish;
- if (spec->stem_id == -1)
- rc = regex_match(spec->regex, key, partial);
- else
- rc = regex_match(spec->regex, buf, partial);
- if (rc == REGEX_MATCH) {
- spec->matches++;
- break;
- } else if (partial && rc == REGEX_MATCH_PARTIAL)
+ rc = regex_match(spec->regex, key, partial);
+ if (rc == REGEX_MATCH || (partial && rc == REGEX_MATCH_PARTIAL)) {
+ if (rc == REGEX_MATCH) {
+ spec->matches++;
+ }
+
+ if (strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
+ errno = ENOENT;
+ goto finish;
+ }
+
+ if (match_count) {
+ result[*match_count] = spec;
+ *match_count += 1;
+ // Continue to find all the matches.
+ continue;
+ }
+ result[0] = spec;
break;
+ }
if (rc == REGEX_NO_MATCH)
continue;
@@ -921,19 +998,58 @@ static struct spec *lookup_common(struct selabel_handle *rec,
}
}
- if (i < 0 || strcmp(spec_arr[i].lr.ctx_raw, "<<none>>") == 0) {
- /* No matching specification. */
- errno = ENOENT;
- goto finish;
- }
-
- errno = 0;
- ret = &spec_arr[i];
-
finish:
free(clean_key);
free(sub);
- return ret;
+ if (result && !result[0]) {
+ free(result);
+ result = NULL;
+ }
+ return result;
+}
+
+static struct spec *lookup_common(struct selabel_handle *rec,
+ const char *key,
+ int type,
+ bool partial) {
+ const struct spec **matches = lookup_all(rec, key, type, partial, NULL);
+ if (!matches) {
+ return NULL;
+ }
+ struct spec *result = (struct spec*)matches[0];
+ free(matches);
+ return result;
+}
+
+static bool hash_all_partial_matches(struct selabel_handle *rec, const char *key, uint8_t *digest)
+{
+ assert(digest);
+
+ size_t total_matches;
+ const struct spec **matches = lookup_all(rec, key, 0, true, &total_matches);
+ if (!matches) {
+ return false;
+ }
+
+ Sha1Context context;
+ Sha1Initialise(&context);
+ size_t i;
+ for (i = 0; i < total_matches; i++) {
+ char* regex_str = matches[i]->regex_str;
+ uint32_t mode = matches[i]->mode;
+ char* ctx_raw = matches[i]->lr.ctx_raw;
+
+ Sha1Update(&context, regex_str, strlen(regex_str) + 1);
+ Sha1Update(&context, &mode, sizeof(uint32_t));
+ Sha1Update(&context, ctx_raw, strlen(ctx_raw) + 1);
+ }
+
+ SHA1_HASH sha1_hash;
+ Sha1Finalise(&context, &sha1_hash);
+ memcpy(digest, sha1_hash.bytes, SHA1_HASH_SIZE);
+
+ free(matches);
+ return true;
}
static struct selabel_lookup_rec *lookup(struct selabel_handle *rec,
@@ -1133,6 +1249,7 @@ int selabel_file_init(struct selabel_handle *rec,
rec->func_stats = &stats;
rec->func_lookup = &lookup;
rec->func_partial_match = &partial_match;
+ rec->func_hash_all_partial_matches = &hash_all_partial_matches;
rec->func_lookup_best_match = &lookup_best_match;
rec->func_cmp = &cmp;
diff --git a/libselinux/src/label_file.h b/libselinux/src/label_file.h
index 47859baf..6f4ee101 100644
--- a/libselinux/src/label_file.h
+++ b/libselinux/src/label_file.h
@@ -336,13 +336,11 @@ static inline int next_entry(void *buf, struct mmap_area *fp, size_t bytes)
return 0;
}
-static inline int compile_regex(struct saved_data *data, struct spec *spec,
- const char **errbuf)
+static inline int compile_regex(struct spec *spec, const char **errbuf)
{
char *reg_buf, *anchored_regex, *cp;
struct regex_error_data error_data;
static char regex_error_format_buffer[256];
- struct stem *stem_arr = data->stem_arr;
size_t len;
int rc;
bool regex_compiled;
@@ -379,11 +377,7 @@ static inline int compile_regex(struct saved_data *data, struct spec *spec,
return 0;
}
- /* Skip the fixed stem. */
reg_buf = spec->regex_str;
- if (spec->stem_id >= 0)
- reg_buf += stem_arr[spec->stem_id].len;
-
/* Anchor the regular expression. */
len = strlen(reg_buf);
cp = anchored_regex = malloc(len + 3);
@@ -501,7 +495,7 @@ static inline int process_line(struct selabel_handle *rec,
data->nspec++;
if (rec->validating
- && compile_regex(data, &spec_arr[nspec], &errbuf)) {
+ && compile_regex(&spec_arr[nspec], &errbuf)) {
COMPAT_LOG(SELINUX_ERROR,
"%s: line %u has invalid regex %s: %s\n",
path, lineno, regex, errbuf);
diff --git a/libselinux/src/label_internal.h b/libselinux/src/label_internal.h
index 0e020557..8add71a5 100644
--- a/libselinux/src/label_internal.h
+++ b/libselinux/src/label_internal.h
@@ -87,6 +87,8 @@ struct selabel_handle {
void (*func_close) (struct selabel_handle *h);
void (*func_stats) (struct selabel_handle *h);
bool (*func_partial_match) (struct selabel_handle *h, const char *key);
+ bool (*func_hash_all_partial_matches) (struct selabel_handle *h,
+ const char *key, uint8_t *digest);
struct selabel_lookup_rec *(*func_lookup_best_match)
(struct selabel_handle *h,
const char *key,
@@ -99,10 +101,12 @@ struct selabel_handle {
void *data;
/*
- * The main spec file used. Note for file contexts the local and/or
+ * The main spec file(s) used. Note for file contexts the local and/or
* homedirs could also have been used to resolve a context.
*/
- char *spec_file;
+ size_t spec_files_len;
+ char **spec_files;
+
/* ptr to SHA1 hash information if SELABEL_OPT_DIGEST set */
struct selabel_digest *digest;
diff --git a/libsepol/Android.bp b/libsepol/Android.bp
new file mode 100644
index 00000000..71935a90
--- /dev/null
+++ b/libsepol/Android.bp
@@ -0,0 +1,107 @@
+common_CFLAGS = [
+ "-D_GNU_SOURCE",
+ "-Wall",
+ "-Werror",
+ "-W",
+ "-Wundef",
+ "-Wshadow",
+ "-Wno-error=missing-noreturn",
+ "-Wmissing-format-attribute",
+]
+
+cc_library {
+ name: "libsepol",
+ host_supported: true,
+ cflags: common_CFLAGS,
+ srcs: [
+ "src/assertion.c",
+ "src/avrule_block.c",
+ "src/avtab.c",
+ "src/boolean_record.c",
+ "src/booleans.c",
+ "src/conditional.c",
+ "src/constraint.c",
+ "src/context.c",
+ "src/context_record.c",
+ "src/debug.c",
+ "src/ebitmap.c",
+ "src/expand.c",
+ "src/genbools.c",
+ "src/genusers.c",
+ "src/handle.c",
+ "src/hashtab.c",
+ "src/hierarchy.c",
+ "src/iface_record.c",
+ "src/interfaces.c",
+ "src/kernel_to_cil.c",
+ "src/kernel_to_common.c",
+ "src/kernel_to_conf.c",
+ "src/link.c",
+ "src/mls.c",
+ "src/module.c",
+ "src/module_to_cil.c",
+ "src/node_record.c",
+ "src/nodes.c",
+ "src/polcaps.c",
+ "src/policydb.c",
+ "src/policydb_convert.c",
+ "src/policydb_public.c",
+ "src/port_record.c",
+ "src/ports.c",
+ "src/roles.c",
+ "src/services.c",
+ "src/sidtab.c",
+ "src/symtab.c",
+ "src/user_record.c",
+ "src/users.c",
+ "src/util.c",
+ "src/write.c",
+ "cil/src/android.c",
+ "cil/src/cil_binary.c",
+ "cil/src/cil_build_ast.c",
+ "cil/src/cil.c",
+ "cil/src/cil_copy_ast.c",
+ "cil/src/cil_find.c",
+ "cil/src/cil_fqn.c",
+ "cil/src/cil_lexer.l",
+ "cil/src/cil_list.c",
+ "cil/src/cil_log.c",
+ "cil/src/cil_mem.c",
+ "cil/src/cil_parser.c",
+ "cil/src/cil_policy.c",
+ "cil/src/cil_post.c",
+ "cil/src/cil_reset_ast.c",
+ "cil/src/cil_resolve_ast.c",
+ "cil/src/cil_stack.c",
+ "cil/src/cil_strpool.c",
+ "cil/src/cil_symtab.c",
+ "cil/src/cil_tree.c",
+ "cil/src/cil_verify.c",
+ "cil/src/cil_write_ast.c",
+ ],
+ local_include_dirs: [
+ "cil/src",
+ "src",
+ ],
+ export_include_dirs: [
+ "cil/include",
+ "include",
+ ],
+ stl: "none",
+ // The host version of libsepol is loaded by the system python, which does
+ // not have the sanitizer runtimes.
+ target: {
+ host: {
+ sanitize: {
+ never: true,
+ },
+ },
+ },
+}
+
+cc_binary_host {
+ name: "chkcon",
+ srcs: ["utils/chkcon.c"],
+ shared_libs: ["libsepol"],
+ cflags: common_CFLAGS,
+}
diff --git a/libsepol/cil/include/cil/android.h b/libsepol/cil/include/cil/android.h
new file mode 100644
index 00000000..5aceda62
--- /dev/null
+++ b/libsepol/cil/include/cil/android.h
@@ -0,0 +1,34 @@
+#ifndef _SEPOL_ANDROID_H_
+#define _SEPOL_ANDROID_H_
+#include <cil/cil.h>
+
+#define PLAT_VERS "curr"
+#define PLAT_ID "p"
+#define NON_PLAT_ID "n"
+
+/*
+ * cil_android_attrib_mapping - extract attributizable elements of the policy in
+ * srcdb and create the mapping file necessary to link the platform and
+ * non-platform policy files after non-platform policy attributization.
+ * mdb - uninitialized cil_db reference to the resulting policy. Caller
+ * responsibility to destroy.
+ * srcdb - initialized and parsed cil_db reference to source public policy.
+ * num - the version string to append types when converted to attributes.
+ * returns SEPOL_OK if successful, otherwise passes on the encountered error.
+ */
+int cil_android_attrib_mapping(struct cil_db **mdb, struct cil_db *srcdb, const char *num);
+
+/*
+ * cil_android_attributize - extract attributizable elements of the policy in
+ * srcdb and convert all usage of those elements in tgtdb to versioned attributes.
+ * Keep the attributes and type definitions so that tgtdb policy is more robust
+ * against future changes to the public policy.
+ * tgtdb - initialized and parsed cil_db reference to modify.
+ * srcdb - initialized and parsed cil_db reference to source public policy
+ * from which to extract attributizable elements.
+ * num - the version string to append types when converted to attributes.
+ * returns SEPOL_OK if successful, otherwise passes on the encountered error.
+ */
+int cil_android_attributize(struct cil_db *tgtdb, struct cil_db *srcdb, const char *num);
+
+#endif /* _SEPOL_ANDROID_H_ */
diff --git a/libsepol/cil/include/cil/cil_write_ast.h b/libsepol/cil/include/cil/cil_write_ast.h
new file mode 100644
index 00000000..77c3087e
--- /dev/null
+++ b/libsepol/cil/include/cil/cil_write_ast.h
@@ -0,0 +1,7 @@
+#ifndef CIL_WRITE_H_
+#define CIL_WRITE_H_
+
+#include <cil/cil.h>
+
+int cil_write_ast(struct cil_db *db, const char* path);
+#endif /* CIL_WRITE_H_ */
diff --git a/libsepol/cil/src/android.c b/libsepol/cil/src/android.c
new file mode 100644
index 00000000..2fa5e965
--- /dev/null
+++ b/libsepol/cil/src/android.c
@@ -0,0 +1,932 @@
+#include <cil/android.h>
+#include <sepol/policydb/hashtab.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cil_build_ast.h"
+#include "cil_internal.h"
+#include "cil_strpool.h"
+#include "cil_symtab.h"
+#include "cil_tree.h"
+
+#define VER_MAP_SZ (1 << 12)
+
+/* added to hashmap - currently unused as hashmap is used as a set */
+struct version_datum {
+ struct cil_db *db;
+ struct cil_tree_node *ast_node;
+ char *orig_name;
+};
+
+struct version_args {
+ struct cil_db *db;
+ hashtab_t vers_map;
+ const char *num;
+};
+
+enum plat_flavor {
+ PLAT_NONE = 0,
+ PLAT_TYPE,
+ PLAT_ATTRIB
+};
+
+static unsigned int ver_map_hash_val(hashtab_t h, const_hashtab_key_t key)
+{
+ /* from cil_stpool.c */
+ char *p, *keyp;
+ size_t size;
+ unsigned int val;
+
+ val = 0;
+ keyp = (char*)key;
+ size = strlen(keyp);
+ for (p = keyp; ((size_t) (p - keyp)) < size; p++)
+ val =
+ (val << 4 | (val >> (8 * sizeof(unsigned int) - 4))) ^ (*p);
+ return val & (h->size - 1);
+}
+
+
+static int ver_map_key_cmp(hashtab_t h __attribute__ ((unused)),
+ const_hashtab_key_t key1, const_hashtab_key_t key2)
+{
+ /* hashtab_key_t is just a const char* underneath */
+ return strcmp(key1, key2);
+}
+
+/*
+ * version_datum pointers all refer to memory owned elsewhere, so just free the
+ * datum itself.
+ */
+static int ver_map_entry_destroy(__attribute__ ((unused))hashtab_key_t k,
+ hashtab_datum_t d, __attribute__ ((unused))void *args)
+{
+ free(d);
+ return 0;
+}
+
+static void ver_map_destroy(hashtab_t h)
+{
+ hashtab_map(h, ver_map_entry_destroy, NULL);
+ hashtab_destroy(h);
+}
+
+static int __extract_attributees_helper(struct cil_tree_node *node, uint32_t *finished, void *extra_args)
+{
+ int rc = SEPOL_ERR;
+ struct version_args *args = (struct version_args *) extra_args;
+ char *key;
+ struct version_datum *datum;
+
+ if (node == NULL || finished == NULL || extra_args == NULL) {
+ goto exit;
+ }
+
+ switch (node->flavor) {
+ case CIL_ROLE:
+ cil_log(CIL_ERR, "%s unsupported statement in attributee policy (line %d)\n",
+ CIL_KEY_ROLE, node->line);
+ rc = SEPOL_ERR;
+ break;
+ case CIL_TYPE:
+ case CIL_TYPEATTRIBUTE:
+ datum = cil_malloc(sizeof(*datum));
+ datum->db = args->db;
+ datum->ast_node = node;
+ datum->orig_name = DATUM(node->data)->name;
+ key = datum->orig_name;
+ if (!strncmp(key, "base_typeattr_", 14)) {
+ /* checkpolicy creates base attributes which are just typeattributesets,
+ of the existing types and attributes. These may be differnt in
+ every checkpolicy output, ignore them here, they'll be dealt with
+ as a special case when attributizing. */
+ free(datum);
+ } else {
+ rc = hashtab_insert(args->vers_map, (hashtab_key_t) key, (hashtab_datum_t) datum);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ }
+ break;
+ case CIL_TYPEALIAS:
+ cil_log(CIL_ERR, "%s unsupported statement in attributee policy (line %d)\n",
+ CIL_KEY_TYPEALIAS, node->line);
+ goto exit;
+ break;
+ case CIL_TYPEPERMISSIVE:
+ cil_log(CIL_ERR, "%s unsupported statement in attributee policy (line %d)\n",
+ CIL_KEY_TYPEPERMISSIVE, node->line);
+ goto exit;
+ break;
+ case CIL_NAMETYPETRANSITION:
+ case CIL_TYPE_RULE:
+ cil_log(CIL_ERR, "%s unsupported statement in attributee policy (line %d)\n",
+ CIL_KEY_TYPETRANSITION, node->line);
+ goto exit;
+ break;
+ default:
+ break;
+ }
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+/*
+ * For the given db, with an already-built AST, fill the vers_map hash table
+ * with every encountered type and attribute. This could eventually be expanded
+ * to include other language constructs, such as users and roles, in which case
+ * multiple hash tables would be needed. These tables can then be used by
+ * attributize() to change all references to these types.
+ */
+int cil_extract_attributees(struct cil_db *db, hashtab_t vers_map)
+{
+ /* walk ast. */
+ int rc = SEPOL_ERR;
+ struct version_args extra_args;
+ extra_args.db = db;
+ extra_args.vers_map = vers_map;
+ extra_args.num = NULL;
+ rc = cil_tree_walk(db->ast->root, __extract_attributees_helper, NULL, NULL, &extra_args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+static enum plat_flavor __cil_get_plat_flavor(hashtab_t vers_map, hashtab_key_t key)
+{
+ enum plat_flavor rc;
+ struct version_datum *vers_datum;
+
+ vers_datum = (struct version_datum *)hashtab_search(vers_map, key);
+ if (vers_datum == NULL) {
+ return PLAT_NONE;
+ }
+ switch (vers_datum->ast_node->flavor) {
+ case CIL_TYPE:
+ rc = PLAT_TYPE;
+ break;
+ case CIL_TYPEATTRIBUTE:
+ rc = PLAT_ATTRIB;
+ break;
+ default:
+ rc = PLAT_NONE;
+ break;
+ }
+ return rc;
+}
+
+/*
+ * Takes the old name and version string and creates a new strpool entry by
+ * combining them.
+ */
+static char *__cil_attrib_get_versname(char *old, const char *vers)
+{
+ size_t len = 0;
+ char *tmp_new = NULL;
+ char *final;
+
+ len += strlen(old) + strlen(vers) + 2;
+ tmp_new = cil_malloc(len);
+ snprintf(tmp_new, len, "%s_%s", old, vers);
+ final = cil_strpool_add(tmp_new);
+ free(tmp_new);
+ return final;
+}
+
+/*
+ * Change type to attribute - create new versioned name based on old, create
+ * typeattribute node add to the existing type node.
+ */
+static int __cil_attrib_convert_type(struct cil_tree_node *node, struct version_args *args)
+{
+ int rc = SEPOL_ERR;
+ struct cil_type *type = (struct cil_type *)node->data;
+ struct cil_typeattribute *typeattr = NULL;
+ struct cil_tree_node *new_ast_node = NULL;
+ char *new_key;
+
+ cil_typeattribute_init(&typeattr);
+
+ new_key = __cil_attrib_get_versname(type->datum.name, args->num);
+
+ /* create new tree node to contain typeattribute and add to tree */
+ cil_tree_node_init(&new_ast_node);
+ new_ast_node->parent = node->parent;
+ new_ast_node->next = node->next;
+ node->next = new_ast_node;
+
+ rc = cil_gen_node(args->db, new_ast_node, (struct cil_symtab_datum *) typeattr,
+ new_key, CIL_SYM_TYPES, CIL_TYPEATTRIBUTE);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+/*
+ * Update datum - create new key, remove entry under old key,
+ * update entry, and insert under new key
+ */
+static int __cil_attrib_swap_symtab_key(struct cil_tree_node *node, char *old_key,
+ const char *num)
+{
+ int rc = SEPOL_ERR;
+ char *new_key;
+ symtab_t *symtab;
+ struct cil_symtab_datum *datum = (struct cil_symtab_datum *) node->data;
+
+ new_key = __cil_attrib_get_versname(old_key, num);
+
+ symtab = datum->symtab;
+
+ /* TODO: remove, but what happens to other nodes on this datum ?*/
+ cil_list_remove(datum->nodes, CIL_NODE, node, 0);
+ cil_symtab_remove_datum(datum);
+
+ rc = cil_symtab_insert(symtab, new_key, datum, node);
+
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+/*
+ * expressions may contains strings which are not in the type-attribute
+ * namespace, so this is not a general cil_expr attributizer.
+ * TODO: add support for other types of expressions which may contain types.
+ */
+static int cil_attrib_type_expr(struct cil_list *expr_str, struct version_args *args)
+{
+ int rc = SEPOL_ERR;
+ struct cil_list_item *curr = NULL;
+ char *new;
+ hashtab_key_t key;
+
+ /* iterate through cil_list, replacing types */
+ cil_list_for_each(curr, expr_str) {
+ switch(curr->flavor) {
+ case CIL_LIST:
+ rc = cil_attrib_type_expr((struct cil_list *)curr->data, args);
+ if (rc != SEPOL_OK)
+ goto exit;
+ break;
+ case CIL_STRING:
+ key = (hashtab_key_t) curr->data;
+ enum plat_flavor pf = __cil_get_plat_flavor(args->vers_map, key);
+ if (!strncmp(curr->data, "base_typeattr_", 14) || pf == PLAT_TYPE) {
+ new = __cil_attrib_get_versname((char *) curr->data, args->num);
+ curr->data = (void *) new;
+ }
+ break;
+ case CIL_DATUM:
+ cil_log(CIL_ERR, "AST already resolved. Not yet supported.\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int cil_attrib_check_context(struct cil_context *ctxt, struct version_args *args)
+{
+ int rc = SEPOL_ERR;
+ hashtab_key_t key;
+
+ if (ctxt->type != NULL) {
+ cil_log(CIL_ERR, "AST already resolved. Not yet supported.\n");
+ goto exit;
+ }
+
+ key = (hashtab_key_t) ctxt->type_str;
+ if (__cil_get_plat_flavor(args->vers_map, key) != PLAT_NONE) {
+ /* TODO: reinstate check, but leave out for now
+ cil_log(CIL_ERR, "AST contains context with platform public type: %s\n",
+ ctxt->type_str);
+ rc = SEPOL_ERR;
+ goto exit; */
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int cil_attrib_sidcontext(struct cil_tree_node *node, struct version_args *args)
+{
+ int rc = SEPOL_ERR;
+ struct cil_sidcontext *sidcon = (struct cil_sidcontext *)node->data;
+
+ if (sidcon->context_str == NULL) {
+ /* sidcon contains an anon context, which needs to have type checked */
+ rc = cil_attrib_check_context(sidcon->context, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int cil_attrib_context(struct cil_tree_node *node, struct version_args *args)
+{
+ struct cil_context *ctxt = (struct cil_context *)node->data;
+
+ return cil_attrib_check_context(ctxt, args);
+}
+
+static int cil_attrib_roletype(struct cil_tree_node *node,
+ __attribute__((unused)) struct version_args *args)
+{
+ int rc = SEPOL_ERR;
+ char *key;
+ struct cil_roletype *roletype = (struct cil_roletype *)node->data;
+
+ if (roletype->role) {
+ cil_log(CIL_ERR, "AST already resolved. !!! Not yet supported.\n");
+ goto exit;
+ }
+ key = roletype->type_str;
+ if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+ roletype->type_str = __cil_attrib_get_versname(key, args->num);
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int cil_attrib_type(struct cil_tree_node *node, struct version_args *args)
+{
+ int rc = SEPOL_ERR;
+ struct cil_type *type = (struct cil_type *)node->data;
+ char *key = type->datum.name;
+
+ if (type->value) {
+ cil_log(CIL_ERR, "AST already resolved. !!! Not yet supported.\n");
+ goto exit;
+ }
+ if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+ rc = __cil_attrib_convert_type(node, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int cil_attrib_typepermissive(struct cil_tree_node *node,
+ struct version_args *args __attribute__ ((unused)))
+{
+ struct cil_typepermissive *typeperm = (struct cil_typepermissive *)node->data;
+
+ if (typeperm->type != NULL) {
+ cil_log(CIL_ERR, "AST already resolved. ### Not yet supported.\n");
+ return SEPOL_ERR;
+ }
+
+ return SEPOL_OK;
+}
+
+static int cil_attrib_typeattribute(struct cil_tree_node *node, struct version_args *args)
+{
+ int rc = SEPOL_ERR;
+ struct cil_typeattribute *typeattr = (struct cil_typeattribute *)node->data;
+ char *key = typeattr->datum.name;
+
+ if (typeattr->types) {
+ cil_log(CIL_ERR, "AST already resolved. Not yet supported (line %d).\n",
+ node->line);
+ goto exit;
+ }
+ if (!strncmp(key, "base_typeattr_", 14)) {
+ rc = __cil_attrib_swap_symtab_key(node, key, args->num);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int cil_attrib_typeattributeset(struct cil_tree_node *node, struct version_args *args)
+{
+ int rc = SEPOL_ERR;
+ char *key;
+ struct cil_typeattributeset *typeattrset = (struct cil_typeattributeset *) node->data;
+
+ if (typeattrset->datum_expr != NULL) {
+ cil_log(CIL_ERR, "AST already resolved. Not yet supported (line %d).\n",
+ node->line);
+ goto exit;
+ }
+
+ key = typeattrset->attr_str;
+ /* first check to see if the attribute to which this set belongs is versioned */
+ if (!strncmp(key, "base_typeattr_", 14)) {
+ typeattrset->attr_str = __cil_attrib_get_versname(key, args->num);
+ }
+
+ rc = cil_attrib_type_expr(typeattrset->str_expr, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int cil_attrib_typealiasactual(struct cil_tree_node *node, struct version_args *args)
+{
+ int rc = SEPOL_ERR;
+ char *key;
+ struct cil_aliasactual *aliasact = (struct cil_aliasactual *)node->data;
+
+ key = aliasact->actual_str;
+ if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) != PLAT_NONE) {
+ cil_log(CIL_ERR, "%s with platform public type not allowed (line %d)\n",
+ CIL_KEY_TYPEALIASACTUAL, node->line);
+ goto exit;
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int cil_attrib_nametypetransition(struct cil_tree_node *node, struct version_args *args)
+{
+ int rc = SEPOL_ERR;
+ char *key;
+ struct cil_nametypetransition *namettrans = (struct cil_nametypetransition *)node->data;
+
+ if (namettrans->src != NULL) {
+ cil_log(CIL_ERR, "AST already resolved. Not yet supported (line %d).\n",
+ node->line);
+ goto exit;
+ }
+ key = namettrans->src_str;
+ if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+ namettrans->src_str = __cil_attrib_get_versname(key, args->num);
+ }
+
+ key = namettrans->tgt_str;
+ if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+ namettrans->tgt_str = __cil_attrib_get_versname(key, args->num);
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+/*
+ * This is exactly the same as cil_attrib_nametypetransition, but the struct
+ * layouts differ, so we can't reuse it.
+ */
+static int cil_attrib_type_rule(struct cil_tree_node *node, struct version_args *args)
+{
+ int rc = SEPOL_ERR;
+ char *key;
+ struct cil_type_rule *type_rule = (struct cil_type_rule *)node->data;
+
+ if (type_rule->src != NULL) {
+ cil_log(CIL_ERR, "AST already resolved. Not yet supported (line %d).\n",
+ node->line);
+ goto exit;
+ }
+ key = type_rule->src_str;
+ if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+ type_rule->src_str = __cil_attrib_get_versname(key, args->num);
+ }
+
+ key = type_rule->tgt_str;
+ if (__cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+ type_rule->tgt_str = __cil_attrib_get_versname(key, args->num);
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int cil_attrib_avrule(struct cil_tree_node *node, struct version_args *args)
+{
+ int rc = SEPOL_ERR;
+ char *key;
+ struct cil_avrule *avrule = (struct cil_avrule *)node->data;
+
+ if (avrule->src != NULL) {
+ cil_log(CIL_ERR, "AST already resolved. Not yet supported (line %d).\n",
+ node->line);
+ goto exit;
+ }
+
+ key = avrule->src_str;
+ if (!strncmp(key, "base_typeattr_", 14) ||
+ __cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+ avrule->src_str = __cil_attrib_get_versname(key, args->num);
+ }
+
+ key = avrule->tgt_str;
+ if (!strncmp(key, "base_typeattr_", 14) ||
+ __cil_get_plat_flavor(args->vers_map, (hashtab_key_t) key) == PLAT_TYPE) {
+ avrule->tgt_str = __cil_attrib_get_versname(key, args->num);
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int cil_attrib_genfscon(struct cil_tree_node *node, struct version_args *args)
+{
+ int rc = SEPOL_ERR;
+
+ struct cil_genfscon *genfscon = (struct cil_genfscon *)node->data;
+
+ if (genfscon->context_str == NULL) {
+ /* genfscon contains an anon context, which needs to have type checked */
+ rc = cil_attrib_check_context(genfscon->context, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int cil_attrib_fsuse(struct cil_tree_node *node, struct version_args *args)
+{
+ int rc = SEPOL_ERR;
+ struct cil_fsuse *fsuse = (struct cil_fsuse *)node->data;
+
+ if (fsuse->context_str == NULL) {
+ /* fsuse contains an anon context, which needs to have type checked */
+ rc = cil_attrib_check_context(fsuse->context, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int __attributize_helper(struct cil_tree_node *node, uint32_t *finished, void *extra_args)
+{
+ int rc = SEPOL_ERR;
+ struct version_args *args = (struct version_args *) extra_args;
+
+ if (node == NULL || finished == NULL || extra_args == NULL) {
+ goto exit;
+ }
+
+ switch (node->flavor) {
+ case CIL_SIDCONTEXT:
+ /* contains type, but shouldn't involve an attributized type, maybe add
+ a check on type and error if it conflicts */
+ rc = cil_attrib_sidcontext(node, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ break;
+ case CIL_ROLE:
+ cil_log(CIL_ERR, "%s declaration illegal non-platform policy (line %d)\n",
+ CIL_KEY_ROLE, node->line);
+ rc = SEPOL_ERR;
+ break;
+ case CIL_ROLETYPE:
+ /* Yes, this is needed if we support roletype in non-platform policy.
+ type_id can be type, typealias or typeattr */
+ rc = cil_attrib_roletype(node, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ break;
+ case CIL_ROLEATTRIBUTE:
+ /* don't think this is needed, only used for cil_gen_req, and we aren't
+ yet supporting roles in non-platform policy. */
+ break;
+ case CIL_TYPE:
+ /* conver to attribute if in policy */
+ rc = cil_attrib_type(node, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ break;
+ case CIL_TYPEPERMISSIVE:
+ rc = cil_attrib_typepermissive(node, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ break;
+ case CIL_TYPEATTRIBUTE:
+ rc = cil_attrib_typeattribute(node, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ break;
+ case CIL_TYPEATTRIBUTESET:
+ rc = cil_attrib_typeattributeset(node, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ break;
+ case CIL_TYPEALIASACTUAL:
+ /* this will break on an attributized type - identify it and throw error */
+ rc = cil_attrib_typealiasactual(node, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ break;
+ case CIL_NAMETYPETRANSITION:
+ /* not allowed in plat-policy. Types present, throw error if attributee */
+ rc = cil_attrib_nametypetransition(node, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ break;
+ case CIL_TYPE_RULE:
+ /* not allowed in plat-policy. Types present, throw error if attributee */
+ rc = cil_attrib_type_rule(node, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ break;
+ case CIL_AVRULE:
+ case CIL_AVRULEX:
+ rc = cil_attrib_avrule(node, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ break;
+ case CIL_CONTEXT:
+ /* not currently found in AOSP policy, but if found would need to be
+ checked to not be attributee */
+ rc = cil_attrib_context(node, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ break;
+ case CIL_GENFSCON:
+ /* not allowed in plat-policy, but types present, throw error if attributee */
+ rc = cil_attrib_genfscon(node, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ break;
+ case CIL_FILECON:
+ case CIL_NODECON:
+ case CIL_PORTCON:
+ case CIL_PIRQCON:
+ case CIL_IOMEMCON:
+ case CIL_IOPORTCON:
+ case CIL_PCIDEVICECON:
+ case CIL_DEVICETREECON:
+ case CIL_VALIDATETRANS:
+ case CIL_MLSVALIDATETRANS:
+ case CIL_CALL:
+ case CIL_MACRO:
+ case CIL_OPTIONAL:
+ /* Not currently found in AOSP and not yet properly handled. Return err until support added. */
+ cil_log(CIL_ERR, "unsupported policy statement (line %d)\n", node->line);
+ rc = SEPOL_ERR;
+ goto exit;
+ case CIL_FSUSE:
+ /* not allowed in plat-policy, but types present, throw error if attributee */
+ cil_attrib_fsuse(node, args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+ break;
+ case CIL_CONSTRAIN:
+ case CIL_MLSCONSTRAIN:
+ /* there is type info here, but not sure if we'll allow non-platform code
+ to have this, or whether or not it's in platform policy. Currently
+ assuming that mlsconstrain is private-platform only, and that normal
+ constrain is verboten. */
+ cil_log(CIL_ERR, "unsupported policy statement (line %d)\n", node->line);
+ rc = SEPOL_ERR;
+ goto exit;
+ default:
+ break;
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+/*
+ * walk ast, replacing previously identified types and attributes with the
+ * attributized version. Also replace previous references to the attributees
+ * with the versioned type.
+ */
+static int cil_attributize(struct cil_db *db, hashtab_t vers_map, const char *num)
+{
+ int rc = SEPOL_ERR;
+ struct version_args extra_args;
+ extra_args.db = db;
+ extra_args.vers_map = vers_map;
+ extra_args.num = num;
+
+ rc = cil_tree_walk(db->ast->root, __attributize_helper, NULL, NULL, &extra_args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+/*
+ * Create typeattributeset mappings from the attributes generated from the
+ * original types/attributes to the original values. This mapping will provide
+ * the basis for the platform policy's mapping to this public version.
+ *
+ * Add these new typeattributeset nodes to the given cil_db.
+ */
+static int cil_build_mappings_tree(hashtab_key_t k, hashtab_datum_t d, void *args)
+{
+ struct cil_typeattributeset *attrset = NULL;
+ struct cil_typeattribute *typeattr = NULL;
+ struct cil_expandtypeattribute *expandattr = NULL;
+ struct cil_tree_node *ast_node = NULL;
+ struct version_args *verargs = (struct version_args *)args;
+ struct cil_tree_node *ast_parent = verargs->db->ast->root;
+ char *orig_type = (char *) k;
+ struct version_datum *vers_datum = (struct version_datum *) d;
+ char *new_key = __cil_attrib_get_versname(orig_type, verargs->num);
+
+ if (vers_datum->ast_node->flavor == CIL_TYPEATTRIBUTE) {
+ // platform attributes are not versioned
+ return SEPOL_OK;
+ }
+ /* create typeattributeset datum */
+ cil_typeattributeset_init(&attrset);
+ cil_list_init(&attrset->str_expr, CIL_TYPE);
+ attrset->attr_str = new_key;
+ cil_list_append(attrset->str_expr, CIL_STRING, orig_type);
+
+ /* create containing tree node */
+ cil_tree_node_init(&ast_node);
+ ast_node->data = attrset;
+ ast_node->flavor = CIL_TYPEATTRIBUTESET;
+
+ /* add to tree */
+ ast_node->parent = ast_parent;
+ if (ast_parent->cl_head == NULL)
+ ast_parent->cl_head = ast_node;
+ else
+ ast_parent->cl_tail->next = ast_node;
+ ast_parent->cl_tail = ast_node;
+
+ /* create expandtypeattribute datum */
+ cil_expandtypeattribute_init(&expandattr);
+ cil_list_init(&expandattr->attr_strs, CIL_TYPE);
+ cil_list_append(expandattr->attr_strs, CIL_STRING, new_key);
+ expandattr->expand = CIL_TRUE;
+
+ /* create containing tree node */
+ cil_tree_node_init(&ast_node);
+ ast_node->data = expandattr;
+ ast_node->flavor = CIL_EXPANDTYPEATTRIBUTE;
+ /* add to tree */
+ ast_node->parent = ast_parent;
+ ast_parent->cl_tail->next = ast_node;
+ ast_parent->cl_tail = ast_node;
+
+ /* re)declare typeattribute. */
+ cil_typeattribute_init(&typeattr);
+ typeattr->datum.name = new_key;
+ cil_tree_node_init(&ast_node);
+ ast_node->data = typeattr;
+ ast_node->flavor = CIL_TYPEATTRIBUTE;
+ ast_node->parent = ast_parent;
+ ast_parent->cl_tail->next = ast_node;
+ ast_parent->cl_tail = ast_node;
+
+ return SEPOL_OK;
+}
+
+/*
+ * Initializes the given db and uses the version mapping generated by
+ * cil_extract_attributees() to fill it with the glue policy required to
+ * connect the attributized policy created by cil_attributize() to the policy
+ * declaring the concrete types.
+ */
+static int cil_attrib_mapping(struct cil_db **db, hashtab_t vers_map, const char *num)
+{
+ int rc = SEPOL_ERR;
+ struct version_args extra_args;
+
+ cil_db_init(db);
+
+ /* foreach entry in vers_map, create typeattributeset node and attach to tree */
+ extra_args.db = *db;
+ extra_args.vers_map = NULL;
+ extra_args.num = num;
+ rc = hashtab_map(vers_map, cil_build_mappings_tree, &extra_args);
+ if (rc != SEPOL_OK) {
+ goto exit;
+ }
+
+ return SEPOL_OK;
+exit:
+ return rc;
+}
+
+int cil_android_attrib_mapping(struct cil_db **mdb, struct cil_db *srcdb, const char *num)
+{
+ int rc = SEPOL_ERR;
+ hashtab_t ver_map_tab = NULL;
+
+ ver_map_tab = hashtab_create(ver_map_hash_val, ver_map_key_cmp, VER_MAP_SZ);
+ if (!ver_map_tab) {
+ cil_log(CIL_ERR, "Unable to create version mapping table.\n");
+ goto exit;
+ }
+ rc = cil_build_ast(srcdb, srcdb->parse->root, srcdb->ast->root);
+ if (rc != SEPOL_OK) {
+ cil_log(CIL_ERR, "Unable to build source db AST.\n");
+ goto exit;
+ }
+ rc = cil_extract_attributees(srcdb, ver_map_tab);
+ if (rc != SEPOL_OK) {
+ cil_log(CIL_ERR, "Unable to extract attributizable elements from source db.\n");
+ goto exit;
+ }
+ rc = cil_attrib_mapping(mdb, ver_map_tab, num);
+ if (rc != SEPOL_OK) {
+ cil_log(CIL_ERR, "Unable to create mapping db from source db.\n");
+ goto exit;
+ }
+exit:
+ ver_map_destroy(ver_map_tab);
+ return rc;
+}
+
+int cil_android_attributize(struct cil_db *tgtdb, struct cil_db *srcdb, const char *num)
+{
+ int rc = SEPOL_ERR;
+ hashtab_t ver_map_tab = NULL;
+
+ ver_map_tab = hashtab_create(ver_map_hash_val, ver_map_key_cmp, VER_MAP_SZ);
+ if (!ver_map_tab) {
+ cil_log(CIL_ERR, "Unable to create version mapping table.\n");
+ goto exit;
+ }
+ rc = cil_build_ast(srcdb, srcdb->parse->root, srcdb->ast->root);
+ if (rc != SEPOL_OK) {
+ cil_log(CIL_ERR, "Unable to build source db AST.\n");
+ goto exit;
+ }
+ rc = cil_extract_attributees(srcdb, ver_map_tab);
+ if (rc != SEPOL_OK) {
+ cil_log(CIL_ERR, "Unable to extract attributizable elements from source db.\n");
+ goto exit;
+ }
+ rc = cil_build_ast(tgtdb, tgtdb->parse->root, tgtdb->ast->root);
+ if (rc != SEPOL_OK) {
+ cil_log(CIL_ERR, "Unable to build target db AST.\n");
+ goto exit;
+ }
+ rc = cil_attributize(tgtdb, ver_map_tab, num);
+ if (rc != SEPOL_OK) {
+ cil_log(CIL_ERR, "Unable to attributize target db.\n");
+ goto exit;
+ }
+exit:
+ ver_map_destroy(ver_map_tab);
+ return rc;
+}
diff --git a/libsepol/cil/src/cil_write_ast.c b/libsepol/cil/src/cil_write_ast.c
new file mode 100644
index 00000000..bcf5f416
--- /dev/null
+++ b/libsepol/cil/src/cil_write_ast.c
@@ -0,0 +1,1509 @@
+#include <stdlib.h>
+
+#include "cil_flavor.h"
+#include "cil_internal.h"
+#include "cil_log.h"
+#include "cil_tree.h"
+
+struct cil_args_write {
+ FILE *cil_out;
+ struct cil_db *db;
+};
+
+static int cil_unfill_expr(struct cil_list *expr_str, char **out_str, int paren);
+static int cil_unfill_classperms_list(struct cil_list *classperms, char **out_str, int paren);
+static int __cil_write_first_child_helper(struct cil_tree_node *node, void *extra_args);
+static int __cil_write_node_helper(struct cil_tree_node *node, uint32_t *finished, void *extra_args);
+static int __cil_write_last_child_helper(struct cil_tree_node *node, void *extra_args);
+
+static int __cil_strlist_concat(struct cil_list *str_list, char **out_str, int paren) {
+ size_t len = paren ? 3 : 1;
+ size_t num_elems = 0;
+ char *p = NULL;
+ struct cil_list_item *curr;
+
+ /* get buffer size */
+ cil_list_for_each(curr, str_list) {
+ len += strlen((char *)curr->data);
+ num_elems++;
+ }
+ if (num_elems != 0) {
+ /* add spaces between elements */
+ len += num_elems - 1;
+ }
+ *out_str = cil_malloc(len);
+ p = *out_str;
+ if (paren)
+ *p++ = '(';
+ cil_list_for_each(curr, str_list) {
+ size_t src_len = strlen((char *)curr->data);
+ memcpy(p, curr->data, src_len);
+ p += src_len;
+ if (curr->next != NULL)
+ *p++ = ' ';
+ }
+ if (paren)
+ *p++ = ')';
+ *p++ = '\0';
+ return SEPOL_OK;
+}
+
+static int __cil_unfill_expr_helper(struct cil_list_item *curr,
+ struct cil_list_item **next, char **out_str, int paren) {
+ int rc = SEPOL_ERR;
+ char *str = NULL;
+ char *operand1 = NULL;
+ char *operand2 = NULL;
+
+ switch(curr->flavor) {
+ case CIL_LIST:
+ rc = cil_unfill_expr((struct cil_list *)curr->data, &str, paren);
+ if (rc != SEPOL_OK)
+ goto exit;
+ *out_str = str;
+ *next = curr->next;
+ break;
+ case CIL_STRING:
+ str = strdup((char *)curr->data);
+ if (!str) {
+ cil_log(CIL_ERR, "OOM. Unable to copy string.\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ *out_str = str;
+ *next = curr->next;
+ break;
+ case CIL_DATUM:
+ str = strdup(((struct cil_symtab_datum *)curr->data)->name);
+ if (!str) {
+ cil_log(CIL_ERR, "OOM. Unable to copy string.\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ *out_str = str;
+ *next = curr->next;
+ break;
+ case CIL_OP: {
+ char *op_str = NULL;
+ size_t len = 0;
+ enum cil_flavor op_flavor = (enum cil_flavor)curr->data;
+ switch (op_flavor) {
+ case CIL_AND:
+ op_str = CIL_KEY_AND;
+ break;
+ case CIL_OR:
+ op_str = CIL_KEY_OR;
+ break;
+ case CIL_NOT:
+ op_str = CIL_KEY_NOT;
+ break;
+ case CIL_ALL:
+ op_str = CIL_KEY_ALL;
+ break;
+ case CIL_EQ:
+ op_str = CIL_KEY_EQ;
+ break;
+ case CIL_NEQ:
+ op_str = CIL_KEY_NEQ;
+ break;
+ case CIL_RANGE:
+ op_str = CIL_KEY_RANGE;
+ break;
+ case CIL_XOR:
+ op_str = CIL_KEY_XOR;
+ break;
+ case CIL_CONS_DOM:
+ op_str = CIL_KEY_CONS_DOM;
+ break;
+ case CIL_CONS_DOMBY:
+ op_str = CIL_KEY_CONS_DOMBY;
+ break;
+ case CIL_CONS_INCOMP:
+ op_str = CIL_KEY_CONS_INCOMP;
+ break;
+ default:
+ cil_log(CIL_ERR, "Unknown operator in expression: %d\n", op_flavor);
+ goto exit;
+ break;
+ }
+ /* all operands take two args except for 'all' and 'not', which take
+ * one and two, respectively */
+ len = strlen(op_str) + 3;
+ if (op_flavor == CIL_ALL) {
+ *out_str = cil_malloc(len);
+ sprintf(*out_str, "(%s)", op_str);
+ *next = curr->next;
+ } else if (op_flavor == CIL_NOT) {
+ rc = __cil_unfill_expr_helper(curr->next, next, &operand1, paren);
+ if (rc != SEPOL_OK)
+ goto exit;
+ len += strlen(operand1) + 1;
+ *out_str = cil_malloc(len);
+ sprintf(*out_str, "(%s %s)", op_str, operand1);
+ // *next already set by recursive call
+ } else {
+ rc = __cil_unfill_expr_helper(curr->next, next, &operand1, paren);
+ if (rc != SEPOL_OK)
+ goto exit;
+ len += strlen(operand1) + 1;
+ // *next contains operand2, but keep track of next after that
+ rc = __cil_unfill_expr_helper(*next, next, &operand2, paren);
+ if (rc != SEPOL_OK)
+ goto exit;
+ len += strlen(operand2) + 1;
+ *out_str = cil_malloc(len);
+ sprintf(*out_str, "(%s %s %s)", op_str, operand1, operand2);
+ // *next already set by recursive call
+ }
+ }
+ break;
+ case CIL_CONS_OPERAND: {
+ enum cil_flavor operand_flavor = (enum cil_flavor)curr->data;
+ char *operand_str = NULL;
+ switch (operand_flavor) {
+ case CIL_CONS_U1:
+ operand_str = CIL_KEY_CONS_U1;
+ break;
+ case CIL_CONS_U2:
+ operand_str = CIL_KEY_CONS_U2;
+ break;
+ case CIL_CONS_U3:
+ operand_str = CIL_KEY_CONS_U3;
+ break;
+ case CIL_CONS_T1:
+ operand_str = CIL_KEY_CONS_T1;
+ break;
+ case CIL_CONS_T2:
+ operand_str = CIL_KEY_CONS_T2;
+ break;
+ case CIL_CONS_T3:
+ operand_str = CIL_KEY_CONS_T3;
+ break;
+ case CIL_CONS_R1:
+ operand_str = CIL_KEY_CONS_R1;
+ break;
+ case CIL_CONS_R2:
+ operand_str = CIL_KEY_CONS_R2;
+ break;
+ case CIL_CONS_R3:
+ operand_str = CIL_KEY_CONS_R3;
+ break;
+ case CIL_CONS_L1:
+ operand_str = CIL_KEY_CONS_L1;
+ break;
+ case CIL_CONS_L2:
+ operand_str = CIL_KEY_CONS_L2;
+ break;
+ case CIL_CONS_H1:
+ operand_str = CIL_KEY_CONS_H1;
+ break;
+ case CIL_CONS_H2:
+ operand_str = CIL_KEY_CONS_H2;
+ break;
+ default:
+ cil_log(CIL_ERR, "Unknown operand in expression\n");
+ goto exit;
+ break;
+ }
+ str = strdup(operand_str);
+ if (!str) {
+ cil_log(CIL_ERR, "OOM. Unable to copy string.\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ *out_str = str;
+ *next = curr->next;
+ }
+ break;
+ default:
+ cil_log(CIL_ERR, "Unknown flavor in expression\n");
+ goto exit;
+ break;
+ }
+ rc = SEPOL_OK;
+exit:
+ free(operand1);
+ free(operand2);
+ return rc;
+}
+
+static int cil_unfill_expr(struct cil_list *expr_str, char **out_str, int paren) {
+ int rc = SEPOL_ERR;
+
+ /* reuse cil_list to keep track of strings */
+ struct cil_list *str_list = NULL;
+ struct cil_list_item *curr = NULL;
+
+ cil_list_init(&str_list, CIL_NONE);
+
+ /* iterate through cil_list, grabbing elements as needed */
+ curr = expr_str->head;
+ while(curr != NULL) {
+ char *str = NULL;
+ struct cil_list_item *next = NULL;
+
+ rc = __cil_unfill_expr_helper(curr, &next, &str, paren);
+ if (rc != SEPOL_OK)
+ goto exit;
+ cil_list_append(str_list, CIL_STRING, (void *) str);
+ str = NULL;
+ curr = next;
+ }
+ rc = __cil_strlist_concat(str_list, out_str, paren);
+ if (rc != SEPOL_OK)
+ goto exit;
+ rc = SEPOL_OK;
+exit:
+ cil_list_for_each(curr, str_list) {
+ free(curr->data);
+ }
+ cil_list_destroy(&str_list, 0);
+ return rc;
+}
+
+static int cil_unfill_cats(struct cil_cats *cats, char **out_str) {
+ return cil_unfill_expr(cats->str_expr, out_str, 0);
+}
+
+static int cil_unfill_level(struct cil_level *lvl, char **out_str) {
+ int rc = SEPOL_ERR;
+ size_t len = 0;
+ char *sens, *cats = NULL;
+ sens = lvl->sens_str;
+ len = strlen(sens) + 3; // '()\0'
+ if (lvl->cats != NULL) {
+ rc = cil_unfill_cats(lvl->cats, &cats);
+ if (rc != SEPOL_OK)
+ goto exit;
+ len += strlen(cats) + 1;
+ }
+ *out_str = cil_malloc(len);
+ if (cats == NULL) {
+ if (sprintf(*out_str, "(%s)", sens) < 0) {
+ cil_log(CIL_ERR, "Error unpacking and writing level\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ } else {
+ if (sprintf(*out_str, "(%s %s)", sens, cats) < 0) {
+ cil_log(CIL_ERR, "Error unpacking and writing level\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ }
+ rc = SEPOL_OK;
+exit:
+ free(cats);
+ return rc;
+}
+
+static int cil_unfill_levelrange(struct cil_levelrange *lvlrnge, char **out_str) {
+ int rc = SEPOL_ERR;
+ size_t len = 0;
+ char *low = NULL, *high = NULL;
+ if (lvlrnge->low_str != NULL) {
+ low = strdup(lvlrnge->low_str);
+ if (low == NULL) {
+ cil_log(CIL_ERR, "OOM. Unable to copy level string.\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ } else {
+ rc = cil_unfill_level(lvlrnge->low, &low);
+ if (rc != SEPOL_OK)
+ goto exit;
+ }
+ if (lvlrnge->high_str != NULL) {
+ high = strdup(lvlrnge->high_str);
+ if (high == NULL) {
+ cil_log(CIL_ERR, "OOM. Unable to copy level string.\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ } else {
+ rc = cil_unfill_level(lvlrnge->high, &high);
+ if (rc != SEPOL_OK)
+ goto exit;
+ }
+ len = strlen(low) + strlen(high) + 4;
+ *out_str = cil_malloc(len);
+ if (sprintf(*out_str, "(%s %s)", low, high) < 0) {
+ cil_log(CIL_ERR, "Error unpacking and writing levelrange\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ rc = SEPOL_OK;
+exit:
+ free(low);
+ free(high);
+ return rc;
+}
+
+static int cil_unfill_context(struct cil_context *context, char **out_str) {
+ int rc = SEPOL_ERR;
+ size_t len = 0;
+ char *user_str, *role_str, *type_str;
+ char *range_str = NULL;
+
+ user_str = context->user_str;
+ role_str = context->role_str;
+ type_str = context->type_str;
+ if (context->range_str != NULL) {
+ range_str = strdup(context->range_str);
+ if (range_str == NULL) {
+ cil_log(CIL_ERR, "OOM. Unable to copy range string.\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ } else {
+ rc = cil_unfill_levelrange(context->range, &range_str);
+ if (rc != SEPOL_OK)
+ goto exit;
+ }
+ len = strlen(user_str) + strlen(role_str) + strlen(type_str)
+ + strlen(range_str) + 6;
+ *out_str = cil_malloc(len);
+ if (sprintf(*out_str, "(%s %s %s %s)", user_str, role_str, type_str, range_str) < 0) {
+ cil_log(CIL_ERR, "Error unpacking and writing context\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ rc = SEPOL_OK;
+exit:
+ free(range_str);
+ return rc;
+}
+
+static int cil_unfill_permx(struct cil_permissionx *permx, char **out_str) {
+ size_t len = 3;
+ int rc = SEPOL_ERR;
+ char *kind, *obj;
+ char *expr = NULL;
+
+ switch (permx->kind) {
+ case CIL_PERMX_KIND_IOCTL:
+ kind = CIL_KEY_IOCTL;
+ break;
+ default:
+ cil_log(CIL_ERR, "Unknown permissionx kind: %d\n", permx->kind);
+ rc = SEPOL_ERR;
+ goto exit;
+ break;
+ }
+ obj = permx->obj_str;
+ rc = cil_unfill_expr(permx->expr_str, &expr, 1);
+ if (rc != SEPOL_OK)
+ goto exit;
+ len += strlen(kind) + strlen(obj) + strlen(expr) + 2;
+ *out_str = cil_malloc(len);
+ if (sprintf(*out_str, "(%s %s %s)", kind, obj, expr) < 0) {
+ cil_log(CIL_ERR, "Error writing xperm\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ rc = SEPOL_OK;
+exit:
+ free(expr);
+ return rc;
+}
+
+#define cil_write_unsupported(flavor) _cil_write_unsupported(flavor, __LINE__)
+static int _cil_write_unsupported(const char *flavor, int line) {
+ cil_log(CIL_ERR,
+ "flavor \"%s\" is not supported, look in file \"%s\""
+ " on line %d to add support.\n", flavor, __FILE__, line);
+ return SEPOL_ENOTSUP;
+}
+
+static int cil_write_policycap(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_policycap *polcap = (struct cil_policycap *)node->data;
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_POLICYCAP, polcap->datum.name);
+ return SEPOL_OK;
+}
+
+static int cil_write_perm(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_perm *perm = (struct cil_perm *)node->data;
+ fprintf(cil_out, "%s", perm->datum.name);
+ if (node->next != NULL)
+ fprintf(cil_out, " ");
+ return SEPOL_OK;
+}
+
+
+static int cil_write_class(struct cil_tree_node *node, uint32_t *finished,
+ struct cil_args_write *extra_args) {
+ int rc = SEPOL_ERR;
+ FILE *cil_out = extra_args->cil_out;
+ struct cil_symtab_datum *datum = (struct cil_symtab_datum *)node->data;
+ char *class_type = (node->flavor == CIL_CLASS) ? CIL_KEY_CLASS : CIL_KEY_COMMON;
+
+ /* print preamble */
+ fprintf(cil_out, "(%s %s ", class_type, datum->name);
+
+ if (node->cl_head == NULL) {
+ /* no associated perms in this part of tree */
+ fprintf(cil_out, "()");
+ } else {
+
+ /* visit subtree (perms) */
+ rc = cil_tree_walk(node, __cil_write_node_helper,
+ __cil_write_first_child_helper,
+ __cil_write_last_child_helper,
+ extra_args);
+ if (rc != SEPOL_OK)
+ goto exit;
+ }
+
+ /* postamble (trailing paren) */
+ fprintf(cil_out, ")\n");
+ *finished = CIL_TREE_SKIP_HEAD;
+ rc = SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int cil_write_classorder(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ char *ord_str = NULL;
+ struct cil_classorder *classord = (struct cil_classorder *)node->data;
+
+ /* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
+ rc = cil_unfill_expr(classord->class_list_str, &ord_str, 1);
+ if (rc != SEPOL_OK)
+ goto exit;
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_CLASSORDER, ord_str);
+ rc = SEPOL_OK;
+exit:
+ free(ord_str);
+ return rc;
+}
+
+static int cil_write_classcommon(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_classcommon *classcommon = (struct cil_classcommon *)node->data;
+ fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_CLASSCOMMON, classcommon->class_str,
+ classcommon->common_str);
+ return SEPOL_OK;
+}
+
+static int cil_write_sid(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_sid *sid = (struct cil_sid *)node->data;
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_SID, sid->datum.name);
+ return SEPOL_OK;
+}
+
+static int cil_write_sidcontext(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ char *sid;
+ char *ctx_str = NULL;
+ struct cil_sidcontext *sidcon = (struct cil_sidcontext *)node->data;
+
+ sid = sidcon->sid_str;
+ if (sidcon->context_str != NULL) {
+ ctx_str = strdup(sidcon->context_str);
+ if (ctx_str == NULL) {
+ cil_log(CIL_ERR, "OOM. Unable to copy context string.\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ } else {
+ rc = cil_unfill_context(sidcon->context, &ctx_str);
+ if (rc != SEPOL_OK)
+ goto exit;
+ }
+ fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_SIDCONTEXT, sid, ctx_str);
+ rc = SEPOL_OK;
+exit:
+ free(ctx_str);
+ return rc;
+}
+
+static int cil_write_sidorder(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ char *ord_str = NULL;
+ struct cil_sidorder *sidord = (struct cil_sidorder *)node->data;
+
+ /* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
+ rc = cil_unfill_expr(sidord->sid_list_str, &ord_str, 1);
+ if (rc != SEPOL_OK)
+ goto exit;
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_SIDORDER, ord_str);
+ rc = SEPOL_OK;
+exit:
+ free(ord_str);
+ return rc;
+}
+
+static int cil_write_user(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_user *user = (struct cil_user *)node->data;
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_USER, user->datum.name);
+ return SEPOL_OK;
+}
+
+static int cil_write_userrole(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_userrole *userrole = (struct cil_userrole *)node->data;
+ fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_USERROLE, userrole->user_str,
+ userrole->role_str);
+ return SEPOL_OK;
+}
+
+static int cil_write_userlevel(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_userlevel *usrlvl = (struct cil_userlevel *)node->data;
+ int rc = SEPOL_ERR;
+ char *usr;
+ char *lvl = NULL;
+
+ usr = usrlvl->user_str;
+ if (usrlvl->level_str != NULL) {
+ lvl = strdup(usrlvl->level_str);
+ if (lvl == NULL) {
+ cil_log(CIL_ERR, "OOM. Unable to copy level string.\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ } else {
+ rc = cil_unfill_level(usrlvl->level, &lvl);
+ if (rc != SEPOL_OK)
+ goto exit;
+ }
+ fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_USERLEVEL, usr, lvl);
+ rc = SEPOL_OK;
+exit:
+ free(lvl);
+ return rc;
+}
+
+static int cil_write_userrange(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_userrange *usrrng = (struct cil_userrange *)node->data;
+ int rc = SEPOL_ERR;
+ char *usr;
+ char *range = NULL;
+
+ usr = usrrng->user_str;
+ if (usrrng->range_str != NULL) {
+ range = strdup(usrrng->range_str);
+ if (range == NULL) {
+ cil_log(CIL_ERR, "OOM. Unable to copy levelrange string.\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ } else {
+ rc = cil_unfill_levelrange(usrrng->range, &range);
+ if (rc != SEPOL_OK)
+ goto exit;
+ }
+ fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_USERRANGE, usr, range);
+ rc = SEPOL_OK;
+exit:
+ free(range);
+ return rc;
+}
+
+static int cil_write_role(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_role *role = (struct cil_role *)node->data;
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_ROLE, role->datum.name);
+ return SEPOL_OK;
+}
+
+static int cil_write_roletype(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_roletype *roletype = (struct cil_roletype *)node->data;
+ fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_ROLETYPE, roletype->role_str, roletype->type_str);
+ return SEPOL_OK;
+}
+
+static int cil_write_roleattribute(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_roleattribute *roleattr = (struct cil_roleattribute *)node->data;
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_ROLEATTRIBUTE, roleattr->datum.name);
+ return SEPOL_OK;
+}
+
+static int cil_write_type(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_type *type = (struct cil_type *)node->data;
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_TYPE, type->datum.name);
+ return SEPOL_OK;
+}
+
+static int cil_write_typepermissive(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_typepermissive *type = (struct cil_typepermissive *)node->data;
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_TYPEPERMISSIVE, type->type_str);
+ return SEPOL_OK;
+}
+
+static int cil_write_typeattribute(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_typeattribute *typeattr = (struct cil_typeattribute *)node->data;
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_TYPEATTRIBUTE, typeattr->datum.name);
+ return SEPOL_OK;
+}
+
+static int cil_write_typeattributeset(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ char *typeattr;
+ char *set_str = NULL;
+ struct cil_typeattributeset *typeattrset = (struct cil_typeattributeset *)node->data;
+
+ typeattr = typeattrset->attr_str;
+ rc = cil_unfill_expr(typeattrset->str_expr, &set_str, 1);
+ if (rc != SEPOL_OK)
+ goto exit;
+
+ fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_TYPEATTRIBUTESET, typeattr, set_str);
+ rc = SEPOL_OK;
+exit:
+ free(set_str);
+ return rc;
+}
+
+static int cil_write_expandtypeattribute(struct cil_tree_node *node, FILE *cil_out)
+{
+ int rc = SEPOL_ERR;
+ char *attr_strs = NULL;
+ struct cil_expandtypeattribute *expandattr = (struct cil_expandtypeattribute *)node->data;
+
+ rc = cil_unfill_expr(expandattr->attr_strs, &attr_strs, 1);
+ if (rc != SEPOL_OK)
+ goto exit;
+
+ fprintf(cil_out, "(%s %s %s)\n", CIL_KEY_EXPANDTYPEATTRIBUTE, attr_strs,
+ expandattr->expand ? CIL_KEY_CONDTRUE : CIL_KEY_CONDFALSE);
+ rc = SEPOL_OK;
+exit:
+ free(attr_strs);
+ return rc;
+}
+
+static int cil_write_alias(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ char *type;
+ struct cil_alias *alias = (struct cil_alias *)node->data;
+
+ switch (node->flavor) {
+ case CIL_TYPEALIAS:
+ type = CIL_KEY_TYPEALIAS;
+ break;
+ case CIL_SENSALIAS:
+ type = CIL_KEY_SENSALIAS;
+ break;
+ case CIL_CATALIAS:
+ type = CIL_KEY_CATALIAS;
+ break;
+ default:
+ cil_log(CIL_ERR, "Unknown alias type: %d\n", node->flavor);
+ rc = SEPOL_ERR;
+ goto exit;
+ break;
+ }
+ fprintf(cil_out, "(%s %s)\n", type, alias->datum.name);
+ rc = SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int cil_write_aliasactual(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ char *type, *alias, *actual;
+ struct cil_aliasactual *aliasact = (struct cil_aliasactual *)node->data;
+
+ switch (node->flavor) {
+ case CIL_TYPEALIASACTUAL:
+ type = CIL_KEY_TYPEALIASACTUAL;
+ break;
+ case CIL_SENSALIASACTUAL:
+ type = CIL_KEY_SENSALIASACTUAL;
+ break;
+ case CIL_CATALIASACTUAL:
+ type = CIL_KEY_CATALIASACTUAL;
+ break;
+ default:
+ cil_log(CIL_ERR, "Unknown alias type: %d\n", node->flavor);
+ rc = SEPOL_ERR;
+ goto exit;
+ break;
+ }
+ alias = aliasact->alias_str;
+ actual = aliasact->actual_str;
+ fprintf(cil_out, "(%s %s %s)\n", type, alias, actual);
+ rc = SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int cil_write_nametypetransition(struct cil_tree_node *node, FILE *cil_out) {
+ char *src, *tgt, *obj, *res, *name;
+ struct cil_nametypetransition *ntrans = (struct cil_nametypetransition *)node->data;
+
+ src = ntrans->src_str;
+ tgt = ntrans->tgt_str;
+ obj = ntrans->obj_str;
+ res = ntrans->result_str;
+ name = ntrans->name_str;
+ fprintf(cil_out, "(%s %s %s %s \"%s\" %s)\n", CIL_KEY_TYPETRANSITION,
+ src, tgt, obj, name, res);
+ return SEPOL_OK;
+}
+
+static int cil_write_avrule_x(struct cil_avrule *avrule, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ char *rulekind, *src, *tgt;
+ char *xperms = NULL;
+
+ switch (avrule->rule_kind) {
+ case CIL_AVRULE_ALLOWED:
+ rulekind = CIL_KEY_ALLOWX;
+ break;
+ case CIL_AVRULE_AUDITALLOW:
+ rulekind = CIL_KEY_AUDITALLOWX;
+ break;
+ case CIL_AVRULE_DONTAUDIT:
+ rulekind = CIL_KEY_DONTAUDITX;
+ break;
+ case CIL_AVRULE_NEVERALLOW:
+ rulekind = CIL_KEY_NEVERALLOWX;
+ break;
+ default:
+ cil_log(CIL_ERR, "Unknown AVRULE type: %d\n", avrule->rule_kind);
+ rc = SEPOL_ERR;
+ goto exit;
+ break;
+ }
+ src = avrule->src_str;
+ tgt = avrule->tgt_str;
+
+ if (avrule->perms.x.permx_str != NULL) {
+ xperms = strdup(avrule->perms.x.permx_str);
+ if (xperms == NULL) {
+ cil_log(CIL_ERR, "OOM. Unable to copy xperms string.\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ } else {
+ rc = cil_unfill_permx(avrule->perms.x.permx, &xperms);
+ if (rc != SEPOL_OK)
+ goto exit;
+ }
+ fprintf(cil_out, "(%s %s %s %s)\n", rulekind, src, tgt, xperms);
+ rc = SEPOL_OK;
+exit:
+ free(xperms);
+ return rc;
+}
+
+static int cil_write_avrule_orig(struct cil_avrule *avrule, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ char *rulekind, *src, *tgt;
+ char *classperms = NULL;
+
+ switch (avrule->rule_kind) {
+ case CIL_AVRULE_ALLOWED:
+ rulekind = CIL_KEY_ALLOW;
+ break;
+ case CIL_AVRULE_AUDITALLOW:
+ rulekind = CIL_KEY_AUDITALLOW;
+ break;
+ case CIL_AVRULE_DONTAUDIT:
+ rulekind = CIL_KEY_DONTAUDIT;
+ break;
+ case CIL_AVRULE_NEVERALLOW:
+ rulekind = CIL_KEY_NEVERALLOW;
+ break;
+ default:
+ cil_log(CIL_ERR, "Unknown AVRULE type: %d\n", avrule->rule_kind);
+ rc = SEPOL_ERR;
+ goto exit;
+ break;
+ }
+ src = avrule->src_str;
+ tgt = avrule->tgt_str;
+
+ rc = cil_unfill_classperms_list(avrule->perms.classperms, &classperms, 0);
+ if (rc != SEPOL_OK)
+ goto exit;
+ fprintf(cil_out, "(%s %s %s %s)\n", rulekind, src, tgt, classperms);
+ rc = SEPOL_OK;
+exit:
+ free(classperms);
+ return rc;
+}
+
+static int cil_write_avrule(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ struct cil_avrule *avrule = (struct cil_avrule *)node->data;
+
+ if (avrule->is_extended)
+ rc = cil_write_avrule_x(avrule, cil_out);
+ else
+ rc = cil_write_avrule_orig(avrule, cil_out);
+ return rc;
+}
+
+static int cil_write_type_rule(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ char *type, *src, *tgt, *obj, *res;
+ struct cil_type_rule *typerule = (struct cil_type_rule *)node->data;
+
+ switch (typerule->rule_kind) {
+ case CIL_TYPE_TRANSITION:
+ type = CIL_KEY_TYPETRANSITION;
+ break;
+ case CIL_TYPE_MEMBER:
+ type = CIL_KEY_TYPEMEMBER;
+ break;
+ case CIL_TYPE_CHANGE:
+ type = CIL_KEY_TYPECHANGE;
+ break;
+ default:
+ cil_log(CIL_ERR, "Unknown TYPERULE type: %d\n", typerule->rule_kind);
+ rc = SEPOL_ERR;
+ goto exit;
+ break;
+ }
+ src = typerule->src_str;
+ tgt = typerule->tgt_str;
+ obj = typerule->obj_str;
+ res = typerule->result_str;
+ fprintf(cil_out, "(%s %s %s %s %s)\n", type, src, tgt, obj, res);
+ rc = SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int cil_write_sens(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_sens *sens = (struct cil_sens *)node->data;
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_SENSITIVITY, sens->datum.name);
+ return SEPOL_OK;
+}
+
+static int cil_write_cat(struct cil_tree_node *node, FILE *cil_out) {
+ struct cil_cat *cat = (struct cil_cat *)node->data;
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_CATEGORY, cat->datum.name);
+ return SEPOL_OK;
+}
+
+static int cil_write_senscat(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ char *sens;
+ char *cats = NULL;
+ struct cil_senscat *senscat = (struct cil_senscat *)node->data;
+
+ sens = senscat->sens_str;
+ rc = cil_unfill_cats(senscat->cats, &cats);
+ if (rc != SEPOL_OK)
+ goto exit;
+ /* TODO: deal with extra/missing parens */
+ fprintf(cil_out, "(%s %s (%s))\n", CIL_KEY_SENSCAT, sens, cats);
+ rc = SEPOL_OK;
+exit:
+ free(cats);
+ return rc;
+}
+
+static int cil_write_catorder(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ char *ord_str = NULL;
+ struct cil_catorder *catord = (struct cil_catorder *)node->data;
+
+ /* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
+ rc = cil_unfill_expr(catord->cat_list_str, &ord_str, 1);
+ if (rc != SEPOL_OK)
+ goto exit;
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_CATORDER, ord_str);
+ rc = SEPOL_OK;
+exit:
+ free(ord_str);
+ return rc;
+}
+
+static int cil_write_sensorder(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ char *ord_str = NULL;
+ struct cil_sensorder *sensord = (struct cil_sensorder *)node->data;
+
+ /* cil_unfill_expr() has logic to stringify a cil_list, reuse that. */
+ rc = cil_unfill_expr(sensord->sens_list_str, &ord_str, 1);
+ if (rc != SEPOL_OK)
+ goto exit;
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_SENSITIVITYORDER, ord_str);
+ rc = SEPOL_OK;
+exit:
+ free(ord_str);
+ return rc;
+}
+
+static int cil_write_genfscon(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ char *ctx_str = NULL;
+
+ struct cil_genfscon *genfscon = (struct cil_genfscon *)node->data;
+ if (genfscon->context_str != NULL) {
+ ctx_str = strdup(genfscon->context_str);
+ if (ctx_str == NULL) {
+ cil_log(CIL_ERR, "OOM. Unable to copy context string.\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ } else {
+ rc = cil_unfill_context(genfscon->context, &ctx_str);
+ if (rc != SEPOL_OK)
+ goto exit;
+ }
+ fprintf(cil_out, "(%s %s %s %s)\n", CIL_KEY_GENFSCON, genfscon->fs_str,
+ genfscon->path_str, ctx_str);
+ rc = SEPOL_OK;
+exit:
+ free(ctx_str);
+ return rc;
+}
+
+static int cil_unfill_classperms(struct cil_list_item *curr, char **out_str) {
+ int rc = SEPOL_ERR;
+ size_t len = 3;
+ char *class_str;
+ char *perms_str = NULL;
+ struct cil_classperms *cp = (struct cil_classperms *)curr->data;
+
+ class_str = cp->class_str;
+ len += strlen(class_str) + 1;
+
+ /* fill_perms just calls gen_expr */
+ rc = cil_unfill_expr(cp->perm_strs, &perms_str, 1);
+ if (rc != SEPOL_OK)
+ goto exit;
+ len += strlen(perms_str);
+ *out_str = cil_malloc(len);
+ sprintf(*out_str, "(%s %s)", class_str, perms_str);
+ rc = SEPOL_OK;
+exit:
+ free(perms_str);
+ return rc;
+}
+
+static int cil_unfill_classperms_list(struct cil_list *classperms, char **out_str, int paren) {
+ int rc = SEPOL_ERR;
+ struct cil_list_item *curr;
+ char *str = NULL;
+
+ /* reuse cil_list to keep track of strings */
+ struct cil_list *str_list = NULL;
+ cil_list_init(&str_list, CIL_NONE);
+ cil_list_for_each(curr, classperms) {
+ switch (curr->flavor) {
+ case CIL_CLASSPERMS_SET:
+ str = strdup(((struct cil_classperms_set *)curr->data)->set_str);
+ if (str == NULL) {
+ cil_log(CIL_ERR, "OOM. Unable to copy classpermset.\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ break;
+ case CIL_CLASSPERMS:
+ rc = cil_unfill_classperms(curr, &str);
+ if (rc != SEPOL_OK)
+ goto exit;
+ break;
+ default:
+ cil_log(CIL_ERR, "Unrecognized classperms flavor\n.");
+ goto exit;
+ }
+ cil_list_append(str_list, CIL_STRING, (void *) str);
+ str = NULL;
+ }
+ rc = __cil_strlist_concat(str_list, out_str, paren);
+ if (rc != SEPOL_OK)
+ goto exit;
+ rc = SEPOL_OK;
+exit:
+ cil_list_for_each(curr, str_list) {
+ free(curr->data);
+ }
+ cil_list_destroy(&str_list, 0);
+ return rc;
+}
+
+static int cil_write_fsuse(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ struct cil_fsuse *fsuse = (struct cil_fsuse *)node->data;
+ char *type, *fsname;
+ char *ctx_str = NULL;
+
+ switch(fsuse->type) {
+ case CIL_FSUSE_XATTR:
+ type = CIL_KEY_XATTR;
+ break;
+ case CIL_FSUSE_TASK:
+ type = CIL_KEY_TASK;
+ break;
+ case CIL_FSUSE_TRANS:
+ type = CIL_KEY_TRANS;
+ break;
+ default:
+ cil_log(CIL_ERR, "Unrecognized fsuse type\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ break;
+ }
+
+ fsname = fsuse->fs_str;
+ if (fsuse->context_str != NULL) {
+ ctx_str = strdup(fsuse->context_str);
+ if (ctx_str == NULL) {
+ cil_log(CIL_ERR, "OOM. Unable to copy context string.\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+ } else {
+ rc = cil_unfill_context(fsuse->context, &ctx_str);
+ if (rc != SEPOL_OK)
+ goto exit;
+ }
+ fprintf(cil_out, "(%s %s %s %s)\n", CIL_KEY_FSUSE, type, fsname, ctx_str);
+exit:
+ free(ctx_str);
+ return rc;
+}
+
+static int cil_write_constrain(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_ERR;
+ struct cil_constrain *cons = (struct cil_constrain *)node->data;
+ char *flav;
+ char *classperms = NULL;
+ char *expr = NULL;
+
+ flav = (node->flavor == CIL_CONSTRAIN) ? CIL_KEY_CONSTRAIN : CIL_KEY_MLSCONSTRAIN;
+
+ rc = cil_unfill_classperms_list(cons->classperms, &classperms, 0);
+ if (rc != SEPOL_OK)
+ goto exit;
+
+ rc = cil_unfill_expr(cons->str_expr, &expr, 0);
+ if (rc != SEPOL_OK)
+ goto exit;
+
+ fprintf(cil_out, "(%s %s %s)\n", flav, classperms, expr);
+exit:
+ free(classperms);
+ free(expr);
+ return rc;
+}
+
+static int cil_write_handleunknown(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_OK;
+ struct cil_handleunknown *handunknown = (struct cil_handleunknown *)node->data;
+ char *val = NULL;
+ switch (handunknown->handle_unknown) {
+ case SEPOL_ALLOW_UNKNOWN:
+ val = CIL_KEY_HANDLEUNKNOWN_ALLOW;
+ break;
+ case SEPOL_DENY_UNKNOWN:
+ val = CIL_KEY_HANDLEUNKNOWN_DENY;
+ break;
+ case SEPOL_REJECT_UNKNOWN:
+ val = CIL_KEY_HANDLEUNKNOWN_REJECT;
+ break;
+ default:
+ cil_log(CIL_ERR, "Unknown handleunknown value: %d.\n",
+ handunknown->handle_unknown);
+ rc = SEPOL_ERR;
+ goto exit;
+ break;
+ }
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_HANDLEUNKNOWN, val);
+exit:
+ return rc;
+}
+
+static int cil_write_mls(struct cil_tree_node *node, FILE *cil_out) {
+ int rc = SEPOL_OK;
+ struct cil_mls *mls = (struct cil_mls *)node->data;
+ char *val = NULL;
+ switch (mls->value) {
+ case CIL_TRUE:
+ val = CIL_KEY_CONDTRUE;
+ break;
+ case CIL_FALSE:
+ val = CIL_KEY_CONDFALSE;
+ break;
+ default:
+ cil_log(CIL_ERR, "Unknown mls value: %d.\n", mls->value);
+ rc = SEPOL_ERR;
+ goto exit;
+ break;
+ }
+ fprintf(cil_out, "(%s %s)\n", CIL_KEY_MLS, val);
+exit:
+ return rc;
+}
+
+static int __cil_write_first_child_helper(struct cil_tree_node *node, void *extra_args)
+{
+ int rc = SEPOL_ERR;
+ struct cil_args_write *args = (struct cil_args_write *) extra_args;
+ FILE *cil_out = NULL;
+
+ if (node == NULL || extra_args == NULL) {
+ goto exit;
+ }
+
+ cil_out = args->cil_out;
+
+ if (node->parent && node->parent->flavor != CIL_ROOT && node->parent->flavor != CIL_SRC_INFO)
+ fprintf(cil_out,"(");
+ rc = SEPOL_OK;
+exit:
+ return rc;
+}
+
+static int __cil_write_node_helper(struct cil_tree_node *node, uint32_t *finished, void *extra_args)
+{
+ int rc = SEPOL_OK;
+ struct cil_args_write *args = NULL;
+ FILE *cil_out = NULL;
+
+ if (node == NULL || extra_args == NULL) {
+ goto exit;
+ }
+
+ args = extra_args;
+ cil_out = args->cil_out;
+
+ switch (node->flavor) {
+ case CIL_BLOCK:
+ rc = cil_write_unsupported("CIL_BLOCK");
+ break;
+ case CIL_BLOCKABSTRACT:
+ rc = cil_write_unsupported("CIL_BLOCKABSTRACT");
+ break;
+ case CIL_BLOCKINHERIT:
+ rc = cil_write_unsupported("CIL_BLOCKINHERIT");
+ break;
+ case CIL_IN:
+ rc = cil_write_unsupported("CIL_IN");
+ break;
+ case CIL_POLICYCAP:
+ cil_write_policycap(node, cil_out);
+ break;
+ case CIL_PERM:
+ rc = cil_write_perm(node, cil_out);
+ break;
+ case CIL_MAP_PERM:
+ rc = cil_write_unsupported("CIL_MAP_PERM");
+ break;
+ case CIL_CLASSMAPPING:
+ rc = cil_write_unsupported("CIL_CLASSMAPPING");
+ break;
+ case CIL_CLASS:
+ rc = cil_write_class(node, finished, extra_args);
+ break;
+ case CIL_COMMON:
+ rc = cil_write_class(node, finished, extra_args);
+ break;
+ case CIL_MAP_CLASS:
+ rc = cil_write_unsupported("CIL_MAP_CLASS");
+ break;
+ case CIL_CLASSORDER:
+ rc = cil_write_classorder(node, cil_out);
+ break;
+ case CIL_CLASSPERMISSION:
+ rc = cil_write_unsupported("CIL_CLASSPERMISSION");
+ break;
+ case CIL_CLASSPERMISSIONSET:
+ rc = cil_write_unsupported("CIL_CLASSPERMISSIONSET");
+ break;
+ case CIL_CLASSCOMMON:
+ rc = cil_write_classcommon(node, cil_out);
+ break;
+ case CIL_SID:
+ rc = cil_write_sid(node, cil_out);
+ break;
+ case CIL_SIDCONTEXT:
+ rc = cil_write_sidcontext(node, cil_out);
+ break;
+ case CIL_SIDORDER:
+ rc = cil_write_sidorder(node, cil_out);
+ break;
+ case CIL_USER:
+ rc = cil_write_user(node, cil_out);
+ break;
+ case CIL_USERATTRIBUTE:
+ rc = cil_write_unsupported("CIL_USERATTRIBUTE");
+ break;
+ case CIL_USERATTRIBUTESET:
+ rc = cil_write_unsupported("CIL_USERATTRIBUTESET");
+ break;
+ case CIL_USERROLE:
+ rc = cil_write_userrole(node, cil_out);
+ break;
+ case CIL_USERLEVEL:
+ rc = cil_write_userlevel(node, cil_out);
+ break;
+ case CIL_USERRANGE:
+ rc = cil_write_userrange(node, cil_out);
+ break;
+ case CIL_USERBOUNDS:
+ rc = cil_write_unsupported("CIL_USERBOUNDS");
+ break;
+ case CIL_USERPREFIX:
+ rc = cil_write_unsupported("CIL_USERPREFIX");
+ break;
+ case CIL_ROLE:
+ rc = cil_write_role(node, cil_out);
+ break;
+ case CIL_ROLETYPE:
+ rc = cil_write_roletype(node, cil_out);
+ break;
+ case CIL_ROLEBOUNDS:
+ rc = cil_write_unsupported("CIL_ROLEBOUNDS");
+ break;
+ case CIL_ROLEATTRIBUTE:
+ cil_write_roleattribute(node, cil_out);
+ break;
+ case CIL_ROLEATTRIBUTESET:
+ rc = cil_write_unsupported("CIL_ROLEATTRIBUTESET");
+ break;
+ case CIL_ROLEALLOW:
+ rc = cil_write_unsupported("CIL_ROLEALLOW");
+ break;
+ case CIL_TYPE:
+ rc = cil_write_type(node, cil_out);
+ break;
+ case CIL_TYPEBOUNDS:
+ rc = cil_write_unsupported("CIL_TYPEBOUNDS");
+ break;
+ case CIL_TYPEPERMISSIVE:
+ rc = cil_write_typepermissive(node, cil_out);
+ break;
+ case CIL_TYPEATTRIBUTE:
+ rc = cil_write_typeattribute(node, cil_out);
+ break;
+ case CIL_TYPEATTRIBUTESET:
+ rc = cil_write_typeattributeset(node, cil_out);
+ break;
+ case CIL_EXPANDTYPEATTRIBUTE:
+ rc = cil_write_expandtypeattribute(node, cil_out);
+ break;
+ case CIL_TYPEALIAS:
+ rc = cil_write_alias(node, cil_out);
+ break;
+ case CIL_TYPEALIASACTUAL:
+ rc = cil_write_aliasactual(node, cil_out);
+ break;
+ case CIL_ROLETRANSITION:
+ rc = cil_write_unsupported("CIL_ROLETRANSITION");
+ break;
+ case CIL_NAMETYPETRANSITION:
+ rc = cil_write_nametypetransition(node, cil_out);
+ break;
+ case CIL_RANGETRANSITION:
+ rc = cil_write_unsupported("CIL_RANGETRANSITION");
+ break;
+ case CIL_TUNABLE:
+ rc = cil_write_unsupported("CIL_TUNABLE");
+ break;
+ case CIL_BOOL:
+ rc = cil_write_unsupported("CIL_BOOL");
+ break;
+ case CIL_AVRULE:
+ case CIL_AVRULEX:
+ rc = cil_write_avrule(node, cil_out);
+ break;
+ case CIL_PERMISSIONX:
+ rc = cil_write_unsupported("CIL_PERMISSIONX");
+ break;
+ case CIL_TYPE_RULE:
+ cil_write_type_rule(node, cil_out);
+ break;
+ case CIL_SENS:
+ rc = cil_write_sens(node, cil_out);
+ break;
+ case CIL_SENSALIAS:
+ rc = cil_write_alias(node, cil_out);
+ break;
+ case CIL_SENSALIASACTUAL:
+ rc = cil_write_aliasactual(node, cil_out);
+ break;
+ case CIL_CAT:
+ rc = cil_write_cat(node, cil_out);
+ break;
+ case CIL_CATALIAS:
+ rc = cil_write_alias(node, cil_out);
+ break;
+ case CIL_CATALIASACTUAL:
+ rc = cil_write_aliasactual(node, cil_out);
+ break;
+ case CIL_CATSET:
+ rc = cil_write_unsupported("CIL_CATSET");
+ break;
+ case CIL_SENSCAT:
+ rc = cil_write_senscat(node, cil_out);
+ break;
+ case CIL_CATORDER:
+ rc = cil_write_catorder(node, cil_out);
+ break;
+ case CIL_SENSITIVITYORDER:
+ rc = cil_write_sensorder(node, cil_out);
+ break;
+ case CIL_LEVEL:
+ rc = cil_write_unsupported("CIL_LEVEL");
+ break;
+ case CIL_LEVELRANGE:
+ rc = cil_write_unsupported("CIL_LEVELRANGE");
+ break;
+ case CIL_CONTEXT:
+ rc = cil_write_unsupported("CIL_CONTEXT");
+ break;
+ case CIL_NETIFCON:
+ rc = cil_write_unsupported("CIL_NETIFCON");
+ break;
+ case CIL_GENFSCON:
+ rc = cil_write_genfscon(node, cil_out);
+ break;
+ case CIL_FILECON:
+ rc = cil_write_unsupported("CIL_FILECON");
+ break;
+ case CIL_NODECON:
+ rc = cil_write_unsupported("CIL_NODECON");
+ break;
+ case CIL_PORTCON:
+ rc = cil_write_unsupported("CIL_PORTCON");
+ break;
+ case CIL_PIRQCON:
+ rc = cil_write_unsupported("CIL_PIRQCON");
+ break;
+ case CIL_IOMEMCON:
+ rc = cil_write_unsupported("CIL_IOMEMCON");
+ break;
+ case CIL_IOPORTCON:
+ rc = cil_write_unsupported("CIL_IOPORTCON");
+ break;
+ case CIL_PCIDEVICECON:
+ rc = cil_write_unsupported("CIL_PCIDEVICECON");
+ break;
+ case CIL_DEVICETREECON:
+ rc = cil_write_unsupported("CIL_DEVICETREECON");
+ break;
+ case CIL_FSUSE:
+ rc = cil_write_fsuse(node, cil_out);
+ break;
+ case CIL_CONSTRAIN:
+ rc = cil_write_unsupported("CIL_CONSTRAIN");
+ break;
+ case CIL_MLSCONSTRAIN:
+ rc = cil_write_constrain(node, cil_out);
+ break;
+ case CIL_VALIDATETRANS:
+ rc = cil_write_unsupported("CIL_VALIDATETRANS");
+ break;
+ case CIL_MLSVALIDATETRANS:
+ rc = cil_write_unsupported("CIL_MLSVALIDATETRANS");
+ break;
+ case CIL_CALL:
+ rc = cil_write_unsupported("CIL_CALL");
+ break;
+ case CIL_MACRO:
+ rc = cil_write_unsupported("CIL_MACRO");
+ break;
+ case CIL_NODE:
+ rc = cil_write_unsupported("CIL_NODE");
+ break;
+ case CIL_OPTIONAL:
+ rc = cil_write_unsupported("CIL_OPTIONAL");
+ break;
+ case CIL_IPADDR:
+ rc = cil_write_unsupported("CIL_IPADDR");
+ break;
+ case CIL_CONDBLOCK:
+ rc = cil_write_unsupported("CIL_CONDBLOCK");
+ break;
+ case CIL_BOOLEANIF:
+ rc = cil_write_unsupported("CIL_BOOLEANIF");
+ break;
+ case CIL_TUNABLEIF:
+ rc = cil_write_unsupported("CIL_TUNABLEIF");
+ break;
+ case CIL_DEFAULTUSER:
+ rc = cil_write_unsupported("CIL_DEFAULTUSER");
+ break;
+ case CIL_DEFAULTROLE:
+ rc = cil_write_unsupported("CIL_DEFAULTROLE");
+ break;
+ case CIL_DEFAULTTYPE:
+ rc = cil_write_unsupported("CIL_DEFAULTTYPE");
+ break;
+ case CIL_DEFAULTRANGE:
+ rc = cil_write_unsupported("CIL_DEFAULTRANGE");
+ break;
+ case CIL_SELINUXUSER:
+ rc = cil_write_unsupported("CIL_SELINUXUSER");
+ break;
+ case CIL_SELINUXUSERDEFAULT:
+ rc = cil_write_unsupported("CIL_SELINUXUSERDEFAULT");
+ break;
+ case CIL_HANDLEUNKNOWN:
+ rc = cil_write_handleunknown(node, cil_out);
+ break;
+ case CIL_MLS:
+ rc = cil_write_mls(node, cil_out);
+ break;
+ case CIL_SRC_INFO:
+ break;
+ case CIL_NONE:
+ // TODO: add proper removal support
+ *finished = CIL_TREE_SKIP_HEAD;
+ break;
+ default:
+ cil_log(CIL_ERR, "Unknown AST flavor: %d.\n", node->flavor);
+ rc = SEPOL_ERR;
+ goto exit;
+ break;
+ }
+exit:
+ return rc;
+}
+
+static int __cil_write_last_child_helper(struct cil_tree_node *node, void *extra_args)
+{
+ int rc = SEPOL_ERR;
+ struct cil_args_write *args = NULL;
+ FILE *cil_out = NULL;
+
+ if (node == NULL || extra_args == NULL) {
+ goto exit;
+ }
+
+ args = extra_args;
+ cil_out = args->cil_out;
+
+ if (node->parent && node->parent->flavor != CIL_ROOT && node->parent->flavor != CIL_SRC_INFO) {
+ fprintf(cil_out,")");
+ }
+ rc = SEPOL_OK;
+exit:
+ return rc;
+}
+
+/* main exported function */
+int cil_write_ast(struct cil_db *db, const char* path) {
+ int rc = SEPOL_ERR;
+ struct cil_args_write extra_args;
+ FILE *cil_out = NULL;
+
+ cil_out = fopen(path, "we");
+ if (cil_out == NULL) {
+ cil_log(CIL_ERR, "Failure opening output file for writing AST\n");
+ rc = SEPOL_ERR;
+ goto exit;
+ }
+
+ extra_args.cil_out = cil_out;
+ extra_args.db = db;
+ rc = cil_tree_walk(db->ast->root, __cil_write_node_helper,
+ __cil_write_first_child_helper,
+ __cil_write_last_child_helper,
+ &extra_args);
+ if (rc != SEPOL_OK) {
+ cil_log(CIL_INFO, "cil_tree_walk failed, rc: %d\n", rc);
+ goto exit;
+ }
+
+exit:
+ fclose(cil_out);
+ cil_out = NULL;
+ return rc;
+}
diff --git a/prebuilts/bin/audit2allow b/prebuilts/bin/audit2allow
new file mode 100755
index 00000000..0d96ca60
--- /dev/null
+++ b/prebuilts/bin/audit2allow
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+if [ -z "${ANDROID_HOST_OUT}" ]; then
+ echo 'ANDROID_HOST_OUT not set. Have you run lunch?'
+ exit 1
+elif [ -z "${ANDROID_BUILD_TOP}" ]; then
+ echo 'ANDROID_BUILD_TOP not set. Have you run lunch?'
+ exit 1
+elif [ ! -f "$ANDROID_HOST_OUT/lib64/libselinux.so" ]; then
+ echo "Cannot find $ANDROID_HOST_OUT/lib64/libselinux.so"
+ echo "Need to build project"
+ exit 1
+fi
+
+unamestr=`uname`
+if [ "$unamestr" = "Linux" -o "$unamestr" = "linux" ]; then
+ export LD_PRELOAD=$ANDROID_HOST_OUT/lib64/libselinux.so
+ export PYTHONPATH=$ANDROID_BUILD_TOP/prebuilts/python/linux-x86/2.7.5/lib/python2.7/site-packages
+ python $ANDROID_BUILD_TOP/external/selinux/python/audit2allow/audit2allow "$@"
+else
+ echo "audit2allow is only supported on linux"
+ exit 1
+fi
diff --git a/prebuilts/bin/audit2why b/prebuilts/bin/audit2why
new file mode 100755
index 00000000..38dd602b
--- /dev/null
+++ b/prebuilts/bin/audit2why
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+if [ -z "${ANDROID_HOST_OUT}" ]; then
+ echo 'ANDROID_HOST_OUT not set. Have you run lunch?'
+ exit 1
+elif [ -z "${ANDROID_BUILD_TOP}" ]; then
+ echo 'ANDROID_BUILD_TOP not set. Have you run lunch?'
+ exit 1
+elif [ ! -f "$ANDROID_HOST_OUT/lib64/libselinux.so" ]; then
+ echo "Cannot find $ANDROID_HOST_OUT/lib64/libselinux.so"
+ echo "Need to build project"
+ exit 1
+fi
+
+unamestr=`uname`
+if [ "$unamestr" = "Linux" -o "$unamestr" = "linux" ]; then
+ export LD_PRELOAD=$ANDROID_HOST_OUT/lib64/libselinux.so
+ export PYTHONPATH=$ANDROID_BUILD_TOP/prebuilts/python/linux-x86/2.7.5/lib/python2.7/site-packages
+ exec python $ANDROID_BUILD_TOP/external/selinux/python/audit2allow/audit2why "$@"
+else
+ echo "audit2why is only supported on linux"
+ exit 1
+fi
diff --git a/prebuilts/bin/sediff b/prebuilts/bin/sediff
new file mode 100755
index 00000000..d9072266
--- /dev/null
+++ b/prebuilts/bin/sediff
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+if [ -z "${ANDROID_BUILD_TOP}" ]; then
+ echo 'ANDROID_BUILD_TOP not set. Have you run lunch?'
+ exit 1
+fi
+
+unamestr=`uname`
+if [ "$unamestr" = "Linux" -o "$unamestr" = "linux" ]; then
+ export PYTHONPATH=$ANDROID_BUILD_TOP/prebuilts/python/linux-x86/2.7.5/lib/python2.7/site-packages
+ python $ANDROID_BUILD_TOP/external/selinux/prebuilts/bin/sediff.py "$@"
+else
+ echo "sediff is only supported on linux"
+ exit 1
+fi
diff --git a/prebuilts/bin/sediff.py b/prebuilts/bin/sediff.py
new file mode 100755
index 00000000..acb67198
--- /dev/null
+++ b/prebuilts/bin/sediff.py
@@ -0,0 +1,1366 @@
+#!/usr/bin/env python
+# Copyright 2015-2016, Tresys Technology, LLC
+#
+# This file is part of SETools.
+#
+# SETools 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.
+#
+# SETools 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 SETools. If not, see <http://www.gnu.org/licenses/>.
+#
+
+from __future__ import print_function
+import setools
+import argparse
+import sys
+import logging
+from itertools import chain
+
+parser = argparse.ArgumentParser(
+ description="SELinux policy semantic difference tool.",
+ epilog="If no differences are selected, all differences will be printed.")
+parser.add_argument("POLICY1", help="Path to the first SELinux policy to diff.", nargs=1)
+parser.add_argument("POLICY2", help="Path to the second SELinux policy to diff.", nargs=1)
+parser.add_argument("--version", action="version", version=setools.__version__)
+parser.add_argument("--stats", action="store_true", help="Display only statistics.")
+parser.add_argument("-v", "--verbose", action="store_true",
+ help="Print extra informational messages")
+parser.add_argument("--debug", action="store_true", dest="debug", help="Enable debugging.")
+
+comp = parser.add_argument_group("component differences")
+comp.add_argument("--common", action="store_true", help="Print common differences")
+comp.add_argument("-c", "--class", action="store_true", help="Print class differences",
+ dest="class_")
+comp.add_argument("-t", "--type", action="store_true", help="Print type differences",
+ dest="type_")
+comp.add_argument("-a", "--attribute", action="store_true", help="Print type attribute differences")
+comp.add_argument("-r", "--role", action="store_true", help="Print role differences")
+comp.add_argument("-u", "--user", action="store_true", help="Print user differences")
+comp.add_argument("-b", "--bool", action="store_true", help="Print Boolean differences",
+ dest="bool_")
+comp.add_argument("--sensitivity", action="store_true", help="Print MLS sensitivity differences")
+comp.add_argument("--category", action="store_true", help="Print MLS category differences")
+comp.add_argument("--level", action="store_true", help="Print MLS level definition differences")
+
+terule = parser.add_argument_group("type enforcement rule differences")
+terule.add_argument("-A", action="store_true", help="Print allow and allowxperm rule differences")
+terule.add_argument("--allow", action="store_true", help="Print allow rule differences")
+terule.add_argument("--neverallow", action="store_true", help="Print neverallow rule differences")
+terule.add_argument("--auditallow", action="store_true", help="Print auditallow rule differences")
+terule.add_argument("--dontaudit", action="store_true", help="Print dontaudit rule differences")
+terule.add_argument("--allowxperm", action="store_true", help="Print allowxperm rule differences")
+terule.add_argument("--neverallowxperm", action="store_true",
+ help="Print neverallowxperm rule differences")
+terule.add_argument("--auditallowxperm", action="store_true",
+ help="Print auditallowxperm rule differences")
+terule.add_argument("--dontauditxperm", action="store_true",
+ help="Print dontauditxperm rule differences")
+terule.add_argument("-T", "--type_trans", action="store_true",
+ help="Print type_transition rule differences")
+terule.add_argument("--type_change", action="store_true", help="Print type_change rule differences")
+terule.add_argument("--type_member", action="store_true",
+ help="Print type_member rule differences")
+
+rbacrule = parser.add_argument_group("RBAC rule differences")
+rbacrule.add_argument("--role_allow", action="store_true", help="Print role allow rule differences")
+rbacrule.add_argument("--role_trans", action="store_true",
+ help="Print role_transition rule differences")
+
+mlsrule = parser.add_argument_group("MLS rule differences")
+mlsrule.add_argument("--range_trans", action="store_true",
+ help="Print range_transition rule differences")
+
+constrain = parser.add_argument_group("Constraint differences")
+constrain.add_argument("--constrain", action="store_true", help="Print constrain differences")
+constrain.add_argument("--mlsconstrain", action="store_true", help="Print mlsconstrain differences")
+constrain.add_argument("--validatetrans", action="store_true",
+ help="Print validatetrans differences")
+constrain.add_argument("--mlsvalidatetrans", action="store_true",
+ help="Print mlsvalidatetrans differences")
+
+labeling = parser.add_argument_group("labeling statement differences")
+labeling.add_argument("--initialsid", action="store_true", help="Print initial SID differences")
+labeling.add_argument("--fs_use", action="store_true", help="Print fs_use_* differences")
+labeling.add_argument("--genfscon", action="store_true", help="Print genfscon differences")
+labeling.add_argument("--netifcon", action="store_true", help="Print netifcon differences")
+labeling.add_argument("--nodecon", action="store_true", help="Print nodecon differences")
+labeling.add_argument("--portcon", action="store_true", help="Print portcon differences")
+
+other = parser.add_argument_group("other differences")
+other.add_argument("--default", action="store_true", help="Print default_* differences")
+other.add_argument("--property", action="store_true",
+ help="Print policy property differences (handle_unknown, version, MLS)")
+other.add_argument("--polcap", action="store_true", help="Print policy capability differences")
+other.add_argument("--typebounds", action="store_true", help="Print typebounds differences")
+
+args = parser.parse_args()
+
+if args.A:
+ args.allow = True
+ args.allowxperm = True
+
+all_differences = not any((args.class_, args.common, args.type_, args.attribute, args.role,
+ args.user, args.bool_, args.sensitivity, args.category, args.level,
+ args.allow, args.neverallow, args.auditallow, args.dontaudit,
+ args.type_trans, args.type_change, args.type_member, args.role_allow,
+ args.role_trans, args.range_trans, args.initialsid, args.genfscon,
+ args.netifcon, args.nodecon, args.portcon, args.fs_use, args.polcap,
+ args.property, args.default, args.constrain, args.mlsconstrain,
+ args.validatetrans, args.mlsvalidatetrans, args.typebounds,
+ args.allowxperm, args.neverallowxperm, args.auditallowxperm,
+ args.dontauditxperm))
+
+if args.debug:
+ logging.basicConfig(level=logging.DEBUG,
+ format='%(asctime)s|%(levelname)s|%(name)s|%(message)s')
+elif args.verbose:
+ logging.basicConfig(level=logging.INFO, format='%(message)s')
+else:
+ logging.basicConfig(level=logging.WARNING, format='%(message)s')
+
+try:
+ p1 = setools.SELinuxPolicy(args.POLICY1[0])
+ p2 = setools.SELinuxPolicy(args.POLICY2[0])
+ diff = setools.PolicyDifference(p1, p2)
+
+ if all_differences or args.property:
+ print("Policy Properties ({0} Modified)".format(len(diff.modified_properties)))
+
+ if diff.modified_properties and not args.stats:
+ for name, added, removed in sorted(diff.modified_properties, key=lambda x: x.property):
+ print(" * {0} +{1} -{2}".format(name, added, removed))
+
+ print()
+
+ if all_differences or args.common:
+ if diff.added_commons or diff.removed_commons or diff.modified_commons or args.common:
+ print("Commons ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_commons), len(diff.removed_commons), len(diff.modified_commons)))
+ if diff.added_commons and not args.stats:
+ print(" Added Commons: {0}".format(len(diff.added_commons)))
+ for c in sorted(diff.added_commons):
+ print(" + {0}".format(c))
+ if diff.removed_commons and not args.stats:
+ print(" Removed Commons: {0}".format(len(diff.removed_commons)))
+ for c in sorted(diff.removed_commons):
+ print(" - {0}".format(c))
+ if diff.modified_commons and not args.stats:
+ print(" Modified Commons: {0}".format(len(diff.modified_commons)))
+ for name, mod in sorted(diff.modified_commons.items()):
+ change = []
+ if mod.added_perms:
+ change.append("{0} Added permissions".format(len(mod.added_perms)))
+ if mod.removed_perms:
+ change.append("{0} Removed permissions".format(len(mod.removed_perms)))
+
+ print(" * {0} ({1})".format(name, ", ".join(change)))
+ for p in sorted(mod.added_perms):
+ print(" + {0}".format(p))
+ for p in sorted(mod.removed_perms):
+ print(" - {0}".format(p))
+ print()
+
+ if all_differences or args.class_:
+ if diff.added_classes or diff.removed_classes or diff.modified_classes or args.class_:
+ print("Classes ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_classes), len(diff.removed_classes), len(diff.modified_classes)))
+ if diff.added_classes and not args.stats:
+ print(" Added Classes: {0}".format(len(diff.added_classes)))
+ for c in sorted(diff.added_classes):
+ print(" + {0}".format(c))
+ if diff.removed_classes and not args.stats:
+ print(" Removed Classes: {0}".format(len(diff.removed_classes)))
+ for c in sorted(diff.removed_classes):
+ print(" - {0}".format(c))
+ if diff.modified_classes and not args.stats:
+ print(" Modified Classes: {0}".format(len(diff.modified_classes)))
+ for name, mod in sorted(diff.modified_classes.items()):
+ change = []
+ if mod.added_perms:
+ change.append("{0} Added permissions".format(len(mod.added_perms)))
+ if mod.removed_perms:
+ change.append("{0} Removed permissions".format(len(mod.removed_perms)))
+
+ print(" * {0} ({1})".format(name, ", ".join(change)))
+ for p in sorted(mod.added_perms):
+ print(" + {0}".format(p))
+ for p in sorted(mod.removed_perms):
+ print(" - {0}".format(p))
+ print()
+
+ if all_differences or args.bool_:
+ if diff.added_booleans or diff.removed_booleans or \
+ diff.modified_booleans or args.bool_:
+ print("Booleans ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_booleans), len(diff.removed_booleans),
+ len(diff.modified_booleans)))
+ if diff.added_booleans and not args.stats:
+ print(" Added Booleans: {0}".format(len(diff.added_booleans)))
+ for a in sorted(diff.added_booleans):
+ print(" + {0}".format(a))
+ if diff.removed_booleans and not args.stats:
+ print(" Removed Booleans: {0}".format(len(diff.removed_booleans)))
+ for a in sorted(diff.removed_booleans):
+ print(" - {0}".format(a))
+ if diff.modified_booleans and not args.stats:
+ print(" Modified Booleans: {0}".format(len(diff.modified_booleans)))
+ for name, mod in sorted(diff.modified_booleans.items()):
+ print(" * {0} (Modified default state)".format(name))
+ print(" + {0}".format(mod.added_state))
+ print(" - {0}".format(mod.removed_state))
+
+ print()
+
+ if all_differences or args.role:
+ if diff.added_roles or diff.removed_roles or diff.modified_roles or args.role:
+ print("Roles ({0} Added, {1} Removed, {2} Modified)".format(len(diff.added_roles),
+ len(diff.removed_roles),
+ len(diff.modified_roles)))
+ if diff.added_roles and not args.stats:
+ print(" Added Roles: {0}".format(len(diff.added_roles)))
+ for r in sorted(diff.added_roles):
+ print(" + {0}".format(r))
+ if diff.removed_roles and not args.stats:
+ print(" Removed Roles: {0}".format(len(diff.removed_roles)))
+ for r in sorted(diff.removed_roles):
+ print(" - {0}".format(r))
+ if diff.modified_roles and not args.stats:
+ print(" Modified Roles: {0}".format(len(diff.modified_roles)))
+ for name, mod in sorted(diff.modified_roles.items()):
+ change = []
+ if mod.added_types:
+ change.append("{0} Added types".format(len(mod.added_types)))
+ if mod.removed_types:
+ change.append("{0} Removed types".format(len(mod.removed_types)))
+
+ print(" * {0} ({1})".format(name, ", ".join(change)))
+ for t in sorted(mod.added_types):
+ print(" + {0}".format(t))
+ for t in sorted(mod.removed_types):
+ print(" - {0}".format(t))
+ print()
+
+ if all_differences or args.type_:
+ if diff.added_types or diff.removed_types or diff.modified_types or args.type_:
+ print("Types ({0} Added, {1} Removed, {2} Modified)".format(len(diff.added_types),
+ len(diff.removed_types),
+ len(diff.modified_types)))
+ if diff.added_types and not args.stats:
+ print(" Added Types: {0}".format(len(diff.added_types)))
+ for r in sorted(diff.added_types):
+ print(" + {0}".format(r))
+ if diff.removed_types and not args.stats:
+ print(" Removed Types: {0}".format(len(diff.removed_types)))
+ for r in sorted(diff.removed_types):
+ print(" - {0}".format(r))
+ if diff.modified_types and not args.stats:
+ print(" Modified Types: {0}".format(len(diff.modified_types)))
+ for name, mod in sorted(diff.modified_types.items()):
+ change = []
+ if mod.added_attributes:
+ change.append("{0} Added attributes".format(len(mod.added_attributes)))
+ if mod.removed_attributes:
+ change.append("{0} Removed attributes".format(len(mod.removed_attributes)))
+ if mod.added_aliases:
+ change.append("{0} Added aliases".format(len(mod.added_aliases)))
+ if mod.removed_aliases:
+ change.append("{0} Removed aliases".format(len(mod.removed_aliases)))
+ if mod.modified_permissive:
+ if mod.permissive:
+ change.append("Removed permissive")
+ else:
+ change.append("Added permissive")
+
+ print(" * {0} ({1})".format(name, ", ".join(change)))
+ if mod.added_attributes or mod.removed_attributes:
+ print(" Attributes:")
+ for t in sorted(mod.added_attributes):
+ print(" + {0}".format(t))
+ for t in sorted(mod.removed_attributes):
+ print(" - {0}".format(t))
+
+ if mod.added_aliases or mod.removed_aliases:
+ print(" Aliases:")
+ for t in sorted(mod.added_aliases):
+ print(" + {0}".format(t))
+ for t in sorted(mod.removed_aliases):
+ print(" - {0}".format(t))
+
+ print()
+
+ if all_differences or args.attribute:
+ if diff.added_type_attributes or diff.removed_type_attributes or \
+ diff.modified_type_attributes or args.attribute:
+ print("Type Attributes ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_type_attributes), len(diff.removed_type_attributes),
+ len(diff.modified_type_attributes)))
+ if diff.added_type_attributes and not args.stats:
+ print(" Added Type Attributes: {0}".format(len(diff.added_type_attributes)))
+ for a in sorted(diff.added_type_attributes):
+ print(" + {0}".format(a))
+ if diff.removed_type_attributes and not args.stats:
+ print(" Removed Type Attributes: {0}".format(len(diff.removed_type_attributes)))
+ for a in sorted(diff.removed_type_attributes):
+ print(" - {0}".format(a))
+ if diff.modified_type_attributes and not args.stats:
+ print(" Modified Type Attributes: {0}".format(len(diff.modified_type_attributes)))
+ for name, mod in sorted(diff.modified_type_attributes.items()):
+ change = []
+ if mod.added_types:
+ change.append("{0} Added types".format(len(mod.added_types)))
+ if mod.removed_types:
+ change.append("{0} Removed types".format(len(mod.removed_types)))
+
+ print(" * {0} ({1})".format(name, ", ".join(change)))
+ for t in sorted(mod.added_types):
+ print(" + {0}".format(t))
+ for t in sorted(mod.removed_types):
+ print(" - {0}".format(t))
+ print()
+
+ if all_differences or args.user:
+ if diff.added_users or diff.removed_users or diff.modified_users or args.user:
+ print("Users ({0} Added, {1} Removed, {2} Modified)".format(len(diff.added_users),
+ len(diff.removed_users),
+ len(diff.modified_users)))
+ if diff.added_users and not args.stats:
+ print(" Added Users: {0}".format(len(diff.added_users)))
+ for u in sorted(diff.added_users):
+ print(" + {0}".format(u))
+ if diff.removed_users and not args.stats:
+ print(" Removed Users: {0}".format(len(diff.removed_users)))
+ for u in sorted(diff.removed_users):
+ print(" - {0}".format(u))
+ if diff.modified_users and not args.stats:
+ print(" Modified Users: {0}".format(len(diff.modified_users)))
+ for name, mod in sorted(diff.modified_users.items()):
+ change = []
+ if mod.added_roles:
+ change.append("{0} Added roles".format(len(mod.added_roles)))
+ if mod.removed_roles:
+ change.append("{0} Removed roles".format(len(mod.removed_roles)))
+ if mod.removed_level:
+ change.append("Modified default level")
+ if mod.removed_range:
+ change.append("Modified range")
+
+ print(" * {0} ({1})".format(name, ", ".join(change)))
+ if mod.added_roles or mod.removed_roles:
+ print(" Roles:")
+ for t in sorted(mod.added_roles):
+ print(" + {0}".format(t))
+ for t in sorted(mod.removed_roles):
+ print(" - {0}".format(t))
+
+ if mod.removed_level:
+ print(" Default level:")
+ print(" + {0}".format(mod.added_level))
+ print(" - {0}".format(mod.removed_level))
+
+ if mod.removed_range:
+ print(" Range:")
+ print(" + {0}".format(mod.added_range))
+ print(" - {0}".format(mod.removed_range))
+ print()
+
+ if all_differences or args.category:
+ if diff.added_categories or diff.removed_categories or diff.modified_categories \
+ or args.category:
+ print("Categories ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_categories), len(diff.removed_categories),
+ len(diff.modified_categories)))
+ if diff.added_categories and not args.stats:
+ print(" Added Categories: {0}".format(len(diff.added_categories)))
+ for c in sorted(diff.added_categories):
+ print(" + {0}".format(c))
+ if diff.removed_categories and not args.stats:
+ print(" Removed Categories: {0}".format(len(diff.removed_categories)))
+ for c in sorted(diff.removed_categories):
+ print(" - {0}".format(c))
+ if diff.modified_categories and not args.stats:
+ print(" Modified Categories: {0}".format(len(diff.modified_categories)))
+ for name, mod in sorted(diff.modified_categories.items()):
+ change = []
+ if mod.added_aliases:
+ change.append("{0} Added Aliases".format(len(mod.added_aliases)))
+ if mod.removed_aliases:
+ change.append("{0} Removed Aliases".format(len(mod.removed_aliases)))
+
+ print(" * {0} ({1})".format(name, ", ".join(change)))
+ print(" Aliases:")
+ for a in sorted(mod.added_aliases):
+ print(" + {0}".format(a))
+ for a in sorted(mod.removed_aliases):
+ print(" - {0}".format(a))
+
+ print()
+
+ if all_differences or args.sensitivity:
+ if diff.added_sensitivities or diff.removed_sensitivities or diff.modified_sensitivities \
+ or args.sensitivity:
+ print("Sensitivities ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_sensitivities), len(diff.removed_sensitivities),
+ len(diff.modified_sensitivities)))
+ if diff.added_sensitivities and not args.stats:
+ print(" Added Sensitivites: {0}".format(len(diff.added_sensitivities)))
+ for s in sorted(diff.added_sensitivities):
+ print(" + {0}".format(s))
+ if diff.removed_sensitivities and not args.stats:
+ print(" Removed Sensitivities: {0}".format(len(diff.removed_sensitivities)))
+ for s in sorted(diff.removed_sensitivities):
+ print(" - {0}".format(s))
+ if diff.modified_sensitivities and not args.stats:
+ print(" Modified Sensitivities: {0}".format(len(diff.modified_sensitivities)))
+ for name, mod in sorted(diff.modified_sensitivities.items()):
+ change = []
+ if mod.added_aliases:
+ change.append("{0} Added Aliases".format(len(mod.added_aliases)))
+ if mod.removed_aliases:
+ change.append("{0} Removed Aliases".format(len(mod.removed_aliases)))
+
+ print(" * {0} ({1})".format(name, ", ".join(change)))
+ print(" Aliases:")
+ for a in sorted(mod.added_aliases):
+ print(" + {0}".format(a))
+ for a in sorted(mod.removed_aliases):
+ print(" - {0}".format(a))
+
+ print()
+
+ if all_differences or args.level:
+ if diff.added_levels or diff.removed_levels or \
+ diff.modified_levels or args.level:
+ print("Levels ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_levels), len(diff.removed_levels),
+ len(diff.modified_levels)))
+ if diff.added_levels and not args.stats:
+ print(" Added Levels: {0}".format(len(diff.added_levels)))
+ for l in sorted(diff.added_levels):
+ print(" + {0}".format(l))
+ if diff.removed_levels and not args.stats:
+ print(" Removed Levels: {0}".format(len(diff.removed_levels)))
+ for l in sorted(diff.removed_levels):
+ print(" - {0}".format(l))
+ if diff.modified_levels and not args.stats:
+ print(" Modified Levels: {0}".format(len(diff.modified_levels)))
+ for level, added_categories, removed_categories, _ in sorted(diff.modified_levels,
+ key=lambda x: x.level):
+ change = []
+ if added_categories:
+ change.append("{0} Added Categories".format(len(added_categories)))
+ if removed_categories:
+ change.append("{0} Removed Categories".format(len(removed_categories)))
+
+ print(" * {0} ({1})".format(level.sensitivity, ", ".join(change)))
+ for c in sorted(added_categories):
+ print(" + {0}".format(c))
+ for c in sorted(removed_categories):
+ print(" - {0}".format(c))
+ print()
+
+ if all_differences or args.allow:
+ if diff.added_allows or diff.removed_allows or diff.modified_allows or args.allow:
+ print("Allow Rules ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_allows), len(diff.removed_allows), len(diff.modified_allows)))
+
+ if diff.added_allows and not args.stats:
+ print(" Added Allow Rules: {0}".format(len(diff.added_allows)))
+ for r in sorted(diff.added_allows):
+ print(" + {0}".format(r))
+
+ if diff.removed_allows and not args.stats:
+ print(" Removed Allow Rules: {0}".format(len(diff.removed_allows)))
+ for r in sorted(diff.removed_allows):
+ print(" - {0}".format(r))
+
+ if diff.modified_allows and not args.stats:
+ print(" Modified Allow Rules: {0}".format(len(diff.modified_allows)))
+
+ for rule, added_perms, removed_perms, matched_perms in sorted(diff.modified_allows,
+ key=lambda x: x.rule):
+ perms = " ".join(chain((p for p in matched_perms),
+ ("+" + p for p in added_perms),
+ ("-" + p for p in removed_perms)))
+ rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {{ {1} }};".format(
+ rule, perms)
+
+ try:
+ rule_string += " [ {0} ]".format(rule.conditional)
+ except AttributeError:
+ pass
+ print(" * {0}".format(rule_string))
+
+ print()
+
+ if all_differences or args.allowxperm:
+ if diff.added_allowxperms or diff.removed_allowxperms or diff.modified_allowxperms \
+ or args.allowxperm:
+
+ print("Allowxperm Rules ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_allowxperms), len(diff.removed_allowxperms),
+ len(diff.modified_allowxperms)))
+
+ if diff.added_allowxperms and not args.stats:
+ print(" Added Allowxperm Rules: {0}".format(len(diff.added_allowxperms)))
+ for r in sorted(diff.added_allowxperms):
+ print(" + {0}".format(r))
+
+ if diff.removed_allowxperms and not args.stats:
+ print(" Removed Allowxperm Rules: {0}".format(len(diff.removed_allowxperms)))
+ for r in sorted(diff.removed_allowxperms):
+ print(" - {0}".format(r))
+
+ if diff.modified_allowxperms and not args.stats:
+ print(" Modified Allowxperm Rules: {0}".format(len(diff.modified_allowxperms)))
+
+ for rule, added_perms, removed_perms, matched_perms in sorted(
+ diff.modified_allowxperms, key=lambda x: x.rule):
+
+ # Process the string representation of the sets
+ # so hex representation and ranges are preserved.
+ # Check if the perm sets have contents, otherwise
+ # split on empty string will be an empty string.
+ # Add brackets to added and removed permissions
+ # in case there is a range of permissions.
+ perms = []
+ if matched_perms:
+ for p in str(matched_perms).split(" "):
+ perms.append(p)
+ if added_perms:
+ for p in str(added_perms).split(" "):
+ if '-' in p:
+ perms.append("+[{0}]".format(p))
+ else:
+ perms.append("+{0}".format(p))
+ if removed_perms:
+ for p in str(removed_perms).split(" "):
+ if '-' in p:
+ perms.append("-[{0}]".format(p))
+ else:
+ perms.append("-{0}".format(p))
+
+ rule_string = \
+ "{0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} {{ {1} }};". \
+ format(rule, perms)
+
+ print(" * {0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} "
+ "{{ {1} }};".format(rule, " ".join(perms)))
+
+ print()
+
+ if all_differences or args.neverallow:
+ if diff.added_neverallows or diff.removed_neverallows or diff.modified_neverallows or \
+ args.neverallow:
+ print("Neverallow Rules ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_neverallows), len(diff.removed_neverallows),
+ len(diff.modified_neverallows)))
+
+ if diff.added_neverallows and not args.stats:
+ print(" Added Neverallow Rules: {0}".format(len(diff.added_neverallows)))
+ for r in sorted(diff.added_neverallows):
+ print(" + {0}".format(r))
+
+ if diff.removed_neverallows and not args.stats:
+ print(" Removed Neverallow Rules: {0}".format(len(diff.removed_neverallows)))
+ for r in sorted(diff.removed_neverallows):
+ print(" - {0}".format(r))
+
+ if diff.modified_neverallows and not args.stats:
+ print(" Modified Neverallow Rules: {0}".format(len(diff.modified_neverallows)))
+
+ for rule, added_perms, removed_perms, matched_perms in sorted(
+ diff.modified_neverallows, key=lambda x: x.rule):
+ perms = " ".join(chain((p for p in matched_perms),
+ ("+" + p for p in added_perms),
+ ("-" + p for p in removed_perms)))
+ rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {{ {1} }};".format(
+ rule, perms)
+
+ try:
+ rule_string += " [ {0} ]".format(rule.conditional)
+ except AttributeError:
+ pass
+ print(" * {0}".format(rule_string))
+
+ print()
+
+ if all_differences or args.neverallowxperm:
+ if diff.added_neverallowxperms or diff.removed_neverallowxperms or \
+ diff.modified_neverallowxperms or args.neverallowxperm:
+
+ print("Neverallowxperm Rules ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_neverallowxperms), len(diff.removed_neverallowxperms),
+ len(diff.modified_neverallowxperms)))
+
+ if diff.added_neverallowxperms and not args.stats:
+ print(" Added Neverallowxperm Rules: {0}".format(
+ len(diff.added_neverallowxperms)))
+ for r in sorted(diff.added_neverallowxperms):
+ print(" + {0}".format(r))
+
+ if diff.removed_neverallowxperms and not args.stats:
+ print(" Removed Neverallowxperm Rules: {0}".format(
+ len(diff.removed_neverallowxperms)))
+ for r in sorted(diff.removed_neverallowxperms):
+ print(" - {0}".format(r))
+
+ if diff.modified_neverallowxperms and not args.stats:
+ print(" Modified Neverallowxperm Rules: {0}".format(
+ len(diff.modified_neverallowxperms)))
+
+ for rule, added_perms, removed_perms, matched_perms in sorted(
+ diff.modified_neverallowxperms, key=lambda x: x.rule):
+
+ # Process the string representation of the sets
+ # so hex representation and ranges are preserved.
+ # Check if the perm sets have contents, otherwise
+ # split on empty string will be an empty string.
+ # Add brackets to added and removed permissions
+ # in case there is a range of permissions.
+ perms = []
+ if matched_perms:
+ for p in str(matched_perms).split(" "):
+ perms.append(p)
+ if added_perms:
+ for p in str(added_perms).split(" "):
+ if '-' in p:
+ perms.append("+[{0}]".format(p))
+ else:
+ perms.append("+{0}".format(p))
+ if removed_perms:
+ for p in str(removed_perms).split(" "):
+ if '-' in p:
+ perms.append("-[{0}]".format(p))
+ else:
+ perms.append("-{0}".format(p))
+
+ rule_string = \
+ "{0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} {{ {1} }};". \
+ format(rule, perms)
+
+ print(" * {0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} "
+ "{{ {1} }};".format(rule, " ".join(perms)))
+
+ print()
+
+ if all_differences or args.auditallow:
+ if diff.added_auditallows or diff.removed_auditallows or diff.modified_auditallows or \
+ args.auditallow:
+ print("Auditallow Rules ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_auditallows), len(diff.removed_auditallows),
+ len(diff.modified_auditallows)))
+
+ if diff.added_auditallows and not args.stats:
+ print(" Added Auditallow Rules: {0}".format(len(diff.added_auditallows)))
+ for r in sorted(diff.added_auditallows):
+ print(" + {0}".format(r))
+
+ if diff.removed_auditallows and not args.stats:
+ print(" Removed Auditallow Rules: {0}".format(len(diff.removed_auditallows)))
+ for r in sorted(diff.removed_auditallows):
+ print(" - {0}".format(r))
+
+ if diff.modified_auditallows and not args.stats:
+ print(" Modified Auditallow Rules: {0}".format(len(diff.modified_auditallows)))
+
+ for rule, added_perms, removed_perms, matched_perms in sorted(
+ diff.modified_auditallows, key=lambda x: x.rule):
+ perms = " ".join(chain((p for p in matched_perms),
+ ("+" + p for p in added_perms),
+ ("-" + p for p in removed_perms)))
+ rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {{ {1} }};".format(
+ rule, perms)
+
+ try:
+ rule_string += " [ {0} ]".format(rule.conditional)
+ except AttributeError:
+ pass
+ print(" * {0}".format(rule_string))
+
+ print()
+
+ if all_differences or args.auditallowxperm:
+ if diff.added_auditallowxperms or diff.removed_auditallowxperms or \
+ diff.modified_auditallowxperms or args.auditallowxperm:
+
+ print("Auditallowxperm Rules ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_auditallowxperms), len(diff.removed_auditallowxperms),
+ len(diff.modified_auditallowxperms)))
+
+ if diff.added_auditallowxperms and not args.stats:
+ print(" Added Auditallowxperm Rules: {0}".format(
+ len(diff.added_auditallowxperms)))
+ for r in sorted(diff.added_auditallowxperms):
+ print(" + {0}".format(r))
+
+ if diff.removed_auditallowxperms and not args.stats:
+ print(" Removed Auditallowxperm Rules: {0}".format(
+ len(diff.removed_auditallowxperms)))
+ for r in sorted(diff.removed_auditallowxperms):
+ print(" - {0}".format(r))
+
+ if diff.modified_auditallowxperms and not args.stats:
+ print(" Modified Auditallowxperm Rules: {0}".format(
+ len(diff.modified_auditallowxperms)))
+
+ for rule, added_perms, removed_perms, matched_perms in sorted(
+ diff.modified_auditallowxperms, key=lambda x: x.rule):
+
+ # Process the string representation of the sets
+ # so hex representation and ranges are preserved.
+ # Check if the perm sets have contents, otherwise
+ # split on empty string will be an empty string.
+ # Add brackets to added and removed permissions
+ # in case there is a range of permissions.
+ perms = []
+ if matched_perms:
+ for p in str(matched_perms).split(" "):
+ perms.append(p)
+ if added_perms:
+ for p in str(added_perms).split(" "):
+ if '-' in p:
+ perms.append("+[{0}]".format(p))
+ else:
+ perms.append("+{0}".format(p))
+ if removed_perms:
+ for p in str(removed_perms).split(" "):
+ if '-' in p:
+ perms.append("-[{0}]".format(p))
+ else:
+ perms.append("-{0}".format(p))
+
+ rule_string = \
+ "{0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} {{ {1} }};". \
+ format(rule, perms)
+
+ print(" * {0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} "
+ "{{ {1} }};".format(rule, " ".join(perms)))
+
+ print()
+
+ if all_differences or args.dontaudit:
+ if diff.added_dontaudits or diff.removed_dontaudits or diff.modified_dontaudits or \
+ args.dontaudit:
+ print("Dontaudit Rules ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_dontaudits), len(diff.removed_dontaudits),
+ len(diff.modified_dontaudits)))
+
+ if diff.added_dontaudits and not args.stats:
+ print(" Added Dontaudit Rules: {0}".format(len(diff.added_dontaudits)))
+ for r in sorted(diff.added_dontaudits):
+ print(" + {0}".format(r))
+
+ if diff.removed_dontaudits and not args.stats:
+ print(" Removed Dontaudit Rules: {0}".format(len(diff.removed_dontaudits)))
+ for r in sorted(diff.removed_dontaudits):
+ print(" - {0}".format(r))
+
+ if diff.modified_dontaudits and not args.stats:
+ print(" Modified Dontaudit Rules: {0}".format(len(diff.modified_dontaudits)))
+
+ for rule, added_perms, removed_perms, matched_perms in sorted(
+ diff.modified_dontaudits, key=lambda x: x.rule):
+ perms = " ".join(chain((p for p in matched_perms),
+ ("+" + p for p in added_perms),
+ ("-" + p for p in removed_perms)))
+ rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {{ {1} }};".format(
+ rule, perms)
+
+ try:
+ rule_string += " [ {0} ]".format(rule.conditional)
+ except AttributeError:
+ pass
+ print(" * {0}".format(rule_string))
+
+ print()
+
+ if all_differences or args.dontauditxperm:
+ if diff.added_dontauditxperms or diff.removed_dontauditxperms or \
+ diff.modified_dontauditxperms or args.dontauditxperm:
+
+ print("Dontauditxperm Rules ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_dontauditxperms), len(diff.removed_dontauditxperms),
+ len(diff.modified_dontauditxperms)))
+
+ if diff.added_dontauditxperms and not args.stats:
+ print(" Added Dontauditxperm Rules: {0}".format(
+ len(diff.added_dontauditxperms)))
+ for r in sorted(diff.added_dontauditxperms):
+ print(" + {0}".format(r))
+
+ if diff.removed_dontauditxperms and not args.stats:
+ print(" Removed Dontauditxperm Rules: {0}".format(
+ len(diff.removed_dontauditxperms)))
+ for r in sorted(diff.removed_dontauditxperms):
+ print(" - {0}".format(r))
+
+ if diff.modified_dontauditxperms and not args.stats:
+ print(" Modified Dontauditxperm Rules: {0}".format(
+ len(diff.modified_dontauditxperms)))
+
+ for rule, added_perms, removed_perms, matched_perms in sorted(
+ diff.modified_dontauditxperms, key=lambda x: x.rule):
+
+ # Process the string representation of the sets
+ # so hex representation and ranges are preserved.
+ # Check if the perm sets have contents, otherwise
+ # split on empty string will be an empty string.
+ # Add brackets to added and removed permissions
+ # in case there is a range of permissions.
+ perms = []
+ if matched_perms:
+ for p in str(matched_perms).split(" "):
+ perms.append(p)
+ if added_perms:
+ for p in str(added_perms).split(" "):
+ if '-' in p:
+ perms.append("+[{0}]".format(p))
+ else:
+ perms.append("+{0}".format(p))
+ if removed_perms:
+ for p in str(removed_perms).split(" "):
+ if '-' in p:
+ perms.append("-[{0}]".format(p))
+ else:
+ perms.append("-{0}".format(p))
+
+ rule_string = \
+ "{0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} {{ {1} }};". \
+ format(rule, perms)
+
+ print(" * {0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} "
+ "{{ {1} }};".format(rule, " ".join(perms)))
+
+ print()
+
+ if all_differences or args.type_trans:
+ if diff.added_type_transitions or diff.removed_type_transitions or \
+ diff.modified_type_transitions or args.type_trans:
+ print("Type_transition Rules ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_type_transitions), len(diff.removed_type_transitions),
+ len(diff.modified_type_transitions)))
+
+ if diff.added_type_transitions and not args.stats:
+ print(" Added Type_transition Rules: {0}".format(
+ len(diff.added_type_transitions)))
+ for r in sorted(diff.added_type_transitions):
+ print(" + {0}".format(r))
+
+ if diff.removed_type_transitions and not args.stats:
+ print(" Removed Type_transition Rules: {0}".format(
+ len(diff.removed_type_transitions)))
+ for r in sorted(diff.removed_type_transitions):
+ print(" - {0}".format(r))
+
+ if diff.modified_type_transitions and not args.stats:
+ print(" Modified Type_transition Rules: {0}".format(
+ len(diff.modified_type_transitions)))
+
+ for rule, added_default, removed_default in sorted(diff.modified_type_transitions,
+ key=lambda x: x.rule):
+ rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} +{1} -{2}".format(
+ rule, added_default, removed_default)
+
+ try:
+ rule_string += " {0}".format(rule.filename)
+ except AttributeError:
+ pass
+
+ rule_string += ";"
+
+ try:
+ rule_string += " [ {0} ]".format(rule.conditional)
+ except AttributeError:
+ pass
+
+ print(" * {0}".format(rule_string))
+
+ print()
+
+ if all_differences or args.type_change:
+ if diff.added_type_changes or diff.removed_type_changes or \
+ diff.modified_type_changes or args.type_change:
+ print("Type_change Rules ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_type_changes), len(diff.removed_type_changes),
+ len(diff.modified_type_changes)))
+
+ if diff.added_type_changes and not args.stats:
+ print(" Added Type_change Rules: {0}".format(len(diff.added_type_changes)))
+ for r in sorted(diff.added_type_changes):
+ print(" + {0}".format(r))
+
+ if diff.removed_type_changes and not args.stats:
+ print(" Removed Type_change Rules: {0}".format(len(diff.removed_type_changes)))
+ for r in sorted(diff.removed_type_changes):
+ print(" - {0}".format(r))
+
+ if diff.modified_type_changes and not args.stats:
+ print(" Modified Type_change Rules: {0}".format(len(diff.modified_type_changes)))
+
+ for rule, added_default, removed_default in sorted(diff.modified_type_changes,
+ key=lambda x: x.rule):
+ rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} +{1} -{2}".format(
+ rule, added_default, removed_default)
+
+ try:
+ rule_string += " {0}".format(rule.filename)
+ except AttributeError:
+ pass
+
+ rule_string += ";"
+
+ try:
+ rule_string += " [ {0} ]".format(rule.conditional)
+ except AttributeError:
+ pass
+
+ print(" * {0}".format(rule_string))
+
+ print()
+
+ if all_differences or args.type_member:
+ if diff.added_type_members or diff.removed_type_members or \
+ diff.modified_type_members or args.type_member:
+ print("Type_member Rules ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_type_members), len(diff.removed_type_members),
+ len(diff.modified_type_members)))
+
+ if diff.added_type_members and not args.stats:
+ print(" Added Type_member Rules: {0}".format(len(diff.added_type_members)))
+ for r in sorted(diff.added_type_members):
+ print(" + {0}".format(r))
+
+ if diff.removed_type_members and not args.stats:
+ print(" Removed Type_member Rules: {0}".format(len(diff.removed_type_members)))
+ for r in sorted(diff.removed_type_members):
+ print(" - {0}".format(r))
+
+ if diff.modified_type_members and not args.stats:
+ print(" Modified Type_member Rules: {0}".format(len(diff.modified_type_members)))
+
+ for rule, added_default, removed_default in sorted(diff.modified_type_members,
+ key=lambda x: x.rule):
+ rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} +{1} -{2}".format(
+ rule, added_default, removed_default)
+
+ try:
+ rule_string += " {0}".format(rule.filename)
+ except AttributeError:
+ pass
+
+ rule_string += ";"
+
+ try:
+ rule_string += " [ {0} ]".format(rule.conditional)
+ except AttributeError:
+ pass
+
+ print(" * {0}".format(rule_string))
+
+ print()
+
+ if all_differences or args.role_allow:
+ if diff.added_role_allows or diff.removed_role_allows or args.role_allow:
+ print("Role allow Rules ({0} Added, {1} Removed)".format(
+ len(diff.added_role_allows), len(diff.removed_role_allows)))
+
+ if diff.added_role_allows and not args.stats:
+ print(" Added Role Allow Rules: {0}".format(
+ len(diff.added_role_allows)))
+ for r in sorted(diff.added_role_allows):
+ print(" + {0}".format(r))
+
+ if diff.removed_role_allows and not args.stats:
+ print(" Removed Role Allow Rules: {0}".format(
+ len(diff.removed_role_allows)))
+ for r in sorted(diff.removed_role_allows):
+ print(" - {0}".format(r))
+
+ print()
+
+ if all_differences or args.role_trans:
+ if diff.added_role_transitions or diff.removed_role_transitions or \
+ diff.modified_role_transitions or args.role_trans:
+ print("Role_transition Rules ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_role_transitions), len(diff.removed_role_transitions),
+ len(diff.modified_role_transitions)))
+
+ if diff.added_role_transitions and not args.stats:
+ print(" Added Role_transition Rules: {0}".format(
+ len(diff.added_role_transitions)))
+ for r in sorted(diff.added_role_transitions):
+ print(" + {0}".format(r))
+
+ if diff.removed_role_transitions and not args.stats:
+ print(" Removed Role_transition Rules: {0}".format(
+ len(diff.removed_role_transitions)))
+ for r in sorted(diff.removed_role_transitions):
+ print(" - {0}".format(r))
+
+ if diff.modified_role_transitions and not args.stats:
+ print(" Modified Role_transition Rules: {0}".format(
+ len(diff.modified_role_transitions)))
+
+ for rule, added_default, removed_default in sorted(diff.modified_role_transitions,
+ key=lambda x: x.rule):
+ rule_string = \
+ "{0.ruletype} {0.source} {0.target}:{0.tclass} +{1} -{2}".format(
+ rule, added_default, removed_default)
+
+ print(" * {0}".format(rule_string))
+
+ print()
+
+ if all_differences or args.range_trans:
+ if diff.added_range_transitions or diff.removed_range_transitions or \
+ diff.modified_range_transitions or args.range_trans:
+ print("Range_transition Rules ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_range_transitions), len(diff.removed_range_transitions),
+ len(diff.modified_range_transitions)))
+
+ if diff.added_range_transitions and not args.stats:
+ print(" Added Range_transition Rules: {0}".format(
+ len(diff.added_range_transitions)))
+ for r in sorted(diff.added_range_transitions):
+ print(" + {0}".format(r))
+
+ if diff.removed_range_transitions and not args.stats:
+ print(" Removed Range_transition Rules: {0}".format(
+ len(diff.removed_range_transitions)))
+ for r in sorted(diff.removed_range_transitions):
+ print(" - {0}".format(r))
+
+ if diff.modified_range_transitions and not args.stats:
+ print(" Modified Range_transition Rules: {0}".format(
+ len(diff.modified_range_transitions)))
+
+ for rule, added_default, removed_default in sorted(diff.modified_range_transitions,
+ key=lambda x: x.rule):
+ # added brackets around range change for clarity since ranges
+ # can have '-' and spaces.
+ rule_string = \
+ "{0.ruletype} {0.source} {0.target}:{0.tclass} +[{1}] -[{2}]".format(
+ rule, added_default, removed_default)
+
+ print(" * {0}".format(rule_string))
+
+ print()
+
+ if all_differences or args.constrain:
+ if diff.added_constrains or diff.removed_constrains or args.constrain:
+ print("Constraints ({0} Added, {1} Removed)".format(
+ len(diff.added_constrains), len(diff.removed_constrains)))
+
+ if diff.added_constrains and not args.stats:
+ print(" Added Constraints: {0}".format(
+ len(diff.added_constrains)))
+ for r in sorted(diff.added_constrains):
+ print(" + {0}".format(r))
+
+ if diff.removed_constrains and not args.stats:
+ print(" Removed Constraints: {0}".format(
+ len(diff.removed_constrains)))
+ for r in sorted(diff.removed_constrains):
+ print(" - {0}".format(r))
+
+ print()
+
+ if all_differences or args.mlsconstrain:
+ if diff.added_mlsconstrains or diff.removed_mlsconstrains or args.mlsconstrain:
+ print("MLS Constraints ({0} Added, {1} Removed)".format(
+ len(diff.added_mlsconstrains), len(diff.removed_mlsconstrains)))
+
+ if diff.added_mlsconstrains and not args.stats:
+ print(" Added MLS Constraints: {0}".format(
+ len(diff.added_mlsconstrains)))
+ for r in sorted(diff.added_mlsconstrains):
+ print(" + {0}".format(r))
+
+ if diff.removed_mlsconstrains and not args.stats:
+ print(" Removed MLS Constraints: {0}".format(
+ len(diff.removed_mlsconstrains)))
+ for r in sorted(diff.removed_mlsconstrains):
+ print(" - {0}".format(r))
+
+ print()
+
+ if all_differences or args.validatetrans:
+ if diff.added_validatetrans or diff.removed_validatetrans or args.validatetrans:
+ print("Validatetrans ({0} Added, {1} Removed)".format(
+ len(diff.added_validatetrans), len(diff.removed_validatetrans)))
+
+ if diff.added_validatetrans and not args.stats:
+ print(" Added Validatetrans: {0}".format(
+ len(diff.added_validatetrans)))
+ for r in sorted(diff.added_validatetrans):
+ print(" + {0}".format(r))
+
+ if diff.removed_validatetrans and not args.stats:
+ print(" Removed Validatetrans: {0}".format(
+ len(diff.removed_validatetrans)))
+ for r in sorted(diff.removed_validatetrans):
+ print(" - {0}".format(r))
+
+ print()
+
+ if all_differences or args.mlsvalidatetrans:
+ if diff.added_mlsvalidatetrans or diff.removed_mlsvalidatetrans or args.mlsvalidatetrans:
+ print("MLS Validatetrans ({0} Added, {1} Removed)".format(
+ len(diff.added_mlsvalidatetrans), len(diff.removed_mlsvalidatetrans)))
+
+ if diff.added_mlsvalidatetrans and not args.stats:
+ print(" Added MLS Validatetrans: {0}".format(
+ len(diff.added_mlsvalidatetrans)))
+ for r in sorted(diff.added_mlsvalidatetrans):
+ print(" + {0}".format(r))
+
+ if diff.removed_mlsvalidatetrans and not args.stats:
+ print(" Removed MLS Validatetrans: {0}".format(
+ len(diff.removed_mlsvalidatetrans)))
+ for r in sorted(diff.removed_mlsvalidatetrans):
+ print(" - {0}".format(r))
+
+ print()
+
+ if all_differences or args.initialsid:
+ if diff.added_initialsids or diff.removed_initialsids or diff.modified_initialsids \
+ or args.initialsid:
+ print("Initial SIDs ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_initialsids), len(diff.removed_initialsids),
+ len(diff.modified_initialsids)))
+ if diff.added_initialsids and not args.stats:
+ print(" Added Initial SIDs: {0}".format(len(diff.added_initialsids)))
+ for s in sorted(diff.added_initialsids):
+ print(" + {0}".format(s.statement()))
+ if diff.removed_initialsids and not args.stats:
+ print(" Removed Initial SIDs: {0}".format(len(diff.removed_initialsids)))
+ for s in sorted(diff.removed_initialsids):
+ print(" - {0}".format(s.statement()))
+ if diff.modified_initialsids and not args.stats:
+ print(" Modified Initial SIDs: {0}".format(len(diff.modified_initialsids)))
+ for name, mod in sorted(diff.modified_initialsids.items()):
+ print(" * {0} +[{1.added_context}] -[{1.removed_context}];".format(
+ name, mod))
+
+ print()
+
+ if all_differences or args.fs_use:
+ if diff.added_fs_uses or diff.removed_fs_uses or diff.modified_fs_uses \
+ or args.fs_use:
+ print("Fs_use ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_fs_uses), len(diff.removed_fs_uses),
+ len(diff.modified_fs_uses)))
+ if diff.added_fs_uses and not args.stats:
+ print(" Added Fs_use: {0}".format(len(diff.added_fs_uses)))
+ for s in sorted(diff.added_fs_uses):
+ print(" + {0}".format(s))
+ if diff.removed_fs_uses and not args.stats:
+ print(" Removed Fs_use: {0}".format(len(diff.removed_fs_uses)))
+ for s in sorted(diff.removed_fs_uses):
+ print(" - {0}".format(s))
+ if diff.modified_fs_uses and not args.stats:
+ print(" Modified Fs_use: {0}".format(len(diff.modified_fs_uses)))
+ for entry in sorted(diff.modified_fs_uses, key=lambda x: x.rule):
+ print(" * {0.ruletype} {0.fs} +[{1}] -[{2}];".format(
+ entry.rule, entry.added_context, entry.removed_context))
+
+ print()
+
+ if all_differences or args.genfscon:
+ if diff.added_genfscons or diff.removed_genfscons or diff.modified_genfscons \
+ or args.genfscon:
+ print("Genfscons ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_genfscons), len(diff.removed_genfscons),
+ len(diff.modified_genfscons)))
+ if diff.added_genfscons and not args.stats:
+ print(" Added Genfscons: {0}".format(len(diff.added_genfscons)))
+ for s in sorted(diff.added_genfscons):
+ print(" + {0}".format(s))
+ if diff.removed_genfscons and not args.stats:
+ print(" Removed Genfscons: {0}".format(len(diff.removed_genfscons)))
+ for s in sorted(diff.removed_genfscons):
+ print(" - {0}".format(s))
+ if diff.modified_genfscons and not args.stats:
+ print(" Modified Genfscons: {0}".format(len(diff.modified_genfscons)))
+ for entry in sorted(diff.modified_genfscons, key=lambda x: x.rule):
+ print(" * genfscon {0.fs} {0.path} {0.filetype} +[{1}] -[{2}];".format(
+ entry.rule, entry.added_context, entry.removed_context))
+
+ print()
+
+ if all_differences or args.netifcon:
+ if diff.added_netifcons or diff.removed_netifcons or \
+ diff.modified_netifcons or args.netifcon:
+ print("Netifcons ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_netifcons), len(diff.removed_netifcons),
+ len(diff.modified_netifcons)))
+ if diff.added_netifcons and not args.stats:
+ print(" Added Netifcons: {0}".format(len(diff.added_netifcons)))
+ for n in sorted(diff.added_netifcons):
+ print(" + {0}".format(n))
+ if diff.removed_netifcons and not args.stats:
+ print(" Removed Netifcons: {0}".format(len(diff.removed_netifcons)))
+ for n in sorted(diff.removed_netifcons):
+ print(" - {0}".format(n))
+ if diff.modified_netifcons and not args.stats:
+ print(" Modified Netifcons: {0}".format(len(diff.modified_netifcons)))
+ for entry in sorted(diff.modified_netifcons, key=lambda x: x.rule):
+ # This output is different than other statements because
+ # it becomes difficult to read if this was condensed
+ # into a single line, especially if both contexts
+ # are modified.
+ change = []
+ if entry.removed_context:
+ change.append("Modified Context")
+ if entry.removed_packet:
+ change.append("Modified Packet Context")
+
+ print(" * {0.netif} ({1})".format(entry.rule, ", ".join(change)))
+
+ if entry.removed_context:
+ print(" Context:")
+ print(" + {0}".format(entry.added_context))
+ print(" - {0}".format(entry.removed_context))
+ if entry.removed_packet:
+ print(" Packet Context:")
+ print(" + {0}".format(entry.added_packet))
+ print(" - {0}".format(entry.removed_packet))
+
+ print()
+
+ if all_differences or args.nodecon:
+ if diff.added_nodecons or diff.removed_nodecons or diff.modified_nodecons \
+ or args.nodecon:
+ print("Nodecons ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_nodecons), len(diff.removed_nodecons),
+ len(diff.modified_nodecons)))
+ if diff.added_nodecons and not args.stats:
+ print(" Added Nodecons: {0}".format(len(diff.added_nodecons)))
+ for n in sorted(diff.added_nodecons):
+ print(" + {0}".format(n))
+ if diff.removed_nodecons and not args.stats:
+ print(" Removed Nodecons: {0}".format(len(diff.removed_nodecons)))
+ for n in sorted(diff.removed_nodecons):
+ print(" - {0}".format(n))
+ if diff.modified_nodecons and not args.stats:
+ print(" Modified Nodecons: {0}".format(len(diff.modified_nodecons)))
+ for con, added_context, removed_context in sorted(diff.modified_nodecons,
+ key=lambda x: x.rule):
+ print(" * nodecon {0.address} {0.netmask} +[{1}] -[{2}];".format(
+ con, added_context, removed_context))
+
+ print()
+
+ if all_differences or args.portcon:
+ if diff.added_portcons or diff.removed_portcons or diff.modified_portcons \
+ or args.portcon:
+ print("Portcons ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_portcons), len(diff.removed_portcons),
+ len(diff.modified_portcons)))
+ if diff.added_portcons and not args.stats:
+ print(" Added Portcons: {0}".format(len(diff.added_portcons)))
+ for n in sorted(diff.added_portcons):
+ print(" + {0}".format(n))
+ if diff.removed_portcons and not args.stats:
+ print(" Removed Portcons: {0}".format(len(diff.removed_portcons)))
+ for n in sorted(diff.removed_portcons):
+ print(" - {0}".format(n))
+ if diff.modified_portcons and not args.stats:
+ print(" Modified Portcons: {0}".format(len(diff.modified_portcons)))
+ for con, added_context, removed_context in sorted(diff.modified_portcons,
+ key=lambda x: x.rule):
+ low, high = con.ports
+ if low == high:
+ print(" * portcon {0.protocol} {1} +[{2}] -[{3}];".format(
+ con, low, added_context, removed_context))
+ else:
+ print(" * portcon {0.protocol} {1}-{2} +[{3}] -[{4}];".format(
+ con, low, high, added_context, removed_context))
+
+ print()
+
+ if all_differences or args.polcap:
+ if diff.added_polcaps or diff.removed_polcaps or args.polcap:
+ print("Policy Capabilities ({0} Added, {1} Removed)".format(
+ len(diff.added_polcaps), len(diff.removed_polcaps)))
+ if diff.added_polcaps and not args.stats:
+ print(" Added Policy Capabilities: {0}".format(len(diff.added_polcaps)))
+ for n in sorted(diff.added_polcaps):
+ print(" + {0}".format(n))
+ if diff.removed_polcaps and not args.stats:
+ print(" Removed Policy Capabilities: {0}".format(len(diff.removed_polcaps)))
+ for n in sorted(diff.removed_polcaps):
+ print(" - {0}".format(n))
+
+ print()
+
+ if all_differences or args.default:
+ if diff.added_defaults or diff.removed_defaults or args.default:
+ print("Defaults ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_defaults), len(diff.removed_defaults), len(diff.modified_defaults)))
+ if diff.added_defaults and not args.stats:
+ print(" Added Defaults: {0}".format(len(diff.added_defaults)))
+ for d in sorted(diff.added_defaults):
+ print(" + {0}".format(d))
+ if diff.removed_defaults and not args.stats:
+ print(" Removed Defaults: {0}".format(len(diff.removed_defaults)))
+ for d in sorted(diff.removed_defaults):
+ print(" - {0}".format(d))
+ if diff.modified_defaults and not args.stats:
+ print(" Modified Defaults: {0}".format(len(diff.modified_defaults)))
+ for default, added_default, removed_default, added_range, removed_range in sorted(
+ diff.modified_defaults, key=lambda x: x.rule):
+ line = " * {0.ruletype} {0.tclass} ".format(default)
+ if removed_default:
+ line += "+{0} -{1}".format(added_default, removed_default)
+ else:
+ line += default.default
+
+ if removed_range:
+ line += " +{0} -{1};".format(added_range, removed_range)
+ else:
+ try:
+ line += " {0};".format(default.default_range)
+ except AttributeError:
+ line += ";"
+ print(line)
+
+ print()
+
+ if all_differences or args.typebounds:
+ if diff.added_typebounds or diff.removed_typebounds or args.typebounds:
+ print("Typebounds ({0} Added, {1} Removed, {2} Modified)".format(
+ len(diff.added_typebounds), len(diff.removed_typebounds),
+ len(diff.modified_typebounds)))
+ if diff.added_typebounds and not args.stats:
+ print(" Added Typebounds: {0}".format(len(diff.added_typebounds)))
+ for d in sorted(diff.added_typebounds):
+ print(" + {0}".format(d))
+ if diff.removed_typebounds and not args.stats:
+ print(" Removed Typebounds: {0}".format(len(diff.removed_typebounds)))
+ for d in sorted(diff.removed_typebounds):
+ print(" - {0}".format(d))
+ if diff.modified_typebounds and not args.stats:
+ print(" Modified Typebounds: {0}".format(len(diff.modified_typebounds)))
+ for bound, added_bound, removed_bound in sorted(
+ diff.modified_typebounds, key=lambda x: x.rule):
+ print(" * {0.ruletype} +{1} -{2} {0.child};".format(
+ bound, added_bound, removed_bound))
+
+ print()
+
+except Exception as err:
+ if args.debug:
+ logging.exception(str(err))
+ else:
+ print(err)
+
+ sys.exit(1)
diff --git a/prebuilts/bin/seinfo b/prebuilts/bin/seinfo
new file mode 100755
index 00000000..a6f6906e
--- /dev/null
+++ b/prebuilts/bin/seinfo
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+if [ -z "${ANDROID_BUILD_TOP}" ]; then
+ echo 'ANDROID_BUILD_TOP not set. Have you run lunch?'
+ exit 1
+fi
+
+unamestr=`uname`
+if [ "$unamestr" = "Linux" -o "$unamestr" = "linux" ]; then
+ export LD_LIBRARY_PATH=$ANDROID_BUILD_TOP/external/selinux/prebuilts/lib
+ export PYTHONPATH=$ANDROID_BUILD_TOP/prebuilts/python/linux-x86/2.7.5/lib/python2.7/site-packages
+ exec python $ANDROID_BUILD_TOP/external/selinux/prebuilts/bin/seinfo.py "$@"
+else
+ echo "seinfo is only supported on linux"
+ exit 1
+fi
diff --git a/prebuilts/bin/seinfo.py b/prebuilts/bin/seinfo.py
new file mode 100755
index 00000000..e794da7f
--- /dev/null
+++ b/prebuilts/bin/seinfo.py
@@ -0,0 +1,366 @@
+#!/usr/bin/env python
+# Copyright 2014-2015, Tresys Technology, LLC
+#
+# This file is part of SETools.
+#
+# SETools 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.
+#
+# SETools 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 SETools. If not, see <http://www.gnu.org/licenses/>.
+#
+
+from __future__ import print_function
+import setools
+import argparse
+import sys
+import logging
+
+
+def expand_attr(attr):
+ """Render type and role attributes."""
+ items = "\n\t".join(sorted(str(i) for i in attr.expand()))
+ contents = items if items else "<empty attribute>"
+ return "{0}\n\t{1}".format(attr.statement(), contents)
+
+parser = argparse.ArgumentParser(
+ description="SELinux policy information tool.")
+parser.add_argument("--version", action="version", version=setools.__version__)
+parser.add_argument("policy", help="Path to the SELinux policy to query.", nargs="?")
+parser.add_argument("-x", "--expand", action="store_true",
+ help="Print additional information about the specified components.")
+parser.add_argument("--flat", help="Print without item count nor indentation.",
+ dest="flat", default=False, action="store_true")
+parser.add_argument("-v", "--verbose", action="store_true",
+ help="Print extra informational messages")
+parser.add_argument("--debug", action="store_true", dest="debug", help="Enable debugging.")
+
+queries = parser.add_argument_group("Component Queries")
+queries.add_argument("-a", "--attribute", help="Print type attributes.", dest="typeattrquery",
+ nargs='?', const=True, metavar="ATTR")
+queries.add_argument("-b", "--bool", help="Print Booleans.", dest="boolquery",
+ nargs='?', const=True, metavar="BOOL")
+queries.add_argument("-c", "--class", help="Print object classes.", dest="classquery",
+ nargs='?', const=True, metavar="CLASS")
+queries.add_argument("-r", "--role", help="Print roles.", dest="rolequery",
+ nargs='?', const=True, metavar="ROLE")
+queries.add_argument("-t", "--type", help="Print types.", dest="typequery",
+ nargs='?', const=True, metavar="TYPE")
+queries.add_argument("-u", "--user", help="Print users.", dest="userquery",
+ nargs='?', const=True, metavar="USER")
+queries.add_argument("--category", help="Print MLS categories.", dest="mlscatsquery",
+ nargs='?', const=True, metavar="CAT")
+queries.add_argument("--common", help="Print common permission set.", dest="commonquery",
+ nargs='?', const=True, metavar="COMMON")
+queries.add_argument("--constrain", help="Print constraints.", dest="constraintquery",
+ nargs='?', const=True, metavar="CLASS")
+queries.add_argument("--default", help="Print default_* rules.", dest="defaultquery",
+ nargs='?', const=True, metavar="CLASS")
+queries.add_argument("--fs_use", help="Print fs_use statements.", dest="fsusequery",
+ nargs='?', const=True, metavar="FS_TYPE")
+queries.add_argument("--genfscon", help="Print genfscon statements.", dest="genfsconquery",
+ nargs='?', const=True, metavar="FS_TYPE")
+queries.add_argument("--initialsid", help="Print initial SIDs (contexts).", dest="initialsidquery",
+ nargs='?', const=True, metavar="NAME")
+queries.add_argument("--netifcon", help="Print netifcon statements.", dest="netifconquery",
+ nargs='?', const=True, metavar="DEVICE")
+queries.add_argument("--nodecon", help="Print nodecon statements.", dest="nodeconquery",
+ nargs='?', const=True, metavar="ADDR")
+queries.add_argument("--permissive", help="Print permissive types.", dest="permissivequery",
+ nargs='?', const=True, metavar="TYPE")
+queries.add_argument("--polcap", help="Print policy capabilities.", dest="polcapquery",
+ nargs='?', const=True, metavar="NAME")
+queries.add_argument("--portcon", help="Print portcon statements.", dest="portconquery",
+ nargs='?', const=True, metavar="PORTNUM[-PORTNUM]")
+queries.add_argument("--sensitivity", help="Print MLS sensitivities.", dest="mlssensquery",
+ nargs='?', const=True, metavar="SENS")
+queries.add_argument("--typebounds", help="Print typebounds statements.", dest="typeboundsquery",
+ nargs='?', const=True, metavar="BOUND_TYPE")
+queries.add_argument("--validatetrans", help="Print validatetrans.", dest="validatetransquery",
+ nargs='?', const=True, metavar="CLASS")
+queries.add_argument("--all", help="Print all of the above. On a Xen policy, the Xen components "
+ "will also be printed", dest="all", default=False, action="store_true")
+
+xen = parser.add_argument_group("Xen Component Queries")
+xen.add_argument("--ioportcon", help="Print all ioportcon statements.", dest="ioportconquery",
+ default=False, action="store_true")
+xen.add_argument("--iomemcon", help="Print all iomemcon statements.", dest="iomemconquery",
+ default=False, action="store_true")
+xen.add_argument("--pcidevicecon", help="Print all pcidevicecon statements.",
+ dest="pcideviceconquery", default=False, action="store_true")
+xen.add_argument("--pirqcon", help="Print all pirqcon statements.", dest="pirqconquery",
+ default=False, action="store_true")
+xen.add_argument("--devicetreecon", help="Print all devicetreecon statements.",
+ dest="devicetreeconquery", default=False, action="store_true")
+
+
+args = parser.parse_args()
+
+if args.debug:
+ logging.basicConfig(level=logging.DEBUG,
+ format='%(asctime)s|%(levelname)s|%(name)s|%(message)s')
+elif args.verbose:
+ logging.basicConfig(level=logging.INFO, format='%(message)s')
+else:
+ logging.basicConfig(level=logging.WARNING, format='%(message)s')
+
+try:
+ p = setools.SELinuxPolicy(args.policy)
+ components = []
+
+ if args.boolquery or args.all:
+ q = setools.BoolQuery(p)
+ if isinstance(args.boolquery, str):
+ q.name = args.boolquery
+
+ components.append(("Booleans", q, lambda x: x.statement()))
+
+ if args.mlscatsquery or args.all:
+ q = setools.CategoryQuery(p)
+ if isinstance(args.mlscatsquery, str):
+ q.name = args.mlscatsquery
+
+ components.append(("Categories", q, lambda x: x.statement()))
+
+ if args.classquery or args.all:
+ q = setools.ObjClassQuery(p)
+ if isinstance(args.classquery, str):
+ q.name = args.classquery
+
+ components.append(("Classes", q, lambda x: x.statement()))
+
+ if args.commonquery or args.all:
+ q = setools.CommonQuery(p)
+ if isinstance(args.commonquery, str):
+ q.name = args.commonquery
+
+ components.append(("Commons", q, lambda x: x.statement()))
+
+ if args.constraintquery or args.all:
+ q = setools.ConstraintQuery(p, ruletype=["constrain", "mlsconstrain"])
+ if isinstance(args.constraintquery, str):
+ q.tclass = [args.constraintquery]
+
+ components.append(("Constraints", q, lambda x: x.statement()))
+
+ if args.defaultquery or args.all:
+ q = setools.DefaultQuery(p)
+ if isinstance(args.defaultquery, str):
+ q.tclass = [args.defaultquery]
+
+ components.append(("Default rules", q, lambda x: x.statement()))
+
+ if args.fsusequery or args.all:
+ q = setools.FSUseQuery(p)
+ if isinstance(args.fsusequery, str):
+ q.fs = args.fsusequery
+
+ components.append(("Fs_use", q, lambda x: x.statement()))
+
+ if args.genfsconquery or args.all:
+ q = setools.GenfsconQuery(p)
+ if isinstance(args.genfsconquery, str):
+ q.fs = args.genfsconquery
+
+ components.append(("Genfscon", q, lambda x: x.statement()))
+
+ if args.initialsidquery or args.all:
+ q = setools.InitialSIDQuery(p)
+ if isinstance(args.initialsidquery, str):
+ q.name = args.initialsidquery
+
+ components.append(("Initial SIDs", q, lambda x: x.statement()))
+
+ if args.netifconquery or args.all:
+ q = setools.NetifconQuery(p)
+ if isinstance(args.netifconquery, str):
+ q.name = args.netifconquery
+
+ components.append(("Netifcon", q, lambda x: x.statement()))
+
+ if args.nodeconquery or args.all:
+ q = setools.NodeconQuery(p)
+ if isinstance(args.nodeconquery, str):
+ q.network = args.nodeconquery
+
+ components.append(("Nodecon", q, lambda x: x.statement()))
+
+ if args.permissivequery or args.all:
+ q = setools.TypeQuery(p, permissive=True, match_permissive=True)
+ if isinstance(args.permissivequery, str):
+ q.name = args.permissivequery
+
+ components.append(("Permissive Types", q, lambda x: x.statement()))
+
+ if args.polcapquery or args.all:
+ q = setools.PolCapQuery(p)
+ if isinstance(args.polcapquery, str):
+ q.name = args.polcapquery
+
+ components.append(("Polcap", q, lambda x: x.statement()))
+
+ if args.portconquery or args.all:
+ q = setools.PortconQuery(p)
+ if isinstance(args.portconquery, str):
+ try:
+ ports = [int(i) for i in args.portconquery.split("-")]
+ except ValueError:
+ parser.error("Enter a port number or range, e.g. 22 or 6000-6020")
+
+ if len(ports) == 2:
+ q.ports = ports
+ elif len(ports) == 1:
+ q.ports = (ports[0], ports[0])
+ else:
+ parser.error("Enter a port number or range, e.g. 22 or 6000-6020")
+
+ components.append(("Portcon", q, lambda x: x.statement()))
+
+ if args.rolequery or args.all:
+ q = setools.RoleQuery(p)
+ if isinstance(args.rolequery, str):
+ q.name = args.rolequery
+
+ components.append(("Roles", q, lambda x: x.statement()))
+
+ if args.mlssensquery or args.all:
+ q = setools.SensitivityQuery(p)
+ if isinstance(args.mlssensquery, str):
+ q.name = args.mlssensquery
+
+ components.append(("Sensitivities", q, lambda x: x.statement()))
+
+ if args.typeboundsquery or args.all:
+ q = setools.BoundsQuery(p, ruletype=["typebounds"])
+ if isinstance(args.typeboundsquery, str):
+ q.child = args.typeboundsquery
+
+ components.append(("Typebounds", q, lambda x: x.statement()))
+
+ if args.typequery or args.all:
+ q = setools.TypeQuery(p)
+ if isinstance(args.typequery, str):
+ q.name = args.typequery
+
+ components.append(("Types", q, lambda x: x.statement()))
+
+ if args.typeattrquery or args.all:
+ q = setools.TypeAttributeQuery(p)
+ if isinstance(args.typeattrquery, str):
+ q.name = args.typeattrquery
+
+ components.append(("Type Attributes", q, expand_attr))
+
+ if args.userquery or args.all:
+ q = setools.UserQuery(p)
+ if isinstance(args.userquery, str):
+ q.name = args.userquery
+
+ components.append(("Users", q, lambda x: x.statement()))
+
+ if args.validatetransquery or args.all:
+ q = setools.ConstraintQuery(p, ruletype=["validatetrans", "mlsvalidatetrans"])
+ if isinstance(args.validatetransquery, str):
+ q.tclass = [args.validatetransquery]
+
+ components.append(("Validatetrans", q, lambda x: x.statement()))
+
+ if p.target_platform == "xen":
+ if args.ioportconquery or args.all:
+ q = setools.IoportconQuery(p)
+ components.append(("Ioportcon", q, lambda x: x.statement()))
+
+ if args.iomemconquery or args.all:
+ q = setools.IomemconQuery(p)
+ components.append(("Iomemcon", q, lambda x: x.statement()))
+
+ if args.pcideviceconquery or args.all:
+ q = setools.PcideviceconQuery(p)
+ components.append(("Pcidevicecon", q, lambda x: x.statement()))
+
+ if args.pirqconquery or args.all:
+ q = setools.PirqconQuery(p)
+ components.append(("Pirqcon", q, lambda x: x.statement()))
+
+ if args.devicetreeconquery or args.all:
+ q = setools.DevicetreeconQuery(p)
+ components.append(("Devicetreecon", q, lambda x: x.statement()))
+
+ if (not components or args.all) and not args.flat:
+ mls = "enabled" if p.mls else "disabled"
+
+ print("Statistics for policy file: {0}".format(p))
+ print("Policy Version: {0} (MLS {1})".format(p.version, mls))
+ print("Target Policy: {0}".format(p.target_platform))
+ print("Handle unknown classes: {0}".format(p.handle_unknown))
+ print(" Classes: {0:7} Permissions: {1:7}".format(
+ p.class_count, p.permission_count))
+ print(" Sensitivities: {0:7} Categories: {1:7}".format(
+ p.level_count, p.category_count))
+ print(" Types: {0:7} Attributes: {1:7}".format(
+ p.type_count, p.type_attribute_count))
+ print(" Users: {0:7} Roles: {1:7}".format(
+ p.user_count, p.role_count))
+ print(" Booleans: {0:7} Cond. Expr.: {1:7}".format(
+ p.boolean_count, p.conditional_count))
+ print(" Allow: {0:7} Neverallow: {1:7}".format(
+ p.allow_count, p.neverallow_count))
+ print(" Auditallow: {0:7} Dontaudit: {1:7}".format(
+ p.auditallow_count, p.dontaudit_count))
+ print(" Type_trans: {0:7} Type_change: {1:7}".format(
+ p.type_transition_count, p.type_change_count))
+ print(" Type_member: {0:7} Range_trans: {1:7}".format(
+ p.type_member_count, p.range_transition_count))
+ print(" Role allow: {0:7} Role_trans: {1:7}".format(
+ p.role_allow_count, p.role_transition_count))
+ print(" Constraints: {0:7} Validatetrans: {1:7}".format(
+ p.constraint_count, p.validatetrans_count))
+ print(" MLS Constrain: {0:7} MLS Val. Tran: {1:7}".format(
+ p.mlsconstraint_count, p.mlsvalidatetrans_count))
+ print(" Permissives: {0:7} Polcap: {1:7}".format(
+ p.permissives_count, p.polcap_count))
+ print(" Defaults: {0:7} Typebounds: {1:7}".format(
+ p.default_count, p.typebounds_count))
+
+ if p.target_platform == "selinux":
+ print(" Allowxperm: {0:7} Neverallowxperm: {1:7}".format(
+ p.allowxperm_count, p.neverallowxperm_count))
+ print(" Auditallowxperm: {0:7} Dontauditxperm: {1:7}".format(
+ p.auditallowxperm_count, p.dontauditxperm_count))
+ print(" Initial SIDs: {0:7} Fs_use: {1:7}".format(
+ p.initialsids_count, p.fs_use_count))
+ print(" Genfscon: {0:7} Portcon: {1:7}".format(
+ p.genfscon_count, p.portcon_count))
+ print(" Netifcon: {0:7} Nodecon: {1:7}".format(
+ p.netifcon_count, p.nodecon_count))
+ elif p.target_platform == "xen":
+ print(" Initial SIDs: {0:7} Devicetreecon {1:7}".format(
+ p.initialsids_count, p.devicetreecon_count))
+ print(" Iomemcon: {0:7} Ioportcon: {1:7}".format(
+ p.iomemcon_count, p.ioportcon_count))
+ print(" Pcidevicecon: {0:7} Pirqcon: {1:7}".format(
+ p.pcidevicecon_count, p.pirqcon_count))
+
+ for desc, component, expander in components:
+ results = sorted(component.results())
+ if not args.flat:
+ print("\n{0}: {1}".format(desc, len(results)))
+ for item in results:
+ result = expander(item) if args.expand else item
+ strfmt = " {0}" if not args.flat else "{0}"
+ print(strfmt.format(result))
+
+except Exception as err:
+ if args.debug:
+ logging.exception(str(err))
+ else:
+ print(err)
+
+ sys.exit(1)
diff --git a/prebuilts/bin/sesearch b/prebuilts/bin/sesearch
new file mode 100755
index 00000000..4e2a5880
--- /dev/null
+++ b/prebuilts/bin/sesearch
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+unamestr=`uname`
+if [ "$unamestr" = "Linux" -o "$unamestr" = "linux" ]; then
+ export PYTHONPATH=$ANDROID_BUILD_TOP/prebuilts/python/linux-x86/2.7.5/lib/python2.7/site-packages:$PYTHONPATH
+ python $ANDROID_BUILD_TOP/external/selinux/prebuilts/bin/sesearch.py "$@"
+else
+ echo "sesearch is only supported on linux"
+fi
diff --git a/prebuilts/bin/sesearch.py b/prebuilts/bin/sesearch.py
new file mode 100755
index 00000000..5879753f
--- /dev/null
+++ b/prebuilts/bin/sesearch.py
@@ -0,0 +1,248 @@
+#!/usr/bin/env python
+# Copyright 2014-2015, Tresys Technology, LLC
+#
+# This file is part of SETools.
+#
+# SETools 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.
+#
+# SETools 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 SETools. If not, see <http://www.gnu.org/licenses/>.
+#
+
+from __future__ import print_function
+import setools
+import argparse
+import sys
+import logging
+
+parser = argparse.ArgumentParser(
+ description="SELinux policy rule search tool.",
+ epilog="TE/MLS rule searches cannot be mixed with RBAC rule searches.")
+parser.add_argument("--version", action="version", version=setools.__version__)
+parser.add_argument("policy", help="Path to the SELinux policy to search.", nargs="?")
+parser.add_argument("-v", "--verbose", action="store_true",
+ help="Print extra informational messages")
+parser.add_argument("--debug", action="store_true", dest="debug", help="Enable debugging.")
+
+rtypes = parser.add_argument_group("TE Rule Types")
+rtypes.add_argument("-A", action="store_true", help="Search allow and allowxperm rules.")
+rtypes.add_argument("--allow", action="append_const",
+ const="allow", dest="tertypes",
+ help="Search allow rules.")
+rtypes.add_argument("--allowxperm", action="append_const",
+ const="allowxperm", dest="tertypes",
+ help="Search allowxperm rules.")
+rtypes.add_argument("--auditallow", action="append_const",
+ const="auditallow", dest="tertypes",
+ help="Search auditallow rules.")
+rtypes.add_argument("--auditallowxperm", action="append_const",
+ const="auditallowxperm", dest="tertypes",
+ help="Search auditallowxperm rules.")
+rtypes.add_argument("--dontaudit", action="append_const",
+ const="dontaudit", dest="tertypes",
+ help="Search dontaudit rules.")
+rtypes.add_argument("--dontauditxperm", action="append_const",
+ const="dontauditxperm", dest="tertypes",
+ help="Search dontauditxperm rules.")
+rtypes.add_argument("--neverallow", action="append_const",
+ const="neverallow", dest="tertypes",
+ help="Search neverallow rules.")
+rtypes.add_argument("--neverallowxperm", action="append_const",
+ const="neverallowxperm", dest="tertypes",
+ help="Search neverallowxperm rules.")
+rtypes.add_argument("-T", "--type_trans", action="append_const",
+ const="type_transition", dest="tertypes",
+ help="Search type_transition rules.")
+rtypes.add_argument("--type_change", action="append_const",
+ const="type_change", dest="tertypes",
+ help="Search type_change rules.")
+rtypes.add_argument("--type_member", action="append_const",
+ const="type_member", dest="tertypes",
+ help="Search type_member rules.")
+rbacrtypes = parser.add_argument_group("RBAC Rule Types")
+rbacrtypes.add_argument("--role_allow", action="append_const",
+ const="allow", dest="rbacrtypes",
+ help="Search role allow rules.")
+rbacrtypes.add_argument("--role_trans", action="append_const",
+ const="role_transition", dest="rbacrtypes",
+ help="Search role_transition rules.")
+
+mlsrtypes = parser.add_argument_group("MLS Rule Types")
+mlsrtypes.add_argument("--range_trans", action="append_const",
+ const="range_transition", dest="mlsrtypes",
+ help="Search range_transition rules.")
+
+expr = parser.add_argument_group("Expressions")
+expr.add_argument("-s", "--source",
+ help="Source type/role of the TE/RBAC rule.")
+expr.add_argument("-t", "--target",
+ help="Target type/role of the TE/RBAC rule.")
+expr.add_argument("-c", "--class", dest="tclass",
+ help="Comma separated list of object classes")
+expr.add_argument("-p", "--perms", metavar="PERMS",
+ help="Comma separated list of permissions.")
+expr.add_argument("-x", "--xperms", metavar="XPERMS",
+ help="Comma separated list of extended permissions.")
+expr.add_argument("-D", "--default",
+ help="Default of the rule. (type/role/range transition rules)")
+expr.add_argument("-b", "--bool", dest="boolean", metavar="BOOL",
+ help="Comma separated list of Booleans in the conditional expression.")
+
+opts = parser.add_argument_group("Search options")
+opts.add_argument("-eb", action="store_true", dest="boolean_equal",
+ help="Match Boolean list exactly instead of matching any listed Boolean.")
+opts.add_argument("-ep", action="store_true", dest="perms_equal",
+ help="Match permission set exactly instead of matching any listed permission.")
+opts.add_argument("-ex", action="store_true", dest="xperms_equal",
+ help="Match extended permission set exactly instead of matching any listed "
+ "permission.")
+opts.add_argument("-ds", action="store_false", dest="source_indirect",
+ help="Match source attributes directly instead of matching member types/roles.")
+opts.add_argument("-dt", action="store_false", dest="target_indirect",
+ help="Match target attributes directly instead of matching member types/roles.")
+opts.add_argument("-rs", action="store_true", dest="source_regex",
+ help="Use regular expression matching for the source type/role.")
+opts.add_argument("-rt", action="store_true", dest="target_regex",
+ help="Use regular expression matching for the target type/role.")
+opts.add_argument("-rc", action="store_true", dest="tclass_regex",
+ help="Use regular expression matching for the object class.")
+opts.add_argument("-rd", action="store_true", dest="default_regex",
+ help="Use regular expression matching for the default type/role.")
+opts.add_argument("-rb", action="store_true", dest="boolean_regex",
+ help="Use regular expression matching for Booleans.")
+
+args = parser.parse_args()
+
+if args.A:
+ try:
+ args.tertypes.extend(["allow", "allowxperm"])
+ except AttributeError:
+ args.tertypes = ["allow", "allowxperm"]
+
+if not args.tertypes and not args.mlsrtypes and not args.rbacrtypes:
+ parser.error("At least one rule type must be specified.")
+
+if args.debug:
+ logging.basicConfig(level=logging.DEBUG,
+ format='%(asctime)s|%(levelname)s|%(name)s|%(message)s')
+elif args.verbose:
+ logging.basicConfig(level=logging.INFO, format='%(message)s')
+else:
+ logging.basicConfig(level=logging.WARNING, format='%(message)s')
+
+try:
+ p = setools.SELinuxPolicy(args.policy)
+
+ if args.tertypes:
+ q = setools.TERuleQuery(p,
+ ruletype=args.tertypes,
+ source=args.source,
+ source_indirect=args.source_indirect,
+ source_regex=args.source_regex,
+ target=args.target,
+ target_indirect=args.target_indirect,
+ target_regex=args.target_regex,
+ tclass_regex=args.tclass_regex,
+ perms_equal=args.perms_equal,
+ xperms_equal=args.xperms_equal,
+ default=args.default,
+ default_regex=args.default_regex,
+ boolean_regex=args.boolean_regex,
+ boolean_equal=args.boolean_equal)
+
+ # these are broken out from the above statement to prevent making a list
+ # with an empty string in it (split on empty string)
+ if args.tclass:
+ if args.tclass_regex:
+ q.tclass = args.tclass
+ else:
+ q.tclass = args.tclass.split(",")
+
+ if args.perms:
+ q.perms = args.perms.split(",")
+
+ if args.xperms:
+ xperms = []
+ for item in args.xperms.split(","):
+ rng = item.split("-")
+ if len(rng) == 2:
+ xperms.append((int(rng[0], base=16), int(rng[1], base=16)))
+ elif len(rng) == 1:
+ xperms.append((int(rng[0], base=16), int(rng[0], base=16)))
+ else:
+ parser.error("Enter an extended permission or extended permission range, e.g. "
+ "0x5411 or 0x8800-0x88ff.")
+
+ q.xperms = xperms
+
+ if args.boolean:
+ if args.boolean_regex:
+ q.boolean = args.boolean
+ else:
+ q.boolean = args.boolean.split(",")
+
+ for r in sorted(q.results()):
+ print(r)
+
+ if args.rbacrtypes:
+ q = setools.RBACRuleQuery(p,
+ ruletype=args.rbacrtypes,
+ source=args.source,
+ source_indirect=args.source_indirect,
+ source_regex=args.source_regex,
+ target=args.target,
+ target_indirect=args.target_indirect,
+ target_regex=args.target_regex,
+ default=args.default,
+ default_regex=args.default_regex,
+ tclass_regex=args.tclass_regex)
+
+ # these are broken out from the above statement to prevent making a list
+ # with an empty string in it (split on empty string)
+ if args.tclass:
+ if args.tclass_regex:
+ q.tclass = args.tclass
+ else:
+ q.tclass = args.tclass.split(",")
+
+ for r in sorted(q.results()):
+ print(r)
+
+ if args.mlsrtypes:
+ q = setools.MLSRuleQuery(p,
+ ruletype=args.mlsrtypes,
+ source=args.source,
+ source_indirect=args.source_indirect,
+ source_regex=args.source_regex,
+ target=args.target,
+ target_indirect=args.target_indirect,
+ target_regex=args.target_regex,
+ tclass_regex=args.tclass_regex,
+ default=args.default)
+
+ # these are broken out from the above statement to prevent making a list
+ # with an empty string in it (split on empty string)
+ if args.tclass:
+ if args.tclass_regex:
+ q.tclass = args.tclass
+ else:
+ q.tclass = args.tclass.split(",")
+
+ for r in sorted(q.results()):
+ print(r)
+
+except Exception as err:
+ if args.debug:
+ logging.exception(str(err))
+ else:
+ print(err)
+
+ sys.exit(1)
diff --git a/python/audit2allow/audit2allow b/python/audit2allow/audit2allow
index 09b06f66..09b06f66 100644..100755
--- a/python/audit2allow/audit2allow
+++ b/python/audit2allow/audit2allow
diff --git a/secilc/Android.bp b/secilc/Android.bp
new file mode 100644
index 00000000..75597dba
--- /dev/null
+++ b/secilc/Android.bp
@@ -0,0 +1,27 @@
+common_CFLAGS = [
+ "-Wall",
+ "-Werror",
+ "-Wshadow",
+]
+
+cc_binary {
+ name: "secilc",
+ host_supported: true,
+ cflags: common_CFLAGS,
+ srcs: ["secilc.c"],
+ static_libs: ["libsepol"],
+ stl: "none",
+ // secilc is a program that is executed very early by init.
+ // Since it is before the mount namespaces are setup, /system/bin/linker
+ // and /system/lib/libc.so point to the mount points where nothing
+ // is mounted on yet. Therefore, secilc has to have explicit knowledge about
+ // the paths where the bootstrap Bionic is. bootstrap:true sets DT_INTERP to
+ // /system/bin/bootstrap/linker. RPATH is explicitly set to /system/lib/bootstrap
+ // where the bootstrap libc.so is.
+ bootstrap: true,
+ target: {
+ android: {
+ ldflags: ["-Wl,--rpath,/system/${LIB}/bootstrap"],
+ },
+ },
+}