diff options
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 @@ -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. @@ -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"], + }, + }, +} |