mirror of
https://github.com/SELinuxProject/setools
synced 2025-03-25 04:26:28 +00:00
Initial public release.
This commit is contained in:
commit
39505d0295
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/build
|
||||
/setools.egg-info
|
||||
*.pyc
|
||||
*.pyo
|
7
COPYING
Normal file
7
COPYING
Normal file
@ -0,0 +1,7 @@
|
||||
The intent is to allow free use of this source code. All programs'
|
||||
source files are copyright protected and freely distributed under the
|
||||
GNU General Public License (see COPYING.GPL). All library source
|
||||
files are copyright under the GNU Lesser General Public License (see
|
||||
COPYING.LGPL). All files distributed with this package indicate the
|
||||
appropriate license to use with that file. Absolutely no warranty is
|
||||
provided or implied.
|
339
COPYING.GPL
Normal file
339
COPYING.GPL
Normal file
@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, 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.
|
||||
|
||||
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 Lesser 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.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser General
|
||||
Public License instead of this License.
|
502
COPYING.LGPL
Normal file
502
COPYING.LGPL
Normal file
@ -0,0 +1,502 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, 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 Street, 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!
|
157
README
Normal file
157
README
Normal file
@ -0,0 +1,157 @@
|
||||
SETools - Policy analysis tools for SELinux
|
||||
setools@tresys.com, https://github.com/TresysTechnology/setools3/wiki
|
||||
|
||||
|
||||
TABLE OF CONTENTS
|
||||
-----------------
|
||||
|
||||
1. Overview
|
||||
2. Installation
|
||||
2.1. building SETools
|
||||
2.2. setup.py flags
|
||||
3. Features
|
||||
3.1. graphical tools
|
||||
3.2. command-line tools
|
||||
3.3. analysis libraries
|
||||
4. Obtaining SETools
|
||||
5. Reporting bugs
|
||||
6. Copyright license
|
||||
|
||||
|
||||
1. Overview
|
||||
-----------
|
||||
|
||||
This file describes SETools, developed by Tresys Technology. SETools
|
||||
is a collection of graphical tools, command-line tools, and libraries
|
||||
designed to facilitate SELinux policy analysis. Please consult the
|
||||
KNOWN-BUGS file prior to reporting bugs.
|
||||
|
||||
|
||||
2. Installation
|
||||
---------------
|
||||
|
||||
SETools uses the Python setuptools build system to build, and install.
|
||||
As such it contains a setup.py script that will install the setools.
|
||||
|
||||
SETools requires the following development packages for building:
|
||||
Python 2.7
|
||||
setuptools
|
||||
|
||||
To build SETools' graphical tools, the following packages are required:
|
||||
TBD
|
||||
|
||||
To run SETools, the following packages are required:
|
||||
Python 2.7
|
||||
sepolgen
|
||||
NetworkX
|
||||
SETools 3.3.9+ libraries w/Python SWIG wrappers
|
||||
|
||||
|
||||
2.1. building SETools
|
||||
---------------------
|
||||
|
||||
Unpack the official distribution or check out the git repository,
|
||||
and perform the following:
|
||||
|
||||
$ cd setools
|
||||
$ python setup.py build
|
||||
$ python setup.py install
|
||||
|
||||
This will put the applications in /usr/bin, data files in /usr/share/setools,
|
||||
and libraries in /usr/lib/pythonX.Y/site-packages/libapol.
|
||||
|
||||
|
||||
2.2. setup.py flags
|
||||
-------------------
|
||||
|
||||
Please see `python setup.py --help` or `python setup.py install --help`
|
||||
for up-to-date information on build and install options, respectively.
|
||||
|
||||
|
||||
2.3 unit tests
|
||||
--------------
|
||||
|
||||
One goal for SETools is to provide confidence in the validity of the
|
||||
output for the tools. The unit tests for SETools can be run with
|
||||
the following command
|
||||
|
||||
$ python setup.py test
|
||||
|
||||
|
||||
3. Features
|
||||
-----------
|
||||
|
||||
SETools encompasses a number of tools, both graphical and command
|
||||
line, and libraries. Many of the programs have help files accessible
|
||||
during runtime.
|
||||
|
||||
|
||||
3.1. graphical tools
|
||||
--------------------
|
||||
|
||||
TBD
|
||||
|
||||
|
||||
3.2. command-line tools
|
||||
-----------------------
|
||||
|
||||
Some tools in the SETools suite may be run in a terminal
|
||||
environment.
|
||||
|
||||
sedta:
|
||||
A tool to perform domain transition analyses on an SELinux policy.
|
||||
|
||||
seinfo:
|
||||
A tool to quickly get a list of components from an SELinux policy.
|
||||
|
||||
seinfoflow:
|
||||
A tool to perform information flow analyses on an SELinux policy.
|
||||
|
||||
sesearch:
|
||||
A tool to search rules (allow, type_transition, etc.)
|
||||
within an SELinux policy.
|
||||
|
||||
|
||||
3.3. analysis libraries
|
||||
-----------------------
|
||||
|
||||
The SETools support libraries are available for use in third-party
|
||||
applications. Although they are not officially supported (and thus
|
||||
subject to change between SETools releases), we will do our best to
|
||||
maintain API stability.
|
||||
|
||||
|
||||
4. Obtaining SETools
|
||||
--------------------
|
||||
|
||||
Official releases of SETools may be freely downloaded from TBD.
|
||||
|
||||
SETools source code is maintained within a github repository.
|
||||
From the command line do:
|
||||
|
||||
$ git clone https://github.com/TresysTechnology/setools.git
|
||||
|
||||
You may also browse the github repository at
|
||||
https://github.com/TresysTechnology/setools.
|
||||
|
||||
SETools included in most Linux distributions which enable
|
||||
SELinux usage, such as Fedora, Red Hat Enterprise Linux, Gentoo,
|
||||
and Debian.
|
||||
|
||||
|
||||
5. Reporting bugs
|
||||
-----------------
|
||||
|
||||
TBD
|
||||
|
||||
|
||||
6. Copyright license
|
||||
--------------------
|
||||
|
||||
The intent is to allow free use of this source code. All programs'
|
||||
source files are copyright protected and freely distributed under the
|
||||
GNU General Public License (see COPYING.GPL). All library source
|
||||
files are copyright under the GNU Lesser General Public License (see
|
||||
COPYING.LGPL). All files distributed with this package indicate the
|
||||
appropriate license to use with that file. Absolutely no warranty is
|
||||
provided or implied.
|
6
UNSTABLE_API
Normal file
6
UNSTABLE_API
Normal file
@ -0,0 +1,6 @@
|
||||
Warning:
|
||||
|
||||
SETools 4.0 is still in early development. The API, classes, package names,
|
||||
etc. should be considered unstable, and should not be used for external
|
||||
applications yet. Of particular note, the libapol package will be
|
||||
renamed to setools once the SETools 3.3 dependency is eliminated.
|
1284
data/perm_map
Normal file
1284
data/perm_map
Normal file
File diff suppressed because it is too large
Load Diff
42
libapol/__init__.py
Normal file
42
libapol/__init__.py
Normal file
@ -0,0 +1,42 @@
|
||||
"""The SETools SELinux policy analysis library."""
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
__version__ = "4.0.0-alpha"
|
||||
|
||||
# Python classes for policy representation
|
||||
import policyrep
|
||||
from policyrep import SELinuxPolicy
|
||||
|
||||
# Component Queries
|
||||
import typequery
|
||||
import boolquery
|
||||
import polcapquery
|
||||
|
||||
# Rule Queries
|
||||
import terulequery
|
||||
import rbacrulequery
|
||||
import mlsrulequery
|
||||
|
||||
# Information Flow Analysis
|
||||
import infoflow
|
||||
import permmap
|
||||
|
||||
# Domain Transition Analysis
|
||||
import dta
|
79
libapol/boolquery.py
Normal file
79
libapol/boolquery.py
Normal file
@ -0,0 +1,79 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import re
|
||||
|
||||
import compquery
|
||||
|
||||
|
||||
class BoolQuery(compquery.ComponentQuery):
|
||||
|
||||
"""Query SELinux policy Booleans."""
|
||||
|
||||
def __init__(self, policy,
|
||||
name="", name_regex=False,
|
||||
default=False, match_default=False):
|
||||
"""
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
name The Boolean name to match.
|
||||
name_regex If true, regular expression matching
|
||||
will be used on the Boolean name.
|
||||
default The default state to match.
|
||||
match_default If true, the default state will be matched.
|
||||
"""
|
||||
|
||||
self.policy = policy
|
||||
self.set_name(name, regex=name_regex)
|
||||
self.set_default(match_default, default=default)
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all Booleans matching the criteria."""
|
||||
|
||||
for b in self.policy.bools():
|
||||
if self.name and not self._match_regex(
|
||||
b,
|
||||
self.name,
|
||||
self.name_regex,
|
||||
self.name_cmp):
|
||||
continue
|
||||
|
||||
if self.match_default and b.state() != self.default:
|
||||
continue
|
||||
|
||||
yield b
|
||||
|
||||
def set_default(self, match, **opts):
|
||||
"""
|
||||
Set if the default Boolean state should be matched.
|
||||
|
||||
Parameter:
|
||||
match If true, the default state will be matched.
|
||||
default The default state to match.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.match_default = bool(match)
|
||||
|
||||
for k in opts.keys():
|
||||
if k == "default":
|
||||
self.default = bool(opts[k])
|
||||
else:
|
||||
raise NameError("Invalid default option: {0}".format(k))
|
51
libapol/compquery.py
Normal file
51
libapol/compquery.py
Normal file
@ -0,0 +1,51 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import re
|
||||
|
||||
import query
|
||||
|
||||
|
||||
class ComponentQuery(query.PolicyQuery):
|
||||
|
||||
"""Abstract base class for SETools component queries."""
|
||||
|
||||
def set_name(self, name, **opts):
|
||||
"""
|
||||
Set the criteria for matching the component's name.
|
||||
|
||||
Parameter:
|
||||
name Name to match the component's name.
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.name = str(name)
|
||||
|
||||
for k in opts.keys():
|
||||
if k == "regex":
|
||||
self.name_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid name option: {0}".format(k))
|
||||
|
||||
if self.name_regex:
|
||||
self.name_cmp = re.compile(self.name)
|
||||
else:
|
||||
self.name_cmp = None
|
437
libapol/dta.py
Normal file
437
libapol/dta.py
Normal file
@ -0,0 +1,437 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import itertools
|
||||
from collections import defaultdict
|
||||
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class DomainTransitionAnalysis(object):
|
||||
|
||||
"""Domain transition analysis."""
|
||||
|
||||
def __init__(self, policy):
|
||||
"""
|
||||
Parameter:
|
||||
policy The policy to analyze.
|
||||
"""
|
||||
self.policy = policy
|
||||
self.rebuildgraph = True
|
||||
self.G = nx.DiGraph()
|
||||
|
||||
def __get_entrypoints(self, source, target):
|
||||
"""
|
||||
Generator which returns the entrypoint, execute, and
|
||||
type_transition rules for each entrypoint.
|
||||
|
||||
Parameter:
|
||||
source The source node for the transition.
|
||||
target The target node for the transition.
|
||||
|
||||
Yield: tuple(type, entry, exec, trans)
|
||||
|
||||
type The entrypoint type.
|
||||
entry The entrypoint rules.
|
||||
exec The execute rules.
|
||||
trans The type_transition rules.
|
||||
"""
|
||||
for e in self.G.edge[source][target]['entrypoint']:
|
||||
if self.G.edge[source][target]['type_transition'][e]:
|
||||
yield e, \
|
||||
self.G.edge[source][target]['entrypoint'][e], \
|
||||
self.G.edge[source][target]['execute'][e], \
|
||||
self.G.edge[source][target]['type_transition'][e]
|
||||
else:
|
||||
yield e, \
|
||||
self.G.edge[source][target]['entrypoint'][e], \
|
||||
self.G.edge[source][target]['execute'][e], \
|
||||
[]
|
||||
|
||||
# TODO: consider making sure source and target are valid
|
||||
# both as types and also in graph
|
||||
# TODO: make reverse an option. on that option,
|
||||
# simply reverse the graph. Will probably have to fix up
|
||||
# __get_steps to output correctly so sedta doesn't have to
|
||||
# change.
|
||||
def __get_steps(self, path):
|
||||
"""
|
||||
Generator which returns the source, target, and associated rules
|
||||
for each domain transition.
|
||||
|
||||
Parameter:
|
||||
path A list of graph node names representing an information flow path.
|
||||
|
||||
Yield: tuple(source, target, transition, entrypoints,
|
||||
setexec, dyntransition, setcurrent)
|
||||
|
||||
source The source type for this step of the domain transition.
|
||||
target The target type for this step of the domain transition.
|
||||
transition The list of TE rules providing transition permissions.
|
||||
entrypoints Generator which provides entrypoint-related rules.
|
||||
setexec The list of setexec rules.
|
||||
dyntranstion The list of dynamic transition rules.
|
||||
setcurrent The list of setcurrent rules.
|
||||
"""
|
||||
|
||||
for s in range(1, len(path)):
|
||||
source = path[s - 1]
|
||||
target = path[s]
|
||||
|
||||
yield source, target, \
|
||||
self.G.edge[source][target]['transition'], \
|
||||
self.__get_entrypoints(source, target), \
|
||||
self.G.edge[source][target]['setexec'], \
|
||||
self.G.edge[source][target]['dyntransition'], \
|
||||
self.G.edge[source][target]['setcurrent']
|
||||
|
||||
def shortest_path(self, source, target):
|
||||
"""
|
||||
Generator which yields one shortest domain transition path
|
||||
between the source and target types (there may be more).
|
||||
|
||||
Parameters:
|
||||
source The source type.
|
||||
target The target type.
|
||||
|
||||
Yield: generator(steps)
|
||||
|
||||
steps A generator that returns the tuple of
|
||||
source, target, and rules for each
|
||||
domain transition.
|
||||
"""
|
||||
if self.rebuildgraph:
|
||||
self._build_graph()
|
||||
|
||||
if source in self.G and target in self.G:
|
||||
try:
|
||||
path = nx.shortest_path(self.G, source, target)
|
||||
except nx.exception.NetworkXNoPath:
|
||||
pass
|
||||
else:
|
||||
yield self.__get_steps(path)
|
||||
|
||||
def all_paths(self, source, target, maxlen=2):
|
||||
"""
|
||||
Generator which yields all domain transition paths between
|
||||
the source and target up to the specified maximum path
|
||||
length.
|
||||
|
||||
Parameters:
|
||||
source The source type.
|
||||
target The target type.
|
||||
maxlen Maximum length of paths.
|
||||
|
||||
Yield: generator(steps)
|
||||
|
||||
steps A generator that returns the tuple of
|
||||
source, target, and rules for each
|
||||
domain transition.
|
||||
"""
|
||||
if self.rebuildgraph:
|
||||
self._build_graph()
|
||||
|
||||
if source in self.G and target in self.G:
|
||||
try:
|
||||
paths = nx.all_simple_paths(self.G, source, target, maxlen)
|
||||
except nx.exception.NetworkXNoPath:
|
||||
pass
|
||||
else:
|
||||
for p in paths:
|
||||
yield self.__get_steps(p)
|
||||
|
||||
def all_shortest_paths(self, source, target):
|
||||
"""
|
||||
Generator which yields all shortest domain transition paths
|
||||
between the source and target types.
|
||||
|
||||
Parameters:
|
||||
source The source type.
|
||||
target The target type.
|
||||
|
||||
Yield: generator(steps)
|
||||
|
||||
steps A generator that returns the tuple of
|
||||
source, target, and rules for each
|
||||
domain transition.
|
||||
"""
|
||||
if self.rebuildgraph:
|
||||
self._build_graph()
|
||||
|
||||
if source in self.G and target in self.G:
|
||||
try:
|
||||
paths = nx.all_shortest_paths(self.G, source, target)
|
||||
except nx.exception.NetworkXNoPath:
|
||||
pass
|
||||
else:
|
||||
for p in paths:
|
||||
yield self.__get_steps(p)
|
||||
|
||||
def transitions(self, source):
|
||||
"""
|
||||
Generator which yields all domain transitions out of a
|
||||
specified source type.
|
||||
|
||||
Parameters:
|
||||
source The starting type.
|
||||
|
||||
Yield: generator(steps)
|
||||
|
||||
steps A generator that returns the tuple of
|
||||
source, target, and rules for each
|
||||
domain transition.
|
||||
"""
|
||||
if self.rebuildgraph:
|
||||
self._build_graph()
|
||||
|
||||
for source, target in self.G.out_edges_iter(source):
|
||||
yield source, target, \
|
||||
self.G.edge[source][target]['transition'], \
|
||||
self.__get_entrypoints(source, target), \
|
||||
self.G.edge[source][target]['setexec'], \
|
||||
self.G.edge[source][target]['dyntransition'], \
|
||||
self.G.edge[source][target]['setcurrent']
|
||||
|
||||
def get_stats(self):
|
||||
"""
|
||||
Get the domain transition graph statistics.
|
||||
|
||||
Return: tuple(nodes, edges)
|
||||
|
||||
nodes The number of nodes (types) in the graph.
|
||||
edges The number of edges (domain transitions) in the graph.
|
||||
"""
|
||||
return (self.G.number_of_nodes(), self.G.number_of_edges())
|
||||
|
||||
# Graph edge properties:
|
||||
# Each entry in the property dict corresponds to
|
||||
# a rule list. For entrypoint/execute/type_transition
|
||||
# it is a dictionary keyed on the entrypoint type.
|
||||
def __add_edge(self, source, target):
|
||||
self.G.add_edge(source, target)
|
||||
if not 'transition' in self.G[source][target]:
|
||||
self.G[source][target]['transition'] = []
|
||||
if not 'entrypoint' in self.G[source][target]:
|
||||
self.G[source][target]['entrypoint'] = defaultdict(list)
|
||||
if not 'execute' in self.G[source][target]:
|
||||
self.G[source][target]['execute'] = defaultdict(list)
|
||||
if not 'type_transition'in self.G[source][target]:
|
||||
self.G[source][target]['type_transition'] = defaultdict(list)
|
||||
if not 'setexec' in self.G[source][target]:
|
||||
self.G[source][target]['setexec'] = []
|
||||
if not 'dyntransition' in self.G[source][target]:
|
||||
self.G[source][target]['dyntransition'] = []
|
||||
if not 'setcurrent' in self.G[source][target]:
|
||||
self.G[source][target]['setcurrent'] = []
|
||||
|
||||
# Domain transition requirements:
|
||||
#
|
||||
# Standard transitions a->b:
|
||||
# allow a b:process transition;
|
||||
# allow a b_exec:file execute;
|
||||
# allow b b_exec:file entrypoint;
|
||||
#
|
||||
# and at least one of:
|
||||
# allow a self:process setexec;
|
||||
# type_transition a b_exec:process b;
|
||||
#
|
||||
# Dynamic transition x->y:
|
||||
# allow x y:process dyntransition;
|
||||
# allow x self:process setcurrent;
|
||||
#
|
||||
# Algorithm summary:
|
||||
# 1. iterate over all rules
|
||||
# 1. skip non allow/type_transition rules
|
||||
# 2. if process transition or dyntransition, create edge,
|
||||
# initialize rule lists, add the (dyn)transition rule
|
||||
# 3. if process setexec or setcurrent, add to appropriate dict
|
||||
# keyed on the subject
|
||||
# 4. if file exec, entrypoint, or type_transition:process,
|
||||
# add to appropriate dict keyed on subject,object.
|
||||
# 2. Iterate over all graph edges:
|
||||
# 1. if there is a transition rule (else add to invalid
|
||||
# transition list):
|
||||
# 1. use set intersection to find matching exec
|
||||
# and entrypoint rules. If none, add to invalid
|
||||
# transition list.
|
||||
# 2. for each valid entrypoint, add rules to the
|
||||
# edge's lists if there is either a
|
||||
# type_transition for it or the source process
|
||||
# has setexec permissions.
|
||||
# 3. If there are neither type_transitions nor
|
||||
# setexec permissions, add to the invalid
|
||||
# transition list
|
||||
# 2. if there is a dyntransition rule (else add to invalid
|
||||
# dyntrans list):
|
||||
# 1. If the source has a setcurrent rule, add it
|
||||
# to the edge's list, else add to invalid
|
||||
# dyntransition list.
|
||||
# 3. Iterate over all graph edges:
|
||||
# 1. if the edge has an invalid trans and dyntrans, delete
|
||||
# the edge.
|
||||
# 2. if the edge has an invalid trans, clear the related
|
||||
# lists on the edge.
|
||||
# 3. if the edge has an invalid dyntrans, clear the related
|
||||
# lists on the edge.
|
||||
#
|
||||
# Note: strings are used for node names temporarily, until the
|
||||
# string->TypeAttr object lookup code is implemented.
|
||||
def _build_graph(self):
|
||||
self.G.clear()
|
||||
|
||||
# hash tables keyed on domain type
|
||||
setexec = defaultdict(list)
|
||||
setcurrent = defaultdict(list)
|
||||
|
||||
# hash tables keyed on (domain, entrypoint file type)
|
||||
# the parameter for defaultdict has to be callable
|
||||
# hence the lambda for the nested defaultdict
|
||||
execute = defaultdict(lambda: defaultdict(list))
|
||||
entrypoint = defaultdict(lambda: defaultdict(list))
|
||||
|
||||
# hash table keyed on (domain, entrypoint, target domain)
|
||||
type_trans = defaultdict(
|
||||
lambda: defaultdict(lambda: defaultdict(list)))
|
||||
|
||||
for r in self.policy.terules():
|
||||
if r.ruletype == "allow":
|
||||
if str(r.tclass) not in ["process", "file"]:
|
||||
continue
|
||||
|
||||
perms = r.perms
|
||||
|
||||
if r.tclass == "process":
|
||||
if "transition" in perms:
|
||||
for s, t in itertools.product(
|
||||
(str(s) for s in r.source.expand()),
|
||||
(str(t) for t in r.target.expand())):
|
||||
self.__add_edge(s, t)
|
||||
self.G[s][t]['transition'].append(r)
|
||||
|
||||
if "dyntransition" in perms:
|
||||
for s, t in itertools.product(
|
||||
(str(s) for s in r.source.expand()),
|
||||
(str(t) for t in r.target.expand())):
|
||||
self.__add_edge(s, t)
|
||||
self.G[s][t]['dyntransition'].append(r)
|
||||
|
||||
if "setexec" in perms:
|
||||
for s in r.source.expand():
|
||||
setexec[str(s)].append(r)
|
||||
|
||||
if "setcurrent" in perms:
|
||||
for s in r.source.expand():
|
||||
setcurrent[str(s)].append(r)
|
||||
|
||||
else:
|
||||
if "execute" in perms:
|
||||
for s, t in itertools.product(
|
||||
(str(s) for s in r.source.expand()),
|
||||
(str(t) for t in r.target.expand())):
|
||||
execute[s][t].append(r)
|
||||
|
||||
if "entrypoint" in perms:
|
||||
for s, t in itertools.product(
|
||||
(str(s) for s in r.source.expand()),
|
||||
(str(t) for t in r.target.expand())):
|
||||
entrypoint[s][t].append(r)
|
||||
|
||||
elif r.ruletype == "type_transition":
|
||||
if r.tclass != "process":
|
||||
continue
|
||||
|
||||
d = str(r.default)
|
||||
for s, t in itertools.product(
|
||||
(str(s) for s in r.source.expand()),
|
||||
(str(t) for t in r.target.expand())):
|
||||
type_trans[s][t][d].append(r)
|
||||
else:
|
||||
continue
|
||||
|
||||
invalid_edge = []
|
||||
clear_transition = []
|
||||
clear_dyntransition = []
|
||||
|
||||
for s, t in self.G.edges_iter():
|
||||
invalid_trans = False
|
||||
invalid_dyntrans = False
|
||||
|
||||
if self.G[s][t]['transition']:
|
||||
# get matching domain exec w/entrypoint type
|
||||
entry = set(entrypoint[t].keys())
|
||||
exe = set(execute[s].keys())
|
||||
match = entry.intersection(exe)
|
||||
|
||||
if not match:
|
||||
# there are no valid entrypoints
|
||||
invalid_trans = True
|
||||
else:
|
||||
# TODO try to improve the
|
||||
# efficiency in this loop
|
||||
for m in match:
|
||||
if s in setexec or type_trans[s][m]:
|
||||
# add subkey for each entrypoint
|
||||
self.G[s][t]['entrypoint'][m] += entrypoint[t][m]
|
||||
self.G[s][t]['execute'][m] += execute[s][m]
|
||||
|
||||
if type_trans[s][m][t]:
|
||||
self.G[s][t]['type_transition'][
|
||||
m] += type_trans[s][m][t]
|
||||
|
||||
if s in setexec:
|
||||
self.G[s][t]['setexec'] += setexec[s]
|
||||
|
||||
if not self.G[s][t]['setexec'] and not self.G[s][t]['type_transition']:
|
||||
invalid_trans = True
|
||||
else:
|
||||
invalid_trans = True
|
||||
|
||||
if self.G[s][t]['dyntransition']:
|
||||
if s in setcurrent:
|
||||
self.G[s][t]['setcurrent'] += setcurrent[s]
|
||||
else:
|
||||
invalid_dyntrans = True
|
||||
else:
|
||||
invalid_dyntrans = True
|
||||
|
||||
# cannot change the edges while iterating over them,
|
||||
# so keep appropriate lists
|
||||
if invalid_trans and invalid_dyntrans:
|
||||
invalid_edge.append((s, t))
|
||||
elif invalid_trans:
|
||||
clear_transition.append((s, t))
|
||||
elif invalid_dyntrans:
|
||||
clear_dyntransition.append((s, t))
|
||||
|
||||
# Remove invalid transitions
|
||||
self.G.remove_edges_from(invalid_edge)
|
||||
for s, t in clear_transition:
|
||||
# if only the regular transition is invalid,
|
||||
# clear the relevant lists
|
||||
del self.G[s][t]['transition'][:]
|
||||
self.G[s][t]['execute'].clear()
|
||||
self.G[s][t]['entrypoint'].clear()
|
||||
self.G[s][t]['type_transition'].clear()
|
||||
del self.G[s][t]['setexec'][:]
|
||||
for s, t in clear_dyntransition:
|
||||
# if only the dynamic transition is invalid,
|
||||
# clear the relevant lists
|
||||
del self.G[s][t]['dyntransition'][:]
|
||||
del self.G[s][t]['setcurrent'][:]
|
||||
|
||||
self.rebuildgraph = False
|
296
libapol/infoflow.py
Normal file
296
libapol/infoflow.py
Normal file
@ -0,0 +1,296 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import itertools
|
||||
|
||||
import policyrep
|
||||
import permmap
|
||||
import networkx as nx
|
||||
|
||||
|
||||
class InfoFlowAnalysis(object):
|
||||
|
||||
"""Information flow analysis."""
|
||||
|
||||
def __init__(self, policy, perm_map, minweight=1, exclude=[]):
|
||||
"""
|
||||
Parameters:
|
||||
policy The policy to analyze.
|
||||
perm_map The permission map or path to the permission map file.
|
||||
minweight The minimum permission weight to include in the analysis.
|
||||
(default is 1)
|
||||
exclude The types excluded from the information flow analysis.
|
||||
(default is none)
|
||||
"""
|
||||
|
||||
self.policy = policy
|
||||
|
||||
self.set_min_weight(minweight)
|
||||
self.set_perm_map(perm_map)
|
||||
self.set_exclude(exclude)
|
||||
self.rebuildgraph = True
|
||||
|
||||
self.G = nx.DiGraph()
|
||||
|
||||
def set_min_weight(self, w):
|
||||
"""
|
||||
Set the minimum permission weight for the information flow analysis.
|
||||
|
||||
Parameter:
|
||||
w Minimum permission weight (1-10)
|
||||
|
||||
Exceptions:
|
||||
ValueError The minimum weight is not 1-10.
|
||||
"""
|
||||
if not 1 <= w <= 10:
|
||||
raise ValueError(
|
||||
"Min information flow weight must be an integer 1-10.")
|
||||
|
||||
self.minweight = w
|
||||
self.rebuildgraph = True
|
||||
|
||||
def set_perm_map(self, perm_map):
|
||||
"""
|
||||
Set the permission map used for the information flow analysis.
|
||||
|
||||
Parameter:
|
||||
perm_map The permission map or path to the permission map file.
|
||||
|
||||
Exceptions:
|
||||
TypeError The map is not a file path or permission map object.
|
||||
"""
|
||||
if not isinstance(perm_map, (str, permmap.PermissionMap)):
|
||||
raise TypeError(
|
||||
"Permission map must be a permission map object or a path to a permission map file.")
|
||||
|
||||
if isinstance(perm_map, str):
|
||||
self.perm_map = permmap.PermissionMap(perm_map)
|
||||
else:
|
||||
self.perm_map = perm_map
|
||||
|
||||
self.rebuildgraph = True
|
||||
|
||||
def set_exclude(self, exclude):
|
||||
"""
|
||||
Set the types to exclude from the information flow analysis.
|
||||
|
||||
Parameter:
|
||||
exclude A list of types.
|
||||
"""
|
||||
|
||||
# TODO: a list comprehension that turns the strings into
|
||||
# Type objects
|
||||
self.exclude = exclude
|
||||
|
||||
def __get_steps(self, path):
|
||||
"""
|
||||
Generator which returns the source, target, and associated rules
|
||||
for each information flow step.
|
||||
|
||||
Parameter:
|
||||
path A list of graph node names representing an information flow path.
|
||||
|
||||
Yield: tuple(source, target, rules)
|
||||
|
||||
source The source type for this step of the information flow.
|
||||
target The target type for this step of the information flow.
|
||||
rules The list of rules creating this information flow step.
|
||||
"""
|
||||
for s in range(1, len(path)):
|
||||
source = path[s - 1]
|
||||
target = path[s]
|
||||
yield source, target, self.G.edge[source][target]['rules']
|
||||
|
||||
def shortest_path(self, source, target):
|
||||
"""
|
||||
Generator which yields one shortest path between the source
|
||||
and target types (there may be more).
|
||||
|
||||
Parameters:
|
||||
source The source type.
|
||||
target The target type.
|
||||
|
||||
Yield: generator(steps)
|
||||
|
||||
steps Yield: tuple(source, target, rules)
|
||||
|
||||
source The source type for this step of the information flow.
|
||||
target The target type for this step of the information flow.
|
||||
rules The list of rules creating this information flow step.
|
||||
"""
|
||||
if self.rebuildgraph:
|
||||
self._build_graph()
|
||||
|
||||
if source in self.G and target in self.G:
|
||||
try:
|
||||
path = nx.shortest_path(self.G, source, target)
|
||||
except nx.exception.NetworkXNoPath:
|
||||
pass
|
||||
else:
|
||||
# written as a generator so the caller code
|
||||
# works the same way independent of the graph alg
|
||||
yield self.__get_steps(path)
|
||||
|
||||
def all_paths(self, source, target, maxlen=2):
|
||||
"""
|
||||
Generator which yields all paths between the source and target
|
||||
up to the specified maximum path length. This algorithm
|
||||
tends to get very expensive above 3-5 steps, depending
|
||||
on the policy complexity.
|
||||
|
||||
Parameters:
|
||||
source The source type.
|
||||
target The target type.
|
||||
maxlen Maximum length of paths.
|
||||
|
||||
Yield: generator(steps)
|
||||
|
||||
steps Yield: tuple(source, target, rules)
|
||||
|
||||
source The source type for this step of the information flow.
|
||||
target The target type for this step of the information flow.
|
||||
rules The list of rules creating this information flow step.
|
||||
"""
|
||||
if self.rebuildgraph:
|
||||
self._build_graph()
|
||||
|
||||
if source in self.G and target in self.G:
|
||||
try:
|
||||
paths = nx.all_simple_paths(self.G, source, target, maxlen)
|
||||
except nx.exception.NetworkXNoPath:
|
||||
pass
|
||||
else:
|
||||
for p in paths:
|
||||
yield self.__get_steps(p)
|
||||
|
||||
def all_shortest_paths(self, source, target):
|
||||
"""
|
||||
Generator which yields all shortest paths between the source
|
||||
and target types.
|
||||
|
||||
Parameters:
|
||||
source The source type.
|
||||
target The target type.
|
||||
|
||||
Yield: generator(steps)
|
||||
|
||||
steps Yield: tuple(source, target, rules)
|
||||
|
||||
source The source type for this step of the information flow.
|
||||
target The target type for this step of the information flow.
|
||||
rules The list of rules creating this information flow step.
|
||||
"""
|
||||
if self.rebuildgraph:
|
||||
self._build_graph()
|
||||
|
||||
if source in self.G and target in self.G:
|
||||
try:
|
||||
paths = nx.all_shortest_paths(self.G, source, target)
|
||||
except nx.exception.NetworkXNoPath:
|
||||
pass
|
||||
else:
|
||||
for p in paths:
|
||||
yield self.__get_steps(p)
|
||||
|
||||
def infoflows(self, source):
|
||||
"""
|
||||
Generator which yields all information flows out of a
|
||||
specified source type.
|
||||
|
||||
Parameters:
|
||||
source The starting type.
|
||||
|
||||
Yield: generator(steps)
|
||||
|
||||
steps A generator that returns the tuple of
|
||||
source, target, and rules for each
|
||||
information flow.
|
||||
"""
|
||||
if self.rebuildgraph:
|
||||
self._build_graph()
|
||||
|
||||
for source, target, data in self.G.out_edges_iter(source, data=True):
|
||||
yield source, target, data["rules"]
|
||||
|
||||
def get_stats(self):
|
||||
"""
|
||||
Get the information flow graph statistics.
|
||||
|
||||
Return: tuple(nodes, edges)
|
||||
|
||||
nodes The number of nodes (types) in the graph.
|
||||
edges The number of edges (information flows between types)
|
||||
in the graph.
|
||||
"""
|
||||
return (self.G.number_of_nodes(), self.G.number_of_edges())
|
||||
|
||||
#
|
||||
#
|
||||
# (Internal) Graph building functions
|
||||
#
|
||||
#
|
||||
# 1. __build_graph determines the flow in each direction for each TE
|
||||
# rule and then expands the rule (ignoring excluded types)
|
||||
# 2. __add_flow Simply creates edges in the appropriate direction.
|
||||
# (decrease chance of coding errors for graph operations)
|
||||
# 3. __add_edge does the actual graph insertions. Nodes are implictly
|
||||
# created by the edge additions, i.e. types that have no info flow
|
||||
# due to permission weights or are excluded do not appear in the graph.
|
||||
def __add_edge(self, source, target, rule, weight):
|
||||
# use capacity to store the info flow weight so
|
||||
# we can use network flow algorithms naturally.
|
||||
# The weight for each edge is 1 since each info
|
||||
# flow step is no more costly than another
|
||||
if self.G.has_edge(source, target):
|
||||
self.G.edge[source][target]['rules'].append(rule)
|
||||
edgecap = self.G.edge[source][target]['capacity']
|
||||
self.G.edge[source][target]['capacity'] = max(edgecap, weight)
|
||||
else:
|
||||
self.G.add_edge(
|
||||
source, target, capacity=weight, weight=1, rules=[rule])
|
||||
|
||||
def __add_flow(self, source, target, rule, ww, rw):
|
||||
assert max(ww, rw) >= self.minweight
|
||||
|
||||
# only add flows if they actually flow
|
||||
# in our out of the source type type
|
||||
if source != target:
|
||||
if ww >= self.minweight:
|
||||
self.__add_edge(source, target, rule, ww)
|
||||
|
||||
if rw >= self.minweight:
|
||||
self.__add_edge(target, source, rule, rw)
|
||||
|
||||
def _build_graph(self):
|
||||
self.G.clear()
|
||||
|
||||
for r in self.policy.terules():
|
||||
if r.ruletype != "allow":
|
||||
continue
|
||||
|
||||
(rweight, wweight) = self.perm_map.rule_weight(r)
|
||||
|
||||
# 1. only proceed if weight meets or exceeds the minimum weight
|
||||
# 2. expand source and target to handle attributes
|
||||
# 3. ignore flow if one of the types is in the exclude list
|
||||
if max(rweight, wweight) >= self.minweight:
|
||||
for s, t in itertools.product(r.source.expand(), r.target.expand()):
|
||||
if s not in self.exclude and t not in self.exclude:
|
||||
self.__add_flow(str(s), str(t), r, wweight, rweight)
|
||||
|
||||
self.rebuildgraph = False
|
105
libapol/mlsrulequery.py
Normal file
105
libapol/mlsrulequery.py
Normal file
@ -0,0 +1,105 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import rulequery
|
||||
|
||||
|
||||
class MLSRuleQuery(rulequery.RuleQuery):
|
||||
|
||||
"""Query MLS rules."""
|
||||
|
||||
def __init__(self, policy,
|
||||
ruletype=[],
|
||||
source="", source_indirect=True, source_regex=False,
|
||||
target="", target_indirect=True, target_regex=False,
|
||||
tclass="", tclass_regex=False):
|
||||
"""
|
||||
Parameters:
|
||||
policy The policy to query.
|
||||
ruletype The rule type(s) to match.
|
||||
source The name of the source type/attribute to match.
|
||||
source_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
source_regex If true, regular expression matching will
|
||||
be used on the source type/attribute.
|
||||
Obeys the source_indirect option.
|
||||
target The name of the target type/attribute to match.
|
||||
target_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
target_regex If true, regular expression matching will
|
||||
be used on the target type/attribute.
|
||||
Obeys target_indirect option.
|
||||
tclass The object class(es) to match.
|
||||
tclass_regex If true, use a regular expression for
|
||||
matching the rule's object class.
|
||||
"""
|
||||
|
||||
self.policy = policy
|
||||
|
||||
self.set_ruletype(ruletype)
|
||||
self.set_source(source, indirect=source_indirect, regex=source_regex)
|
||||
self.set_target(target, indirect=target_indirect, regex=target_regex)
|
||||
self.set_tclass(tclass, regex=tclass_regex)
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching MLS rules."""
|
||||
|
||||
for r in self.policy.mlsrules():
|
||||
#
|
||||
# Matching on rule type
|
||||
#
|
||||
if self.ruletype:
|
||||
if not r.ruletype in self.ruletype:
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on source type
|
||||
#
|
||||
if self.source and not self._match_indirect_regex(
|
||||
r.source,
|
||||
self.source,
|
||||
self.source_indirect,
|
||||
self.source_regex,
|
||||
self.source_cmp):
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on target type
|
||||
#
|
||||
if self.target and not self._match_indirect_regex(
|
||||
r.target,
|
||||
self.target,
|
||||
self.target_indirect,
|
||||
self.target_regex,
|
||||
self.target_cmp):
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on object class
|
||||
#
|
||||
if self.tclass and not self._match_object_class(
|
||||
r.tclass,
|
||||
self.tclass,
|
||||
self.tclass_regex,
|
||||
self.tclass_cmp):
|
||||
continue
|
||||
|
||||
# TODO: something with the range
|
||||
|
||||
# if we get here, we have matched all available criteria
|
||||
yield r
|
67
libapol/permmap.py
Normal file
67
libapol/permmap.py
Normal file
@ -0,0 +1,67 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
from sepolgen import objectmodel as om
|
||||
import policyrep
|
||||
|
||||
# build off of sepolgen perm map implementation
|
||||
|
||||
|
||||
class PermissionMap(object, om.PermMappings):
|
||||
|
||||
"""Permission Map for information flow analysis."""
|
||||
|
||||
def __init__(self, permmapfile="/usr/share/setools/perm_map"):
|
||||
"""
|
||||
Parameter:
|
||||
permmapfile The path to the permission map to load.
|
||||
"""
|
||||
|
||||
om.PermMappings.__init__(self)
|
||||
with open(permmapfile, "r") as fd:
|
||||
self.from_file(fd)
|
||||
|
||||
def rule_weight(self, rule):
|
||||
"""
|
||||
Get the type enforcement rule's information flow read and write weights.
|
||||
|
||||
Parameter:
|
||||
rule A type enforcement rule.
|
||||
|
||||
Return: Tuple(read_weight, write_weight)
|
||||
read_weight The type enforcement rule's read weight.
|
||||
write_weight The type enforcement rule's write weight.
|
||||
"""
|
||||
|
||||
write_weight = 0
|
||||
read_weight = 0
|
||||
|
||||
# iterate over the permissions and determine the
|
||||
# weight of the rule in each direction. The result
|
||||
# is the largest-weight permission in each direction
|
||||
for perm in rule.perms:
|
||||
mapping = self.get(str(rule.tclass), perm)
|
||||
|
||||
if mapping.dir & om.FLOW_READ:
|
||||
read_weight = max(read_weight, mapping.weight)
|
||||
|
||||
if mapping.dir & om.FLOW_WRITE:
|
||||
write_weight = max(write_weight, mapping.weight)
|
||||
|
||||
return (read_weight, write_weight)
|
51
libapol/polcapquery.py
Normal file
51
libapol/polcapquery.py
Normal file
@ -0,0 +1,51 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import re
|
||||
|
||||
import compquery
|
||||
|
||||
|
||||
class PolCapQuery(compquery.ComponentQuery):
|
||||
|
||||
"""Query SELinux policy capabilities"""
|
||||
|
||||
def __init__(self, policy,
|
||||
name="", name_regex=False):
|
||||
"""
|
||||
Parameters:
|
||||
name The name of the policy capability to match.
|
||||
name_regex If true, regular expression matching will
|
||||
be used for matching the name.
|
||||
"""
|
||||
|
||||
self.policy = policy
|
||||
self.set_name(name, regex=name_regex)
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching policy capabilities."""
|
||||
|
||||
for cap in self.policy.polcaps():
|
||||
if self.name and not self._match_regex(
|
||||
cap,
|
||||
self.name,
|
||||
self.name_regex,
|
||||
self.name_cmp):
|
||||
continue
|
||||
|
||||
yield cap
|
208
libapol/policyrep/__init__.py
Normal file
208
libapol/policyrep/__init__.py
Normal file
@ -0,0 +1,208 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Create a Python representation of the policy.
|
||||
# The idea is that this is module provides convenient
|
||||
# abstractions and methods for accessing the policy
|
||||
# structures.
|
||||
|
||||
import setools.qpol as qpol
|
||||
|
||||
# The libqpol SWIG class is not quite natural for
|
||||
# Python, since void* are passed around from the
|
||||
# generic C iterator implementation in libqpol
|
||||
# (note the _from_void calls). Additionally,
|
||||
# the policy is repeatedly referenced in the
|
||||
# function calls, which makes sense for C code
|
||||
# but not for python code, so each object keeps
|
||||
# a reference to the policy for internal use.
|
||||
# This also makes sense since an object would only
|
||||
# be valid for the policy it comes from.
|
||||
|
||||
# Components
|
||||
import objclass
|
||||
import typeattr
|
||||
import boolcond
|
||||
import role
|
||||
import user
|
||||
import mls
|
||||
import polcap
|
||||
|
||||
# Rules
|
||||
import terule
|
||||
import rbacrule
|
||||
import mlsrule
|
||||
|
||||
# Constraints
|
||||
import constraint
|
||||
import mlsconstraint
|
||||
|
||||
# In-policy Labeling
|
||||
import initsid
|
||||
import fscontext
|
||||
import netcontext
|
||||
|
||||
|
||||
class SELinuxPolicy(object):
|
||||
|
||||
"""The complete SELinux policy."""
|
||||
|
||||
def __init__(self, policyfile):
|
||||
"""
|
||||
Parameter:
|
||||
policyfile str Path to a policy to open.
|
||||
"""
|
||||
|
||||
self.policy = qpol.qpol_policy_t(policyfile, 0)
|
||||
|
||||
# libqpol's SWIG wrapper doesn't throw exceptions, so we don't
|
||||
# know what kind of error there was when opening the policy
|
||||
if not self.policy.this:
|
||||
raise RuntimeError(
|
||||
"Error opening policy file \"{0}\"".format(policyfile))
|
||||
|
||||
#
|
||||
# Policy components generators
|
||||
#
|
||||
def types(self):
|
||||
"""Generator which yields all types."""
|
||||
|
||||
# libqpol unfortunately iterates over attributes and aliases
|
||||
qiter = self.policy.get_type_iter()
|
||||
while not qiter.end():
|
||||
t = typeattr.TypeAttr(
|
||||
self.policy, qpol.qpol_type_from_void(qiter.get_item()))
|
||||
if not t.isattr and not t.isalias:
|
||||
yield t
|
||||
qiter.next()
|
||||
|
||||
def bools(self):
|
||||
"""Generator which yields all Booleans."""
|
||||
|
||||
qiter = self.policy.get_bool_iter()
|
||||
while not qiter.end():
|
||||
yield boolcond.Boolean(self.policy, qpol.qpol_bool_from_void(qiter.get_item()))
|
||||
qiter.next()
|
||||
|
||||
def polcaps(self):
|
||||
"""Generator which yields all policy capabilities."""
|
||||
|
||||
qiter = self.policy.get_polcap_iter()
|
||||
while not qiter.end():
|
||||
yield polcap.PolicyCapability(self.policy, qpol.qpol_polcap_from_void(qiter.get_item()))
|
||||
qiter.next()
|
||||
|
||||
#
|
||||
# Policy rules generators
|
||||
#
|
||||
def terules(self):
|
||||
"""Generator which yields all type enforcement rules."""
|
||||
|
||||
av_ruletype = qpol.QPOL_RULE_ALLOW | qpol.QPOL_RULE_AUDITALLOW | qpol.QPOL_RULE_DONTAUDIT
|
||||
te_ruletype = qpol.QPOL_RULE_TYPE_TRANS | qpol.QPOL_RULE_TYPE_CHANGE | qpol.QPOL_RULE_TYPE_MEMBER
|
||||
|
||||
qiter = self.policy.get_avrule_iter(av_ruletype)
|
||||
while not qiter.end():
|
||||
yield terule.TERule(self.policy, qpol.qpol_avrule_from_void(qiter.get_item()))
|
||||
qiter.next()
|
||||
|
||||
qiter = self.policy.get_terule_iter(te_ruletype)
|
||||
while not qiter.end():
|
||||
yield terule.TERule(self.policy, qpol.qpol_terule_from_void(qiter.get_item()))
|
||||
qiter.next()
|
||||
|
||||
qiter = self.policy.get_filename_trans_iter()
|
||||
while not qiter.end():
|
||||
yield terule.TERule(self.policy, qpol.qpol_filename_trans_from_void(qiter.get_item()))
|
||||
qiter.next()
|
||||
|
||||
def rbacrules(self):
|
||||
"""Generator which yields all RBAC rules."""
|
||||
|
||||
qiter = self.policy.get_role_allow_iter()
|
||||
while not qiter.end():
|
||||
yield rbacrule.RBACRule(self.policy, qpol.qpol_role_allow_from_void(qiter.get_item()))
|
||||
qiter.next()
|
||||
|
||||
qiter = self.policy.get_role_trans_iter()
|
||||
while not qiter.end():
|
||||
yield rbacrule.RBACRule(self.policy, qpol.qpol_role_trans_from_void(qiter.get_item()))
|
||||
qiter.next()
|
||||
|
||||
def mlsrules(self):
|
||||
"""Generator which yields all MLS rules."""
|
||||
|
||||
qiter = self.policy.get_range_trans_iter()
|
||||
while not qiter.end():
|
||||
yield mlsrule.MLSRule(self.policy, qpol.qpol_range_trans_from_void(qiter.get_item()))
|
||||
qiter.next()
|
||||
|
||||
#
|
||||
# Constraints generators
|
||||
#
|
||||
|
||||
#
|
||||
# In-policy Labeling statement generators
|
||||
#
|
||||
def initialsids(self):
|
||||
"""Generator which yields all initial SID statements."""
|
||||
|
||||
qiter = self.policy.get_isid_iter()
|
||||
while not qiter.end():
|
||||
yield initsid.InitialSID(self.policy, qpol.qpol_isid_from_void(qiter.get_item()))
|
||||
qiter.next()
|
||||
|
||||
def fs_uses(self):
|
||||
"""Generator which yields all fs_use_* statements."""
|
||||
|
||||
qiter = self.policy.get_fs_use_iter()
|
||||
while not qiter.end():
|
||||
yield fscontext.FSUse(self.policy, qpol.qpol_fs_use_from_void(qiter.get_item()))
|
||||
qiter.next()
|
||||
|
||||
def genfscons(self):
|
||||
"""Generator which yields all genfscon statements."""
|
||||
|
||||
qiter = self.policy.get_genfscon_iter()
|
||||
while not qiter.end():
|
||||
yield fscontext.Genfscon(self.policy, qpol.qpol_genfscon_from_void(qiter.get_item()))
|
||||
qiter.next()
|
||||
|
||||
def netifcons(self):
|
||||
"""Generator which yields all netifcon statements."""
|
||||
|
||||
qiter = self.policy.get_netifcon_iter()
|
||||
while not qiter.end():
|
||||
yield netcontext.Netifcon(self.policy, qpol.qpol_netifcon_from_void(qiter.get_item()))
|
||||
qiter.next()
|
||||
|
||||
def nodecons(self):
|
||||
"""Generator which yields all nodecon statements."""
|
||||
|
||||
qiter = self.policy.get_nodecon_iter()
|
||||
while not qiter.end():
|
||||
yield netcontext.Nodecon(self.policy, qpol.qpol_nodecon_from_void(qiter.get_item()))
|
||||
qiter.next()
|
||||
|
||||
def portcons(self):
|
||||
"""Generator which yields all portcon statements."""
|
||||
|
||||
qiter = self.policy.get_portcon_iter()
|
||||
while not qiter.end():
|
||||
yield netcontext.Portcon(self.policy, qpol.qpol_portcon_from_void(qiter.get_item()))
|
||||
qiter.next()
|
123
libapol/policyrep/boolcond.py
Normal file
123
libapol/policyrep/boolcond.py
Normal file
@ -0,0 +1,123 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import setools.qpol as qpol
|
||||
import string
|
||||
import symbol
|
||||
|
||||
|
||||
class Boolean(symbol.PolicySymbol):
|
||||
|
||||
"""A Boolean."""
|
||||
|
||||
def state(self):
|
||||
"""The default state of the Boolean."""
|
||||
return bool(self.qpol_symbol.get_state(self.policy))
|
||||
|
||||
def statement(self):
|
||||
"""The policy statement."""
|
||||
return "bool {0} {1};".format(self, str(self.state()).lower())
|
||||
|
||||
|
||||
class ConditionalExpr(symbol.PolicySymbol):
|
||||
|
||||
"""A conditional policy expression."""
|
||||
|
||||
_cond_expr_val_to_text = {
|
||||
qpol.QPOL_COND_EXPR_NOT: "!",
|
||||
qpol.QPOL_COND_EXPR_OR: "||",
|
||||
qpol.QPOL_COND_EXPR_AND: "&&",
|
||||
qpol.QPOL_COND_EXPR_XOR: "^",
|
||||
qpol.QPOL_COND_EXPR_EQ: "==",
|
||||
qpol.QPOL_COND_EXPR_NEQ: "!="}
|
||||
|
||||
_cond_expr_val_to_precedence = {
|
||||
qpol.QPOL_COND_EXPR_NOT: 5,
|
||||
qpol.QPOL_COND_EXPR_OR: 1,
|
||||
qpol.QPOL_COND_EXPR_AND: 3,
|
||||
qpol.QPOL_COND_EXPR_XOR: 2,
|
||||
qpol.QPOL_COND_EXPR_EQ: 4,
|
||||
qpol.QPOL_COND_EXPR_NEQ: 4}
|
||||
|
||||
def __str__(self):
|
||||
qpol_iter = self.qpol_symbol.get_expr_node_iter(self.policy)
|
||||
|
||||
# qpol representation is in postfix notation. This code
|
||||
# converts it to infix notation. Parentheses are added
|
||||
# to ensure correct expressions, though they may end up
|
||||
# being overused. Set previous operator at start to the
|
||||
# highest precedence (NOT) so if there is a single binary
|
||||
# operator, no parentheses are output
|
||||
stack = []
|
||||
prev_oper = qpol.QPOL_COND_EXPR_NOT
|
||||
while not qpol_iter.end():
|
||||
expr_node = qpol.qpol_cond_expr_node_from_void(
|
||||
qpol_iter.get_item())
|
||||
expr_node_type = expr_node.get_expr_type(self.policy)
|
||||
|
||||
if expr_node_type == qpol.QPOL_COND_EXPR_BOOL:
|
||||
# append the boolean name
|
||||
nodebool = Boolean(
|
||||
self.policy, expr_node.get_bool(self.policy))
|
||||
stack.append(str(nodebool))
|
||||
elif expr_node_type == qpol.QPOL_COND_EXPR_NOT: # unary operator
|
||||
operand = stack.pop()
|
||||
|
||||
# NOT is the highest precedence, so only need
|
||||
# parentheses if the operand is a subexpression
|
||||
if isinstance(operand, list):
|
||||
subexpr = [
|
||||
self._cond_expr_val_to_text[expr_node_type], "(", operand, ")"]
|
||||
else:
|
||||
subexpr = [
|
||||
self._cond_expr_val_to_text[expr_node_type], operand]
|
||||
|
||||
stack.append(subexpr)
|
||||
prev_oper = expr_node_type
|
||||
else:
|
||||
operand1 = stack.pop()
|
||||
operand2 = stack.pop()
|
||||
|
||||
if self._cond_expr_val_to_precedence[prev_oper] > \
|
||||
self._cond_expr_val_to_precedence[expr_node_type]:
|
||||
# if previous operator is of higher precedence
|
||||
# no parentheses are needed.
|
||||
subexpr = [
|
||||
operand1, self._cond_expr_val_to_text[expr_node_type], operand2]
|
||||
else:
|
||||
subexpr = [
|
||||
"(", operand1, self._cond_expr_val_to_text[expr_node_type], operand2, ")"]
|
||||
|
||||
stack.append(subexpr)
|
||||
prev_oper = expr_node_type
|
||||
|
||||
qpol_iter.next()
|
||||
|
||||
return self.__unwind_subexpression(stack)
|
||||
|
||||
def __unwind_subexpression(self, expr):
|
||||
ret = []
|
||||
|
||||
# do a string.join on sublists (subexpressions)
|
||||
for i in expr:
|
||||
if isinstance(i, list):
|
||||
ret.append(self.__unwind_subexpression(i))
|
||||
else:
|
||||
ret.append(i)
|
||||
|
||||
return string.join(ret)
|
32
libapol/policyrep/constraint.py
Normal file
32
libapol/policyrep/constraint.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import symbol
|
||||
import setools.qpol as qpol
|
||||
|
||||
|
||||
class Constraint(symbol.PolicySymbol):
|
||||
|
||||
"""A constraint rule."""
|
||||
pass
|
||||
|
||||
|
||||
class ValidateTrans(symbol.PolicySymbol):
|
||||
|
||||
"""A validate transition rule."""
|
||||
pass
|
60
libapol/policyrep/context.py
Normal file
60
libapol/policyrep/context.py
Normal file
@ -0,0 +1,60 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import setools.qpol as qpol
|
||||
|
||||
import symbol
|
||||
import user
|
||||
import role
|
||||
import typeattr
|
||||
import mls
|
||||
|
||||
|
||||
class Context(symbol.PolicySymbol):
|
||||
|
||||
"""A SELinux security context/security attribute."""
|
||||
|
||||
def __str__(self):
|
||||
ctx = "{0.user}:{0.role}:{0.type_}".format(self)
|
||||
|
||||
# TODO qpol doesn't currently export a way to check if
|
||||
# MLS is enabled. It also will segfault if we try to get
|
||||
# a range on a policy w/o MLS
|
||||
# if mls:
|
||||
# ctx += ":{0}".format(self.mls)
|
||||
return ctx
|
||||
|
||||
@property
|
||||
def user(self):
|
||||
"""The user portion of the context."""
|
||||
return user.User(self.policy, self.qpol_symbol.get_user(self.policy))
|
||||
|
||||
@property
|
||||
def role(self):
|
||||
"""The role portion of the context."""
|
||||
return role.Role(self.policy, self.qpol_symbol.get_role(self.policy))
|
||||
|
||||
@property
|
||||
def type_(self):
|
||||
"""The type portion of the context."""
|
||||
return typeattr.TypeAttr(self.policy, self.qpol_symbol.get_type(self.policy))
|
||||
|
||||
@property
|
||||
def mls(self):
|
||||
"""The MLS portion (range) of the context."""
|
||||
return mls.MLSRange(self.policy, self.qpol_symbol.get_range(self.policy))
|
76
libapol/policyrep/fscontext.py
Normal file
76
libapol/policyrep/fscontext.py
Normal file
@ -0,0 +1,76 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from setools import qpol
|
||||
|
||||
import symbol
|
||||
import context
|
||||
|
||||
|
||||
class FSContext(symbol.PolicySymbol):
|
||||
|
||||
"""Abstract base class for in-policy labeling rules."""
|
||||
|
||||
def __str__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def fs(self):
|
||||
"""The filesystem type for this statement."""
|
||||
return self.qpol_symbol.get_name(self.policy)
|
||||
|
||||
@property
|
||||
def context(self):
|
||||
"""The context for this statement."""
|
||||
return context.Context(self.policy, self.qpol_symbol.get_context(self.policy))
|
||||
|
||||
def statement(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
class Genfscon(FSContext):
|
||||
|
||||
"""A genfscon statement."""
|
||||
|
||||
def __str__(self):
|
||||
return "genfscon {0.fs} {0.path} {0.context}".format(self)
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
"""The path for this genfscon statement."""
|
||||
return self.qpol_symbol.get_path(self.policy)
|
||||
|
||||
|
||||
class FSUse(FSContext):
|
||||
|
||||
"""A fs_use_* statement."""
|
||||
|
||||
# there are more rule types, but modern SELinux
|
||||
# only supports these three.
|
||||
_ruletype_to_text = {
|
||||
qpol.QPOL_FS_USE_XATTR: 'fs_use_xattr',
|
||||
qpol.QPOL_FS_USE_TRANS: 'fs_use_trans',
|
||||
qpol.QPOL_FS_USE_TASK: 'fs_use_task'}
|
||||
|
||||
def __str__(self):
|
||||
return "{0.ruletype} {0.fs} {0.context};".format(self)
|
||||
|
||||
@property
|
||||
def ruletype(self):
|
||||
"""The rule type for this fs_use_* statement."""
|
||||
return self._ruletype_to_text[self.qpol_symbol.get_behavior(self.policy)]
|
35
libapol/policyrep/initsid.py
Normal file
35
libapol/policyrep/initsid.py
Normal file
@ -0,0 +1,35 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import setools.qpol as qpol
|
||||
|
||||
import symbol
|
||||
import context
|
||||
|
||||
|
||||
class InitialSID(symbol.PolicySymbol):
|
||||
|
||||
"""An initial SID statement."""
|
||||
|
||||
@property
|
||||
def context(self):
|
||||
"""The context for this initial SID."""
|
||||
return context.Context(self.policy, self.qpol_symbol.get_context(self.policy))
|
||||
|
||||
def statement(self):
|
||||
return "sid {0} {1}".format(self, self.context)
|
86
libapol/policyrep/mls.py
Normal file
86
libapol/policyrep/mls.py
Normal file
@ -0,0 +1,86 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import setools.qpol as qpol
|
||||
import symbol
|
||||
|
||||
|
||||
class MLSCategory(symbol.PolicySymbol):
|
||||
|
||||
"""An MLS category."""
|
||||
|
||||
@property
|
||||
def isalias(self):
|
||||
"""(T/F) this is an alias."""
|
||||
return self.qpol_symbol.get_isalias(self.policy)
|
||||
|
||||
def aliases(self):
|
||||
"""Generator that yields all aliases for this category."""
|
||||
|
||||
aiter = self.qpol_symbol.get_alias_iter(self.policy)
|
||||
while not aiter.end():
|
||||
yield qpol.to_str(aiter.get_item())
|
||||
aiter.next()
|
||||
|
||||
# libqpol does not expose sensitivities as an individual component
|
||||
class MLSSensitivity(symbol.PolicySymbol):
|
||||
pass
|
||||
|
||||
|
||||
class MLSLevel(symbol.PolicySymbol):
|
||||
|
||||
"""An MLS level."""
|
||||
|
||||
def __str__(self):
|
||||
# TODO: add compact category notation
|
||||
return self.qpol_symbol.get_sens_name(self.policy)
|
||||
|
||||
def categories(self):
|
||||
"""
|
||||
Generator that yields all individual categories for this level.
|
||||
All categories are yielded, not a compact notation such as
|
||||
c0.c255
|
||||
"""
|
||||
|
||||
citer = self.qpol_symbol.get_cat_iter(self.policy)
|
||||
while not citer.end():
|
||||
yield MLSCategory(self.policy, qpol.qpol_cat_from_void(citer.get_item()))
|
||||
citer.next()
|
||||
|
||||
|
||||
class MLSRange(symbol.PolicySymbol):
|
||||
|
||||
"""An MLS range"""
|
||||
|
||||
def __str__(self):
|
||||
high = self.high
|
||||
low = self.low
|
||||
if high == low:
|
||||
return str(low)
|
||||
|
||||
return "{0} - {1}".format(low, high)
|
||||
|
||||
@property
|
||||
def high(self):
|
||||
"""The high end/clearance level of this range."""
|
||||
return MLSLevel(self.policy, self.qpol_symbol.get_high_level(self.policy))
|
||||
|
||||
@property
|
||||
def low(self):
|
||||
"""The low end/current level of this range."""
|
||||
return MLSLevel(self.policy, self.qpol_symbol.get_low_level(self.policy))
|
32
libapol/policyrep/mlsconstraint.py
Normal file
32
libapol/policyrep/mlsconstraint.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import constraint
|
||||
import setools.qpol as qpol
|
||||
|
||||
|
||||
class MLSConstraint(constraint.Constraint):
|
||||
|
||||
"""An MLS constraint rule."""
|
||||
pass
|
||||
|
||||
|
||||
class MLSValidateTrans(constraint.ValidateTrans):
|
||||
|
||||
"""An MLS validate transition rule."""
|
||||
pass
|
59
libapol/policyrep/mlsrule.py
Normal file
59
libapol/policyrep/mlsrule.py
Normal file
@ -0,0 +1,59 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import setools.qpol as qpol
|
||||
|
||||
import rule
|
||||
import typeattr
|
||||
import mls
|
||||
import objclass
|
||||
import boolcond
|
||||
|
||||
|
||||
class MLSRule(rule.PolicyRule):
|
||||
|
||||
"""An MLS rule."""
|
||||
|
||||
def __str__(self):
|
||||
# TODO: If we ever get more MLS rules, fix this format.
|
||||
return "range_transition {0.source} {0.target}:{0.tclass} {0.default};".format(self)
|
||||
|
||||
@property
|
||||
def ruletype(self):
|
||||
"""The rule type."""
|
||||
return "range_transition"
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
"""The rule's source type/attribute."""
|
||||
return typeattr.TypeAttr(self.policy, self.qpol_symbol.get_source_type(self.policy))
|
||||
|
||||
@property
|
||||
def target(self):
|
||||
"""The rule's target type/attribute."""
|
||||
return typeattr.TypeAttr(self.policy, self.qpol_symbol.get_target_type(self.policy))
|
||||
|
||||
@property
|
||||
def tclass(self):
|
||||
"""The rule's object class."""
|
||||
return objclass.ObjClass(self.policy, self.qpol_symbol.get_target_class(self.policy))
|
||||
|
||||
@property
|
||||
def default(self):
|
||||
"""The rule's default range."""
|
||||
return mls.MLSRange(self.policy, self.qpol_symbol.get_range(self.policy))
|
138
libapol/policyrep/netcontext.py
Normal file
138
libapol/policyrep/netcontext.py
Normal file
@ -0,0 +1,138 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import socket
|
||||
|
||||
from setools import qpol
|
||||
|
||||
import symbol
|
||||
import context
|
||||
|
||||
|
||||
class NetContext(symbol.PolicySymbol):
|
||||
|
||||
"""Abstract base class for in-policy network labeling rules."""
|
||||
|
||||
def __str__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def context(self):
|
||||
"""The context for this statement."""
|
||||
return context.Context(self.policy, self.qpol_symbol.get_context(self.policy))
|
||||
|
||||
def statement(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
class Netifcon(NetContext):
|
||||
|
||||
"""A netifcon statement."""
|
||||
|
||||
def __str__(self):
|
||||
return "netifcon {0.netif} {0.context} {0.packet}".format(self)
|
||||
|
||||
@property
|
||||
def netif(self):
|
||||
"""The network interface name."""
|
||||
return self.qpol_symbol.get_name(self.policy)
|
||||
|
||||
@property
|
||||
def context(self):
|
||||
"""The context for the interface."""
|
||||
return context.Context(self.policy, self.qpol_symbol.get_if_con(self.policy))
|
||||
|
||||
@property
|
||||
def packet(self):
|
||||
"""The context for the packets."""
|
||||
return context.Context(self.policy, self.qpol_symbol.get_msg_con(self.policy))
|
||||
|
||||
|
||||
class Nodecon(NetContext):
|
||||
|
||||
"""A nodecon statement."""
|
||||
|
||||
def __str__(self):
|
||||
# TODO: not sure the SWIG is functional on these addresses.
|
||||
#ver = self.ip_version()
|
||||
#subnet = socket.inet_ntop(ver, self.subnet)
|
||||
#netmask = socket.inet_ntop(ver, self.netmask)
|
||||
#return "nodecon {0} {1} {2}".format(subnet, netmask, self.context)
|
||||
return "nodecon - - {0.context}".format(self)
|
||||
|
||||
@property
|
||||
def ip_version(self):
|
||||
"""
|
||||
The IP version for the nodecon (socket.AF_INET or
|
||||
socket.AF_INET6).
|
||||
"""
|
||||
if self.qpol_symbol.get_protocol(self.policy) == qpol.QPOL_IPV6:
|
||||
return socket.AF_INET6
|
||||
|
||||
return socket.AF_INET
|
||||
|
||||
@property
|
||||
def subnet(self):
|
||||
# TODO: determine if this packed byte representation
|
||||
# should be used here and below, or if it should be
|
||||
# converted into the human-readable string version.
|
||||
# IPv(4|6)Network looks good for this (with mask below)
|
||||
# but it is limited to Python >= 3.3
|
||||
return self.qpol_symbol.get_addr(self.policy)
|
||||
|
||||
@property
|
||||
def netmask(self):
|
||||
return self.qpol_symbol.get_mask(self.policy)
|
||||
|
||||
|
||||
class Portcon(NetContext):
|
||||
|
||||
"""A portcon statement."""
|
||||
|
||||
_proto_to_text = {socket.IPPROTO_TCP: 'tcp',
|
||||
socket.IPPROTO_UDP: 'udp'}
|
||||
|
||||
def __str__(self):
|
||||
low, high = self.ports
|
||||
proto = self._proto_to_text[self.protocol]
|
||||
|
||||
if low == high:
|
||||
return "portcon {0} {1} {2}".format(proto, low, self.context)
|
||||
else:
|
||||
return "portcon {0} {1}-{2} {3}".format(proto, low, high, self.context)
|
||||
|
||||
@property
|
||||
def protocol(self):
|
||||
"""
|
||||
The protocol number for the portcon (socket.IPPROTO_TCP
|
||||
or socket.IPPROTO_UDP).
|
||||
"""
|
||||
return self.qpol_symbol.get_protocol(self.policy)
|
||||
|
||||
@property
|
||||
def ports(self):
|
||||
"""
|
||||
The port range for this portcon.
|
||||
|
||||
Return: Tuple(low, high)
|
||||
low The low port of the range.
|
||||
high The high port of the range.
|
||||
"""
|
||||
low = self.qpol_symbol.get_low_port(self.policy)
|
||||
high = self.qpol_symbol.get_high_port(self.policy)
|
||||
return (low, high)
|
32
libapol/policyrep/objclass.py
Normal file
32
libapol/policyrep/objclass.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import symbol
|
||||
import setools.qpol as qpol
|
||||
|
||||
|
||||
class Common(symbol.PolicySymbol):
|
||||
|
||||
"""A common permission set."""
|
||||
pass
|
||||
|
||||
|
||||
class ObjClass(symbol.PolicySymbol):
|
||||
|
||||
"""An object class."""
|
||||
pass
|
29
libapol/policyrep/polcap.py
Normal file
29
libapol/policyrep/polcap.py
Normal file
@ -0,0 +1,29 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import setools.qpol as qpol
|
||||
|
||||
import symbol
|
||||
|
||||
|
||||
class PolicyCapability(symbol.PolicySymbol):
|
||||
|
||||
"""A policy capability."""
|
||||
|
||||
def statement(self):
|
||||
return "policycap {0};".format(self)
|
83
libapol/policyrep/rbacrule.py
Normal file
83
libapol/policyrep/rbacrule.py
Normal file
@ -0,0 +1,83 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import setools.qpol as qpol
|
||||
|
||||
import rule
|
||||
import role
|
||||
import typeattr
|
||||
import objclass
|
||||
|
||||
|
||||
class RBACRule(rule.PolicyRule):
|
||||
|
||||
"""An RBAC rule."""
|
||||
|
||||
def __str__(self):
|
||||
try:
|
||||
# qpol doesnt currently support role transitons
|
||||
# with an object class specified (v26+)
|
||||
return "role_transition {0.source} {0.target} {0.default};".format(self)
|
||||
except rule.InvalidRuleUse:
|
||||
return "allow {0.source} {0.target};".format(self)
|
||||
|
||||
@property
|
||||
def ruletype(self):
|
||||
"""The rule type."""
|
||||
if isinstance(self.qpol_symbol, qpol.qpol_role_allow_t):
|
||||
return "allow"
|
||||
else:
|
||||
return "role_transition"
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
"""The rule's source role."""
|
||||
return role.Role(self.policy, self.qpol_symbol.get_source_role(self.policy))
|
||||
|
||||
@property
|
||||
def target(self):
|
||||
"""
|
||||
The rule's target role (role allow) or target type/attribute
|
||||
(role_transition).
|
||||
"""
|
||||
try:
|
||||
return role.Role(self.policy, self.qpol_symbol.get_target_role(self.policy))
|
||||
except AttributeError:
|
||||
return typeattr.TypeAttr(self.policy, self.qpol_symbol.get_target_type(self.policy))
|
||||
|
||||
@property
|
||||
def tclass(self):
|
||||
"""The rule's object class."""
|
||||
# qpol doesnt currently support role transitions
|
||||
# with an object class specified (v26+)
|
||||
raise NotImplementedError
|
||||
|
||||
try:
|
||||
return objclass.ObjClass(self.policy, self.qpol_symbol.get_target_class(self.policy))
|
||||
except AttributeError:
|
||||
raise rule.InvalidRuleUse(
|
||||
"Role allow rules do not have an object class.")
|
||||
|
||||
@property
|
||||
def default(self):
|
||||
"""The rule's default role."""
|
||||
try:
|
||||
return role.Role(self.policy, self.qpol_symbol.get_default_role(self.policy))
|
||||
except AttributeError:
|
||||
raise rule.InvalidRuleUse(
|
||||
"Role allow rules do not have a default role.")
|
39
libapol/policyrep/role.py
Normal file
39
libapol/policyrep/role.py
Normal file
@ -0,0 +1,39 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import setools.qpol as qpol
|
||||
import symbol
|
||||
|
||||
|
||||
class Role(symbol.PolicySymbol):
|
||||
|
||||
"""A role."""
|
||||
|
||||
def expand(self):
|
||||
"""
|
||||
Generator that expands this attribute into its member roles.
|
||||
If this is a role, the role itself will be yielded.
|
||||
"""
|
||||
# Role attributes are already expanded in the binary policy
|
||||
yield self
|
||||
|
||||
@property
|
||||
def isattr(self):
|
||||
"""(T/F) this is an attribute."""
|
||||
# Role attributes are already expanded in the binary policy
|
||||
return False
|
97
libapol/policyrep/rule.py
Normal file
97
libapol/policyrep/rule.py
Normal file
@ -0,0 +1,97 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import setools.qpol as qpol
|
||||
|
||||
import symbol
|
||||
|
||||
|
||||
class InvalidRuleUse(Exception):
|
||||
|
||||
"""
|
||||
Exception when getting incorrect parameters for a rule. For
|
||||
example, trying to get the permissions of a rule that has no
|
||||
permissions.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class RuleNotConditional(Exception):
|
||||
|
||||
"""
|
||||
Exception when getting the conditional expression for rules
|
||||
that are unconditional (not conditional).
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class PolicyRule(symbol.PolicySymbol):
|
||||
|
||||
"""This is base class for policy rules."""
|
||||
|
||||
def __str__(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def ruletype(self):
|
||||
"""
|
||||
The rule type for the rule. This should be overridden by
|
||||
subclasses.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
"""
|
||||
The source for the rule. This should be overridden by
|
||||
subclasses.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def target(self):
|
||||
"""
|
||||
The target for the rule. This should be overridden by
|
||||
subclasses.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def tclass(self):
|
||||
"""
|
||||
The object class for the rule. This should be overridden by
|
||||
subclasses.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def default(self):
|
||||
"""
|
||||
The default for the rule. This should be overridden by
|
||||
subclasses.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def conditional(self):
|
||||
"""The conditional expression for this rule."""
|
||||
# Most rules cannot be conditional.
|
||||
raise RuleNotConditional
|
||||
|
||||
def statement(self):
|
||||
return str(self)
|
66
libapol/policyrep/symbol.py
Normal file
66
libapol/policyrep/symbol.py
Normal file
@ -0,0 +1,66 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import setools.qpol as qpol
|
||||
|
||||
|
||||
class PolicySymbol(object):
|
||||
|
||||
"""This is a base class for all policy objects."""
|
||||
|
||||
def __init__(self, policy, qpol_symbol):
|
||||
"""
|
||||
Parameters:
|
||||
policy The low-level policy object.
|
||||
qpol_symbol The low-level policy symbol object.
|
||||
"""
|
||||
|
||||
self.policy = policy
|
||||
self.qpol_symbol = qpol_symbol
|
||||
|
||||
def __str__(self):
|
||||
return self.qpol_symbol.get_name(self.policy)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.qpol_symbol)
|
||||
|
||||
def __eq__(self, other):
|
||||
# this assumes the policy for both objects is the same.
|
||||
# if this is not the case, the subclass will need to
|
||||
# handle the comparison as there is insufficient
|
||||
# information here.
|
||||
|
||||
if isinstance(other, str):
|
||||
return (str(self) == other)
|
||||
elif isinstance(other, self.__class__):
|
||||
return (self.qpol_symbol == other.qpol_symbol)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
__ne__ = lambda self, other: not self == other
|
||||
|
||||
def __lt__(self, other):
|
||||
"""Comparison used by Python sorting functions."""
|
||||
return (str(self) < str(other))
|
||||
|
||||
def statement(self):
|
||||
"""
|
||||
A rendering of the policy statement. This should be
|
||||
overridden by subclasses.
|
||||
"""
|
||||
raise NotImplementedError
|
161
libapol/policyrep/terule.py
Normal file
161
libapol/policyrep/terule.py
Normal file
@ -0,0 +1,161 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import string
|
||||
|
||||
import setools.qpol as qpol
|
||||
|
||||
import rule
|
||||
import typeattr
|
||||
import objclass
|
||||
import boolcond
|
||||
|
||||
|
||||
class TERuleNoFilename(Exception):
|
||||
|
||||
"""
|
||||
Exception when getting the file name of a
|
||||
type_transition rule that has no file name.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class TERule(rule.PolicyRule):
|
||||
|
||||
"""A type enforcement rule."""
|
||||
|
||||
# This class abstracts away the policydb implementation detail
|
||||
# that 'AV' rules and 'TE' rules are in separate tables
|
||||
|
||||
_teruletype_val_to_text = {
|
||||
qpol.QPOL_RULE_ALLOW: 'allow',
|
||||
qpol.QPOL_RULE_NEVERALLOW: 'neverallow',
|
||||
qpol.QPOL_RULE_DONTAUDIT: 'dontaudit',
|
||||
qpol.QPOL_RULE_AUDITALLOW: 'auditallow',
|
||||
qpol.QPOL_RULE_TYPE_TRANS: 'type_transition',
|
||||
qpol.QPOL_RULE_TYPE_CHANGE: 'type_change',
|
||||
qpol.QPOL_RULE_TYPE_MEMBER: 'type_member'}
|
||||
|
||||
def __str__(self):
|
||||
rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} ".format(
|
||||
self)
|
||||
|
||||
if isinstance(self.qpol_symbol, qpol.qpol_avrule_t):
|
||||
perms = self.perms
|
||||
|
||||
if len(perms) > 1:
|
||||
rule_string += "{{ {0} }};".format(string.join(perms))
|
||||
else:
|
||||
# convert to list since sets cannot be indexed
|
||||
rule_string += "{0};".format(list(perms)[0])
|
||||
else:
|
||||
rule_string += str(self.default)
|
||||
|
||||
try:
|
||||
rule_string += " \"{0}\"".format(self.filename)
|
||||
except TERuleNoFilename:
|
||||
pass
|
||||
|
||||
rule_string += ";"
|
||||
|
||||
try:
|
||||
cond = self.conditional
|
||||
except rule.RuleNotConditional:
|
||||
pass
|
||||
else:
|
||||
rule_string += " [ {0} ]".format(cond)
|
||||
|
||||
return rule_string
|
||||
|
||||
@property
|
||||
def ruletype(self):
|
||||
"""The rule type."""
|
||||
try:
|
||||
return self._teruletype_val_to_text[self.qpol_symbol.get_rule_type(self.policy)]
|
||||
except AttributeError:
|
||||
# qpol does not have a rule type function for name filetrans rules
|
||||
return "type_transition"
|
||||
|
||||
@property
|
||||
def source(self):
|
||||
"""The rule's source type/attribute."""
|
||||
return typeattr.TypeAttr(self.policy, self.qpol_symbol.get_source_type(self.policy))
|
||||
|
||||
@property
|
||||
def target(self):
|
||||
"""The rule's target type/attribute."""
|
||||
return typeattr.TypeAttr(self.policy, self.qpol_symbol.get_target_type(self.policy))
|
||||
|
||||
@property
|
||||
def tclass(self):
|
||||
"""The rule's object class."""
|
||||
return objclass.ObjClass(self.policy, self.qpol_symbol.get_object_class(self.policy))
|
||||
|
||||
@property
|
||||
def perms(self):
|
||||
"""The rule's permission set."""
|
||||
try:
|
||||
# create permission list
|
||||
iter = self.qpol_symbol.get_perm_iter(self.policy)
|
||||
except AttributeError:
|
||||
raise rule.InvalidRuleUse(
|
||||
"{0} rules do not have a permission set.".format(self.ruletype))
|
||||
|
||||
p = set()
|
||||
|
||||
while not iter.end():
|
||||
p.add(qpol.to_str(iter.get_item()))
|
||||
iter.next()
|
||||
|
||||
return p
|
||||
|
||||
@property
|
||||
def default(self):
|
||||
"""The rule's default type."""
|
||||
try:
|
||||
return typeattr.TypeAttr(self.policy, self.qpol_symbol.get_default_type(self.policy))
|
||||
except AttributeError:
|
||||
raise rule.InvalidRuleUse(
|
||||
"{0} rules do not have a default type.".format(self.ruletype))
|
||||
|
||||
@property
|
||||
def filename(self):
|
||||
"""The type_transition rule's file name."""
|
||||
try:
|
||||
return self.qpol_symbol.get_filename(self.policy)
|
||||
except AttributeError:
|
||||
if self.ruletype == "type_transition":
|
||||
raise TERuleNoFilename
|
||||
else:
|
||||
raise rule.InvalidRuleUse(
|
||||
"{0} rules do not have file names".format(self.ruletype))
|
||||
|
||||
@property
|
||||
def conditional(self):
|
||||
"""The rule's conditional expression."""
|
||||
try:
|
||||
qpol_cond = self.qpol_symbol.get_cond(self.policy)
|
||||
except AttributeError:
|
||||
# name filetrans rules cannot be conditional (so no member
|
||||
# function)
|
||||
raise rule.RuleNotConditional
|
||||
|
||||
if not qpol_cond:
|
||||
raise rule.RuleNotConditional
|
||||
|
||||
return boolcond.ConditionalExpr(self.policy, qpol_cond)
|
96
libapol/policyrep/typeattr.py
Normal file
96
libapol/policyrep/typeattr.py
Normal file
@ -0,0 +1,96 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import string
|
||||
|
||||
import setools.qpol as qpol
|
||||
|
||||
import symbol
|
||||
|
||||
|
||||
class TypeAttr(symbol.PolicySymbol):
|
||||
|
||||
"""A type or attribute."""
|
||||
|
||||
@property
|
||||
def ispermissive(self):
|
||||
"""(T/F) the type is permissive."""
|
||||
return self.qpol_symbol.ispermissive(self.policy)
|
||||
|
||||
@property
|
||||
def isattr(self):
|
||||
"""(T/F) this is an attribute."""
|
||||
return self.qpol_symbol.get_isattr(self.policy)
|
||||
|
||||
@property
|
||||
def isalias(self):
|
||||
"""(T/F) this is an alias."""
|
||||
return self.qpol_symbol.get_isalias(self.policy)
|
||||
|
||||
def expand(self):
|
||||
"""
|
||||
Generator that expands this attribute into its member types.
|
||||
If this is a type, the type itself will be yielded.
|
||||
"""
|
||||
# if this is not an attribute, yield only the type itself
|
||||
if not self.isattr:
|
||||
yield self
|
||||
else:
|
||||
aiter = self.qpol_symbol.get_type_iter(self.policy)
|
||||
while not aiter.end():
|
||||
yield TypeAttr(self.policy, qpol.qpol_type_from_void(aiter.get_item()))
|
||||
aiter.next()
|
||||
|
||||
def attributes(self):
|
||||
"""Generator that yields all attributes for this type."""
|
||||
if self.isattr:
|
||||
raise TypeError(
|
||||
"{0} is an attribute, thus does not have attributes.".format(self))
|
||||
|
||||
aiter = self.qpol_symbol.get_attr_iter(self.policy)
|
||||
while not aiter.end():
|
||||
yield TypeAttr(self.policy, qpol.qpol_type_from_void(aiter.get_item()))
|
||||
aiter.next()
|
||||
|
||||
def aliases(self):
|
||||
"""Generator that yields all aliases for this type."""
|
||||
if self.isattr:
|
||||
raise TypeError(
|
||||
"{0} is an attribute, thus does not have aliases.".format(self))
|
||||
|
||||
aiter = self.qpol_symbol.get_alias_iter(self.policy)
|
||||
while not aiter.end():
|
||||
yield qpol.to_str(aiter.get_item())
|
||||
aiter.next()
|
||||
|
||||
def statement(self):
|
||||
if self.isattr:
|
||||
return "attribute {0};".format(self)
|
||||
else:
|
||||
attrs = list(self.attributes())
|
||||
aliases = list(self.aliases())
|
||||
stmt = "type {0}".format(self)
|
||||
if aliases:
|
||||
if len(aliases) > 1:
|
||||
stmt += " alias {{ {0} }}".format(string.join(aliases))
|
||||
else:
|
||||
stmt += " alias {0}".format(aliases[0])
|
||||
for a in attrs:
|
||||
stmt += ", {0}".format(a)
|
||||
stmt += ";"
|
||||
return stmt
|
26
libapol/policyrep/user.py
Normal file
26
libapol/policyrep/user.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import setools.qpol as qpol
|
||||
import symbol
|
||||
|
||||
|
||||
class User(symbol.PolicySymbol):
|
||||
|
||||
"""A user."""
|
||||
pass
|
106
libapol/query.py
Normal file
106
libapol/query.py
Normal file
@ -0,0 +1,106 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import re
|
||||
|
||||
|
||||
class PolicyQuery(object):
|
||||
|
||||
"""Abstract base class for SELinux policy queries."""
|
||||
|
||||
@staticmethod
|
||||
def _match_regex(obj, criteria, regex, recomp):
|
||||
"""
|
||||
Match the object with optional regular expression.
|
||||
|
||||
Parameters:
|
||||
obj The object to match.
|
||||
criteria The criteria to match.
|
||||
regex If regular expression matching should be used.
|
||||
recomp The compiled regular expression.
|
||||
"""
|
||||
|
||||
if regex:
|
||||
return bool(recomp.search(str(obj)))
|
||||
else:
|
||||
return (obj == criteria)
|
||||
|
||||
@staticmethod
|
||||
def _match_set(obj, criteria, equal):
|
||||
"""
|
||||
Match the object (a set) with optional set equality.
|
||||
|
||||
Parameters:
|
||||
obj The object to match. (a set)
|
||||
criteria The criteria to match. (a set)
|
||||
equal If set equality should be used. Otherwise
|
||||
any set intersection will match.
|
||||
"""
|
||||
|
||||
if equal:
|
||||
return (obj == criteria)
|
||||
else:
|
||||
return bool(obj.intersection(criteria))
|
||||
|
||||
@staticmethod
|
||||
def _match_in_set(obj, criteria, regex, recomp):
|
||||
"""
|
||||
Match if the criteria is in the list, with optional
|
||||
regular expression matching.
|
||||
|
||||
Parameters:
|
||||
obj The object to match.
|
||||
criteria The criteria to match.
|
||||
regex If regular expression matching should be used.
|
||||
recomp The compiled regular expression.
|
||||
"""
|
||||
|
||||
if regex:
|
||||
return bool(filter(recomp.search, (str(m) for m in obj)))
|
||||
else:
|
||||
return (criteria in obj)
|
||||
|
||||
@staticmethod
|
||||
def _match_regex_or_set(obj, criteria, equal, regex, recomp):
|
||||
"""
|
||||
Match the object (a set) with either set comparisons
|
||||
(equality or intersection) or by regex matching of the
|
||||
set members. Regular expression matching will override
|
||||
the set equality option.
|
||||
|
||||
Parameters:
|
||||
obj The object to match. (a set)
|
||||
criteria The criteria to match.
|
||||
equal If set equality should be used. Otherwise
|
||||
any set intersection will match. Ignored
|
||||
if regular expression matching is used.
|
||||
regex If regular expression matching should be used.
|
||||
recomp The compiled regular expression.
|
||||
"""
|
||||
|
||||
if regex:
|
||||
return bool(filter(recomp.search, (str(m) for m in obj)))
|
||||
else:
|
||||
return PolicyQuery._match_set(obj, set(criteria), equal)
|
||||
|
||||
def results(self):
|
||||
"""
|
||||
Generator which returns the matches for the query. This method
|
||||
should be overridden by subclasses.
|
||||
"""
|
||||
raise NotImplementedError
|
127
libapol/rbacrulequery.py
Normal file
127
libapol/rbacrulequery.py
Normal file
@ -0,0 +1,127 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from policyrep.rule import InvalidRuleUse
|
||||
import rulequery
|
||||
|
||||
|
||||
class RBACRuleQuery(rulequery.RuleQuery):
|
||||
|
||||
"""Query the RBAC rules."""
|
||||
|
||||
def __init__(self, policy,
|
||||
ruletype=[],
|
||||
source="", source_regex=False, source_indirect=True,
|
||||
target="", target_regex=False, target_indirect=True,
|
||||
tclass="", tclass_regex=False,
|
||||
default="", default_regex=False):
|
||||
"""
|
||||
Parameters:
|
||||
policy The policy to query.
|
||||
ruletype The rule type(s) to match.
|
||||
source The name of the source role/attribute to match.
|
||||
source_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
source_regex If true, regular expression matching will
|
||||
be used on the source role/attribute.
|
||||
Obeys the source_indirect option.
|
||||
target The name of the target role/attribute to match.
|
||||
target_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
target_regex If true, regular expression matching will
|
||||
be used on the target role/attribute.
|
||||
Obeys target_indirect option.
|
||||
tclass The object class(es) to match.
|
||||
tclass_regex If true, use a regular expression for
|
||||
matching the rule's object class.
|
||||
default The name of the default role to match.
|
||||
default_regex If true, regular expression matching will
|
||||
be used on the default role.
|
||||
"""
|
||||
|
||||
self.policy = policy
|
||||
|
||||
self.set_ruletype(ruletype)
|
||||
self.set_source(source, indirect=source_indirect, regex=source_regex)
|
||||
self.set_target(target, indirect=target_indirect, regex=target_regex)
|
||||
self.set_tclass(tclass, regex=tclass_regex)
|
||||
self.set_default(default, regex=default_regex)
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching RBAC rules."""
|
||||
|
||||
for r in self.policy.rbacrules():
|
||||
#
|
||||
# Matching on rule type
|
||||
#
|
||||
if self.ruletype:
|
||||
if not r.ruletype in self.ruletype:
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on source role
|
||||
#
|
||||
if self.source and not self._match_indirect_regex(
|
||||
r.source,
|
||||
self.source,
|
||||
self.source_indirect,
|
||||
self.source_regex,
|
||||
self.source_cmp):
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on target type (role_transition)/role(allow)
|
||||
#
|
||||
if self.target and not self._match_indirect_regex(
|
||||
r.target,
|
||||
self.target,
|
||||
self.target_indirect,
|
||||
self.target_regex,
|
||||
self.target_cmp):
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on object class
|
||||
#
|
||||
if self.tclass:
|
||||
try:
|
||||
if not self._match_object_class(
|
||||
r.tclass,
|
||||
self.tclass,
|
||||
self.tclass_regex,
|
||||
self.tclass_cmp):
|
||||
continue
|
||||
except InvalidRuleUse:
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on default role
|
||||
#
|
||||
if self.default:
|
||||
try:
|
||||
if not self._match_regex(
|
||||
r.default,
|
||||
self.default,
|
||||
self.default_regex,
|
||||
self.default_cmp):
|
||||
continue
|
||||
except InvalidRuleUse:
|
||||
continue
|
||||
|
||||
# if we get here, we have matched all available criteria
|
||||
yield r
|
207
libapol/rulequery.py
Normal file
207
libapol/rulequery.py
Normal file
@ -0,0 +1,207 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import re
|
||||
|
||||
from query import PolicyQuery
|
||||
|
||||
|
||||
class RuleQuery(PolicyQuery):
|
||||
|
||||
"""Abstract base class for rule queries."""
|
||||
|
||||
@staticmethod
|
||||
def _match_indirect_regex(obj, criteria, indirect, regex, recomp):
|
||||
"""
|
||||
Match the object with optional regular expression and indirection.
|
||||
|
||||
Parameters:
|
||||
obj The object to match.
|
||||
criteria The criteria to match.
|
||||
regex If regular expression matching should be used.
|
||||
indirect If object indirection should be used, e.g.
|
||||
expanding an attribute.
|
||||
recomp The compiled regular expression.
|
||||
"""
|
||||
|
||||
if indirect:
|
||||
return PolicyQuery._match_in_set(
|
||||
(str(o) for o in obj.expand()),
|
||||
criteria,
|
||||
regex,
|
||||
recomp)
|
||||
else:
|
||||
return PolicyQuery._match_regex(
|
||||
obj,
|
||||
criteria,
|
||||
regex,
|
||||
recomp)
|
||||
|
||||
@staticmethod
|
||||
def _match_object_class(obj, criteria, regex, recomp):
|
||||
"""
|
||||
Match the object class with optional regular expression.
|
||||
|
||||
Parameters:
|
||||
obj The object to match.
|
||||
criteria The criteria to match.
|
||||
regex If regular expression matching should be used.
|
||||
recomp The compiled regular expression.
|
||||
"""
|
||||
|
||||
if isinstance(criteria, set):
|
||||
return (str(obj) in criteria)
|
||||
elif regex:
|
||||
return bool(recomp.search(str(obj)))
|
||||
else:
|
||||
return (obj == criteria)
|
||||
|
||||
def set_ruletype(self, ruletype):
|
||||
"""
|
||||
Set the rule types for the rule query.
|
||||
|
||||
Parameter:
|
||||
ruletype The rule types to match.
|
||||
"""
|
||||
|
||||
self.ruletype = ruletype
|
||||
|
||||
def set_source(self, source, **opts):
|
||||
"""
|
||||
Set the criteria for the rule's source.
|
||||
|
||||
Parameter:
|
||||
source Name to match the rule's source.
|
||||
|
||||
Keyword Options:
|
||||
indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
regex If true, regular expression matching will
|
||||
be used. Obeys the indirect option.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.source = source
|
||||
|
||||
for k in opts.keys():
|
||||
if k == "indirect":
|
||||
self.source_indirect = opts[k]
|
||||
elif k == "regex":
|
||||
self.source_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid source option: {0}".format(k))
|
||||
|
||||
if self.source_regex:
|
||||
self.source_cmp = re.compile(self.source)
|
||||
else:
|
||||
self.source_cmp = None
|
||||
|
||||
def set_target(self, target, **opts):
|
||||
"""
|
||||
Set the criteria for the rule's target.
|
||||
|
||||
Parameter:
|
||||
target Name to match the rule's target.
|
||||
|
||||
Keyword Options:
|
||||
indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
regex If true, regular expression matching will
|
||||
be used. Obeys the indirect option.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.target = target
|
||||
|
||||
for k in opts.keys():
|
||||
if k == "indirect":
|
||||
self.target_indirect = opts[k]
|
||||
elif k == "regex":
|
||||
self.target_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid target option: {0}".format(k))
|
||||
|
||||
if self.target_regex:
|
||||
self.target_cmp = re.compile(self.target)
|
||||
else:
|
||||
self.target_cmp = None
|
||||
|
||||
def set_tclass(self, tclass, **opts):
|
||||
"""
|
||||
Set the object class(es) for the rule query.
|
||||
|
||||
Parameter:
|
||||
tclass The name of the object classes to match.
|
||||
This must be a string if regular expression
|
||||
matching is used.
|
||||
|
||||
Keyword Options:
|
||||
regex If true, use a regular expression for
|
||||
matching the object class. If false, any
|
||||
set intersection will match.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
if isinstance(tclass, str):
|
||||
self.tclass = tclass
|
||||
else:
|
||||
self.tclass = set(tclass)
|
||||
|
||||
for k in opts.keys():
|
||||
if k == "regex":
|
||||
self.tclass_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid object class option: {0}".format(k))
|
||||
|
||||
if self.tclass_regex:
|
||||
self.tclass_cmp = re.compile(self.tclass)
|
||||
else:
|
||||
self.tclass_cmp = None
|
||||
|
||||
def set_default(self, default, **opts):
|
||||
"""
|
||||
Set the criteria for the rule's default.
|
||||
|
||||
Parameter:
|
||||
default Name to match the rule's default.
|
||||
|
||||
Keyword Options:
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.default = default
|
||||
|
||||
for k in opts.keys():
|
||||
if k == "regex":
|
||||
self.default_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid default option: {0}".format(k))
|
||||
|
||||
if self.default_regex:
|
||||
self.default_cmp = re.compile(self.default)
|
||||
else:
|
||||
self.default_cmp = None
|
171
libapol/terulequery.py
Normal file
171
libapol/terulequery.py
Normal file
@ -0,0 +1,171 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from policyrep.rule import InvalidRuleUse
|
||||
import rulequery
|
||||
|
||||
|
||||
class TERuleQuery(rulequery.RuleQuery):
|
||||
|
||||
"""Query the Type Enforcement rules."""
|
||||
|
||||
def __init__(self, policy,
|
||||
ruletype=[],
|
||||
source="", source_regex=False, source_indirect=True,
|
||||
target="", target_regex=False, target_indirect=True,
|
||||
tclass="", tclass_regex=False,
|
||||
perms=set(), perms_equal=False,
|
||||
default="", default_regex=False):
|
||||
"""
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
ruletype The rule type(s) to match.
|
||||
source The name of the source type/attribute to match.
|
||||
source_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
source_regex If true, regular expression matching will
|
||||
be used on the source type/attribute.
|
||||
Obeys the source_indirect option.
|
||||
target The name of the target type/attribute to match.
|
||||
target_indirect If true, members of an attribute will be
|
||||
matched rather than the attribute itself.
|
||||
target_regex If true, regular expression matching will
|
||||
be used on the target type/attribute.
|
||||
Obeys target_indirect option.
|
||||
tclass The object class(es) to match.
|
||||
tclass_regex If true, use a regular expression for
|
||||
matching the rule's object class.
|
||||
perms The permission(s) to match.
|
||||
perms_equal If true, the permission set of the rule
|
||||
must exactly match the permissions
|
||||
criteria. If false, any set intersection
|
||||
will match.
|
||||
default The name of the default type to match.
|
||||
default_regex If true, regular expression matching will be
|
||||
used on the default type.
|
||||
"""
|
||||
|
||||
self.policy = policy
|
||||
|
||||
self.set_ruletype(ruletype)
|
||||
self.set_source(source, indirect=source_indirect, regex=source_regex)
|
||||
self.set_target(target, indirect=target_indirect, regex=target_regex)
|
||||
self.set_tclass(tclass, regex=tclass_regex)
|
||||
self.set_perms(perms, equal=perms_equal)
|
||||
self.set_default(default, regex=default_regex)
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching TE rules."""
|
||||
|
||||
for r in self.policy.terules():
|
||||
#
|
||||
# Matching on rule type
|
||||
#
|
||||
if self.ruletype:
|
||||
if not r.ruletype in self.ruletype:
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on source type
|
||||
#
|
||||
if self.source and not self._match_indirect_regex(
|
||||
r.source,
|
||||
self.source,
|
||||
self.source_indirect,
|
||||
self.source_regex,
|
||||
self.source_cmp):
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on target type
|
||||
#
|
||||
if self.target and not self._match_indirect_regex(
|
||||
r.target,
|
||||
self.target,
|
||||
self.target_indirect,
|
||||
self.target_regex,
|
||||
self.target_cmp):
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on object class
|
||||
#
|
||||
if self.tclass and not self._match_object_class(
|
||||
r.tclass,
|
||||
self.tclass,
|
||||
self.tclass_regex,
|
||||
self.tclass_cmp):
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on permission set
|
||||
#
|
||||
if self.perms:
|
||||
try:
|
||||
if not self._match_set(
|
||||
r.perms,
|
||||
self.perms,
|
||||
self.perms_equal):
|
||||
continue
|
||||
except InvalidRuleUse:
|
||||
continue
|
||||
|
||||
#
|
||||
# Matching on default type
|
||||
#
|
||||
if self.default:
|
||||
try:
|
||||
if not self._match_regex(
|
||||
r.default,
|
||||
self.default,
|
||||
self.default_regex,
|
||||
self.default_cmp):
|
||||
continue
|
||||
except InvalidRuleUse:
|
||||
continue
|
||||
|
||||
# if we get here, we have matched all available criteria
|
||||
yield r
|
||||
|
||||
def set_perms(self, perms, **opts):
|
||||
"""
|
||||
Set the permission set for the TE rule query.
|
||||
|
||||
Parameter:
|
||||
perms The permissions to match.
|
||||
|
||||
Options:
|
||||
equal If true, the permission set of the rule
|
||||
must equal the permissions criteria to
|
||||
match. If false, permission in the critera
|
||||
will cause a rule match.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid permission set keyword option.
|
||||
"""
|
||||
|
||||
if isinstance(perms, str):
|
||||
self.perms = perms
|
||||
else:
|
||||
self.perms = set(perms)
|
||||
|
||||
for k in opts.keys():
|
||||
if k == "equal":
|
||||
self.perms_equal = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid permission set option: {0}".format(k))
|
141
libapol/typequery.py
Normal file
141
libapol/typequery.py
Normal file
@ -0,0 +1,141 @@
|
||||
# Copyright 2014, 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 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.
|
||||
#
|
||||
# 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 Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with SETools. If not, see
|
||||
# <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import re
|
||||
|
||||
import compquery
|
||||
|
||||
|
||||
class TypeQuery(compquery.ComponentQuery):
|
||||
|
||||
"""Query SELinux policy types."""
|
||||
|
||||
def __init__(self, policy,
|
||||
name="", name_regex=False,
|
||||
alias="", alias_regex=False,
|
||||
attrs=set(), attrs_equal=False, attrs_regex=False):
|
||||
"""
|
||||
Parameter:
|
||||
policy The policy to query.
|
||||
name The type name to match.
|
||||
name_regex If true, regular expression matching
|
||||
will be used on the type names.
|
||||
alias The alias name to match.
|
||||
alias_regex If true, regular expression matching
|
||||
will be used on the alias names.
|
||||
attrs The attribute to match.
|
||||
attrs_equal If true, only types with attribute sets
|
||||
that are equal to the criteria will
|
||||
match. Otherwise, any intersection
|
||||
will match.
|
||||
attrs_regex If true, regular expression matching
|
||||
will be used on the attribute names.
|
||||
"""
|
||||
|
||||
self.policy = policy
|
||||
self.set_name(name, regex=name_regex)
|
||||
self.set_alias(alias, regex=alias_regex)
|
||||
self.set_attrs(attrs, regex=attrs_regex, equal=attrs_equal)
|
||||
|
||||
def results(self):
|
||||
"""Generator which yields all matching types."""
|
||||
|
||||
for t in self.policy.types():
|
||||
if self.name and not self._match_regex(
|
||||
t,
|
||||
self.name,
|
||||
self.name_regex,
|
||||
self.name_cmp):
|
||||
continue
|
||||
|
||||
if self.alias and not self._match_in_set(
|
||||
set(str(a) for a in t.aliases()),
|
||||
self.alias,
|
||||
self.alias_regex,
|
||||
self.alias_cmp):
|
||||
continue
|
||||
|
||||
if self.attrs and not self._match_regex_or_set(
|
||||
set(str(a) for a in t.attributes()),
|
||||
self.attrs,
|
||||
self.attrs_equal,
|
||||
self.attrs_regex,
|
||||
self.attrs_cmp):
|
||||
continue
|
||||
|
||||
yield t
|
||||
|
||||
def set_alias(self, alias, **opts):
|
||||
"""
|
||||
Set the criteria for the type's aliases.
|
||||
|
||||
Parameter:
|
||||
alias Name to match the component's aliases.
|
||||
|
||||
Keyword Options:
|
||||
regex If true, regular expression matching will be used.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.alias = alias
|
||||
|
||||
for k in opts.keys():
|
||||
if k == "regex":
|
||||
self.alias_regex = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid alias option: {0}".format(k))
|
||||
|
||||
if self.alias_regex:
|
||||
self.alias_cmp = re.compile(self.alias)
|
||||
else:
|
||||
self.alias_cmp = None
|
||||
|
||||
def set_attrs(self, attrs, **opts):
|
||||
"""
|
||||
Set the criteria for the type's attributes.
|
||||
|
||||
Parameter:
|
||||
alias Name to match the component's attributes.
|
||||
|
||||
Keyword Options:
|
||||
regex If true, regular expression matching will be used.
|
||||
equal If true, the attribute set of the type
|
||||
must equal the attributes criteria to
|
||||
match. If false, any intersection in the
|
||||
critera will cause a rule match.
|
||||
|
||||
Exceptions:
|
||||
NameError Invalid keyword option.
|
||||
"""
|
||||
|
||||
self.attrs = attrs
|
||||
|
||||
for k in opts.keys():
|
||||
if k == "regex":
|
||||
self.attrs_regex = opts[k]
|
||||
elif k == "equal":
|
||||
self.attrs_equal = opts[k]
|
||||
else:
|
||||
raise NameError("Invalid alias option: {0}".format(k))
|
||||
|
||||
if self.attrs_regex:
|
||||
self.attrs_cmp = re.compile(self.attrs)
|
||||
else:
|
||||
self.attrs_cmp = None
|
138
sedta
Executable file
138
sedta
Executable file
@ -0,0 +1,138 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright 2014, 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 sys
|
||||
import argparse
|
||||
|
||||
import libapol
|
||||
|
||||
|
||||
def print_transition(trans, entrypoints, setexec, dyntrans, setcur):
|
||||
if trans:
|
||||
print("Domain transition rule(s):")
|
||||
for t in trans:
|
||||
print(t)
|
||||
|
||||
if setexec:
|
||||
print("\nSet execution context rule(s):")
|
||||
for s in setexec:
|
||||
print(s)
|
||||
|
||||
for name, entry, exe, type_trans in entrypoints:
|
||||
print("\nEntrypoint {0}:".format(name))
|
||||
|
||||
print("\tDomain entrypoint rule(s):")
|
||||
for e in entry:
|
||||
print("\t{0}".format(e))
|
||||
|
||||
print("\n\tFile execute rule(s):")
|
||||
for e in exe:
|
||||
print("\t{0}".format(e))
|
||||
|
||||
if type_trans:
|
||||
print("\n\tType transition rule(s):")
|
||||
for t in type_trans:
|
||||
print("\t{0}".format(t))
|
||||
|
||||
print()
|
||||
|
||||
if dyntrans:
|
||||
print("Dynamic transition rule(s):")
|
||||
for d in dyntrans:
|
||||
print(d)
|
||||
|
||||
print("\nSet current process context rule(s):")
|
||||
for s in setcur:
|
||||
print(s)
|
||||
|
||||
print()
|
||||
|
||||
print()
|
||||
|
||||
parser = argparse.ArgumentParser(description="SELinux policy domain transition analysis tool.",
|
||||
epilog="If no analysis algorithm is selected, all transitions out of a domain will be calculated.")
|
||||
parser.add_argument("--version", action="version", version=libapol.__version__)
|
||||
parser.add_argument(
|
||||
"-p", "--policy", help="Path to SELinux policy to analyze.", required=True)
|
||||
parser.add_argument(
|
||||
"-s", "--source", help="Source type of the analysis.", required=True, default="")
|
||||
parser.add_argument(
|
||||
"-t", "--target", help="Target type of the analysis.", default="")
|
||||
parser.add_argument("--stats", action="store_true",
|
||||
help="Display statistics at the end of the analysis.")
|
||||
|
||||
alg = parser.add_argument_group("Analysis algorithm")
|
||||
alg.add_argument("-S", "--shortest_path", action="store_true",
|
||||
help="Calculate all shortest paths.")
|
||||
alg.add_argument("-A", "--all_paths",
|
||||
help="Calculate all paths, with the specified maximum path length. (Expensive)", type=int, metavar="MAX_STEPS")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.target and (args.shortest_path or args.all_paths):
|
||||
parser.error("The target type must be specified to determine a path.")
|
||||
|
||||
try:
|
||||
p = libapol.SELinuxPolicy(args.policy)
|
||||
g = libapol.dta.DomainTransitionAnalysis(p)
|
||||
except Exception as err:
|
||||
print(err)
|
||||
sys.exit(1)
|
||||
|
||||
if args.shortest_path or args.all_paths:
|
||||
try:
|
||||
if args.shortest_path:
|
||||
paths = g.all_shortest_paths(args.source, args.target)
|
||||
else:
|
||||
paths = g.all_paths(args.source, args.target, args.all_paths)
|
||||
except Exception as err:
|
||||
print(err)
|
||||
sys.exit(1)
|
||||
|
||||
i = 0
|
||||
for i, path in enumerate(paths, start=1):
|
||||
print("Domain transition path {0}:".format(i))
|
||||
|
||||
for step, (src, tgt, trans, entrypoints, setexec, dyntrans, setcur) in enumerate(path, start=1):
|
||||
print("Step {0}: {1} -> {2}\n".format(step, src, tgt))
|
||||
print_transition(trans, entrypoints, setexec, dyntrans, setcur)
|
||||
|
||||
print(i, "domain transition path(s) found.")
|
||||
|
||||
else: # single transition
|
||||
try:
|
||||
transitions = g.transitions(args.source)
|
||||
except Exception as err:
|
||||
print(err)
|
||||
sys.exit(1)
|
||||
|
||||
i = 0
|
||||
for i, (src, tgt, trans, entrypoints, setexec, dyntrans, setcur) in enumerate(transitions, start=1):
|
||||
print("Transition {0}: {1} -> {2}\n".format(i, src, tgt))
|
||||
print_transition(trans, entrypoints, setexec, dyntrans, setcur)
|
||||
|
||||
|
||||
print(i, "domain transition(s) found.")
|
||||
|
||||
if args.stats:
|
||||
print("Domain transition graph size:")
|
||||
nnodes, nedges = g.get_stats()
|
||||
print(" Nodes:", nnodes)
|
||||
print(" Edges:", nedges)
|
110
seinfo
Executable file
110
seinfo
Executable file
@ -0,0 +1,110 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright 2014, 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 libapol
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="SELinux policy information tool.")
|
||||
parser.add_argument("--version", action="version", version=libapol.__version__)
|
||||
parser.add_argument("policy", help="Path to the SELinux policy to query.")
|
||||
parser.add_argument(
|
||||
"-x", "--expand", help="Print additional information about the specified components.", action="store_true")
|
||||
|
||||
queries = parser.add_argument_group("Component Queries")
|
||||
queries.add_argument("-c", "--class", help="Print object classes.",
|
||||
dest="classquery", default="", nargs='?', const=True, metavar="CLASS")
|
||||
queries.add_argument("-t", "--type", help="Print types.",
|
||||
dest="typequery", default="", nargs='?', const=True, metavar="TYPE")
|
||||
queries.add_argument("-a", "--attribute", help="Print type attributes.",
|
||||
dest="attrquery", default="", nargs='?', const=True, metavar="ATTR")
|
||||
queries.add_argument("-r", "--role", help="Print roles.",
|
||||
dest="rolequery", default="", nargs='?', const=True, metavar="ROLE")
|
||||
queries.add_argument("-u", "--user", help="Print users.",
|
||||
dest="userquery", default="", nargs='?', const=True, metavar="USER")
|
||||
queries.add_argument("-b", "--bool", help="Print Booleans.",
|
||||
dest="boolquery", default="", nargs='?', const=True, metavar="BOOL")
|
||||
queries.add_argument("--sensitivity", help="Print MLS sensitivities.",
|
||||
dest="mlssensquery", default="", nargs='?', const=True, metavar="SENS")
|
||||
queries.add_argument("--category", help="Print MLS categories.",
|
||||
dest="mlssensquery", default="", nargs='?', const=True, metavar="CAT")
|
||||
queries.add_argument("--constrain", help="Print constraints.",
|
||||
dest="constraintquery", default="", nargs='?', const=True, metavar="CLASS")
|
||||
queries.add_argument("--initialsid", help="Print initial SIDs (contexts).",
|
||||
dest="initialsidquery", default="", nargs='?', const=True, metavar="NAME")
|
||||
queries.add_argument("--fs_use", help="Print fs_use statements.",
|
||||
dest="fsusequery", default="", nargs='?', const=True, metavar="FS_TYPE")
|
||||
queries.add_argument("--genfscon", help="Print genfscon statements.",
|
||||
dest="genfsconquery", default="", nargs='?', const=True, metavar="FS_TYPE")
|
||||
queries.add_argument("--netifcon", help="Print netifcon statements.",
|
||||
dest="netifconquery", default="", nargs='?', const=True, metavar="DEVICE")
|
||||
queries.add_argument("--nodecon", help="Print nodecon statements.",
|
||||
dest="nodeconquery", default="", nargs='?', const=True, metavar="ADDR")
|
||||
queries.add_argument("--portcon", help="Print portcon statements.",
|
||||
dest="portconquery", default="", nargs='?', const=True, metavar="PORT_NUM")
|
||||
queries.add_argument("--permissive", help="Print permissive statements.",
|
||||
dest="permissivequery", default="", nargs='?', const=True, metavar="TYPE")
|
||||
queries.add_argument("--polcap", help="Print policy capabilities.",
|
||||
dest="polcapquery", default="", nargs='?', const=True, metavar="NAME")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
p = libapol.SELinuxPolicy(args.policy)
|
||||
except RuntimeError as err:
|
||||
print(err)
|
||||
sys.exit(-1)
|
||||
|
||||
if args.typequery:
|
||||
if isinstance(args.typequery, str):
|
||||
q = libapol.typequery.TypeQuery(p, args.typequery)
|
||||
else:
|
||||
q = libapol.typequery.TypeQuery(p)
|
||||
|
||||
for t in sorted(q.results()):
|
||||
if args.expand:
|
||||
print(t.statement())
|
||||
else:
|
||||
print(t)
|
||||
|
||||
if args.boolquery:
|
||||
if isinstance(args.boolquery, str):
|
||||
q = libapol.boolquery.BoolQuery(p, args.boolquery)
|
||||
else:
|
||||
q = libapol.boolquery.BoolQuery(p)
|
||||
|
||||
for b in sorted(q.results()):
|
||||
if args.expand:
|
||||
print(b.statement())
|
||||
else:
|
||||
print(b)
|
||||
|
||||
if args.polcapquery:
|
||||
if isinstance(args.polcapquery, str):
|
||||
q = libapol.polcapquery.PolCapQuery(p, args.polcapquery)
|
||||
else:
|
||||
q = libapol.polcapquery.PolCapQuery(p)
|
||||
|
||||
for cap in sorted(q.results()):
|
||||
if args.expand:
|
||||
print(cap.statement())
|
||||
else:
|
||||
print(cap)
|
115
seinfoflow
Executable file
115
seinfoflow
Executable file
@ -0,0 +1,115 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright 2014, 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 libapol
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
parser = argparse.ArgumentParser(description="SELinux policy information flow analysis tool.",
|
||||
epilog="If no analysis algorithm is selected, all information flow out of the source domain will be calculated.")
|
||||
parser.add_argument("--version", action="version", version=libapol.__version__)
|
||||
parser.add_argument("--stats", action="store_true",
|
||||
help="Display statistics at the end of the analysis.")
|
||||
|
||||
settings = parser.add_argument_group("Analysis settings")
|
||||
settings.add_argument(
|
||||
"-p", "--policy", help="Path to SELinux policy to analyze.", required=True)
|
||||
settings.add_argument(
|
||||
"-m", "--map", help="Path to permission map file.", required=True)
|
||||
settings.add_argument(
|
||||
"-s", "--source", help="Source type of the analysis.", required=True, default="")
|
||||
settings.add_argument(
|
||||
"-t", "--target", help="Target type of the analysis.", default="")
|
||||
|
||||
alg = parser.add_argument_group("Analysis algorithm")
|
||||
alg.add_argument("-S", "--shortest_path", action="store_true",
|
||||
help="Calculate all shortest paths.")
|
||||
alg.add_argument("-A", "--all_paths",
|
||||
help="Calculate all paths, with the specified maximum path length. (Expensive)", type=int, metavar="MAX_STEPS")
|
||||
|
||||
opts = parser.add_argument_group("Analysis options")
|
||||
opts.add_argument("-w", "--min_weight",
|
||||
help="Minimum permission weight. Default is 3.", default=3, type=int)
|
||||
opts.add_argument("-l", "--limit_flows",
|
||||
help="Limit to the specified number of flows. Default is unlimited.", default=0, type=int)
|
||||
opts.add_argument(
|
||||
"exclude", help="List of excluded types in the analysis.", nargs="*")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.target and (args.shortest_path or args.all_paths):
|
||||
parser.error("The target type must be specified to determine a path.")
|
||||
|
||||
if args.limit_flows < 0:
|
||||
parser.error("Limit on information flows cannot be negative.")
|
||||
|
||||
try:
|
||||
p = libapol.SELinuxPolicy(args.policy)
|
||||
g = libapol.infoflow.InfoFlowAnalysis(
|
||||
p, args.map, minweight=args.min_weight, exclude=args.exclude)
|
||||
except Exception as err:
|
||||
print(err)
|
||||
sys.exit(1)
|
||||
|
||||
if args.shortest_path or args.all_paths:
|
||||
try:
|
||||
if args.shortest_path:
|
||||
paths = g.all_shortest_paths(args.source, args.target)
|
||||
else:
|
||||
paths = g.all_paths(args.source, args.target, args.all_paths)
|
||||
except Exception as err:
|
||||
print(err)
|
||||
sys.exit(1)
|
||||
|
||||
flownum = 0
|
||||
for flownum, path in enumerate(paths, start=1):
|
||||
print("Flow {0}:".format(flownum))
|
||||
for stepnum, (src, tgt, rules) in enumerate(path, start=1):
|
||||
print(" Step {0}: {1} -> {2}".format(stepnum, src, tgt))
|
||||
|
||||
for rule in sorted(rules):
|
||||
print(" ", rule)
|
||||
|
||||
print()
|
||||
|
||||
if args.limit_flows and flownum >= args.limit_flows:
|
||||
break
|
||||
|
||||
print()
|
||||
|
||||
else: # single direct info flow
|
||||
flownum = 0
|
||||
for flownum, (src, tgt, rules) in enumerate(g.infoflows(args.source), start=1):
|
||||
print("Flow {0}: {1} -> {2}".format(flownum, src, tgt))
|
||||
for rule in sorted(rules):
|
||||
print(" ", rule)
|
||||
|
||||
print()
|
||||
|
||||
if args.limit_flows and flownum >= args.limit_flows:
|
||||
break
|
||||
|
||||
print(flownum, "information flow(s) found.\n")
|
||||
|
||||
if args.stats:
|
||||
print("Information flow graph size:")
|
||||
nnodes, nedges = g.get_stats()
|
||||
print(" Nodes:", nnodes)
|
||||
print(" Edges:", nedges)
|
155
sesearch
Executable file
155
sesearch
Executable file
@ -0,0 +1,155 @@
|
||||
#!/usr/bin/python
|
||||
# Copyright 2014, 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 libapol
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
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=libapol.__version__)
|
||||
parser.add_argument(
|
||||
"policy", type=str, help="Path to the SELinux policy to search.")
|
||||
|
||||
rtypes = parser.add_argument_group("TE Rule Types")
|
||||
rtypes.add_argument("-A", "--allow", action="append_const",
|
||||
const="allow", dest="tertypes", help="Search allow rules.")
|
||||
#rtypes.add_argument("--neverallow", action="append_const", const="neverallow", dest="tertypes", help="Search neverallow rules.")
|
||||
rtypes.add_argument("--auditallow", action="append_const",
|
||||
const="auditallow", dest="tertypes", help="Search auditallow rules.")
|
||||
rtypes.add_argument("--dontaudit", action="append_const",
|
||||
const="dontaudit", dest="tertypes", help="Search dontaudit 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", type=str,
|
||||
help="Source type/role of the TE/RBAC rule.", default="")
|
||||
expr.add_argument("-t", "--target", type=str,
|
||||
help="Target type/role of the TE/RBAC rule.", default="")
|
||||
expr.add_argument(
|
||||
"-c", "--class", help="Comma separated list of object classes", dest="tclass", default="")
|
||||
expr.add_argument(
|
||||
"-p", "--perms", help="Comma separated list of permissions.", metavar="PERMS", default="")
|
||||
expr.add_argument(
|
||||
"-D", "--default", help="Default type of the TE/RBAC rule.", default="")
|
||||
expr.add_argument("-b", "--bool", help="Conditional rules with the Boolean in the expression.",
|
||||
dest="boolname", metavar="BOOL", default="")
|
||||
|
||||
opts = parser.add_argument_group("Search options")
|
||||
opts.add_argument("-e", action="store_true",
|
||||
help="Match permission set exactly instead of matching any listed permission.", dest="perms_equal")
|
||||
opts.add_argument("-ds", action="store_false",
|
||||
help="Match source attributes directly instead of matching member types/roles.", dest="source_indirect")
|
||||
opts.add_argument("-dt", action="store_false",
|
||||
help="Match target attributes directly instead of matching member types/roles.", dest="target_indirect")
|
||||
opts.add_argument("-rs", action="store_true",
|
||||
help="Use regular expression matching for the source type/role.", dest="source_regex")
|
||||
opts.add_argument("-rt", action="store_true",
|
||||
help="Use regular expression matching for the target type/role.", dest="target_regex")
|
||||
opts.add_argument("-rc", action="store_true",
|
||||
help="Use regular expression matching for the object class.", dest="tclass_regex")
|
||||
opts.add_argument("-rd", action="store_true",
|
||||
help="Use regular expression matching for the default type/role.", dest="default_regex")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.tertypes and not args.mlsrtypes and not args.rbacrtypes:
|
||||
parser.error("At least one rule type must be specified.")
|
||||
|
||||
if (args.tertypes or args.mlsrtypes) and args.rbacrtypes:
|
||||
parser.error(
|
||||
"TE/MLS rule searches cannot be mixed with RBAC rule searches.")
|
||||
|
||||
try:
|
||||
p = libapol.SELinuxPolicy(args.policy)
|
||||
except RuntimeError as err:
|
||||
print(err)
|
||||
sys.exit(-1)
|
||||
|
||||
if args.tertypes:
|
||||
q = libapol.terulequery.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,
|
||||
default=args.default, default_regex=args.default_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.set_tclass(args.tclass)
|
||||
else:
|
||||
q.set_tclass(args.tclass.split(","))
|
||||
|
||||
if args.perms:
|
||||
q.set_perms(args.perms.split(","))
|
||||
|
||||
for r in sorted(q.results()):
|
||||
print(r)
|
||||
|
||||
if args.rbacrtypes:
|
||||
q = libapol.rbacrulequery.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.set_tclass(args.tclass)
|
||||
else:
|
||||
q.set_tclass(args.tclass.split(","))
|
||||
|
||||
for r in sorted(q.results()):
|
||||
print(r)
|
||||
|
||||
if args.mlsrtypes:
|
||||
q = libapol.mlsrulequery.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)
|
||||
|
||||
# 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.set_tclass(args.tclass)
|
||||
else:
|
||||
q.set_tclass(args.tclass.split(","))
|
||||
|
||||
for r in sorted(q.results()):
|
||||
print(r)
|
23
setup.py
Normal file
23
setup.py
Normal file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from setuptools import setup
|
||||
from libapol import __version__
|
||||
|
||||
setup(name='setools',
|
||||
version=__version__,
|
||||
description='SELinux Policy tools.',
|
||||
author='Tresys Technology, LLC',
|
||||
author_email='setools@tresys.com',
|
||||
url='https://github.com/TresysTechnology/setools',
|
||||
packages=['libapol', 'libapol.policyrep'],
|
||||
scripts = ['seinfo', 'seinfoflow', 'sesearch', 'sedta'],
|
||||
data_files=[('/usr/share/setools', ['data/perm_map'])],
|
||||
test_suite='tests',
|
||||
license='GPLv2+, LGPLv2.1+',
|
||||
classifiers=[
|
||||
'Environment :: Console',
|
||||
'Intended Audience :: Information Technology',
|
||||
'Topic :: Security',
|
||||
'Topic :: Utilities',
|
||||
],
|
||||
)
|
23
tests/__init__.py
Normal file
23
tests/__init__.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright 2014, 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/>.
|
||||
#
|
||||
import boolquery
|
||||
import dta
|
||||
import polcapquery
|
||||
import infoflow
|
||||
import terulequery
|
||||
import typequery
|
133
tests/boolquery.conf
Normal file
133
tests/boolquery.conf
Normal file
@ -0,0 +1,133 @@
|
||||
class infoflow
|
||||
class infoflow2
|
||||
class infoflow3
|
||||
class infoflow4
|
||||
class infoflow5
|
||||
class infoflow6
|
||||
class infoflow7
|
||||
|
||||
sid kernel
|
||||
sid security
|
||||
|
||||
common infoflow
|
||||
{
|
||||
low_w
|
||||
med_w
|
||||
hi_w
|
||||
low_r
|
||||
med_r
|
||||
hi_r
|
||||
}
|
||||
|
||||
class infoflow
|
||||
inherits infoflow
|
||||
|
||||
class infoflow2
|
||||
inherits infoflow
|
||||
{
|
||||
super_w
|
||||
super_r
|
||||
}
|
||||
|
||||
class infoflow3
|
||||
{
|
||||
null
|
||||
}
|
||||
|
||||
class infoflow4
|
||||
inherits infoflow
|
||||
|
||||
class infoflow5
|
||||
inherits infoflow
|
||||
|
||||
class infoflow6
|
||||
inherits infoflow
|
||||
|
||||
class infoflow7
|
||||
inherits infoflow
|
||||
{
|
||||
super_w
|
||||
super_r
|
||||
super_none
|
||||
super_both
|
||||
super_unmapped
|
||||
}
|
||||
|
||||
sensitivity low_s;
|
||||
sensitivity medium_s alias med;
|
||||
sensitivity high_s;
|
||||
|
||||
dominance { low_s med high_s }
|
||||
|
||||
category here;
|
||||
category there;
|
||||
category elsewhere alias lost;
|
||||
|
||||
#level decl
|
||||
level low_s:here.there;
|
||||
level med:here, elsewhere;
|
||||
level high_s:here.lost;
|
||||
|
||||
#some constraints
|
||||
mlsconstrain infoflow hi_r ((l1 dom l2) or (t1 == mls_exempt));
|
||||
|
||||
attribute mls_exempt;
|
||||
|
||||
type system;
|
||||
role system;
|
||||
role system types system;
|
||||
|
||||
################################################################################
|
||||
# Type enforcement declarations and rules
|
||||
|
||||
########################################
|
||||
#
|
||||
# Booleans Query
|
||||
#
|
||||
|
||||
# test 1
|
||||
# name: test1
|
||||
# default: unset
|
||||
bool test1 true;
|
||||
|
||||
# test 2
|
||||
# name: test2(a|b) regex
|
||||
# default: unset
|
||||
bool test2a true;
|
||||
bool test2b true;
|
||||
|
||||
# test 10
|
||||
# name: unset
|
||||
# default: false;
|
||||
bool test10a false;
|
||||
bool test10b false;
|
||||
|
||||
################################################################################
|
||||
|
||||
#users
|
||||
user system roles system level med range low_s - high_s:here.lost;
|
||||
|
||||
#normal constraints
|
||||
constrain infoflow hi_w (u1 == u2);
|
||||
|
||||
#isids
|
||||
sid kernel system:system:system:medium_s:here
|
||||
sid security system:system:system:high_s:lost
|
||||
|
||||
#fs_use
|
||||
fs_use_trans devpts system:object_r:system:low_s;
|
||||
fs_use_xattr ext3 system:object_r:system:low_s;
|
||||
fs_use_task pipefs system:object_r:system:low_s;
|
||||
|
||||
#genfscon
|
||||
genfscon proc / system:object_r:system:med
|
||||
genfscon proc /sys system:object_r:system:low_s
|
||||
genfscon selinuxfs / system:object_r:system:high_s:here.there
|
||||
|
||||
portcon tcp 80 system:object_r:system:low_s
|
||||
|
||||
netifcon eth0 system:object_r:system:low_s system:object_r:system:low_s
|
||||
|
||||
nodecon 127.0.0.1 255.255.255.255 system:object_r:system:low_s:here
|
||||
nodecon ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff system:object_r:system:low_s:here
|
||||
|
73
tests/boolquery.py
Normal file
73
tests/boolquery.py
Normal file
@ -0,0 +1,73 @@
|
||||
# Copyright 2014, 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/>.
|
||||
#
|
||||
import unittest
|
||||
|
||||
from libapol import SELinuxPolicy
|
||||
from libapol.boolquery import BoolQuery
|
||||
|
||||
|
||||
class BoolQueryTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.p = SELinuxPolicy("tests/boolquery.conf")
|
||||
|
||||
def test_000_unset(self):
|
||||
"""Boolean query with no criteria."""
|
||||
# query with no parameters gets all Booleans.
|
||||
for numbools, b in enumerate(self.p.bools(), start=1):
|
||||
pass
|
||||
|
||||
q = BoolQuery(self.p)
|
||||
for q_numbools, b in enumerate(q.results(), start=1):
|
||||
pass
|
||||
|
||||
self.assertEqual(numbools, q_numbools)
|
||||
|
||||
def test_001_name_exact(self):
|
||||
"""Boolean query with exact match"""
|
||||
q = BoolQuery(self.p, name="test1")
|
||||
|
||||
# manually consume the generator:
|
||||
bools = q.results()
|
||||
b = bools.next()
|
||||
|
||||
self.assertEqual(b, "test1")
|
||||
|
||||
self.assertRaises(StopIteration, bools.next)
|
||||
|
||||
def test_002_name_regex(self):
|
||||
"""Boolean query with regex match."""
|
||||
q = BoolQuery(self.p, name="test2(a|b)", name_regex=True)
|
||||
|
||||
# manually consume the generator:
|
||||
b = sorted(q.results())
|
||||
self.assertEqual(len(b), 2)
|
||||
|
||||
self.assertEqual(b[0], "test2a")
|
||||
self.assertEqual(b[1], "test2b")
|
||||
|
||||
def test_010_default(self):
|
||||
"""Boolean query with default state match."""
|
||||
q = BoolQuery(self.p, match_default=True, default=False)
|
||||
|
||||
# manually consume the generator:
|
||||
b = sorted(q.results())
|
||||
self.assertEqual(len(b), 2)
|
||||
|
||||
self.assertEqual(b[0], "test10a")
|
||||
self.assertEqual(b[1], "test10b")
|
222
tests/dta.conf
Normal file
222
tests/dta.conf
Normal file
@ -0,0 +1,222 @@
|
||||
class infoflow
|
||||
class infoflow2
|
||||
class infoflow3
|
||||
class infoflow4
|
||||
class infoflow5
|
||||
class infoflow6
|
||||
class infoflow7
|
||||
class process
|
||||
class file
|
||||
|
||||
sid kernel
|
||||
sid security
|
||||
|
||||
common infoflow
|
||||
{
|
||||
low_w
|
||||
med_w
|
||||
hi_w
|
||||
low_r
|
||||
med_r
|
||||
hi_r
|
||||
}
|
||||
|
||||
class infoflow
|
||||
inherits infoflow
|
||||
|
||||
class infoflow2
|
||||
inherits infoflow
|
||||
{
|
||||
super_w
|
||||
super_r
|
||||
}
|
||||
|
||||
class infoflow3
|
||||
{
|
||||
null
|
||||
}
|
||||
|
||||
class infoflow4
|
||||
inherits infoflow
|
||||
|
||||
class infoflow5
|
||||
inherits infoflow
|
||||
|
||||
class infoflow6
|
||||
inherits infoflow
|
||||
|
||||
class infoflow7
|
||||
inherits infoflow
|
||||
{
|
||||
super_w
|
||||
super_r
|
||||
super_none
|
||||
super_both
|
||||
super_unmapped
|
||||
}
|
||||
|
||||
class process
|
||||
{
|
||||
transition
|
||||
dyntransition
|
||||
setexec
|
||||
setcurrent
|
||||
}
|
||||
|
||||
class file
|
||||
{
|
||||
execute
|
||||
entrypoint
|
||||
}
|
||||
|
||||
|
||||
sensitivity low_s;
|
||||
sensitivity medium_s alias med;
|
||||
sensitivity high_s;
|
||||
|
||||
dominance { low_s med high_s }
|
||||
|
||||
category here;
|
||||
category there;
|
||||
category elsewhere alias lost;
|
||||
|
||||
#level decl
|
||||
level low_s:here.there;
|
||||
level med:here, elsewhere;
|
||||
level high_s:here.lost;
|
||||
|
||||
#some constraints
|
||||
mlsconstrain infoflow hi_r ((l1 dom l2) or (t1 == mls_exempt));
|
||||
|
||||
attribute mls_exempt;
|
||||
|
||||
type system;
|
||||
role system;
|
||||
role system types system;
|
||||
|
||||
################################################################################
|
||||
# Type enforcement declarations and rules
|
||||
|
||||
########################################
|
||||
#
|
||||
# Domain transition analysis
|
||||
#
|
||||
|
||||
# The extent of the valid transition graph:
|
||||
#
|
||||
# start -> trans1 -> trans2 -> trans3 -> trans5
|
||||
# |
|
||||
# | -> dyntrans100 -> bothtrans200
|
||||
#
|
||||
# Everything else is invalid
|
||||
|
||||
# test 1
|
||||
# type_transition start -> trans1
|
||||
type start;
|
||||
type trans1;
|
||||
type trans1_exec;
|
||||
allow start trans1:process transition;
|
||||
allow start trans1_exec:file execute;
|
||||
allow trans1 trans1_exec:file entrypoint;
|
||||
type_transition start trans1_exec:process trans1;
|
||||
|
||||
# test 2
|
||||
# setexec trans1 -> trans2
|
||||
type trans2;
|
||||
type trans2_exec;
|
||||
allow trans1 self:process setexec;
|
||||
allow trans1 trans2:process transition;
|
||||
allow trans1 trans2_exec:file execute;
|
||||
allow trans2 trans2_exec:file entrypoint;
|
||||
|
||||
# test 3
|
||||
# type_transiton and setexec trans2 -> trans3
|
||||
# 2 entrypoints
|
||||
type trans3;
|
||||
type trans3_exec1;
|
||||
type trans3_exec2;
|
||||
allow trans2 self:process setexec;
|
||||
allow trans2 trans3:process transition;
|
||||
allow trans2 { trans3_exec1 trans3_exec2 }:file execute;
|
||||
allow trans3 { trans3_exec1 trans3_exec2 }:file entrypoint;
|
||||
type_transition trans2 trans3_exec1:process trans3;
|
||||
|
||||
# test 4
|
||||
# invalid transition, no type_transition/setexec
|
||||
type trans4;
|
||||
type trans4_exec;
|
||||
allow start trans4:process transition;
|
||||
allow start trans4_exec:file execute;
|
||||
allow trans4 trans4_exec:file entrypoint;
|
||||
|
||||
# test 5
|
||||
# type_transition trans3 -> trans5
|
||||
# 1 entrypoint w/conditional type_trans
|
||||
# This makes sure the type_transition fully
|
||||
# matches as expected.
|
||||
type trans5;
|
||||
type trans5_exec;
|
||||
bool trans5 false;
|
||||
allow trans3 trans5:process transition;
|
||||
allow trans3 trans5_exec:file execute;
|
||||
allow trans5 trans5_exec:file entrypoint;
|
||||
if(trans5) {
|
||||
type_transition trans3 trans5_exec:process trans5;
|
||||
} else {
|
||||
type_transition trans3 trans5_exec:process trans4;
|
||||
}
|
||||
|
||||
|
||||
# test 100
|
||||
# dyntrans start -> dyntrans100
|
||||
type dyntrans100;
|
||||
allow start self:process setcurrent;
|
||||
allow start dyntrans100:process dyntransition;
|
||||
|
||||
# test 101
|
||||
# invalid dyntransition, no setcurrent
|
||||
type dyntrans101;
|
||||
type dyntrans102;
|
||||
allow dyntrans101 dyntrans102:process dyntransition;
|
||||
|
||||
|
||||
# test 200
|
||||
# all transition possiblities dyntrans100 -> bothtrans200
|
||||
type bothtrans200;
|
||||
type bothtrans200_exec;
|
||||
allow dyntrans100 self:process { setexec setcurrent };
|
||||
allow dyntrans100 bothtrans200:process { transition dyntransition };
|
||||
allow dyntrans100 bothtrans200_exec:file execute;
|
||||
allow bothtrans200 bothtrans200_exec:file entrypoint;
|
||||
type_transition dyntrans100 bothtrans200_exec:process bothtrans200;
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
#users
|
||||
user system roles system level med range low_s - high_s:here.lost;
|
||||
|
||||
#normal constraints
|
||||
constrain infoflow hi_w (u1 == u2);
|
||||
|
||||
#isids
|
||||
sid kernel system:system:system:medium_s:here
|
||||
sid security system:system:system:high_s:lost
|
||||
|
||||
#fs_use
|
||||
fs_use_trans devpts system:object_r:system:low_s;
|
||||
fs_use_xattr ext3 system:object_r:system:low_s;
|
||||
fs_use_task pipefs system:object_r:system:low_s;
|
||||
|
||||
#genfscon
|
||||
genfscon proc / system:object_r:system:med
|
||||
genfscon proc /sys system:object_r:system:low_s
|
||||
genfscon selinuxfs / system:object_r:system:high_s:here.there
|
||||
|
||||
portcon tcp 80 system:object_r:system:low_s
|
||||
|
||||
netifcon eth0 system:object_r:system:low_s system:object_r:system:low_s
|
||||
|
||||
nodecon 127.0.0.1 255.255.255.255 system:object_r:system:low_s:here
|
||||
nodecon ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff system:object_r:system:low_s:here
|
||||
|
470
tests/dta.py
Normal file
470
tests/dta.py
Normal file
@ -0,0 +1,470 @@
|
||||
# Copyright 2014, 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/>.
|
||||
#
|
||||
import unittest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
from libapol import SELinuxPolicy
|
||||
from libapol.dta import DomainTransitionAnalysis
|
||||
from libapol.policyrep.rule import RuleNotConditional
|
||||
|
||||
|
||||
class InfoFlowAnalysisTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.p = SELinuxPolicy("tests/dta.conf")
|
||||
self.a = DomainTransitionAnalysis(self.p)
|
||||
self.a._build_graph()
|
||||
|
||||
def test_000_graph_structure(self):
|
||||
"""DTA: verify graph structure."""
|
||||
# don't check node list since the disconnected nodes are not
|
||||
# removed after removing invalid domain transitions
|
||||
|
||||
edges = sorted(list(self.a.G.out_edges_iter()))
|
||||
self.assertListEqual(edges, [("dyntrans100", "bothtrans200"),
|
||||
("start", "dyntrans100"),
|
||||
("start", "trans1"),
|
||||
("trans1", "trans2"),
|
||||
("trans2", "trans3"),
|
||||
("trans3", "trans5")])
|
||||
|
||||
def test_001_bothtrans(self):
|
||||
"""DTA: type_transition, setexeccon(), and setcon() transitions."""
|
||||
|
||||
s = "dyntrans100"
|
||||
t = "bothtrans200"
|
||||
e = "bothtrans200_exec"
|
||||
|
||||
# regular transition
|
||||
r = self.a.G.edge[s][t]["transition"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, t)
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertSetEqual(set(["transition", "dyntransition"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# setexec perms
|
||||
r = self.a.G.edge[s][t]["setexec"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, s)
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertSetEqual(set(["setexec", "setcurrent"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# exec perms
|
||||
k = self.a.G.edge[s][t]["execute"].keys()
|
||||
self.assertEqual(k, [e])
|
||||
|
||||
r = self.a.G.edge[s][t]["execute"][e]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, e)
|
||||
self.assertEqual(r[0].tclass, "file")
|
||||
self.assertSetEqual(set(["execute"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# entrypoint perms
|
||||
k = self.a.G.edge[s][t]["entrypoint"].keys()
|
||||
self.assertEqual(k, [e])
|
||||
|
||||
r = self.a.G.edge[s][t]["entrypoint"][e]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, t)
|
||||
self.assertEqual(r[0].target, e)
|
||||
self.assertEqual(r[0].tclass, "file")
|
||||
self.assertSetEqual(set(["entrypoint"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# type_transition
|
||||
k = self.a.G.edge[s][t]["type_transition"].keys()
|
||||
self.assertEqual(k, [e])
|
||||
|
||||
r = self.a.G.edge[s][t]["type_transition"][e]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "type_transition")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, e)
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertEqual(r[0].default, t)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# dynamic transition
|
||||
r = self.a.G.edge[s][t]["dyntransition"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, t)
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertSetEqual(set(["transition", "dyntransition"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# setcurrent
|
||||
r = self.a.G.edge[s][t]["setcurrent"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, s)
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertSetEqual(set(["setexec", "setcurrent"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
def test_010_dyntrans(self):
|
||||
"""DTA: setcon() transition."""
|
||||
|
||||
s = "start"
|
||||
t = "dyntrans100"
|
||||
|
||||
# regular transition
|
||||
r = self.a.G.edge[s][t]["transition"]
|
||||
self.assertEqual(len(r), 0)
|
||||
|
||||
# setexec perms
|
||||
r = self.a.G.edge[s][t]["setexec"]
|
||||
self.assertEqual(len(r), 0)
|
||||
|
||||
# exec perms
|
||||
k = self.a.G.edge[s][t]["execute"].keys()
|
||||
self.assertEqual(len(k), 0)
|
||||
|
||||
# entrypoint perms
|
||||
k = self.a.G.edge[s][t]["entrypoint"].keys()
|
||||
self.assertEqual(len(k), 0)
|
||||
|
||||
# type_transition
|
||||
k = self.a.G.edge[s][t]["type_transition"].keys()
|
||||
self.assertEqual(len(k), 0)
|
||||
|
||||
# dynamic transition
|
||||
r = self.a.G.edge[s][t]["dyntransition"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, t)
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertSetEqual(set(["dyntransition"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# setcurrent
|
||||
r = self.a.G.edge[s][t]["setcurrent"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, s)
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertSetEqual(set(["setcurrent"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
def test_020_trans(self):
|
||||
"""DTA: type_transition transition."""
|
||||
|
||||
s = "start"
|
||||
t = "trans1"
|
||||
e = "trans1_exec"
|
||||
|
||||
# regular transition
|
||||
r = self.a.G.edge[s][t]["transition"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, t)
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertSetEqual(set(["transition"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# setexec perms
|
||||
r = self.a.G.edge[s][t]["setexec"]
|
||||
self.assertEqual(len(r), 0)
|
||||
|
||||
# exec perms
|
||||
k = self.a.G.edge[s][t]["execute"].keys()
|
||||
self.assertEqual(k, [e])
|
||||
|
||||
r = self.a.G.edge[s][t]["execute"][e]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, e)
|
||||
self.assertEqual(r[0].tclass, "file")
|
||||
self.assertSetEqual(set(["execute"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# entrypoint perms
|
||||
k = self.a.G.edge[s][t]["entrypoint"].keys()
|
||||
self.assertEqual(k, [e])
|
||||
|
||||
r = self.a.G.edge[s][t]["entrypoint"][e]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, t)
|
||||
self.assertEqual(r[0].target, e)
|
||||
self.assertEqual(r[0].tclass, "file")
|
||||
self.assertSetEqual(set(["entrypoint"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# type_transition
|
||||
k = self.a.G.edge[s][t]["type_transition"].keys()
|
||||
self.assertEqual(k, [e])
|
||||
|
||||
r = self.a.G.edge[s][t]["type_transition"][e]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "type_transition")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, e)
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertEqual(r[0].default, t)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# dynamic transition
|
||||
r = self.a.G.edge[s][t]["dyntransition"]
|
||||
self.assertEqual(len(r), 0)
|
||||
|
||||
# setcurrent
|
||||
r = self.a.G.edge[s][t]["setcurrent"]
|
||||
self.assertEqual(len(r), 0)
|
||||
|
||||
def test_030_setexec(self):
|
||||
"""DTA: setexec() transition."""
|
||||
|
||||
s = "trans1"
|
||||
t = "trans2"
|
||||
e = "trans2_exec"
|
||||
|
||||
# regular transition
|
||||
r = self.a.G.edge[s][t]["transition"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, t)
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertSetEqual(set(["transition"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# setexec perms
|
||||
r = self.a.G.edge[s][t]["setexec"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, s)
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertSetEqual(set(["setexec"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# exec perms
|
||||
k = self.a.G.edge[s][t]["execute"].keys()
|
||||
self.assertEqual(k, [e])
|
||||
|
||||
r = self.a.G.edge[s][t]["execute"][e]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, e)
|
||||
self.assertEqual(r[0].tclass, "file")
|
||||
self.assertSetEqual(set(["execute"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# entrypoint perms
|
||||
k = self.a.G.edge[s][t]["entrypoint"].keys()
|
||||
self.assertEqual(k, [e])
|
||||
|
||||
r = self.a.G.edge[s][t]["entrypoint"][e]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, t)
|
||||
self.assertEqual(r[0].target, e)
|
||||
self.assertEqual(r[0].tclass, "file")
|
||||
self.assertSetEqual(set(["entrypoint"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# type_transition
|
||||
k = self.a.G.edge[s][t]["type_transition"].keys()
|
||||
self.assertEqual(len(k), 0)
|
||||
|
||||
# dynamic transition
|
||||
r = self.a.G.edge[s][t]["dyntransition"]
|
||||
self.assertEqual(len(r), 0)
|
||||
|
||||
# setcurrent
|
||||
r = self.a.G.edge[s][t]["setcurrent"]
|
||||
self.assertEqual(len(r), 0)
|
||||
|
||||
def test_040_two_entrypoint(self):
|
||||
"""DTA: 2 entrypoints, only one by type_transition."""
|
||||
|
||||
s = "trans2"
|
||||
t = "trans3"
|
||||
e = ["trans3_exec1", "trans3_exec2"]
|
||||
|
||||
# regular transition
|
||||
r = self.a.G.edge[s][t]["transition"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, t)
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertSetEqual(set(["transition"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# setexec perms
|
||||
r = self.a.G.edge[s][t]["setexec"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, s)
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertSetEqual(set(["setexec"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# exec perms
|
||||
k = self.a.G.edge[s][t]["execute"].keys()
|
||||
self.assertEqual(k, e)
|
||||
|
||||
r = self.a.G.edge[s][t]["execute"][e[0]]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, e[0])
|
||||
self.assertEqual(r[0].tclass, "file")
|
||||
self.assertSetEqual(set(["execute"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = self.a.G.edge[s][t]["execute"][e[1]]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, e[1])
|
||||
self.assertEqual(r[0].tclass, "file")
|
||||
self.assertSetEqual(set(["execute"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# entrypoint perms
|
||||
k = self.a.G.edge[s][t]["entrypoint"].keys()
|
||||
self.assertEqual(k, e)
|
||||
|
||||
r = self.a.G.edge[s][t]["entrypoint"][e[0]]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, t)
|
||||
self.assertEqual(r[0].target, e[0])
|
||||
self.assertEqual(r[0].tclass, "file")
|
||||
self.assertSetEqual(set(["entrypoint"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = self.a.G.edge[s][t]["entrypoint"][e[1]]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, t)
|
||||
self.assertEqual(r[0].target, e[1])
|
||||
self.assertEqual(r[0].tclass, "file")
|
||||
self.assertSetEqual(set(["entrypoint"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# type_transition
|
||||
k = self.a.G.edge[s][t]["type_transition"].keys()
|
||||
self.assertEqual(k, [e[0]])
|
||||
|
||||
r = self.a.G.edge[s][t]["type_transition"][e[0]]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "type_transition")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, e[0])
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertEqual(r[0].default, t)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# dynamic transition
|
||||
r = self.a.G.edge[s][t]["dyntransition"]
|
||||
self.assertEqual(len(r), 0)
|
||||
|
||||
# setcurrent
|
||||
r = self.a.G.edge[s][t]["setcurrent"]
|
||||
self.assertEqual(len(r), 0)
|
||||
|
||||
def test_050_cond_type_trans(self):
|
||||
"""DTA: conditional type_transition."""
|
||||
|
||||
s = "trans3"
|
||||
t = "trans5"
|
||||
e = "trans5_exec"
|
||||
|
||||
# regular transition
|
||||
r = self.a.G.edge[s][t]["transition"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, t)
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertSetEqual(set(["transition"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# setexec perms
|
||||
r = self.a.G.edge[s][t]["setexec"]
|
||||
self.assertEqual(len(r), 0)
|
||||
|
||||
# exec perms
|
||||
k = self.a.G.edge[s][t]["execute"].keys()
|
||||
self.assertEqual(k, [e])
|
||||
|
||||
r = self.a.G.edge[s][t]["execute"][e]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, e)
|
||||
self.assertEqual(r[0].tclass, "file")
|
||||
self.assertSetEqual(set(["execute"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# entrypoint perms
|
||||
k = self.a.G.edge[s][t]["entrypoint"].keys()
|
||||
self.assertEqual(k, [e])
|
||||
|
||||
r = self.a.G.edge[s][t]["entrypoint"][e]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, t)
|
||||
self.assertEqual(r[0].target, e)
|
||||
self.assertEqual(r[0].tclass, "file")
|
||||
self.assertSetEqual(set(["entrypoint"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# type_transition
|
||||
k = self.a.G.edge[s][t]["type_transition"].keys()
|
||||
self.assertEqual(k, [e])
|
||||
|
||||
r = self.a.G.edge[s][t]["type_transition"][e]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "type_transition")
|
||||
self.assertEqual(r[0].source, s)
|
||||
self.assertEqual(r[0].target, e)
|
||||
self.assertEqual(r[0].tclass, "process")
|
||||
self.assertEqual(r[0].default, t)
|
||||
self.assertEqual(r[0].conditional, "trans5")
|
||||
|
||||
# dynamic transition
|
||||
r = self.a.G.edge[s][t]["dyntransition"]
|
||||
self.assertEqual(len(r), 0)
|
||||
|
||||
# setcurrent
|
||||
r = self.a.G.edge[s][t]["setcurrent"]
|
||||
self.assertEqual(len(r), 0)
|
186
tests/infoflow.conf
Normal file
186
tests/infoflow.conf
Normal file
@ -0,0 +1,186 @@
|
||||
class infoflow
|
||||
class infoflow2
|
||||
class infoflow3
|
||||
class file
|
||||
class process
|
||||
|
||||
sid kernel
|
||||
sid security
|
||||
|
||||
common infoflow
|
||||
{
|
||||
low_w
|
||||
med_w
|
||||
hi_w
|
||||
low_r
|
||||
med_r
|
||||
hi_r
|
||||
}
|
||||
|
||||
class infoflow
|
||||
inherits infoflow
|
||||
|
||||
class infoflow2
|
||||
inherits infoflow
|
||||
{
|
||||
super
|
||||
}
|
||||
|
||||
class infoflow3
|
||||
{
|
||||
null
|
||||
}
|
||||
|
||||
class file
|
||||
{
|
||||
execute
|
||||
entrypoint
|
||||
}
|
||||
|
||||
class process
|
||||
{
|
||||
transition
|
||||
}
|
||||
|
||||
sensitivity low_s;
|
||||
sensitivity medium_s alias med;
|
||||
sensitivity high_s;
|
||||
|
||||
dominance { low_s med high_s }
|
||||
|
||||
category here;
|
||||
category there;
|
||||
category elsewhere alias lost;
|
||||
|
||||
#level decl
|
||||
level low_s:here.there;
|
||||
level med:here, elsewhere;
|
||||
level high_s:here.lost;
|
||||
|
||||
#some constraints
|
||||
mlsconstrain infoflow hi_r ((l1 dom l2) or (t1 == mls_exempt));
|
||||
|
||||
attribute mls_exempt;
|
||||
|
||||
type system;
|
||||
role system;
|
||||
role system types system;
|
||||
|
||||
################################################################################
|
||||
# Note: these tests should be to determine if the graph
|
||||
# is being constructed correctly. It is assumed that the
|
||||
# graph algorithms being used are correct, as they are
|
||||
# unit tested by the NetworkX project itself.
|
||||
#
|
||||
#
|
||||
# Max steps for all flows: 6
|
||||
#
|
||||
# Graph if min weight is 8
|
||||
#
|
||||
#
|
||||
# 4 -> 6 -> 7 d1 <-> d2
|
||||
# ^
|
||||
# 1 -> 2-/
|
||||
#
|
||||
# 3 5 -> 8 <-> 9
|
||||
#
|
||||
# Graph if min weight is 3
|
||||
#
|
||||
#
|
||||
# 4 -> 6 -> 7 d1 <-> d2
|
||||
# ^ |
|
||||
# 1 -> 2-/ |
|
||||
# \ v
|
||||
# -> 3 5 -> 8 <-> 9
|
||||
#
|
||||
# Graph if min weight is 1
|
||||
#
|
||||
#
|
||||
# 4 -> 6 -> 7 d1 <-> d2
|
||||
# ^ |
|
||||
# 1 -> 2-/ |
|
||||
# \ v
|
||||
# -> 3 -> 5 -> 8 <-> 9
|
||||
#
|
||||
#
|
||||
#
|
||||
attribute allnodes;
|
||||
type node1, allnodes;
|
||||
type node2, allnodes;
|
||||
type node3, allnodes;
|
||||
type node4, allnodes;
|
||||
type node5, allnodes;
|
||||
type node6, allnodes;
|
||||
type node7, allnodes;
|
||||
type node8, allnodes;
|
||||
type node9, allnodes;
|
||||
|
||||
# no infoflow
|
||||
allow allnodes allnodes:infoflow3 null;
|
||||
|
||||
# 1->2 (10, 5)
|
||||
allow node1 node2:infoflow med_w;
|
||||
allow node2 node1:infoflow hi_r;
|
||||
|
||||
# 1->3 (5, 1)
|
||||
allow node3 node1:infoflow { low_r med_r };
|
||||
|
||||
# 2->4 (10)
|
||||
allow node2 node4:infoflow hi_w;
|
||||
|
||||
# 3->5 (1)
|
||||
allow node5 node3:infoflow low_r;
|
||||
|
||||
# 4->6 (10)
|
||||
allow node4 node6:infoflow2 hi_w;
|
||||
|
||||
# 6->5 (5)
|
||||
allow node5 node6:infoflow med_r;
|
||||
|
||||
# 6->7 (10)
|
||||
allow node6 node7:infoflow hi_w;
|
||||
|
||||
# 5->8 (10)
|
||||
allow node5 node8:infoflow2 hi_w;
|
||||
|
||||
# 8 <-> 9 (10)
|
||||
allow node8 node9:infoflow2 super;
|
||||
|
||||
|
||||
# disconnected from the main graph
|
||||
# for testing the handling of no
|
||||
# paths.
|
||||
type disconnected1;
|
||||
type disconnected2;
|
||||
allow disconnected1 disconnected2:infoflow2 super;
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
#users
|
||||
user system roles system level med range low_s - high_s:here.lost;
|
||||
|
||||
#normal constraints
|
||||
constrain infoflow hi_w (u1 == u2);
|
||||
|
||||
#isids
|
||||
sid kernel system:system:system:medium_s:here
|
||||
sid security system:system:system:high_s:lost
|
||||
|
||||
#fs_use
|
||||
fs_use_trans devpts system:object_r:system:low_s;
|
||||
fs_use_xattr ext3 system:object_r:system:low_s;
|
||||
fs_use_task pipefs system:object_r:system:low_s;
|
||||
|
||||
#genfscon
|
||||
genfscon proc / system:object_r:system:med
|
||||
genfscon proc /sys system:object_r:system:low_s
|
||||
genfscon selinuxfs / system:object_r:system:high_s:here.there
|
||||
|
||||
portcon tcp 80 system:object_r:system:low_s
|
||||
|
||||
netifcon eth0 system:object_r:system:low_s system:object_r:system:low_s
|
||||
|
||||
nodecon 127.0.0.1 255.255.255.255 system:object_r:system:low_s:here
|
||||
nodecon ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff system:object_r:system:low_s:here
|
||||
|
405
tests/infoflow.py
Normal file
405
tests/infoflow.py
Normal file
@ -0,0 +1,405 @@
|
||||
# Copyright 2014, 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/>.
|
||||
#
|
||||
import unittest
|
||||
|
||||
import networkx as nx
|
||||
|
||||
from libapol import SELinuxPolicy
|
||||
from libapol.infoflow import InfoFlowAnalysis
|
||||
from libapol.permmap import PermissionMap
|
||||
from libapol.policyrep.rule import RuleNotConditional
|
||||
|
||||
|
||||
class InfoFlowAnalysisTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.p = SELinuxPolicy("tests/infoflow.conf")
|
||||
self.m = PermissionMap("tests/perm_map")
|
||||
|
||||
def test_001_no_minimum(self):
|
||||
"""Information flow analysis with no minimum weight."""
|
||||
|
||||
a = InfoFlowAnalysis(self.p, self.m)
|
||||
a._build_graph()
|
||||
|
||||
nodes = sorted(list(a.G.nodes_iter()))
|
||||
self.assertListEqual(nodes, ["disconnected1", "disconnected2", "node1",
|
||||
"node2", "node3", "node4", "node5",
|
||||
"node6", "node7", "node8", "node9"])
|
||||
|
||||
edges = sorted(list(a.G.out_edges_iter()))
|
||||
self.assertListEqual(edges, [("disconnected1", "disconnected2"),
|
||||
("disconnected2", "disconnected1"),
|
||||
("node1", "node2"),
|
||||
("node1", "node3"),
|
||||
("node2", "node4"),
|
||||
("node3", "node5"),
|
||||
("node4", "node6"),
|
||||
("node5", "node8"),
|
||||
("node6", "node5"),
|
||||
("node6", "node7"),
|
||||
("node8", "node9"),
|
||||
("node9", "node8")])
|
||||
|
||||
r = a.G.edge["disconnected1"]["disconnected2"]["rules"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "disconnected1")
|
||||
self.assertEqual(r[0].target, "disconnected2")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = a.G.edge["disconnected2"]["disconnected1"]["rules"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "disconnected1")
|
||||
self.assertEqual(r[0].target, "disconnected2")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node1"]["node2"]["rules"])
|
||||
self.assertEqual(len(r), 2)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node1")
|
||||
self.assertEqual(r[0].target, "node2")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["med_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
self.assertEqual(r[1].ruletype, "allow")
|
||||
self.assertEqual(r[1].source, "node2")
|
||||
self.assertEqual(r[1].target, "node1")
|
||||
self.assertEqual(r[1].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_r"]), r[1].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[1], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node1"]["node3"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node3")
|
||||
self.assertEqual(r[0].target, "node1")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["low_r", "med_r"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node2"]["node4"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node2")
|
||||
self.assertEqual(r[0].target, "node4")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node3"]["node5"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node5")
|
||||
self.assertEqual(r[0].target, "node3")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["low_r"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node4"]["node6"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node4")
|
||||
self.assertEqual(r[0].target, "node6")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node5"]["node8"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node5")
|
||||
self.assertEqual(r[0].target, "node8")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node6"]["node5"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node5")
|
||||
self.assertEqual(r[0].target, "node6")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["med_r"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node6"]["node7"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node6")
|
||||
self.assertEqual(r[0].target, "node7")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node8"]["node9"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node8")
|
||||
self.assertEqual(r[0].target, "node9")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node9"]["node8"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node8")
|
||||
self.assertEqual(r[0].target, "node9")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
def test_100_minimum_3(self):
|
||||
"""Information flow analysis with minimum weight 3."""
|
||||
|
||||
a = InfoFlowAnalysis(self.p, self.m, minweight=3)
|
||||
a._build_graph()
|
||||
|
||||
nodes = sorted(list(a.G.nodes_iter()))
|
||||
self.assertListEqual(nodes, ["disconnected1", "disconnected2", "node1",
|
||||
"node2", "node3", "node4", "node5",
|
||||
"node6", "node7", "node8", "node9"])
|
||||
|
||||
edges = sorted(list(a.G.out_edges_iter()))
|
||||
self.assertListEqual(edges, [("disconnected1", "disconnected2"),
|
||||
("disconnected2", "disconnected1"),
|
||||
("node1", "node2"),
|
||||
("node1", "node3"),
|
||||
("node2", "node4"),
|
||||
("node4", "node6"),
|
||||
("node5", "node8"),
|
||||
("node6", "node5"),
|
||||
("node6", "node7"),
|
||||
("node8", "node9"),
|
||||
("node9", "node8")])
|
||||
|
||||
r = a.G.edge["disconnected1"]["disconnected2"]["rules"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "disconnected1")
|
||||
self.assertEqual(r[0].target, "disconnected2")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = a.G.edge["disconnected2"]["disconnected1"]["rules"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "disconnected1")
|
||||
self.assertEqual(r[0].target, "disconnected2")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node1"]["node2"]["rules"])
|
||||
self.assertEqual(len(r), 2)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node1")
|
||||
self.assertEqual(r[0].target, "node2")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["med_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
self.assertEqual(r[1].ruletype, "allow")
|
||||
self.assertEqual(r[1].source, "node2")
|
||||
self.assertEqual(r[1].target, "node1")
|
||||
self.assertEqual(r[1].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_r"]), r[1].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[1], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node1"]["node3"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node3")
|
||||
self.assertEqual(r[0].target, "node1")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["low_r", "med_r"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node2"]["node4"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node2")
|
||||
self.assertEqual(r[0].target, "node4")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node4"]["node6"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node4")
|
||||
self.assertEqual(r[0].target, "node6")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node5"]["node8"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node5")
|
||||
self.assertEqual(r[0].target, "node8")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node6"]["node5"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node5")
|
||||
self.assertEqual(r[0].target, "node6")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["med_r"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node6"]["node7"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node6")
|
||||
self.assertEqual(r[0].target, "node7")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node8"]["node9"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node8")
|
||||
self.assertEqual(r[0].target, "node9")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node9"]["node8"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node8")
|
||||
self.assertEqual(r[0].target, "node9")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
def test_200_minimum_8(self):
|
||||
"""Information flow analysis with minimum weight 8."""
|
||||
|
||||
a = InfoFlowAnalysis(self.p, self.m, minweight=8)
|
||||
a._build_graph()
|
||||
|
||||
nodes = sorted(list(a.G.nodes_iter()))
|
||||
self.assertListEqual(nodes, ["disconnected1", "disconnected2", "node1",
|
||||
"node2", "node4", "node5",
|
||||
"node6", "node7", "node8", "node9"])
|
||||
|
||||
edges = sorted(list(a.G.out_edges_iter()))
|
||||
self.assertListEqual(edges, [("disconnected1", "disconnected2"),
|
||||
("disconnected2", "disconnected1"),
|
||||
("node1", "node2"),
|
||||
("node2", "node4"),
|
||||
("node4", "node6"),
|
||||
("node5", "node8"),
|
||||
("node6", "node7"),
|
||||
("node8", "node9"),
|
||||
("node9", "node8")])
|
||||
|
||||
r = a.G.edge["disconnected1"]["disconnected2"]["rules"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "disconnected1")
|
||||
self.assertEqual(r[0].target, "disconnected2")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = a.G.edge["disconnected2"]["disconnected1"]["rules"]
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "disconnected1")
|
||||
self.assertEqual(r[0].target, "disconnected2")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node1"]["node2"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node2")
|
||||
self.assertEqual(r[0].target, "node1")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_r"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node2"]["node4"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node2")
|
||||
self.assertEqual(r[0].target, "node4")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node4"]["node6"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node4")
|
||||
self.assertEqual(r[0].target, "node6")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node5"]["node8"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node5")
|
||||
self.assertEqual(r[0].target, "node8")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node6"]["node7"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node6")
|
||||
self.assertEqual(r[0].target, "node7")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node8"]["node9"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node8")
|
||||
self.assertEqual(r[0].target, "node9")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
r = sorted(a.G.edge["node9"]["node8"]["rules"])
|
||||
self.assertEqual(len(r), 1)
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "node8")
|
||||
self.assertEqual(r[0].target, "node9")
|
||||
self.assertEqual(r[0].tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
28
tests/perm_map
Normal file
28
tests/perm_map
Normal file
@ -0,0 +1,28 @@
|
||||
5
|
||||
|
||||
class infoflow 6
|
||||
low_w w 1
|
||||
med_w w 5
|
||||
hi_w w 10
|
||||
low_r r 1
|
||||
med_r r 5
|
||||
hi_r r 10
|
||||
|
||||
class infoflow2 7
|
||||
low_w w 1
|
||||
med_w w 5
|
||||
hi_w w 10
|
||||
low_r r 1
|
||||
med_r r 5
|
||||
hi_r r 10
|
||||
super b 10
|
||||
|
||||
class infoflow3 1
|
||||
null n 1
|
||||
|
||||
class file 2
|
||||
execute r 10
|
||||
entrypoint r 10
|
||||
|
||||
class process 1
|
||||
transition w 10
|
130
tests/polcapquery.conf
Normal file
130
tests/polcapquery.conf
Normal file
@ -0,0 +1,130 @@
|
||||
class infoflow
|
||||
class infoflow2
|
||||
class infoflow3
|
||||
class infoflow4
|
||||
class infoflow5
|
||||
class infoflow6
|
||||
class infoflow7
|
||||
|
||||
sid kernel
|
||||
sid security
|
||||
|
||||
common infoflow
|
||||
{
|
||||
low_w
|
||||
med_w
|
||||
hi_w
|
||||
low_r
|
||||
med_r
|
||||
hi_r
|
||||
}
|
||||
|
||||
class infoflow
|
||||
inherits infoflow
|
||||
|
||||
class infoflow2
|
||||
inherits infoflow
|
||||
{
|
||||
super_w
|
||||
super_r
|
||||
}
|
||||
|
||||
class infoflow3
|
||||
{
|
||||
null
|
||||
}
|
||||
|
||||
class infoflow4
|
||||
inherits infoflow
|
||||
|
||||
class infoflow5
|
||||
inherits infoflow
|
||||
|
||||
class infoflow6
|
||||
inherits infoflow
|
||||
|
||||
class infoflow7
|
||||
inherits infoflow
|
||||
{
|
||||
super_w
|
||||
super_r
|
||||
super_none
|
||||
super_both
|
||||
super_unmapped
|
||||
}
|
||||
|
||||
sensitivity low_s;
|
||||
sensitivity medium_s alias med;
|
||||
sensitivity high_s;
|
||||
|
||||
dominance { low_s med high_s }
|
||||
|
||||
category here;
|
||||
category there;
|
||||
category elsewhere alias lost;
|
||||
|
||||
#level decl
|
||||
level low_s:here.there;
|
||||
level med:here, elsewhere;
|
||||
level high_s:here.lost;
|
||||
|
||||
#some constraints
|
||||
mlsconstrain infoflow hi_r ((l1 dom l2) or (t1 == mls_exempt));
|
||||
|
||||
attribute mls_exempt;
|
||||
|
||||
type system;
|
||||
role system;
|
||||
role system types system;
|
||||
|
||||
################################################################################
|
||||
# Type enforcement declarations and rules
|
||||
|
||||
########################################
|
||||
#
|
||||
# Policy capabilities query
|
||||
#
|
||||
|
||||
# this is not as good of a test suite since
|
||||
# the capabilities can only have specific names.
|
||||
|
||||
# test 1
|
||||
# name: open_perms
|
||||
policycap open_perms;
|
||||
|
||||
# test 2
|
||||
# name: pe?er regex
|
||||
# default: unset
|
||||
# note this should get both capabilities.
|
||||
policycap network_peer_controls;
|
||||
|
||||
|
||||
################################################################################
|
||||
|
||||
#users
|
||||
user system roles system level med range low_s - high_s:here.lost;
|
||||
|
||||
#normal constraints
|
||||
constrain infoflow hi_w (u1 == u2);
|
||||
|
||||
#isids
|
||||
sid kernel system:system:system:medium_s:here
|
||||
sid security system:system:system:high_s:lost
|
||||
|
||||
#fs_use
|
||||
fs_use_trans devpts system:object_r:system:low_s;
|
||||
fs_use_xattr ext3 system:object_r:system:low_s;
|
||||
fs_use_task pipefs system:object_r:system:low_s;
|
||||
|
||||
#genfscon
|
||||
genfscon proc / system:object_r:system:med
|
||||
genfscon proc /sys system:object_r:system:low_s
|
||||
genfscon selinuxfs / system:object_r:system:high_s:here.there
|
||||
|
||||
portcon tcp 80 system:object_r:system:low_s
|
||||
|
||||
netifcon eth0 system:object_r:system:low_s system:object_r:system:low_s
|
||||
|
||||
nodecon 127.0.0.1 255.255.255.255 system:object_r:system:low_s:here
|
||||
nodecon ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff system:object_r:system:low_s:here
|
||||
|
63
tests/polcapquery.py
Normal file
63
tests/polcapquery.py
Normal file
@ -0,0 +1,63 @@
|
||||
# Copyright 2014, 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/>.
|
||||
#
|
||||
import unittest
|
||||
|
||||
from libapol import SELinuxPolicy
|
||||
from libapol.polcapquery import PolCapQuery
|
||||
|
||||
|
||||
class PolCapQueryTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.p = SELinuxPolicy("tests/polcapquery.conf")
|
||||
|
||||
def test_000_unset(self):
|
||||
"""Policy capability query with no criteria"""
|
||||
# query with no parameters gets all capabilities.
|
||||
for numcaps, c in enumerate(self.p.polcaps(), start=1):
|
||||
pass
|
||||
|
||||
q = PolCapQuery(self.p)
|
||||
for q_numcaps, c in enumerate(q.results(), start=1):
|
||||
pass
|
||||
|
||||
self.assertEqual(numcaps, q_numcaps)
|
||||
|
||||
def test_001_name_exact(self):
|
||||
"""Policy capability query with exact match"""
|
||||
q = PolCapQuery(self.p, name="open_perms", name_regex=False)
|
||||
|
||||
# manually consume the generator:
|
||||
caps = q.results()
|
||||
c = caps.next()
|
||||
|
||||
self.assertEqual(c, "open_perms")
|
||||
|
||||
self.assertRaises(StopIteration, caps.next)
|
||||
|
||||
def test_002_name_regex(self):
|
||||
"""Policy capability query with regex match"""
|
||||
|
||||
q = PolCapQuery(self.p, name="pe?er", name_regex=True)
|
||||
|
||||
# manually consume the generator:
|
||||
c = sorted(q.results())
|
||||
self.assertEqual(len(c), 2)
|
||||
|
||||
self.assertEqual(c[0], "network_peer_controls")
|
||||
self.assertEqual(c[1], "open_perms")
|
316
tests/terulequery.conf
Normal file
316
tests/terulequery.conf
Normal file
@ -0,0 +1,316 @@
|
||||
class infoflow
|
||||
class infoflow2
|
||||
class infoflow3
|
||||
class infoflow4
|
||||
class infoflow5
|
||||
class infoflow6
|
||||
class infoflow7
|
||||
|
||||
sid kernel
|
||||
sid security
|
||||
|
||||
common infoflow
|
||||
{
|
||||
low_w
|
||||
med_w
|
||||
hi_w
|
||||
low_r
|
||||
med_r
|
||||
hi_r
|
||||
}
|
||||
|
||||
class infoflow
|
||||
inherits infoflow
|
||||
|
||||
class infoflow2
|
||||
inherits infoflow
|
||||
{
|
||||
super_w
|
||||
super_r
|
||||
}
|
||||
|
||||
class infoflow3
|
||||
{
|
||||
null
|
||||
}
|
||||
|
||||
class infoflow4
|
||||
inherits infoflow
|
||||
|
||||
class infoflow5
|
||||
inherits infoflow
|
||||
|
||||
class infoflow6
|
||||
inherits infoflow
|
||||
|
||||
class infoflow7
|
||||
inherits infoflow
|
||||
{
|
||||
super_w
|
||||
super_r
|
||||
super_none
|
||||
super_both
|
||||
super_unmapped
|
||||
}
|
||||
|
||||
sensitivity low_s;
|
||||
sensitivity medium_s alias med;
|
||||
sensitivity high_s;
|
||||
|
||||
dominance { low_s med high_s }
|
||||
|
||||
category here;
|
||||
category there;
|
||||
category elsewhere alias lost;
|
||||
|
||||
#level decl
|
||||
level low_s:here.there;
|
||||
level med:here, elsewhere;
|
||||
level high_s:here.lost;
|
||||
|
||||
#some constraints
|
||||
mlsconstrain infoflow hi_r ((l1 dom l2) or (t1 == mls_exempt));
|
||||
|
||||
attribute mls_exempt;
|
||||
|
||||
type system;
|
||||
role system;
|
||||
role system types system;
|
||||
|
||||
################################################################################
|
||||
# Type enforcement declarations and rules
|
||||
|
||||
########################################
|
||||
#
|
||||
# TE Rule Query
|
||||
#
|
||||
|
||||
# test 1
|
||||
# ruletype: unset
|
||||
# source: test1a, direct, no regex
|
||||
# target: unset
|
||||
# class: unset
|
||||
# perms: unset
|
||||
attribute test1a;
|
||||
type test1s, test1a;
|
||||
type test1t;
|
||||
type test1FAIL, test1a;
|
||||
allow test1a test1t:infoflow hi_w;
|
||||
allow test1FAIL self:infoflow hi_w;
|
||||
|
||||
# test 2
|
||||
# ruletype: unset
|
||||
# source: test2s, indirect, no regex
|
||||
# target: unset
|
||||
# class: unset
|
||||
# perms: unset
|
||||
attribute test2a;
|
||||
type test2s, test2a;
|
||||
type test2t;
|
||||
allow test2a test2t:infoflow hi_w;
|
||||
#allow test2s test2t:infoflow low_r;
|
||||
|
||||
# test 3
|
||||
# ruletype: unset
|
||||
# source: test3a.*, direct, regex
|
||||
# target: unset
|
||||
# class: unset
|
||||
# perms: unset
|
||||
attribute test3aS;
|
||||
attribute test3b;
|
||||
type test3s, test3aS;
|
||||
type test3t;
|
||||
type test3aFAIL, test3b;
|
||||
allow test3s test3t:infoflow hi_w;
|
||||
allow test3aS test3t:infoflow low_r;
|
||||
allow test3b test3t:infoflow med_w;
|
||||
|
||||
# test 4
|
||||
# ruletype: unset
|
||||
# source: test4(s|t), indirect, regex
|
||||
# target: unset
|
||||
# class: unset
|
||||
# perms: unset
|
||||
attribute test4a1;
|
||||
attribute test4a2;
|
||||
type test4s1, test4a1;
|
||||
type test4t1, test4a2;
|
||||
type test4FAIL;
|
||||
allow test4a1 test4a1:infoflow hi_w;
|
||||
allow test4a2 test4a2:infoflow low_r;
|
||||
allow test4FAIL self:infoflow med_w;
|
||||
|
||||
# test 5
|
||||
# ruletype: unset
|
||||
# source: unset
|
||||
# target: test5a, direct, no regex
|
||||
# class: unset
|
||||
# perms: unset
|
||||
attribute test5a;
|
||||
type test5s;
|
||||
type test5t, test5a;
|
||||
allow test5s test5a:infoflow hi_w;
|
||||
allow test5s test5t:infoflow hi_w;
|
||||
|
||||
# test 6
|
||||
# ruletype: unset
|
||||
# source: unset
|
||||
# target: test6t, indirect, no regex
|
||||
# class: unset
|
||||
# perms: unset
|
||||
attribute test6a;
|
||||
type test6s;
|
||||
type test6t, test6a;
|
||||
allow test6s test6a:infoflow hi_w;
|
||||
allow test6s test6t:infoflow low_r;
|
||||
|
||||
# test 7
|
||||
# ruletype: unset
|
||||
# source: unset
|
||||
# target: test7a.*, direct, regex
|
||||
# class: unset
|
||||
# perms: unset
|
||||
attribute test7aPASS;
|
||||
attribute test7b;
|
||||
type test7s;
|
||||
type test7t, test7aPASS;
|
||||
type test7aFAIL, test7b;
|
||||
allow test7s test7t:infoflow hi_w;
|
||||
allow test7s test7aPASS:infoflow low_r;
|
||||
allow test7s test7b:infoflow med_w;
|
||||
|
||||
# test 8
|
||||
# ruletype: unset
|
||||
# source: unset
|
||||
# target: test8(s|t), indirect, regex
|
||||
# class: unset
|
||||
# perms: unset
|
||||
attribute test8a1;
|
||||
attribute test8a2;
|
||||
type test8s1, test8a1;
|
||||
type test8t1, test8a2;
|
||||
type test8FAIL;
|
||||
allow test8a1 test8a1:infoflow hi_w;
|
||||
allow test8a2 test8a2:infoflow low_r;
|
||||
allow test8FAIL self:infoflow med_w;
|
||||
|
||||
# test 9
|
||||
# ruletype: unset
|
||||
# source: unset
|
||||
# target: unset
|
||||
# class: infoflow2, no regex
|
||||
# perms: unset
|
||||
type test9;
|
||||
allow test9 self:infoflow hi_w;
|
||||
allow test9 self:infoflow2 super_w;
|
||||
|
||||
# test 10
|
||||
# ruletype: unset
|
||||
# source: unset
|
||||
# target: unset
|
||||
# class: infoflow3,infoflow4 , no regex
|
||||
# perms: unset
|
||||
type test10;
|
||||
allow test10 self:infoflow hi_w;
|
||||
allow test10 self:infoflow4 hi_w;
|
||||
allow test10 self:infoflow3 null;
|
||||
|
||||
# test 11
|
||||
# ruletype: unset
|
||||
# source: unset
|
||||
# target: unset
|
||||
# class: infoflow(5|6), regex
|
||||
# perms: unset
|
||||
type test11;
|
||||
allow test11 self:infoflow hi_w;
|
||||
allow test11 self:infoflow5 low_w;
|
||||
allow test11 self:infoflow6 med_r;
|
||||
|
||||
# test 12
|
||||
# ruletype: unset
|
||||
# source: unset
|
||||
# target: unset
|
||||
# class: unset
|
||||
# perms: super_r, any
|
||||
type test12a;
|
||||
type test12b;
|
||||
allow test12a self:infoflow7 super_r;
|
||||
allow test12b self:infoflow7 { super_r super_none };
|
||||
|
||||
# test 13
|
||||
# ruletype: unset
|
||||
# source: unset
|
||||
# target: unset
|
||||
# class: unset
|
||||
# perms: super_w,super_none,super_both equal
|
||||
type test13a;
|
||||
type test13b;
|
||||
type test13c;
|
||||
type test13d;
|
||||
allow test13a self:infoflow7 super_w;
|
||||
allow test13b self:infoflow7 { super_w super_none };
|
||||
allow test13c self:infoflow7 { super_w super_none super_both };
|
||||
allow test13d self:infoflow7 { super_w super_none super_both super_unmapped };
|
||||
|
||||
# test 14
|
||||
# ruletype: dontaudit,auditallow
|
||||
# source: unset
|
||||
# target: unset
|
||||
# class: unset
|
||||
# perms: unset
|
||||
type test14;
|
||||
auditallow test14 self:infoflow7 super_both;
|
||||
dontaudit test14 self:infoflow7 super_unmapped;
|
||||
|
||||
|
||||
# test 100
|
||||
# ruletype: unset
|
||||
# source: unset
|
||||
# target: unset
|
||||
# class: unset
|
||||
# default: test100d
|
||||
type test100;
|
||||
type test100d;
|
||||
type_transition test100 test100:infoflow7 test100d;
|
||||
|
||||
# test 101
|
||||
# ruletype: unset
|
||||
# source: unset
|
||||
# target: unset
|
||||
# class: unset
|
||||
# default: test101.
|
||||
type test101;
|
||||
type test101d;
|
||||
type test101e;
|
||||
type_transition test101 test101d:infoflow7 test101e;
|
||||
type_transition test101 test101e:infoflow7 test101d;
|
||||
|
||||
################################################################################
|
||||
|
||||
#users
|
||||
user system roles system level med range low_s - high_s:here.lost;
|
||||
|
||||
#normal constraints
|
||||
constrain infoflow hi_w (u1 == u2);
|
||||
|
||||
#isids
|
||||
sid kernel system:system:system:medium_s:here
|
||||
sid security system:system:system:high_s:lost
|
||||
|
||||
#fs_use
|
||||
fs_use_trans devpts system:object_r:system:low_s;
|
||||
fs_use_xattr ext3 system:object_r:system:low_s;
|
||||
fs_use_task pipefs system:object_r:system:low_s;
|
||||
|
||||
#genfscon
|
||||
genfscon proc / system:object_r:system:med
|
||||
genfscon proc /sys system:object_r:system:low_s
|
||||
genfscon selinuxfs / system:object_r:system:high_s:here.there
|
||||
|
||||
portcon tcp 80 system:object_r:system:low_s
|
||||
|
||||
netifcon eth0 system:object_r:system:low_s system:object_r:system:low_s
|
||||
|
||||
nodecon 127.0.0.1 255.255.255.255 system:object_r:system:low_s:here
|
||||
nodecon ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff system:object_r:system:low_s:here
|
||||
|
379
tests/terulequery.py
Normal file
379
tests/terulequery.py
Normal file
@ -0,0 +1,379 @@
|
||||
# Copyright 2014, 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/>.
|
||||
#
|
||||
import unittest
|
||||
|
||||
from libapol import SELinuxPolicy
|
||||
from libapol.terulequery import TERuleQuery
|
||||
from libapol.policyrep.rule import RuleNotConditional
|
||||
|
||||
|
||||
class TERuleQueryTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.p = SELinuxPolicy("tests/terulequery.conf")
|
||||
|
||||
def test_000_unset(self):
|
||||
"""TE rule query with no criteria."""
|
||||
# query with no parameters gets all TE rules.
|
||||
for numrules, r in enumerate(self.p.terules(), start=1):
|
||||
pass
|
||||
|
||||
q = TERuleQuery(self.p)
|
||||
for q_numrules, r in enumerate(q.results(), start=1):
|
||||
pass
|
||||
|
||||
self.assertEqual(numrules, q_numrules)
|
||||
|
||||
def test_001_source_direct(self):
|
||||
"""TE rule query with exact, direct, source match."""
|
||||
q = TERuleQuery(
|
||||
self.p, source="test1a", source_indirect=False, source_regex=False)
|
||||
|
||||
# manually consume the generator:
|
||||
rules = q.results()
|
||||
r = rules.next()
|
||||
|
||||
self.assertEqual(r.ruletype, "allow")
|
||||
self.assertEqual(r.source, "test1a")
|
||||
self.assertEqual(r.target, "test1t")
|
||||
self.assertEqual(r.tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r.perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r, "conditional")
|
||||
|
||||
self.assertRaises(StopIteration, rules.next)
|
||||
|
||||
def test_002_source_indirect(self):
|
||||
"""TE rule query with exact, indirect, source match."""
|
||||
q = TERuleQuery(
|
||||
self.p, source="test2s", source_indirect=True, source_regex=False)
|
||||
|
||||
# manually consume the generator:
|
||||
rules = q.results()
|
||||
r = rules.next()
|
||||
|
||||
self.assertEqual(r.ruletype, "allow")
|
||||
self.assertEqual(r.source, "test2a")
|
||||
self.assertEqual(r.target, "test2t")
|
||||
self.assertEqual(r.tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r.perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r, "conditional")
|
||||
|
||||
self.assertRaises(StopIteration, rules.next)
|
||||
|
||||
def test_003_source_direct_regex(self):
|
||||
"""TE rule query with regex, direct, source match."""
|
||||
q = TERuleQuery(
|
||||
self.p, source="test3a.*", source_indirect=False, source_regex=True)
|
||||
|
||||
# manually consume the generator:
|
||||
rules = q.results()
|
||||
r = rules.next()
|
||||
|
||||
self.assertEqual(r.ruletype, "allow")
|
||||
self.assertEqual(r.source, "test3aS")
|
||||
self.assertEqual(r.target, "test3t")
|
||||
self.assertEqual(r.tclass, "infoflow")
|
||||
self.assertSetEqual(set(["low_r"]), r.perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r, "conditional")
|
||||
|
||||
self.assertRaises(StopIteration, rules.next)
|
||||
|
||||
def test_004_source_indirect_regex(self):
|
||||
"""TE rule query with regex, indirect, source match."""
|
||||
q = TERuleQuery(
|
||||
self.p, source="test4(s|t)", source_indirect=True, source_regex=True)
|
||||
|
||||
# manually consume the generator:
|
||||
r = sorted(q.results())
|
||||
self.assertEqual(len(r), 2)
|
||||
|
||||
# verify first rule
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "test4a1")
|
||||
self.assertEqual(r[0].target, "test4a1")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# verify second rule
|
||||
self.assertEqual(r[1].ruletype, "allow")
|
||||
self.assertEqual(r[1].source, "test4a2")
|
||||
self.assertEqual(r[1].target, "test4a2")
|
||||
self.assertEqual(r[1].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["low_r"]), r[1].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[1], "conditional")
|
||||
|
||||
def test_005_target_direct(self):
|
||||
"""TE rule query with exact, direct, target match."""
|
||||
q = TERuleQuery(
|
||||
self.p, target="test5a", target_indirect=False, target_regex=False)
|
||||
|
||||
# manually consume the generator:
|
||||
rules = q.results()
|
||||
r = rules.next()
|
||||
|
||||
self.assertEqual(r.ruletype, "allow")
|
||||
self.assertEqual(r.source, "test5s")
|
||||
self.assertEqual(r.target, "test5a")
|
||||
self.assertEqual(r.tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r.perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r, "conditional")
|
||||
|
||||
self.assertRaises(StopIteration, rules.next)
|
||||
|
||||
def test_006_target_indirect(self):
|
||||
"""TE rule query with exact, indirect, target match."""
|
||||
q = TERuleQuery(
|
||||
self.p, target="test6t", target_indirect=True, target_regex=False)
|
||||
|
||||
# manually consume the generator:
|
||||
r = sorted(q.results())
|
||||
self.assertEqual(len(r), 2)
|
||||
|
||||
# verify first rule
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "test6s")
|
||||
self.assertEqual(r[0].target, "test6a")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# verify second rule
|
||||
self.assertEqual(r[1].ruletype, "allow")
|
||||
self.assertEqual(r[1].source, "test6s")
|
||||
self.assertEqual(r[1].target, "test6t")
|
||||
self.assertEqual(r[1].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["low_r"]), r[1].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[1], "conditional")
|
||||
|
||||
def test_007_target_direct_regex(self):
|
||||
"""TE rule query with regex, direct, target match."""
|
||||
q = TERuleQuery(
|
||||
self.p, target="test7a.*", target_indirect=False, target_regex=True)
|
||||
|
||||
# manually consume the generator:
|
||||
rules = q.results()
|
||||
r = rules.next()
|
||||
|
||||
self.assertEqual(r.ruletype, "allow")
|
||||
self.assertEqual(r.source, "test7s")
|
||||
self.assertEqual(r.target, "test7aPASS")
|
||||
self.assertEqual(r.tclass, "infoflow")
|
||||
self.assertSetEqual(set(["low_r"]), r.perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r, "conditional")
|
||||
|
||||
self.assertRaises(StopIteration, rules.next)
|
||||
|
||||
def test_008_target_indirect_regex(self):
|
||||
"""TE rule query with regex, indirect, target match."""
|
||||
q = TERuleQuery(
|
||||
self.p, target="test8(s|t)", target_indirect=True, target_regex=True)
|
||||
|
||||
# manually consume the generator:
|
||||
r = sorted(q.results())
|
||||
self.assertEqual(len(r), 2)
|
||||
|
||||
# verify first rule
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "test8a1")
|
||||
self.assertEqual(r[0].target, "test8a1")
|
||||
self.assertEqual(r[0].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["hi_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# verify second rule
|
||||
self.assertEqual(r[1].ruletype, "allow")
|
||||
self.assertEqual(r[1].source, "test8a2")
|
||||
self.assertEqual(r[1].target, "test8a2")
|
||||
self.assertEqual(r[1].tclass, "infoflow")
|
||||
self.assertSetEqual(set(["low_r"]), r[1].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[1], "conditional")
|
||||
|
||||
def test_009_class(self):
|
||||
"""TE rule query with exact object class match."""
|
||||
q = TERuleQuery(self.p, tclass="infoflow2", tclass_regex=False)
|
||||
|
||||
# manually consume the generator:
|
||||
rules = q.results()
|
||||
r = rules.next()
|
||||
|
||||
self.assertEqual(r.ruletype, "allow")
|
||||
self.assertEqual(r.source, "test9")
|
||||
self.assertEqual(r.target, "test9")
|
||||
self.assertEqual(r.tclass, "infoflow2")
|
||||
self.assertSetEqual(set(["super_w"]), r.perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r, "conditional")
|
||||
|
||||
self.assertRaises(StopIteration, rules.next)
|
||||
|
||||
def test_010_class_list(self):
|
||||
"""TE rule query with object class list match."""
|
||||
q = TERuleQuery(
|
||||
self.p, tclass=["infoflow3", "infoflow4"], tclass_regex=False)
|
||||
|
||||
# manually consume the generator:
|
||||
r = sorted(q.results())
|
||||
self.assertEqual(len(r), 2)
|
||||
|
||||
# verify first rule
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "test10")
|
||||
self.assertEqual(r[0].target, "test10")
|
||||
self.assertEqual(r[0].tclass, "infoflow3")
|
||||
self.assertSetEqual(set(["null"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# verify second rule
|
||||
self.assertEqual(r[1].ruletype, "allow")
|
||||
self.assertEqual(r[1].source, "test10")
|
||||
self.assertEqual(r[1].target, "test10")
|
||||
self.assertEqual(r[1].tclass, "infoflow4")
|
||||
self.assertSetEqual(set(["hi_w"]), r[1].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[1], "conditional")
|
||||
|
||||
def test_011_class_regex(self):
|
||||
"""TE rule query with object class regex match."""
|
||||
q = TERuleQuery(self.p, tclass="infoflow(5|6)", tclass_regex=True)
|
||||
|
||||
# manually consume the generator:
|
||||
r = sorted(q.results())
|
||||
self.assertEqual(len(r), 2)
|
||||
|
||||
# verify first rule
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "test11")
|
||||
self.assertEqual(r[0].target, "test11")
|
||||
self.assertEqual(r[0].tclass, "infoflow5")
|
||||
self.assertSetEqual(set(["low_w"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# verify second rule
|
||||
self.assertEqual(r[1].ruletype, "allow")
|
||||
self.assertEqual(r[1].source, "test11")
|
||||
self.assertEqual(r[1].target, "test11")
|
||||
self.assertEqual(r[1].tclass, "infoflow6")
|
||||
self.assertSetEqual(set(["med_r"]), r[1].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[1], "conditional")
|
||||
|
||||
def test_012_perms_any(self):
|
||||
"""TE rule query with permission set intersection."""
|
||||
q = TERuleQuery(self.p, perms=["super_r"], perms_equal=False)
|
||||
|
||||
# manually consume the generator:
|
||||
r = sorted(q.results())
|
||||
self.assertEqual(len(r), 2)
|
||||
|
||||
# verify first rule
|
||||
self.assertEqual(r[0].ruletype, "allow")
|
||||
self.assertEqual(r[0].source, "test12a")
|
||||
self.assertEqual(r[0].target, "test12a")
|
||||
self.assertEqual(r[0].tclass, "infoflow7")
|
||||
self.assertSetEqual(set(["super_r"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# verify second rule
|
||||
self.assertEqual(r[1].ruletype, "allow")
|
||||
self.assertEqual(r[1].source, "test12b")
|
||||
self.assertEqual(r[1].target, "test12b")
|
||||
self.assertEqual(r[1].tclass, "infoflow7")
|
||||
self.assertSetEqual(set(["super_r", "super_none"]), r[1].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[1], "conditional")
|
||||
|
||||
def test_013_perms_equal(self):
|
||||
"""TE rule query with permission set equality."""
|
||||
q = TERuleQuery(
|
||||
self.p, perms=["super_w", "super_none", "super_both"], perms_equal=True)
|
||||
|
||||
# manually consume the generator:
|
||||
rules = q.results()
|
||||
r = rules.next()
|
||||
|
||||
self.assertEqual(r.ruletype, "allow")
|
||||
self.assertEqual(r.source, "test13c")
|
||||
self.assertEqual(r.target, "test13c")
|
||||
self.assertEqual(r.tclass, "infoflow7")
|
||||
self.assertSetEqual(
|
||||
set(["super_w", "super_none", "super_both"]), r.perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r, "conditional")
|
||||
|
||||
self.assertRaises(StopIteration, rules.next)
|
||||
|
||||
def test_014_ruletype(self):
|
||||
"""TE rule query with rule type match."""
|
||||
q = TERuleQuery(self.p, ruletype=["auditallow", "dontaudit"])
|
||||
|
||||
# manually consume the generator:
|
||||
r = sorted(q.results())
|
||||
self.assertEqual(len(r), 2)
|
||||
|
||||
# verify first rule
|
||||
self.assertEqual(r[0].ruletype, "auditallow")
|
||||
self.assertEqual(r[0].source, "test14")
|
||||
self.assertEqual(r[0].target, "test14")
|
||||
self.assertEqual(r[0].tclass, "infoflow7")
|
||||
self.assertSetEqual(set(["super_both"]), r[0].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# verify second rule
|
||||
self.assertEqual(r[1].ruletype, "dontaudit")
|
||||
self.assertEqual(r[1].source, "test14")
|
||||
self.assertEqual(r[1].target, "test14")
|
||||
self.assertEqual(r[1].tclass, "infoflow7")
|
||||
self.assertSetEqual(set(["super_unmapped"]), r[1].perms)
|
||||
self.assertRaises(RuleNotConditional, getattr, r[1], "conditional")
|
||||
|
||||
def test_100_default(self):
|
||||
"""TE rule query with default type exact match."""
|
||||
q = TERuleQuery(self.p, default="test100d", default_regex=False)
|
||||
|
||||
# manually consume the generator:
|
||||
rules = q.results()
|
||||
r = rules.next()
|
||||
|
||||
self.assertEqual(r.ruletype, "type_transition")
|
||||
self.assertEqual(r.source, "test100")
|
||||
self.assertEqual(r.target, "test100")
|
||||
self.assertEqual(r.tclass, "infoflow7")
|
||||
self.assertEqual(r.default, "test100d")
|
||||
self.assertRaises(RuleNotConditional, getattr, r, "conditional")
|
||||
|
||||
self.assertRaises(StopIteration, rules.next)
|
||||
|
||||
def test_101_default_regex(self):
|
||||
"""TE rule query with default type regex match."""
|
||||
q = TERuleQuery(self.p, default="test101.", default_regex=True)
|
||||
|
||||
# manually consume the generator:
|
||||
r = sorted(q.results())
|
||||
self.assertEqual(len(r), 2)
|
||||
|
||||
# verify first rule
|
||||
self.assertEqual(r[0].ruletype, "type_transition")
|
||||
self.assertEqual(r[0].source, "test101")
|
||||
self.assertEqual(r[0].target, "test101d")
|
||||
self.assertEqual(r[0].tclass, "infoflow7")
|
||||
self.assertEqual(r[0].default, "test101e")
|
||||
self.assertRaises(RuleNotConditional, getattr, r[0], "conditional")
|
||||
|
||||
# verify second rule
|
||||
self.assertEqual(r[1].ruletype, "type_transition")
|
||||
self.assertEqual(r[1].source, "test101")
|
||||
self.assertEqual(r[1].target, "test101e")
|
||||
self.assertEqual(r[1].tclass, "infoflow7")
|
||||
self.assertEqual(r[1].default, "test101d")
|
||||
self.assertRaises(RuleNotConditional, getattr, r[1], "conditional")
|
189
tests/typequery.conf
Normal file
189
tests/typequery.conf
Normal file
@ -0,0 +1,189 @@
|
||||
class infoflow
|
||||
class infoflow2
|
||||
class infoflow3
|
||||
class infoflow4
|
||||
class infoflow5
|
||||
class infoflow6
|
||||
class infoflow7
|
||||
|
||||
sid kernel
|
||||
sid security
|
||||
|
||||
common infoflow
|
||||
{
|
||||
low_w
|
||||
med_w
|
||||
hi_w
|
||||
low_r
|
||||
med_r
|
||||
hi_r
|
||||
}
|
||||
|
||||
class infoflow
|
||||
inherits infoflow
|
||||
|
||||
class infoflow2
|
||||
inherits infoflow
|
||||
{
|
||||
super_w
|
||||
super_r
|
||||
}
|
||||
|
||||
class infoflow3
|
||||
{
|
||||
null
|
||||
}
|
||||
|
||||
class infoflow4
|
||||
inherits infoflow
|
||||
|
||||
class infoflow5
|
||||
inherits infoflow
|
||||
|
||||
class infoflow6
|
||||
inherits infoflow
|
||||
|
||||
class infoflow7
|
||||
inherits infoflow
|
||||
{
|
||||
super_w
|
||||
super_r
|
||||
super_none
|
||||
super_both
|
||||
super_unmapped
|
||||
}
|
||||
|
||||
sensitivity low_s;
|
||||
sensitivity medium_s alias med;
|
||||
sensitivity high_s;
|
||||
|
||||
dominance { low_s med high_s }
|
||||
|
||||
category here;
|
||||
category there;
|
||||
category elsewhere alias lost;
|
||||
|
||||
#level decl
|
||||
level low_s:here.there;
|
||||
level med:here, elsewhere;
|
||||
level high_s:here.lost;
|
||||
|
||||
#some constraints
|
||||
mlsconstrain infoflow hi_r ((l1 dom l2) or (t1 == mls_exempt));
|
||||
|
||||
attribute mls_exempt;
|
||||
|
||||
type system;
|
||||
role system;
|
||||
role system types system;
|
||||
|
||||
################################################################################
|
||||
# Type enforcement declarations and rules
|
||||
|
||||
########################################
|
||||
#
|
||||
# Type Query
|
||||
#
|
||||
|
||||
# test 1
|
||||
# name: test1
|
||||
# attrs: unset
|
||||
# alias: unset
|
||||
type test1;
|
||||
|
||||
# test 2
|
||||
# name: test2(a|b) regex
|
||||
# attrs: unset
|
||||
# alias: unset
|
||||
type test2a;
|
||||
type test2b;
|
||||
|
||||
# test 10
|
||||
# name: unset
|
||||
# attrs: test10a,test10b
|
||||
# alias: unset
|
||||
attribute test10a;
|
||||
attribute test10b;
|
||||
attribute test10c;
|
||||
type test10t1, test10a;
|
||||
type test10t2, test10a, test10b;
|
||||
type test10t3, test10a, test10b, test10c;
|
||||
type test10t4, test10b, test10c;
|
||||
type test10t5, test10a, test10c;
|
||||
type test10t6, test10b;
|
||||
type test10t7, test10c;
|
||||
|
||||
# test 11
|
||||
# name: unset
|
||||
# attrs: test11a,test11b equal
|
||||
# alias: unset
|
||||
attribute test11a;
|
||||
attribute test11b;
|
||||
attribute test11c;
|
||||
type test11t1, test11a;
|
||||
type test11t2, test11a, test11b;
|
||||
type test11t3, test11a, test11b, test11c;
|
||||
type test11t4, test11b, test11c;
|
||||
type test11t5, test11a, test11c;
|
||||
type test11t6, test11b;
|
||||
type test11t7, test11c;
|
||||
|
||||
# test 12
|
||||
# name: unset
|
||||
# attrs: test12(a|b) regex
|
||||
# alias: unset
|
||||
attribute test12a;
|
||||
attribute test12b;
|
||||
attribute test12c;
|
||||
type test12t1, test12a;
|
||||
type test12t2, test12a, test12b;
|
||||
type test12t3, test12a, test12b, test12c;
|
||||
type test12t4, test12b, test12c;
|
||||
type test12t5, test12a, test12c;
|
||||
type test12t6, test12b;
|
||||
type test12t7, test12c;
|
||||
|
||||
# test 20
|
||||
# name: unset
|
||||
# attrs: unset
|
||||
# alias: test20a
|
||||
type test20t1 alias { test20a test20c };
|
||||
type test20t2 alias { test20b test20d };
|
||||
|
||||
# test 21
|
||||
# name: unset
|
||||
# attrs: unset
|
||||
# alias: test21(a|b)
|
||||
type test21t1 alias { test21a test21c };
|
||||
type test21t2 alias { test21b test21d };
|
||||
type test21t3 alias { test21e test21f };
|
||||
|
||||
################################################################################
|
||||
|
||||
#users
|
||||
user system roles system level med range low_s - high_s:here.lost;
|
||||
|
||||
#normal constraints
|
||||
constrain infoflow hi_w (u1 == u2);
|
||||
|
||||
#isids
|
||||
sid kernel system:system:system:medium_s:here
|
||||
sid security system:system:system:high_s:lost
|
||||
|
||||
#fs_use
|
||||
fs_use_trans devpts system:object_r:system:low_s;
|
||||
fs_use_xattr ext3 system:object_r:system:low_s;
|
||||
fs_use_task pipefs system:object_r:system:low_s;
|
||||
|
||||
#genfscon
|
||||
genfscon proc / system:object_r:system:med
|
||||
genfscon proc /sys system:object_r:system:low_s
|
||||
genfscon selinuxfs / system:object_r:system:high_s:here.there
|
||||
|
||||
portcon tcp 80 system:object_r:system:low_s
|
||||
|
||||
netifcon eth0 system:object_r:system:low_s system:object_r:system:low_s
|
||||
|
||||
nodecon 127.0.0.1 255.255.255.255 system:object_r:system:low_s:here
|
||||
nodecon ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff system:object_r:system:low_s:here
|
||||
|
127
tests/typequery.py
Normal file
127
tests/typequery.py
Normal file
@ -0,0 +1,127 @@
|
||||
# Copyright 2014, 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/>.
|
||||
#
|
||||
import unittest
|
||||
|
||||
from libapol import SELinuxPolicy
|
||||
from libapol.typequery import TypeQuery
|
||||
|
||||
|
||||
class TypeQueryTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.p = SELinuxPolicy("tests/typequery.conf")
|
||||
|
||||
def test_000_unset(self):
|
||||
"""Type query with no criteria."""
|
||||
# query with no parameters gets all types.
|
||||
for numtypes, t in enumerate(self.p.types(), start=1):
|
||||
pass
|
||||
|
||||
q = TypeQuery(self.p)
|
||||
for q_numtypes, t in enumerate(q.results(), start=1):
|
||||
pass
|
||||
|
||||
self.assertEqual(numtypes, q_numtypes)
|
||||
|
||||
def test_001_name_exact(self):
|
||||
"""Type query with exact name match."""
|
||||
q = TypeQuery(self.p, name="test1")
|
||||
|
||||
# manually consume the generator:
|
||||
types = q.results()
|
||||
t = types.next()
|
||||
|
||||
self.assertEqual(t, "test1")
|
||||
|
||||
self.assertRaises(StopIteration, types.next)
|
||||
|
||||
def test_002_name_regex(self):
|
||||
"""Type query with regex name match."""
|
||||
q = TypeQuery(self.p, name="test2(a|b)", name_regex=True)
|
||||
|
||||
# manually consume the generator:
|
||||
t = sorted(q.results())
|
||||
self.assertEqual(len(t), 2)
|
||||
|
||||
self.assertEqual(t[0], "test2a")
|
||||
self.assertEqual(t[1], "test2b")
|
||||
|
||||
def test_010_attr_intersect(self):
|
||||
"""Type query with attribute set intersection."""
|
||||
q = TypeQuery(self.p, attrs=["test10a", "test10b"])
|
||||
|
||||
# manually consume the generator:
|
||||
t = sorted(q.results())
|
||||
self.assertEqual(len(t), 6)
|
||||
|
||||
self.assertEqual(t[0], "test10t1")
|
||||
self.assertEqual(t[1], "test10t2")
|
||||
self.assertEqual(t[2], "test10t3")
|
||||
self.assertEqual(t[3], "test10t4")
|
||||
self.assertEqual(t[4], "test10t5")
|
||||
self.assertEqual(t[5], "test10t6")
|
||||
|
||||
def test_011_attr_equality(self):
|
||||
"""Type query with attribute set equality."""
|
||||
q = TypeQuery(self.p, attrs=["test11a", "test11b"], attrs_equal=True)
|
||||
|
||||
# manually consume the generator:
|
||||
types = q.results()
|
||||
t = types.next()
|
||||
|
||||
self.assertEqual(t, "test11t2")
|
||||
|
||||
self.assertRaises(StopIteration, types.next)
|
||||
|
||||
def test_012_attr_regex(self):
|
||||
"""Type query with attribute regex match."""
|
||||
q = TypeQuery(self.p, attrs="test12(a|b)", attrs_regex=True)
|
||||
|
||||
# manually consume the generator:
|
||||
t = sorted(q.results())
|
||||
self.assertEqual(len(t), 6)
|
||||
|
||||
self.assertEqual(t[0], "test12t1")
|
||||
self.assertEqual(t[1], "test12t2")
|
||||
self.assertEqual(t[2], "test12t3")
|
||||
self.assertEqual(t[3], "test12t4")
|
||||
self.assertEqual(t[4], "test12t5")
|
||||
self.assertEqual(t[5], "test12t6")
|
||||
|
||||
def test_020_alias_exact(self):
|
||||
"""Type query with exact alias match."""
|
||||
q = TypeQuery(self.p, alias="test20a")
|
||||
|
||||
# manually consume the generator:
|
||||
types = q.results()
|
||||
t = types.next()
|
||||
|
||||
self.assertEqual(t, "test20t1")
|
||||
|
||||
self.assertRaises(StopIteration, types.next)
|
||||
|
||||
def test_021_alias_regex(self):
|
||||
"""Type query with regex alias match."""
|
||||
q = TypeQuery(self.p, alias="test21(a|b)", alias_regex=True)
|
||||
|
||||
# manually consume the generator:
|
||||
t = sorted(q.results())
|
||||
self.assertEqual(len(t), 2)
|
||||
|
||||
self.assertEqual(t[0], "test21t1")
|
||||
self.assertEqual(t[1], "test21t2")
|
Loading…
Reference in New Issue
Block a user