add susfs-dev branch files
This commit is contained in:
339
LICENSE
Normal file
339
LICENSE
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.
|
||||||
548
kernel/.clang-format
Normal file
548
kernel/.clang-format
Normal file
@@ -0,0 +1,548 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
#
|
||||||
|
# clang-format configuration file. Intended for clang-format >= 4.
|
||||||
|
#
|
||||||
|
# For more information, see:
|
||||||
|
#
|
||||||
|
# Documentation/process/clang-format.rst
|
||||||
|
# https://clang.llvm.org/docs/ClangFormat.html
|
||||||
|
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
|
||||||
|
#
|
||||||
|
---
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveAssignments: false
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
#AlignEscapedNewlines: Left # Unknown to clang-format-4.0
|
||||||
|
AlignOperands: true
|
||||||
|
AlignTrailingComments: false
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: None
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AlwaysBreakTemplateDeclarations: false
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
BraceWrapping:
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: true
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
#AfterExternBlock: false # Unknown to clang-format-5.0
|
||||||
|
BeforeCatch: false
|
||||||
|
BeforeElse: false
|
||||||
|
IndentBraces: false
|
||||||
|
#SplitEmptyFunction: true # Unknown to clang-format-4.0
|
||||||
|
#SplitEmptyRecord: true # Unknown to clang-format-4.0
|
||||||
|
#SplitEmptyNamespace: true # Unknown to clang-format-4.0
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeBraces: Custom
|
||||||
|
#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0
|
||||||
|
BreakBeforeTernaryOperators: false
|
||||||
|
BreakConstructorInitializersBeforeComma: false
|
||||||
|
#BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0
|
||||||
|
BreakAfterJavaFieldAnnotations: false
|
||||||
|
BreakStringLiterals: false
|
||||||
|
ColumnLimit: 80
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
#CompactNamespaces: false # Unknown to clang-format-4.0
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
ConstructorInitializerIndentWidth: 8
|
||||||
|
ContinuationIndentWidth: 8
|
||||||
|
Cpp11BracedListStyle: false
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
DisableFormat: false
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
#FixNamespaceComments: false # Unknown to clang-format-4.0
|
||||||
|
|
||||||
|
# Taken from:
|
||||||
|
# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ \
|
||||||
|
# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \
|
||||||
|
# | sort | uniq
|
||||||
|
ForEachMacros:
|
||||||
|
- 'apei_estatus_for_each_section'
|
||||||
|
- 'ata_for_each_dev'
|
||||||
|
- 'ata_for_each_link'
|
||||||
|
- '__ata_qc_for_each'
|
||||||
|
- 'ata_qc_for_each'
|
||||||
|
- 'ata_qc_for_each_raw'
|
||||||
|
- 'ata_qc_for_each_with_internal'
|
||||||
|
- 'ax25_for_each'
|
||||||
|
- 'ax25_uid_for_each'
|
||||||
|
- '__bio_for_each_bvec'
|
||||||
|
- 'bio_for_each_bvec'
|
||||||
|
- 'bio_for_each_bvec_all'
|
||||||
|
- 'bio_for_each_integrity_vec'
|
||||||
|
- '__bio_for_each_segment'
|
||||||
|
- 'bio_for_each_segment'
|
||||||
|
- 'bio_for_each_segment_all'
|
||||||
|
- 'bio_list_for_each'
|
||||||
|
- 'bip_for_each_vec'
|
||||||
|
- 'bitmap_for_each_clear_region'
|
||||||
|
- 'bitmap_for_each_set_region'
|
||||||
|
- 'blkg_for_each_descendant_post'
|
||||||
|
- 'blkg_for_each_descendant_pre'
|
||||||
|
- 'blk_queue_for_each_rl'
|
||||||
|
- 'bond_for_each_slave'
|
||||||
|
- 'bond_for_each_slave_rcu'
|
||||||
|
- 'bpf_for_each_spilled_reg'
|
||||||
|
- 'btree_for_each_safe128'
|
||||||
|
- 'btree_for_each_safe32'
|
||||||
|
- 'btree_for_each_safe64'
|
||||||
|
- 'btree_for_each_safel'
|
||||||
|
- 'card_for_each_dev'
|
||||||
|
- 'cgroup_taskset_for_each'
|
||||||
|
- 'cgroup_taskset_for_each_leader'
|
||||||
|
- 'cpufreq_for_each_entry'
|
||||||
|
- 'cpufreq_for_each_entry_idx'
|
||||||
|
- 'cpufreq_for_each_valid_entry'
|
||||||
|
- 'cpufreq_for_each_valid_entry_idx'
|
||||||
|
- 'css_for_each_child'
|
||||||
|
- 'css_for_each_descendant_post'
|
||||||
|
- 'css_for_each_descendant_pre'
|
||||||
|
- 'device_for_each_child_node'
|
||||||
|
- 'dma_fence_chain_for_each'
|
||||||
|
- 'do_for_each_ftrace_op'
|
||||||
|
- 'drm_atomic_crtc_for_each_plane'
|
||||||
|
- 'drm_atomic_crtc_state_for_each_plane'
|
||||||
|
- 'drm_atomic_crtc_state_for_each_plane_state'
|
||||||
|
- 'drm_atomic_for_each_plane_damage'
|
||||||
|
- 'drm_client_for_each_connector_iter'
|
||||||
|
- 'drm_client_for_each_modeset'
|
||||||
|
- 'drm_connector_for_each_possible_encoder'
|
||||||
|
- 'drm_for_each_bridge_in_chain'
|
||||||
|
- 'drm_for_each_connector_iter'
|
||||||
|
- 'drm_for_each_crtc'
|
||||||
|
- 'drm_for_each_encoder'
|
||||||
|
- 'drm_for_each_encoder_mask'
|
||||||
|
- 'drm_for_each_fb'
|
||||||
|
- 'drm_for_each_legacy_plane'
|
||||||
|
- 'drm_for_each_plane'
|
||||||
|
- 'drm_for_each_plane_mask'
|
||||||
|
- 'drm_for_each_privobj'
|
||||||
|
- 'drm_mm_for_each_hole'
|
||||||
|
- 'drm_mm_for_each_node'
|
||||||
|
- 'drm_mm_for_each_node_in_range'
|
||||||
|
- 'drm_mm_for_each_node_safe'
|
||||||
|
- 'flow_action_for_each'
|
||||||
|
- 'for_each_active_dev_scope'
|
||||||
|
- 'for_each_active_drhd_unit'
|
||||||
|
- 'for_each_active_iommu'
|
||||||
|
- 'for_each_aggr_pgid'
|
||||||
|
- 'for_each_available_child_of_node'
|
||||||
|
- 'for_each_bio'
|
||||||
|
- 'for_each_board_func_rsrc'
|
||||||
|
- 'for_each_bvec'
|
||||||
|
- 'for_each_card_auxs'
|
||||||
|
- 'for_each_card_auxs_safe'
|
||||||
|
- 'for_each_card_components'
|
||||||
|
- 'for_each_card_dapms'
|
||||||
|
- 'for_each_card_pre_auxs'
|
||||||
|
- 'for_each_card_prelinks'
|
||||||
|
- 'for_each_card_rtds'
|
||||||
|
- 'for_each_card_rtds_safe'
|
||||||
|
- 'for_each_card_widgets'
|
||||||
|
- 'for_each_card_widgets_safe'
|
||||||
|
- 'for_each_cgroup_storage_type'
|
||||||
|
- 'for_each_child_of_node'
|
||||||
|
- 'for_each_clear_bit'
|
||||||
|
- 'for_each_clear_bit_from'
|
||||||
|
- 'for_each_cmsghdr'
|
||||||
|
- 'for_each_compatible_node'
|
||||||
|
- 'for_each_component_dais'
|
||||||
|
- 'for_each_component_dais_safe'
|
||||||
|
- 'for_each_comp_order'
|
||||||
|
- 'for_each_console'
|
||||||
|
- 'for_each_cpu'
|
||||||
|
- 'for_each_cpu_and'
|
||||||
|
- 'for_each_cpu_not'
|
||||||
|
- 'for_each_cpu_wrap'
|
||||||
|
- 'for_each_dapm_widgets'
|
||||||
|
- 'for_each_dev_addr'
|
||||||
|
- 'for_each_dev_scope'
|
||||||
|
- 'for_each_displayid_db'
|
||||||
|
- 'for_each_dma_cap_mask'
|
||||||
|
- 'for_each_dpcm_be'
|
||||||
|
- 'for_each_dpcm_be_rollback'
|
||||||
|
- 'for_each_dpcm_be_safe'
|
||||||
|
- 'for_each_dpcm_fe'
|
||||||
|
- 'for_each_drhd_unit'
|
||||||
|
- 'for_each_dss_dev'
|
||||||
|
- 'for_each_efi_memory_desc'
|
||||||
|
- 'for_each_efi_memory_desc_in_map'
|
||||||
|
- 'for_each_element'
|
||||||
|
- 'for_each_element_extid'
|
||||||
|
- 'for_each_element_id'
|
||||||
|
- 'for_each_endpoint_of_node'
|
||||||
|
- 'for_each_evictable_lru'
|
||||||
|
- 'for_each_fib6_node_rt_rcu'
|
||||||
|
- 'for_each_fib6_walker_rt'
|
||||||
|
- 'for_each_free_mem_pfn_range_in_zone'
|
||||||
|
- 'for_each_free_mem_pfn_range_in_zone_from'
|
||||||
|
- 'for_each_free_mem_range'
|
||||||
|
- 'for_each_free_mem_range_reverse'
|
||||||
|
- 'for_each_func_rsrc'
|
||||||
|
- 'for_each_hstate'
|
||||||
|
- 'for_each_if'
|
||||||
|
- 'for_each_iommu'
|
||||||
|
- 'for_each_ip_tunnel_rcu'
|
||||||
|
- 'for_each_irq_nr'
|
||||||
|
- 'for_each_link_codecs'
|
||||||
|
- 'for_each_link_cpus'
|
||||||
|
- 'for_each_link_platforms'
|
||||||
|
- 'for_each_lru'
|
||||||
|
- 'for_each_matching_node'
|
||||||
|
- 'for_each_matching_node_and_match'
|
||||||
|
- 'for_each_member'
|
||||||
|
- 'for_each_mem_region'
|
||||||
|
- 'for_each_memblock_type'
|
||||||
|
- 'for_each_memcg_cache_index'
|
||||||
|
- 'for_each_mem_pfn_range'
|
||||||
|
- '__for_each_mem_range'
|
||||||
|
- 'for_each_mem_range'
|
||||||
|
- '__for_each_mem_range_rev'
|
||||||
|
- 'for_each_mem_range_rev'
|
||||||
|
- 'for_each_migratetype_order'
|
||||||
|
- 'for_each_msi_entry'
|
||||||
|
- 'for_each_msi_entry_safe'
|
||||||
|
- 'for_each_net'
|
||||||
|
- 'for_each_net_continue_reverse'
|
||||||
|
- 'for_each_netdev'
|
||||||
|
- 'for_each_netdev_continue'
|
||||||
|
- 'for_each_netdev_continue_rcu'
|
||||||
|
- 'for_each_netdev_continue_reverse'
|
||||||
|
- 'for_each_netdev_feature'
|
||||||
|
- 'for_each_netdev_in_bond_rcu'
|
||||||
|
- 'for_each_netdev_rcu'
|
||||||
|
- 'for_each_netdev_reverse'
|
||||||
|
- 'for_each_netdev_safe'
|
||||||
|
- 'for_each_net_rcu'
|
||||||
|
- 'for_each_new_connector_in_state'
|
||||||
|
- 'for_each_new_crtc_in_state'
|
||||||
|
- 'for_each_new_mst_mgr_in_state'
|
||||||
|
- 'for_each_new_plane_in_state'
|
||||||
|
- 'for_each_new_private_obj_in_state'
|
||||||
|
- 'for_each_node'
|
||||||
|
- 'for_each_node_by_name'
|
||||||
|
- 'for_each_node_by_type'
|
||||||
|
- 'for_each_node_mask'
|
||||||
|
- 'for_each_node_state'
|
||||||
|
- 'for_each_node_with_cpus'
|
||||||
|
- 'for_each_node_with_property'
|
||||||
|
- 'for_each_nonreserved_multicast_dest_pgid'
|
||||||
|
- 'for_each_of_allnodes'
|
||||||
|
- 'for_each_of_allnodes_from'
|
||||||
|
- 'for_each_of_cpu_node'
|
||||||
|
- 'for_each_of_pci_range'
|
||||||
|
- 'for_each_old_connector_in_state'
|
||||||
|
- 'for_each_old_crtc_in_state'
|
||||||
|
- 'for_each_old_mst_mgr_in_state'
|
||||||
|
- 'for_each_oldnew_connector_in_state'
|
||||||
|
- 'for_each_oldnew_crtc_in_state'
|
||||||
|
- 'for_each_oldnew_mst_mgr_in_state'
|
||||||
|
- 'for_each_oldnew_plane_in_state'
|
||||||
|
- 'for_each_oldnew_plane_in_state_reverse'
|
||||||
|
- 'for_each_oldnew_private_obj_in_state'
|
||||||
|
- 'for_each_old_plane_in_state'
|
||||||
|
- 'for_each_old_private_obj_in_state'
|
||||||
|
- 'for_each_online_cpu'
|
||||||
|
- 'for_each_online_node'
|
||||||
|
- 'for_each_online_pgdat'
|
||||||
|
- 'for_each_pci_bridge'
|
||||||
|
- 'for_each_pci_dev'
|
||||||
|
- 'for_each_pci_msi_entry'
|
||||||
|
- 'for_each_pcm_streams'
|
||||||
|
- 'for_each_physmem_range'
|
||||||
|
- 'for_each_populated_zone'
|
||||||
|
- 'for_each_possible_cpu'
|
||||||
|
- 'for_each_present_cpu'
|
||||||
|
- 'for_each_prime_number'
|
||||||
|
- 'for_each_prime_number_from'
|
||||||
|
- 'for_each_process'
|
||||||
|
- 'for_each_process_thread'
|
||||||
|
- 'for_each_property_of_node'
|
||||||
|
- 'for_each_registered_fb'
|
||||||
|
- 'for_each_requested_gpio'
|
||||||
|
- 'for_each_requested_gpio_in_range'
|
||||||
|
- 'for_each_reserved_mem_range'
|
||||||
|
- 'for_each_reserved_mem_region'
|
||||||
|
- 'for_each_rtd_codec_dais'
|
||||||
|
- 'for_each_rtd_codec_dais_rollback'
|
||||||
|
- 'for_each_rtd_components'
|
||||||
|
- 'for_each_rtd_cpu_dais'
|
||||||
|
- 'for_each_rtd_cpu_dais_rollback'
|
||||||
|
- 'for_each_rtd_dais'
|
||||||
|
- 'for_each_set_bit'
|
||||||
|
- 'for_each_set_bit_from'
|
||||||
|
- 'for_each_set_clump8'
|
||||||
|
- 'for_each_sg'
|
||||||
|
- 'for_each_sg_dma_page'
|
||||||
|
- 'for_each_sg_page'
|
||||||
|
- 'for_each_sgtable_dma_page'
|
||||||
|
- 'for_each_sgtable_dma_sg'
|
||||||
|
- 'for_each_sgtable_page'
|
||||||
|
- 'for_each_sgtable_sg'
|
||||||
|
- 'for_each_sibling_event'
|
||||||
|
- 'for_each_subelement'
|
||||||
|
- 'for_each_subelement_extid'
|
||||||
|
- 'for_each_subelement_id'
|
||||||
|
- '__for_each_thread'
|
||||||
|
- 'for_each_thread'
|
||||||
|
- 'for_each_unicast_dest_pgid'
|
||||||
|
- 'for_each_wakeup_source'
|
||||||
|
- 'for_each_zone'
|
||||||
|
- 'for_each_zone_zonelist'
|
||||||
|
- 'for_each_zone_zonelist_nodemask'
|
||||||
|
- 'fwnode_for_each_available_child_node'
|
||||||
|
- 'fwnode_for_each_child_node'
|
||||||
|
- 'fwnode_graph_for_each_endpoint'
|
||||||
|
- 'gadget_for_each_ep'
|
||||||
|
- 'genradix_for_each'
|
||||||
|
- 'genradix_for_each_from'
|
||||||
|
- 'hash_for_each'
|
||||||
|
- 'hash_for_each_possible'
|
||||||
|
- 'hash_for_each_possible_rcu'
|
||||||
|
- 'hash_for_each_possible_rcu_notrace'
|
||||||
|
- 'hash_for_each_possible_safe'
|
||||||
|
- 'hash_for_each_rcu'
|
||||||
|
- 'hash_for_each_safe'
|
||||||
|
- 'hctx_for_each_ctx'
|
||||||
|
- 'hlist_bl_for_each_entry'
|
||||||
|
- 'hlist_bl_for_each_entry_rcu'
|
||||||
|
- 'hlist_bl_for_each_entry_safe'
|
||||||
|
- 'hlist_for_each'
|
||||||
|
- 'hlist_for_each_entry'
|
||||||
|
- 'hlist_for_each_entry_continue'
|
||||||
|
- 'hlist_for_each_entry_continue_rcu'
|
||||||
|
- 'hlist_for_each_entry_continue_rcu_bh'
|
||||||
|
- 'hlist_for_each_entry_from'
|
||||||
|
- 'hlist_for_each_entry_from_rcu'
|
||||||
|
- 'hlist_for_each_entry_rcu'
|
||||||
|
- 'hlist_for_each_entry_rcu_bh'
|
||||||
|
- 'hlist_for_each_entry_rcu_notrace'
|
||||||
|
- 'hlist_for_each_entry_safe'
|
||||||
|
- '__hlist_for_each_rcu'
|
||||||
|
- 'hlist_for_each_safe'
|
||||||
|
- 'hlist_nulls_for_each_entry'
|
||||||
|
- 'hlist_nulls_for_each_entry_from'
|
||||||
|
- 'hlist_nulls_for_each_entry_rcu'
|
||||||
|
- 'hlist_nulls_for_each_entry_safe'
|
||||||
|
- 'i3c_bus_for_each_i2cdev'
|
||||||
|
- 'i3c_bus_for_each_i3cdev'
|
||||||
|
- 'ide_host_for_each_port'
|
||||||
|
- 'ide_port_for_each_dev'
|
||||||
|
- 'ide_port_for_each_present_dev'
|
||||||
|
- 'idr_for_each_entry'
|
||||||
|
- 'idr_for_each_entry_continue'
|
||||||
|
- 'idr_for_each_entry_continue_ul'
|
||||||
|
- 'idr_for_each_entry_ul'
|
||||||
|
- 'in_dev_for_each_ifa_rcu'
|
||||||
|
- 'in_dev_for_each_ifa_rtnl'
|
||||||
|
- 'inet_bind_bucket_for_each'
|
||||||
|
- 'inet_lhash2_for_each_icsk_rcu'
|
||||||
|
- 'key_for_each'
|
||||||
|
- 'key_for_each_safe'
|
||||||
|
- 'klp_for_each_func'
|
||||||
|
- 'klp_for_each_func_safe'
|
||||||
|
- 'klp_for_each_func_static'
|
||||||
|
- 'klp_for_each_object'
|
||||||
|
- 'klp_for_each_object_safe'
|
||||||
|
- 'klp_for_each_object_static'
|
||||||
|
- 'kunit_suite_for_each_test_case'
|
||||||
|
- 'kvm_for_each_memslot'
|
||||||
|
- 'kvm_for_each_vcpu'
|
||||||
|
- 'list_for_each'
|
||||||
|
- 'list_for_each_codec'
|
||||||
|
- 'list_for_each_codec_safe'
|
||||||
|
- 'list_for_each_continue'
|
||||||
|
- 'list_for_each_entry'
|
||||||
|
- 'list_for_each_entry_continue'
|
||||||
|
- 'list_for_each_entry_continue_rcu'
|
||||||
|
- 'list_for_each_entry_continue_reverse'
|
||||||
|
- 'list_for_each_entry_from'
|
||||||
|
- 'list_for_each_entry_from_rcu'
|
||||||
|
- 'list_for_each_entry_from_reverse'
|
||||||
|
- 'list_for_each_entry_lockless'
|
||||||
|
- 'list_for_each_entry_rcu'
|
||||||
|
- 'list_for_each_entry_reverse'
|
||||||
|
- 'list_for_each_entry_safe'
|
||||||
|
- 'list_for_each_entry_safe_continue'
|
||||||
|
- 'list_for_each_entry_safe_from'
|
||||||
|
- 'list_for_each_entry_safe_reverse'
|
||||||
|
- 'list_for_each_prev'
|
||||||
|
- 'list_for_each_prev_safe'
|
||||||
|
- 'list_for_each_safe'
|
||||||
|
- 'llist_for_each'
|
||||||
|
- 'llist_for_each_entry'
|
||||||
|
- 'llist_for_each_entry_safe'
|
||||||
|
- 'llist_for_each_safe'
|
||||||
|
- 'mci_for_each_dimm'
|
||||||
|
- 'media_device_for_each_entity'
|
||||||
|
- 'media_device_for_each_intf'
|
||||||
|
- 'media_device_for_each_link'
|
||||||
|
- 'media_device_for_each_pad'
|
||||||
|
- 'nanddev_io_for_each_page'
|
||||||
|
- 'netdev_for_each_lower_dev'
|
||||||
|
- 'netdev_for_each_lower_private'
|
||||||
|
- 'netdev_for_each_lower_private_rcu'
|
||||||
|
- 'netdev_for_each_mc_addr'
|
||||||
|
- 'netdev_for_each_uc_addr'
|
||||||
|
- 'netdev_for_each_upper_dev_rcu'
|
||||||
|
- 'netdev_hw_addr_list_for_each'
|
||||||
|
- 'nft_rule_for_each_expr'
|
||||||
|
- 'nla_for_each_attr'
|
||||||
|
- 'nla_for_each_nested'
|
||||||
|
- 'nlmsg_for_each_attr'
|
||||||
|
- 'nlmsg_for_each_msg'
|
||||||
|
- 'nr_neigh_for_each'
|
||||||
|
- 'nr_neigh_for_each_safe'
|
||||||
|
- 'nr_node_for_each'
|
||||||
|
- 'nr_node_for_each_safe'
|
||||||
|
- 'of_for_each_phandle'
|
||||||
|
- 'of_property_for_each_string'
|
||||||
|
- 'of_property_for_each_u32'
|
||||||
|
- 'pci_bus_for_each_resource'
|
||||||
|
- 'pcm_for_each_format'
|
||||||
|
- 'ping_portaddr_for_each_entry'
|
||||||
|
- 'plist_for_each'
|
||||||
|
- 'plist_for_each_continue'
|
||||||
|
- 'plist_for_each_entry'
|
||||||
|
- 'plist_for_each_entry_continue'
|
||||||
|
- 'plist_for_each_entry_safe'
|
||||||
|
- 'plist_for_each_safe'
|
||||||
|
- 'pnp_for_each_card'
|
||||||
|
- 'pnp_for_each_dev'
|
||||||
|
- 'protocol_for_each_card'
|
||||||
|
- 'protocol_for_each_dev'
|
||||||
|
- 'queue_for_each_hw_ctx'
|
||||||
|
- 'radix_tree_for_each_slot'
|
||||||
|
- 'radix_tree_for_each_tagged'
|
||||||
|
- 'rbtree_postorder_for_each_entry_safe'
|
||||||
|
- 'rdma_for_each_block'
|
||||||
|
- 'rdma_for_each_port'
|
||||||
|
- 'rdma_umem_for_each_dma_block'
|
||||||
|
- 'resource_list_for_each_entry'
|
||||||
|
- 'resource_list_for_each_entry_safe'
|
||||||
|
- 'rhl_for_each_entry_rcu'
|
||||||
|
- 'rhl_for_each_rcu'
|
||||||
|
- 'rht_for_each'
|
||||||
|
- 'rht_for_each_entry'
|
||||||
|
- 'rht_for_each_entry_from'
|
||||||
|
- 'rht_for_each_entry_rcu'
|
||||||
|
- 'rht_for_each_entry_rcu_from'
|
||||||
|
- 'rht_for_each_entry_safe'
|
||||||
|
- 'rht_for_each_from'
|
||||||
|
- 'rht_for_each_rcu'
|
||||||
|
- 'rht_for_each_rcu_from'
|
||||||
|
- '__rq_for_each_bio'
|
||||||
|
- 'rq_for_each_bvec'
|
||||||
|
- 'rq_for_each_segment'
|
||||||
|
- 'scsi_for_each_prot_sg'
|
||||||
|
- 'scsi_for_each_sg'
|
||||||
|
- 'sctp_for_each_hentry'
|
||||||
|
- 'sctp_skb_for_each'
|
||||||
|
- 'shdma_for_each_chan'
|
||||||
|
- '__shost_for_each_device'
|
||||||
|
- 'shost_for_each_device'
|
||||||
|
- 'sk_for_each'
|
||||||
|
- 'sk_for_each_bound'
|
||||||
|
- 'sk_for_each_entry_offset_rcu'
|
||||||
|
- 'sk_for_each_from'
|
||||||
|
- 'sk_for_each_rcu'
|
||||||
|
- 'sk_for_each_safe'
|
||||||
|
- 'sk_nulls_for_each'
|
||||||
|
- 'sk_nulls_for_each_from'
|
||||||
|
- 'sk_nulls_for_each_rcu'
|
||||||
|
- 'snd_array_for_each'
|
||||||
|
- 'snd_pcm_group_for_each_entry'
|
||||||
|
- 'snd_soc_dapm_widget_for_each_path'
|
||||||
|
- 'snd_soc_dapm_widget_for_each_path_safe'
|
||||||
|
- 'snd_soc_dapm_widget_for_each_sink_path'
|
||||||
|
- 'snd_soc_dapm_widget_for_each_source_path'
|
||||||
|
- 'tb_property_for_each'
|
||||||
|
- 'tcf_exts_for_each_action'
|
||||||
|
- 'udp_portaddr_for_each_entry'
|
||||||
|
- 'udp_portaddr_for_each_entry_rcu'
|
||||||
|
- 'usb_hub_for_each_child'
|
||||||
|
- 'v4l2_device_for_each_subdev'
|
||||||
|
- 'v4l2_m2m_for_each_dst_buf'
|
||||||
|
- 'v4l2_m2m_for_each_dst_buf_safe'
|
||||||
|
- 'v4l2_m2m_for_each_src_buf'
|
||||||
|
- 'v4l2_m2m_for_each_src_buf_safe'
|
||||||
|
- 'virtio_device_for_each_vq'
|
||||||
|
- 'while_for_each_ftrace_op'
|
||||||
|
- 'xa_for_each'
|
||||||
|
- 'xa_for_each_marked'
|
||||||
|
- 'xa_for_each_range'
|
||||||
|
- 'xa_for_each_start'
|
||||||
|
- 'xas_for_each'
|
||||||
|
- 'xas_for_each_conflict'
|
||||||
|
- 'xas_for_each_marked'
|
||||||
|
- 'xbc_array_for_each_value'
|
||||||
|
- 'xbc_for_each_key_value'
|
||||||
|
- 'xbc_node_for_each_array_value'
|
||||||
|
- 'xbc_node_for_each_child'
|
||||||
|
- 'xbc_node_for_each_key_value'
|
||||||
|
- 'zorro_for_each_dev'
|
||||||
|
|
||||||
|
#IncludeBlocks: Preserve # Unknown to clang-format-5.0
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 1
|
||||||
|
IncludeIsMainRegex: '(Test)?$'
|
||||||
|
IndentCaseLabels: false
|
||||||
|
#IndentPPDirectives: None # Unknown to clang-format-5.0
|
||||||
|
IndentWidth: 8
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
JavaScriptQuotes: Leave
|
||||||
|
JavaScriptWrapImports: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
|
||||||
|
ObjCBlockIndentWidth: 8
|
||||||
|
ObjCSpaceAfterProperty: true
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
|
||||||
|
# Taken from git's rules
|
||||||
|
#PenaltyBreakAssignment: 10 # Unknown to clang-format-4.0
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 30
|
||||||
|
PenaltyBreakComment: 10
|
||||||
|
PenaltyBreakFirstLessLess: 0
|
||||||
|
PenaltyBreakString: 10
|
||||||
|
PenaltyExcessCharacter: 100
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: false
|
||||||
|
SortIncludes: false
|
||||||
|
#SortUsingDeclarations: false # Unknown to clang-format-4.0
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterTemplateKeyword: true
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
#SpaceBeforeCtorInitializerColon: true # Unknown to clang-format-5.0
|
||||||
|
#SpaceBeforeInheritanceColon: true # Unknown to clang-format-5.0
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
#SpaceBeforeRangeBasedForLoopColon: true # Unknown to clang-format-5.0
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
Standard: Cpp03
|
||||||
|
TabWidth: 8
|
||||||
|
UseTab: Always
|
||||||
|
...
|
||||||
4
kernel/.clangd
Normal file
4
kernel/.clangd
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Diagnostics:
|
||||||
|
UnusedIncludes: Strict
|
||||||
|
ClangTidy:
|
||||||
|
Remove: bugprone-sizeof-expression
|
||||||
182
kernel/Kconfig
Normal file
182
kernel/Kconfig
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
menu "KernelSU"
|
||||||
|
|
||||||
|
config KSU
|
||||||
|
tristate "KernelSU function support"
|
||||||
|
depends on OVERLAY_FS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Enable kernel-level root privileges on Android System.
|
||||||
|
To compile as a module, choose M here: the
|
||||||
|
module will be called kernelsu.
|
||||||
|
|
||||||
|
config KSU_DEBUG
|
||||||
|
bool "KernelSU debug mode"
|
||||||
|
depends on KSU
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enable KernelSU debug mode.
|
||||||
|
|
||||||
|
config KSU_ALLOWLIST_WORKAROUND
|
||||||
|
bool "KernelSU Session Keyring Init workaround"
|
||||||
|
depends on KSU
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enable session keyring init workaround for problematic devices.
|
||||||
|
Useful for situations where the SU allowlist is not kept after a reboot
|
||||||
|
|
||||||
|
config KSU_MANUAL_HOOK
|
||||||
|
bool "Manual hooking GKI kernels without kprobes"
|
||||||
|
depends on KSU && KSU != m
|
||||||
|
depends on KPROBES
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Keep KPROBES enabled but do not use KPROBES to implement
|
||||||
|
the hooks required by KernelSU, but instead hook them manually.
|
||||||
|
This function only available on GKI kernels, non-GKI are not
|
||||||
|
affected.
|
||||||
|
|
||||||
|
menu "KernelSU - SUSFS"
|
||||||
|
config KSU_SUSFS
|
||||||
|
bool "KernelSU addon - SUSFS"
|
||||||
|
depends on KSU
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Patch and Enable SUSFS to kernel with KernelSU.
|
||||||
|
|
||||||
|
config KSU_SUSFS_HAS_MAGIC_MOUNT
|
||||||
|
bool "Say yes if the current KernelSU repo has magic mount implemented (default n)"
|
||||||
|
depends on KSU
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
- Enable to indicate that the current SUSFS kernel supports the auto hide features for 5ec1cff's Magic Mount KernelSU
|
||||||
|
- Every mounts from /debug_ramdisk/workdir will be treated as magic mount and processed differently by susfs
|
||||||
|
|
||||||
|
config KSU_SUSFS_SUS_PATH
|
||||||
|
bool "Enable to hide suspicious path (NOT recommended)"
|
||||||
|
depends on KSU_SUSFS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
- Allow hiding the user-defined path and all its sub-paths from various system calls.
|
||||||
|
- tmpfs filesystem is not allowed to be added.
|
||||||
|
- Effective only on zygote spawned user app process.
|
||||||
|
- Use with cautious as it may cause performance loss and will be vulnerable to side channel attacks,
|
||||||
|
just disable this feature if it doesn't work for you or you don't need it at all.
|
||||||
|
|
||||||
|
config KSU_SUSFS_SUS_MOUNT
|
||||||
|
bool "Enable to hide suspicious mounts"
|
||||||
|
depends on KSU_SUSFS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
- Allow hiding the user-defined mount paths from /proc/self/[mounts|mountinfo|mountstat].
|
||||||
|
- Effective on all processes for hiding mount entries.
|
||||||
|
- Mounts mounted by process with ksu domain will be forced to be assigned the dev name "KSU".
|
||||||
|
- mnt_id and mnt_group_id of the sus mount will be assigned to a much bigger number to solve the issue of id not being contiguous.
|
||||||
|
|
||||||
|
config KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT
|
||||||
|
bool "Enable to hide KSU's default mounts automatically (experimental)"
|
||||||
|
depends on KSU_SUSFS_SUS_MOUNT
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
- Automatically add KSU's default mounts to sus_mount.
|
||||||
|
- No susfs command is needed in userspace.
|
||||||
|
- Only mount operation from process with ksu domain will be checked.
|
||||||
|
|
||||||
|
config KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT
|
||||||
|
bool "Enable to hide suspicious bind mounts automatically (experimental)"
|
||||||
|
depends on KSU_SUSFS_SUS_MOUNT
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
- Automatically add binded mounts to sus_mount.
|
||||||
|
- No susfs command is needed in userspace.
|
||||||
|
- Only mount operation from process with ksu domain will be checked.
|
||||||
|
|
||||||
|
config KSU_SUSFS_SUS_KSTAT
|
||||||
|
bool "Enable to spoof suspicious kstat"
|
||||||
|
depends on KSU_SUSFS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
- Allow spoofing the kstat of user-defined file/directory.
|
||||||
|
- Effective only on zygote spawned user app process.
|
||||||
|
|
||||||
|
config KSU_SUSFS_SUS_OVERLAYFS
|
||||||
|
bool "Enable to automatically spoof kstat and kstatfs for overlayed files/directories"
|
||||||
|
depends on KSU_SUSFS
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
- Automatically spoof the kstat and kstatfs for overlayed files/directories.
|
||||||
|
- Enable it if you are using legacy KernelSU and dont have auto hide features enabled.
|
||||||
|
- No susfs command is needed in userspace.
|
||||||
|
- Effective on all processes.
|
||||||
|
|
||||||
|
config KSU_SUSFS_TRY_UMOUNT
|
||||||
|
bool "Enable to use ksu's ksu_try_umount"
|
||||||
|
depends on KSU_SUSFS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
- Allow using ksu_try_umount to umount other user-defined mount paths prior to ksu's default umount paths.
|
||||||
|
- Effective on all NO-root-access-granted processes.
|
||||||
|
|
||||||
|
config KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT
|
||||||
|
bool "Enable to add bind mounts to ksu's ksu_try_umount automatically (experimental)"
|
||||||
|
depends on KSU_SUSFS_TRY_UMOUNT
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
- Automatically add binded mounts to ksu's ksu_try_umount.
|
||||||
|
- No susfs command is needed in userspace.
|
||||||
|
- Only mount operation from process with ksu domain will be checked.
|
||||||
|
|
||||||
|
config KSU_SUSFS_SPOOF_UNAME
|
||||||
|
bool "Enable to spoof uname"
|
||||||
|
depends on KSU_SUSFS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
- Allow spoofing the string returned by uname syscall to user-defined string.
|
||||||
|
- Effective on all processes.
|
||||||
|
|
||||||
|
config KSU_SUSFS_ENABLE_LOG
|
||||||
|
bool "Enable logging susfs log to kernel"
|
||||||
|
depends on KSU_SUSFS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
- Allow logging susfs log to kernel, uncheck it to completely disable all susfs log.
|
||||||
|
|
||||||
|
config KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS
|
||||||
|
bool "Enable to automatically hide ksu and susfs symbols from /proc/kallsyms"
|
||||||
|
depends on KSU_SUSFS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
- Automatically hide ksu and susfs symbols from '/proc/kallsyms'.
|
||||||
|
- Effective on all processes.
|
||||||
|
|
||||||
|
config KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG
|
||||||
|
bool "Enable to spoof /proc/bootconfig (gki) or /proc/cmdline (non-gki)"
|
||||||
|
depends on KSU_SUSFS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
- Spoof the output of /proc/bootconfig (gki) or /proc/cmdline (non-gki) with a user-defined file.
|
||||||
|
- Effective on all processes.
|
||||||
|
|
||||||
|
config KSU_SUSFS_OPEN_REDIRECT
|
||||||
|
bool "Enable to redirect a path to be opened with another path (experimental)"
|
||||||
|
depends on KSU_SUSFS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
- Allow redirecting a target path to be opened with another user-defined path.
|
||||||
|
- Effective only on processes with uid < 2000.
|
||||||
|
- Please be reminded that process with open access to the target and redirected path can be detected.
|
||||||
|
|
||||||
|
config KSU_SUSFS_SUS_SU
|
||||||
|
bool "Enable SUS-SU in runtime temporarily"
|
||||||
|
depends on KSU_SUSFS && KPROBES && HAVE_KPROBES && KPROBE_EVENTS
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
- Allow user to enable or disable core ksu kprobes hooks temporarily in runtime. There are 2 working modes for sus_su.
|
||||||
|
- Mode 0 (default): Disable sus_su, and enable ksu kprobe hooks for su instead.
|
||||||
|
- Mode 1 (deprecated):
|
||||||
|
- Mode 2: Enable sus_su, and disable ksu kprobe hooks for su, which means the kernel inline hooks are enabled,
|
||||||
|
the same as the su implementaion of non-gki kernel without kprobe supported.
|
||||||
|
- Only apps with root access granted by ksu manager are allowed to get root.
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
|
||||||
|
endmenu
|
||||||
339
kernel/LICENSE
Normal file
339
kernel/LICENSE
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.
|
||||||
145
kernel/Makefile
Normal file
145
kernel/Makefile
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
kernelsu-objs := ksu.o
|
||||||
|
kernelsu-objs += allowlist.o
|
||||||
|
kernelsu-objs += apk_sign.o
|
||||||
|
kernelsu-objs += sucompat.o
|
||||||
|
kernelsu-objs += throne_tracker.o
|
||||||
|
kernelsu-objs += core_hook.o
|
||||||
|
kernelsu-objs += ksud.o
|
||||||
|
kernelsu-objs += embed_ksud.o
|
||||||
|
kernelsu-objs += kernel_compat.o
|
||||||
|
|
||||||
|
kernelsu-objs += selinux/selinux.o
|
||||||
|
kernelsu-objs += selinux/sepolicy.o
|
||||||
|
kernelsu-objs += selinux/rules.o
|
||||||
|
ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
|
||||||
|
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
|
||||||
|
|
||||||
|
# Do checks before compile
|
||||||
|
ifneq ($(shell grep -q "int path_umount" $(srctree)/fs/namespace.c; echo $$?),0)
|
||||||
|
$(error -- Backporting path_umount is mandatory !! Read: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#how-to-backport-path-umount)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# https://github.com/tiann/KernelSU/pull/2102/files#diff-3a325663233178293ee38b8161f3be511a466af7e0156b9d03d5aed0497564bfR19
|
||||||
|
IS_GKI := $(strip $(shell \
|
||||||
|
if [ "$(VERSION)" -ge "5" -a "$(PATCHLEVEL)" -ge "10" ]; then \
|
||||||
|
echo TRUE; \
|
||||||
|
else \
|
||||||
|
echo FALSE; \
|
||||||
|
fi \
|
||||||
|
))
|
||||||
|
|
||||||
|
ifeq ($(IS_GKI),TRUE)
|
||||||
|
$(info -- KernelSU: Kernel version is GKI.)
|
||||||
|
# GKI manual hook checks
|
||||||
|
# https://github.com/Pzqqt/android_kernel_xiaomi_marble/commit/5b8596b5604bcd0e6e12697a01136a0bb9eb0257
|
||||||
|
ifeq ($(strip $(CONFIG_KSU_MANUAL_HOOK)),y)
|
||||||
|
$(info -- KernelSU: Hooks with Manual hook!)
|
||||||
|
ifeq ($(strip $(CONFIG_KSU)),m)
|
||||||
|
$(error CONFIG_KSU_MANUAL_HOOK cannot be enabled when compiling KernelSU as LKM!)
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
ccflags-y += -DKSU_HOOK_WITH_KPROBES
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
obj-$(CONFIG_KSU) += kernelsu.o
|
||||||
|
|
||||||
|
|
||||||
|
KSU_MANUAL_VERSION := 12500
|
||||||
|
|
||||||
|
ifeq ($(strip $(KSU_MANUAL_VERSION)),)
|
||||||
|
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
|
||||||
|
$(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin [ -f ../.git/shallow ] && git fetch --unshallow)
|
||||||
|
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count HEAD)
|
||||||
|
# ksu_version: major * 10000 + git version + 600 for historical reasons
|
||||||
|
$(eval KSU_VERSION=$(shell expr 12000 + $(KSU_GIT_VERSION) + 500))
|
||||||
|
$(info -- KernelSU version (Git-based): $(KSU_VERSION))
|
||||||
|
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
||||||
|
else
|
||||||
|
# .git is a text file while the module is imported by 'git submodule add'.
|
||||||
|
$(warning "KSU_GIT_VERSION not defined! Using default version.")
|
||||||
|
KSU_VERSION := 12500
|
||||||
|
$(info -- KernelSU version (Default): $(KSU_VERSION))
|
||||||
|
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
KSU_VERSION := $(KSU_MANUAL_VERSION)
|
||||||
|
$(info -- KernelSU version (Manual): $(KSU_VERSION))
|
||||||
|
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# SELinux drivers check
|
||||||
|
ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE
|
||||||
|
endif
|
||||||
|
|
||||||
|
# This feature was introduced in linux 5.0-rc1
|
||||||
|
ifeq ($(shell grep -q "get_cred_rcu" $(srctree)/include/linux/cred.h; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_COMPAT_HAS_GET_CRED_RCU
|
||||||
|
else
|
||||||
|
# https://github.com/gregkh/linux/commit/123e44b9a49a42ff5313ec65256f1540d7c52fa0
|
||||||
|
ifeq ($(shell grep -q "atomic_long_t\s\+\usage" $(srctree)/include/linux/cred.h; echo $$?),0)
|
||||||
|
$(info -- KernelSU compat: atomic_long_t detected.)
|
||||||
|
ccflags-y += -DKSU_COMPAT_ATOMIC_LONG
|
||||||
|
else
|
||||||
|
$(info -- KernelSU compat: atomic_t detected.)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Handle optional backports
|
||||||
|
ifeq ($(shell grep -q "strncpy_from_user_nofault" $(srctree)/include/linux/uaccess.h; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_STRNCPY_FROM_USER_NOFAULT
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(shell grep -q "ssize_t kernel_read" $(srctree)/fs/read_write.c; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_KERNEL_READ
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(shell grep "ssize_t kernel_write" $(srctree)/fs/read_write.c | grep -q "const void" ; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_KERNEL_WRITE
|
||||||
|
endif
|
||||||
|
# Checks Samsung UH drivers
|
||||||
|
ifeq ($(shell grep -q "CONFIG_KDP_CRED" $(srctree)/kernel/cred.c; echo $$?),0)
|
||||||
|
ccflags-y += -DSAMSUNG_UH_DRIVER_EXIST
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifndef KSU_EXPECTED_SIZE
|
||||||
|
KSU_EXPECTED_SIZE := 0x35c
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifndef KSU_EXPECTED_HASH
|
||||||
|
KSU_EXPECTED_HASH := 947ae944f3de4ed4c21a7e4f7953ecf351bfa2b36239da37a34111ad29993eef
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifdef KSU_MANAGER_PACKAGE
|
||||||
|
ccflags-y += -DKSU_MANAGER_PACKAGE=\"$(KSU_MANAGER_PACKAGE)\"
|
||||||
|
$(info -- KernelSU Manager package name: $(KSU_MANAGER_PACKAGE))
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(info -- KernelSU Manager signature size: $(KSU_EXPECTED_SIZE))
|
||||||
|
$(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))
|
||||||
|
$(info -- Supported Unofficial Manager: 5ec1cff (GKI) rsuntk (Non-GKI) ShirkNeko (GKI and non-GKI))
|
||||||
|
KERNEL_VERSION := $(VERSION).$(PATCHLEVEL)
|
||||||
|
$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
|
||||||
|
|
||||||
|
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
|
||||||
|
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
|
||||||
|
|
||||||
|
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||||
|
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
||||||
|
|
||||||
|
## For susfs stuff ##
|
||||||
|
ifeq ($(shell test -e $(srctree)/fs/susfs.c; echo $$?),0)
|
||||||
|
$(eval SUSFS_VERSION=$(shell cat $(srctree)/include/linux/susfs.h | grep -E '^#define SUSFS_VERSION' | cut -d' ' -f3 | sed 's/"//g'))
|
||||||
|
$(info )
|
||||||
|
$(info -- SUSFS_VERSION: $(SUSFS_VERSION))
|
||||||
|
else
|
||||||
|
$(info -- You have not integrate susfs in your kernel.)
|
||||||
|
$(info -- Read: https://gitlab.com/simonpunk/susfs4ksu)
|
||||||
|
endif
|
||||||
|
# Keep a new line here!! Because someone may append config
|
||||||
|
|
||||||
533
kernel/allowlist.c
Normal file
533
kernel/allowlist.c
Normal file
@@ -0,0 +1,533 @@
|
|||||||
|
#include <linux/capability.h>
|
||||||
|
#include <linux/compiler.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/gfp.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||||
|
#include <linux/compiler_types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "ksu.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "manager.h"
|
||||||
|
|
||||||
|
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
|
||||||
|
#define FILE_FORMAT_VERSION 3 // u32
|
||||||
|
|
||||||
|
#define KSU_APP_PROFILE_PRESERVE_UID 9999 // NOBODY_UID
|
||||||
|
#define KSU_DEFAULT_SELINUX_DOMAIN "u:r:su:s0"
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(allowlist_mutex);
|
||||||
|
|
||||||
|
// default profiles, these may be used frequently, so we cache it
|
||||||
|
static struct root_profile default_root_profile;
|
||||||
|
static struct non_root_profile default_non_root_profile;
|
||||||
|
|
||||||
|
static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly __aligned(PAGE_SIZE);
|
||||||
|
static int allow_list_pointer __read_mostly = 0;
|
||||||
|
|
||||||
|
static void remove_uid_from_arr(uid_t uid)
|
||||||
|
{
|
||||||
|
int *temp_arr;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
if (allow_list_pointer == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL);
|
||||||
|
if (temp_arr == NULL) {
|
||||||
|
pr_err("%s: unable to allocate memory\n", __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = j = 0; i < allow_list_pointer; i++) {
|
||||||
|
if (allow_list_arr[i] == uid)
|
||||||
|
continue;
|
||||||
|
temp_arr[j++] = allow_list_arr[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
allow_list_pointer = j;
|
||||||
|
|
||||||
|
for (; j < ARRAY_SIZE(allow_list_arr); j++)
|
||||||
|
temp_arr[j] = -1;
|
||||||
|
|
||||||
|
memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
|
||||||
|
kfree(temp_arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_default_profiles()
|
||||||
|
{
|
||||||
|
kernel_cap_t full_cap = CAP_FULL_SET;
|
||||||
|
|
||||||
|
default_root_profile.uid = 0;
|
||||||
|
default_root_profile.gid = 0;
|
||||||
|
default_root_profile.groups_count = 1;
|
||||||
|
default_root_profile.groups[0] = 0;
|
||||||
|
memcpy(&default_root_profile.capabilities.effective, &full_cap,
|
||||||
|
sizeof(default_root_profile.capabilities.effective));
|
||||||
|
default_root_profile.namespaces = 0;
|
||||||
|
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
||||||
|
|
||||||
|
// This means that we will umount modules by default!
|
||||||
|
default_non_root_profile.umount_modules = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct perm_data {
|
||||||
|
struct list_head list;
|
||||||
|
struct app_profile profile;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct list_head allow_list;
|
||||||
|
|
||||||
|
static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE);
|
||||||
|
#define BITMAP_UID_MAX ((sizeof(allow_list_bitmap) * BITS_PER_BYTE) - 1)
|
||||||
|
|
||||||
|
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
|
||||||
|
|
||||||
|
static struct work_struct ksu_save_work;
|
||||||
|
static struct work_struct ksu_load_work;
|
||||||
|
|
||||||
|
static bool persistent_allow_list(void);
|
||||||
|
|
||||||
|
void ksu_show_allow_list(void)
|
||||||
|
{
|
||||||
|
struct perm_data *p = NULL;
|
||||||
|
struct list_head *pos = NULL;
|
||||||
|
pr_info("ksu_show_allow_list\n");
|
||||||
|
list_for_each (pos, &allow_list) {
|
||||||
|
p = list_entry(pos, struct perm_data, list);
|
||||||
|
pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
|
||||||
|
p->profile.allow_su);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
static void ksu_grant_root_to_shell()
|
||||||
|
{
|
||||||
|
struct app_profile profile = {
|
||||||
|
.version = KSU_APP_PROFILE_VER,
|
||||||
|
.allow_su = true,
|
||||||
|
.current_uid = 2000,
|
||||||
|
};
|
||||||
|
strcpy(profile.key, "com.android.shell");
|
||||||
|
strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
||||||
|
ksu_set_app_profile(&profile, false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool ksu_get_app_profile(struct app_profile *profile)
|
||||||
|
{
|
||||||
|
struct perm_data *p = NULL;
|
||||||
|
struct list_head *pos = NULL;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
list_for_each (pos, &allow_list) {
|
||||||
|
p = list_entry(pos, struct perm_data, list);
|
||||||
|
bool uid_match = profile->current_uid == p->profile.current_uid;
|
||||||
|
if (uid_match) {
|
||||||
|
// found it, override it with ours
|
||||||
|
memcpy(profile, &p->profile, sizeof(*profile));
|
||||||
|
found = true;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool forbid_system_uid(uid_t uid) {
|
||||||
|
#define SHELL_UID 2000
|
||||||
|
#define SYSTEM_UID 1000
|
||||||
|
return uid < SHELL_UID && uid != SYSTEM_UID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool profile_valid(struct app_profile *profile)
|
||||||
|
{
|
||||||
|
if (!profile) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forbid_system_uid(profile->current_uid)) {
|
||||||
|
pr_err("uid lower than 2000 is unsupported: %d\n", profile->current_uid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile->version < KSU_APP_PROFILE_VER) {
|
||||||
|
pr_info("Unsupported profile version: %d\n", profile->version);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile->allow_su) {
|
||||||
|
if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen(profile->rp_config.profile.selinux_domain) == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ksu_set_app_profile(struct app_profile *profile, bool persist)
|
||||||
|
{
|
||||||
|
struct perm_data *p = NULL;
|
||||||
|
struct list_head *pos = NULL;
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
if (!profile_valid(profile)) {
|
||||||
|
pr_err("Failed to set app profile: invalid profile!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each (pos, &allow_list) {
|
||||||
|
p = list_entry(pos, struct perm_data, list);
|
||||||
|
// both uid and package must match, otherwise it will break multiple package with different user id
|
||||||
|
if (profile->current_uid == p->profile.current_uid &&
|
||||||
|
!strcmp(profile->key, p->profile.key)) {
|
||||||
|
// found it, just override it all!
|
||||||
|
memcpy(&p->profile, profile, sizeof(*profile));
|
||||||
|
result = true;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not found, alloc a new node!
|
||||||
|
p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL);
|
||||||
|
if (!p) {
|
||||||
|
pr_err("ksu_set_app_profile alloc failed\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&p->profile, profile, sizeof(*profile));
|
||||||
|
if (profile->allow_su) {
|
||||||
|
pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n",
|
||||||
|
profile->key, profile->current_uid,
|
||||||
|
profile->rp_config.profile.gid,
|
||||||
|
profile->rp_config.profile.selinux_domain);
|
||||||
|
} else {
|
||||||
|
pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n",
|
||||||
|
profile->key, profile->current_uid,
|
||||||
|
profile->nrp_config.profile.umount_modules);
|
||||||
|
}
|
||||||
|
list_add_tail(&p->list, &allow_list);
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (profile->current_uid <= BITMAP_UID_MAX) {
|
||||||
|
if (profile->allow_su)
|
||||||
|
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |= 1 << (profile->current_uid % BITS_PER_BYTE);
|
||||||
|
else
|
||||||
|
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &= ~(1 << (profile->current_uid % BITS_PER_BYTE));
|
||||||
|
} else {
|
||||||
|
if (profile->allow_su) {
|
||||||
|
/*
|
||||||
|
* 1024 apps with uid higher than BITMAP_UID_MAX
|
||||||
|
* registered to request superuser?
|
||||||
|
*/
|
||||||
|
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
|
||||||
|
pr_err("too many apps registered\n");
|
||||||
|
WARN_ON(1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
allow_list_arr[allow_list_pointer++] = profile->current_uid;
|
||||||
|
} else {
|
||||||
|
remove_uid_from_arr(profile->current_uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = true;
|
||||||
|
|
||||||
|
// check if the default profiles is changed, cache it to a single struct to accelerate access.
|
||||||
|
if (unlikely(!strcmp(profile->key, "$"))) {
|
||||||
|
// set default non root profile
|
||||||
|
memcpy(&default_non_root_profile, &profile->nrp_config.profile,
|
||||||
|
sizeof(default_non_root_profile));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(!strcmp(profile->key, "#"))) {
|
||||||
|
// set default root profile
|
||||||
|
memcpy(&default_root_profile, &profile->rp_config.profile,
|
||||||
|
sizeof(default_root_profile));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (persist)
|
||||||
|
persistent_allow_list();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool __ksu_is_allow_uid(uid_t uid)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (unlikely(uid == 0)) {
|
||||||
|
// already root, but only allow our domain.
|
||||||
|
return ksu_is_ksu_domain();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forbid_system_uid(uid)) {
|
||||||
|
// do not bother going through the list if it's system
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
|
||||||
|
// manager is always allowed!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (likely(uid <= BITMAP_UID_MAX)) {
|
||||||
|
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE)));
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < allow_list_pointer; i++) {
|
||||||
|
if (allow_list_arr[i] == uid)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ksu_uid_should_umount(uid_t uid)
|
||||||
|
{
|
||||||
|
struct app_profile profile = { .current_uid = uid };
|
||||||
|
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
|
||||||
|
// we should not umount on manager!
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool found = ksu_get_app_profile(&profile);
|
||||||
|
if (!found) {
|
||||||
|
// no app profile found, it must be non root app
|
||||||
|
return default_non_root_profile.umount_modules;
|
||||||
|
}
|
||||||
|
if (profile.allow_su) {
|
||||||
|
// if found and it is granted to su, we shouldn't umount for it
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// found an app profile
|
||||||
|
if (profile.nrp_config.use_default) {
|
||||||
|
return default_non_root_profile.umount_modules;
|
||||||
|
} else {
|
||||||
|
return profile.nrp_config.profile.umount_modules;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct root_profile *ksu_get_root_profile(uid_t uid)
|
||||||
|
{
|
||||||
|
struct perm_data *p = NULL;
|
||||||
|
struct list_head *pos = NULL;
|
||||||
|
|
||||||
|
list_for_each (pos, &allow_list) {
|
||||||
|
p = list_entry(pos, struct perm_data, list);
|
||||||
|
if (uid == p->profile.current_uid && p->profile.allow_su) {
|
||||||
|
if (!p->profile.rp_config.use_default) {
|
||||||
|
return &p->profile.rp_config.profile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use default profile
|
||||||
|
return &default_root_profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ksu_get_allow_list(int *array, int *length, bool allow)
|
||||||
|
{
|
||||||
|
struct perm_data *p = NULL;
|
||||||
|
struct list_head *pos = NULL;
|
||||||
|
int i = 0;
|
||||||
|
list_for_each (pos, &allow_list) {
|
||||||
|
p = list_entry(pos, struct perm_data, list);
|
||||||
|
// pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
|
||||||
|
if (p->profile.allow_su == allow) {
|
||||||
|
array[i++] = p->profile.current_uid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*length = i;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_save_allow_list(struct work_struct *work)
|
||||||
|
{
|
||||||
|
u32 magic = FILE_MAGIC;
|
||||||
|
u32 version = FILE_FORMAT_VERSION;
|
||||||
|
struct perm_data *p = NULL;
|
||||||
|
struct list_head *pos = NULL;
|
||||||
|
loff_t off = 0;
|
||||||
|
|
||||||
|
struct file *fp =
|
||||||
|
ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// store magic and version
|
||||||
|
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) !=
|
||||||
|
sizeof(magic)) {
|
||||||
|
pr_err("save_allow_list write magic failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) !=
|
||||||
|
sizeof(version)) {
|
||||||
|
pr_err("save_allow_list write version failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each (pos, &allow_list) {
|
||||||
|
p = list_entry(pos, struct perm_data, list);
|
||||||
|
pr_info("save allow list, name: %s uid :%d, allow: %d\n",
|
||||||
|
p->profile.key, p->profile.current_uid,
|
||||||
|
p->profile.allow_su);
|
||||||
|
|
||||||
|
ksu_kernel_write_compat(fp, &p->profile, sizeof(p->profile),
|
||||||
|
&off);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
filp_close(fp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_load_allow_list(struct work_struct *work)
|
||||||
|
{
|
||||||
|
loff_t off = 0;
|
||||||
|
ssize_t ret = 0;
|
||||||
|
struct file *fp = NULL;
|
||||||
|
u32 magic;
|
||||||
|
u32 version;
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
// always allow adb shell by default
|
||||||
|
ksu_grant_root_to_shell();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// load allowlist now!
|
||||||
|
fp = ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// verify magic
|
||||||
|
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) !=
|
||||||
|
sizeof(magic) ||
|
||||||
|
magic != FILE_MAGIC) {
|
||||||
|
pr_err("allowlist file invalid: %d!\n", magic);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) !=
|
||||||
|
sizeof(version)) {
|
||||||
|
pr_err("allowlist read version: %d failed\n", version);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("allowlist version: %d\n", version);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
struct app_profile profile;
|
||||||
|
|
||||||
|
ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile),
|
||||||
|
&off);
|
||||||
|
|
||||||
|
if (ret <= 0) {
|
||||||
|
pr_info("load_allow_list read err: %zd\n", ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n",
|
||||||
|
profile.key, profile.current_uid, profile.allow_su);
|
||||||
|
ksu_set_app_profile(&profile, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
ksu_show_allow_list();
|
||||||
|
filp_close(fp, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), void *data)
|
||||||
|
{
|
||||||
|
struct perm_data *np = NULL;
|
||||||
|
struct perm_data *n = NULL;
|
||||||
|
|
||||||
|
bool modified = false;
|
||||||
|
// TODO: use RCU!
|
||||||
|
mutex_lock(&allowlist_mutex);
|
||||||
|
list_for_each_entry_safe (np, n, &allow_list, list) {
|
||||||
|
uid_t uid = np->profile.current_uid;
|
||||||
|
char *package = np->profile.key;
|
||||||
|
// we use this uid for special cases, don't prune it!
|
||||||
|
bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID;
|
||||||
|
if (!is_preserved_uid && !is_uid_valid(uid, package, data)) {
|
||||||
|
modified = true;
|
||||||
|
pr_info("prune uid: %d, package: %s\n", uid, package);
|
||||||
|
list_del(&np->list);
|
||||||
|
if (likely(uid <= BITMAP_UID_MAX)) {
|
||||||
|
allow_list_bitmap[uid / BITS_PER_BYTE] &= ~(1 << (uid % BITS_PER_BYTE));
|
||||||
|
}
|
||||||
|
remove_uid_from_arr(uid);
|
||||||
|
smp_mb();
|
||||||
|
kfree(np);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&allowlist_mutex);
|
||||||
|
|
||||||
|
if (modified) {
|
||||||
|
persistent_allow_list();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure allow list works cross boot
|
||||||
|
static bool persistent_allow_list(void)
|
||||||
|
{
|
||||||
|
return ksu_queue_work(&ksu_save_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ksu_load_allow_list(void)
|
||||||
|
{
|
||||||
|
return ksu_queue_work(&ksu_load_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_allowlist_init(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
|
||||||
|
BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
|
||||||
|
allow_list_arr[i] = -1;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&allow_list);
|
||||||
|
|
||||||
|
INIT_WORK(&ksu_save_work, do_save_allow_list);
|
||||||
|
INIT_WORK(&ksu_load_work, do_load_allow_list);
|
||||||
|
|
||||||
|
init_default_profiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_allowlist_exit(void)
|
||||||
|
{
|
||||||
|
struct perm_data *np = NULL;
|
||||||
|
struct perm_data *n = NULL;
|
||||||
|
|
||||||
|
do_save_allow_list(NULL);
|
||||||
|
|
||||||
|
// free allowlist
|
||||||
|
mutex_lock(&allowlist_mutex);
|
||||||
|
list_for_each_entry_safe (np, n, &allow_list, list) {
|
||||||
|
list_del(&np->list);
|
||||||
|
kfree(np);
|
||||||
|
}
|
||||||
|
mutex_unlock(&allowlist_mutex);
|
||||||
|
}
|
||||||
27
kernel/allowlist.h
Normal file
27
kernel/allowlist.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef __KSU_H_ALLOWLIST
|
||||||
|
#define __KSU_H_ALLOWLIST
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include "ksu.h"
|
||||||
|
|
||||||
|
void ksu_allowlist_init(void);
|
||||||
|
|
||||||
|
void ksu_allowlist_exit(void);
|
||||||
|
|
||||||
|
bool ksu_load_allow_list(void);
|
||||||
|
|
||||||
|
void ksu_show_allow_list(void);
|
||||||
|
|
||||||
|
bool __ksu_is_allow_uid(uid_t uid);
|
||||||
|
#define ksu_is_allow_uid(uid) unlikely(__ksu_is_allow_uid(uid))
|
||||||
|
|
||||||
|
bool ksu_get_allow_list(int *array, int *length, bool allow);
|
||||||
|
|
||||||
|
void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, char *, void *), void *data);
|
||||||
|
|
||||||
|
bool ksu_get_app_profile(struct app_profile *);
|
||||||
|
bool ksu_set_app_profile(struct app_profile *, bool persist);
|
||||||
|
|
||||||
|
bool ksu_uid_should_umount(uid_t uid);
|
||||||
|
struct root_profile *ksu_get_root_profile(uid_t uid);
|
||||||
|
#endif
|
||||||
331
kernel/apk_sign.c
Normal file
331
kernel/apk_sign.c
Normal file
@@ -0,0 +1,331 @@
|
|||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/gfp.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
#include <linux/moduleparam.h>
|
||||||
|
#endif
|
||||||
|
#include <crypto/hash.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
#include <crypto/sha2.h>
|
||||||
|
#else
|
||||||
|
#include <crypto/sha.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "apk_sign.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct sdesc {
|
||||||
|
struct shash_desc shash;
|
||||||
|
char ctx[];
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct sdesc *init_sdesc(struct crypto_shash *alg)
|
||||||
|
{
|
||||||
|
struct sdesc *sdesc;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
|
||||||
|
sdesc = kmalloc(size, GFP_KERNEL);
|
||||||
|
if (!sdesc)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
sdesc->shash.tfm = alg;
|
||||||
|
return sdesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int calc_hash(struct crypto_shash *alg, const unsigned char *data,
|
||||||
|
unsigned int datalen, unsigned char *digest)
|
||||||
|
{
|
||||||
|
struct sdesc *sdesc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
sdesc = init_sdesc(alg);
|
||||||
|
if (IS_ERR(sdesc)) {
|
||||||
|
pr_info("can't alloc sdesc\n");
|
||||||
|
return PTR_ERR(sdesc);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
|
||||||
|
kfree(sdesc);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ksu_sha256(const unsigned char *data, unsigned int datalen,
|
||||||
|
unsigned char *digest)
|
||||||
|
{
|
||||||
|
struct crypto_shash *alg;
|
||||||
|
char *hash_alg_name = "sha256";
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
alg = crypto_alloc_shash(hash_alg_name, 0, 0);
|
||||||
|
if (IS_ERR(alg)) {
|
||||||
|
pr_info("can't alloc alg %s\n", hash_alg_name);
|
||||||
|
return PTR_ERR(alg);
|
||||||
|
}
|
||||||
|
ret = calc_hash(alg, data, datalen, digest);
|
||||||
|
crypto_free_shash(alg);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
|
||||||
|
unsigned expected_size, const char *expected_sha256)
|
||||||
|
{
|
||||||
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
|
||||||
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
|
||||||
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length
|
||||||
|
|
||||||
|
*offset += 0x4 * 3;
|
||||||
|
|
||||||
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length
|
||||||
|
|
||||||
|
*pos += *size4;
|
||||||
|
*offset += 0x4 + *size4;
|
||||||
|
|
||||||
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length
|
||||||
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length
|
||||||
|
*offset += 0x4 * 2;
|
||||||
|
|
||||||
|
if (*size4 == expected_size) {
|
||||||
|
*offset += *size4;
|
||||||
|
|
||||||
|
#define CERT_MAX_LENGTH 1024
|
||||||
|
char cert[CERT_MAX_LENGTH];
|
||||||
|
if (*size4 > CERT_MAX_LENGTH) {
|
||||||
|
pr_info("cert length overlimit\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ksu_kernel_read_compat(fp, cert, *size4, pos);
|
||||||
|
unsigned char digest[SHA256_DIGEST_SIZE];
|
||||||
|
if (IS_ERR(ksu_sha256(cert, *size4, digest))) {
|
||||||
|
pr_info("sha256 error\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
|
||||||
|
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
||||||
|
|
||||||
|
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
||||||
|
pr_info("sha256: %s, expected: %s\n", hash_str,
|
||||||
|
expected_sha256);
|
||||||
|
if (strcmp(expected_sha256, hash_str) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct zip_entry_header {
|
||||||
|
uint32_t signature;
|
||||||
|
uint16_t version;
|
||||||
|
uint16_t flags;
|
||||||
|
uint16_t compression;
|
||||||
|
uint16_t mod_time;
|
||||||
|
uint16_t mod_date;
|
||||||
|
uint32_t crc32;
|
||||||
|
uint32_t compressed_size;
|
||||||
|
uint32_t uncompressed_size;
|
||||||
|
uint16_t file_name_length;
|
||||||
|
uint16_t extra_field_length;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
// This is a necessary but not sufficient condition, but it is enough for us
|
||||||
|
static bool has_v1_signature_file(struct file *fp)
|
||||||
|
{
|
||||||
|
struct zip_entry_header header;
|
||||||
|
const char MANIFEST[] = "META-INF/MANIFEST.MF";
|
||||||
|
|
||||||
|
loff_t pos = 0;
|
||||||
|
|
||||||
|
while (ksu_kernel_read_compat(fp, &header,
|
||||||
|
sizeof(struct zip_entry_header), &pos) ==
|
||||||
|
sizeof(struct zip_entry_header)) {
|
||||||
|
if (header.signature != 0x04034b50) {
|
||||||
|
// ZIP magic: 'PK'
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Read the entry file name
|
||||||
|
if (header.file_name_length == sizeof(MANIFEST) - 1) {
|
||||||
|
char fileName[sizeof(MANIFEST)];
|
||||||
|
ksu_kernel_read_compat(fp, fileName,
|
||||||
|
header.file_name_length, &pos);
|
||||||
|
fileName[header.file_name_length] = '\0';
|
||||||
|
|
||||||
|
// Check if the entry matches META-INF/MANIFEST.MF
|
||||||
|
if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) ==
|
||||||
|
0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Skip the entry file name
|
||||||
|
pos += header.file_name_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip to the next entry
|
||||||
|
pos += header.extra_field_length + header.compressed_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __always_inline bool check_v2_signature(char *path,
|
||||||
|
unsigned expected_size,
|
||||||
|
const char *expected_sha256)
|
||||||
|
{
|
||||||
|
unsigned char buffer[0x11] = { 0 };
|
||||||
|
u32 size4;
|
||||||
|
u64 size8, size_of_block;
|
||||||
|
|
||||||
|
loff_t pos;
|
||||||
|
|
||||||
|
bool v2_signing_valid = false;
|
||||||
|
int v2_signing_blocks = 0;
|
||||||
|
bool v3_signing_exist = false;
|
||||||
|
bool v3_1_signing_exist = false;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("open %s error.\n", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable inotify for this file
|
||||||
|
fp->f_mode |= FMODE_NONOTIFY;
|
||||||
|
|
||||||
|
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
|
||||||
|
for (i = 0;; ++i) {
|
||||||
|
unsigned short n;
|
||||||
|
pos = generic_file_llseek(fp, -i - 2, SEEK_END);
|
||||||
|
ksu_kernel_read_compat(fp, &n, 2, &pos);
|
||||||
|
if (n == i) {
|
||||||
|
pos -= 22;
|
||||||
|
ksu_kernel_read_compat(fp, &size4, 4, &pos);
|
||||||
|
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == 0xffff) {
|
||||||
|
pr_info("error: cannot find eocd\n");
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += 12;
|
||||||
|
// offset
|
||||||
|
ksu_kernel_read_compat(fp, &size4, 0x4, &pos);
|
||||||
|
pos = size4 - 0x18;
|
||||||
|
|
||||||
|
ksu_kernel_read_compat(fp, &size8, 0x8, &pos);
|
||||||
|
ksu_kernel_read_compat(fp, buffer, 0x10, &pos);
|
||||||
|
if (strcmp((char *)buffer, "APK Sig Block 42")) {
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = size4 - (size8 + 0x8);
|
||||||
|
ksu_kernel_read_compat(fp, &size_of_block, 0x8, &pos);
|
||||||
|
if (size_of_block != size8) {
|
||||||
|
goto clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
int loop_count = 0;
|
||||||
|
while (loop_count++ < 10) {
|
||||||
|
uint32_t id;
|
||||||
|
uint32_t offset;
|
||||||
|
ksu_kernel_read_compat(fp, &size8, 0x8,
|
||||||
|
&pos); // sequence length
|
||||||
|
if (size8 == size_of_block) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id
|
||||||
|
offset = 4;
|
||||||
|
if (id == 0x7109871au) {
|
||||||
|
v2_signing_blocks++;
|
||||||
|
v2_signing_valid =
|
||||||
|
check_block(fp, &size4, &pos, &offset,
|
||||||
|
expected_size, expected_sha256);
|
||||||
|
} else if (id == 0xf05368c0u) {
|
||||||
|
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
|
||||||
|
v3_signing_exist = true;
|
||||||
|
} else if (id == 0x1b93ad61u) {
|
||||||
|
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74
|
||||||
|
v3_1_signing_exist = true;
|
||||||
|
} else {
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
pr_info("Unknown id: 0x%08x\n", id);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
pos += (size8 - offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v2_signing_blocks != 1) {
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
pr_err("Unexpected v2 signature count: %d\n",
|
||||||
|
v2_signing_blocks);
|
||||||
|
#endif
|
||||||
|
v2_signing_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v2_signing_valid) {
|
||||||
|
int has_v1_signing = has_v1_signature_file(fp);
|
||||||
|
if (has_v1_signing) {
|
||||||
|
pr_err("Unexpected v1 signature scheme found!\n");
|
||||||
|
filp_close(fp, 0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clean:
|
||||||
|
filp_close(fp, 0);
|
||||||
|
|
||||||
|
if (v3_signing_exist || v3_1_signing_exist) {
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
pr_err("Unexpected v3 signature scheme found!\n");
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return v2_signing_valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
|
||||||
|
int ksu_debug_manager_uid = -1;
|
||||||
|
|
||||||
|
#include "manager.h"
|
||||||
|
|
||||||
|
static int set_expected_size(const char *val, const struct kernel_param *kp)
|
||||||
|
{
|
||||||
|
int rv = param_set_uint(val, kp);
|
||||||
|
ksu_set_manager_uid(ksu_debug_manager_uid);
|
||||||
|
pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct kernel_param_ops expected_size_ops = {
|
||||||
|
.set = set_expected_size,
|
||||||
|
.get = param_get_uint,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
|
||||||
|
&ksu_debug_manager_uid, S_IRUSR | S_IWUSR);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// include custom manager header
|
||||||
|
#include "manager_sign.h"
|
||||||
|
|
||||||
|
bool ksu_is_manager_apk(char *path)
|
||||||
|
{
|
||||||
|
return (check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH) ||
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||||
|
check_v2_signature(path, EXPECTED_SIZE_5EC1CFF, EXPECTED_HASH_5EC1CFF) ||
|
||||||
|
check_v2_signature(path, EXPECTED_SIZE_WEISHU, EXPECTED_HASH_WEISHU) ||
|
||||||
|
check_v2_signature(path, EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO) ||
|
||||||
|
#endif
|
||||||
|
check_v2_signature(path, EXPECTED_SIZE_RSUNTK, EXPECTED_HASH_RSUNTK) ||
|
||||||
|
check_v2_signature(path, EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO) ||
|
||||||
|
check_v2_signature(path, EXPECTED_SIZE_NEKO, EXPECTED_HASH_NEKO));
|
||||||
|
}
|
||||||
8
kernel/apk_sign.h
Normal file
8
kernel/apk_sign.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef __KSU_H_APK_V2_SIGN
|
||||||
|
#define __KSU_H_APK_V2_SIGN
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
bool ksu_is_manager_apk(char *path);
|
||||||
|
|
||||||
|
#endif
|
||||||
102
kernel/arch.h
Normal file
102
kernel/arch.h
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#ifndef __KSU_H_ARCH
|
||||||
|
#define __KSU_H_ARCH
|
||||||
|
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#if defined(__aarch64__)
|
||||||
|
|
||||||
|
#define __PT_PARM1_REG regs[0]
|
||||||
|
#define __PT_PARM2_REG regs[1]
|
||||||
|
#define __PT_PARM3_REG regs[2]
|
||||||
|
#define __PT_SYSCALL_PARM4_REG regs[3]
|
||||||
|
#define __PT_CCALL_PARM4_REG regs[3]
|
||||||
|
#define __PT_PARM5_REG regs[4]
|
||||||
|
#define __PT_PARM6_REG regs[5]
|
||||||
|
#define __PT_RET_REG regs[30]
|
||||||
|
#define __PT_FP_REG regs[29] /* Works only with CONFIG_FRAME_POINTER */
|
||||||
|
#define __PT_RC_REG regs[0]
|
||||||
|
#define __PT_SP_REG sp
|
||||||
|
#define __PT_IP_REG pc
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||||
|
#define PRCTL_SYMBOL "__arm64_sys_prctl"
|
||||||
|
#define SYS_READ_SYMBOL "__arm64_sys_read"
|
||||||
|
#define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat"
|
||||||
|
#define SYS_FSTATAT64_SYMBOL "__arm64_sys_fstatat64"
|
||||||
|
#define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat"
|
||||||
|
#define SYS_EXECVE_SYMBOL "__arm64_sys_execve"
|
||||||
|
#define SYS_EXECVE_COMPAT_SYMBOL "__arm64_compat_sys_execve"
|
||||||
|
#else
|
||||||
|
#define PRCTL_SYMBOL "sys_prctl"
|
||||||
|
#define SYS_READ_SYMBOL "sys_read"
|
||||||
|
#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat"
|
||||||
|
#define SYS_FSTATAT64_SYMBOL "sys_fstatat64"
|
||||||
|
#define SYS_FACCESSAT_SYMBOL "sys_faccessat"
|
||||||
|
#define SYS_EXECVE_SYMBOL "sys_execve"
|
||||||
|
#define SYS_EXECVE_COMPAT_SYMBOL "compat_sys_execve"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#elif defined(__x86_64__)
|
||||||
|
|
||||||
|
#define __PT_PARM1_REG di
|
||||||
|
#define __PT_PARM2_REG si
|
||||||
|
#define __PT_PARM3_REG dx
|
||||||
|
/* syscall uses r10 for PARM4 */
|
||||||
|
#define __PT_SYSCALL_PARM4_REG r10
|
||||||
|
#define __PT_CCALL_PARM4_REG cx
|
||||||
|
#define __PT_PARM5_REG r8
|
||||||
|
#define __PT_PARM6_REG r9
|
||||||
|
#define __PT_RET_REG sp
|
||||||
|
#define __PT_FP_REG bp
|
||||||
|
#define __PT_RC_REG ax
|
||||||
|
#define __PT_SP_REG sp
|
||||||
|
#define __PT_IP_REG ip
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||||
|
#define PRCTL_SYMBOL "__x64_sys_prctl"
|
||||||
|
#define SYS_READ_SYMBOL "__x64_sys_read"
|
||||||
|
#define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat"
|
||||||
|
#define SYS_FSTATAT64_SYMBOL "__x64_sys_fstatat64"
|
||||||
|
#define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat"
|
||||||
|
#define SYS_EXECVE_SYMBOL "__x64_sys_execve"
|
||||||
|
#define SYS_EXECVE_COMPAT_SYMBOL "__x64_compat_sys_execve"
|
||||||
|
#else
|
||||||
|
#define PRCTL_SYMBOL "sys_prctl"
|
||||||
|
#define SYS_READ_SYMBOL "sys_read"
|
||||||
|
#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat"
|
||||||
|
#define SYS_FSTATAT64_SYMBOL "sys_fstatat64"
|
||||||
|
#define SYS_FACCESSAT_SYMBOL "sys_faccessat"
|
||||||
|
#define SYS_EXECVE_SYMBOL "sys_execve"
|
||||||
|
#define SYS_EXECVE_COMPAT_SYMBOL "compat_sys_execve"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
#ifdef KSU_HOOK_WITH_KPROBES
|
||||||
|
#error "Unsupported arch"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* allow some architecutres to override `struct pt_regs` */
|
||||||
|
#ifndef __PT_REGS_CAST
|
||||||
|
#define __PT_REGS_CAST(x) (x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PT_REGS_PARM1(x) (__PT_REGS_CAST(x)->__PT_PARM1_REG)
|
||||||
|
#define PT_REGS_PARM2(x) (__PT_REGS_CAST(x)->__PT_PARM2_REG)
|
||||||
|
#define PT_REGS_PARM3(x) (__PT_REGS_CAST(x)->__PT_PARM3_REG)
|
||||||
|
#define PT_REGS_SYSCALL_PARM4(x) (__PT_REGS_CAST(x)->__PT_SYSCALL_PARM4_REG)
|
||||||
|
#define PT_REGS_CCALL_PARM4(x) (__PT_REGS_CAST(x)->__PT_CCALL_PARM4_REG)
|
||||||
|
#define PT_REGS_PARM5(x) (__PT_REGS_CAST(x)->__PT_PARM5_REG)
|
||||||
|
#define PT_REGS_PARM6(x) (__PT_REGS_CAST(x)->__PT_PARM6_REG)
|
||||||
|
#define PT_REGS_RET(x) (__PT_REGS_CAST(x)->__PT_RET_REG)
|
||||||
|
#define PT_REGS_FP(x) (__PT_REGS_CAST(x)->__PT_FP_REG)
|
||||||
|
#define PT_REGS_RC(x) (__PT_REGS_CAST(x)->__PT_RC_REG)
|
||||||
|
#define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG)
|
||||||
|
#define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG)
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||||
|
#define PT_REAL_REGS(regs) ((struct pt_regs *)PT_REGS_PARM1(regs))
|
||||||
|
#else
|
||||||
|
#define PT_REAL_REGS(regs) ((regs))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
1432
kernel/core_hook.c
Normal file
1432
kernel/core_hook.c
Normal file
File diff suppressed because it is too large
Load Diff
9
kernel/core_hook.h
Normal file
9
kernel/core_hook.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#ifndef __KSU_H_KSU_CORE
|
||||||
|
#define __KSU_H_KSU_CORE
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
|
||||||
|
void __init ksu_core_init(void);
|
||||||
|
void ksu_core_exit(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
5
kernel/embed_ksud.c
Normal file
5
kernel/embed_ksud.c
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
// WARNING: THIS IS A STUB FILE
|
||||||
|
// This file will be regenerated by CI
|
||||||
|
|
||||||
|
unsigned int ksud_size = 0;
|
||||||
|
const char ksud[0] = {};
|
||||||
2
kernel/export_symbol.txt
Normal file
2
kernel/export_symbol.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
register_kprobe
|
||||||
|
unregister_kprobe
|
||||||
28
kernel/include/ksu_hook.h
Normal file
28
kernel/include/ksu_hook.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#ifndef __KSU_H_KSHOOK
|
||||||
|
#define __KSU_H_KSHOOK
|
||||||
|
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
// For sucompat
|
||||||
|
|
||||||
|
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||||
|
int *flags);
|
||||||
|
|
||||||
|
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
||||||
|
|
||||||
|
// For ksud
|
||||||
|
|
||||||
|
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||||
|
size_t *count_ptr, loff_t **pos);
|
||||||
|
|
||||||
|
// For ksud and sucompat
|
||||||
|
|
||||||
|
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
||||||
|
void *envp, int *flags);
|
||||||
|
|
||||||
|
// For volume button
|
||||||
|
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
||||||
|
int *value);
|
||||||
|
|
||||||
|
#endif
|
||||||
185
kernel/kernel_compat.c
Normal file
185
kernel/kernel_compat.c
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/nsproxy.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||||
|
#include <linux/sched/task.h>
|
||||||
|
#else
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#endif
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "kernel_compat.h" // Add check Huawei Device
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||||
|
#include <linux/key.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
struct key *init_session_keyring = NULL;
|
||||||
|
|
||||||
|
static inline int install_session_keyring(struct key *keyring)
|
||||||
|
{
|
||||||
|
struct cred *new;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
new = prepare_creds();
|
||||||
|
if (!new)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = install_session_keyring_to_cred(new, keyring);
|
||||||
|
if (ret < 0) {
|
||||||
|
abort_creds(new);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return commit_creds(new);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern struct task_struct init_task;
|
||||||
|
|
||||||
|
// mnt_ns context switch for environment that android_init->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns, such as WSA
|
||||||
|
struct ksu_ns_fs_saved {
|
||||||
|
struct nsproxy *ns;
|
||||||
|
struct fs_struct *fs;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ksu_save_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
||||||
|
{
|
||||||
|
ns_fs_saved->ns = current->nsproxy;
|
||||||
|
ns_fs_saved->fs = current->fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ksu_load_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
||||||
|
{
|
||||||
|
current->nsproxy = ns_fs_saved->ns;
|
||||||
|
current->fs = ns_fs_saved->fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool android_context_saved_checked = false;
|
||||||
|
static bool android_context_saved_enabled = false;
|
||||||
|
static struct ksu_ns_fs_saved android_context_saved;
|
||||||
|
|
||||||
|
void ksu_android_ns_fs_check()
|
||||||
|
{
|
||||||
|
if (android_context_saved_checked)
|
||||||
|
return;
|
||||||
|
android_context_saved_checked = true;
|
||||||
|
task_lock(current);
|
||||||
|
if (current->nsproxy && current->fs &&
|
||||||
|
current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) {
|
||||||
|
android_context_saved_enabled = true;
|
||||||
|
pr_info("android context saved enabled due to init mnt_ns(%p) != android mnt_ns(%p)\n",
|
||||||
|
current->nsproxy->mnt_ns, init_task.nsproxy->mnt_ns);
|
||||||
|
ksu_save_ns_fs(&android_context_saved);
|
||||||
|
} else {
|
||||||
|
pr_info("android context saved disabled\n");
|
||||||
|
}
|
||||||
|
task_unlock(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_access_ok(const void *addr, unsigned long size) {
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
|
||||||
|
/* For kernels before 5.0.0, pass the type argument to access_ok. */
|
||||||
|
return access_ok(VERIFY_READ, addr, size);
|
||||||
|
#else
|
||||||
|
/* For kernels 5.0.0 and later, ignore the type argument. */
|
||||||
|
return access_ok(addr, size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||||
|
if (init_session_keyring != NULL && !current_cred()->session_keyring &&
|
||||||
|
(current->flags & PF_WQ_WORKER)) {
|
||||||
|
pr_info("installing init session keyring for older kernel\n");
|
||||||
|
install_session_keyring(init_session_keyring);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns
|
||||||
|
struct ksu_ns_fs_saved saved;
|
||||||
|
if (android_context_saved_enabled) {
|
||||||
|
pr_info("start switch current nsproxy and fs to android context\n");
|
||||||
|
task_lock(current);
|
||||||
|
ksu_save_ns_fs(&saved);
|
||||||
|
ksu_load_ns_fs(&android_context_saved);
|
||||||
|
task_unlock(current);
|
||||||
|
}
|
||||||
|
struct file *fp = filp_open(filename, flags, mode);
|
||||||
|
if (android_context_saved_enabled) {
|
||||||
|
task_lock(current);
|
||||||
|
ksu_load_ns_fs(&saved);
|
||||||
|
task_unlock(current);
|
||||||
|
pr_info("switch current nsproxy and fs back to saved successfully\n");
|
||||||
|
}
|
||||||
|
return fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
||||||
|
loff_t *pos)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || defined(KSU_KERNEL_READ)
|
||||||
|
return kernel_read(p, buf, count, pos);
|
||||||
|
#else
|
||||||
|
loff_t offset = pos ? *pos : 0;
|
||||||
|
ssize_t result = kernel_read(p, offset, (char *)buf, count);
|
||||||
|
if (pos && result > 0) {
|
||||||
|
*pos = offset + result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count,
|
||||||
|
loff_t *pos)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || defined(KSU_KERNEL_WRITE)
|
||||||
|
return kernel_write(p, buf, count, pos);
|
||||||
|
#else
|
||||||
|
loff_t offset = pos ? *pos : 0;
|
||||||
|
ssize_t result = kernel_write(p, buf, count, offset);
|
||||||
|
if (pos && result > 0) {
|
||||||
|
*pos = offset + result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) || defined(KSU_STRNCPY_FROM_USER_NOFAULT)
|
||||||
|
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||||
|
long count)
|
||||||
|
{
|
||||||
|
return strncpy_from_user_nofault(dst, unsafe_addr, count);
|
||||||
|
}
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
|
||||||
|
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||||
|
long count)
|
||||||
|
{
|
||||||
|
return strncpy_from_unsafe_user(dst, unsafe_addr, count);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Copied from: https://elixir.bootlin.com/linux/v4.9.337/source/mm/maccess.c#L201
|
||||||
|
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||||
|
long count)
|
||||||
|
{
|
||||||
|
mm_segment_t old_fs = get_fs();
|
||||||
|
long ret;
|
||||||
|
|
||||||
|
if (unlikely(count <= 0))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
set_fs(USER_DS);
|
||||||
|
pagefault_disable();
|
||||||
|
ret = strncpy_from_user(dst, unsafe_addr, count);
|
||||||
|
pagefault_enable();
|
||||||
|
set_fs(old_fs);
|
||||||
|
|
||||||
|
if (ret >= count) {
|
||||||
|
ret = count;
|
||||||
|
dst[ret - 1] = '\0';
|
||||||
|
} else if (ret > 0) {
|
||||||
|
ret++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
67
kernel/kernel_compat.h
Normal file
67
kernel/kernel_compat.h
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#ifndef __KSU_H_KERNEL_COMPAT
|
||||||
|
#define __KSU_H_KERNEL_COMPAT
|
||||||
|
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include "ss/policydb.h"
|
||||||
|
#include "linux/key.h"
|
||||||
|
|
||||||
|
// for kernel without get_cred_rcu
|
||||||
|
#ifndef KSU_COMPAT_HAS_GET_CRED_RCU
|
||||||
|
static inline const struct cred *get_cred_rcu(const struct cred *cred)
|
||||||
|
{
|
||||||
|
struct cred *nonconst_cred = (struct cred *) cred;
|
||||||
|
if (!cred)
|
||||||
|
return NULL;
|
||||||
|
#ifdef KSU_COMPAT_ATOMIC_LONG
|
||||||
|
if (!atomic_long_inc_not_zero(&nonconst_cred->usage))
|
||||||
|
#else
|
||||||
|
if (!atomic_inc_not_zero(&nonconst_cred->usage))
|
||||||
|
#endif
|
||||||
|
return NULL;
|
||||||
|
validate_creds(cred);
|
||||||
|
nonconst_cred->non_rcu = 0;
|
||||||
|
return cred;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adapt to Huawei HISI kernel without affecting other kernels ,
|
||||||
|
* Huawei Hisi Kernel EBITMAP Enable or Disable Flag ,
|
||||||
|
* From ss/ebitmap.h
|
||||||
|
*/
|
||||||
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \
|
||||||
|
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \
|
||||||
|
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \
|
||||||
|
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
|
||||||
|
#ifdef HISI_SELINUX_EBITMAP_RO
|
||||||
|
#define CONFIG_IS_HW_HISI
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Checks for UH, KDP and RKP
|
||||||
|
#ifdef SAMSUNG_UH_DRIVER_EXIST
|
||||||
|
#if defined(CONFIG_UH) || defined(CONFIG_KDP) || defined(CONFIG_RKP)
|
||||||
|
#error "CONFIG_UH, CONFIG_KDP and CONFIG_RKP is enabled! Please disable or remove it before compile a kernel with KernelSU!"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern long ksu_strncpy_from_user_nofault(char *dst,
|
||||||
|
const void __user *unsafe_addr,
|
||||||
|
long count);
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||||
|
extern struct key *init_session_keyring;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern void ksu_android_ns_fs_check();
|
||||||
|
extern int ksu_access_ok(const void *addr, unsigned long size);
|
||||||
|
extern struct file *ksu_filp_open_compat(const char *filename, int flags,
|
||||||
|
umode_t mode);
|
||||||
|
extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
||||||
|
loff_t *pos);
|
||||||
|
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
|
||||||
|
size_t count, loff_t *pos);
|
||||||
|
|
||||||
|
#endif
|
||||||
11
kernel/klog.h
Normal file
11
kernel/klog.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef __KSU_H_KLOG
|
||||||
|
#define __KSU_H_KLOG
|
||||||
|
|
||||||
|
#include <linux/printk.h>
|
||||||
|
|
||||||
|
#ifdef pr_fmt
|
||||||
|
#undef pr_fmt
|
||||||
|
#define pr_fmt(fmt) "KernelSU: " fmt
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
108
kernel/ksu.c
Normal file
108
kernel/ksu.c
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/kobject.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "arch.h"
|
||||||
|
#include "core_hook.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "ksu.h"
|
||||||
|
#include "throne_tracker.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_SUSFS
|
||||||
|
#include <linux/susfs.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct workqueue_struct *ksu_workqueue;
|
||||||
|
|
||||||
|
bool ksu_queue_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
return queue_work(ksu_workqueue, work);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||||
|
void *argv, void *envp, int *flags);
|
||||||
|
|
||||||
|
extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||||
|
void *argv, void *envp, int *flags);
|
||||||
|
|
||||||
|
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
||||||
|
void *envp, int *flags)
|
||||||
|
{
|
||||||
|
ksu_handle_execveat_ksud(fd, filename_ptr, argv, envp, flags);
|
||||||
|
return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp,
|
||||||
|
flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void ksu_sucompat_init();
|
||||||
|
extern void ksu_sucompat_exit();
|
||||||
|
extern void ksu_ksud_init();
|
||||||
|
extern void ksu_ksud_exit();
|
||||||
|
|
||||||
|
int __init ksu_kernelsu_init(void)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
pr_alert("*************************************************************");
|
||||||
|
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
||||||
|
pr_alert("** **");
|
||||||
|
pr_alert("** You are running KernelSU in DEBUG mode **");
|
||||||
|
pr_alert("** **");
|
||||||
|
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
||||||
|
pr_alert("*************************************************************");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_SUSFS
|
||||||
|
susfs_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ksu_core_init();
|
||||||
|
|
||||||
|
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
||||||
|
|
||||||
|
ksu_allowlist_init();
|
||||||
|
|
||||||
|
ksu_throne_tracker_init();
|
||||||
|
|
||||||
|
#ifdef KSU_HOOK_WITH_KPROBES
|
||||||
|
ksu_sucompat_init();
|
||||||
|
ksu_ksud_init();
|
||||||
|
#else
|
||||||
|
pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef MODULE
|
||||||
|
#ifndef CONFIG_KSU_DEBUG
|
||||||
|
kobject_del(&THIS_MODULE->mkobj.kobj);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_kernelsu_exit(void)
|
||||||
|
{
|
||||||
|
ksu_allowlist_exit();
|
||||||
|
|
||||||
|
ksu_throne_tracker_exit();
|
||||||
|
|
||||||
|
destroy_workqueue(ksu_workqueue);
|
||||||
|
|
||||||
|
#ifdef KSU_HOOK_WITH_KPROBES
|
||||||
|
ksu_ksud_exit();
|
||||||
|
ksu_sucompat_exit();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ksu_core_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(ksu_kernelsu_init);
|
||||||
|
module_exit(ksu_kernelsu_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("weishu");
|
||||||
|
MODULE_DESCRIPTION("Android KernelSU");
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
||||||
|
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
|
||||||
|
#endif
|
||||||
101
kernel/ksu.h
Normal file
101
kernel/ksu.h
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
#ifndef __KSU_H_KSU
|
||||||
|
#define __KSU_H_KSU
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
|
#define KERNEL_SU_VERSION KSU_VERSION
|
||||||
|
#define KERNEL_SU_OPTION 0xDEADBEEF
|
||||||
|
|
||||||
|
#define CMD_GRANT_ROOT 0
|
||||||
|
#define CMD_BECOME_MANAGER 1
|
||||||
|
#define CMD_GET_VERSION 2
|
||||||
|
#define CMD_ALLOW_SU 3
|
||||||
|
#define CMD_DENY_SU 4
|
||||||
|
#define CMD_GET_ALLOW_LIST 5
|
||||||
|
#define CMD_GET_DENY_LIST 6
|
||||||
|
#define CMD_REPORT_EVENT 7
|
||||||
|
#define CMD_SET_SEPOLICY 8
|
||||||
|
#define CMD_CHECK_SAFEMODE 9
|
||||||
|
#define CMD_GET_APP_PROFILE 10
|
||||||
|
#define CMD_SET_APP_PROFILE 11
|
||||||
|
#define CMD_UID_GRANTED_ROOT 12
|
||||||
|
#define CMD_UID_SHOULD_UMOUNT 13
|
||||||
|
#define CMD_IS_SU_ENABLED 14
|
||||||
|
#define CMD_ENABLE_SU 15
|
||||||
|
|
||||||
|
#define EVENT_POST_FS_DATA 1
|
||||||
|
#define EVENT_BOOT_COMPLETED 2
|
||||||
|
#define EVENT_MODULE_MOUNTED 3
|
||||||
|
|
||||||
|
#define KSU_APP_PROFILE_VER 2
|
||||||
|
#define KSU_MAX_PACKAGE_NAME 256
|
||||||
|
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
|
||||||
|
#define KSU_MAX_GROUPS 32
|
||||||
|
#define KSU_SELINUX_DOMAIN 64
|
||||||
|
|
||||||
|
struct root_profile {
|
||||||
|
int32_t uid;
|
||||||
|
int32_t gid;
|
||||||
|
|
||||||
|
int32_t groups_count;
|
||||||
|
int32_t groups[KSU_MAX_GROUPS];
|
||||||
|
|
||||||
|
// kernel_cap_t is u32[2] for capabilities v3
|
||||||
|
struct {
|
||||||
|
u64 effective;
|
||||||
|
u64 permitted;
|
||||||
|
u64 inheritable;
|
||||||
|
} capabilities;
|
||||||
|
|
||||||
|
char selinux_domain[KSU_SELINUX_DOMAIN];
|
||||||
|
|
||||||
|
int32_t namespaces;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct non_root_profile {
|
||||||
|
bool umount_modules;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct app_profile {
|
||||||
|
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
|
||||||
|
u32 version;
|
||||||
|
|
||||||
|
// this is usually the package of the app, but can be other value for special apps
|
||||||
|
char key[KSU_MAX_PACKAGE_NAME];
|
||||||
|
int32_t current_uid;
|
||||||
|
bool allow_su;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
bool use_default;
|
||||||
|
char template_name[KSU_MAX_PACKAGE_NAME];
|
||||||
|
|
||||||
|
struct root_profile profile;
|
||||||
|
} rp_config;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool use_default;
|
||||||
|
|
||||||
|
struct non_root_profile profile;
|
||||||
|
} nrp_config;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ksu_queue_work(struct work_struct *work);
|
||||||
|
|
||||||
|
static inline int startswith(char *s, char *prefix)
|
||||||
|
{
|
||||||
|
return strncmp(s, prefix, strlen(prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int endswith(const char *s, const char *t)
|
||||||
|
{
|
||||||
|
size_t slen = strlen(s);
|
||||||
|
size_t tlen = strlen(t);
|
||||||
|
if (tlen > slen)
|
||||||
|
return 1;
|
||||||
|
return strcmp(s + slen - tlen, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
706
kernel/ksud.c
Normal file
706
kernel/ksud.c
Normal file
@@ -0,0 +1,706 @@
|
|||||||
|
#include <asm/current.h>
|
||||||
|
#include <linux/compat.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/dcache.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/file.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||||
|
#include <linux/input-event-codes.h>
|
||||||
|
#else
|
||||||
|
#include <uapi/linux/input.h>
|
||||||
|
#endif
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
|
||||||
|
#include <linux/aio.h>
|
||||||
|
#endif
|
||||||
|
#include <linux/kprobes.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "arch.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "ksud.h"
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
|
||||||
|
#define KERNEL_VERSION_5_10 KERNEL_VERSION(5, 10, 0)
|
||||||
|
|
||||||
|
static const char KERNEL_SU_RC[] =
|
||||||
|
"\n"
|
||||||
|
"on post-fs-data\n"
|
||||||
|
" start logd\n"
|
||||||
|
" exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n"
|
||||||
|
"\n"
|
||||||
|
"on nonencrypted\n"
|
||||||
|
" exec u:r:su:s0 root -- " KSUD_PATH " services\n"
|
||||||
|
"\n"
|
||||||
|
"on property:vold.decrypt=trigger_restart_framework\n"
|
||||||
|
" exec u:r:su:s0 root -- " KSUD_PATH " services\n"
|
||||||
|
"\n"
|
||||||
|
"on property:sys.boot_completed=1\n"
|
||||||
|
" exec u:r:su:s0 root -- " KSUD_PATH " boot-completed\n"
|
||||||
|
"\n";
|
||||||
|
|
||||||
|
static void stop_vfs_read_hook();
|
||||||
|
static void stop_execve_hook();
|
||||||
|
static void stop_input_hook();
|
||||||
|
|
||||||
|
#ifdef KSU_HOOK_WITH_KPROBES
|
||||||
|
static struct work_struct stop_vfs_read_work;
|
||||||
|
static struct work_struct stop_execve_hook_work;
|
||||||
|
static struct work_struct stop_input_hook_work;
|
||||||
|
#else
|
||||||
|
bool ksu_vfs_read_hook __read_mostly = true;
|
||||||
|
bool ksu_execveat_hook __read_mostly = true;
|
||||||
|
bool ksu_input_hook __read_mostly = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_SUSFS_SUS_SU
|
||||||
|
bool ksu_devpts_hook = false;
|
||||||
|
bool susfs_is_sus_su_ready = false;
|
||||||
|
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_SU
|
||||||
|
|
||||||
|
u32 ksu_devpts_sid;
|
||||||
|
|
||||||
|
// Detect whether it is on or not
|
||||||
|
static bool is_boot_phase = true;
|
||||||
|
|
||||||
|
void ksu_on_post_fs_data(void)
|
||||||
|
{
|
||||||
|
static bool done = false;
|
||||||
|
if (done) {
|
||||||
|
pr_info("ksu_on_post_fs_data already done\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
done = true;
|
||||||
|
pr_info("ksu_on_post_fs_data!\n");
|
||||||
|
ksu_load_allow_list();
|
||||||
|
stop_input_hook();
|
||||||
|
|
||||||
|
ksu_devpts_sid = ksu_get_devpts_sid();
|
||||||
|
pr_info("devpts sid: %d\n", ksu_devpts_sid);
|
||||||
|
|
||||||
|
// End of boot state
|
||||||
|
is_boot_phase = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_ARG_STRINGS 0x7FFFFFFF
|
||||||
|
struct user_arg_ptr {
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
bool is_compat;
|
||||||
|
#endif
|
||||||
|
union {
|
||||||
|
const char __user *const __user *native;
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
const compat_uptr_t __user *compat;
|
||||||
|
#endif
|
||||||
|
} ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
|
||||||
|
{
|
||||||
|
const char __user *native;
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
if (unlikely(argv.is_compat)) {
|
||||||
|
compat_uptr_t compat;
|
||||||
|
|
||||||
|
if (get_user(compat, argv.ptr.compat + nr))
|
||||||
|
return ERR_PTR(-EFAULT);
|
||||||
|
|
||||||
|
return compat_ptr(compat);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (get_user(native, argv.ptr.native + nr))
|
||||||
|
return ERR_PTR(-EFAULT);
|
||||||
|
|
||||||
|
return native;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* count() counts the number of strings in array ARGV.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure old GCC compiler can use __maybe_unused,
|
||||||
|
* Test passed in 4.4.x ~ 4.9.x when use GCC.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int __maybe_unused count(struct user_arg_ptr argv, int max)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (argv.ptr.native != NULL) {
|
||||||
|
for (;;) {
|
||||||
|
const char __user *p = get_user_arg_ptr(argv, i);
|
||||||
|
|
||||||
|
if (!p)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (IS_ERR(p))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (i >= max)
|
||||||
|
return -E2BIG;
|
||||||
|
++i;
|
||||||
|
|
||||||
|
if (fatal_signal_pending(current))
|
||||||
|
return -ERESTARTNOHAND;
|
||||||
|
cond_resched();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version
|
||||||
|
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||||
|
struct user_arg_ptr *argv,
|
||||||
|
struct user_arg_ptr *envp, int *flags)
|
||||||
|
{
|
||||||
|
#ifndef KSU_HOOK_WITH_KPROBES
|
||||||
|
if (!ksu_execveat_hook) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
struct filename *filename;
|
||||||
|
|
||||||
|
static const char app_process[] = "/system/bin/app_process";
|
||||||
|
static bool first_app_process = true;
|
||||||
|
|
||||||
|
/* This applies to versions Android 10+ */
|
||||||
|
static const char system_bin_init[] = "/system/bin/init";
|
||||||
|
/* This applies to versions between Android 6 ~ 9 */
|
||||||
|
static const char old_system_init[] = "/init";
|
||||||
|
static bool init_second_stage_executed = false;
|
||||||
|
|
||||||
|
if (!filename_ptr)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
filename = *filename_ptr;
|
||||||
|
if (IS_ERR(filename)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(!memcmp(filename->name, system_bin_init,
|
||||||
|
sizeof(system_bin_init) - 1) &&
|
||||||
|
argv)) {
|
||||||
|
// /system/bin/init executed
|
||||||
|
int argc = count(*argv, MAX_ARG_STRINGS);
|
||||||
|
pr_info("/system/bin/init argc: %d\n", argc);
|
||||||
|
if (argc > 1 && !init_second_stage_executed) {
|
||||||
|
const char __user *p = get_user_arg_ptr(*argv, 1);
|
||||||
|
if (p && !IS_ERR(p)) {
|
||||||
|
char first_arg[16];
|
||||||
|
ksu_strncpy_from_user_nofault(
|
||||||
|
first_arg, p, sizeof(first_arg));
|
||||||
|
pr_info("/system/bin/init first arg: %s\n",
|
||||||
|
first_arg);
|
||||||
|
if (!strcmp(first_arg, "second_stage")) {
|
||||||
|
pr_info("/system/bin/init second_stage executed\n");
|
||||||
|
ksu_apply_kernelsu_rules();
|
||||||
|
init_second_stage_executed = true;
|
||||||
|
ksu_android_ns_fs_check();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pr_err("/system/bin/init parse args err!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (unlikely(!memcmp(filename->name, old_system_init,
|
||||||
|
sizeof(old_system_init) - 1) &&
|
||||||
|
argv)) {
|
||||||
|
// /init executed
|
||||||
|
int argc = count(*argv, MAX_ARG_STRINGS);
|
||||||
|
pr_info("/init argc: %d\n", argc);
|
||||||
|
if (argc > 1 && !init_second_stage_executed) {
|
||||||
|
/* This applies to versions between Android 6 ~ 7 */
|
||||||
|
const char __user *p = get_user_arg_ptr(*argv, 1);
|
||||||
|
if (p && !IS_ERR(p)) {
|
||||||
|
char first_arg[16];
|
||||||
|
ksu_strncpy_from_user_nofault(
|
||||||
|
first_arg, p, sizeof(first_arg));
|
||||||
|
pr_info("/init first arg: %s\n", first_arg);
|
||||||
|
if (!strcmp(first_arg, "--second-stage")) {
|
||||||
|
pr_info("/init second_stage executed\n");
|
||||||
|
ksu_apply_kernelsu_rules();
|
||||||
|
init_second_stage_executed = true;
|
||||||
|
ksu_android_ns_fs_check();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pr_err("/init parse args err!\n");
|
||||||
|
}
|
||||||
|
} else if (argc == 1 && !init_second_stage_executed && envp) {
|
||||||
|
/* This applies to versions between Android 8 ~ 9 */
|
||||||
|
int envc = count(*envp, MAX_ARG_STRINGS);
|
||||||
|
if (envc > 0) {
|
||||||
|
int n;
|
||||||
|
for (n = 1; n <= envc; n++) {
|
||||||
|
const char __user *p =
|
||||||
|
get_user_arg_ptr(*envp, n);
|
||||||
|
if (!p || IS_ERR(p)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
char env[256];
|
||||||
|
// Reading environment variable strings from user space
|
||||||
|
if (ksu_strncpy_from_user_nofault(
|
||||||
|
env, p, sizeof(env)) < 0)
|
||||||
|
continue;
|
||||||
|
// Parsing environment variable names and values
|
||||||
|
char *env_name = env;
|
||||||
|
char *env_value = strchr(env, '=');
|
||||||
|
if (env_value == NULL)
|
||||||
|
continue;
|
||||||
|
// Replace equal sign with string terminator
|
||||||
|
*env_value = '\0';
|
||||||
|
env_value++;
|
||||||
|
// Check if the environment variable name and value are matching
|
||||||
|
if (!strcmp(env_name,
|
||||||
|
"INIT_SECOND_STAGE") &&
|
||||||
|
(!strcmp(env_value, "1") ||
|
||||||
|
!strcmp(env_value, "true"))) {
|
||||||
|
pr_info("/init second_stage executed\n");
|
||||||
|
ksu_apply_kernelsu_rules();
|
||||||
|
init_second_stage_executed =
|
||||||
|
true;
|
||||||
|
ksu_android_ns_fs_check();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(first_app_process && !memcmp(filename->name, app_process,
|
||||||
|
sizeof(app_process) - 1))) {
|
||||||
|
first_app_process = false;
|
||||||
|
pr_info("exec app_process, /data prepared, second_stage: %d\n",
|
||||||
|
init_second_stage_executed);
|
||||||
|
ksu_on_post_fs_data(); // we keep this for old ksud
|
||||||
|
stop_execve_hook();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *);
|
||||||
|
static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *);
|
||||||
|
static struct file_operations fops_proxy;
|
||||||
|
static ssize_t read_count_append = 0;
|
||||||
|
|
||||||
|
static ssize_t read_proxy(struct file *file, char __user *buf, size_t count,
|
||||||
|
loff_t *pos)
|
||||||
|
{
|
||||||
|
bool first_read = file->f_pos == 0;
|
||||||
|
ssize_t ret = orig_read(file, buf, count, pos);
|
||||||
|
if (first_read) {
|
||||||
|
pr_info("read_proxy append %ld + %ld\n", ret,
|
||||||
|
read_count_append);
|
||||||
|
ret += read_count_append;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to)
|
||||||
|
{
|
||||||
|
bool first_read = iocb->ki_pos == 0;
|
||||||
|
ssize_t ret = orig_read_iter(iocb, to);
|
||||||
|
if (first_read) {
|
||||||
|
pr_info("read_iter_proxy append %ld + %ld\n", ret,
|
||||||
|
read_count_append);
|
||||||
|
ret += read_count_append;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||||
|
size_t *count_ptr, loff_t **pos)
|
||||||
|
{
|
||||||
|
#ifndef KSU_HOOK_WITH_KPROBES
|
||||||
|
if (!ksu_vfs_read_hook) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
struct file *file;
|
||||||
|
char __user *buf;
|
||||||
|
size_t count;
|
||||||
|
|
||||||
|
if (strcmp(current->comm, "init")) {
|
||||||
|
// we are only interest in `init` process
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
file = *file_ptr;
|
||||||
|
if (IS_ERR(file)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!d_is_reg(file->f_path.dentry)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *short_name = file->f_path.dentry->d_name.name;
|
||||||
|
if (strcmp(short_name, "atrace.rc")) {
|
||||||
|
// we are only interest `atrace.rc` file name file
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
char path[256];
|
||||||
|
char *dpath = d_path(&file->f_path, path, sizeof(path));
|
||||||
|
|
||||||
|
if (IS_ERR(dpath)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(dpath, "/system/etc/init/atrace.rc")) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we only process the first read
|
||||||
|
static bool rc_inserted = false;
|
||||||
|
if (rc_inserted) {
|
||||||
|
// we don't need this kprobe, unregister it!
|
||||||
|
stop_vfs_read_hook();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rc_inserted = true;
|
||||||
|
|
||||||
|
// now we can sure that the init process is reading
|
||||||
|
// `/system/etc/init/atrace.rc`
|
||||||
|
buf = *buf_ptr;
|
||||||
|
count = *count_ptr;
|
||||||
|
|
||||||
|
size_t rc_count = strlen(KERNEL_SU_RC);
|
||||||
|
|
||||||
|
pr_info("vfs_read: %s, comm: %s, count: %zu, rc_count: %zu\n", dpath,
|
||||||
|
current->comm, count, rc_count);
|
||||||
|
|
||||||
|
if (count < rc_count) {
|
||||||
|
pr_err("count: %zu < rc_count: %zu\n", count, rc_count);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ret = copy_to_user(buf, KERNEL_SU_RC, rc_count);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("copy ksud.rc failed: %zu\n", ret);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we've succeed to insert ksud.rc, now we need to proxy the read and modify the result!
|
||||||
|
// But, we can not modify the file_operations directly, because it's in read-only memory.
|
||||||
|
// We just replace the whole file_operations with a proxy one.
|
||||||
|
memcpy(&fops_proxy, file->f_op, sizeof(struct file_operations));
|
||||||
|
orig_read = file->f_op->read;
|
||||||
|
if (orig_read) {
|
||||||
|
fops_proxy.read = read_proxy;
|
||||||
|
}
|
||||||
|
orig_read_iter = file->f_op->read_iter;
|
||||||
|
if (orig_read_iter) {
|
||||||
|
fops_proxy.read_iter = read_iter_proxy;
|
||||||
|
}
|
||||||
|
// replace the file_operations
|
||||||
|
file->f_op = &fops_proxy;
|
||||||
|
read_count_append = rc_count;
|
||||||
|
|
||||||
|
*buf_ptr = buf + rc_count;
|
||||||
|
*count_ptr = count - rc_count;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr,
|
||||||
|
size_t *count_ptr)
|
||||||
|
{
|
||||||
|
struct file *file = fget(fd);
|
||||||
|
if (!file) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int result = ksu_handle_vfs_read(&file, buf_ptr, count_ptr, NULL);
|
||||||
|
fput(file);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int volumedown_pressed_count = 0;
|
||||||
|
|
||||||
|
static bool is_volumedown_enough(unsigned int count)
|
||||||
|
{
|
||||||
|
return count >= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
||||||
|
int *value)
|
||||||
|
{
|
||||||
|
#ifndef KSU_HOOK_WITH_KPROBES
|
||||||
|
if (!ksu_input_hook) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (*type == EV_KEY && *code == KEY_VOLUMEDOWN) {
|
||||||
|
int val = *value;
|
||||||
|
pr_info("KEY_VOLUMEDOWN val: %d\n", val);
|
||||||
|
if (val && is_boot_phase) { // Accumulates only during the power-up phase
|
||||||
|
volumedown_pressed_count += 1;
|
||||||
|
if (is_volumedown_enough(volumedown_pressed_count)) {
|
||||||
|
stop_input_hook();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ksu_is_safe_mode()
|
||||||
|
{
|
||||||
|
static bool safe_mode = false;
|
||||||
|
if (safe_mode) {
|
||||||
|
// don't need to check again, userspace may call multiple times
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop hook first!
|
||||||
|
stop_input_hook();
|
||||||
|
|
||||||
|
pr_info("volumedown_pressed_count: %d\n", volumedown_pressed_count);
|
||||||
|
if (is_volumedown_enough(volumedown_pressed_count)) {
|
||||||
|
// pressed over 3 times
|
||||||
|
pr_info("KEY_VOLUMEDOWN pressed max times, safe mode detected!\n");
|
||||||
|
safe_mode = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef KSU_HOOK_WITH_KPROBES
|
||||||
|
|
||||||
|
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
int *fd = (int *)&PT_REGS_PARM1(regs);
|
||||||
|
struct filename **filename_ptr =
|
||||||
|
(struct filename **)&PT_REGS_PARM2(regs);
|
||||||
|
struct user_arg_ptr argv;
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
argv.is_compat = PT_REGS_PARM3(regs);
|
||||||
|
if (unlikely(argv.is_compat)) {
|
||||||
|
argv.ptr.compat = PT_REGS_CCALL_PARM4(regs);
|
||||||
|
} else {
|
||||||
|
argv.ptr.native = PT_REGS_CCALL_PARM4(regs);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
argv.ptr.native = PT_REGS_PARM3(regs);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
|
const char __user **filename_user =
|
||||||
|
(const char **)&PT_REGS_PARM1(real_regs);
|
||||||
|
const char __user *const __user *__argv =
|
||||||
|
(const char __user *const __user *)PT_REGS_PARM2(real_regs);
|
||||||
|
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||||
|
struct filename filename_in, *filename_p;
|
||||||
|
char path[32];
|
||||||
|
|
||||||
|
if (!filename_user)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
ksu_strncpy_from_user_nofault(path, *filename_user, 32);
|
||||||
|
filename_in.name = path;
|
||||||
|
|
||||||
|
filename_p = &filename_in;
|
||||||
|
return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
__maybe_unused static int vfs_read_handler_pre(struct kprobe *p,
|
||||||
|
struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct file **file_ptr = (struct file **)&PT_REGS_PARM1(regs);
|
||||||
|
char __user **buf_ptr = (char **)&PT_REGS_PARM2(regs);
|
||||||
|
size_t *count_ptr = (size_t *)&PT_REGS_PARM3(regs);
|
||||||
|
loff_t **pos_ptr = (loff_t **)&PT_REGS_CCALL_PARM4(regs);
|
||||||
|
|
||||||
|
return ksu_handle_vfs_read(file_ptr, buf_ptr, count_ptr, pos_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
|
unsigned int fd = PT_REGS_PARM1(real_regs);
|
||||||
|
char __user **buf_ptr = (char __user **)&PT_REGS_PARM2(real_regs);
|
||||||
|
size_t count_ptr = (size_t *)&PT_REGS_PARM3(real_regs);
|
||||||
|
|
||||||
|
return ksu_handle_sys_read(fd, buf_ptr, count_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int input_handle_event_handler_pre(struct kprobe *p,
|
||||||
|
struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
unsigned int *type = (unsigned int *)&PT_REGS_PARM2(regs);
|
||||||
|
unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs);
|
||||||
|
int *value = (int *)&PT_REGS_CCALL_PARM4(regs);
|
||||||
|
return ksu_handle_input_handle_event(type, code, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
static struct kprobe execve_kp = {
|
||||||
|
.symbol_name = SYS_EXECVE_SYMBOL,
|
||||||
|
.pre_handler = sys_execve_handler_pre,
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
static struct kprobe execve_kp = {
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||||
|
.symbol_name = "do_execveat_common",
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
||||||
|
.symbol_name = "__do_execve_file",
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
||||||
|
.symbol_name = "do_execveat_common",
|
||||||
|
#endif
|
||||||
|
.pre_handler = execve_handler_pre,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
static struct kprobe vfs_read_kp = {
|
||||||
|
.symbol_name = SYS_READ_SYMBOL,
|
||||||
|
.pre_handler = sys_read_handler_pre,
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
static struct kprobe vfs_read_kp = {
|
||||||
|
.symbol_name = "vfs_read",
|
||||||
|
.pre_handler = vfs_read_handler_pre,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct kprobe input_event_kp = {
|
||||||
|
.symbol_name = "input_event",
|
||||||
|
.pre_handler = input_handle_event_handler_pre,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void do_stop_vfs_read_hook(struct work_struct *work)
|
||||||
|
{
|
||||||
|
unregister_kprobe(&vfs_read_kp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_stop_execve_hook(struct work_struct *work)
|
||||||
|
{
|
||||||
|
unregister_kprobe(&execve_kp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_stop_input_hook(struct work_struct *work)
|
||||||
|
{
|
||||||
|
unregister_kprobe(&input_event_kp);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
* ksu_handle_execve_ksud, execve_ksud handler for non kprobe
|
||||||
|
* adapted from sys_execve_handler_pre
|
||||||
|
* https://github.com/tiann/KernelSU/commit/2027ac3
|
||||||
|
*/
|
||||||
|
__maybe_unused int ksu_handle_execve_ksud(const char __user *filename_user,
|
||||||
|
const char __user *const __user *__argv)
|
||||||
|
{
|
||||||
|
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||||
|
struct filename filename_in, *filename_p;
|
||||||
|
char path[32];
|
||||||
|
|
||||||
|
// return early if disabled.
|
||||||
|
if (!ksu_execveat_hook) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filename_user)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
ksu_strncpy_from_user_nofault(path, filename_user, 32);
|
||||||
|
|
||||||
|
// this is because ksu_handle_execveat_ksud calls it filename->name
|
||||||
|
filename_in.name = path;
|
||||||
|
filename_p = &filename_in;
|
||||||
|
|
||||||
|
return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL, NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void stop_vfs_read_hook()
|
||||||
|
{
|
||||||
|
#ifdef KSU_HOOK_WITH_KPROBES
|
||||||
|
bool ret = schedule_work(&stop_vfs_read_work);
|
||||||
|
pr_info("unregister vfs_read kprobe: %d!\n", ret);
|
||||||
|
#else
|
||||||
|
ksu_vfs_read_hook = false;
|
||||||
|
pr_info("stop vfs_read_hook\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stop_execve_hook()
|
||||||
|
{
|
||||||
|
#ifdef KSU_HOOK_WITH_KPROBES
|
||||||
|
bool ret = schedule_work(&stop_execve_hook_work);
|
||||||
|
pr_info("unregister execve kprobe: %d!\n", ret);
|
||||||
|
#else
|
||||||
|
ksu_execveat_hook = false;
|
||||||
|
pr_info("stop execve_hook\n");
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_KSU_SUSFS_SUS_SU
|
||||||
|
susfs_is_sus_su_ready = true;
|
||||||
|
pr_info("susfs: sus_su is ready\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stop_input_hook()
|
||||||
|
{
|
||||||
|
#ifdef KSU_HOOK_WITH_KPROBES
|
||||||
|
static bool input_hook_stopped = false;
|
||||||
|
if (input_hook_stopped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
input_hook_stopped = true;
|
||||||
|
|
||||||
|
bool ret = schedule_work(&stop_input_hook_work);
|
||||||
|
pr_info("unregister input kprobe: %d!\n", ret);
|
||||||
|
#else
|
||||||
|
if (!ksu_input_hook) { return; }
|
||||||
|
ksu_input_hook = false;
|
||||||
|
pr_info("stop input_hook\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// ksud: module support
|
||||||
|
void ksu_ksud_init()
|
||||||
|
{
|
||||||
|
#ifdef KSU_HOOK_WITH_KPROBES
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = register_kprobe(&execve_kp);
|
||||||
|
pr_info("ksud: execve_kp: %d\n", ret);
|
||||||
|
|
||||||
|
ret = register_kprobe(&vfs_read_kp);
|
||||||
|
pr_info("ksud: vfs_read_kp: %d\n", ret);
|
||||||
|
|
||||||
|
ret = register_kprobe(&input_event_kp);
|
||||||
|
pr_info("ksud: input_event_kp: %d\n", ret);
|
||||||
|
|
||||||
|
INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook);
|
||||||
|
INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook);
|
||||||
|
INIT_WORK(&stop_input_hook_work, do_stop_input_hook);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_ksud_exit()
|
||||||
|
{
|
||||||
|
#ifdef KSU_HOOK_WITH_KPROBES
|
||||||
|
unregister_kprobe(&execve_kp);
|
||||||
|
// this should be done before unregister vfs_read_kp
|
||||||
|
// unregister_kprobe(&vfs_read_kp);
|
||||||
|
unregister_kprobe(&input_event_kp);
|
||||||
|
#endif
|
||||||
|
is_boot_phase = false;
|
||||||
|
volumedown_pressed_count = 0;
|
||||||
|
}
|
||||||
14
kernel/ksud.h
Normal file
14
kernel/ksud.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef __KSU_H_KSUD
|
||||||
|
#define __KSU_H_KSUD
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#define KSUD_PATH "/data/adb/ksud"
|
||||||
|
|
||||||
|
void ksu_on_post_fs_data(void);
|
||||||
|
|
||||||
|
bool ksu_is_safe_mode(void);
|
||||||
|
|
||||||
|
extern u32 ksu_devpts_sid;
|
||||||
|
|
||||||
|
#endif
|
||||||
36
kernel/manager.h
Normal file
36
kernel/manager.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
#ifndef __KSU_H_KSU_MANAGER
|
||||||
|
#define __KSU_H_KSU_MANAGER
|
||||||
|
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#define KSU_INVALID_UID -1
|
||||||
|
|
||||||
|
extern uid_t ksu_manager_uid; // DO NOT DIRECT USE
|
||||||
|
|
||||||
|
static inline bool ksu_is_manager_uid_valid()
|
||||||
|
{
|
||||||
|
return ksu_manager_uid != KSU_INVALID_UID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool ksu_is_manager()
|
||||||
|
{
|
||||||
|
return unlikely(ksu_manager_uid == current_uid().val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uid_t ksu_get_manager_uid()
|
||||||
|
{
|
||||||
|
return ksu_manager_uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ksu_set_manager_uid(uid_t uid)
|
||||||
|
{
|
||||||
|
ksu_manager_uid = uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ksu_invalidate_manager_uid()
|
||||||
|
{
|
||||||
|
ksu_manager_uid = KSU_INVALID_UID;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
24
kernel/manager_sign.h
Normal file
24
kernel/manager_sign.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#ifndef MANAGER_SIGN_H
|
||||||
|
#define MANAGER_SIGN_H
|
||||||
|
|
||||||
|
// rsuntk/KernelSU
|
||||||
|
#define EXPECTED_SIZE_RSUNTK 0x396
|
||||||
|
#define EXPECTED_HASH_RSUNTK "f415f4ed9435427e1fdf7f1fccd4dbc07b3d6b8751e4dbcec6f19671f427870b"
|
||||||
|
|
||||||
|
// 5ec1cff/KernelSU
|
||||||
|
#define EXPECTED_SIZE_5EC1CFF 384
|
||||||
|
#define EXPECTED_HASH_5EC1CFF "7e0c6d7278a3bb8e364e0fcba95afaf3666cf5ff3c245a3b63c8833bd0445cc4"
|
||||||
|
|
||||||
|
// ShirkNeko/KernelSU
|
||||||
|
#define EXPECTED_SIZE_SHIRKNEKO 0x35c
|
||||||
|
#define EXPECTED_HASH_SHIRKNEKO "947ae944f3de4ed4c21a7e4f7953ecf351bfa2b36239da37a34111ad29993eef"
|
||||||
|
|
||||||
|
// weishu/KernelSU
|
||||||
|
#define EXPECTED_SIZE_WEISHU 0x033b
|
||||||
|
#define EXPECTED_HASH_WEISHU "c371061b19d8c7d7d6133c6a9bafe198fa944e50c1b31c9d8daa8d7f1fc2d2d6"
|
||||||
|
|
||||||
|
// Neko/KernelSU
|
||||||
|
#define EXPECTED_SIZE_NEKO 0x29c
|
||||||
|
#define EXPECTED_HASH_NEKO "946b0557e450a6430a0ba6b6bccee5bc12953ec8735d55e26139b0ec12303b21"
|
||||||
|
|
||||||
|
#endif /* MANAGER_SIGN_H */
|
||||||
16
kernel/selinux/Makefile
Normal file
16
kernel/selinux/Makefile
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
obj-y += selinux.o
|
||||||
|
obj-y += sepolicy.o
|
||||||
|
obj-y += rules.o
|
||||||
|
|
||||||
|
ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE
|
||||||
|
endif
|
||||||
|
|
||||||
|
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion
|
||||||
|
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
||||||
|
ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
|
||||||
|
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
|
||||||
483
kernel/selinux/rules.c
Normal file
483
kernel/selinux/rules.c
Normal file
@@ -0,0 +1,483 @@
|
|||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#include "../klog.h" // IWYU pragma: keep
|
||||||
|
#include "selinux.h"
|
||||||
|
#include "sepolicy.h"
|
||||||
|
#include "ss/services.h"
|
||||||
|
#include "linux/lsm_audit.h"
|
||||||
|
#include "xfrm.h"
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||||
|
#define SELINUX_POLICY_INSTEAD_SELINUX_SS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define KERNEL_SU_DOMAIN "su"
|
||||||
|
#define KERNEL_SU_FILE "ksu_file"
|
||||||
|
#define KERNEL_EXEC_TYPE "ksu_exec"
|
||||||
|
#define ALL NULL
|
||||||
|
|
||||||
|
static struct policydb *get_policydb(void)
|
||||||
|
{
|
||||||
|
struct policydb *db;
|
||||||
|
// selinux_state does not exists before 4.19
|
||||||
|
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||||
|
#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS
|
||||||
|
struct selinux_policy *policy = rcu_dereference(selinux_state.policy);
|
||||||
|
db = &policy->policydb;
|
||||||
|
#else
|
||||||
|
struct selinux_ss *ss = rcu_dereference(selinux_state.ss);
|
||||||
|
db = &ss->policydb;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
db = &policydb;
|
||||||
|
#endif
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_apply_kernelsu_rules()
|
||||||
|
{
|
||||||
|
if (!ksu_getenforce()) {
|
||||||
|
pr_info("SELinux permissive or disabled, apply rules!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
struct policydb *db = get_policydb();
|
||||||
|
|
||||||
|
ksu_permissive(db, KERNEL_SU_DOMAIN);
|
||||||
|
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject");
|
||||||
|
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "netdomain");
|
||||||
|
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain");
|
||||||
|
|
||||||
|
// Create unconstrained file type
|
||||||
|
ksu_type(db, KERNEL_SU_FILE, "file_type");
|
||||||
|
ksu_typeattribute(db, KERNEL_SU_FILE, "mlstrustedobject");
|
||||||
|
ksu_allow(db, ALL, KERNEL_SU_FILE, ALL, ALL);
|
||||||
|
|
||||||
|
// allow all!
|
||||||
|
ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL);
|
||||||
|
|
||||||
|
// allow us do any ioctl
|
||||||
|
if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) {
|
||||||
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "blk_file", ALL);
|
||||||
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "fifo_file", ALL);
|
||||||
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL);
|
||||||
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "file", ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need to save allowlist in /data/adb/ksu
|
||||||
|
ksu_allow(db, "kernel", "adb_data_file", "dir", ALL);
|
||||||
|
ksu_allow(db, "kernel", "adb_data_file", "file", ALL);
|
||||||
|
// we need to search /data/app
|
||||||
|
ksu_allow(db, "kernel", "apk_data_file", "file", "open");
|
||||||
|
ksu_allow(db, "kernel", "apk_data_file", "dir", "open");
|
||||||
|
ksu_allow(db, "kernel", "apk_data_file", "dir", "read");
|
||||||
|
ksu_allow(db, "kernel", "apk_data_file", "dir", "search");
|
||||||
|
// we may need to do mount on shell
|
||||||
|
ksu_allow(db, "kernel", "shell_data_file", "file", ALL);
|
||||||
|
// we need to read /data/system/packages.list
|
||||||
|
ksu_allow(db, "kernel", "kernel", "capability", "dac_override");
|
||||||
|
// Android 10+:
|
||||||
|
// http://aospxref.com/android-12.0.0_r3/xref/system/sepolicy/private/file_contexts#512
|
||||||
|
ksu_allow(db, "kernel", "packages_list_file", "file", ALL);
|
||||||
|
// Kernel 4.4
|
||||||
|
ksu_allow(db, "kernel", "packages_list_file", "dir", ALL);
|
||||||
|
// Android 9-:
|
||||||
|
// http://aospxref.com/android-9.0.0_r61/xref/system/sepolicy/private/file_contexts#360
|
||||||
|
ksu_allow(db, "kernel", "system_data_file", "file", ALL);
|
||||||
|
ksu_allow(db, "kernel", "system_data_file", "dir", ALL);
|
||||||
|
// our ksud triggered by init
|
||||||
|
ksu_allow(db, "init", "adb_data_file", "file", ALL);
|
||||||
|
ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289
|
||||||
|
ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
|
||||||
|
// we need to umount modules in zygote
|
||||||
|
ksu_allow(db, "zygote", "adb_data_file", "dir", "search");
|
||||||
|
|
||||||
|
// copied from Magisk rules
|
||||||
|
// suRights
|
||||||
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
||||||
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "read");
|
||||||
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
||||||
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
||||||
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "process", "getattr");
|
||||||
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "process", "sigchld");
|
||||||
|
|
||||||
|
// allowLog
|
||||||
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "dir", "search");
|
||||||
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "read");
|
||||||
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "open");
|
||||||
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "getattr");
|
||||||
|
|
||||||
|
// dumpsys
|
||||||
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fd", "use");
|
||||||
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "write");
|
||||||
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "read");
|
||||||
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "open");
|
||||||
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "getattr");
|
||||||
|
|
||||||
|
// bootctl
|
||||||
|
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
||||||
|
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
||||||
|
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
||||||
|
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process",
|
||||||
|
"getattr");
|
||||||
|
|
||||||
|
// For mounting loop devices, mirrors, tmpfs
|
||||||
|
ksu_allow(db, "kernel", ALL, "file", "read");
|
||||||
|
ksu_allow(db, "kernel", ALL, "file", "write");
|
||||||
|
|
||||||
|
// Allow all binder transactions
|
||||||
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
|
||||||
|
|
||||||
|
// Allow system server kill su process
|
||||||
|
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
||||||
|
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_SUSFS
|
||||||
|
// Allow umount in zygote process without installing zygisk
|
||||||
|
ksu_allow(db, "zygote", "labeledfs", "filesystem", "unmount");
|
||||||
|
susfs_set_init_sid();
|
||||||
|
susfs_set_ksu_sid();
|
||||||
|
susfs_set_zygote_sid();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_SEPOL_LEN 128
|
||||||
|
|
||||||
|
#define CMD_NORMAL_PERM 1
|
||||||
|
#define CMD_XPERM 2
|
||||||
|
#define CMD_TYPE_STATE 3
|
||||||
|
#define CMD_TYPE 4
|
||||||
|
#define CMD_TYPE_ATTR 5
|
||||||
|
#define CMD_ATTR 6
|
||||||
|
#define CMD_TYPE_TRANSITION 7
|
||||||
|
#define CMD_TYPE_CHANGE 8
|
||||||
|
#define CMD_GENFSCON 9
|
||||||
|
|
||||||
|
struct sepol_data {
|
||||||
|
u32 cmd;
|
||||||
|
u32 subcmd;
|
||||||
|
char __user *sepol1;
|
||||||
|
char __user *sepol2;
|
||||||
|
char __user *sepol3;
|
||||||
|
char __user *sepol4;
|
||||||
|
char __user *sepol5;
|
||||||
|
char __user *sepol6;
|
||||||
|
char __user *sepol7;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
||||||
|
char **object)
|
||||||
|
{
|
||||||
|
if (!user_object) {
|
||||||
|
*object = ALL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncpy_from_user(buf, user_object, buf_sz) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*object = buf;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset avc cache table, otherwise the new rules will not take effect if already denied
|
||||||
|
static void reset_avc_cache()
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || !defined(KSU_COMPAT_USE_SELINUX_STATE)
|
||||||
|
avc_ss_reset(0);
|
||||||
|
selnl_notify_policyload(0);
|
||||||
|
selinux_status_update_policyload(0);
|
||||||
|
#else
|
||||||
|
struct selinux_avc *avc = selinux_state.avc;
|
||||||
|
avc_ss_reset(avc, 0);
|
||||||
|
selnl_notify_policyload(0);
|
||||||
|
selinux_status_update_policyload(&selinux_state, 0);
|
||||||
|
#endif
|
||||||
|
selinux_xfrm_notify_policyload();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||||
|
{
|
||||||
|
if (!arg4) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_getenforce()) {
|
||||||
|
pr_info("SELinux permissive or disabled when handle policy!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sepol_data data;
|
||||||
|
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
||||||
|
pr_err("sepol: copy sepol_data failed.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 cmd = data.cmd;
|
||||||
|
u32 subcmd = data.subcmd;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
struct policydb *db = get_policydb();
|
||||||
|
|
||||||
|
int ret = -1;
|
||||||
|
if (cmd == CMD_NORMAL_PERM) {
|
||||||
|
char src_buf[MAX_SEPOL_LEN];
|
||||||
|
char tgt_buf[MAX_SEPOL_LEN];
|
||||||
|
char cls_buf[MAX_SEPOL_LEN];
|
||||||
|
char perm_buf[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
|
char *s, *t, *c, *p;
|
||||||
|
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
|
||||||
|
pr_err("sepol: copy src failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||||
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
|
||||||
|
pr_err("sepol: copy cls failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_object(perm_buf, data.sepol4, sizeof(perm_buf), &p) <
|
||||||
|
0) {
|
||||||
|
pr_err("sepol: copy perm failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
if (subcmd == 1) {
|
||||||
|
success = ksu_allow(db, s, t, c, p);
|
||||||
|
} else if (subcmd == 2) {
|
||||||
|
success = ksu_deny(db, s, t, c, p);
|
||||||
|
} else if (subcmd == 3) {
|
||||||
|
success = ksu_auditallow(db, s, t, c, p);
|
||||||
|
} else if (subcmd == 4) {
|
||||||
|
success = ksu_dontaudit(db, s, t, c, p);
|
||||||
|
} else {
|
||||||
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
|
}
|
||||||
|
ret = success ? 0 : -1;
|
||||||
|
|
||||||
|
} else if (cmd == CMD_XPERM) {
|
||||||
|
char src_buf[MAX_SEPOL_LEN];
|
||||||
|
char tgt_buf[MAX_SEPOL_LEN];
|
||||||
|
char cls_buf[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
|
char __maybe_unused
|
||||||
|
operation[MAX_SEPOL_LEN]; // it is always ioctl now!
|
||||||
|
char perm_set[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
|
char *s, *t, *c;
|
||||||
|
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
|
||||||
|
pr_err("sepol: copy src failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||||
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
|
||||||
|
pr_err("sepol: copy cls failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(operation, data.sepol4,
|
||||||
|
sizeof(operation)) < 0) {
|
||||||
|
pr_err("sepol: copy operation failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(perm_set, data.sepol5, sizeof(perm_set)) <
|
||||||
|
0) {
|
||||||
|
pr_err("sepol: copy perm_set failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
if (subcmd == 1) {
|
||||||
|
success = ksu_allowxperm(db, s, t, c, perm_set);
|
||||||
|
} else if (subcmd == 2) {
|
||||||
|
success = ksu_auditallowxperm(db, s, t, c, perm_set);
|
||||||
|
} else if (subcmd == 3) {
|
||||||
|
success = ksu_dontauditxperm(db, s, t, c, perm_set);
|
||||||
|
} else {
|
||||||
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
|
}
|
||||||
|
ret = success ? 0 : -1;
|
||||||
|
} else if (cmd == CMD_TYPE_STATE) {
|
||||||
|
char src[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
|
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
||||||
|
pr_err("sepol: copy src failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
if (subcmd == 1) {
|
||||||
|
success = ksu_permissive(db, src);
|
||||||
|
} else if (subcmd == 2) {
|
||||||
|
success = ksu_enforce(db, src);
|
||||||
|
} else {
|
||||||
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
|
}
|
||||||
|
if (success)
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
|
||||||
|
char type[MAX_SEPOL_LEN];
|
||||||
|
char attr[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
|
if (strncpy_from_user(type, data.sepol1, sizeof(type)) < 0) {
|
||||||
|
pr_err("sepol: copy type failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(attr, data.sepol2, sizeof(attr)) < 0) {
|
||||||
|
pr_err("sepol: copy attr failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
if (cmd == CMD_TYPE) {
|
||||||
|
success = ksu_type(db, type, attr);
|
||||||
|
} else {
|
||||||
|
success = ksu_typeattribute(db, type, attr);
|
||||||
|
}
|
||||||
|
if (!success) {
|
||||||
|
pr_err("sepol: %d failed.\n", cmd);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
} else if (cmd == CMD_ATTR) {
|
||||||
|
char attr[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
|
if (strncpy_from_user(attr, data.sepol1, sizeof(attr)) < 0) {
|
||||||
|
pr_err("sepol: copy attr failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!ksu_attribute(db, attr)) {
|
||||||
|
pr_err("sepol: %d failed.\n", cmd);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
} else if (cmd == CMD_TYPE_TRANSITION) {
|
||||||
|
char src[MAX_SEPOL_LEN];
|
||||||
|
char tgt[MAX_SEPOL_LEN];
|
||||||
|
char cls[MAX_SEPOL_LEN];
|
||||||
|
char default_type[MAX_SEPOL_LEN];
|
||||||
|
char object[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
|
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
||||||
|
pr_err("sepol: copy src failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
|
||||||
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(cls, data.sepol3, sizeof(cls)) < 0) {
|
||||||
|
pr_err("sepol: copy cls failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(default_type, data.sepol4,
|
||||||
|
sizeof(default_type)) < 0) {
|
||||||
|
pr_err("sepol: copy default_type failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
char *real_object;
|
||||||
|
if (data.sepol5 == NULL) {
|
||||||
|
real_object = NULL;
|
||||||
|
} else {
|
||||||
|
if (strncpy_from_user(object, data.sepol5,
|
||||||
|
sizeof(object)) < 0) {
|
||||||
|
pr_err("sepol: copy object failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
real_object = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = ksu_type_transition(db, src, tgt, cls,
|
||||||
|
default_type, real_object);
|
||||||
|
if (success)
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
} else if (cmd == CMD_TYPE_CHANGE) {
|
||||||
|
char src[MAX_SEPOL_LEN];
|
||||||
|
char tgt[MAX_SEPOL_LEN];
|
||||||
|
char cls[MAX_SEPOL_LEN];
|
||||||
|
char default_type[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
|
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
|
||||||
|
pr_err("sepol: copy src failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
|
||||||
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(cls, data.sepol3, sizeof(cls)) < 0) {
|
||||||
|
pr_err("sepol: copy cls failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(default_type, data.sepol4,
|
||||||
|
sizeof(default_type)) < 0) {
|
||||||
|
pr_err("sepol: copy default_type failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
bool success = false;
|
||||||
|
if (subcmd == 1) {
|
||||||
|
success = ksu_type_change(db, src, tgt, cls,
|
||||||
|
default_type);
|
||||||
|
} else if (subcmd == 2) {
|
||||||
|
success = ksu_type_member(db, src, tgt, cls,
|
||||||
|
default_type);
|
||||||
|
} else {
|
||||||
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
|
}
|
||||||
|
if (success)
|
||||||
|
ret = 0;
|
||||||
|
} else if (cmd == CMD_GENFSCON) {
|
||||||
|
char name[MAX_SEPOL_LEN];
|
||||||
|
char path[MAX_SEPOL_LEN];
|
||||||
|
char context[MAX_SEPOL_LEN];
|
||||||
|
if (strncpy_from_user(name, data.sepol1, sizeof(name)) < 0) {
|
||||||
|
pr_err("sepol: copy name failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(path, data.sepol2, sizeof(path)) < 0) {
|
||||||
|
pr_err("sepol: copy path failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strncpy_from_user(context, data.sepol3, sizeof(context)) <
|
||||||
|
0) {
|
||||||
|
pr_err("sepol: copy context failed.\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_genfscon(db, name, path, context)) {
|
||||||
|
pr_err("sepol: %d failed.\n", cmd);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
pr_err("sepol: unknown cmd: %d\n", cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
|
// only allow and xallow needs to reset avc cache, but we cannot do that because
|
||||||
|
// we are in atomic context. so we just reset it every time.
|
||||||
|
reset_avc_cache();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
230
kernel/selinux/selinux.c
Normal file
230
kernel/selinux/selinux.c
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
#include "selinux.h"
|
||||||
|
#include "objsec.h"
|
||||||
|
#include "linux/version.h"
|
||||||
|
#include "../klog.h" // IWYU pragma: keep
|
||||||
|
#ifndef KSU_COMPAT_USE_SELINUX_STATE
|
||||||
|
#include "avc.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define KERNEL_SU_DOMAIN "u:r:su:s0"
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_SUSFS
|
||||||
|
#define KERNEL_INIT_DOMAIN "u:r:init:s0"
|
||||||
|
#define KERNEL_ZYGOTE_DOMAIN "u:r:zygote:s0"
|
||||||
|
u32 susfs_ksu_sid = 0;
|
||||||
|
u32 susfs_init_sid = 0;
|
||||||
|
u32 susfs_zygote_sid = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int transive_to_domain(const char *domain)
|
||||||
|
{
|
||||||
|
struct cred *cred;
|
||||||
|
struct task_security_struct *tsec;
|
||||||
|
u32 sid;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
cred = (struct cred *)__task_cred(current);
|
||||||
|
|
||||||
|
tsec = cred->security;
|
||||||
|
if (!tsec) {
|
||||||
|
pr_err("tsec == NULL!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = security_secctx_to_secid(domain, strlen(domain), &sid);
|
||||||
|
if (error) {
|
||||||
|
pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n",
|
||||||
|
domain, sid, error);
|
||||||
|
}
|
||||||
|
if (!error) {
|
||||||
|
tsec->sid = sid;
|
||||||
|
tsec->create_sid = 0;
|
||||||
|
tsec->keycreate_sid = 0;
|
||||||
|
tsec->sockcreate_sid = 0;
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_setup_selinux(const char *domain)
|
||||||
|
{
|
||||||
|
if (transive_to_domain(domain)) {
|
||||||
|
pr_err("transive domain failed.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we didn't need this now, we have change selinux rules when boot!
|
||||||
|
if (!is_domain_permissive) {
|
||||||
|
if (set_domain_permissive() == 0) {
|
||||||
|
is_domain_permissive = true;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_setenforce(bool enforce)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||||
|
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||||
|
selinux_state.enforcing = enforce;
|
||||||
|
#else
|
||||||
|
selinux_enforcing = enforce;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ksu_getenforce()
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
||||||
|
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||||
|
if (selinux_state.disabled) {
|
||||||
|
#else
|
||||||
|
if (selinux_disabled) {
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||||
|
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||||
|
return selinux_state.enforcing;
|
||||||
|
#else
|
||||||
|
return selinux_enforcing;
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \
|
||||||
|
!defined(KSU_COMPAT_HAS_CURRENT_SID)
|
||||||
|
/*
|
||||||
|
* get the subjective security ID of the current task
|
||||||
|
*/
|
||||||
|
static inline u32 current_sid(void)
|
||||||
|
{
|
||||||
|
const struct task_security_struct *tsec = current_security();
|
||||||
|
|
||||||
|
return tsec->sid;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool ksu_is_ksu_domain()
|
||||||
|
{
|
||||||
|
char *domain;
|
||||||
|
u32 seclen;
|
||||||
|
bool result;
|
||||||
|
int err = security_secid_to_secctx(current_sid(), &domain, &seclen);
|
||||||
|
if (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result = strncmp(KERNEL_SU_DOMAIN, domain, seclen) == 0;
|
||||||
|
security_release_secctx(domain, seclen);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ksu_is_zygote(void *sec)
|
||||||
|
{
|
||||||
|
struct task_security_struct *tsec = (struct task_security_struct *)sec;
|
||||||
|
if (!tsec) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char *domain;
|
||||||
|
u32 seclen;
|
||||||
|
bool result;
|
||||||
|
int err = security_secid_to_secctx(tsec->sid, &domain, &seclen);
|
||||||
|
if (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result = strncmp("u:r:zygote:s0", domain, seclen) == 0;
|
||||||
|
security_release_secctx(domain, seclen);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_SUSFS
|
||||||
|
static inline void susfs_set_sid(const char *secctx_name, u32 *out_sid)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!secctx_name || !out_sid) {
|
||||||
|
pr_err("secctx_name || out_sid is NULL\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = security_secctx_to_secid(secctx_name, strlen(secctx_name),
|
||||||
|
out_sid);
|
||||||
|
if (err) {
|
||||||
|
pr_err("failed setting sid for '%s', err: %d\n", secctx_name, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pr_info("sid '%u' is set for secctx_name '%s'\n", *out_sid, secctx_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool susfs_is_sid_equal(void *sec, u32 sid2) {
|
||||||
|
struct task_security_struct *tsec = (struct task_security_struct *)sec;
|
||||||
|
if (!tsec) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return tsec->sid == sid2;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 susfs_get_sid_from_name(const char *secctx_name)
|
||||||
|
{
|
||||||
|
u32 out_sid = 0;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!secctx_name) {
|
||||||
|
pr_err("secctx_name is NULL\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
err = security_secctx_to_secid(secctx_name, strlen(secctx_name),
|
||||||
|
&out_sid);
|
||||||
|
if (err) {
|
||||||
|
pr_err("failed getting sid from secctx_name: %s, err: %d\n", secctx_name, err);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return out_sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 susfs_get_current_sid(void) {
|
||||||
|
return current_sid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void susfs_set_zygote_sid(void)
|
||||||
|
{
|
||||||
|
susfs_set_sid(KERNEL_ZYGOTE_DOMAIN, &susfs_zygote_sid);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool susfs_is_current_zygote_domain(void) {
|
||||||
|
return unlikely(current_sid() == susfs_zygote_sid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void susfs_set_ksu_sid(void)
|
||||||
|
{
|
||||||
|
susfs_set_sid(KERNEL_SU_DOMAIN, &susfs_ksu_sid);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool susfs_is_current_ksu_domain(void) {
|
||||||
|
return unlikely(current_sid() == susfs_ksu_sid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void susfs_set_init_sid(void)
|
||||||
|
{
|
||||||
|
susfs_set_sid(KERNEL_INIT_DOMAIN, &susfs_init_sid);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool susfs_is_current_init_domain(void) {
|
||||||
|
return unlikely(current_sid() == susfs_init_sid);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DEVPTS_DOMAIN "u:object_r:ksu_file:s0"
|
||||||
|
|
||||||
|
u32 ksu_get_devpts_sid()
|
||||||
|
{
|
||||||
|
u32 devpts_sid = 0;
|
||||||
|
int err = security_secctx_to_secid(DEVPTS_DOMAIN, strlen(DEVPTS_DOMAIN),
|
||||||
|
&devpts_sid);
|
||||||
|
if (err) {
|
||||||
|
pr_info("get devpts sid err %d\n", err);
|
||||||
|
}
|
||||||
|
return devpts_sid;
|
||||||
|
}
|
||||||
37
kernel/selinux/selinux.h
Normal file
37
kernel/selinux/selinux.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#ifndef __KSU_H_SELINUX
|
||||||
|
#define __KSU_H_SELINUX
|
||||||
|
|
||||||
|
#include "linux/types.h"
|
||||||
|
#include "linux/version.h"
|
||||||
|
|
||||||
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || defined(KSU_COMPAT_HAS_SELINUX_STATE)
|
||||||
|
#define KSU_COMPAT_USE_SELINUX_STATE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void ksu_setup_selinux(const char *);
|
||||||
|
|
||||||
|
void ksu_setenforce(bool);
|
||||||
|
|
||||||
|
bool ksu_getenforce();
|
||||||
|
|
||||||
|
bool ksu_is_ksu_domain();
|
||||||
|
|
||||||
|
bool ksu_is_zygote(void *cred);
|
||||||
|
|
||||||
|
void ksu_apply_kernelsu_rules();
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
|
||||||
|
bool susfs_is_sid_equal(void *sec, u32 sid2);
|
||||||
|
u32 susfs_get_sid_from_name(const char *secctx_name);
|
||||||
|
u32 susfs_get_current_sid(void);
|
||||||
|
void susfs_set_zygote_sid(void);
|
||||||
|
bool susfs_is_current_zygote_domain(void);
|
||||||
|
void susfs_set_ksu_sid(void);
|
||||||
|
bool susfs_is_current_ksu_domain(void);
|
||||||
|
void susfs_set_init_sid(void);
|
||||||
|
bool susfs_is_current_init_domain(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
u32 ksu_get_devpts_sid();
|
||||||
|
|
||||||
|
#endif
|
||||||
1070
kernel/selinux/sepolicy.c
Normal file
1070
kernel/selinux/sepolicy.c
Normal file
File diff suppressed because it is too large
Load Diff
46
kernel/selinux/sepolicy.h
Normal file
46
kernel/selinux/sepolicy.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#ifndef __KSU_H_SEPOLICY
|
||||||
|
#define __KSU_H_SEPOLICY
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include "ss/policydb.h"
|
||||||
|
|
||||||
|
// Operation on types
|
||||||
|
bool ksu_type(struct policydb *db, const char *name, const char *attr);
|
||||||
|
bool ksu_attribute(struct policydb *db, const char *name);
|
||||||
|
bool ksu_permissive(struct policydb *db, const char *type);
|
||||||
|
bool ksu_enforce(struct policydb *db, const char *type);
|
||||||
|
bool ksu_typeattribute(struct policydb *db, const char *type, const char *attr);
|
||||||
|
bool ksu_exists(struct policydb *db, const char *type);
|
||||||
|
|
||||||
|
// Access vector rules
|
||||||
|
bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
|
||||||
|
const char *cls, const char *perm);
|
||||||
|
bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
|
||||||
|
const char *cls, const char *perm);
|
||||||
|
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
|
||||||
|
const char *cls, const char *perm);
|
||||||
|
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
|
||||||
|
const char *cls, const char *perm);
|
||||||
|
|
||||||
|
// Extended permissions access vector rules
|
||||||
|
bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||||
|
const char *cls, const char *range);
|
||||||
|
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||||
|
const char *cls, const char *range);
|
||||||
|
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
|
||||||
|
const char *cls, const char *range);
|
||||||
|
|
||||||
|
// Type rules
|
||||||
|
bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
|
||||||
|
const char *cls, const char *def, const char *obj);
|
||||||
|
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
|
||||||
|
const char *cls, const char *def);
|
||||||
|
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
|
||||||
|
const char *cls, const char *def);
|
||||||
|
|
||||||
|
// File system labeling
|
||||||
|
bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
|
||||||
|
const char *ctx);
|
||||||
|
|
||||||
|
#endif
|
||||||
75
kernel/setup.sh
Normal file
75
kernel/setup.sh
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
GKI_ROOT=$(pwd)
|
||||||
|
|
||||||
|
display_usage() {
|
||||||
|
echo "Usage: $0 [--cleanup | <commit-or-tag>]"
|
||||||
|
echo " --cleanup: Cleans up previous modifications made by the script."
|
||||||
|
echo " <commit-or-tag>: Sets up or updates the KernelSU to specified tag or commit."
|
||||||
|
echo " -h, --help: Displays this usage information."
|
||||||
|
echo " (no args): Sets up or updates the KernelSU environment to the latest tagged version."
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize_variables() {
|
||||||
|
if test -d "$GKI_ROOT/common/drivers"; then
|
||||||
|
DRIVER_DIR="$GKI_ROOT/common/drivers"
|
||||||
|
elif test -d "$GKI_ROOT/drivers"; then
|
||||||
|
DRIVER_DIR="$GKI_ROOT/drivers"
|
||||||
|
else
|
||||||
|
echo '[ERROR] "drivers/" directory not found.'
|
||||||
|
exit 127
|
||||||
|
fi
|
||||||
|
|
||||||
|
DRIVER_MAKEFILE=$DRIVER_DIR/Makefile
|
||||||
|
DRIVER_KCONFIG=$DRIVER_DIR/Kconfig
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reverts modifications made by this script
|
||||||
|
perform_cleanup() {
|
||||||
|
echo "[+] Cleaning up..."
|
||||||
|
[ -L "$DRIVER_DIR/kernelsu" ] && rm "$DRIVER_DIR/kernelsu" && echo "[-] Symlink removed."
|
||||||
|
grep -q "kernelsu" "$DRIVER_MAKEFILE" && sed -i '/kernelsu/d' "$DRIVER_MAKEFILE" && echo "[-] Makefile reverted."
|
||||||
|
grep -q "drivers/kernelsu/Kconfig" "$DRIVER_KCONFIG" && sed -i '/drivers\/kernelsu\/Kconfig/d' "$DRIVER_KCONFIG" && echo "[-] Kconfig reverted."
|
||||||
|
if [ -d "$GKI_ROOT/KernelSU" ]; then
|
||||||
|
rm -rf "$GKI_ROOT/KernelSU" && echo "[-] KernelSU directory deleted."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Sets up or update KernelSU environment
|
||||||
|
setup_kernelsu() {
|
||||||
|
echo "[+] Setting up KernelSU..."
|
||||||
|
test -d "$GKI_ROOT/KernelSU" || git clone https://github.com/ShirkNeko/KernelSU && echo "[+] Repository cloned."
|
||||||
|
cd "$GKI_ROOT/KernelSU"
|
||||||
|
git stash && echo "[-] Stashed current changes."
|
||||||
|
if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then
|
||||||
|
git checkout main && echo "[-] Switched to main branch."
|
||||||
|
fi
|
||||||
|
git pull && echo "[+] Repository updated."
|
||||||
|
if [ -z "${1-}" ]; then
|
||||||
|
git checkout "$(git describe --abbrev=0 --tags)" && echo "[-] Checked out latest tag."
|
||||||
|
else
|
||||||
|
git checkout "$1" && echo "[-] Checked out $1." || echo "[-] Checkout default branch"
|
||||||
|
fi
|
||||||
|
cd "$DRIVER_DIR"
|
||||||
|
ln -sf "$(realpath --relative-to="$DRIVER_DIR" "$GKI_ROOT/KernelSU/kernel")" "kernelsu" && echo "[+] Symlink created."
|
||||||
|
|
||||||
|
# Add entries in Makefile and Kconfig if not already existing
|
||||||
|
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" && echo "[+] Modified Makefile."
|
||||||
|
grep -q "source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" && echo "[+] Modified Kconfig."
|
||||||
|
echo '[+] Done.'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Process command-line arguments
|
||||||
|
if [ "$#" -eq 0 ]; then
|
||||||
|
initialize_variables
|
||||||
|
setup_kernelsu
|
||||||
|
elif [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
||||||
|
display_usage
|
||||||
|
elif [ "$1" = "--cleanup" ]; then
|
||||||
|
initialize_variables
|
||||||
|
perform_cleanup
|
||||||
|
else
|
||||||
|
initialize_variables
|
||||||
|
setup_kernelsu "$@"
|
||||||
|
fi
|
||||||
441
kernel/sucompat.c
Normal file
441
kernel/sucompat.c
Normal file
@@ -0,0 +1,441 @@
|
|||||||
|
#include <linux/dcache.h>
|
||||||
|
#include <linux/security.h>
|
||||||
|
#include <asm/current.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/kprobes.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||||
|
#include <linux/sched/task_stack.h>
|
||||||
|
#else
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "objsec.h"
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "arch.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "ksud.h"
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
|
||||||
|
#define SU_PATH "/system/bin/su"
|
||||||
|
#define SH_PATH "/system/bin/sh"
|
||||||
|
|
||||||
|
bool ksu_faccessat_hook __read_mostly = true;
|
||||||
|
bool ksu_stat_hook __read_mostly = true;
|
||||||
|
bool ksu_execve_sucompat_hook __read_mostly = true;
|
||||||
|
bool ksu_execveat_sucompat_hook __read_mostly = true;
|
||||||
|
#ifndef CONFIG_KSU_SUSFS_SUS_SU
|
||||||
|
bool ksu_devpts_hook __read_mostly = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern void ksu_escape_to_root();
|
||||||
|
|
||||||
|
static void __user *userspace_stack_buffer(const void *d, size_t len)
|
||||||
|
{
|
||||||
|
/* To avoid having to mmap a page in userspace, just write below the stack
|
||||||
|
* pointer. */
|
||||||
|
char __user *p = (void __user *)current_user_stack_pointer() - len;
|
||||||
|
|
||||||
|
return copy_to_user(p, d, len) ? NULL : p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char __user *sh_user_path(void)
|
||||||
|
{
|
||||||
|
static const char sh_path[] = SH_PATH;
|
||||||
|
return userspace_stack_buffer(sh_path, sizeof(sh_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
static char __user *ksud_user_path(void)
|
||||||
|
{
|
||||||
|
static const char ksud_path[] = KSUD_PATH;
|
||||||
|
|
||||||
|
return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int *__unused_flags)
|
||||||
|
{
|
||||||
|
const char su[] = SU_PATH;
|
||||||
|
|
||||||
|
#ifndef KSU_HOOK_WITH_KPROBES
|
||||||
|
if (!ksu_faccessat_hook) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!ksu_is_allow_uid(current_uid().val)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char path[sizeof(su) + 1];
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
|
|
||||||
|
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
||||||
|
pr_info("faccessat su->sh!\n");
|
||||||
|
*filename_user = sh_user_path();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) && defined(CONFIG_KSU_SUSFS_SUS_SU)
|
||||||
|
struct filename* susfs_ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags) {
|
||||||
|
const char su[] = SU_PATH;
|
||||||
|
struct filename *name = getname_flags(*filename_user, getname_statx_lookup_flags(*flags), NULL);
|
||||||
|
|
||||||
|
if (unlikely(IS_ERR(name) || name->name == NULL)) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_is_allow_uid(current_uid().val)) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (likely(memcmp(name->name, su, sizeof(su)))) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char sh[] = SH_PATH;
|
||||||
|
pr_info("vfs_fstatat su->sh!\n");
|
||||||
|
memcpy((void *)name->name, sh, sizeof(sh));
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
||||||
|
{
|
||||||
|
// const char sh[] = SH_PATH;
|
||||||
|
const char su[] = SU_PATH;
|
||||||
|
|
||||||
|
#ifndef KSU_HOOK_WITH_KPROBES
|
||||||
|
if (!ksu_stat_hook){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!ksu_is_allow_uid(current_uid().val)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(!filename_user)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char path[sizeof(su) + 1];
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
// Remove this later!! we use syscall hook, so this will never happen!!!!!
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0
|
||||||
|
// it becomes a `struct filename *` after 5.18
|
||||||
|
// https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216
|
||||||
|
const char sh[] = SH_PATH;
|
||||||
|
struct filename *filename = *((struct filename **)filename_user);
|
||||||
|
if (IS_ERR(filename)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (likely(memcmp(filename->name, su, sizeof(su))))
|
||||||
|
return 0;
|
||||||
|
pr_info("vfs_statx su->sh!\n");
|
||||||
|
memcpy((void *)filename->name, sh, sizeof(sh));
|
||||||
|
#else
|
||||||
|
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
|
|
||||||
|
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
||||||
|
pr_info("newfstatat su->sh!\n");
|
||||||
|
*filename_user = sh_user_path();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, void *__never_use_argv, void *__never_use_envp, int *__never_use_flags)
|
||||||
|
{
|
||||||
|
struct filename *filename;
|
||||||
|
const char su[] = SU_PATH;
|
||||||
|
const char ksud[] = KSUD_PATH;
|
||||||
|
#ifndef KSU_HOOK_WITH_KPROBES
|
||||||
|
if (!ksu_execveat_sucompat_hook) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (unlikely(!filename_ptr)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = *filename_ptr;
|
||||||
|
|
||||||
|
if (IS_ERR(filename)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (likely(memcmp(filename->name, su, sizeof(su))))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!ksu_is_allow_uid(current_uid().val))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pr_info("do_execveat_common su found\n");
|
||||||
|
memcpy((void *)filename->name, ksud, sizeof(ksud));
|
||||||
|
|
||||||
|
ksu_escape_to_root();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user, void *__never_use_argv, void *__never_use_envp, int *__never_use_flags)
|
||||||
|
{
|
||||||
|
const char su[] = SU_PATH;
|
||||||
|
char path[sizeof(su) + 1];
|
||||||
|
|
||||||
|
#ifndef KSU_HOOK_WITH_KPROBES
|
||||||
|
if (!ksu_execve_sucompat_hook) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (unlikely(!filename_user))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
|
|
||||||
|
if (likely(memcmp(path, su, sizeof(su))))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!ksu_is_allow_uid(current_uid().val))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pr_info("sys_execve su found\n");
|
||||||
|
*filename_user = ksud_user_path();
|
||||||
|
|
||||||
|
ksu_escape_to_root();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_handle_devpts(struct inode *inode)
|
||||||
|
{
|
||||||
|
#ifndef KSU_HOOK_WITH_KPROBES
|
||||||
|
if (!ksu_devpts_hook) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!current->mm) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uid_t uid = current_uid().val;
|
||||||
|
if (uid % 100000 < 10000) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_is_allow_uid(uid))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ksu_devpts_sid) {
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
||||||
|
struct inode_security_struct *sec = selinux_inode(inode);
|
||||||
|
#else
|
||||||
|
struct inode_security_struct *sec = (struct inode_security_struct *)inode->i_security;
|
||||||
|
#endif
|
||||||
|
if (sec) {
|
||||||
|
sec->sid = ksu_devpts_sid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef KSU_HOOK_WITH_KPROBES
|
||||||
|
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
|
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
||||||
|
const char __user **filename_user =
|
||||||
|
(const char **)&PT_REGS_PARM2(real_regs);
|
||||||
|
int *mode = (int *)&PT_REGS_PARM3(real_regs);
|
||||||
|
|
||||||
|
return ksu_handle_faccessat(dfd, filename_user, mode, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
|
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
||||||
|
const char __user **filename_user =
|
||||||
|
(const char **)&PT_REGS_PARM2(real_regs);
|
||||||
|
int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs);
|
||||||
|
|
||||||
|
return ksu_handle_stat(dfd, filename_user, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
|
const char __user **filename_user =
|
||||||
|
(const char **)&PT_REGS_PARM1(real_regs);
|
||||||
|
|
||||||
|
return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
static struct kprobe faccessat_kp = {
|
||||||
|
.symbol_name = SYS_FACCESSAT_SYMBOL,
|
||||||
|
.pre_handler = faccessat_handler_pre,
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
static struct kprobe faccessat_kp = {
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
|
||||||
|
.symbol_name = "do_faccessat",
|
||||||
|
#else
|
||||||
|
.symbol_name = "sys_faccessat",
|
||||||
|
#endif
|
||||||
|
.pre_handler = faccessat_handler_pre,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
static struct kprobe newfstatat_kp = {
|
||||||
|
.symbol_name = SYS_NEWFSTATAT_SYMBOL,
|
||||||
|
.pre_handler = newfstatat_handler_pre,
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
static struct kprobe newfstatat_kp = {
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||||
|
.symbol_name = "vfs_statx",
|
||||||
|
#else
|
||||||
|
.symbol_name = "vfs_fstatat",
|
||||||
|
#endif
|
||||||
|
.pre_handler = newfstatat_handler_pre,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
static struct kprobe execve_kp = {
|
||||||
|
.symbol_name = SYS_EXECVE_SYMBOL,
|
||||||
|
.pre_handler = execve_handler_pre,
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
static struct kprobe execve_kp = {
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||||
|
.symbol_name = "do_execveat_common",
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
||||||
|
.symbol_name = "__do_execve_file",
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
||||||
|
.symbol_name = "do_execveat_common",
|
||||||
|
#endif
|
||||||
|
.pre_handler = execve_handler_pre,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct inode *inode;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
|
||||||
|
struct file *file = (struct file *)PT_REGS_PARM2(regs);
|
||||||
|
inode = file->f_path.dentry->d_inode;
|
||||||
|
#else
|
||||||
|
inode = (struct inode *)PT_REGS_PARM2(regs);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ksu_handle_devpts(inode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct kprobe pts_unix98_lookup_kp = { .symbol_name =
|
||||||
|
"pts_unix98_lookup",
|
||||||
|
.pre_handler =
|
||||||
|
pts_unix98_lookup_pre };
|
||||||
|
|
||||||
|
static struct kprobe *init_kprobe(const char *name,
|
||||||
|
kprobe_pre_handler_t handler)
|
||||||
|
{
|
||||||
|
struct kprobe *kp = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
|
||||||
|
if (!kp)
|
||||||
|
return NULL;
|
||||||
|
kp->symbol_name = name;
|
||||||
|
kp->pre_handler = handler;
|
||||||
|
|
||||||
|
int ret = register_kprobe(kp);
|
||||||
|
pr_info("sucompat: register_%s kprobe: %d\n", name, ret);
|
||||||
|
if (ret) {
|
||||||
|
kfree(kp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destroy_kprobe(struct kprobe **kp_ptr)
|
||||||
|
{
|
||||||
|
struct kprobe *kp = *kp_ptr;
|
||||||
|
if (!kp)
|
||||||
|
return;
|
||||||
|
unregister_kprobe(kp);
|
||||||
|
synchronize_rcu();
|
||||||
|
kfree(kp);
|
||||||
|
*kp_ptr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct kprobe *su_kps[4];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// sucompat: permited process can execute 'su' to gain root access.
|
||||||
|
void ksu_sucompat_init()
|
||||||
|
{
|
||||||
|
#ifdef KSU_HOOK_WITH_KPROBES
|
||||||
|
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
|
||||||
|
su_kps[1] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre);
|
||||||
|
su_kps[2] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre);
|
||||||
|
su_kps[3] = init_kprobe("pts_unix98_lookup", pts_unix98_lookup_pre);
|
||||||
|
#else
|
||||||
|
ksu_faccessat_hook = true;
|
||||||
|
ksu_stat_hook = true;
|
||||||
|
ksu_execve_sucompat_hook = true;
|
||||||
|
ksu_execveat_sucompat_hook = true;
|
||||||
|
ksu_devpts_hook = true;
|
||||||
|
pr_info("ksu_sucompat_init: hooks enabled: execve/execveat_su, faccessat, stat, devpts\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sucompat_exit()
|
||||||
|
{
|
||||||
|
#ifdef KSU_HOOK_WITH_KPROBES
|
||||||
|
for (int i = 0; i < ARRAY_SIZE(su_kps); i++) {
|
||||||
|
destroy_kprobe(&su_kps[i]);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ksu_faccessat_hook = false;
|
||||||
|
ksu_stat_hook = false;
|
||||||
|
ksu_execve_sucompat_hook = false;
|
||||||
|
ksu_execveat_sucompat_hook = false;
|
||||||
|
ksu_devpts_hook = false;
|
||||||
|
pr_info("ksu_sucompat_exit: hooks disabled: execve/execveat_su, faccessat, stat, devpts\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_SUSFS_SUS_SU
|
||||||
|
extern bool ksu_devpts_hook;
|
||||||
|
|
||||||
|
void ksu_susfs_disable_sus_su(void) {
|
||||||
|
enable_kprobe(&execve_kp);
|
||||||
|
enable_kprobe(&newfstatat_kp);
|
||||||
|
enable_kprobe(&faccessat_kp);
|
||||||
|
enable_kprobe(&pts_unix98_lookup_kp);
|
||||||
|
ksu_devpts_hook = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_susfs_enable_sus_su(void) {
|
||||||
|
disable_kprobe(&execve_kp);
|
||||||
|
disable_kprobe(&newfstatat_kp);
|
||||||
|
disable_kprobe(&faccessat_kp);
|
||||||
|
disable_kprobe(&pts_unix98_lookup_kp);
|
||||||
|
ksu_devpts_hook = true;
|
||||||
|
}
|
||||||
|
#endif // CONFIG_KSU_SUSFS_SUS_SU
|
||||||
388
kernel/throne_tracker.c
Normal file
388
kernel/throne_tracker.c
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "ksu.h"
|
||||||
|
#include "manager.h"
|
||||||
|
#include "throne_tracker.h"
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
|
||||||
|
uid_t ksu_manager_uid = KSU_INVALID_UID;
|
||||||
|
|
||||||
|
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list.tmp"
|
||||||
|
|
||||||
|
struct uid_data {
|
||||||
|
struct list_head list;
|
||||||
|
u32 uid;
|
||||||
|
char package[KSU_MAX_PACKAGE_NAME];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int get_pkg_from_apk_path(char *pkg, const char *path)
|
||||||
|
{
|
||||||
|
int len = strlen(path);
|
||||||
|
if (len >= KSU_MAX_PACKAGE_NAME || len < 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
const char *last_slash = NULL;
|
||||||
|
const char *second_last_slash = NULL;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = len - 1; i >= 0; i--) {
|
||||||
|
if (path[i] == '/') {
|
||||||
|
if (!last_slash) {
|
||||||
|
last_slash = &path[i];
|
||||||
|
} else {
|
||||||
|
second_last_slash = &path[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!last_slash || !second_last_slash)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
const char *last_hyphen = strchr(second_last_slash, '-');
|
||||||
|
if (!last_hyphen || last_hyphen > last_slash)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int pkg_len = last_hyphen - second_last_slash - 1;
|
||||||
|
if (pkg_len >= KSU_MAX_PACKAGE_NAME || pkg_len <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// Copying the package name
|
||||||
|
strncpy(pkg, second_last_slash + 1, pkg_len);
|
||||||
|
pkg[pkg_len] = '\0';
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void crown_manager(const char *apk, struct list_head *uid_data)
|
||||||
|
{
|
||||||
|
char pkg[KSU_MAX_PACKAGE_NAME];
|
||||||
|
if (get_pkg_from_apk_path(pkg, apk) < 0) {
|
||||||
|
pr_err("Failed to get package name from apk path: %s\n", apk);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("manager pkg: %s\n", pkg);
|
||||||
|
|
||||||
|
#ifdef KSU_MANAGER_PACKAGE
|
||||||
|
// pkg is `/<real package>`
|
||||||
|
if (strncmp(pkg, KSU_MANAGER_PACKAGE, sizeof(KSU_MANAGER_PACKAGE))) {
|
||||||
|
pr_info("manager package is inconsistent with kernel build: %s\n",
|
||||||
|
KSU_MANAGER_PACKAGE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
struct list_head *list = (struct list_head *)uid_data;
|
||||||
|
struct uid_data *np;
|
||||||
|
|
||||||
|
list_for_each_entry (np, list, list) {
|
||||||
|
if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) {
|
||||||
|
pr_info("Crowning manager: %s(uid=%d)\n", pkg, np->uid);
|
||||||
|
ksu_set_manager_uid(np->uid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DATA_PATH_LEN 384 // 384 is enough for /data/app/<package>/base.apk
|
||||||
|
|
||||||
|
struct data_path {
|
||||||
|
char dirpath[DATA_PATH_LEN];
|
||||||
|
int depth;
|
||||||
|
struct list_head list;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct apk_path_hash {
|
||||||
|
unsigned int hash;
|
||||||
|
bool exists;
|
||||||
|
struct list_head list;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct list_head apk_path_hash_list = LIST_HEAD_INIT(apk_path_hash_list);
|
||||||
|
|
||||||
|
struct my_dir_context {
|
||||||
|
struct dir_context ctx;
|
||||||
|
struct list_head *data_path_list;
|
||||||
|
char *parent_dir;
|
||||||
|
void *private_data;
|
||||||
|
int depth;
|
||||||
|
int *stop;
|
||||||
|
};
|
||||||
|
// https://docs.kernel.org/filesystems/porting.html
|
||||||
|
// filldir_t (readdir callbacks) calling conventions have changed. Instead of returning 0 or -E... it returns bool now. false means "no more" (as -E... used to) and true - "keep going" (as 0 in old calling conventions). Rationale: callers never looked at specific -E... values anyway. -> iterate_shared() instances require no changes at all, all filldir_t ones in the tree converted.
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||||
|
#define FILLDIR_RETURN_TYPE bool
|
||||||
|
#define FILLDIR_ACTOR_CONTINUE true
|
||||||
|
#define FILLDIR_ACTOR_STOP false
|
||||||
|
#else
|
||||||
|
#define FILLDIR_RETURN_TYPE int
|
||||||
|
#define FILLDIR_ACTOR_CONTINUE 0
|
||||||
|
#define FILLDIR_ACTOR_STOP -EINVAL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||||
|
int namelen, loff_t off, u64 ino,
|
||||||
|
unsigned int d_type)
|
||||||
|
{
|
||||||
|
struct my_dir_context *my_ctx =
|
||||||
|
container_of(ctx, struct my_dir_context, ctx);
|
||||||
|
char dirpath[DATA_PATH_LEN];
|
||||||
|
|
||||||
|
if (!my_ctx) {
|
||||||
|
pr_err("Invalid context\n");
|
||||||
|
return FILLDIR_ACTOR_STOP;
|
||||||
|
}
|
||||||
|
if (my_ctx->stop && *my_ctx->stop) {
|
||||||
|
pr_info("Stop searching\n");
|
||||||
|
return FILLDIR_ACTOR_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen))
|
||||||
|
return FILLDIR_ACTOR_CONTINUE; // Skip "." and ".."
|
||||||
|
|
||||||
|
if (d_type == DT_DIR && namelen >= 8 && !strncmp(name, "vmdl", 4) &&
|
||||||
|
!strncmp(name + namelen - 4, ".tmp", 4)) {
|
||||||
|
pr_info("Skipping directory: %.*s\n", namelen, name);
|
||||||
|
return FILLDIR_ACTOR_CONTINUE; // Skip staging package
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir,
|
||||||
|
namelen, name) >= DATA_PATH_LEN) {
|
||||||
|
pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen,
|
||||||
|
name);
|
||||||
|
return FILLDIR_ACTOR_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d_type == DT_DIR && my_ctx->depth > 0 &&
|
||||||
|
(my_ctx->stop && !*my_ctx->stop)) {
|
||||||
|
struct data_path *data = kmalloc(sizeof(struct data_path), GFP_ATOMIC);
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
pr_err("Failed to allocate memory for %s\n", dirpath);
|
||||||
|
return FILLDIR_ACTOR_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
strscpy(data->dirpath, dirpath, DATA_PATH_LEN);
|
||||||
|
data->depth = my_ctx->depth - 1;
|
||||||
|
list_add_tail(&data->list, my_ctx->data_path_list);
|
||||||
|
} else {
|
||||||
|
if ((namelen == 8) && (strncmp(name, "base.apk", namelen) == 0)) {
|
||||||
|
struct apk_path_hash *pos, *n;
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
|
||||||
|
unsigned int hash = full_name_hash(dirpath, strlen(dirpath));
|
||||||
|
#else
|
||||||
|
unsigned int hash = full_name_hash(NULL, dirpath, strlen(dirpath));
|
||||||
|
#endif
|
||||||
|
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
||||||
|
if (hash == pos->hash) {
|
||||||
|
pos->exists = true;
|
||||||
|
return FILLDIR_ACTOR_CONTINUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_manager = ksu_is_manager_apk(dirpath);
|
||||||
|
pr_info("Found new base.apk at path: %s, is_manager: %d\n",
|
||||||
|
dirpath, is_manager);
|
||||||
|
if (is_manager) {
|
||||||
|
crown_manager(dirpath, my_ctx->private_data);
|
||||||
|
*my_ctx->stop = 1;
|
||||||
|
|
||||||
|
// Manager found, clear APK cache list
|
||||||
|
list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) {
|
||||||
|
list_del(&pos->list);
|
||||||
|
kfree(pos);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC);
|
||||||
|
apk_data->hash = hash;
|
||||||
|
apk_data->exists = true;
|
||||||
|
list_add_tail(&apk_data->list, &apk_path_hash_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FILLDIR_ACTOR_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_manager(const char *path, int depth, struct list_head *uid_data)
|
||||||
|
{
|
||||||
|
int i, stop = 0;
|
||||||
|
struct list_head data_path_list;
|
||||||
|
INIT_LIST_HEAD(&data_path_list);
|
||||||
|
|
||||||
|
// Initialize APK cache list
|
||||||
|
struct apk_path_hash *pos, *n;
|
||||||
|
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
||||||
|
pos->exists = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First depth
|
||||||
|
struct data_path data;
|
||||||
|
strscpy(data.dirpath, path, DATA_PATH_LEN);
|
||||||
|
data.depth = depth;
|
||||||
|
list_add_tail(&data.list, &data_path_list);
|
||||||
|
|
||||||
|
for (i = depth; i > 0; i--) {
|
||||||
|
struct data_path *pos, *n;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(pos, n, &data_path_list, list) {
|
||||||
|
struct my_dir_context ctx = { .ctx.actor = my_actor,
|
||||||
|
.data_path_list = &data_path_list,
|
||||||
|
.parent_dir = pos->dirpath,
|
||||||
|
.private_data = uid_data,
|
||||||
|
.depth = pos->depth,
|
||||||
|
.stop = &stop };
|
||||||
|
struct file *file;
|
||||||
|
|
||||||
|
if (!stop) {
|
||||||
|
file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0);
|
||||||
|
if (IS_ERR(file)) {
|
||||||
|
pr_err("Failed to open directory: %s, err: %ld\n", pos->dirpath, PTR_ERR(file));
|
||||||
|
goto skip_iterate;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterate_dir(file, &ctx.ctx);
|
||||||
|
filp_close(file, NULL);
|
||||||
|
}
|
||||||
|
skip_iterate:
|
||||||
|
list_del(&pos->list);
|
||||||
|
if (pos != &data)
|
||||||
|
kfree(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove stale cached APK entries
|
||||||
|
list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) {
|
||||||
|
if (!pos->exists) {
|
||||||
|
list_del(&pos->list);
|
||||||
|
kfree(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_uid_exist(uid_t uid, char *package, void *data)
|
||||||
|
{
|
||||||
|
struct list_head *list = (struct list_head *)data;
|
||||||
|
struct uid_data *np;
|
||||||
|
|
||||||
|
bool exist = false;
|
||||||
|
list_for_each_entry (np, list, list) {
|
||||||
|
if (np->uid == uid % 100000 &&
|
||||||
|
strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) {
|
||||||
|
exist = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return exist;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_track_throne()
|
||||||
|
{
|
||||||
|
struct file *fp =
|
||||||
|
ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n",
|
||||||
|
__func__, PTR_ERR(fp));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct list_head uid_list;
|
||||||
|
INIT_LIST_HEAD(&uid_list);
|
||||||
|
|
||||||
|
char chr = 0;
|
||||||
|
loff_t pos = 0;
|
||||||
|
loff_t line_start = 0;
|
||||||
|
char buf[KSU_MAX_PACKAGE_NAME];
|
||||||
|
for (;;) {
|
||||||
|
ssize_t count =
|
||||||
|
ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos);
|
||||||
|
if (count != sizeof(chr))
|
||||||
|
break;
|
||||||
|
if (chr != '\n')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
count = ksu_kernel_read_compat(fp, buf, sizeof(buf),
|
||||||
|
&line_start);
|
||||||
|
|
||||||
|
struct uid_data *data =
|
||||||
|
kzalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
||||||
|
if (!data) {
|
||||||
|
filp_close(fp, 0);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *tmp = buf;
|
||||||
|
const char *delim = " ";
|
||||||
|
char *package = strsep(&tmp, delim);
|
||||||
|
char *uid = strsep(&tmp, delim);
|
||||||
|
if (!uid || !package) {
|
||||||
|
pr_err("update_uid: package or uid is NULL!\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 res;
|
||||||
|
if (kstrtou32(uid, 10, &res)) {
|
||||||
|
pr_err("update_uid: uid parse err\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data->uid = res;
|
||||||
|
strncpy(data->package, package, KSU_MAX_PACKAGE_NAME);
|
||||||
|
list_add_tail(&data->list, &uid_list);
|
||||||
|
// reset line start
|
||||||
|
line_start = pos;
|
||||||
|
}
|
||||||
|
filp_close(fp, 0);
|
||||||
|
|
||||||
|
// now update uid list
|
||||||
|
struct uid_data *np;
|
||||||
|
struct uid_data *n;
|
||||||
|
|
||||||
|
// first, check if manager_uid exist!
|
||||||
|
bool manager_exist = false;
|
||||||
|
list_for_each_entry (np, &uid_list, list) {
|
||||||
|
// if manager is installed in work profile, the uid in packages.list is still equals main profile
|
||||||
|
// don't delete it in this case!
|
||||||
|
int manager_uid = ksu_get_manager_uid() % 100000;
|
||||||
|
if (np->uid == manager_uid) {
|
||||||
|
manager_exist = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!manager_exist) {
|
||||||
|
if (ksu_is_manager_uid_valid()) {
|
||||||
|
pr_info("manager is uninstalled, invalidate it!\n");
|
||||||
|
ksu_invalidate_manager_uid();
|
||||||
|
}
|
||||||
|
pr_info("Searching manager...\n");
|
||||||
|
search_manager("/data/app", 2, &uid_list);
|
||||||
|
pr_info("Search manager finished\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// then prune the allowlist
|
||||||
|
ksu_prune_allowlist(is_uid_exist, &uid_list);
|
||||||
|
out:
|
||||||
|
// free uid_list
|
||||||
|
list_for_each_entry_safe (np, n, &uid_list, list) {
|
||||||
|
list_del(&np->list);
|
||||||
|
kfree(np);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_throne_tracker_init()
|
||||||
|
{
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_throne_tracker_exit()
|
||||||
|
{
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
10
kernel/throne_tracker.h
Normal file
10
kernel/throne_tracker.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef __KSU_H_UID_OBSERVER
|
||||||
|
#define __KSU_H_UID_OBSERVER
|
||||||
|
|
||||||
|
void ksu_throne_tracker_init();
|
||||||
|
|
||||||
|
void ksu_throne_tracker_exit();
|
||||||
|
|
||||||
|
void ksu_track_throne();
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user