diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index f288702..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. 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
-them 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 prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. 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.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey 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;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If 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 convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU 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 that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- 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.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-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.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- 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
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 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, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program 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, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU 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. But first, please read
-.
diff --git a/README.md b/README.md
deleted file mode 100644
index e5a86f0..0000000
--- a/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# sukisu
-
diff --git a/SECURITY.md b/SECURITY.md
deleted file mode 100644
index 83040d9..0000000
--- a/SECURITY.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Reporting Security Issues
-
-The KernelSU team and community take security bugs in KernelSU seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
-
-To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/tiann/KernelSU/security/advisories/new) tab, or you can mailto [weishu](mailto:twsxtd@gmail.com) directly.
-
-The KernelSU team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.
diff --git a/crowdin.yml b/crowdin.yml
deleted file mode 100644
index ce0c41a..0000000
--- a/crowdin.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-project_id_env: CROWDIN_PROJECT_ID
-api_token_env: CROWDIN_API_TOKEN
-preserve_hierarchy: 1
-files:
- - source: /manager/app/src/main/res/values/strings.xml
- translation: /manager/app/src/main/res/values-%two_letters_code%/strings.xml
diff --git a/docs/guide/tracepoint-hook.md b/docs/guide/tracepoint-hook.md
index 8333561..af5fa72 100644
--- a/docs/guide/tracepoint-hook.md
+++ b/docs/guide/tracepoint-hook.md
@@ -44,7 +44,7 @@ Generally need to modify the `do_execve` and `compat_do_execve` methods in `fs/e
.ptr.compat = __envp,
};
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
-+ trace_ksu_trace_execveat_sucompat_hook((int *)AT_FDCWD, &filename, NULL, NULL, NULL); /* 32-bit su */
++ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0); // 32-bit su and 32-on-64 support
+#endif
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}
@@ -237,34 +237,3 @@ Need to modify the `input_event` method in `drivers/input/input.c`, not `input_h
spin_lock_irqsave(&dev->event_lock, flags);
```
-
-### devpts Hook (`pty.c`)
-
-Need to modify the `pts_unix98_lookup` method in `drivers/tty/pty.c`
-
-```patch
---- a/drivers/tty/pty.c
-+++ b/drivers/tty/pty.c
-@@ -31,6 +31,10 @@
- #include
- #include "tty.h"
-
-+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
-+#include <../../drivers/kernelsu/ksu_trace.h>
-+#endif
-+
- #undef TTY_DEBUG_HANGUP
- #ifdef TTY_DEBUG_HANGUP
- # define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args)
-@@ -707,6 +711,10 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
- {
- struct tty_struct *tty;
-
-+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
-+ trace_ksu_trace_devpts_hook((struct inode *)file->f_path.dentry->d_inode);
-+#endif
-+
- mutex_lock(&devpts_mutex);
- tty = devpts_get_priv(file->f_path.dentry);
- mutex_unlock(&devpts_mutex);
-```
diff --git a/docs/zh/guide/tracepoint-hook.md b/docs/zh/guide/tracepoint-hook.md
index 3b98fbd..7dde784 100644
--- a/docs/zh/guide/tracepoint-hook.md
+++ b/docs/zh/guide/tracepoint-hook.md
@@ -44,7 +44,7 @@
.ptr.compat = __envp,
};
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
-+ trace_ksu_trace_execveat_sucompat_hook((int *)AT_FDCWD, &filename, NULL, NULL, NULL); /* 32-bit su */
++ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0)); // 32-bit su and 32-on-64 support
+#endif
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}
@@ -236,35 +236,4 @@
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
-```
-
-### devpts 钩子 (`pty.c`)
-
-需要修改 `drivers/tty/pty.c` 的 `pts_unix98_lookup` 方法
-
-```patch
---- a/drivers/tty/pty.c
-+++ b/drivers/tty/pty.c
-@@ -31,6 +31,10 @@
- #include
- #include "tty.h"
-
-+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
-+#include <../../drivers/kernelsu/ksu_trace.h>
-+#endif
-+
- #undef TTY_DEBUG_HANGUP
- #ifdef TTY_DEBUG_HANGUP
- # define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args)
-@@ -707,6 +711,10 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
- {
- struct tty_struct *tty;
-
-+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
-+ trace_ksu_trace_devpts_hook((struct inode *)file->f_path.dentry->d_inode);
-+#endif
-+
- mutex_lock(&devpts_mutex);
- tty = devpts_get_priv(file->f_path.dentry);
- mutex_unlock(&devpts_mutex);
-```
+```
\ No newline at end of file
diff --git a/icon/logo.png b/icon/logo.png
deleted file mode 100644
index cddff92..0000000
Binary files a/icon/logo.png and /dev/null differ
diff --git a/justfile b/justfile
deleted file mode 100644
index 51bef76..0000000
--- a/justfile
+++ /dev/null
@@ -1,14 +0,0 @@
-alias bk := build_ksud
-alias bm := build_manager
-
-build_ksud:
- cross build --target aarch64-linux-android --release --manifest-path ./userspace/ksud/Cargo.toml
-
-build_manager: build_ksud
- cp userspace/ksud/target/aarch64-linux-android/release/ksud manager/app/src/main/jniLibs/arm64-v8a/libksud.so
- cd manager && ./gradlew aDebug
-
-clippy:
- cargo fmt --manifest-path ./userspace/ksud/Cargo.toml
- cross clippy --target x86_64-pc-windows-gnu --release --manifest-path ./userspace/ksud/Cargo.toml
- cross clippy --target aarch64-linux-android --release --manifest-path ./userspace/ksud/Cargo.toml
diff --git a/kernel/.clang-format b/kernel/.clang-format
index 10dc5a9..6453cf9 100644
--- a/kernel/.clang-format
+++ b/kernel/.clang-format
@@ -56,8 +56,8 @@ ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
#CompactNamespaces: false # Unknown to clang-format-4.0
ConstructorInitializerAllOnOneLineOrOnePerLine: false
-ConstructorInitializerIndentWidth: 8
-ContinuationIndentWidth: 8
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
@@ -501,7 +501,7 @@ IncludeCategories:
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
#IndentPPDirectives: None # Unknown to clang-format-5.0
-IndentWidth: 8
+IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
@@ -511,7 +511,7 @@ MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
-ObjCBlockIndentWidth: 8
+ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
@@ -543,6 +543,6 @@ SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp03
-TabWidth: 8
-UseTab: Always
+TabWidth: 4
+UseTab: Never
...
diff --git a/kernel/.gitignore b/kernel/.gitignore
new file mode 100644
index 0000000..20d68ae
--- /dev/null
+++ b/kernel/.gitignore
@@ -0,0 +1,22 @@
+.cache/
+.thinlto-cache/
+compile_commands.json
+*.ko
+*.o
+*.mod
+*.lds
+*.mod.o
+.*.o*
+.*.mod*
+*.ko*
+*.mod.c
+*.symvers*
+*.order
+.*.ko.cmd
+.tmp_versions/
+libs/
+obj/
+
+CLAUDE.md
+.ddk-version
+.vscode/settings.json
\ No newline at end of file
diff --git a/kernel/.vscode/c_cpp_properties.json b/kernel/.vscode/c_cpp_properties.json
new file mode 100644
index 0000000..f661370
--- /dev/null
+++ b/kernel/.vscode/c_cpp_properties.json
@@ -0,0 +1,11 @@
+{
+ "configurations": [
+ {
+ "name": "Linux",
+ "cStandard": "c11",
+ "intelliSenseMode": "gcc-arm64",
+ "compileCommands": "${workspaceFolder}/compile_commands.json"
+ }
+ ],
+ "version": 4
+}
\ No newline at end of file
diff --git a/kernel/.vscode/generate_compdb.py b/kernel/.vscode/generate_compdb.py
new file mode 100644
index 0000000..8866913
--- /dev/null
+++ b/kernel/.vscode/generate_compdb.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+
+from __future__ import print_function, division
+
+import argparse
+import fnmatch
+import functools
+import json
+import math
+import multiprocessing
+import os
+import re
+import sys
+
+
+CMD_VAR_RE = re.compile(r'^\s*(?:saved)?cmd_(\S+)\s*:=\s*(.+)\s*$', re.MULTILINE)
+SOURCE_VAR_RE = re.compile(r'^\s*source_(\S+)\s*:=\s*(.+)\s*$', re.MULTILINE)
+
+
+def print_progress_bar(progress):
+ progress_bar = '[' + '|' * int(50 * progress) + '-' * int(50 * (1.0 - progress)) + ']'
+ print('\r', progress_bar, "{0:.1%}".format(progress), end='\r', file=sys.stderr)
+
+
+def parse_cmd_file(out_dir, cmdfile_path):
+ with open(cmdfile_path, 'r') as cmdfile:
+ cmdfile_content = cmdfile.read()
+
+ commands = { match.group(1): match.group(2) for match in CMD_VAR_RE.finditer(cmdfile_content) }
+ sources = { match.group(1): match.group(2) for match in SOURCE_VAR_RE.finditer(cmdfile_content) }
+
+ return [{
+ 'directory': out_dir,
+ 'command': commands[o_file_name],
+ 'file': source,
+ 'output': o_file_name
+ } for o_file_name, source in sources.items()]
+
+
+def gen_compile_commands(cmd_file_search_path, out_dir):
+ print("Building *.o.cmd file list...", file=sys.stderr)
+
+ out_dir = os.path.abspath(out_dir)
+
+ if not cmd_file_search_path:
+ cmd_file_search_path = [out_dir]
+
+ cmd_files = []
+ for search_path in cmd_file_search_path:
+ if (os.path.isdir(search_path)):
+ for cur_dir, subdir, files in os.walk(search_path):
+ cmd_files.extend(os.path.join(cur_dir, cmdfile_name) for cmdfile_name in fnmatch.filter(files, '*.o.cmd'))
+ else:
+ cmd_files.extend(search_path)
+
+ if not cmd_files:
+ print("No *.o.cmd files found in", ", ".join(cmd_file_search_path), file=sys.stderr)
+ return
+
+ print("Parsing *.o.cmd files...", file=sys.stderr)
+
+ n_processed = 0
+ print_progress_bar(0)
+
+ compdb = []
+ pool = multiprocessing.Pool()
+ try:
+ for compdb_chunk in pool.imap_unordered(functools.partial(parse_cmd_file, out_dir), cmd_files, chunksize=int(math.sqrt(len(cmd_files)))):
+ compdb.extend(compdb_chunk)
+ n_processed += 1
+ print_progress_bar(n_processed / len(cmd_files))
+
+ finally:
+ pool.terminate()
+ pool.join()
+
+ print(file=sys.stderr)
+ print("Writing compile_commands.json...", file=sys.stderr)
+
+ with open('compile_commands.json', 'w') as compdb_file:
+ json.dump(compdb, compdb_file, indent=1)
+
+
+def main():
+ cmd_parser = argparse.ArgumentParser()
+ cmd_parser.add_argument('-O', '--out-dir', type=str, default=os.getcwd(), help="Build output directory")
+ cmd_parser.add_argument('cmd_file_search_path', nargs='*', help="*.cmd file search path")
+ gen_compile_commands(**vars(cmd_parser.parse_args()))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/kernel/.vscode/tasks.json b/kernel/.vscode/tasks.json
new file mode 100644
index 0000000..4ed9adb
--- /dev/null
+++ b/kernel/.vscode/tasks.json
@@ -0,0 +1,16 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Generate compile_commands.json",
+ "type": "process",
+ "command": "python",
+ "args": [
+ "${workspaceRoot}/.vscode/generate_compdb.py"
+ ],
+ "problemMatcher": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/kernel/Kconfig b/kernel/Kconfig
index a24a5b9..fd5e0c1 100644
--- a/kernel/Kconfig
+++ b/kernel/Kconfig
@@ -1,57 +1,42 @@
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.
+ tristate "KernelSU function support"
+ 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.
+ bool "KernelSU debug mode"
+ depends on KSU
+ default n
+ help
+ Enable KernelSU debug mode.
+
+config KSU_MANUAL_SU
+ bool "Use manual su"
+ depends on KSU
+ default y
+ help
+ Use manual su and authorize the corresponding command line and application via prctl
config KPM
- bool "Enable SukiSU KPM"
- depends on KSU && 64BIT
- default n
- help
- Enabling this option will activate the KPM feature of SukiSU.
- This option is suitable for scenarios where you need to force KPM to be enabled.
- but it may affect system stability.
- select KALLSYMS
- select KALLSYMS_ALL
-
-choice
- prompt "KernelSU hook type"
- depends on KSU
- default KSU_KPROBES_HOOK
+ bool "Enable SukiSU KPM"
+ depends on KSU && 64BIT
+ default n
help
- Hook type for KernelSU
-
-config KSU_KPROBES_HOOK
- bool "Hook KernelSU with Kprobes"
- depends on KPROBES
- help
- If enabled, Hook required KernelSU syscalls with Kernel-probe.
-
-config KSU_TRACEPOINT_HOOK
- bool "Hook KernelSU with Tracepoint"
- depends on TRACEPOINTS
- help
- If enabled, Hook required KernelSU syscalls with Tracepoint.
+ Enabling this option will activate the KPM feature of SukiSU.
+ This option is suitable for scenarios where you need to force KPM to be enabled.
+ but it may affect system stability.
+ select KALLSYMS
+ select KALLSYMS_ALL
config KSU_MANUAL_HOOK
bool "Hook KernelSU manually"
- depends on KSU != m
- help
- If enabled, Hook required KernelSU syscalls with manually-patched function.
-
-endchoice
+ depends on KSU != m
+ help
+ If enabled, Hook required KernelSU syscalls with manually-patched function.
endmenu
diff --git a/kernel/Makefile b/kernel/Makefile
index 0c2ba39..8b246bd 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -1,17 +1,28 @@
kernelsu-objs := ksu.o
kernelsu-objs += allowlist.o
+kernelsu-objs += app_profile.o
kernelsu-objs += dynamic_manager.o
kernelsu-objs += apk_sign.o
kernelsu-objs += sucompat.o
+kernelsu-objs += syscall_hook_manager.o
kernelsu-objs += throne_tracker.o
-kernelsu-objs += core_hook.o
+kernelsu-objs += pkg_observer.o
+kernelsu-objs += throne_tracker.o
+kernelsu-objs += umount_manager.o
+kernelsu-objs += setuid_hook.o
+kernelsu-objs += kernel_umount.o
+kernelsu-objs += supercalls.o
+kernelsu-objs += feature.o
kernelsu-objs += ksud.o
kernelsu-objs += embed_ksud.o
-kernelsu-objs += kernel_compat.o
+kernelsu-objs += seccomp_cache.o
+kernelsu-objs += file_wrapper.o
kernelsu-objs += throne_comm.o
+kernelsu-objs += sulog.o
-ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y)
-kernelsu-objs += ksu_trace.o
+ifeq ($(CONFIG_KSU_MANUAL_SU), y)
+ccflags-y += -DCONFIG_KSU_MANUAL_SU
+kernelsu-objs += manual_su.o
endif
kernelsu-objs += selinux/selinux.o
@@ -21,46 +32,62 @@ 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
obj-$(CONFIG_KSU) += kernelsu.o
-obj-$(CONFIG_KSU_TRACEPOINT_HOOK) += ksu_trace_export.o
obj-$(CONFIG_KPM) += kpm/
-
REPO_OWNER := SukiSU-Ultra
REPO_NAME := SukiSU-Ultra
REPO_BRANCH := main
-KSU_VERSION_API := 3.2.0
+KSU_VERSION_API := 4.0.0
GIT_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git
CURL_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin curl
+KDIR := $(KDIR)
+MDIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
+
+ifneq ($(KDIR),)
+$(info -- KDIR: $(KDIR))
+$(info -- MDIR: $(MDIR))
+endif
+
KSU_GITHUB_VERSION := $(shell $(CURL_BIN) -s "https://api.github.com/repos/$(REPO_OWNER)/$(REPO_NAME)/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
KSU_GITHUB_VERSION_COMMIT := $(shell $(CURL_BIN) -sI "https://api.github.com/repos/$(REPO_OWNER)/$(REPO_NAME)/commits?sha=$(REPO_BRANCH)&per_page=1" | grep -i "link:" | sed -n 's/.*page=\([0-9]*\)>; rel="last".*/\1/p')
-LOCAL_GIT_EXISTS := $(shell test -e $(srctree)/$(src)/../.git && echo 1 || echo 0)
+ifeq ($(findstring $(srctree),$(src)),$(srctree))
+ KSU_SRC := $(src)
+else
+ KSU_SRC := $(srctree)/$(src)
+endif
+
+ifneq ($(shell test -e $(KSU_SRC)/../.git && echo "in-tree"),in-tree)
+ KSU_SRC := $(MDIR)
+endif
+
+LOCAL_GIT_EXISTS := $(shell test -e $(KSU_SRC)/../.git && echo 1 || echo 0)
define get_ksu_version_full
-v$1-$(shell cd $(srctree)/$(src); $(GIT_BIN) rev-parse --short=8 HEAD)@$(shell cd $(srctree)/$(src); $(GIT_BIN) rev-parse --abbrev-ref HEAD)
+v$1-$(shell cd $(KSU_SRC); $(GIT_BIN) rev-parse --short=8 HEAD)@$(shell cd $(KSU_SRC); $(GIT_BIN) rev-parse --abbrev-ref HEAD)
endef
ifeq ($(KSU_GITHUB_VERSION_COMMIT),)
ifeq ($(LOCAL_GIT_EXISTS),1)
- $(shell cd $(srctree)/$(src); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
- KSU_LOCAL_VERSION := $(shell cd $(srctree)/$(src); $(GIT_BIN) rev-list --count $(REPO_BRANCH))
- KSU_VERSION := $(shell expr 10000 + $(KSU_LOCAL_VERSION) + 700)
+ $(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
+ KSU_LOCAL_VERSION := $(shell cd $(KSU_SRC); $(GIT_BIN) rev-list --count $(REPO_BRANCH))
+ KSU_VERSION := $(shell expr 40000 + $(KSU_LOCAL_VERSION) - 2815)
$(info -- $(REPO_NAME) version (local .git): $(KSU_VERSION))
else
KSU_VERSION := 13000
$(warning -- Could not fetch version online or via local .git! Using fallback version: $(KSU_VERSION))
endif
else
- KSU_VERSION := $(shell expr 10000 + $(KSU_GITHUB_VERSION_COMMIT) + 700)
+ KSU_VERSION := $(shell expr 40000 + $(KSU_GITHUB_VERSION_COMMIT) - 2815)
$(info -- $(REPO_NAME) version (GitHub): $(KSU_VERSION))
endif
ifeq ($(KSU_GITHUB_VERSION),)
ifeq ($(LOCAL_GIT_EXISTS),1)
- $(shell cd $(srctree)/$(src); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
+ $(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
KSU_VERSION_FULL := $(call get_ksu_version_full,$(KSU_VERSION_API))
$(info -- $(REPO_NAME) version (local .git): $(KSU_VERSION_FULL))
$(info -- $(REPO_NAME) Formatted version (local .git): $(KSU_VERSION))
@@ -69,7 +96,7 @@ ifeq ($(KSU_GITHUB_VERSION),)
$(warning -- $(REPO_NAME) version: $(KSU_VERSION_FULL))
endif
else
- $(shell cd $(srctree)/$(src); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
+ $(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
KSU_VERSION_FULL := $(call get_ksu_version_full,$(KSU_GITHUB_VERSION))
$(info -- $(REPO_NAME) version (Github): $(KSU_VERSION_FULL))
endif
@@ -93,14 +120,13 @@ ccflags-y += -DKSU_MANAGER_PACKAGE=\"$(KSU_MANAGER_PACKAGE)\"
$(info -- SukiSU Manager package name: $(KSU_MANAGER_PACKAGE))
endif
-$(info -- Supported Unofficial Manager: 5ec1cff (GKI) ShirkNeko udochina (GKI and KPM))
-
-ifeq ($(CONFIG_KSU_KPROBES_HOOK), y)
-$(info -- SukiSU: CONFIG_KSU_KPROBES_HOOK)
-else ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y)
-$(info -- SukiSU: CONFIG_KSU_TRACEPOINT_HOOK)
-else ifeq ($(CONFIG_KSU_MANUAL_HOOK), y)
-$(info -- SukiSU: CONFIG_KSU_MANUAL_HOOK)
+ifeq ($(CONFIG_KSU_MANUAL_HOOK), y)
+ccflags-y += -DKSU_MANUAL_HOOK
+$(info -- SukiSU: KSU_MANUAL_HOOK Temporarily discontinued))
+else
+ccflags-y += -DKSU_KPROBES_HOOK
+ccflags-y += -DKSU_TP_HOOK
+$(info -- SukiSU: KSU_TRACEPOINT_HOOK)
endif
KERNEL_VERSION := $(VERSION).$(PATCHLEVEL)
@@ -117,14 +143,30 @@ endif
$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
$(info -- KERNEL_TYPE: $(KERNEL_TYPE))
-$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
ifeq ($(CONFIG_KPM), y)
$(info -- KPM is enabled)
else
$(info -- KPM is disabled)
endif
-ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
-ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
+# Check new vfs_getattr()
+ifeq ($(shell grep -A1 "^int vfs_getattr" $(srctree)/fs/stat.c | grep -q "query_flags" ; echo $$?),0)
+ccflags-y += -DKSU_HAS_NEW_VFS_GETATTR
+endif
+
+# Function proc_ops check
+ifeq ($(shell grep -q "struct proc_ops " $(srctree)/include/linux/proc_fs.h; echo $$?),0)
+ccflags-y += -DKSU_COMPAT_HAS_PROC_OPS
+endif
+
+ccflags-y += -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat -Wno-missing-prototypes
+ccflags-y += -Wno-declaration-after-statement -Wno-unused-function -Wno-unused-variable
+
+all:
+ make -C $(KDIR) M=$(MDIR) modules
+compdb:
+ python3 $(MDIR)/.vscode/generate_compdb.py -O $(KDIR) $(MDIR)
+clean:
+ make -C $(KDIR) M=$(MDIR) clean
# Keep a new line here!! Because someone may append config
diff --git a/kernel/allowlist.c b/kernel/allowlist.c
index 9745567..914fefe 100644
--- a/kernel/allowlist.c
+++ b/kernel/allowlist.c
@@ -1,3 +1,5 @@
+#include
+#include
#include
#include
#include
@@ -8,14 +10,16 @@
#include
#include
#include
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
#include
+#endif
-#include "ksu.h"
#include "klog.h" // IWYU pragma: keep
+#include "ksud.h"
#include "selinux/selinux.h"
-#include "kernel_compat.h"
#include "allowlist.h"
#include "manager.h"
+#include "syscall_hook_manager.h"
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
#define FILE_FORMAT_VERSION 3 // u32
@@ -29,58 +33,61 @@ static DEFINE_MUTEX(allowlist_mutex);
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);
+void persistent_allow_list(void);
+
+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;
+ int *temp_arr;
+ int i, j;
- if (allow_list_pointer == 0)
- return;
+ 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;
- }
+ 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];
- }
+ 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;
+ allow_list_pointer = j;
- for (; j < ARRAY_SIZE(allow_list_arr); j++)
- temp_arr[j] = -1;
+ for (; j < ARRAY_SIZE(allow_list_arr); j++)
+ temp_arr[j] = -1;
- memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
- kfree(temp_arr);
+ memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
+ kfree(temp_arr);
}
-static void init_default_profiles()
+static void init_default_profiles(void)
{
- kernel_cap_t full_cap = CAP_FULL_SET;
+ 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);
+ 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;
+ // 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;
+ struct list_head list;
+ struct app_profile profile;
};
static struct list_head allow_list;
@@ -90,437 +97,535 @@ static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE);
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
-static struct work_struct ksu_save_work;
-static struct work_struct ksu_load_work;
-
-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);
- }
+ 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()
+static void ksu_grant_root_to_shell(void)
{
- 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);
+ 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;
+ 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;
- }
- }
+ 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;
+ 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 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 (!profile) {
+ return false;
+ }
- if (profile->version < KSU_APP_PROFILE_VER) {
- pr_info("Unsupported profile version: %d\n", profile->version);
- 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 (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;
- }
- }
+ if (strlen(profile->rp_config.profile.selinux_domain) == 0) {
+ return false;
+ }
+ }
- return true;
+ 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;
+ 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;
- }
+ 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;
- }
- }
+ 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;
- }
+ // 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);
+ 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;
+ 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));
- }
+ // 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 (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();
+ if (persist) {
+ persistent_allow_list();
+ // FIXME: use a new flag
+ ksu_mark_running_process();
+ }
- return result;
+ return result;
}
bool __ksu_is_allow_uid(uid_t uid)
{
- int i;
+ int i;
- if (unlikely(uid == 0)) {
- // already root, but only allow our domain.
- return is_ksu_domain();
- }
+ if (forbid_system_uid(uid)) {
+ // do not bother going through the list if it's system
+ return false;
+ }
- 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(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;
+ }
+ }
- 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;
+}
- return false;
+bool __ksu_is_allow_uid_for_current(uid_t uid)
+{
+ if (unlikely(uid == 0)) {
+ // already root, but only allow our domain.
+ return is_ksu_domain();
+ }
+ return __ksu_is_allow_uid(uid);
}
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 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;
+ 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;
- }
- }
- }
+ 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;
+ // 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;
+ 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;
+ return true;
}
-void do_save_allow_list(struct work_struct *work)
+static void do_persistent_allow_list(struct callback_head *_cb)
{
- u32 magic = FILE_MAGIC;
- u32 version = FILE_FORMAT_VERSION;
- struct perm_data *p = NULL;
- struct list_head *pos = NULL;
- loff_t off = 0;
+ 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;
- }
+ mutex_lock(&allowlist_mutex);
+ struct file *fp =
+ filp_open(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));
+ goto unlock;
+ }
- // 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;
- }
+ // store magic and version
+ if (kernel_write(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
+ pr_err("save_allow_list write magic failed.\n");
+ goto close_file;
+ }
- if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) !=
- sizeof(version)) {
- pr_err("save_allow_list write version failed.\n");
- goto exit;
- }
+ if (kernel_write(fp, &version, sizeof(version), &off) != sizeof(version)) {
+ pr_err("save_allow_list write version failed.\n");
+ goto close_file;
+ }
- 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);
+ 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);
- }
+ kernel_write(fp, &p->profile, sizeof(p->profile), &off);
+ }
-exit:
- filp_close(fp, 0);
+close_file:
+ filp_close(fp, 0);
+unlock:
+ mutex_unlock(&allowlist_mutex);
+ kfree(_cb);
}
-void do_load_allow_list(struct work_struct *work)
+void persistent_allow_list()
{
- loff_t off = 0;
- ssize_t ret = 0;
- struct file *fp = NULL;
- u32 magic;
- u32 version;
+ struct task_struct *tsk;
+
+ tsk = get_pid_task(find_vpid(1), PIDTYPE_PID);
+ if (!tsk) {
+ pr_err("save_allow_list find init task err\n");
+ return;
+ }
+
+ struct callback_head *cb =
+ kzalloc(sizeof(struct callback_head), GFP_KERNEL);
+ if (!cb) {
+ pr_err("save_allow_list alloc cb err\b");
+ goto put_task;
+ }
+ cb->func = do_persistent_allow_list;
+ task_work_add(tsk, cb, TWA_RESUME);
+
+put_task:
+ put_task_struct(tsk);
+}
+
+void ksu_load_allow_list()
+{
+ 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();
+ // 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;
- }
+ // load allowlist now!
+ fp = filp_open(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;
- }
+ // verify magic
+ if (kernel_read(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;
- }
+ if (kernel_read(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);
+ pr_info("allowlist version: %d\n", version);
- while (true) {
- struct app_profile profile;
+ while (true) {
+ struct app_profile profile;
- ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile),
- &off);
+ ret = kernel_read(fp, &profile, sizeof(profile), &off);
- if (ret <= 0) {
- pr_info("load_allow_list read err: %zd\n", ret);
- break;
- }
+ 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);
- }
+ 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);
+ ksu_show_allow_list();
+ filp_close(fp, 0);
}
-void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), void *data)
+void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *),
+ void *data)
{
- struct perm_data *np = NULL;
- struct perm_data *n = NULL;
+ 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 (!ksu_boot_completed) {
+ pr_info("boot not completed, skip prune\n");
+ return;
+ }
- if (modified) {
- persistent_allow_list();
- }
-}
+ 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);
-// make sure allow list works cross boot
-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);
+ if (modified) {
+ persistent_allow_list();
+ }
}
void ksu_allowlist_init(void)
{
- int i;
+ int i;
- BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
- BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
+ 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;
+ for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
+ allow_list_arr[i] = -1;
- INIT_LIST_HEAD(&allow_list);
+ 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();
+ init_default_profiles();
}
void ksu_allowlist_exit(void)
{
- struct perm_data *np = NULL;
- struct perm_data *n = NULL;
+ 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);
+ // 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);
}
+
+#ifdef CONFIG_KSU_MANUAL_SU
+bool ksu_temp_grant_root_once(uid_t uid)
+{
+ struct app_profile profile = {
+ .version = KSU_APP_PROFILE_VER,
+ .allow_su = true,
+ .current_uid = uid,
+ };
+
+ const char *default_key = "com.temp.once";
+
+ 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);
+ if (p->profile.current_uid == uid) {
+ strcpy(profile.key, p->profile.key);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ strcpy(profile.key, default_key);
+ }
+
+ profile.rp_config.profile.uid = default_root_profile.uid;
+ profile.rp_config.profile.gid = default_root_profile.gid;
+ profile.rp_config.profile.groups_count = default_root_profile.groups_count;
+ memcpy(profile.rp_config.profile.groups, default_root_profile.groups, sizeof(default_root_profile.groups));
+ memcpy(&profile.rp_config.profile.capabilities, &default_root_profile.capabilities, sizeof(default_root_profile.capabilities));
+ profile.rp_config.profile.namespaces = default_root_profile.namespaces;
+ strcpy(profile.rp_config.profile.selinux_domain, default_root_profile.selinux_domain);
+
+ bool ok = ksu_set_app_profile(&profile, false);
+ if (ok)
+ pr_info("pending_root: UID=%d granted and persisted\n", uid);
+ return ok;
+}
+
+void ksu_temp_revoke_root_once(uid_t uid)
+{
+ struct app_profile profile = {
+ .version = KSU_APP_PROFILE_VER,
+ .allow_su = false,
+ .current_uid = uid,
+ };
+
+ const char *default_key = "com.temp.once";
+
+ 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);
+ if (p->profile.current_uid == uid) {
+ strcpy(profile.key, p->profile.key);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ strcpy(profile.key, default_key);
+ }
+
+ profile.nrp_config.profile.umount_modules = default_non_root_profile.umount_modules;
+ strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
+
+ ksu_set_app_profile(&profile, false);
+ persistent_allow_list();
+ pr_info("pending_root: UID=%d removed and persist updated\n", uid);
+}
+#endif
\ No newline at end of file
diff --git a/kernel/allowlist.h b/kernel/allowlist.h
index e89bf71..4bac8c3 100644
--- a/kernel/allowlist.h
+++ b/kernel/allowlist.h
@@ -2,19 +2,29 @@
#define __KSU_H_ALLOWLIST
#include
-#include "ksu.h"
+#include
+#include "app_profile.h"
+
+#define PER_USER_RANGE 100000
+#define FIRST_APPLICATION_UID 10000
+#define LAST_APPLICATION_UID 19999
void ksu_allowlist_init(void);
void ksu_allowlist_exit(void);
-bool ksu_load_allow_list(void);
+void ksu_load_allow_list(void);
void ksu_show_allow_list(void);
+// Check if the uid is in allow list
bool __ksu_is_allow_uid(uid_t uid);
#define ksu_is_allow_uid(uid) unlikely(__ksu_is_allow_uid(uid))
+// Check if the uid is in allow list, or current is ksu domain root
+bool __ksu_is_allow_uid_for_current(uid_t uid);
+#define ksu_is_allow_uid_for_current(uid) unlikely(__ksu_is_allow_uid_for_current(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);
@@ -24,4 +34,16 @@ 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);
+
+static inline bool is_appuid(uid_t uid)
+{
+ uid_t appid = uid % PER_USER_RANGE;
+ return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
+}
+
+#ifdef CONFIG_KSU_MANUAL_SU
+bool ksu_temp_grant_root_once(uid_t uid);
+void ksu_temp_revoke_root_once(uid_t uid);
+#endif
+
#endif
diff --git a/kernel/apk_sign.c b/kernel/apk_sign.c
index 3c22ca0..1d49985 100644
--- a/kernel/apk_sign.c
+++ b/kernel/apk_sign.c
@@ -17,69 +17,65 @@
#include "apk_sign.h"
#include "dynamic_manager.h"
#include "klog.h" // IWYU pragma: keep
-#include "kernel_compat.h"
#include "manager_sign.h"
struct sdesc {
- struct shash_desc shash;
- char ctx[];
+ struct shash_desc shash;
+ char ctx[];
};
-static struct apk_sign_key {
- unsigned size;
- const char *sha256;
-} apk_sign_keys[] = {
- {EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // ShirkNeko/SukiSU
+static apk_sign_key_t apk_sign_keys[] = {
+ {EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // ShirkNeko/SukiSU
#ifdef EXPECTED_SIZE
- {EXPECTED_SIZE, EXPECTED_HASH}, // Custom
+ {EXPECTED_SIZE, EXPECTED_HASH}, // Custom
#endif
};
static struct sdesc *init_sdesc(struct crypto_shash *alg)
{
- struct sdesc *sdesc;
- int size;
+ 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;
+ 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)
+ unsigned int datalen, unsigned char *digest)
{
- struct sdesc *sdesc;
- int ret;
+ struct sdesc *sdesc;
+ int ret;
- sdesc = init_sdesc(alg);
- if (IS_ERR(sdesc)) {
- pr_info("can't alloc sdesc\n");
- return PTR_ERR(sdesc);
- }
+ 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;
+ 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)
+ unsigned char *digest)
{
- struct crypto_shash *alg;
- char *hash_alg_name = "sha256";
- int ret;
+ 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;
+ 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;
}
@@ -87,304 +83,307 @@ static struct dynamic_sign_key dynamic_sign = DYNAMIC_SIGN_DEFAULT_CONFIG;
static bool check_dynamic_sign(struct file *fp, u32 size4, loff_t *pos, int *matched_index)
{
- struct dynamic_sign_key current_dynamic_key = dynamic_sign;
-
- if (ksu_get_dynamic_manager_config(¤t_dynamic_key.size, ¤t_dynamic_key.hash)) {
- pr_debug("Using dynamic manager config: size=0x%x, hash=%.16s...\n",
- current_dynamic_key.size, current_dynamic_key.hash);
- }
-
- if (size4 != current_dynamic_key.size) {
- return false;
- }
+ struct dynamic_sign_key current_dynamic_key = dynamic_sign;
+
+ if (ksu_get_dynamic_manager_config(¤t_dynamic_key.size, ¤t_dynamic_key.hash)) {
+ pr_debug("Using dynamic manager config: size=0x%x, hash=%.16s...\n",
+ current_dynamic_key.size, current_dynamic_key.hash);
+ }
+
+ if (size4 != current_dynamic_key.size) {
+ return false;
+ }
#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 (ksu_sha256(cert, size4, digest) < 0) {
- pr_info("sha256 error\n");
- return false;
- }
+ char cert[CERT_MAX_LENGTH];
+ if (size4 > CERT_MAX_LENGTH) {
+ pr_info("cert length overlimit\n");
+ return false;
+ }
+
+ kernel_read(fp, cert, size4, pos);
+
+ unsigned char digest[SHA256_DIGEST_SIZE];
+ if (ksu_sha256(cert, size4, digest) < 0) {
+ 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, index: dynamic\n", hash_str, current_dynamic_key.hash);
-
- if (strcmp(current_dynamic_key.hash, hash_str) == 0) {
- if (matched_index) {
- *matched_index = DYNAMIC_SIGN_INDEX;
- }
- return true;
- }
-
- 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, index: dynamic\n", hash_str, current_dynamic_key.hash);
+
+ if (strcmp(current_dynamic_key.hash, hash_str) == 0) {
+ if (matched_index) {
+ *matched_index = DYNAMIC_SIGN_INDEX;
+ }
+ return true;
+ }
+
+ return false;
}
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, int *matched_index)
{
- int i;
- struct apk_sign_key sign_key;
- bool signature_valid = false;
+ int i;
+ apk_sign_key_t sign_key;
+ bool signature_valid = false;
- 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
+ kernel_read(fp, size4, 0x4, pos); // signer-sequence length
+ kernel_read(fp, size4, 0x4, pos); // signer length
+ kernel_read(fp, size4, 0x4, pos); // signed data length
- *offset += 0x4 * 3;
+ *offset += 0x4 * 3;
- ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length
+ kernel_read(fp, size4, 0x4, pos); // digests-sequence length
- *pos += *size4;
- *offset += 0x4 + *size4;
+ *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;
+ kernel_read(fp, size4, 0x4, pos); // certificates length
+ kernel_read(fp, size4, 0x4, pos); // certificate length
+ *offset += 0x4 * 2;
- if (ksu_is_dynamic_manager_enabled()) {
- loff_t temp_pos = *pos;
- if (check_dynamic_sign(fp, *size4, &temp_pos, matched_index)) {
- *pos = temp_pos;
- *offset += *size4;
- return true;
- }
- }
+ if (ksu_is_dynamic_manager_enabled()) {
+ loff_t temp_pos = *pos;
+ if (check_dynamic_sign(fp, *size4, &temp_pos, matched_index)) {
+ *pos = temp_pos;
+ *offset += *size4;
+ return true;
+ }
+ }
- for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
- sign_key = apk_sign_keys[i];
+ for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
+ sign_key = apk_sign_keys[i];
- if (*size4 != sign_key.size)
- continue;
- *offset += *size4;
+ if (*size4 != sign_key.size)
+ continue;
+ *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 cert[CERT_MAX_LENGTH];
+ if (*size4 > CERT_MAX_LENGTH) {
+ pr_info("cert length overlimit\n");
+ return false;
+ }
+ kernel_read(fp, cert, *size4, pos);
+ unsigned char digest[SHA256_DIGEST_SIZE];
+ if (ksu_sha256(cert, *size4, digest) < 0 ) {
+ pr_info("sha256 error\n");
+ return false;
+ }
- char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
- hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
+ 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, index: %d\n", hash_str, sign_key.sha256, i);
-
- if (strcmp(sign_key.sha256, hash_str) == 0) {
- signature_valid = true;
- if (matched_index) {
- *matched_index = i;
- }
- break;
- }
- }
- return signature_valid;
+ bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
+ pr_info("sha256: %s, expected: %s, index: %d\n", hash_str, sign_key.sha256, i);
+
+ if (strcmp(sign_key.sha256, hash_str) == 0) {
+ signature_valid = true;
+ if (matched_index) {
+ *matched_index = i;
+ }
+ break;
+ }
+ }
+ return signature_valid;
}
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;
+ 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";
+ struct zip_entry_header header;
+ const char MANIFEST[] = "META-INF/MANIFEST.MF";
- loff_t pos = 0;
+ 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';
+ while (kernel_read(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)];
+ kernel_read(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;
- }
+ // 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;
- }
+ // Skip to the next entry
+ pos += header.extra_field_length + header.compressed_size;
+ }
- return false;
+ return false;
}
static __always_inline bool check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
{
- 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 matched_index = -1;
- 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;
- }
+ unsigned char buffer[0x11] = { 0 };
+ u32 size4;
+ u64 size8, size_of_block;
- // If you want to check for multi-manager APK signing, but dynamic managering is not enabled, skip
- if (check_multi_manager && !ksu_is_dynamic_manager_enabled()) {
- filp_close(fp, 0);
- return 0;
- }
+ loff_t pos;
- // disable inotify for this file
- fp->f_mode |= FMODE_NONOTIFY;
+ bool v2_signing_valid = false;
+ int v2_signing_blocks = 0;
+ bool v3_signing_exist = false;
+ bool v3_1_signing_exist = false;
+ int matched_index = -1;
+ int i;
+ struct file *fp = filp_open(path, O_RDONLY, 0);
+ if (IS_ERR(fp)) {
+ pr_err("open %s error.\n", path);
+ return false;
+ }
- // 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;
- }
- }
+ // If you want to check for multi-manager APK signing, but dynamic managering is not enabled, skip
+ if (check_multi_manager && !ksu_is_dynamic_manager_enabled()) {
+ filp_close(fp, 0);
+ return 0;
+ }
- pos += 12;
- // offset
- ksu_kernel_read_compat(fp, &size4, 0x4, &pos);
- pos = size4 - 0x18;
+ // disable inotify for this file
+ fp->f_mode |= FMODE_NONOTIFY;
- 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;
- }
+ // 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);
+ kernel_read(fp, &n, 2, &pos);
+ if (n == i) {
+ pos -= 22;
+ kernel_read(fp, &size4, 4, &pos);
+ if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
+ break;
+ }
+ }
+ if (i == 0xffff) {
+ pr_info("error: cannot find eocd\n");
+ goto clean;
+ }
+ }
- pos = size4 - (size8 + 0x8);
- ksu_kernel_read_compat(fp, &size_of_block, 0x8, &pos);
- if (size_of_block != size8) {
- goto clean;
- }
+ pos += 12;
+ // offset
+ kernel_read(fp, &size4, 0x4, &pos);
+ pos = size4 - 0x18;
- 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++;
- bool result = check_block(fp, &size4, &pos, &offset, &matched_index);
- if (result) {
- v2_signing_valid = true;
- }
- } 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 {
+ kernel_read(fp, &size8, 0x8, &pos);
+ kernel_read(fp, buffer, 0x10, &pos);
+ if (strcmp((char *)buffer, "APK Sig Block 42")) {
+ goto clean;
+ }
+
+ pos = size4 - (size8 + 0x8);
+ kernel_read(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;
+ kernel_read(fp, &size8, 0x8,
+ &pos); // sequence length
+ if (size8 == size_of_block) {
+ break;
+ }
+ kernel_read(fp, &id, 0x4, &pos); // id
+ offset = 4;
+ if (id == 0x7109871au) {
+ v2_signing_blocks++;
+ bool result = check_block(fp, &size4, &pos, &offset, &matched_index);
+ if (result) {
+ v2_signing_valid = true;
+ }
+ } 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);
+ pr_info("Unknown id: 0x%08x\n", id);
#endif
- }
- pos += (size8 - offset);
- }
+ }
+ pos += (size8 - offset);
+ }
- if (v2_signing_blocks != 1) {
+ if (v2_signing_blocks != 1) {
#ifdef CONFIG_KSU_DEBUG
- pr_err("Unexpected v2 signature count: %d\n",
- v2_signing_blocks);
+ pr_err("Unexpected v2 signature count: %d\n",
+ v2_signing_blocks);
#endif
- v2_signing_valid = false;
- }
+ 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;
- }
- }
+ 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);
+ filp_close(fp, 0);
- if (v3_signing_exist || v3_1_signing_exist) {
+ if (v3_signing_exist || v3_1_signing_exist) {
#ifdef CONFIG_KSU_DEBUG
- pr_err("Unexpected v3 signature scheme found!\n");
+ pr_err("Unexpected v3 signature scheme found!\n");
#endif
- return false;
- }
+ return false;
+ }
- if (v2_signing_valid) {
- if (signature_index) {
- *signature_index = matched_index;
- }
-
- if (check_multi_manager) {
- // 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX : Dynamic Sign
- if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) {
- pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index);
- return true;
- }
- return false;
- } else {
- // Common manager check: any valid signature will do
- return true;
- }
- }
- return false;
+ if (v2_signing_valid) {
+ if (signature_index) {
+ *signature_index = matched_index;
+ }
+
+ if (check_multi_manager) {
+ // 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX : Dynamic Sign
+ if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) {
+ pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index);
+ return true;
+ }
+ return false;
+ } else {
+ // Common manager check: any valid signature will do
+ return true;
+ }
+ }
+ return false;
}
#ifdef CONFIG_KSU_DEBUG
@@ -395,19 +394,19 @@ int ksu_debug_manager_uid = -1;
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;
+ 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,
+ .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);
+ &ksu_debug_manager_uid, S_IRUSR | S_IWUSR);
#endif
diff --git a/kernel/app_profile.c b/kernel/app_profile.c
new file mode 100644
index 0000000..9567e3d
--- /dev/null
+++ b/kernel/app_profile.c
@@ -0,0 +1,302 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "objsec.h"
+
+#include "allowlist.h"
+#include "app_profile.h"
+#include "klog.h" // IWYU pragma: keep
+#include "selinux/selinux.h"
+#include "syscall_hook_manager.h"
+#include "sucompat.h"
+#include "sulog.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION (6, 7, 0)
+ static struct group_info root_groups = { .usage = REFCOUNT_INIT(2), };
+#else
+ static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
+#endif
+
+static void setup_groups(struct root_profile *profile, struct cred *cred)
+{
+ if (profile->groups_count > KSU_MAX_GROUPS) {
+ pr_warn("Failed to setgroups, too large group: %d!\n",
+ profile->uid);
+ return;
+ }
+
+ if (profile->groups_count == 1 && profile->groups[0] == 0) {
+ // setgroup to root and return early.
+ if (cred->group_info)
+ put_group_info(cred->group_info);
+ cred->group_info = get_group_info(&root_groups);
+ return;
+ }
+
+ u32 ngroups = profile->groups_count;
+ struct group_info *group_info = groups_alloc(ngroups);
+ if (!group_info) {
+ pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid);
+ return;
+ }
+
+ int i;
+ for (i = 0; i < ngroups; i++) {
+ gid_t gid = profile->groups[i];
+ kgid_t kgid = make_kgid(current_user_ns(), gid);
+ if (!gid_valid(kgid)) {
+ pr_warn("Failed to setgroups, invalid gid: %d\n", gid);
+ put_group_info(group_info);
+ return;
+ }
+ group_info->gid[i] = kgid;
+ }
+
+ groups_sort(group_info);
+ set_groups(cred, group_info);
+ put_group_info(group_info);
+}
+
+void disable_seccomp(void)
+{
+ assert_spin_locked(¤t->sighand->siglock);
+ // disable seccomp
+#if defined(CONFIG_GENERIC_ENTRY) && \
+ LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
+ clear_syscall_work(SECCOMP);
+#else
+ clear_thread_flag(TIF_SECCOMP);
+#endif
+
+#ifdef CONFIG_SECCOMP
+ current->seccomp.mode = 0;
+ current->seccomp.filter = NULL;
+ atomic_set(¤t->seccomp.filter_count, 0);
+#else
+#endif
+}
+
+void escape_with_root_profile(void)
+{
+ struct cred *cred;
+ struct task_struct *p = current;
+ struct task_struct *t;
+
+ cred = prepare_creds();
+ if (!cred) {
+ pr_warn("prepare_creds failed!\n");
+ return;
+ }
+
+ if (cred->euid.val == 0) {
+ pr_warn("Already root, don't escape!\n");
+#if __SULOG_GATE
+ ksu_sulog_report_su_grant(current_euid().val, NULL, "escape_to_root_failed");
+#endif
+ abort_creds(cred);
+ return;
+ }
+
+ struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
+
+ cred->uid.val = profile->uid;
+ cred->suid.val = profile->uid;
+ cred->euid.val = profile->uid;
+ cred->fsuid.val = profile->uid;
+
+ cred->gid.val = profile->gid;
+ cred->fsgid.val = profile->gid;
+ cred->sgid.val = profile->gid;
+ cred->egid.val = profile->gid;
+ cred->securebits = 0;
+
+ BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
+ sizeof(kernel_cap_t));
+
+ // setup capabilities
+ // we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process
+ // we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec!
+ u64 cap_for_ksud =
+ profile->capabilities.effective | CAP_DAC_READ_SEARCH;
+ memcpy(&cred->cap_effective, &cap_for_ksud,
+ sizeof(cred->cap_effective));
+ memcpy(&cred->cap_permitted, &profile->capabilities.effective,
+ sizeof(cred->cap_permitted));
+ memcpy(&cred->cap_bset, &profile->capabilities.effective,
+ sizeof(cred->cap_bset));
+
+ setup_groups(profile, cred);
+
+ commit_creds(cred);
+
+ // Refer to kernel/seccomp.c: seccomp_set_mode_strict
+ // When disabling Seccomp, ensure that current->sighand->siglock is held during the operation.
+ spin_lock_irq(¤t->sighand->siglock);
+ disable_seccomp();
+ spin_unlock_irq(¤t->sighand->siglock);
+
+ setup_selinux(profile->selinux_domain);
+#if __SULOG_GATE
+ ksu_sulog_report_su_grant(current_euid().val, NULL, "escape_to_root");
+#endif
+
+ for_each_thread (p, t) {
+ ksu_set_task_tracepoint_flag(t);
+ }
+}
+
+#ifdef CONFIG_KSU_MANUAL_SU
+
+#include "ksud.h"
+
+#ifndef DEVPTS_SUPER_MAGIC
+#define DEVPTS_SUPER_MAGIC 0x1cd1
+#endif
+
+static int __manual_su_handle_devpts(struct inode *inode)
+{
+ if (!current->mm) {
+ return 0;
+ }
+
+ uid_t uid = current_uid().val;
+ if (uid % 100000 < 10000) {
+ // not untrusted_app, ignore it
+ return 0;
+ }
+
+ if (likely(!ksu_is_allow_uid_for_current(uid)))
+ return 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) || defined(KSU_OPTIONAL_SELINUX_INODE)
+ struct inode_security_struct *sec = selinux_inode(inode);
+#else
+ struct inode_security_struct *sec =
+ (struct inode_security_struct *)inode->i_security;
+#endif
+ if (ksu_file_sid && sec)
+ sec->sid = ksu_file_sid;
+
+ return 0;
+}
+
+static void disable_seccomp_for_task(struct task_struct *tsk)
+{
+ assert_spin_locked(&tsk->sighand->siglock);
+#ifdef CONFIG_SECCOMP
+ if (tsk->seccomp.mode == SECCOMP_MODE_DISABLED && !tsk->seccomp.filter)
+ return;
+#endif
+ clear_tsk_thread_flag(tsk, TIF_SECCOMP);
+#ifdef CONFIG_SECCOMP
+ tsk->seccomp.mode = SECCOMP_MODE_DISABLED;
+ if (tsk->seccomp.filter) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
+ seccomp_filter_release(tsk);
+#else
+ put_seccomp_filter(tsk);
+ tsk->seccomp.filter = NULL;
+#endif
+ }
+#endif
+}
+
+void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid)
+{
+ struct cred *newcreds;
+ struct task_struct *target_task;
+ unsigned long flags;
+ struct task_struct *p = current;
+ struct task_struct *t;
+
+ pr_info("cmd_su: escape_to_root_for_cmd_su called for UID: %d, PID: %d\n", target_uid, target_pid);
+
+ // Find target task by PID
+ rcu_read_lock();
+ target_task = pid_task(find_vpid(target_pid), PIDTYPE_PID);
+ if (!target_task) {
+ rcu_read_unlock();
+ pr_err("cmd_su: target task not found for PID: %d\n", target_pid);
+#if __SULOG_GATE
+ ksu_sulog_report_su_grant(target_uid, "cmd_su", "target_not_found");
+#endif
+ return;
+ }
+ get_task_struct(target_task);
+ rcu_read_unlock();
+
+ if (task_uid(target_task).val == 0) {
+ pr_warn("cmd_su: target task is already root, PID: %d\n", target_pid);
+ put_task_struct(target_task);
+ return;
+ }
+
+ newcreds = prepare_kernel_cred(target_task);
+ if (newcreds == NULL) {
+ pr_err("cmd_su: failed to allocate new cred for PID: %d\n", target_pid);
+#if __SULOG_GATE
+ ksu_sulog_report_su_grant(target_uid, "cmd_su", "cred_alloc_failed");
+#endif
+ put_task_struct(target_task);
+ return;
+ }
+
+ struct root_profile *profile = ksu_get_root_profile(target_uid);
+
+ newcreds->uid.val = profile->uid;
+ newcreds->suid.val = profile->uid;
+ newcreds->euid.val = profile->uid;
+ newcreds->fsuid.val = profile->uid;
+
+ newcreds->gid.val = profile->gid;
+ newcreds->fsgid.val = profile->gid;
+ newcreds->sgid.val = profile->gid;
+ newcreds->egid.val = profile->gid;
+ newcreds->securebits = 0;
+
+ u64 cap_for_cmd_su = profile->capabilities.effective | CAP_DAC_READ_SEARCH | CAP_SETUID | CAP_SETGID;
+ memcpy(&newcreds->cap_effective, &cap_for_cmd_su, sizeof(newcreds->cap_effective));
+ memcpy(&newcreds->cap_permitted, &profile->capabilities.effective, sizeof(newcreds->cap_permitted));
+ memcpy(&newcreds->cap_bset, &profile->capabilities.effective, sizeof(newcreds->cap_bset));
+
+ setup_groups(profile, newcreds);
+ task_lock(target_task);
+
+ const struct cred *old_creds = get_task_cred(target_task);
+
+ rcu_assign_pointer(target_task->real_cred, newcreds);
+ rcu_assign_pointer(target_task->cred, get_cred(newcreds));
+ task_unlock(target_task);
+
+ if (target_task->sighand) {
+ spin_lock_irqsave(&target_task->sighand->siglock, flags);
+ disable_seccomp_for_task(target_task);
+ spin_unlock_irqrestore(&target_task->sighand->siglock, flags);
+ }
+
+ setup_selinux(profile->selinux_domain);
+ put_cred(old_creds);
+ wake_up_process(target_task);
+
+ if (target_task->signal->tty) {
+ struct inode *inode = target_task->signal->tty->driver_data;
+ if (inode && inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) {
+ __manual_su_handle_devpts(inode);
+ }
+ }
+
+ put_task_struct(target_task);
+#if __SULOG_GATE
+ ksu_sulog_report_su_grant(target_uid, "cmd_su", "manual_escalation");
+#endif
+ for_each_thread (p, t) {
+ ksu_set_task_tracepoint_flag(t);
+ }
+ pr_info("cmd_su: privilege escalation completed for UID: %d, PID: %d\n", target_uid, target_pid);
+}
+#endif
diff --git a/kernel/app_profile.h b/kernel/app_profile.h
new file mode 100644
index 0000000..871abb6
--- /dev/null
+++ b/kernel/app_profile.h
@@ -0,0 +1,70 @@
+#ifndef __KSU_H_APP_PROFILE
+#define __KSU_H_APP_PROFILE
+
+#include
+
+// Forward declarations
+struct cred;
+
+#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;
+ };
+};
+
+// Escalate current process to root with the appropriate profile
+void escape_with_root_profile(void);
+
+void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid);
+
+void disable_seccomp(void);
+
+#endif
diff --git a/kernel/arch.h b/kernel/arch.h
index eec38c2..ee2b16c 100644
--- a/kernel/arch.h
+++ b/kernel/arch.h
@@ -18,10 +18,8 @@
#define __PT_SP_REG sp
#define __PT_IP_REG pc
-#define PRCTL_SYMBOL "__arm64_sys_prctl"
+#define REBOOT_SYMBOL "__arm64_sys_reboot"
#define SYS_READ_SYMBOL "__arm64_sys_read"
-#define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat"
-#define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat"
#define SYS_EXECVE_SYMBOL "__arm64_sys_execve"
#elif defined(__x86_64__)
@@ -39,10 +37,8 @@
#define __PT_RC_REG ax
#define __PT_SP_REG sp
#define __PT_IP_REG ip
-#define PRCTL_SYMBOL "__x64_sys_prctl"
+#define REBOOT_SYMBOL "__x64_sys_reboot"
#define SYS_READ_SYMBOL "__x64_sys_read"
-#define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat"
-#define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat"
#define SYS_EXECVE_SYMBOL "__x64_sys_execve"
#else
diff --git a/kernel/core_hook.c b/kernel/core_hook.c
deleted file mode 100644
index 63c066f..0000000
--- a/kernel/core_hook.c
+++ /dev/null
@@ -1,1092 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-
-#ifdef MODULE
-#include
-#include
-#include
-#include
-#include
-#endif
-
-#include "allowlist.h"
-#include "arch.h"
-#include "core_hook.h"
-#include "klog.h" // IWYU pragma: keep
-#include "ksu.h"
-#include "ksud.h"
-#include "manager.h"
-#include "selinux/selinux.h"
-#include "throne_tracker.h"
-#include "throne_comm.h"
-#include "kernel_compat.h"
-
-#include "kpm/kpm.h"
-#include "dynamic_manager.h"
-
-static bool ksu_module_mounted = false;
-
-extern int handle_sepolicy(unsigned long arg3, void __user *arg4);
-
-static bool ksu_su_compat_enabled = true;
-extern void ksu_sucompat_init();
-extern void ksu_sucompat_exit();
-
-static inline bool is_allow_su()
-{
- if (is_manager()) {
- // we are manager, allow!
- return true;
- }
- return ksu_is_allow_uid(current_uid().val);
-}
-
-static inline bool is_unsupported_app_uid(uid_t uid)
-{
-#define LAST_APPLICATION_UID 19999
- uid_t appid = uid % 100000;
- return appid > LAST_APPLICATION_UID;
-}
-
-static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
-
-static void setup_groups(struct root_profile *profile, struct cred *cred)
-{
- if (profile->groups_count > KSU_MAX_GROUPS) {
- pr_warn("Failed to setgroups, too large group: %d!\n",
- profile->uid);
- return;
- }
-
- if (profile->groups_count == 1 && profile->groups[0] == 0) {
- // setgroup to root and return early.
- if (cred->group_info)
- put_group_info(cred->group_info);
- cred->group_info = get_group_info(&root_groups);
- return;
- }
-
- u32 ngroups = profile->groups_count;
- struct group_info *group_info = groups_alloc(ngroups);
- if (!group_info) {
- pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid);
- return;
- }
-
- int i;
- for (i = 0; i < ngroups; i++) {
- gid_t gid = profile->groups[i];
- kgid_t kgid = make_kgid(current_user_ns(), gid);
- if (!gid_valid(kgid)) {
- pr_warn("Failed to setgroups, invalid gid: %d\n", gid);
- put_group_info(group_info);
- return;
- }
- group_info->gid[i] = kgid;
- }
-
- groups_sort(group_info);
- set_groups(cred, group_info);
- put_group_info(group_info);
-}
-
-static void disable_seccomp()
-{
- assert_spin_locked(¤t->sighand->siglock);
- // disable seccomp
-#if defined(CONFIG_GENERIC_ENTRY) && \
- LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
- current_thread_info()->syscall_work &= ~SYSCALL_WORK_SECCOMP;
-#else
- current_thread_info()->flags &= ~(TIF_SECCOMP | _TIF_SECCOMP);
-#endif
-
-#ifdef CONFIG_SECCOMP
- current->seccomp.mode = 0;
- current->seccomp.filter = NULL;
-#else
-#endif
-}
-
-void escape_to_root(void)
-{
- struct cred *cred;
-
- cred = prepare_creds();
- if (!cred) {
- pr_warn("prepare_creds failed!\n");
- return;
- }
-
- if (cred->euid.val == 0) {
- pr_warn("Already root, don't escape!\n");
- abort_creds(cred);
- return;
- }
-
- struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
-
- cred->uid.val = profile->uid;
- cred->suid.val = profile->uid;
- cred->euid.val = profile->uid;
- cred->fsuid.val = profile->uid;
-
- cred->gid.val = profile->gid;
- cred->fsgid.val = profile->gid;
- cred->sgid.val = profile->gid;
- cred->egid.val = profile->gid;
- cred->securebits = 0;
-
- BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
- sizeof(kernel_cap_t));
-
- // setup capabilities
- // we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process
- // we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec!
- u64 cap_for_ksud =
- profile->capabilities.effective | CAP_DAC_READ_SEARCH;
- memcpy(&cred->cap_effective, &cap_for_ksud,
- sizeof(cred->cap_effective));
- memcpy(&cred->cap_permitted, &profile->capabilities.effective,
- sizeof(cred->cap_permitted));
- memcpy(&cred->cap_bset, &profile->capabilities.effective,
- sizeof(cred->cap_bset));
-
- setup_groups(profile, cred);
-
- commit_creds(cred);
-
- // Refer to kernel/seccomp.c: seccomp_set_mode_strict
- // When disabling Seccomp, ensure that current->sighand->siglock is held during the operation.
- spin_lock_irq(¤t->sighand->siglock);
- disable_seccomp();
- spin_unlock_irq(¤t->sighand->siglock);
-
- setup_selinux(profile->selinux_domain);
-}
-
-int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
-{
- if (!current->mm) {
- // skip kernel threads
- return 0;
- }
-
- if (current_uid().val != 1000) {
- // skip non system uid
- return 0;
- }
-
- if (!old_dentry || !new_dentry) {
- return 0;
- }
-
- // /data/system/packages.list.tmp -> /data/system/packages.list
- if (strcmp(new_dentry->d_iname, "packages.list")) {
- return 0;
- }
-
- char path[128];
- char *buf = dentry_path_raw(new_dentry, path, sizeof(path));
- if (IS_ERR(buf)) {
- pr_err("dentry_path_raw failed.\n");
- return 0;
- }
-
- if (!strstr(buf, "/system/packages.list")) {
- return 0;
- }
- pr_info("renameat: %s -> %s, new path: %s\n", old_dentry->d_iname,
- new_dentry->d_iname, buf);
-
- track_throne();
-
- // Also request userspace scan for next time
- ksu_request_userspace_scan();
-
- return 0;
-}
-
-#ifdef CONFIG_EXT4_FS
-static void nuke_ext4_sysfs() {
- struct path path;
- int err = kern_path("/data/adb/modules", 0, &path);
- if (err) {
- pr_err("nuke path err: %d\n", err);
- return;
- }
-
- struct super_block* sb = path.dentry->d_inode->i_sb;
- const char* name = sb->s_type->name;
- if (strcmp(name, "ext4") != 0) {
- pr_info("nuke but module aren't mounted\n");
- return;
- }
-
- ext4_unregister_sysfs(sb);
- path_put(&path);
-}
-#else
-static inline void nuke_ext4_sysfs() { }
-#endif
-
-static bool is_system_bin_su()
-{
- // YES in_execve becomes 0 when it succeeds.
- if (!current->mm || current->in_execve)
- return false;
-
- // quick af check
- return (current->mm->exe_file && !strcmp(current->mm->exe_file->f_path.dentry->d_name.name, "su"));
-}
-
-int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
- unsigned long arg4, unsigned long arg5)
-{
- // if success, we modify the arg5 as result!
- u32 *result = (u32 *)arg5;
- u32 reply_ok = KERNEL_SU_OPTION;
-
- if (KERNEL_SU_OPTION != option) {
- return 0;
- }
-
- // TODO: find it in throne tracker!
- uid_t current_uid_val = current_uid().val;
- uid_t manager_uid = ksu_get_manager_uid();
- if (current_uid_val != manager_uid &&
- current_uid_val % 100000 == manager_uid) {
- ksu_set_manager_uid(current_uid_val);
- }
-
- bool from_root = 0 == current_uid().val;
- bool from_manager = is_manager();
-
- if (!from_root && !from_manager
- && !(is_allow_su() && is_system_bin_su())) {
- // only root or manager can access this interface
- return 0;
- }
-
-#ifdef CONFIG_KSU_DEBUG
- pr_info("option: 0x%x, cmd: %ld\n", option, arg2);
-#endif
-
- if (arg2 == CMD_BECOME_MANAGER) {
- if (from_manager) {
- if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
- pr_err("become_manager: prctl reply error\n");
- }
- return 0;
- }
- return 0;
- }
-
- if (arg2 == CMD_GRANT_ROOT) {
- if (is_allow_su()) {
- pr_info("allow root for: %d\n", current_uid().val);
- escape_to_root();
- if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
- pr_err("grant_root: prctl reply error\n");
- }
- }
- return 0;
- }
-
- // Both root manager and root processes should be allowed to get version
- if (arg2 == CMD_GET_VERSION) {
- u32 version = KERNEL_SU_VERSION;
- if (copy_to_user(arg3, &version, sizeof(version))) {
- pr_err("prctl reply error, cmd: %lu\n", arg2);
- }
- u32 version_flags = 2;
-#ifdef MODULE
- version_flags |= 0x1;
-#endif
- if (arg4 &&
- copy_to_user(arg4, &version_flags, sizeof(version_flags))) {
- pr_err("prctl reply error, cmd: %lu\n", arg2);
- }
- return 0;
- }
-
- // Allow root manager to get full version strings
- if (arg2 == CMD_GET_FULL_VERSION) {
- char ksu_version_full[KSU_FULL_VERSION_STRING] = {0};
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
- strscpy(ksu_version_full, KSU_VERSION_FULL, KSU_FULL_VERSION_STRING);
-#else
- strlcpy(ksu_version_full, KSU_VERSION_FULL, KSU_FULL_VERSION_STRING);
-#endif
- if (copy_to_user((void __user *)arg3, ksu_version_full, KSU_FULL_VERSION_STRING)) {
- pr_err("prctl reply error, cmd: %lu\n", arg2);
- return -EFAULT;
- }
- return 0;
- }
-
- // Allow the root manager to configure dynamic manageratures
- if (arg2 == CMD_DYNAMIC_MANAGER) {
- if (!from_root && !from_manager) {
- return 0;
- }
-
- struct dynamic_manager_user_config config;
-
- if (copy_from_user(&config, (void __user *)arg3, sizeof(config))) {
- pr_err("copy dynamic manager config failed\n");
- return 0;
- }
-
- int ret = ksu_handle_dynamic_manager(&config);
-
- if (ret == 0 && config.operation == DYNAMIC_MANAGER_OP_GET) {
- if (copy_to_user((void __user *)arg3, &config, sizeof(config))) {
- pr_err("copy dynamic manager config back failed\n");
- return 0;
- }
- }
-
- if (ret == 0) {
- if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
- pr_err("dynamic_manager: prctl reply error\n");
- }
- }
- return 0;
- }
-
- // Allow root manager to get active managers
- if (arg2 == CMD_GET_MANAGERS) {
- if (!from_root && !from_manager) {
- return 0;
- }
-
- struct manager_list_info manager_info;
- int ret = ksu_get_active_managers(&manager_info);
-
- if (ret == 0) {
- if (copy_to_user((void __user *)arg3, &manager_info, sizeof(manager_info))) {
- pr_err("copy manager list failed\n");
- return 0;
- }
- if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
- pr_err("get_managers: prctl reply error\n");
- }
- }
- return 0;
- }
-
- if (arg2 == CMD_REPORT_EVENT) {
- if (!from_root) {
- return 0;
- }
- switch (arg3) {
- case EVENT_POST_FS_DATA: {
- static bool post_fs_data_lock = false;
- if (!post_fs_data_lock) {
- post_fs_data_lock = true;
- pr_info("post-fs-data triggered\n");
- on_post_fs_data();
- // Initialize throne communication
- ksu_throne_comm_init();
- // Initializing Dynamic Signatures
- ksu_dynamic_manager_init();
- pr_info("Dynamic sign config loaded during post-fs-data\n");
- }
- break;
- }
- case EVENT_BOOT_COMPLETED: {
- static bool boot_complete_lock = false;
- if (!boot_complete_lock) {
- boot_complete_lock = true;
- pr_info("boot_complete triggered\n");
- }
- break;
- }
- case EVENT_MODULE_MOUNTED: {
- ksu_module_mounted = true;
- pr_info("module mounted!\n");
- nuke_ext4_sysfs();
- break;
- }
- default:
- break;
- }
- return 0;
- }
-
- if (arg2 == CMD_SET_SEPOLICY) {
- if (!from_root) {
- return 0;
- }
- if (!handle_sepolicy(arg3, arg4)) {
- if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
- pr_err("sepolicy: prctl reply error\n");
- }
- }
-
- return 0;
- }
-
- if (arg2 == CMD_CHECK_SAFEMODE) {
- if (ksu_is_safe_mode()) {
- pr_warn("safemode enabled!\n");
- if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
- pr_err("safemode: prctl reply error\n");
- }
- }
- return 0;
- }
-
- if (arg2 == CMD_GET_ALLOW_LIST || arg2 == CMD_GET_DENY_LIST) {
- u32 array[128];
- u32 array_length;
- bool success = ksu_get_allow_list(array, &array_length,
- arg2 == CMD_GET_ALLOW_LIST);
- if (success) {
- if (!copy_to_user(arg4, &array_length,
- sizeof(array_length)) &&
- !copy_to_user(arg3, array,
- sizeof(u32) * array_length)) {
- if (copy_to_user(result, &reply_ok,
- sizeof(reply_ok))) {
- pr_err("prctl reply error, cmd: %lu\n",
- arg2);
- }
- } else {
- pr_err("prctl copy allowlist error\n");
- }
- }
- return 0;
- }
-
- if (arg2 == CMD_UID_GRANTED_ROOT || arg2 == CMD_UID_SHOULD_UMOUNT) {
- uid_t target_uid = (uid_t)arg3;
- bool allow = false;
- if (arg2 == CMD_UID_GRANTED_ROOT) {
- allow = ksu_is_allow_uid(target_uid);
- } else if (arg2 == CMD_UID_SHOULD_UMOUNT) {
- allow = ksu_uid_should_umount(target_uid);
- } else {
- pr_err("unknown cmd: %lu\n", arg2);
- }
- if (!copy_to_user(arg4, &allow, sizeof(allow))) {
- if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
- pr_err("prctl reply error, cmd: %lu\n", arg2);
- }
- } else {
- pr_err("prctl copy err, cmd: %lu\n", arg2);
- }
- return 0;
- }
-
- if (arg2 == CMD_ENABLE_SU) {
- bool enabled = (arg3 != 0);
- if (enabled == ksu_su_compat_enabled) {
- pr_info("cmd enable su but no need to change.\n");
- if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {// return the reply_ok directly
- pr_err("prctl reply error, cmd: %lu\n", arg2);
- }
- return 0;
- }
-
- if (enabled) {
- ksu_sucompat_init();
- } else {
- ksu_sucompat_exit();
- }
- ksu_su_compat_enabled = enabled;
-
- if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
- pr_err("prctl reply error, cmd: %lu\n", arg2);
- }
-
- return 0;
- }
-
- #ifdef CONFIG_KPM
- // ADD: 添加KPM模块控制
- if(sukisu_is_kpm_control_code(arg2)) {
- int res;
-
- pr_info("KPM: calling before arg2=%d\n", (int) arg2);
-
- res = sukisu_handle_kpm(arg2, arg3, arg4, arg5);
-
- return 0;
- }
- #endif
-
- if (arg2 == CMD_ENABLE_KPM) {
- bool KPM_Enabled = IS_ENABLED(CONFIG_KPM);
- if (copy_to_user((void __user *)arg3, &KPM_Enabled, sizeof(KPM_Enabled)))
- pr_info("KPM: copy_to_user() failed\n");
- return 0;
- }
-
- // Checking hook usage
- if (arg2 == CMD_HOOK_TYPE) {
- const char *hook_type = "Kprobes";
-#if defined(CONFIG_KSU_TRACEPOINT_HOOK)
- hook_type = "Tracepoint";
-#elif defined(CONFIG_KSU_MANUAL_HOOK)
- hook_type = "Manual";
-#endif
-
- size_t len = strlen(hook_type) + 1;
- if (copy_to_user((void __user *)arg3, hook_type, len)) {
- pr_err("hook_type: copy_to_user failed\n");
- return 0;
- }
-
- if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
- pr_err("hook_type: prctl reply error\n");
- }
- return 0;
- }
-
-
- // all other cmds are for 'root manager'
- if (!from_manager) {
- return 0;
- }
-
- // we are already manager
- if (arg2 == CMD_GET_APP_PROFILE) {
- struct app_profile profile;
- if (copy_from_user(&profile, arg3, sizeof(profile))) {
- pr_err("copy profile failed\n");
- return 0;
- }
-
- bool success = ksu_get_app_profile(&profile);
- if (success) {
- if (copy_to_user(arg3, &profile, sizeof(profile))) {
- pr_err("copy profile failed\n");
- return 0;
- }
- if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
- pr_err("prctl reply error, cmd: %lu\n", arg2);
- }
- }
- return 0;
- }
-
- if (arg2 == CMD_SET_APP_PROFILE) {
- struct app_profile profile;
- if (copy_from_user(&profile, arg3, sizeof(profile))) {
- pr_err("copy profile failed\n");
- return 0;
- }
-
- // todo: validate the params
- if (ksu_set_app_profile(&profile, true)) {
- if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
- pr_err("prctl reply error, cmd: %lu\n", arg2);
- }
- }
- return 0;
- }
-
- if (arg2 == CMD_IS_SU_ENABLED) {
- if (copy_to_user(arg3, &ksu_su_compat_enabled,
- sizeof(ksu_su_compat_enabled))) {
- pr_err("copy su compat failed\n");
- return 0;
- }
- if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
- pr_err("prctl reply error, cmd: %lu\n", arg2);
- }
- return 0;
- }
-
- return 0;
-}
-
-static bool is_non_appuid(kuid_t uid)
-{
-#define PER_USER_RANGE 100000
-#define FIRST_APPLICATION_UID 10000
-
- uid_t appid = uid.val % PER_USER_RANGE;
- return appid < FIRST_APPLICATION_UID;
-}
-
-static bool should_umount(struct path *path)
-{
- if (!path) {
- return false;
- }
-
- if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
- pr_info("ignore global mnt namespace process: %d\n",
- current_uid().val);
- return false;
- }
-
- if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) {
- const char *fstype = path->mnt->mnt_sb->s_type->name;
- return strcmp(fstype, "overlay") == 0;
- }
- return false;
-}
-
-static void ksu_umount_mnt(struct path *path, int flags)
-{
- int err = path_umount(path, flags);
- if (err) {
- pr_info("umount %s failed: %d\n", path->dentry->d_iname, err);
- }
-}
-
-static void try_umount(const char *mnt, bool check_mnt, int flags)
-{
- struct path path;
- int err = kern_path(mnt, 0, &path);
- if (err) {
- return;
- }
-
- if (path.dentry != path.mnt->mnt_root) {
- // it is not root mountpoint, maybe umounted by others already.
- path_put(&path);
- return;
- }
-
- // we are only interest in some specific mounts
- if (check_mnt && !should_umount(&path)) {
- path_put(&path);
- return;
- }
-
- ksu_umount_mnt(&path, flags);
-}
-
-int ksu_handle_setuid(struct cred *new, const struct cred *old)
-{
- // this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it!
- if (!ksu_module_mounted) {
- return 0;
- }
-
- if (!new || !old) {
- return 0;
- }
-
- kuid_t new_uid = new->uid;
- kuid_t old_uid = old->uid;
-
- if (0 != old_uid.val) {
- // old process is not root, ignore it.
- return 0;
- }
-
- if (is_non_appuid(new_uid)) {
-#ifdef CONFIG_KSU_DEBUG
- pr_info("handle setuid ignore non application uid: %d\n", new_uid.val);
-#endif
- return 0;
- }
-
- // isolated process may be directly forked from zygote, always unmount
- if (is_unsupported_app_uid(new_uid.val)) {
-#ifdef CONFIG_KSU_DEBUG
- pr_info("handle umount for unsupported application uid: %d\n", new_uid.val);
-#endif
- goto do_umount;
- }
-
- if (ksu_is_allow_uid(new_uid.val)) {
-#ifdef CONFIG_KSU_DEBUG
- pr_info("handle setuid ignore allowed application: %d\n", new_uid.val);
-#endif
- return 0;
- }
-
- if (!ksu_uid_should_umount(new_uid.val)) {
- return 0;
- } else {
-#ifdef CONFIG_KSU_DEBUG
- pr_info("uid: %d should not umount!\n", current_uid().val);
-#endif
- }
-
-do_umount:
- // check old process's selinux context, if it is not zygote, ignore it!
- // because some su apps may setuid to untrusted_app but they are in global mount namespace
- // when we umount for such process, that is a disaster!
- if (!is_zygote(old->security)) {
- pr_info("handle umount ignore non zygote child: %d\n",
- current->pid);
- return 0;
- }
-#ifdef CONFIG_KSU_DEBUG
- // umount the target mnt
- pr_info("handle umount for uid: %d, pid: %d\n", new_uid.val,
- current->pid);
-#endif
-
- // fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
- // filter the mountpoint whose target is `/data/adb`
- try_umount("/system", true, 0);
- try_umount("/vendor", true, 0);
- try_umount("/product", true, 0);
- try_umount("/system_ext", true, 0);
- try_umount("/data/adb/modules", false, MNT_DETACH);
-
- // try umount ksu temp path
- try_umount("/debug_ramdisk", false, MNT_DETACH);
-
- return 0;
-}
-
-// Init functons
-
-static int handler_pre(struct kprobe *p, struct pt_regs *regs)
-{
- struct pt_regs *real_regs = PT_REAL_REGS(regs);
- int option = (int)PT_REGS_PARM1(real_regs);
- unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs);
- unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs);
- // PRCTL_SYMBOL is the arch-specificed one, which receive raw pt_regs from syscall
- unsigned long arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs);
- unsigned long arg5 = (unsigned long)PT_REGS_PARM5(real_regs);
-
- return ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
-}
-
-static struct kprobe prctl_kp = {
- .symbol_name = PRCTL_SYMBOL,
- .pre_handler = handler_pre,
-};
-
-static int renameat_handler_pre(struct kprobe *p, struct pt_regs *regs)
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
- // https://elixir.bootlin.com/linux/v5.12-rc1/source/include/linux/fs.h
- struct renamedata *rd = PT_REGS_PARM1(regs);
- struct dentry *old_entry = rd->old_dentry;
- struct dentry *new_entry = rd->new_dentry;
-#else
- struct dentry *old_entry = (struct dentry *)PT_REGS_PARM2(regs);
- struct dentry *new_entry = (struct dentry *)PT_REGS_CCALL_PARM4(regs);
-#endif
-
- return ksu_handle_rename(old_entry, new_entry);
-}
-
-static struct kprobe renameat_kp = {
- .symbol_name = "vfs_rename",
- .pre_handler = renameat_handler_pre,
-};
-
-__maybe_unused int ksu_kprobe_init(void)
-{
- int rc = 0;
- rc = register_kprobe(&prctl_kp);
-
- if (rc) {
- pr_info("prctl kprobe failed: %d.\n", rc);
- return rc;
- }
-
- rc = register_kprobe(&renameat_kp);
- pr_info("renameat kp: %d\n", rc);
-
- return rc;
-}
-
-__maybe_unused int ksu_kprobe_exit(void)
-{
- unregister_kprobe(&prctl_kp);
- unregister_kprobe(&renameat_kp);
- return 0;
-}
-
-#ifndef DEVPTS_SUPER_MAGIC
-#define DEVPTS_SUPER_MAGIC 0x1cd1
-#endif
-
-extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c
-
-int ksu_inode_permission(struct inode *inode, int mask)
-{
- if (inode && inode->i_sb
- && unlikely(inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)) {
- //pr_info("%s: handling devpts for: %s \n", __func__, current->comm);
- __ksu_handle_devpts(inode);
- }
- return 0;
-}
-
-#ifdef CONFIG_COMPAT
-bool ksu_is_compat __read_mostly = false;
-#endif
-
-int ksu_bprm_check(struct linux_binprm *bprm)
-{
- char *filename = (char *)bprm->filename;
-
- if (likely(!ksu_execveat_hook))
- return 0;
-
-#ifdef CONFIG_COMPAT
- static bool compat_check_done __read_mostly = false;
- if ( unlikely(!compat_check_done) && unlikely(!strcmp(filename, "/data/adb/ksud"))
- && !memcmp(bprm->buf, "\x7f\x45\x4c\x46", 4) ) {
- if (bprm->buf[4] == 0x01 )
- ksu_is_compat = true;
-
- pr_info("%s: %s ELF magic found! ksu_is_compat: %d \n", __func__, filename, ksu_is_compat);
- compat_check_done = true;
- }
-#endif
-
- ksu_handle_pre_ksud(filename);
-
- return 0;
-
-}
-
-static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
- unsigned long arg4, unsigned long arg5)
-{
- ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
- return -ENOSYS;
-}
-
-static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
- struct inode *new_inode, struct dentry *new_dentry)
-{
- return ksu_handle_rename(old_dentry, new_dentry);
-}
-
-static int ksu_task_fix_setuid(struct cred *new, const struct cred *old,
- int flags)
-{
- return ksu_handle_setuid(new, old);
-}
-
-#ifndef MODULE
-static struct security_hook_list ksu_hooks[] = {
- LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
- LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
- LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid),
- LSM_HOOK_INIT(inode_permission, ksu_inode_permission),
-#ifndef CONFIG_KSU_KPROBES_HOOK
- LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check),
-#endif
-};
-
-void __init ksu_lsm_hook_init(void)
-{
- security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu");
-}
-
-#else
-static int override_security_head(void *head, const void *new_head, size_t len)
-{
- unsigned long base = (unsigned long)head & PAGE_MASK;
- unsigned long offset = offset_in_page(head);
-
- // this is impossible for our case because the page alignment
- // but be careful for other cases!
- BUG_ON(offset + len > PAGE_SIZE);
- struct page *page = phys_to_page(__pa(base));
- if (!page) {
- return -EFAULT;
- }
-
- void *addr = vmap(&page, 1, VM_MAP, PAGE_KERNEL);
- if (!addr) {
- return -ENOMEM;
- }
- local_irq_disable();
- memcpy(addr + offset, new_head, len);
- local_irq_enable();
- vunmap(addr);
- return 0;
-}
-
-static void free_security_hook_list(struct hlist_head *head)
-{
- struct hlist_node *temp;
- struct security_hook_list *entry;
-
- if (!head)
- return;
-
- hlist_for_each_entry_safe (entry, temp, head, list) {
- hlist_del(&entry->list);
- kfree(entry);
- }
-
- kfree(head);
-}
-
-struct hlist_head *copy_security_hlist(struct hlist_head *orig)
-{
- struct hlist_head *new_head = kmalloc(sizeof(*new_head), GFP_KERNEL);
- if (!new_head)
- return NULL;
-
- INIT_HLIST_HEAD(new_head);
-
- struct security_hook_list *entry;
- struct security_hook_list *new_entry;
-
- hlist_for_each_entry (entry, orig, list) {
- new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
- if (!new_entry) {
- free_security_hook_list(new_head);
- return NULL;
- }
-
- *new_entry = *entry;
-
- hlist_add_tail_rcu(&new_entry->list, new_head);
- }
-
- return new_head;
-}
-
-#define LSM_SEARCH_MAX 180 // This should be enough to iterate
-static void *find_head_addr(void *security_ptr, int *index)
-{
- if (!security_ptr) {
- return NULL;
- }
- struct hlist_head *head_start =
- (struct hlist_head *)&security_hook_heads;
-
- for (int i = 0; i < LSM_SEARCH_MAX; i++) {
- struct hlist_head *head = head_start + i;
- struct security_hook_list *pos;
- hlist_for_each_entry (pos, head, list) {
- if (pos->hook.capget == security_ptr) {
- if (index) {
- *index = i;
- }
- return head;
- }
- }
- }
-
- return NULL;
-}
-
-#define GET_SYMBOL_ADDR(sym) \
- ({ \
- void *addr = kallsyms_lookup_name(#sym ".cfi_jt"); \
- if (!addr) { \
- addr = kallsyms_lookup_name(#sym); \
- } \
- addr; \
- })
-
-#define KSU_LSM_HOOK_HACK_INIT(head_ptr, name, func) \
- do { \
- static struct security_hook_list hook = { \
- .hook = { .name = func } \
- }; \
- hook.head = head_ptr; \
- hook.lsm = "ksu"; \
- struct hlist_head *new_head = copy_security_hlist(hook.head); \
- if (!new_head) { \
- pr_err("Failed to copy security list: %s\n", #name); \
- break; \
- } \
- hlist_add_tail_rcu(&hook.list, new_head); \
- if (override_security_head(hook.head, new_head, \
- sizeof(*new_head))) { \
- free_security_hook_list(new_head); \
- pr_err("Failed to hack lsm for: %s\n", #name); \
- } \
- } while (0)
-
-void __init ksu_lsm_hook_init(void)
-{
- void *cap_prctl = GET_SYMBOL_ADDR(cap_task_prctl);
- void *prctl_head = find_head_addr(cap_prctl, NULL);
- if (prctl_head) {
- if (prctl_head != &security_hook_heads.task_prctl) {
- pr_warn("prctl's address has shifted!\n");
- }
- KSU_LSM_HOOK_HACK_INIT(prctl_head, task_prctl, ksu_task_prctl);
- } else {
- pr_warn("Failed to find task_prctl!\n");
- }
-
- int inode_killpriv_index = -1;
- void *cap_killpriv = GET_SYMBOL_ADDR(cap_inode_killpriv);
- find_head_addr(cap_killpriv, &inode_killpriv_index);
- if (inode_killpriv_index < 0) {
- pr_warn("Failed to find inode_rename, use kprobe instead!\n");
- register_kprobe(&renameat_kp);
- } else {
- int inode_rename_index = inode_killpriv_index +
- &security_hook_heads.inode_rename -
- &security_hook_heads.inode_killpriv;
- struct hlist_head *head_start =
- (struct hlist_head *)&security_hook_heads;
- void *inode_rename_head = head_start + inode_rename_index;
- if (inode_rename_head != &security_hook_heads.inode_rename) {
- pr_warn("inode_rename's address has shifted!\n");
- }
- KSU_LSM_HOOK_HACK_INIT(inode_rename_head, inode_rename,
- ksu_inode_rename);
- }
- void *cap_setuid = GET_SYMBOL_ADDR(cap_task_fix_setuid);
- void *setuid_head = find_head_addr(cap_setuid, NULL);
- if (setuid_head) {
- if (setuid_head != &security_hook_heads.task_fix_setuid) {
- pr_warn("setuid's address has shifted!\n");
- }
- KSU_LSM_HOOK_HACK_INIT(setuid_head, task_fix_setuid,
- ksu_task_fix_setuid);
- } else {
- pr_warn("Failed to find task_fix_setuid!\n");
- }
- smp_mb();
-}
-#endif
-
-void __init ksu_core_init(void)
-{
- ksu_lsm_hook_init();
-}
-
-void ksu_core_exit(void)
-{
- ksu_throne_comm_exit();
-#ifdef CONFIG_KPROBE
- pr_info("ksu_core_kprobe_exit\n");
- // we dont use this now
- // ksu_kprobe_exit();
-#endif
-}
diff --git a/kernel/core_hook.h b/kernel/core_hook.h
deleted file mode 100644
index 6ed328a..0000000
--- a/kernel/core_hook.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef __KSU_H_KSU_CORE
-#define __KSU_H_KSU_CORE
-
-#include
-#include "apk_sign.h"
-
-void __init ksu_core_init(void);
-void ksu_core_exit(void);
-
-#endif
diff --git a/kernel/dynamic_manager.c b/kernel/dynamic_manager.c
index 6f34d19..96bc710 100644
--- a/kernel/dynamic_manager.c
+++ b/kernel/dynamic_manager.c
@@ -17,7 +17,6 @@
#include "dynamic_manager.h"
#include "klog.h" // IWYU pragma: keep
-#include "kernel_compat.h"
#include "manager.h"
#define MAX_MANAGERS 2
@@ -233,23 +232,23 @@ static void do_save_dynamic_manager(struct work_struct *work)
return;
}
- fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ fp = filp_open(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) {
pr_err("save_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
return;
}
- if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
+ if (kernel_write(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
pr_err("save_dynamic_manager write magic failed.\n");
goto exit;
}
- if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
+ if (kernel_write(fp, &version, sizeof(version), &off) != sizeof(version)) {
pr_err("save_dynamic_manager write version failed.\n");
goto exit;
}
- if (ksu_kernel_write_compat(fp, &config_to_save, sizeof(config_to_save), &off) != sizeof(config_to_save)) {
+ if (kernel_write(fp, &config_to_save, sizeof(config_to_save), &off) != sizeof(config_to_save)) {
pr_err("save_dynamic_manager write config failed.\n");
goto exit;
}
@@ -271,7 +270,7 @@ static void do_load_dynamic_manager(struct work_struct *work)
unsigned long flags;
int i;
- fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_RDONLY, 0);
+ fp = filp_open(KERNEL_SU_DYNAMIC_MANAGER, O_RDONLY, 0);
if (IS_ERR(fp)) {
if (PTR_ERR(fp) == -ENOENT) {
pr_info("No saved dynamic manager config found\n");
@@ -281,20 +280,20 @@ static void do_load_dynamic_manager(struct work_struct *work)
return;
}
- if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
+ if (kernel_read(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
magic != DYNAMIC_MANAGER_FILE_MAGIC) {
pr_err("dynamic manager file invalid magic: %x!\n", magic);
goto exit;
}
- if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
+ if (kernel_read(fp, &version, sizeof(version), &off) != sizeof(version)) {
pr_err("dynamic manager read version failed\n");
goto exit;
}
pr_info("dynamic manager file version: %d\n", version);
- ret = ksu_kernel_read_compat(fp, &loaded_config, sizeof(loaded_config), &off);
+ ret = kernel_read(fp, &loaded_config, sizeof(loaded_config), &off);
if (ret <= 0) {
pr_info("load_dynamic_manager read err: %zd\n", ret);
goto exit;
@@ -348,14 +347,14 @@ static void do_clear_dynamic_manager(struct work_struct *work)
memset(zero_buffer, 0, sizeof(zero_buffer));
- fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ fp = filp_open(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) {
pr_err("clear_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
return;
}
// Write null bytes to overwrite the file content
- if (ksu_kernel_write_compat(fp, zero_buffer, sizeof(zero_buffer), &off) != sizeof(zero_buffer)) {
+ if (kernel_write(fp, zero_buffer, sizeof(zero_buffer), &off) != sizeof(zero_buffer)) {
pr_err("clear_dynamic_manager write null bytes failed.\n");
} else {
pr_info("Dynamic sign config file cleared successfully\n");
diff --git a/kernel/feature.c b/kernel/feature.c
new file mode 100644
index 0000000..99277b5
--- /dev/null
+++ b/kernel/feature.c
@@ -0,0 +1,173 @@
+#include "feature.h"
+#include "klog.h" // IWYU pragma: keep
+
+#include
+
+static const struct ksu_feature_handler *feature_handlers[KSU_FEATURE_MAX];
+
+static DEFINE_MUTEX(feature_mutex);
+
+int ksu_register_feature_handler(const struct ksu_feature_handler *handler)
+{
+ if (!handler) {
+ pr_err("feature: register handler is NULL\n");
+ return -EINVAL;
+ }
+
+ if (handler->feature_id >= KSU_FEATURE_MAX) {
+ pr_err("feature: invalid feature_id %u\n", handler->feature_id);
+ return -EINVAL;
+ }
+
+ if (!handler->get_handler && !handler->set_handler) {
+ pr_err("feature: no handler provided for feature %u\n", handler->feature_id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&feature_mutex);
+
+ if (feature_handlers[handler->feature_id]) {
+ pr_warn("feature: handler for %u already registered, overwriting\n",
+ handler->feature_id);
+ }
+
+ feature_handlers[handler->feature_id] = handler;
+
+ pr_info("feature: registered handler for %s (id=%u)\n",
+ handler->name ? handler->name : "unknown", handler->feature_id);
+
+ mutex_unlock(&feature_mutex);
+ return 0;
+}
+
+int ksu_unregister_feature_handler(u32 feature_id)
+{
+ int ret = 0;
+
+ if (feature_id >= KSU_FEATURE_MAX) {
+ pr_err("feature: invalid feature_id %u\n", feature_id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&feature_mutex);
+
+ if (!feature_handlers[feature_id]) {
+ pr_warn("feature: no handler registered for %u\n", feature_id);
+ ret = -ENOENT;
+ goto out;
+ }
+
+ feature_handlers[feature_id] = NULL;
+
+ pr_info("feature: unregistered handler for id=%u\n", feature_id);
+
+out:
+ mutex_unlock(&feature_mutex);
+ return ret;
+}
+
+int ksu_get_feature(u32 feature_id, u64 *value, bool *supported)
+{
+ int ret = 0;
+ const struct ksu_feature_handler *handler;
+
+ if (feature_id >= KSU_FEATURE_MAX) {
+ pr_err("feature: invalid feature_id %u\n", feature_id);
+ return -EINVAL;
+ }
+
+ if (!value || !supported) {
+ pr_err("feature: invalid parameters\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&feature_mutex);
+
+ handler = feature_handlers[feature_id];
+
+ if (!handler) {
+ *supported = false;
+ *value = 0;
+ pr_debug("feature: feature %u not supported\n", feature_id);
+ goto out;
+ }
+
+ *supported = true;
+
+ if (!handler->get_handler) {
+ pr_warn("feature: no get_handler for feature %u\n", feature_id);
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = handler->get_handler(value);
+ if (ret) {
+ pr_err("feature: get_handler for %u failed: %d\n", feature_id, ret);
+ }
+
+out:
+ mutex_unlock(&feature_mutex);
+ return ret;
+}
+
+int ksu_set_feature(u32 feature_id, u64 value)
+{
+ int ret = 0;
+ const struct ksu_feature_handler *handler;
+
+ if (feature_id >= KSU_FEATURE_MAX) {
+ pr_err("feature: invalid feature_id %u\n", feature_id);
+ return -EINVAL;
+ }
+
+ mutex_lock(&feature_mutex);
+
+ handler = feature_handlers[feature_id];
+
+ if (!handler) {
+ pr_err("feature: feature %u not registered\n", feature_id);
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (!handler->set_handler) {
+ pr_warn("feature: no set_handler for feature %u\n", feature_id);
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
+ ret = handler->set_handler(value);
+ if (ret) {
+ pr_err("feature: set_handler for %u failed: %d\n", feature_id, ret);
+ }
+
+out:
+ mutex_unlock(&feature_mutex);
+ return ret;
+}
+
+void ksu_feature_init(void)
+{
+ int i;
+
+ for (i = 0; i < KSU_FEATURE_MAX; i++) {
+ feature_handlers[i] = NULL;
+ }
+
+ pr_info("feature: feature management initialized\n");
+}
+
+void ksu_feature_exit(void)
+{
+ int i;
+
+ mutex_lock(&feature_mutex);
+
+ for (i = 0; i < KSU_FEATURE_MAX; i++) {
+ feature_handlers[i] = NULL;
+ }
+
+ mutex_unlock(&feature_mutex);
+
+ pr_info("feature: feature management cleaned up\n");
+}
diff --git a/kernel/feature.h b/kernel/feature.h
new file mode 100644
index 0000000..d9cbc92
--- /dev/null
+++ b/kernel/feature.h
@@ -0,0 +1,36 @@
+#ifndef __KSU_H_FEATURE
+#define __KSU_H_FEATURE
+
+#include
+
+enum ksu_feature_id {
+ KSU_FEATURE_SU_COMPAT = 0,
+ KSU_FEATURE_KERNEL_UMOUNT = 1,
+ KSU_FEATURE_ENHANCED_SECURITY = 2,
+
+ KSU_FEATURE_MAX
+};
+
+typedef int (*ksu_feature_get_t)(u64 *value);
+typedef int (*ksu_feature_set_t)(u64 value);
+
+struct ksu_feature_handler {
+ u32 feature_id;
+ const char *name;
+ ksu_feature_get_t get_handler;
+ ksu_feature_set_t set_handler;
+};
+
+int ksu_register_feature_handler(const struct ksu_feature_handler *handler);
+
+int ksu_unregister_feature_handler(u32 feature_id);
+
+int ksu_get_feature(u32 feature_id, u64 *value, bool *supported);
+
+int ksu_set_feature(u32 feature_id, u64 value);
+
+void ksu_feature_init(void);
+
+void ksu_feature_exit(void);
+
+#endif // __KSU_H_FEATURE
diff --git a/kernel/file_wrapper.c b/kernel/file_wrapper.c
new file mode 100644
index 0000000..d73cf5d
--- /dev/null
+++ b/kernel/file_wrapper.c
@@ -0,0 +1,341 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "klog.h" // IWYU pragma: keep
+#include "selinux/selinux.h"
+
+#include "file_wrapper.h"
+
+static loff_t ksu_wrapper_llseek(struct file *fp, loff_t off, int flags) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ return orig->f_op->llseek(data->orig, off, flags);
+}
+
+static ssize_t ksu_wrapper_read(struct file *fp, char __user *ptr, size_t sz, loff_t *off) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ return orig->f_op->read(orig, ptr, sz, off);
+}
+
+static ssize_t ksu_wrapper_write(struct file *fp, const char __user *ptr, size_t sz, loff_t *off) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ return orig->f_op->write(orig, ptr, sz, off);
+}
+
+static ssize_t ksu_wrapper_read_iter(struct kiocb *iocb, struct iov_iter *iovi) {
+ struct ksu_file_wrapper* data = iocb->ki_filp->private_data;
+ struct file* orig = data->orig;
+ iocb->ki_filp = orig;
+ return orig->f_op->read_iter(iocb, iovi);
+}
+
+static ssize_t ksu_wrapper_write_iter(struct kiocb *iocb, struct iov_iter *iovi) {
+ struct ksu_file_wrapper* data = iocb->ki_filp->private_data;
+ struct file* orig = data->orig;
+ iocb->ki_filp = orig;
+ return orig->f_op->write_iter(iocb, iovi);
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
+static int ksu_wrapper_iopoll(struct kiocb *kiocb, struct io_comp_batch* icb, unsigned int v) {
+ struct ksu_file_wrapper* data = kiocb->ki_filp->private_data;
+ struct file* orig = data->orig;
+ kiocb->ki_filp = orig;
+ return orig->f_op->iopoll(kiocb, icb, v);
+}
+#else
+static int ksu_wrapper_iopoll(struct kiocb *kiocb, bool spin) {
+ struct ksu_file_wrapper* data = kiocb->ki_filp->private_data;
+ struct file* orig = data->orig;
+ kiocb->ki_filp = orig;
+ return orig->f_op->iopoll(kiocb, spin);
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
+static int ksu_wrapper_iterate (struct file *fp, struct dir_context *dc) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ return orig->f_op->iterate(orig, dc);
+}
+#endif
+
+static int ksu_wrapper_iterate_shared(struct file *fp, struct dir_context *dc) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ return orig->f_op->iterate_shared(orig, dc);
+}
+
+static __poll_t ksu_wrapper_poll(struct file *fp, struct poll_table_struct *pts) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ return orig->f_op->poll(orig, pts);
+}
+
+static long ksu_wrapper_unlocked_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ return orig->f_op->unlocked_ioctl(orig, cmd, arg);
+}
+
+static long ksu_wrapper_compat_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ return orig->f_op->compat_ioctl(orig, cmd, arg);
+}
+
+static int ksu_wrapper_mmap(struct file *fp, struct vm_area_struct * vma) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ return orig->f_op->mmap(orig, vma);
+}
+
+// static unsigned long mmap_supported_flags {}
+
+static int ksu_wrapper_open(struct inode *ino, struct file *fp) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ struct inode *orig_ino = file_inode(orig);
+ return orig->f_op->open(orig_ino, orig);
+}
+
+static int ksu_wrapper_flush(struct file *fp, fl_owner_t id) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ return orig->f_op->flush(orig, id);
+}
+
+
+static int ksu_wrapper_fsync(struct file *fp, loff_t off1, loff_t off2, int datasync) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ return orig->f_op->fsync(orig, off1, off2, datasync);
+}
+
+static int ksu_wrapper_fasync(int arg, struct file *fp, int arg2) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ return orig->f_op->fasync(arg, orig, arg2);
+}
+
+static int ksu_wrapper_lock(struct file *fp, int arg1, struct file_lock *fl) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ return orig->f_op->lock(orig, arg1, fl);
+}
+
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
+static ssize_t ksu_wrapper_sendpage(struct file *fp, struct page *pg, int arg1, size_t sz, loff_t *off, int arg2) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ if (orig->f_op->sendpage) {
+ return orig->f_op->sendpage(orig, pg, arg1, sz, off, arg2);
+ }
+ return -EINVAL;
+}
+#endif
+
+static unsigned long ksu_wrapper_get_unmapped_area(struct file *fp, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ if (orig->f_op->get_unmapped_area) {
+ return orig->f_op->get_unmapped_area(orig, arg1, arg2, arg3, arg4);
+ }
+ return -EINVAL;
+}
+
+// static int ksu_wrapper_check_flags(int arg) {}
+
+static int ksu_wrapper_flock(struct file *fp, int arg1, struct file_lock *fl) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ if (orig->f_op->flock) {
+ return orig->f_op->flock(orig, arg1, fl);
+ }
+ return -EINVAL;
+}
+
+static ssize_t ksu_wrapper_splice_write(struct pipe_inode_info * pii, struct file *fp, loff_t *off, size_t sz, unsigned int arg1) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ if (orig->f_op->splice_write) {
+ return orig->f_op->splice_write(pii, orig, off, sz, arg1);
+ }
+ return -EINVAL;
+}
+
+static ssize_t ksu_wrapper_splice_read(struct file *fp, loff_t *off, struct pipe_inode_info *pii, size_t sz, unsigned int arg1) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ if (orig->f_op->splice_read) {
+ return orig->f_op->splice_read(orig, off, pii, sz, arg1);
+ }
+ return -EINVAL;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
+void ksu_wrapper_splice_eof(struct file *fp) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ if (orig->f_op->splice_eof) {
+ return orig->f_op->splice_eof(orig);
+ }
+}
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
+static int ksu_wrapper_setlease(struct file *fp, int arg1, struct file_lease **fl, void **p) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ if (orig->f_op->setlease) {
+ return orig->f_op->setlease(orig, arg1, fl, p);
+ }
+ return -EINVAL;
+}
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
+static int ksu_wrapper_setlease(struct file *fp, int arg1, struct file_lock **fl, void **p) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ if (orig->f_op->setlease) {
+ return orig->f_op->setlease(orig, arg1, fl, p);
+ }
+ return -EINVAL;
+}
+#else
+static int ksu_wrapper_setlease(struct file *fp, long arg1, struct file_lock **fl, void **p) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ if (orig->f_op->setlease) {
+ return orig->f_op->setlease(orig, arg1, fl, p);
+ }
+ return -EINVAL;
+}
+#endif
+
+static long ksu_wrapper_fallocate(struct file *fp, int mode, loff_t offset, loff_t len) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ if (orig->f_op->fallocate) {
+ return orig->f_op->fallocate(orig, mode, offset, len);
+ }
+ return -EINVAL;
+}
+
+static void ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f) {
+ struct ksu_file_wrapper* data = f->private_data;
+ struct file* orig = data->orig;
+ if (orig->f_op->show_fdinfo) {
+ orig->f_op->show_fdinfo(m, orig);
+ }
+}
+
+static ssize_t ksu_wrapper_copy_file_range(struct file *f1, loff_t off1, struct file *f2,
+ loff_t off2, size_t sz, unsigned int flags) {
+ // TODO: determine which file to use
+ struct ksu_file_wrapper* data = f1->private_data;
+ struct file* orig = data->orig;
+ if (orig->f_op->copy_file_range) {
+ return orig->f_op->copy_file_range(orig, off1, f2, off2, sz, flags);
+ }
+ return -EINVAL;
+}
+
+static loff_t ksu_wrapper_remap_file_range(struct file *file_in, loff_t pos_in,
+ struct file *file_out, loff_t pos_out,
+ loff_t len, unsigned int remap_flags) {
+ // TODO: determine which file to use
+ struct ksu_file_wrapper* data = file_in->private_data;
+ struct file* orig = data->orig;
+ if (orig->f_op->remap_file_range) {
+ return orig->f_op->remap_file_range(orig, pos_in, file_out, pos_out, len, remap_flags);
+ }
+ return -EINVAL;
+}
+
+static int ksu_wrapper_fadvise(struct file *fp, loff_t off1, loff_t off2, int flags) {
+ struct ksu_file_wrapper* data = fp->private_data;
+ struct file* orig = data->orig;
+ if (orig->f_op->fadvise) {
+ return orig->f_op->fadvise(orig, off1, off2, flags);
+ }
+ return -EINVAL;
+}
+
+static int ksu_wrapper_release(struct inode *inode, struct file *filp) {
+ ksu_delete_file_wrapper(filp->private_data);
+ return 0;
+}
+
+struct ksu_file_wrapper* ksu_create_file_wrapper(struct file* fp) {
+ struct ksu_file_wrapper* p = kcalloc(sizeof(struct ksu_file_wrapper), 1, GFP_KERNEL);
+ if (!p) {
+ return NULL;
+ }
+
+ get_file(fp);
+
+ p->orig = fp;
+ p->ops.owner = THIS_MODULE;
+ p->ops.llseek = fp->f_op->llseek ? ksu_wrapper_llseek : NULL;
+ p->ops.read = fp->f_op->read ? ksu_wrapper_read : NULL;
+ p->ops.write = fp->f_op->write ? ksu_wrapper_write : NULL;
+ p->ops.read_iter = fp->f_op->read_iter ? ksu_wrapper_read_iter : NULL;
+ p->ops.write_iter = fp->f_op->write_iter ? ksu_wrapper_write_iter : NULL;
+ p->ops.iopoll = fp->f_op->iopoll ? ksu_wrapper_iopoll : NULL;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
+ p->ops.iterate = fp->f_op->iterate ? ksu_wrapper_iterate : NULL;
+#endif
+ p->ops.iterate_shared = fp->f_op->iterate_shared ? ksu_wrapper_iterate_shared : NULL;
+ p->ops.poll = fp->f_op->poll ? ksu_wrapper_poll : NULL;
+ p->ops.unlocked_ioctl = fp->f_op->unlocked_ioctl ? ksu_wrapper_unlocked_ioctl : NULL;
+ p->ops.compat_ioctl = fp->f_op->compat_ioctl ? ksu_wrapper_compat_ioctl : NULL;
+ p->ops.mmap = fp->f_op->mmap ? ksu_wrapper_mmap : NULL;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
+ p->ops.fop_flags = fp->f_op->fop_flags;
+#else
+ p->ops.mmap_supported_flags = fp->f_op->mmap_supported_flags;
+#endif
+ p->ops.open = fp->f_op->open ? ksu_wrapper_open : NULL;
+ p->ops.flush = fp->f_op->flush ? ksu_wrapper_flush : NULL;
+ p->ops.release = ksu_wrapper_release;
+ p->ops.fsync = fp->f_op->fsync ? ksu_wrapper_fsync : NULL;
+ p->ops.fasync = fp->f_op->fasync ? ksu_wrapper_fasync : NULL;
+ p->ops.lock = fp->f_op->lock ? ksu_wrapper_lock : NULL;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
+ p->ops.sendpage = fp->f_op->sendpage ? ksu_wrapper_sendpage : NULL;
+#endif
+ p->ops.get_unmapped_area = fp->f_op->get_unmapped_area ? ksu_wrapper_get_unmapped_area : NULL;
+ p->ops.check_flags = fp->f_op->check_flags;
+ p->ops.flock = fp->f_op->flock ? ksu_wrapper_flock : NULL;
+ p->ops.splice_write = fp->f_op->splice_write ? ksu_wrapper_splice_write : NULL;
+ p->ops.splice_read = fp->f_op->splice_read ? ksu_wrapper_splice_read : NULL;
+ p->ops.setlease = fp->f_op->setlease ? ksu_wrapper_setlease : NULL;
+ p->ops.fallocate = fp->f_op->fallocate ? ksu_wrapper_fallocate : NULL;
+ p->ops.show_fdinfo = fp->f_op->show_fdinfo ? ksu_wrapper_show_fdinfo : NULL;
+ p->ops.copy_file_range = fp->f_op->copy_file_range ? ksu_wrapper_copy_file_range : NULL;
+ p->ops.remap_file_range = fp->f_op->remap_file_range ? ksu_wrapper_remap_file_range : NULL;
+ p->ops.fadvise = fp->f_op->fadvise ? ksu_wrapper_fadvise : NULL;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
+ p->ops.splice_eof = fp->f_op->splice_eof ? ksu_wrapper_splice_eof : NULL;
+#endif
+
+ return p;
+}
+
+void ksu_delete_file_wrapper(struct ksu_file_wrapper* data) {
+ fput((struct file*) data->orig);
+ kfree(data);
+}
\ No newline at end of file
diff --git a/kernel/file_wrapper.h b/kernel/file_wrapper.h
new file mode 100644
index 0000000..421e20e
--- /dev/null
+++ b/kernel/file_wrapper.h
@@ -0,0 +1,14 @@
+#ifndef KSU_FILE_WRAPPER_H
+#define KSU_FILE_WRAPPER_H
+
+#include
+#include
+
+struct ksu_file_wrapper {
+ struct file* orig;
+ struct file_operations ops;
+};
+
+struct ksu_file_wrapper* ksu_create_file_wrapper(struct file* fp);
+void ksu_delete_file_wrapper(struct ksu_file_wrapper* data);
+#endif // KSU_FILE_WRAPPER_H
\ No newline at end of file
diff --git a/kernel/include/ksu_hook.h b/kernel/include/ksu_hook.h
deleted file mode 100644
index ea0b04d..0000000
--- a/kernel/include/ksu_hook.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef __KSU_H_KSHOOK
-#define __KSU_H_KSHOOK
-
-#include
-#include
-
-// 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
diff --git a/kernel/kernel_compat.c b/kernel/kernel_compat.c
deleted file mode 100644
index 3fb7ecd..0000000
--- a/kernel/kernel_compat.c
+++ /dev/null
@@ -1,94 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include "klog.h" // IWYU pragma: keep
-#include "kernel_compat.h"
-
-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;
-#ifdef CONFIG_KSU_DEBUG
- 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);
-#endif
- ksu_save_ns_fs(&android_context_saved);
- } else {
- pr_info("android context saved disabled\n");
- }
- task_unlock(current);
-}
-
-struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
-{
- // 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) {
-#ifdef CONFIG_KSU_DEBUG
- pr_info("start switch current nsproxy and fs to android context\n");
-#endif
- 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);
-#ifdef CONFIG_KSU_DEBUG
- pr_info("switch current nsproxy and fs back to saved successfully\n");
-#endif
- }
- return fp;
-}
-
-ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
- loff_t *pos)
-{
- return kernel_read(p, buf, count, pos);
-}
-
-ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count,
- loff_t *pos)
-{
- return kernel_write(p, buf, count, pos);
-}
-
-long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
- long count)
-{
- return strncpy_from_user_nofault(dst, unsafe_addr, count);
-}
diff --git a/kernel/kernel_compat.h b/kernel/kernel_compat.h
index ed02888..14e1cb2 100644
--- a/kernel/kernel_compat.h
+++ b/kernel/kernel_compat.h
@@ -3,63 +3,7 @@
#include
#include
-#include "ss/policydb.h"
-#include "linux/key.h"
-/**
- * list_count_nodes - count the number of nodes in a list
- * @head: the head of the list
- *
- * This function iterates over the list starting from @head and counts
- * the number of nodes in the list. It does not modify the list.
- *
- * Context: Any context. The function is safe to call in any context,
- * including interrupt context, as it does not sleep or allocate
- * memory.
- *
- * Return: the number of nodes in the list (excluding the head)
- */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
-static inline __maybe_unused size_t list_count_nodes(const struct list_head *head)
-{
- const struct list_head *pos;
- size_t count = 0;
-
- if (!head)
- return 0;
-
- list_for_each(pos, head)
- count++;
-
- return count;
-}
-#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
-
-extern long ksu_strncpy_from_user_nofault(char *dst,
- const void __user *unsafe_addr,
- long count);
-
-extern void ksu_android_ns_fs_check();
-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);
/*
* ksu_copy_from_user_retry
* try nofault copy first, if it fails, try with plain
@@ -67,14 +11,14 @@ extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
* 0 = success
*/
static long ksu_copy_from_user_retry(void *to,
- const void __user *from, unsigned long count)
+ const void __user *from, unsigned long count)
{
- long ret = copy_from_user_nofault(to, from, count);
- if (likely(!ret))
- return ret;
+ long ret = copy_from_user_nofault(to, from, count);
+ if (likely(!ret))
+ return ret;
- // we faulted! fallback to slow path
- return copy_from_user(to, from, count);
+ // we faulted! fallback to slow path
+ return copy_from_user(to, from, count);
}
#endif
diff --git a/kernel/kernel_umount.c b/kernel/kernel_umount.c
new file mode 100644
index 0000000..d714a22
--- /dev/null
+++ b/kernel/kernel_umount.c
@@ -0,0 +1,193 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "kernel_umount.h"
+#include "klog.h" // IWYU pragma: keep
+#include "allowlist.h"
+#include "selinux/selinux.h"
+#include "feature.h"
+#include "ksud.h"
+
+#include "umount_manager.h"
+#include "sulog.h"
+
+static bool ksu_kernel_umount_enabled = true;
+
+static int kernel_umount_feature_get(u64 *value)
+{
+ *value = ksu_kernel_umount_enabled ? 1 : 0;
+ return 0;
+}
+
+static int kernel_umount_feature_set(u64 value)
+{
+ bool enable = value != 0;
+ ksu_kernel_umount_enabled = enable;
+ pr_info("kernel_umount: set to %d\n", enable);
+ return 0;
+}
+
+static const struct ksu_feature_handler kernel_umount_handler = {
+ .feature_id = KSU_FEATURE_KERNEL_UMOUNT,
+ .name = "kernel_umount",
+ .get_handler = kernel_umount_feature_get,
+ .set_handler = kernel_umount_feature_set,
+};
+
+static bool should_umount(struct path *path)
+{
+ if (!path) {
+ return false;
+ }
+
+ if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
+ pr_info("ignore global mnt namespace process: %d\n", current_uid().val);
+ return false;
+ }
+
+ if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) {
+ const char *fstype = path->mnt->mnt_sb->s_type->name;
+ return strcmp(fstype, "overlay") == 0;
+ }
+ return false;
+}
+
+extern int path_umount(struct path *path, int flags);
+
+static void ksu_umount_mnt(struct path *path, int flags)
+{
+ int err = path_umount(path, flags);
+ if (err) {
+ pr_info("umount %s failed: %d\n", path->dentry->d_iname, err);
+ }
+}
+
+void try_umount(const char *mnt, bool check_mnt, int flags)
+{
+ struct path path;
+ int err = kern_path(mnt, 0, &path);
+ if (err) {
+ return;
+ }
+
+ if (path.dentry != path.mnt->mnt_root) {
+ // it is not root mountpoint, maybe umounted by others already.
+ path_put(&path);
+ return;
+ }
+
+ // we are only interest in some specific mounts
+ if (check_mnt && !should_umount(&path)) {
+ path_put(&path);
+ return;
+ }
+
+ ksu_umount_mnt(&path, flags);
+}
+
+struct umount_tw {
+ struct callback_head cb;
+ const struct cred *old_cred;
+};
+
+static void umount_tw_func(struct callback_head *cb)
+{
+ struct umount_tw *tw = container_of(cb, struct umount_tw, cb);
+ const struct cred *saved = NULL;
+ if (tw->old_cred) {
+ saved = override_creds(tw->old_cred);
+ }
+
+ // fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
+ // filter the mountpoint whose target is `/data/adb`
+ ksu_umount_manager_execute_all(tw->old_cred);
+
+ if (saved)
+ revert_creds(saved);
+
+ if (tw->old_cred)
+ put_cred(tw->old_cred);
+
+ kfree(tw);
+}
+
+int ksu_handle_umount(uid_t old_uid, uid_t new_uid)
+{
+ struct umount_tw *tw;
+
+ // this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it!
+ if (!ksu_module_mounted) {
+ return 0;
+ }
+
+ if (!ksu_kernel_umount_enabled) {
+ return 0;
+ }
+
+ // FIXME: isolated process which directly forks from zygote is not handled
+ if (!is_appuid(new_uid)) {
+ return 0;
+ }
+
+ if (!ksu_uid_should_umount(new_uid)) {
+ return 0;
+ }
+
+ // check old process's selinux context, if it is not zygote, ignore it!
+ // because some su apps may setuid to untrusted_app but they are in global mount namespace
+ // when we umount for such process, that is a disaster!
+ bool is_zygote_child = is_zygote(get_current_cred());
+ if (!is_zygote_child) {
+ pr_info("handle umount ignore non zygote child: %d\n", current->pid);
+ return 0;
+ }
+#if __SULOG_GATE
+ ksu_sulog_report_syscall(new_uid, NULL, "setuid", NULL);
+#endif
+ // umount the target mnt
+ pr_info("handle umount for uid: %d, pid: %d\n", new_uid, current->pid);
+
+ tw = kmalloc(sizeof(*tw), GFP_ATOMIC);
+ if (!tw)
+ return 0;
+
+ tw->old_cred = get_current_cred();
+ tw->cb.func = umount_tw_func;
+
+ int err = task_work_add(current, &tw->cb, TWA_RESUME);
+ if (err) {
+ if (tw->old_cred) {
+ put_cred(tw->old_cred);
+ }
+ kfree(tw);
+ pr_warn("unmount add task_work failed\n");
+ }
+
+ return 0;
+}
+
+void ksu_kernel_umount_init(void)
+{
+ int rc = 0;
+ rc = ksu_umount_manager_init();
+ if (rc) {
+ pr_err("Failed to initialize umount manager: %d\n", rc);
+ }
+ if (ksu_register_feature_handler(&kernel_umount_handler)) {
+ pr_err("Failed to register kernel_umount feature handler\n");
+ }
+}
+
+void ksu_kernel_umount_exit(void)
+{
+ ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT);
+}
\ No newline at end of file
diff --git a/kernel/kernel_umount.h b/kernel/kernel_umount.h
new file mode 100644
index 0000000..68d2f75
--- /dev/null
+++ b/kernel/kernel_umount.h
@@ -0,0 +1,14 @@
+#ifndef __KSU_H_KERNEL_UMOUNT
+#define __KSU_H_KERNEL_UMOUNT
+
+#include
+
+void ksu_kernel_umount_init(void);
+void ksu_kernel_umount_exit(void);
+
+void try_umount(const char *mnt, bool check_mnt, int flags);
+
+// Handler function to be called from setresuid hook
+int ksu_handle_umount(uid_t old_uid, uid_t new_uid);
+
+#endif
\ No newline at end of file
diff --git a/kernel/kpm/compact.c b/kernel/kpm/compact.c
index 8af317c..5791db4 100644
--- a/kernel/kpm/compact.c
+++ b/kernel/kpm/compact.c
@@ -31,7 +31,7 @@
static int sukisu_is_su_allow_uid(uid_t uid)
{
- return ksu_is_allow_uid(uid) ? 1 : 0;
+ return ksu_is_allow_uid_for_current(uid) ? 1 : 0;
}
static int sukisu_get_ap_mod_exclude(uid_t uid)
diff --git a/kernel/kpm/kpm.c b/kernel/kpm/kpm.c
index 32a58ce..e31384b 100644
--- a/kernel/kpm/kpm.c
+++ b/kernel/kpm/kpm.c
@@ -9,13 +9,10 @@
* 并参照KernelPatch的标准KPM格式实现加载和控制
*/
-#include
-#include
#include
#include
#include
#include
-#include
#include
#include
#include
@@ -25,26 +22,25 @@
#include
#include
#include
-#include
#include
#include
#include
#include
-#include
#include
-#include
#include
#include
#include
#include
#include
-#include
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) && defined(CONFIG_MODULES)
#include
#endif
#include "kpm.h"
#include "compact.h"
+#define KPM_NAME_LEN 32
+#define KPM_ARGS_LEN 1024
+
#ifndef NO_OPTIMIZE
#if defined(__GNUC__) && !defined(__clang__)
#define NO_OPTIMIZE __attribute__((optimize("O0")))
@@ -56,156 +52,231 @@
#endif
noinline NO_OPTIMIZE void sukisu_kpm_load_module_path(const char *path,
- const char *args, void *ptr, void __user *result)
+ const char *args, void *ptr, int *result)
{
- int res = -1;
-
- printk("KPM: Stub function called (sukisu_kpm_load_module_path). "
+ pr_info("kpm: Stub function called (sukisu_kpm_load_module_path). "
"path=%s args=%s ptr=%p\n", path, args, ptr);
__asm__ volatile("nop");
-
- if (copy_to_user(result, &res, sizeof(res)) < 1)
- printk("KPM: Copy to user failed.");
}
EXPORT_SYMBOL(sukisu_kpm_load_module_path);
noinline NO_OPTIMIZE void sukisu_kpm_unload_module(const char *name,
- void *ptr, void __user *result)
+ void *ptr, int *result)
{
- int res = -1;
-
- printk("KPM: Stub function called (sukisu_kpm_unload_module). "
+ pr_info("kpm: Stub function called (sukisu_kpm_unload_module). "
"name=%s ptr=%p\n", name, ptr);
__asm__ volatile("nop");
-
- if (copy_to_user(result, &res, sizeof(res)) < 1)
- printk("KPM: Copy to user failed.");
}
EXPORT_SYMBOL(sukisu_kpm_unload_module);
-noinline NO_OPTIMIZE void sukisu_kpm_num(void __user *result)
+noinline NO_OPTIMIZE void sukisu_kpm_num(int *result)
{
- int res = 0;
-
- printk("KPM: Stub function called (sukisu_kpm_num).\n");
+ pr_info("kpm: Stub function called (sukisu_kpm_num).\n");
__asm__ volatile("nop");
-
- if (copy_to_user(result, &res, sizeof(res)) < 1)
- printk("KPM: Copy to user failed.");
}
EXPORT_SYMBOL(sukisu_kpm_num);
-noinline NO_OPTIMIZE void sukisu_kpm_info(const char *name, void __user *out,
- void __user *result)
+noinline NO_OPTIMIZE void sukisu_kpm_info(const char *name, char *buf, int bufferSize,
+ int *size)
{
- int res = -1;
-
- printk("KPM: Stub function called (sukisu_kpm_info). "
- "name=%s buffer=%p\n", name, out);
+ pr_info("kpm: Stub function called (sukisu_kpm_info). "
+ "name=%s buffer=%p\n", name, buf);
__asm__ volatile("nop");
-
- if (copy_to_user(result, &res, sizeof(res)) < 1)
- printk("KPM: Copy to user failed.");
}
EXPORT_SYMBOL(sukisu_kpm_info);
-noinline NO_OPTIMIZE void sukisu_kpm_list(void __user *out, unsigned int bufferSize,
- void __user *result)
+noinline NO_OPTIMIZE void sukisu_kpm_list(void *out, int bufferSize,
+ int *result)
{
- int res = -1;
-
- printk("KPM: Stub function called (sukisu_kpm_list). "
+ pr_info("kpm: Stub function called (sukisu_kpm_list). "
"buffer=%p size=%d\n", out, bufferSize);
-
- if (copy_to_user(result, &res, sizeof(res)) < 1)
- printk("KPM: Copy to user failed.");
}
EXPORT_SYMBOL(sukisu_kpm_list);
-noinline NO_OPTIMIZE void sukisu_kpm_control(void __user *name, void __user *args,
- void __user *result)
+noinline NO_OPTIMIZE void sukisu_kpm_control(const char *name, const char *args, long arg_len,
+ int *result)
{
- int res = -1;
-
- printk("KPM: Stub function called (sukisu_kpm_control). "
- "name=%p args=%p\n", name, args);
+ pr_info("kpm: Stub function called (sukisu_kpm_control). "
+ "name=%p args=%p arg_len=%ld\n", name, args, arg_len);
__asm__ volatile("nop");
-
- if (copy_to_user(result, &res, sizeof(res)) < 1)
- printk("KPM: Copy to user failed.");
}
EXPORT_SYMBOL(sukisu_kpm_control);
-noinline NO_OPTIMIZE void sukisu_kpm_version(void __user *out, unsigned int bufferSize,
- void __user *result)
+noinline NO_OPTIMIZE void sukisu_kpm_version(char *buf, int bufferSize)
{
- int res = -1;
-
- printk("KPM: Stub function called (sukisu_kpm_version). "
- "buffer=%p size=%d\n", out, bufferSize);
-
- if (copy_to_user(result, &res, sizeof(res)) < 1)
- printk("KPM: Copy to user failed.");
+ pr_info("kpm: Stub function called (sukisu_kpm_version). "
+ "buffer=%p\n", buf);
}
EXPORT_SYMBOL(sukisu_kpm_version);
-noinline int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4,
- unsigned long arg5)
+noinline int sukisu_handle_kpm(unsigned long control_code, unsigned long arg1, unsigned long arg2,
+ unsigned long result_code)
{
- if (arg2 == SUKISU_KPM_LOAD) {
- char kernel_load_path[256] = { 0 };
- char kernel_args_buffer[256] = { 0 };
+ int res = -1;
+ if (control_code == SUKISU_KPM_LOAD) {
+ char kernel_load_path[256];
+ char kernel_args_buffer[256];
- if (arg3 == 0)
- return -1;
+ if (arg1 == 0) {
+ res = -EINVAL;
+ goto exit;
+ }
+
+ if (!access_ok(arg1, 255)) {
+ goto invalid_arg;
+ }
- strncpy_from_user((char *)&kernel_load_path, (const char __user *)arg3, 255);
+ strncpy_from_user((char *)&kernel_load_path, (const char *)arg1, 255);
- if (arg4 != 0)
- strncpy_from_user((char *)&kernel_args_buffer, (const char __user *)arg4, 255);
+ if (arg2 != 0) {
+ if (!access_ok(arg2, 255)) {
+ goto invalid_arg;
+ }
+
+ strncpy_from_user((char *)&kernel_args_buffer, (const char *)arg2, 255);
+ }
sukisu_kpm_load_module_path((const char *)&kernel_load_path,
- (const char *)&kernel_args_buffer, NULL, (void __user *)arg5);
- } else if (arg2 == SUKISU_KPM_UNLOAD) {
- char kernel_name_buffer[256] = { 0 };
+ (const char *)&kernel_args_buffer, NULL, &res);
+ } else if (control_code == SUKISU_KPM_UNLOAD) {
+ char kernel_name_buffer[256];
- if (arg3 == 0)
- return -1;
-
- strncpy_from_user((char *)&kernel_name_buffer, (const char __user *)arg3, 255);
-
- sukisu_kpm_unload_module((const char *)&kernel_name_buffer, NULL,
- (void __user *)arg5);
- } else if (arg2 == SUKISU_KPM_NUM) {
- sukisu_kpm_num((void __user *)arg5);
- } else if (arg2 == SUKISU_KPM_INFO) {
- char kernel_name_buffer[256] = { 0 };
+ if (arg1 == 0) {
+ res = -EINVAL;
+ goto exit;
+ }
- if (arg3 == 0 || arg4 == 0)
- return -1;
+ if (!access_ok(arg1, sizeof(kernel_name_buffer))) {
+ goto invalid_arg;
+ }
- strncpy_from_user((char *)&kernel_name_buffer, (const char __user *)arg3, 255);
+ strncpy_from_user((char *)&kernel_name_buffer, (const char *)arg1, sizeof(kernel_name_buffer));
- sukisu_kpm_info((const char *)&kernel_name_buffer, (char __user *)arg4,
- (void __user *)arg5);
- } else if (arg2 == SUKISU_KPM_LIST) {
- sukisu_kpm_list((char __user *)arg3, (unsigned int)arg4, (void __user *)arg5);
- } else if (arg2 == SUKISU_KPM_CONTROL) {
- sukisu_kpm_control((char __user *)arg3, (char __user *)arg4, (void __user *)arg5);
- } else if (arg2 == SUKISU_KPM_VERSION) {
- sukisu_kpm_version((char __user *)arg3, (unsigned int)arg4, (void __user *)arg5);
+ sukisu_kpm_unload_module((const char *)&kernel_name_buffer, NULL, &res);
+ } else if (control_code == SUKISU_KPM_NUM) {
+ sukisu_kpm_num(&res);
+ } else if (control_code == SUKISU_KPM_INFO) {
+ char kernel_name_buffer[256];
+ char buf[256];
+ int size;
+
+ if (arg1 == 0 || arg2 == 0) {
+ res = -EINVAL;
+ goto exit;
+ }
+
+ if (!access_ok(arg1, sizeof(kernel_name_buffer))) {
+ goto invalid_arg;
+ }
+
+ strncpy_from_user((char *)&kernel_name_buffer, (const char __user *)arg1, sizeof(kernel_name_buffer));
+
+ sukisu_kpm_info((const char *)&kernel_name_buffer, (char *)&buf, sizeof(buf), &size);
+
+ if (!access_ok(arg2, size)) {
+ goto invalid_arg;
+ }
+
+ res = copy_to_user(arg2, &buf, size);
+
+ } else if (control_code == SUKISU_KPM_LIST) {
+ char buf[1024];
+ int len = (int) arg2;
+
+ if (len <= 0) {
+ res = -EINVAL;
+ goto exit;
+ }
+
+ if (!access_ok(arg2, len)) {
+ goto invalid_arg;
+ }
+
+ sukisu_kpm_list((char *)&buf, sizeof(buf), &res);
+
+ if (res > len) {
+ res = -ENOBUFS;
+ goto exit;
+ }
+
+ if (copy_to_user(arg1, &buf, len) != 0)
+ pr_info("kpm: Copy to user failed.");
+
+ } else if (control_code == SUKISU_KPM_CONTROL) {
+ char kpm_name[KPM_NAME_LEN] = { 0 };
+ char kpm_args[KPM_ARGS_LEN] = { 0 };
+
+ if (!access_ok(arg1, sizeof(kpm_name))) {
+ goto invalid_arg;
+ }
+
+ if (!access_ok(arg2, sizeof(kpm_args))) {
+ goto invalid_arg;
+ }
+
+ long name_len = strncpy_from_user((char *)&kpm_name, (const char __user *)arg1, sizeof(kpm_name));
+ if (name_len <= 0) {
+ res = -EINVAL;
+ goto exit;
+ }
+
+ long arg_len = strncpy_from_user((char *)&kpm_args, (const char __user *)arg2, sizeof(kpm_args));
+
+ sukisu_kpm_control((const char *)&kpm_name, (const char *)&kpm_args, arg_len, &res);
+
+ } else if (control_code == SUKISU_KPM_VERSION) {
+ char buffer[256] = {0};
+
+ sukisu_kpm_version((char*) &buffer, sizeof(buffer));
+
+ unsigned int outlen = (unsigned int) arg2;
+ int len = strlen(buffer);
+ if (len >= outlen) len = outlen - 1;
+
+ res = copy_to_user(arg1, &buffer, len + 1);
}
+
+exit:
+ if (copy_to_user(result_code, &res, sizeof(res)) != 0)
+ pr_info("kpm: Copy to user failed.");
return 0;
+invalid_arg:
+ pr_err("kpm: invalid pointer detected! arg1: %px arg2: %px\n", (void *)arg1, (void *)arg2);
+ res = -EFAULT;
+ goto exit;
}
EXPORT_SYMBOL(sukisu_handle_kpm);
-int sukisu_is_kpm_control_code(unsigned long arg2) {
- return (arg2 >= CMD_KPM_CONTROL &&
- arg2 <= CMD_KPM_CONTROL_MAX) ? 1 : 0;
+int sukisu_is_kpm_control_code(unsigned long control_code) {
+ return (control_code >= CMD_KPM_CONTROL &&
+ control_code <= CMD_KPM_CONTROL_MAX) ? 1 : 0;
}
+
+int do_kpm(void __user *arg)
+{
+ struct ksu_kpm_cmd cmd;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ pr_err("kpm: copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ if (!access_ok(cmd.control_code, sizeof(int))) {
+ pr_err("kpm: invalid control_code pointer %px\n", (void *)cmd.control_code);
+ return -EFAULT;
+ }
+
+ if (!access_ok(cmd.result_code, sizeof(int))) {
+ pr_err("kpm: invalid result_code pointer %px\n", (void *)cmd.result_code);
+ return -EFAULT;
+ }
+
+ return sukisu_handle_kpm(cmd.control_code, cmd.arg1, cmd.arg2, cmd.result_code);
+}
+
diff --git a/kernel/kpm/kpm.h b/kernel/kpm/kpm.h
index e8349d3..4fdcc20 100644
--- a/kernel/kpm/kpm.h
+++ b/kernel/kpm/kpm.h
@@ -1,58 +1,70 @@
#ifndef __SUKISU_KPM_H
#define __SUKISU_KPM_H
-extern int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4,
- unsigned long arg5);
-extern int sukisu_is_kpm_control_code(unsigned long arg2);
+#include
+#include
+
+struct ksu_kpm_cmd {
+ __aligned_u64 __user control_code;
+ __aligned_u64 __user arg1;
+ __aligned_u64 __user arg2;
+ __aligned_u64 __user result_code;
+};
+
+int sukisu_handle_kpm(unsigned long control_code, unsigned long arg3, unsigned long arg4, unsigned long result_code);
+int sukisu_is_kpm_control_code(unsigned long control_code);
+int do_kpm(void __user *arg);
+
+#define KSU_IOCTL_KPM _IOC(_IOC_READ|_IOC_WRITE, 'K', 200, 0)
/* KPM Control Code */
-#define CMD_KPM_CONTROL 28
-#define CMD_KPM_CONTROL_MAX 35
+#define CMD_KPM_CONTROL 1
+#define CMD_KPM_CONTROL_MAX 10
/* Control Code */
/*
- * prctl(xxx, 28, "PATH", "ARGS")
+ * prctl(xxx, 1, "PATH", "ARGS")
* success return 0, error return -N
*/
-#define SUKISU_KPM_LOAD 28
+#define SUKISU_KPM_LOAD 1
/*
- * prctl(xxx, 29, "NAME")
+ * prctl(xxx, 2, "NAME")
* success return 0, error return -N
*/
-#define SUKISU_KPM_UNLOAD 29
+#define SUKISU_KPM_UNLOAD 2
/*
- * num = prctl(xxx, 30)
+ * num = prctl(xxx, 3)
* error return -N
* success return +num or 0
*/
-#define SUKISU_KPM_NUM 30
+#define SUKISU_KPM_NUM 3
/*
- * prctl(xxx, 31, Buffer, BufferSize)
+ * prctl(xxx, 4, Buffer, BufferSize)
* success return +out, error return -N
*/
-#define SUKISU_KPM_LIST 31
+#define SUKISU_KPM_LIST 4
/*
- * prctl(xxx, 32, "NAME", Buffer[256])
+ * prctl(xxx, 5, "NAME", Buffer[256])
* success return +out, error return -N
*/
-#define SUKISU_KPM_INFO 32
+#define SUKISU_KPM_INFO 5
/*
- * prctl(xxx, 33, "NAME", "ARGS")
+ * prctl(xxx, 6, "NAME", "ARGS")
* success return KPM's result value
* error return -N
*/
-#define SUKISU_KPM_CONTROL 33
+#define SUKISU_KPM_CONTROL 6
/*
- * prctl(xxx, 34, buffer, bufferSize)
+ * prctl(xxx, 7, buffer, bufferSize)
* success return KPM's result value
* error return -N
*/
-#define SUKISU_KPM_VERSION 34
+#define SUKISU_KPM_VERSION 7
#endif
diff --git a/kernel/ksu.c b/kernel/ksu.c
index 37a1754..850050c 100644
--- a/kernel/ksu.c
+++ b/kernel/ksu.c
@@ -3,89 +3,103 @@
#include
#include
#include
+#include
#include "allowlist.h"
-#include "arch.h"
-#include "core_hook.h"
+#include "feature.h"
#include "klog.h" // IWYU pragma: keep
-#include "ksu.h"
#include "throne_tracker.h"
+#include "syscall_hook_manager.h"
+#include "ksud.h"
+#include "supercalls.h"
+
+#include "sulog.h"
+#include "throne_comm.h"
+#include "dynamic_manager.h"
static struct workqueue_struct *ksu_workqueue;
bool ksu_queue_work(struct work_struct *work)
{
- return queue_work(ksu_workqueue, 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);
+void sukisu_custom_config_init(void)
+{
+}
-extern void ksu_sucompat_init();
-extern void ksu_sucompat_exit();
-extern void ksu_ksud_init();
-extern void ksu_ksud_exit();
-#ifdef CONFIG_KSU_TRACEPOINT_HOOK
-extern void ksu_trace_register();
-extern void ksu_trace_unregister();
+void sukisu_custom_config_exit(void)
+{
+ ksu_uid_exit();
+ ksu_throne_comm_exit();
+ ksu_dynamic_manager_exit();
+#if __SULOG_GATE
+ ksu_sulog_exit();
#endif
+}
int __init 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("*************************************************************");
+ 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
- ksu_core_init();
+ ksu_feature_init();
- ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
+ ksu_supercalls_init();
- ksu_allowlist_init();
+ sukisu_custom_config_init();
- ksu_throne_tracker_init();
-#ifdef CONFIG_KSU_KPROBES_HOOK
- ksu_sucompat_init();
- ksu_ksud_init();
+ ksu_syscall_hook_manager_init();
+
+ ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
+
+ ksu_allowlist_init();
+
+ ksu_throne_tracker_init();
+
+#ifdef KSU_KPROBES_HOOK
+ 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 CONFIG_KSU_TRACEPOINT_HOOK
- ksu_trace_register();
+ 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);
+ kobject_del(&THIS_MODULE->mkobj.kobj);
#endif
#endif
- return 0;
+ return 0;
}
+extern void ksu_observer_exit(void);
void kernelsu_exit(void)
{
- ksu_allowlist_exit();
+ ksu_allowlist_exit();
- ksu_throne_tracker_exit();
+ ksu_observer_exit();
- destroy_workqueue(ksu_workqueue);
+ ksu_throne_tracker_exit();
-#ifdef CONFIG_KSU_KPROBES_HOOK
- ksu_ksud_exit();
- ksu_sucompat_exit();
+ destroy_workqueue(ksu_workqueue);
+
+#ifdef KSU_KPROBES_HOOK
+ ksu_ksud_exit();
#endif
-#ifdef CONFIG_KSU_TRACEPOINT_HOOK
- ksu_trace_unregister();
-#endif
+ ksu_syscall_hook_manager_exit();
- ksu_core_exit();
+ sukisu_custom_config_exit();
+
+ ksu_supercalls_exit();
+
+ ksu_feature_exit();
}
module_init(kernelsu_init);
@@ -94,4 +108,9 @@ module_exit(kernelsu_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("weishu");
MODULE_DESCRIPTION("Android KernelSU");
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0)
+MODULE_IMPORT_NS("VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver");
+#else
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
+#endif
diff --git a/kernel/ksu.h b/kernel/ksu.h
index 5320180..93750af 100644
--- a/kernel/ksu.h
+++ b/kernel/ksu.h
@@ -7,40 +7,12 @@
#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 CMD_GET_FULL_VERSION 0xC0FFEE1A
-
-#define CMD_ENABLE_KPM 100
-#define CMD_HOOK_TYPE 101
-#define CMD_DYNAMIC_MANAGER 103
-#define CMD_GET_MANAGERS 104
+extern bool ksu_uid_scanner_enabled;
#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
-
// SukiSU Ultra kernel su version full strings
#ifndef KSU_VERSION_FULL
#define KSU_VERSION_FULL "v3.x-00000000@unknown"
@@ -51,6 +23,10 @@
#define DYNAMIC_MANAGER_OP_GET 1
#define DYNAMIC_MANAGER_OP_CLEAR 2
+#define UID_SCANNER_OP_GET_STATUS 0
+#define UID_SCANNER_OP_TOGGLE 1
+#define UID_SCANNER_OP_CLEAR_ENV 2
+
struct dynamic_manager_user_config {
unsigned int operation;
unsigned int size;
@@ -65,68 +41,22 @@ struct manager_list_info {
} managers[2];
};
-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);
+#if 0
static inline int startswith(char *s, char *prefix)
{
- return strncmp(s, prefix, strlen(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);
+ size_t slen = strlen(s);
+ size_t tlen = strlen(t);
+ if (tlen > slen)
+ return 1;
+ return strcmp(s + slen - tlen, t);
}
+#endif
#endif
diff --git a/kernel/ksu_trace.c b/kernel/ksu_trace.c
deleted file mode 100644
index 5acf092..0000000
--- a/kernel/ksu_trace.c
+++ /dev/null
@@ -1,69 +0,0 @@
-#include "ksu_trace.h"
-
-
-// extern kernelsu functions
-extern bool ksu_vfs_read_hook __read_mostly;
-extern bool ksu_input_hook __read_mostly;
-extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags);
-extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int *flags);
-extern int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr, size_t *count_ptr);
-extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
-extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
-// end kernelsu functions
-
-
-// tracepoint callback functions
-void ksu_trace_execveat_sucompat_hook_callback(void *data, int *fd, struct filename **filename_ptr,
- void *argv, void *envp, int *flags)
-{
- ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, flags);
-}
-
-void ksu_trace_faccessat_hook_callback(void *data, int *dfd, const char __user **filename_user,
- int *mode, int *flags)
-{
- ksu_handle_faccessat(dfd, filename_user, mode, flags);
-}
-
-void ksu_trace_sys_read_hook_callback(void *data, unsigned int fd, char __user **buf_ptr,
- size_t *count_ptr)
-{
- if (unlikely(ksu_vfs_read_hook))
- ksu_handle_sys_read(fd, buf_ptr, count_ptr);
-}
-
-void ksu_trace_stat_hook_callback(void *data, int *dfd, const char __user **filename_user,
- int *flags)
-{
- ksu_handle_stat(dfd, filename_user, flags);
-}
-
-void ksu_trace_input_hook_callback(void *data, unsigned int *type, unsigned int *code,
- int *value)
-{
- if (unlikely(ksu_input_hook))
- ksu_handle_input_handle_event(type, code, value);
-}
-
-// end tracepoint callback functions
-
-
-// register tracepoint callback functions
-void ksu_trace_register(void)
-{
- register_trace_ksu_trace_execveat_sucompat_hook(ksu_trace_execveat_sucompat_hook_callback, NULL);
- register_trace_ksu_trace_faccessat_hook(ksu_trace_faccessat_hook_callback, NULL);
- register_trace_ksu_trace_sys_read_hook(ksu_trace_sys_read_hook_callback, NULL);
- register_trace_ksu_trace_stat_hook(ksu_trace_stat_hook_callback, NULL);
- register_trace_ksu_trace_input_hook(ksu_trace_input_hook_callback, NULL);
-}
-
-// unregister tracepoint callback functions
-void ksu_trace_unregister(void)
-{
- unregister_trace_ksu_trace_execveat_sucompat_hook(ksu_trace_execveat_sucompat_hook_callback, NULL);
- unregister_trace_ksu_trace_faccessat_hook(ksu_trace_faccessat_hook_callback, NULL);
- unregister_trace_ksu_trace_sys_read_hook(ksu_trace_sys_read_hook_callback, NULL);
- unregister_trace_ksu_trace_stat_hook(ksu_trace_stat_hook_callback, NULL);
- unregister_trace_ksu_trace_input_hook(ksu_trace_input_hook_callback, NULL);
-}
diff --git a/kernel/ksu_trace.h b/kernel/ksu_trace.h
deleted file mode 100644
index dc5394b..0000000
--- a/kernel/ksu_trace.h
+++ /dev/null
@@ -1,37 +0,0 @@
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM ksu_trace
-
-#if !defined(_KSU_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
-#define _KSU_TRACE_H
-
-#include
-#include
-
-DECLARE_TRACE(ksu_trace_execveat_sucompat_hook,
- TP_PROTO(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags),
- TP_ARGS(fd, filename_ptr, argv, envp, flags));
-
-DECLARE_TRACE(ksu_trace_faccessat_hook,
- TP_PROTO(int *dfd, const char __user **filename_user, int *mode, int *flags),
- TP_ARGS(dfd, filename_user, mode, flags));
-
-DECLARE_TRACE(ksu_trace_sys_read_hook,
- TP_PROTO(unsigned int fd, char __user **buf_ptr, size_t *count_ptr),
- TP_ARGS(fd, buf_ptr, count_ptr));
-
-DECLARE_TRACE(ksu_trace_stat_hook,
- TP_PROTO(int *dfd, const char __user **filename_user, int *flags),
- TP_ARGS(dfd, filename_user, flags));
-
-DECLARE_TRACE(ksu_trace_input_hook,
- TP_PROTO(unsigned int *type, unsigned int *code, int *value),
- TP_ARGS(type, code, value));
-
-#endif /* _KSU_TRACE_H */
-
-#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH .
-#undef TRACE_INCLUDE_FILE
-#define TRACE_INCLUDE_FILE ksu_trace
-
-#include
diff --git a/kernel/ksu_trace_export.c b/kernel/ksu_trace_export.c
deleted file mode 100644
index afa4472..0000000
--- a/kernel/ksu_trace_export.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#define CREATE_TRACE_POINTS
-#include "ksu_trace.h"
-
-EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_execveat_sucompat_hook);
-EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_faccessat_hook);
-EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_sys_read_hook);
-EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_stat_hook);
-EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_input_hook);
diff --git a/kernel/ksud.c b/kernel/ksud.c
index f121b62..4431231 100644
--- a/kernel/ksud.c
+++ b/kernel/ksud.c
@@ -1,3 +1,6 @@
+#include
+#include
+#include
#include
#include
#include
@@ -11,226 +14,318 @@
#include
#include
#include
+#include
#include
+#include "manager.h"
#include "allowlist.h"
#include "arch.h"
#include "klog.h" // IWYU pragma: keep
#include "ksud.h"
-#include "kernel_compat.h"
#include "selinux/selinux.h"
+#include "throne_tracker.h"
+bool ksu_module_mounted __read_mostly = false;
+bool ksu_boot_completed __read_mostly = false;
static const char KERNEL_SU_RC[] =
- "\n"
+ "\n"
- "on post-fs-data\n"
- " start logd\n"
- // We should wait for the post-fs-data finish
- " exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n"
- "\n"
+ "on post-fs-data\n"
+ " start logd\n"
+ // We should wait for the post-fs-data finish
+ " 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 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: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"
+ "on property:sys.boot_completed=1\n"
+ " exec u:r:su:s0 root -- " KSUD_PATH " boot-completed\n"
+ "\n"
- "\n";
+ "\n";
static void stop_vfs_read_hook();
static void stop_execve_hook();
static void stop_input_hook();
-#ifdef CONFIG_KSU_KPROBES_HOOK
+#ifdef KSU_KPROBES_HOOK
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
-bool ksu_execveat_hook __read_mostly = true;
-u32 ksu_devpts_sid;
+u32 ksu_file_sid;
// Detect whether it is on or not
static bool is_boot_phase = true;
void on_post_fs_data(void)
{
- static bool done = false;
- if (done) {
- pr_info("on_post_fs_data already done\n");
- return;
- }
- done = true;
- pr_info("on_post_fs_data!\n");
- ksu_load_allow_list();
- // sanity check, this may influence the performance
- stop_input_hook();
+ static bool done = false;
+ if (done) {
+ pr_info("on_post_fs_data already done\n");
+ return;
+ }
+ done = true;
+ pr_info("on_post_fs_data!\n");
+ ksu_load_allow_list();
+ ksu_observer_init();
+ // sanity check, this may influence the performance
+ stop_input_hook();
- ksu_devpts_sid = ksu_get_devpts_sid();
- pr_info("devpts sid: %d\n", ksu_devpts_sid);
-
- // End of boot state
+ // End of boot state
is_boot_phase = false;
+
+ ksu_file_sid = ksu_get_ksu_file_sid();
+ pr_info("ksu_file sid: %d\n", ksu_file_sid);
}
-// since _ksud handler only uses argv and envp for comparisons
-// this can probably work
-// adapted from ksu_handle_execveat_ksud
-static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const char *envp, size_t envp_len)
+extern void ext4_unregister_sysfs(struct super_block *sb);
+static void nuke_ext4_sysfs(void)
{
- static const char app_process[] = "/system/bin/app_process";
- static bool first_app_process = true;
+#ifdef CONFIG_EXT4_FS
+ struct path path;
+ int err = kern_path("/data/adb/modules", 0, &path);
+ if (err) {
+ pr_err("nuke path err: %d\n", err);
+ return;
+ }
- /* 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;
+ struct super_block *sb = path.dentry->d_inode->i_sb;
+ const char *name = sb->s_type->name;
+ if (strcmp(name, "ext4") != 0) {
+ pr_info("nuke but module aren't mounted\n");
+ path_put(&path);
+ return;
+ }
- // return early when disabled
- if (!ksu_execveat_hook)
- return 0;
+ ext4_unregister_sysfs(sb);
+ path_put(&path);
+#endif
+}
- if (!filename)
- return 0;
+void on_module_mounted(void){
+ pr_info("on_module_mounted!\n");
+ ksu_module_mounted = true;
+ nuke_ext4_sysfs();
+}
- // debug! remove me!
- pr_info("%s: filename: %s argv1: %s envp_len: %zu\n", __func__, filename, argv1, envp_len);
+void on_boot_completed(void){
+ ksu_boot_completed = true;
+ pr_info("on_boot_completed!\n");
+ track_throne(true);
+}
-#ifdef CONFIG_KSU_DEBUG
- const char *envp_n = envp;
- unsigned int envc = 1;
- do {
- pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n);
- envp_n += strlen(envp_n) + 1;
- envc++;
- } while (envp_n < envp + 256);
+#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 (init_second_stage_executed)
- goto first_app_process;
+ if (get_user(native, argv.ptr.native + nr))
+ return ERR_PTR(-EFAULT);
- // /system/bin/init with argv1
- if (!init_second_stage_executed
- && (!memcmp(filename, system_bin_init, sizeof(system_bin_init) - 1))) {
- if (argv1 && !strcmp(argv1, "second_stage")) {
- pr_info("%s: /system/bin/init second_stage executed\n", __func__);
- apply_kernelsu_rules();
- init_second_stage_executed = true;
- ksu_android_ns_fs_check();
- }
- }
-
- // /init with argv1
- if (!init_second_stage_executed
- && (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) {
- if (argv1 && !strcmp(argv1, "--second-stage")) {
- pr_info("%s: /init --second-stage executed\n", __func__);
- apply_kernelsu_rules();
- init_second_stage_executed = true;
- ksu_android_ns_fs_check();
- }
- }
-
- if (!envp || !envp_len)
- goto first_app_process;
-
- // /init without argv1/useless-argv1 but usable envp
- // untested! TODO: test and debug me!
- if (!init_second_stage_executed && (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) {
-
- // we hunt for "INIT_SECOND_STAGE"
- const char *envp_n = envp;
- unsigned int envc = 1;
- do {
- if (strstarts(envp_n, "INIT_SECOND_STAGE"))
- break;
- envp_n += strlen(envp_n) + 1;
- envc++;
- } while (envp_n < envp + envp_len);
- pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n);
-
- if (!strcmp(envp_n, "INIT_SECOND_STAGE=1")
- || !strcmp(envp_n, "INIT_SECOND_STAGE=true") ) {
- pr_info("%s: /init +envp: INIT_SECOND_STAGE executed\n", __func__);
- apply_kernelsu_rules();
- init_second_stage_executed = true;
- ksu_android_ns_fs_check();
- }
- }
-
-first_app_process:
- if (first_app_process && !memcmp(filename, app_process, sizeof(app_process) - 1)) {
- first_app_process = false;
- pr_info("%s: exec app_process, /data prepared, second_stage: %d\n", __func__, init_second_stage_executed);
- on_post_fs_data();
- stop_execve_hook();
- }
-
- return 0;
+ return native;
}
-int ksu_handle_pre_ksud(const char *filename)
+/*
+ * 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)
{
- if (likely(!ksu_execveat_hook))
- return 0;
+ int i = 0;
- // not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy)
- // return 0;
- if (likely(strcmp(filename, "/system/bin/init") && strcmp(filename, "/init")
- && !strstarts(filename, "/system/bin/app_process") ))
- return 0;
+ if (argv.ptr.native != NULL) {
+ for (;;) {
+ const char __user *p = get_user_arg_ptr(argv, i);
- if (!current || !current->mm)
- return 0;
+ if (!p)
+ break;
- // https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/mm_types.h#L429
- // unsigned long arg_start, arg_end, env_start, env_end;
- unsigned long arg_start = current->mm->arg_start;
- unsigned long arg_end = current->mm->arg_end;
- unsigned long env_start = current->mm->env_start;
- unsigned long env_end = current->mm->env_end;
+ if (IS_ERR(p))
+ return -EFAULT;
- size_t arg_len = arg_end - arg_start;
- size_t envp_len = env_end - env_start;
+ if (i >= max)
+ return -E2BIG;
+ ++i;
- if (arg_len <= 0 || envp_len <= 0) // this wont make sense, filter it
- return 0;
+ if (fatal_signal_pending(current))
+ return -ERESTARTNOHAND;
+ }
+ }
+ return i;
+}
- #define ARGV_MAX 32 // this is enough for argv1
- #define ENVP_MAX 256 // this is enough for INIT_SECOND_STAGE
- char args[ARGV_MAX];
- size_t argv_copy_len = (arg_len > ARGV_MAX) ? ARGV_MAX : arg_len;
- char envp[ENVP_MAX];
- size_t envp_copy_len = (envp_len > ENVP_MAX) ? ENVP_MAX : envp_len;
+static void on_post_fs_data_cbfun(struct callback_head *cb)
+{
+ on_post_fs_data();
+}
- // we cant use strncpy on here, else it will truncate once it sees \0
- if (ksu_copy_from_user_retry(args, (void __user *)arg_start, argv_copy_len))
- return 0;
+static struct callback_head on_post_fs_data_cb = { .func =
+ on_post_fs_data_cbfun };
- if (ksu_copy_from_user_retry(envp, (void __user *)env_start, envp_copy_len))
- return 0;
+// 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_KPROBES_HOOK
+ if (!ksu_execveat_hook) {
+ return 0;
+ }
+#endif
+ struct filename *filename;
- args[ARGV_MAX - 1] = '\0';
- envp[ENVP_MAX - 1] = '\0';
+ static const char app_process[] = "/system/bin/app_process";
+ static bool first_app_process = true;
- // we only need argv1 !
- // abuse strlen here since it only gets length up to \0
- char *argv1 = args + strlen(args) + 1;
- if (argv1 >= args + argv_copy_len) // out of bounds!
- argv1 = "";
+ /* 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;
- return ksu_handle_bprm_ksud(filename, argv1, envp, envp_copy_len);
+ 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];
+ 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");
+ apply_kernelsu_rules();
+ init_second_stage_executed = true;
+ }
+ } 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];
+ 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");
+ apply_kernelsu_rules();
+ init_second_stage_executed = true;
+ }
+ } 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 (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");
+ apply_kernelsu_rules();
+ init_second_stage_executed = true;
+ }
+ }
+ }
+ }
+ }
+
+ 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);
+ struct task_struct *init_task;
+ rcu_read_lock();
+ init_task = rcu_dereference(current->real_parent);
+ if (init_task) {
+ task_work_add(init_task, &on_post_fs_data_cb, TWA_RESUME);
+ }
+ rcu_read_unlock();
+
+ stop_execve_hook();
+ }
+
+ return 0;
}
static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *);
@@ -239,426 +334,326 @@ 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)
+ 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;
+ 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;
+ 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)
+static int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
+ size_t *count_ptr, loff_t **pos)
{
-#ifndef CONFIG_KSU_KPROBES_HOOK
- if (!ksu_vfs_read_hook) {
- return 0;
- }
+#ifndef KSU_KPROBES_HOOK
+ if (!ksu_vfs_read_hook) {
+ return 0;
+ }
#endif
- struct file *file;
- char __user *buf;
- size_t count;
+ struct file *file;
+ char __user *buf;
+ size_t count;
- if (strcmp(current->comm, "init")) {
- // we are only interest in `init` process
- return 0;
- }
+ if (strcmp(current->comm, "init")) {
+ // we are only interest in `init` process
+ return 0;
+ }
- file = *file_ptr;
- if (IS_ERR(file)) {
- return 0;
- }
+ file = *file_ptr;
+ if (IS_ERR(file)) {
+ return 0;
+ }
- if (!d_is_reg(file->f_path.dentry)) {
- 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));
+ 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 (IS_ERR(dpath)) {
+ return 0;
+ }
- if (strcmp(dpath, "/system/etc/init/atrace.rc")) {
- 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;
+ // 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;
+ // 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);
+ 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);
+ 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;
- }
+ 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;
- }
+ 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;
+ // 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;
+ *buf_ptr = buf + rc_count;
+ *count_ptr = count - rc_count;
- return 0;
+ return 0;
}
int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr,
- size_t *count_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;
+ 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;
+ return count >= 3;
}
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
- int *value)
+ int *value)
{
-#ifndef CONFIG_KSU_KPROBES_HOOK
- if (!ksu_input_hook) {
- return 0;
- }
+#ifndef KSU_KPROBES_HOOK
+ 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) {
- // key pressed, count it
- volumedown_pressed_count += 1;
- if (is_volumedown_enough(volumedown_pressed_count)) {
- stop_input_hook();
- }
- }
- }
+ if (*type == EV_KEY && *code == KEY_VOLUMEDOWN) {
+ int val = *value;
+ pr_info("KEY_VOLUMEDOWN val: %d\n", val);
+ if (val && is_boot_phase) {
+ // key pressed, count it
+ volumedown_pressed_count += 1;
+ if (is_volumedown_enough(volumedown_pressed_count)) {
+ stop_input_hook();
+ }
+ }
+ }
- return 0;
+ 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;
- }
+ 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();
+ // 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;
- }
+ 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;
+ return false;
}
-#ifdef CONFIG_KSU_KPROBES_HOOK
+#ifdef KSU_KPROBES_HOOK
+
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
- /*
- asmlinkage int sys_execve(const char __user *filenamei,
- const char __user *const __user *argv,
- const char __user *const __user *envp, struct pt_regs *regs)
- */
- struct pt_regs *real_regs = PT_REAL_REGS(regs);
- const char __user *filename_user = (const char __user *)PT_REGS_PARM1(real_regs);
- const char __user *const __user *__argv = (const char __user *const __user *)PT_REGS_PARM2(real_regs);
- const char __user *const __user *__envp = (const char __user *const __user *)PT_REGS_PARM3(real_regs);
- char path[32];
+ 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;
+ if (!filename_user)
+ return 0;
-// filename stage
- if (ksu_copy_from_user_retry(path, filename_user, sizeof(path)))
- return 0;
+ memset(path, 0, sizeof(path));
+ strncpy_from_user_nofault(path, *filename_user, 32);
+ filename_in.name = path;
- path[sizeof(path) - 1] = '\0';
-
- // not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy)
- // we dont care !!
- if (likely(strcmp(path, "/system/bin/init") && strcmp(path, "/init")
- && !strstarts(path, "/system/bin/app_process") ))
- return 0;
-
-// argv stage
- char argv1[32] = {0};
- // memzero_explicit(argv1, 32);
- if (__argv) {
- const char __user *arg1_user = NULL;
- // grab argv[1] pointer
- // this looks like
- /*
- * 0x1000 ./program << this is __argv
- * 0x1001 -o
- * 0x1002 arg
- */
- if (ksu_copy_from_user_retry(&arg1_user, __argv + 1, sizeof(arg1_user)))
- goto no_argv1; // copy argv[1] pointer fail, probably no argv1 !!
-
- if (arg1_user)
- ksu_copy_from_user_retry(argv1, arg1_user, sizeof(argv1));
- }
-
-no_argv1:
- argv1[sizeof(argv1) - 1] = '\0';
-
-// envp stage
- #define ENVP_MAX 256
- char envp[ENVP_MAX] = {0};
- char *dst = envp;
- size_t envp_len = 0;
- int i = 0; // to track user pointer offset from __envp
-
- // memzero_explicit(envp, ENVP_MAX);
-
- if (__envp) {
- do {
- const char __user *env_entry_user = NULL;
- // this is also like argv above
- /*
- * 0x1001 PATH=/bin
- * 0x1002 VARIABLE=value
- * 0x1002 some_more_env_var=1
- */
-
- // check if pointer exists
- if (ksu_copy_from_user_retry(&env_entry_user, __envp + i, sizeof(env_entry_user)))
- break;
-
- // check if no more env entry
- if (!env_entry_user)
- break;
-
- // probably redundant to while condition but ok
- if (envp_len >= ENVP_MAX - 1)
- break;
-
- // copy strings from env_entry_user pointer that we collected
- // also break if failed
- if (ksu_copy_from_user_retry(dst, env_entry_user, ENVP_MAX - envp_len))
- break;
-
- // get the length of that new copy above
- // get lngth of dst as far as ENVP_MAX - current collected envp_len
- size_t len = strnlen(dst, ENVP_MAX - envp_len);
- if (envp_len + len + 1 > ENVP_MAX)
- break; // if more than 255 bytes, bail
-
- dst[len] = '\0';
- // collect total number of copied strings
- envp_len = envp_len + len + 1;
- // increment dst address since we need to put something on next iter
- dst = dst + len + 1;
- // pointer walk, __envp + i
- i++;
- } while (envp_len < ENVP_MAX);
- }
-
- /*
- at this point, we shoul've collected envp from
- * 0x1001 PATH=/bin
- * 0x1002 VARIABLE=value
- * 0x1002 some_more_env_var=1
- to
- * 0x1234 PATH=/bin\0VARIABLE=value\0some_more_env_var=1\0\0\0\0
- */
-
- envp[ENVP_MAX - 1] = '\0';
-
-#ifdef CONFIG_KSU_DEBUG
- pr_info("%s: filename: %s argv[1]:%s envp_len: %zu\n", __func__, path, argv1, envp_len);
-#endif
- return ksu_handle_bprm_ksud(path, argv1, envp, envp_len);
+ filename_p = &filename_in;
+ return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL, NULL);
}
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);
+ 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);
+ return ksu_handle_sys_read(fd, buf_ptr, count_ptr);
}
static int input_handle_event_handler_pre(struct kprobe *p,
- struct pt_regs *regs)
+ 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);
+ 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);
}
static struct kprobe execve_kp = {
- .symbol_name = SYS_EXECVE_SYMBOL,
- .pre_handler = sys_execve_handler_pre,
+ .symbol_name = SYS_EXECVE_SYMBOL,
+ .pre_handler = sys_execve_handler_pre,
};
static struct kprobe vfs_read_kp = {
- .symbol_name = SYS_READ_SYMBOL,
- .pre_handler = sys_read_handler_pre,
+ .symbol_name = SYS_READ_SYMBOL,
+ .pre_handler = sys_read_handler_pre,
};
static struct kprobe input_event_kp = {
- .symbol_name = "input_event",
- .pre_handler = input_handle_event_handler_pre,
+ .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);
+ unregister_kprobe(&vfs_read_kp);
}
static void do_stop_execve_hook(struct work_struct *work)
{
- unregister_kprobe(&execve_kp);
+ unregister_kprobe(&execve_kp);
}
static void do_stop_input_hook(struct work_struct *work)
{
- unregister_kprobe(&input_event_kp);
+ unregister_kprobe(&input_event_kp);
}
#endif
static void stop_vfs_read_hook()
{
-#ifdef CONFIG_KSU_KPROBES_HOOK
- bool ret = schedule_work(&stop_vfs_read_work);
- pr_info("unregister vfs_read kprobe: %d!\n", ret);
+#ifdef KSU_KPROBES_HOOK
+ 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");
+ ksu_vfs_read_hook = false;
+ pr_info("stop vfs_read_hook\n");
#endif
}
static void stop_execve_hook()
{
-#ifdef CONFIG_KSU_KPROBES_HOOK
- bool ret = schedule_work(&stop_execve_hook_work);
- pr_info("unregister execve kprobe: %d!\n", ret);
+#ifdef KSU_KPROBES_HOOK
+ bool ret = schedule_work(&stop_execve_hook_work);
+ pr_info("unregister execve kprobe: %d!\n", ret);
#else
- pr_info("stop execve_hook\n");
+ ksu_execveat_hook = false;
+ pr_info("stop execve_hook\n");
#endif
- ksu_execveat_hook = false;
}
static void stop_input_hook()
{
- static bool input_hook_stopped = false;
- if (input_hook_stopped) {
- return;
- }
- input_hook_stopped = true;
-#ifdef CONFIG_KSU_KPROBES_HOOK
- bool ret = schedule_work(&stop_input_hook_work);
- pr_info("unregister input kprobe: %d!\n", ret);
+ static bool input_hook_stopped = false;
+ if (input_hook_stopped) {
+ return;
+ }
+ input_hook_stopped = true;
+#ifdef KSU_KPROBES_HOOK
+ bool ret = schedule_work(&stop_input_hook_work);
+ pr_info("unregister input kprobe: %d!\n", ret);
#else
- ksu_input_hook = false;
- pr_info("stop input_hook\n");
+ ksu_input_hook = false;
+ pr_info("stop input_hook\n");
#endif
}
// ksud: module support
void ksu_ksud_init()
{
-#ifdef CONFIG_KSU_KPROBES_HOOK
- int ret;
+#ifdef KSU_KPROBES_HOOK
+ int ret;
- ret = register_kprobe(&execve_kp);
- pr_info("ksud: execve_kp: %d\n", 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(&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);
+ 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);
+ 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 CONFIG_KSU_KPROBES_HOOK
- unregister_kprobe(&execve_kp);
- // this should be done before unregister vfs_read_kp
- // unregister_kprobe(&vfs_read_kp);
- unregister_kprobe(&input_event_kp);
+#ifdef KSU_KPROBES_HOOK
+ 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;
+ is_boot_phase = false;
}
diff --git a/kernel/ksud.h b/kernel/ksud.h
index 7dd4cd0..271c354 100644
--- a/kernel/ksud.h
+++ b/kernel/ksud.h
@@ -3,13 +3,17 @@
#define KSUD_PATH "/data/adb/ksud"
+void ksu_ksud_init();
+void ksu_ksud_exit();
+
void on_post_fs_data(void);
+void on_module_mounted(void);
+void on_boot_completed(void);
bool ksu_is_safe_mode(void);
-extern u32 ksu_devpts_sid;
-
-extern bool ksu_execveat_hook __read_mostly;
-extern int ksu_handle_pre_ksud(const char *filename);
+extern u32 ksu_file_sid;
+extern bool ksu_module_mounted;
+extern bool ksu_boot_completed;
#endif
diff --git a/kernel/manager.h b/kernel/manager.h
index f27afbb..2421da7 100644
--- a/kernel/manager.h
+++ b/kernel/manager.h
@@ -13,30 +13,31 @@ extern void ksu_add_manager(uid_t uid, int signature_index);
extern void ksu_remove_manager(uid_t uid);
extern int ksu_get_manager_signature_index(uid_t uid);
-static inline bool ksu_is_manager_uid_valid()
+static inline bool ksu_is_manager_uid_valid(void)
{
- return ksu_manager_uid != KSU_INVALID_UID;
+ return ksu_manager_uid != KSU_INVALID_UID;
}
-static inline bool is_manager()
+static inline bool is_manager(void)
{
- return unlikely(ksu_is_any_manager(current_uid().val) ||
- (ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val));
+ return unlikely(ksu_is_any_manager(current_uid().val) ||
+ (ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val));
}
-static inline uid_t ksu_get_manager_uid()
+static inline uid_t ksu_get_manager_uid(void)
{
- return ksu_manager_uid;
+ return ksu_manager_uid;
}
static inline void ksu_set_manager_uid(uid_t uid)
{
- ksu_manager_uid = uid;
+ ksu_manager_uid = uid;
}
-static inline void ksu_invalidate_manager_uid()
+static inline void ksu_invalidate_manager_uid(void)
{
- ksu_manager_uid = KSU_INVALID_UID;
+ ksu_manager_uid = KSU_INVALID_UID;
}
-#endif
\ No newline at end of file
+int ksu_observer_init(void);
+#endif
diff --git a/kernel/manager_sign.h b/kernel/manager_sign.h
index b7a8e8c..265ef35 100644
--- a/kernel/manager_sign.h
+++ b/kernel/manager_sign.h
@@ -9,5 +9,9 @@
#define EXPECTED_SIZE_OTHER 0x300
#define EXPECTED_HASH_OTHER "0000000000000000000000000000000000000000000000000000000000000000"
+typedef struct {
+ unsigned size;
+ const char *sha256;
+} apk_sign_key_t;
-#endif /* MANAGER_SIGN_H */
\ No newline at end of file
+#endif /* MANAGER_SIGN_H */
diff --git a/kernel/manual_su.c b/kernel/manual_su.c
new file mode 100644
index 0000000..c132d31
--- /dev/null
+++ b/kernel/manual_su.c
@@ -0,0 +1,356 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "manual_su.h"
+#include "ksu.h"
+#include "allowlist.h"
+#include "manager.h"
+#include "app_profile.h"
+
+static bool current_verified = false;
+static void ksu_cleanup_expired_tokens(void);
+static bool is_current_verified(void);
+static void add_pending_root(uid_t uid);
+
+static struct pending_uid pending_uids[MAX_PENDING] = {0};
+static int pending_cnt = 0;
+static struct ksu_token_entry auth_tokens[MAX_TOKENS] = {0};
+static int token_count = 0;
+static DEFINE_SPINLOCK(token_lock);
+
+static char* get_token_from_envp(void)
+{
+ struct mm_struct *mm;
+ char *envp_start, *envp_end;
+ char *env_ptr, *token = NULL;
+ unsigned long env_len;
+ char *env_copy = NULL;
+
+ if (!current->mm)
+ return NULL;
+
+ mm = current->mm;
+
+ down_read(&mm->mmap_lock);
+
+ envp_start = (char *)mm->env_start;
+ envp_end = (char *)mm->env_end;
+ env_len = envp_end - envp_start;
+
+ if (env_len <= 0 || env_len > PAGE_SIZE * 32) {
+ up_read(&mm->mmap_lock);
+ return NULL;
+ }
+
+ env_copy = kmalloc(env_len + 1, GFP_KERNEL);
+ if (!env_copy) {
+ up_read(&mm->mmap_lock);
+ return NULL;
+ }
+
+ if (copy_from_user(env_copy, envp_start, env_len)) {
+ kfree(env_copy);
+ up_read(&mm->mmap_lock);
+ return NULL;
+ }
+
+ up_read(&mm->mmap_lock);
+
+ env_copy[env_len] = '\0';
+ env_ptr = env_copy;
+
+ while (env_ptr < env_copy + env_len) {
+ if (strncmp(env_ptr, KSU_TOKEN_ENV_NAME "=", strlen(KSU_TOKEN_ENV_NAME) + 1) == 0) {
+ char *token_start = env_ptr + strlen(KSU_TOKEN_ENV_NAME) + 1;
+ char *token_end = strchr(token_start, '\0');
+
+ if (token_end && (token_end - token_start) == KSU_TOKEN_LENGTH) {
+ token = kmalloc(KSU_TOKEN_LENGTH + 1, GFP_KERNEL);
+ if (token) {
+ memcpy(token, token_start, KSU_TOKEN_LENGTH);
+ token[KSU_TOKEN_LENGTH] = '\0';
+ pr_info("manual_su: found auth token in environment\n");
+ }
+ }
+ break;
+ }
+
+ env_ptr += strlen(env_ptr) + 1;
+ }
+
+ kfree(env_copy);
+ return token;
+}
+
+static char* ksu_generate_auth_token(void)
+{
+ static char token_buffer[KSU_TOKEN_LENGTH + 1];
+ unsigned long flags;
+ int i;
+
+ ksu_cleanup_expired_tokens();
+
+ spin_lock_irqsave(&token_lock, flags);
+
+ if (token_count >= MAX_TOKENS) {
+ for (i = 0; i < MAX_TOKENS - 1; i++) {
+ auth_tokens[i] = auth_tokens[i + 1];
+ }
+ token_count = MAX_TOKENS - 1;
+ }
+
+ for (i = 0; i < KSU_TOKEN_LENGTH; i++) {
+ u8 rand_byte;
+ get_random_bytes(&rand_byte, 1);
+ int char_type = rand_byte % 3;
+ if (char_type == 0) {
+ token_buffer[i] = 'A' + (rand_byte % 26);
+ } else if (char_type == 1) {
+ token_buffer[i] = 'a' + (rand_byte % 26);
+ } else {
+ token_buffer[i] = '0' + (rand_byte % 10);
+ }
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
+ strscpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1);
+#else
+ strlcpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1);
+#endif
+ auth_tokens[token_count].expire_time = jiffies + KSU_TOKEN_EXPIRE_TIME * HZ;
+ auth_tokens[token_count].used = false;
+ token_count++;
+
+ spin_unlock_irqrestore(&token_lock, flags);
+
+ pr_info("manual_su: generated new auth token (expires in %d seconds)\n", KSU_TOKEN_EXPIRE_TIME);
+ return token_buffer;
+}
+
+static bool ksu_verify_auth_token(const char *token)
+{
+ unsigned long flags;
+ bool valid = false;
+ int i;
+
+ if (!token || strlen(token) != KSU_TOKEN_LENGTH) {
+ return false;
+ }
+
+ spin_lock_irqsave(&token_lock, flags);
+
+ for (i = 0; i < token_count; i++) {
+ if (!auth_tokens[i].used &&
+ time_before(jiffies, auth_tokens[i].expire_time) &&
+ strcmp(auth_tokens[i].token, token) == 0) {
+
+ auth_tokens[i].used = true;
+ valid = true;
+ pr_info("manual_su: auth token verified successfully\n");
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&token_lock, flags);
+
+ if (!valid) {
+ pr_warn("manual_su: invalid or expired auth token\n");
+ }
+
+ return valid;
+}
+
+static void ksu_cleanup_expired_tokens(void)
+{
+ unsigned long flags;
+ int i, j;
+
+ spin_lock_irqsave(&token_lock, flags);
+
+ for (i = 0; i < token_count; ) {
+ if (time_after(jiffies, auth_tokens[i].expire_time) || auth_tokens[i].used) {
+ for (j = i; j < token_count - 1; j++) {
+ auth_tokens[j] = auth_tokens[j + 1];
+ }
+ token_count--;
+ pr_debug("manual_su: cleaned up expired/used token\n");
+ } else {
+ i++;
+ }
+ }
+
+ spin_unlock_irqrestore(&token_lock, flags);
+}
+
+static int handle_token_generation(struct manual_su_request *request)
+{
+ if (current_uid().val > 2000) {
+ pr_warn("manual_su: token generation denied for app UID %d\n", current_uid().val);
+ return -EPERM;
+ }
+
+ char *new_token = ksu_generate_auth_token();
+ if (!new_token) {
+ pr_err("manual_su: failed to generate token\n");
+ return -ENOMEM;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
+ strscpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1);
+#else
+ strlcpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1);
+#endif
+
+ pr_info("manual_su: auth token generated successfully\n");
+ return 0;
+}
+
+static int handle_escalation_request(struct manual_su_request *request)
+{
+ uid_t target_uid = request->target_uid;
+ pid_t target_pid = request->target_pid;
+ struct task_struct *tsk;
+
+ rcu_read_lock();
+ tsk = pid_task(find_vpid(target_pid), PIDTYPE_PID);
+ if (!tsk || ksu_task_is_dead(tsk)) {
+ rcu_read_unlock();
+ pr_err("cmd_su: PID %d is invalid or dead\n", target_pid);
+ return -ESRCH;
+ }
+ rcu_read_unlock();
+
+ if (current_uid().val == 0 || is_manager() || ksu_is_allow_uid_for_current(current_uid().val))
+ goto allowed;
+
+ char *env_token = get_token_from_envp();
+ if (!env_token) {
+ pr_warn("manual_su: no auth token found in environment\n");
+ return -EACCES;
+ }
+
+ bool token_valid = ksu_verify_auth_token(env_token);
+ kfree(env_token);
+
+ if (!token_valid) {
+ pr_warn("manual_su: token verification failed\n");
+ return -EACCES;
+ }
+
+allowed:
+ current_verified = true;
+ escape_to_root_for_cmd_su(target_uid, target_pid);
+ return 0;
+}
+
+static int handle_add_pending_request(struct manual_su_request *request)
+{
+ uid_t target_uid = request->target_uid;
+
+ if (!is_current_verified()) {
+ pr_warn("manual_su: add_pending denied, not verified\n");
+ return -EPERM;
+ }
+
+ add_pending_root(target_uid);
+ current_verified = false;
+ pr_info("manual_su: pending root added for UID %d\n", target_uid);
+ return 0;
+}
+
+int ksu_handle_manual_su_request(int option, struct manual_su_request *request)
+{
+ if (!request) {
+ pr_err("manual_su: invalid request pointer\n");
+ return -EINVAL;
+ }
+
+ switch (option) {
+ case MANUAL_SU_OP_GENERATE_TOKEN:
+ pr_info("manual_su: handling token generation request\n");
+ return handle_token_generation(request);
+
+ case MANUAL_SU_OP_ESCALATE:
+ pr_info("manual_su: handling escalation request for UID %d, PID %d\n",
+ request->target_uid, request->target_pid);
+ return handle_escalation_request(request);
+
+ case MANUAL_SU_OP_ADD_PENDING:
+ pr_info("manual_su: handling add pending request for UID %d\n", request->target_uid);
+ return handle_add_pending_request(request);
+
+ default:
+ pr_err("manual_su: unknown option %d\n", option);
+ return -EINVAL;
+ }
+}
+
+static bool is_current_verified(void)
+{
+ return current_verified;
+}
+
+bool is_pending_root(uid_t uid)
+{
+ for (int i = 0; i < pending_cnt; i++) {
+ if (pending_uids[i].uid == uid) {
+ pending_uids[i].use_count++;
+ pending_uids[i].remove_calls++;
+ return true;
+ }
+ }
+ return false;
+}
+
+void remove_pending_root(uid_t uid)
+{
+ for (int i = 0; i < pending_cnt; i++) {
+ if (pending_uids[i].uid == uid) {
+ pending_uids[i].remove_calls++;
+
+ if (pending_uids[i].remove_calls >= REMOVE_DELAY_CALLS) {
+ pending_uids[i] = pending_uids[--pending_cnt];
+ pr_info("pending_root: removed UID %d after %d calls\n", uid, REMOVE_DELAY_CALLS);
+ ksu_temp_revoke_root_once(uid);
+ } else {
+ pr_info("pending_root: UID %d remove_call=%d (<%d)\n",
+ uid, pending_uids[i].remove_calls, REMOVE_DELAY_CALLS);
+ }
+ return;
+ }
+ }
+}
+
+static void add_pending_root(uid_t uid)
+{
+ if (pending_cnt >= MAX_PENDING) {
+ pr_warn("pending_root: cache full\n");
+ return;
+ }
+ for (int i = 0; i < pending_cnt; i++) {
+ if (pending_uids[i].uid == uid) {
+ pending_uids[i].use_count = 0;
+ pending_uids[i].remove_calls = 0;
+ return;
+ }
+ }
+ pending_uids[pending_cnt++] = (struct pending_uid){uid, 0};
+ ksu_temp_grant_root_once(uid);
+ pr_info("pending_root: cached UID %d\n", uid);
+}
+
+void ksu_try_escalate_for_uid(uid_t uid)
+{
+ if (!is_pending_root(uid))
+ return;
+
+ pr_info("pending_root: UID=%d temporarily allowed\n", uid);
+ remove_pending_root(uid);
+}
\ No newline at end of file
diff --git a/kernel/manual_su.h b/kernel/manual_su.h
new file mode 100644
index 0000000..419dbfc
--- /dev/null
+++ b/kernel/manual_su.h
@@ -0,0 +1,49 @@
+#ifndef __KSU_MANUAL_SU_H
+#define __KSU_MANUAL_SU_H
+
+#include
+#include
+#include
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0)
+#define mmap_lock mmap_sem
+#endif
+
+#define ksu_task_is_dead(t) ((t)->exit_state != 0)
+
+#define MAX_PENDING 16
+#define REMOVE_DELAY_CALLS 150
+#define MAX_TOKENS 10
+
+#define KSU_SU_VERIFIED_BIT (1UL << 0)
+#define KSU_TOKEN_LENGTH 32
+#define KSU_TOKEN_ENV_NAME "KSU_AUTH_TOKEN"
+#define KSU_TOKEN_EXPIRE_TIME 150
+
+#define MANUAL_SU_OP_GENERATE_TOKEN 0
+#define MANUAL_SU_OP_ESCALATE 1
+#define MANUAL_SU_OP_ADD_PENDING 2
+
+struct pending_uid {
+ uid_t uid;
+ int use_count;
+ int remove_calls;
+};
+
+struct manual_su_request {
+ uid_t target_uid;
+ pid_t target_pid;
+ char token_buffer[KSU_TOKEN_LENGTH + 1];
+};
+
+struct ksu_token_entry {
+ char token[KSU_TOKEN_LENGTH + 1];
+ unsigned long expire_time;
+ bool used;
+};
+
+int ksu_handle_manual_su_request(int option, struct manual_su_request *request);
+bool is_pending_root(uid_t uid);
+void remove_pending_root(uid_t uid);
+void ksu_try_escalate_for_uid(uid_t uid);
+#endif
\ No newline at end of file
diff --git a/kernel/pkg_observer.c b/kernel/pkg_observer.c
new file mode 100644
index 0000000..b632cd1
--- /dev/null
+++ b/kernel/pkg_observer.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "klog.h" // IWYU pragma: keep
+#include "ksu.h"
+#include "throne_tracker.h"
+#include "throne_comm.h"
+
+#define MASK_SYSTEM (FS_CREATE | FS_MOVE | FS_EVENT_ON_CHILD)
+
+struct watch_dir {
+ const char *path;
+ u32 mask;
+ struct path kpath;
+ struct inode *inode;
+ struct fsnotify_mark *mark;
+};
+
+static struct fsnotify_group *g;
+
+static int ksu_handle_inode_event(struct fsnotify_mark *mark, u32 mask,
+ struct inode *inode, struct inode *dir,
+ const struct qstr *file_name, u32 cookie)
+{
+ if (!file_name)
+ return 0;
+ if (mask & FS_ISDIR)
+ return 0;
+ if (file_name->len == 13 &&
+ !memcmp(file_name->name, "packages.list", 13)) {
+ pr_info("packages.list detected: %d\n", mask);
+ if (ksu_uid_scanner_enabled) {
+ ksu_request_userspace_scan();
+ }
+ track_throne(false);
+ }
+ return 0;
+}
+
+static const struct fsnotify_ops ksu_ops = {
+ .handle_inode_event = ksu_handle_inode_event,
+};
+
+static int add_mark_on_inode(struct inode *inode, u32 mask,
+ struct fsnotify_mark **out)
+{
+ struct fsnotify_mark *m;
+
+ m = kzalloc(sizeof(*m), GFP_KERNEL);
+ if (!m)
+ return -ENOMEM;
+
+ fsnotify_init_mark(m, g);
+ m->mask = mask;
+
+ if (fsnotify_add_inode_mark(m, inode, 0)) {
+ fsnotify_put_mark(m);
+ return -EINVAL;
+ }
+ *out = m;
+ return 0;
+}
+
+static int watch_one_dir(struct watch_dir *wd)
+{
+ int ret = kern_path(wd->path, LOOKUP_FOLLOW, &wd->kpath);
+ if (ret) {
+ pr_info("path not ready: %s (%d)\n", wd->path, ret);
+ return ret;
+ }
+ wd->inode = d_inode(wd->kpath.dentry);
+ ihold(wd->inode);
+
+ ret = add_mark_on_inode(wd->inode, wd->mask, &wd->mark);
+ if (ret) {
+ pr_err("Add mark failed for %s (%d)\n", wd->path, ret);
+ path_put(&wd->kpath);
+ iput(wd->inode);
+ wd->inode = NULL;
+ return ret;
+ }
+ pr_info("watching %s\n", wd->path);
+ return 0;
+}
+
+static void unwatch_one_dir(struct watch_dir *wd)
+{
+ if (wd->mark) {
+ fsnotify_destroy_mark(wd->mark, g);
+ fsnotify_put_mark(wd->mark);
+ wd->mark = NULL;
+ }
+ if (wd->inode) {
+ iput(wd->inode);
+ wd->inode = NULL;
+ }
+ if (wd->kpath.dentry) {
+ path_put(&wd->kpath);
+ memset(&wd->kpath, 0, sizeof(wd->kpath));
+ }
+}
+
+static struct watch_dir g_watch = { .path = "/data/system",
+ .mask = MASK_SYSTEM };
+
+int ksu_observer_init(void)
+{
+ int ret = 0;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
+ g = fsnotify_alloc_group(&ksu_ops, 0);
+#else
+ g = fsnotify_alloc_group(&ksu_ops);
+#endif
+ if (IS_ERR(g))
+ return PTR_ERR(g);
+
+ ret = watch_one_dir(&g_watch);
+ pr_info("observer init done\n");
+ return 0;
+}
+
+void ksu_observer_exit(void)
+{
+ unwatch_one_dir(&g_watch);
+ fsnotify_put_group(g);
+ pr_info("observer exit done\n");
+}
\ No newline at end of file
diff --git a/kernel/seccomp_cache.c b/kernel/seccomp_cache.c
new file mode 100644
index 0000000..286b5ca
--- /dev/null
+++ b/kernel/seccomp_cache.c
@@ -0,0 +1,69 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "klog.h" // IWYU pragma: keep
+#include "seccomp_cache.h"
+
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 2) // Android backport this feature in 5.10.2
+struct action_cache {
+ DECLARE_BITMAP(allow_native, SECCOMP_ARCH_NATIVE_NR);
+#ifdef SECCOMP_ARCH_COMPAT
+ DECLARE_BITMAP(allow_compat, SECCOMP_ARCH_COMPAT_NR);
+#endif
+};
+
+struct seccomp_filter {
+ refcount_t refs;
+ refcount_t users;
+ bool log;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
+ bool wait_killable_recv;
+#endif
+ struct action_cache cache;
+ struct seccomp_filter *prev;
+ struct bpf_prog *prog;
+ struct notification *notif;
+ struct mutex notify_lock;
+ wait_queue_head_t wqh;
+};
+
+void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr)
+{
+ if (!filter) {
+ return;
+ }
+
+ if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) {
+ clear_bit(nr, filter->cache.allow_native);
+ }
+
+#ifdef SECCOMP_ARCH_COMPAT
+ if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) {
+ clear_bit(nr, filter->cache.allow_compat);
+ }
+#endif
+}
+
+void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr)
+{
+ if (!filter) {
+ return;
+ }
+
+ if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) {
+ set_bit(nr, filter->cache.allow_native);
+ }
+
+#ifdef SECCOMP_ARCH_COMPAT
+ if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) {
+ set_bit(nr, filter->cache.allow_compat);
+ }
+#endif
+}
+
+#endif
\ No newline at end of file
diff --git a/kernel/seccomp_cache.h b/kernel/seccomp_cache.h
new file mode 100644
index 0000000..ce88328
--- /dev/null
+++ b/kernel/seccomp_cache.h
@@ -0,0 +1,12 @@
+#ifndef __KSU_H_SECCOMP_CACHE
+#define __KSU_H_SECCOMP_CACHE
+
+#include
+#include
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 2) // Android backport this feature in 5.10.2
+extern void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr);
+extern void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr);
+#endif
+
+#endif
\ No newline at end of file
diff --git a/kernel/selinux/Makefile b/kernel/selinux/Makefile
index 870750b..d35413d 100644
--- a/kernel/selinux/Makefile
+++ b/kernel/selinux/Makefile
@@ -2,15 +2,7 @@ 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-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
diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c
index c60159c..98d7475 100644
--- a/kernel/selinux/rules.c
+++ b/kernel/selinux/rules.c
@@ -6,7 +6,7 @@
#include "selinux.h"
#include "sepolicy.h"
#include "ss/services.h"
-#include "linux/lsm_audit.h"
+#include "linux/lsm_audit.h" // IWYU pragma: keep
#include "xfrm.h"
#define SELINUX_POLICY_INSTEAD_SELINUX_SS
@@ -18,119 +18,119 @@
static struct policydb *get_policydb(void)
{
- struct policydb *db;
- struct selinux_policy *policy = selinux_state.policy;
- db = &policy->policydb;
- return db;
+ struct policydb *db;
+ struct selinux_policy *policy = selinux_state.policy;
+ db = &policy->policydb;
+ return db;
}
static DEFINE_MUTEX(ksu_rules);
void apply_kernelsu_rules()
{
- struct policydb *db;
+ struct policydb *db;
- if (!getenforce()) {
- pr_info("SELinux permissive or disabled, apply rules!\n");
- }
+ if (!getenforce()) {
+ pr_info("SELinux permissive or disabled, apply rules!\n");
+ }
- mutex_lock(&ksu_rules);
+ mutex_lock(&ksu_rules);
- db = get_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");
+ 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);
+ // 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 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);
- }
+ // 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");
+ // 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");
+ // 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");
+ // 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");
+ // 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");
+ // 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");
+ // 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 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");
- // https://android-review.googlesource.com/c/platform/system/logging/+/3725346
- ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr");
+ // https://android-review.googlesource.com/c/platform/system/logging/+/3725346
+ ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr");
- mutex_unlock(&ksu_rules);
+ mutex_unlock(&ksu_rules);
}
#define MAX_SEPOL_LEN 128
@@ -145,401 +145,333 @@ void apply_kernelsu_rules()
#define CMD_TYPE_CHANGE 8
#define CMD_GENFSCON 9
-#ifdef CONFIG_64BIT
struct sepol_data {
- u32 cmd;
- u32 subcmd;
- u64 field_sepol1;
- u64 field_sepol2;
- u64 field_sepol3;
- u64 field_sepol4;
- u64 field_sepol5;
- u64 field_sepol6;
- u64 field_sepol7;
+ 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;
};
-#ifdef CONFIG_COMPAT
-extern bool ksu_is_compat __read_mostly;
-struct sepol_compat_data {
- u32 cmd;
- u32 subcmd;
- u32 field_sepol1;
- u32 field_sepol2;
- u32 field_sepol3;
- u32 field_sepol4;
- u32 field_sepol5;
- u32 field_sepol6;
- u32 field_sepol7;
-};
-#endif // CONFIG_COMPAT
-#else
-struct sepol_data {
- u32 cmd;
- u32 subcmd;
- u32 field_sepol1;
- u32 field_sepol2;
- u32 field_sepol3;
- u32 field_sepol4;
- u32 field_sepol5;
- u32 field_sepol6;
- u32 field_sepol7;
-};
-#endif // CONFIG_64BIT
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
- char **object)
+ char **object)
{
- if (!user_object) {
- *object = ALL;
- return 0;
- }
+ if (!user_object) {
+ *object = ALL;
+ return 0;
+ }
- if (strncpy_from_user(buf, user_object, buf_sz) < 0) {
- return -1;
- }
+ if (strncpy_from_user(buf, user_object, buf_sz) < 0) {
+ return -EINVAL;
+ }
- *object = buf;
+ *object = buf;
- return 0;
+ return 0;
}
-
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
+extern int avc_ss_reset(u32 seqno);
+#else
+extern int avc_ss_reset(struct selinux_avc *avc, u32 seqno);
+#endif
// 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))
- avc_ss_reset(0);
- selnl_notify_policyload(0);
- selinux_status_update_policyload(0);
+ 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);
+ 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();
+ selinux_xfrm_notify_policyload();
}
int handle_sepolicy(unsigned long arg3, void __user *arg4)
{
- struct policydb *db;
+ struct policydb *db;
- if (!arg4) {
- return -1;
- }
+ if (!arg4) {
+ return -EINVAL;
+ }
- if (!getenforce()) {
- pr_info("SELinux permissive or disabled when handle policy!\n");
- }
-
- u32 cmd, subcmd;
- char __user *sepol1, *sepol2, *sepol3, *sepol4, *sepol5, *sepol6, *sepol7;
+ if (!getenforce()) {
+ pr_info("SELinux permissive or disabled when handle policy!\n");
+ }
-#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
- if (unlikely(ksu_is_compat)) {
- struct sepol_compat_data compat_data;
- if (copy_from_user(&compat_data, arg4, sizeof(struct sepol_compat_data))) {
- pr_err("sepol: copy sepol_data failed.\n");
- return -1;
- }
- sepol1 = compat_ptr(compat_data.field_sepol1);
- sepol2 = compat_ptr(compat_data.field_sepol2);
- sepol3 = compat_ptr(compat_data.field_sepol3);
- sepol4 = compat_ptr(compat_data.field_sepol4);
- sepol5 = compat_ptr(compat_data.field_sepol5);
- sepol6 = compat_ptr(compat_data.field_sepol6);
- sepol7 = compat_ptr(compat_data.field_sepol7);
- cmd = compat_data.cmd;
- subcmd = compat_data.subcmd;
- } else {
- struct sepol_data data;
- if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
- pr_err("sepol: copy sepol_data failed.\n");
- return -1;
- }
- sepol1 = data.field_sepol1;
- sepol2 = data.field_sepol2;
- sepol3 = data.field_sepol3;
- sepol4 = data.field_sepol4;
- sepol5 = data.field_sepol5;
- sepol6 = data.field_sepol6;
- sepol7 = data.field_sepol7;
- cmd = data.cmd;
- subcmd = data.subcmd;
- }
-#else
- // basically for full native, say (64BIT=y COMPAT=n) || (64BIT=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;
- }
- sepol1 = data.field_sepol1;
- sepol2 = data.field_sepol2;
- sepol3 = data.field_sepol3;
- sepol4 = data.field_sepol4;
- sepol5 = data.field_sepol5;
- sepol6 = data.field_sepol6;
- sepol7 = data.field_sepol7;
- cmd = data.cmd;
- subcmd = data.subcmd;
-#endif
+ struct sepol_data data;
+ if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
+ pr_err("sepol: copy sepol_data failed.\n");
+ return -EINVAL;
+ }
- mutex_lock(&ksu_rules);
+ u32 cmd = data.cmd;
+ u32 subcmd = data.subcmd;
- db = get_policydb();
+ mutex_lock(&ksu_rules);
- 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];
+ db = get_policydb();
- char *s, *t, *c, *p;
- if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
- pr_err("sepol: copy src failed.\n");
- goto exit;
- }
+ int ret = -EINVAL;
+ 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];
- if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
- pr_err("sepol: copy tgt failed.\n");
- goto exit;
- }
+ 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(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
- pr_err("sepol: copy cls 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(perm_buf, sepol4, sizeof(perm_buf), &p) <
- 0) {
- pr_err("sepol: copy perm 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;
+ }
- 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;
+ if (get_object(perm_buf, data.sepol4, sizeof(perm_buf), &p) <
+ 0) {
+ pr_err("sepol: copy perm failed.\n");
+ goto exit;
+ }
- } else if (cmd == CMD_XPERM) {
- char src_buf[MAX_SEPOL_LEN];
- char tgt_buf[MAX_SEPOL_LEN];
- char cls_buf[MAX_SEPOL_LEN];
+ 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 : -EINVAL;
- char __maybe_unused
- operation[MAX_SEPOL_LEN]; // it is always ioctl now!
- char perm_set[MAX_SEPOL_LEN];
+ } else if (cmd == CMD_XPERM) {
+ char src_buf[MAX_SEPOL_LEN];
+ char tgt_buf[MAX_SEPOL_LEN];
+ char cls_buf[MAX_SEPOL_LEN];
- char *s, *t, *c;
- if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
- pr_err("sepol: copy src failed.\n");
- goto exit;
- }
- if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
- pr_err("sepol: copy tgt failed.\n");
- goto exit;
- }
- if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
- pr_err("sepol: copy cls failed.\n");
- goto exit;
- }
- if (strncpy_from_user(operation, sepol4,
- sizeof(operation)) < 0) {
- pr_err("sepol: copy operation failed.\n");
- goto exit;
- }
- if (strncpy_from_user(perm_set, sepol5, sizeof(perm_set)) <
- 0) {
- pr_err("sepol: copy perm_set failed.\n");
- goto exit;
- }
+ char __maybe_unused
+ operation[MAX_SEPOL_LEN]; // it is always ioctl now!
+ char perm_set[MAX_SEPOL_LEN];
- 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];
+ 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;
+ }
- if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
- pr_err("sepol: copy src 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 : -EINVAL;
+ } else if (cmd == CMD_TYPE_STATE) {
+ char src[MAX_SEPOL_LEN];
- 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;
+ if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
+ pr_err("sepol: copy src failed.\n");
+ goto exit;
+ }
- } else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
- char type[MAX_SEPOL_LEN];
- char attr[MAX_SEPOL_LEN];
+ 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;
- if (strncpy_from_user(type, sepol1, sizeof(type)) < 0) {
- pr_err("sepol: copy type failed.\n");
- goto exit;
- }
- if (strncpy_from_user(attr, sepol2, sizeof(attr)) < 0) {
- pr_err("sepol: copy attr failed.\n");
- goto exit;
- }
+ } else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
+ char type[MAX_SEPOL_LEN];
+ char attr[MAX_SEPOL_LEN];
- 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;
+ 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;
+ }
- } else if (cmd == CMD_ATTR) {
- char attr[MAX_SEPOL_LEN];
+ 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;
- if (strncpy_from_user(attr, 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_ATTR) {
+ char attr[MAX_SEPOL_LEN];
- } 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(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;
- if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
- pr_err("sepol: copy src failed.\n");
- goto exit;
- }
- if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
- pr_err("sepol: copy tgt failed.\n");
- goto exit;
- }
- if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
- pr_err("sepol: copy cls failed.\n");
- goto exit;
- }
- if (strncpy_from_user(default_type, sepol4,
- sizeof(default_type)) < 0) {
- pr_err("sepol: copy default_type failed.\n");
- goto exit;
- }
- char *real_object;
- if (sepol5 == NULL) {
- real_object = NULL;
- } else {
- if (strncpy_from_user(object, sepol5,
- sizeof(object)) < 0) {
- pr_err("sepol: copy object failed.\n");
- goto exit;
- }
- real_object = object;
- }
+ } 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];
- bool success = ksu_type_transition(db, src, tgt, cls,
- default_type, real_object);
- if (success)
- ret = 0;
+ 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;
+ }
- } 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];
+ bool success = ksu_type_transition(db, src, tgt, cls,
+ default_type, real_object);
+ if (success)
+ ret = 0;
- if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
- pr_err("sepol: copy src failed.\n");
- goto exit;
- }
- if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
- pr_err("sepol: copy tgt failed.\n");
- goto exit;
- }
- if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
- pr_err("sepol: copy cls failed.\n");
- goto exit;
- }
- if (strncpy_from_user(default_type, 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, sepol1, sizeof(name)) < 0) {
- pr_err("sepol: copy name failed.\n");
- goto exit;
- }
- if (strncpy_from_user(path, sepol2, sizeof(path)) < 0) {
- pr_err("sepol: copy path failed.\n");
- goto exit;
- }
- if (strncpy_from_user(context, sepol3, sizeof(context)) <
- 0) {
- pr_err("sepol: copy context failed.\n");
- goto exit;
- }
+ } 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 (!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);
- }
+ 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:
- mutex_unlock(&ksu_rules);
+ mutex_unlock(&ksu_rules);
- // 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();
+ // 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;
-}
+ return ret;
+}
\ No newline at end of file
diff --git a/kernel/selinux/selinux.c b/kernel/selinux/selinux.c
index 17a25da..dfc4831 100644
--- a/kernel/selinux/selinux.c
+++ b/kernel/selinux/selinux.c
@@ -1,4 +1,6 @@
#include "selinux.h"
+#include "linux/cred.h"
+#include "linux/sched.h"
#include "objsec.h"
#include "linux/version.h"
#include "../klog.h" // IWYU pragma: keep
@@ -7,124 +9,146 @@
static int transive_to_domain(const char *domain)
{
- struct cred *cred;
- struct task_security_struct *tsec;
- u32 sid;
- int error;
+ struct cred *cred;
+ struct task_security_struct *tsec;
+ u32 sid;
+ int error;
- cred = (struct cred *)__task_cred(current);
+ cred = (struct cred *)__task_cred(current);
- tsec = cred->security;
- if (!tsec) {
- pr_err("tsec == NULL!\n");
- return -1;
- }
+ 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;
+ 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 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;
- }
-}*/
+ if (transive_to_domain(domain)) {
+ pr_err("transive domain failed.\n");
+ return;
+ }
}
void setenforce(bool enforce)
{
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
- selinux_state.enforcing = enforce;
+ selinux_state.enforcing = enforce;
#endif
}
bool getenforce()
{
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
- if (selinux_state.disabled) {
- return false;
- }
+ if (selinux_state.disabled) {
+ return false;
+ }
#endif
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
- return selinux_state.enforcing;
+ return selinux_state.enforcing;
#else
- return true;
+ 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)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 14, 0)
+struct lsm_context {
+ char *context;
+ u32 len;
+};
+
+static int __security_secid_to_secctx(u32 secid, struct lsm_context *cp)
{
- const struct task_security_struct *tsec = current_security();
-
- return tsec->sid;
+ return security_secid_to_secctx(secid, &cp->context, &cp->len);
}
+static void __security_release_secctx(struct lsm_context *cp)
+{
+ return security_release_secctx(cp->context, cp->len);
+}
+#else
+#define __security_secid_to_secctx security_secid_to_secctx
+#define __security_release_secctx security_release_secctx
#endif
+bool is_task_ksu_domain(const struct cred* cred)
+{
+ struct lsm_context ctx;
+ bool result;
+ if (!cred) {
+ return false;
+ }
+ const struct task_security_struct *tsec = selinux_cred(cred);
+ if (!tsec) {
+ return false;
+ }
+ int err = __security_secid_to_secctx(tsec->sid, &ctx);
+ if (err) {
+ return false;
+ }
+ result = strncmp(KERNEL_SU_DOMAIN, ctx.context, ctx.len) == 0;
+ __security_release_secctx(&ctx);
+ return result;
+}
+
bool 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;
+ current_sid();
+ return is_task_ksu_domain(current_cred());
}
-bool is_zygote(void *sec)
+bool is_context(const struct cred* cred, const char* context)
{
- 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;
+ if (!cred) {
+ return false;
+ }
+ const struct task_security_struct * tsec = selinux_cred(cred);
+ if (!tsec) {
+ return false;
+ }
+ struct lsm_context ctx;
+ bool result;
+ int err = __security_secid_to_secctx(tsec->sid, &ctx);
+ if (err) {
+ return false;
+ }
+ result = strncmp(context, ctx.context, ctx.len) == 0;
+ __security_release_secctx(&ctx);
+ return result;
}
-#define DEVPTS_DOMAIN "u:object_r:ksu_file:s0"
-
-u32 ksu_get_devpts_sid()
+bool is_zygote(const struct cred* cred)
{
- 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;
+ return is_context(cred, "u:r:zygote:s0");
+}
+
+bool is_init(const struct cred* cred) {
+ return is_context(cred, "u:r:init:s0");
+}
+
+#define KSU_FILE_DOMAIN "u:object_r:ksu_file:s0"
+
+u32 ksu_get_ksu_file_sid()
+{
+ u32 ksu_file_sid = 0;
+ int err = security_secctx_to_secid(KSU_FILE_DOMAIN, strlen(KSU_FILE_DOMAIN),
+ &ksu_file_sid);
+ if (err) {
+ pr_info("get ksufile sid err %d\n", err);
+ }
+ return ksu_file_sid;
}
diff --git a/kernel/selinux/selinux.h b/kernel/selinux/selinux.h
index 88f1e7d..431e044 100644
--- a/kernel/selinux/selinux.h
+++ b/kernel/selinux/selinux.h
@@ -3,6 +3,7 @@
#include "linux/types.h"
#include "linux/version.h"
+#include "linux/cred.h"
void setup_selinux(const char *);
@@ -10,12 +11,18 @@ void setenforce(bool);
bool getenforce();
+bool is_task_ksu_domain(const struct cred* cred);
+
bool is_ksu_domain();
-bool is_zygote(void *cred);
+bool is_zygote(const struct cred* cred);
+
+bool is_init(const struct cred* cred);
void apply_kernelsu_rules();
-u32 ksu_get_devpts_sid();
+u32 ksu_get_ksu_file_sid();
+
+int handle_sepolicy(unsigned long arg3, void __user *arg4);
#endif
diff --git a/kernel/selinux/sepolicy.c b/kernel/selinux/sepolicy.c
index 7759602..e31fc08 100644
--- a/kernel/selinux/sepolicy.c
+++ b/kernel/selinux/sepolicy.c
@@ -6,7 +6,6 @@
#include "sepolicy.h"
#include "../klog.h" // IWYU pragma: keep
#include "ss/symtab.h"
-#include "../kernel_compat.h" // Add check Huawei Device
#define KSU_SUPPORT_ADD_TYPE
@@ -15,44 +14,44 @@
//////////////////////////////////////////////////////
static struct avtab_node *get_avtab_node(struct policydb *db,
- struct avtab_key *key,
- struct avtab_extended_perms *xperms);
+ struct avtab_key *key,
+ struct avtab_extended_perms *xperms);
static bool add_rule(struct policydb *db, const char *s, const char *t,
- const char *c, const char *p, int effect, bool invert);
+ const char *c, const char *p, int effect, bool invert);
static void add_rule_raw(struct policydb *db, struct type_datum *src,
- struct type_datum *tgt, struct class_datum *cls,
- struct perm_datum *perm, int effect, bool invert);
+ struct type_datum *tgt, struct class_datum *cls,
+ struct perm_datum *perm, int effect, bool invert);
static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
- struct type_datum *tgt, struct class_datum *cls,
- uint16_t low, uint16_t high, int effect,
- bool invert);
+ struct type_datum *tgt, struct class_datum *cls,
+ uint16_t low, uint16_t high, int effect,
+ bool invert);
static bool add_xperm_rule(struct policydb *db, const char *s, const char *t,
- const char *c, const char *range, int effect,
- bool invert);
+ const char *c, const char *range, int effect,
+ bool invert);
static bool add_type_rule(struct policydb *db, const char *s, const char *t,
- const char *c, const char *d, int effect);
+ const char *c, const char *d, int effect);
static bool add_filename_trans(struct policydb *db, const char *s,
- const char *t, const char *c, const char *d,
- const char *o);
+ const char *t, const char *c, const char *d,
+ const char *o);
static bool add_genfscon(struct policydb *db, const char *fs_name,
- const char *path, const char *context);
+ const char *path, const char *context);
static bool add_type(struct policydb *db, const char *type_name, bool attr);
static bool set_type_state(struct policydb *db, const char *type_name,
- bool permissive);
+ bool permissive);
static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
- struct type_datum *attr);
+ struct type_datum *attr);
static bool add_typeattribute(struct policydb *db, const char *type,
- const char *attr);
+ const char *attr);
//////////////////////////////////////////////////////
// Implementation
@@ -63,18 +62,18 @@ static bool add_typeattribute(struct policydb *db, const char *type,
#define strip_av(effect, invert) ((effect == AVTAB_AUDITDENY) == !invert)
#define ksu_hash_for_each(node_ptr, n_slot, cur) \
- int i; \
- for (i = 0; i < n_slot; ++i) \
- for (cur = node_ptr[i]; cur; cur = cur->next)
+ int i; \
+ for (i = 0; i < n_slot; ++i) \
+ for (cur = node_ptr[i]; cur; cur = cur->next)
// htable is a struct instead of pointer above 5.8.0:
// https://elixir.bootlin.com/linux/v5.8-rc1/source/security/selinux/ss/symtab.h
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
#define ksu_hashtab_for_each(htab, cur) \
- ksu_hash_for_each(htab.htable, htab.size, cur)
+ ksu_hash_for_each(htab.htable, htab.size, cur)
#else
#define ksu_hashtab_for_each(htab, cur) \
- ksu_hash_for_each(htab->htable, htab->size, cur)
+ ksu_hash_for_each(htab->htable, htab->size, cur)
#endif
// symtab_search is introduced on 5.9.0:
@@ -85,186 +84,186 @@ static bool add_typeattribute(struct policydb *db, const char *type,
#endif
#define avtab_for_each(avtab, cur) \
- ksu_hash_for_each(avtab.htable, avtab.nslot, cur);
+ ksu_hash_for_each(avtab.htable, avtab.nslot, cur);
static struct avtab_node *get_avtab_node(struct policydb *db,
- struct avtab_key *key,
- struct avtab_extended_perms *xperms)
+ struct avtab_key *key,
+ struct avtab_extended_perms *xperms)
{
- struct avtab_node *node;
+ struct avtab_node *node;
- /* AVTAB_XPERMS entries are not necessarily unique */
- if (key->specified & AVTAB_XPERMS) {
- bool match = false;
- node = avtab_search_node(&db->te_avtab, key);
- while (node) {
- if ((node->datum.u.xperms->specified ==
- xperms->specified) &&
- (node->datum.u.xperms->driver == xperms->driver)) {
- match = true;
- break;
- }
- node = avtab_search_node_next(node, key->specified);
- }
- if (!match)
- node = NULL;
- } else {
- node = avtab_search_node(&db->te_avtab, key);
- }
+ /* AVTAB_XPERMS entries are not necessarily unique */
+ if (key->specified & AVTAB_XPERMS) {
+ bool match = false;
+ node = avtab_search_node(&db->te_avtab, key);
+ while (node) {
+ if ((node->datum.u.xperms->specified ==
+ xperms->specified) &&
+ (node->datum.u.xperms->driver == xperms->driver)) {
+ match = true;
+ break;
+ }
+ node = avtab_search_node_next(node, key->specified);
+ }
+ if (!match)
+ node = NULL;
+ } else {
+ node = avtab_search_node(&db->te_avtab, key);
+ }
- if (!node) {
- struct avtab_datum avdatum = {};
- /*
+ if (!node) {
+ struct avtab_datum avdatum = {};
+ /*
* AUDITDENY, aka DONTAUDIT, are &= assigned, versus |= for
* others. Initialize the data accordingly.
*/
- if (key->specified & AVTAB_XPERMS) {
- avdatum.u.xperms = xperms;
- } else {
- avdatum.u.data =
- key->specified == AVTAB_AUDITDENY ? ~0U : 0U;
- }
- /* this is used to get the node - insertion is actually unique */
- node = avtab_insert_nonunique(&db->te_avtab, key, &avdatum);
+ if (key->specified & AVTAB_XPERMS) {
+ avdatum.u.xperms = xperms;
+ } else {
+ avdatum.u.data =
+ key->specified == AVTAB_AUDITDENY ? ~0U : 0U;
+ }
+ /* this is used to get the node - insertion is actually unique */
+ node = avtab_insert_nonunique(&db->te_avtab, key, &avdatum);
- int grow_size = sizeof(struct avtab_key);
- grow_size += sizeof(struct avtab_datum);
- if (key->specified & AVTAB_XPERMS) {
- grow_size += sizeof(u8);
- grow_size += sizeof(u8);
- grow_size += sizeof(u32) *
- ARRAY_SIZE(avdatum.u.xperms->perms.p);
- }
- db->len += grow_size;
- }
+ int grow_size = sizeof(struct avtab_key);
+ grow_size += sizeof(struct avtab_datum);
+ if (key->specified & AVTAB_XPERMS) {
+ grow_size += sizeof(u8);
+ grow_size += sizeof(u8);
+ grow_size += sizeof(u32) *
+ ARRAY_SIZE(avdatum.u.xperms->perms.p);
+ }
+ db->len += grow_size;
+ }
- return node;
+ return node;
}
static bool add_rule(struct policydb *db, const char *s, const char *t,
- const char *c, const char *p, int effect, bool invert)
+ const char *c, const char *p, int effect, bool invert)
{
- struct type_datum *src = NULL, *tgt = NULL;
- struct class_datum *cls = NULL;
- struct perm_datum *perm = NULL;
+ struct type_datum *src = NULL, *tgt = NULL;
+ struct class_datum *cls = NULL;
+ struct perm_datum *perm = NULL;
- if (s) {
- src = symtab_search(&db->p_types, s);
- if (src == NULL) {
- pr_info("source type %s does not exist\n", s);
- return false;
- }
- }
+ if (s) {
+ src = symtab_search(&db->p_types, s);
+ if (src == NULL) {
+ pr_info("source type %s does not exist\n", s);
+ return false;
+ }
+ }
- if (t) {
- tgt = symtab_search(&db->p_types, t);
- if (tgt == NULL) {
- pr_info("target type %s does not exist\n", t);
- return false;
- }
- }
+ if (t) {
+ tgt = symtab_search(&db->p_types, t);
+ if (tgt == NULL) {
+ pr_info("target type %s does not exist\n", t);
+ return false;
+ }
+ }
- if (c) {
- cls = symtab_search(&db->p_classes, c);
- if (cls == NULL) {
- pr_info("class %s does not exist\n", c);
- return false;
- }
- }
+ if (c) {
+ cls = symtab_search(&db->p_classes, c);
+ if (cls == NULL) {
+ pr_info("class %s does not exist\n", c);
+ return false;
+ }
+ }
- if (p) {
- if (c == NULL) {
- pr_info("No class is specified, cannot add perm [%s] \n",
- p);
- return false;
- }
+ if (p) {
+ if (c == NULL) {
+ pr_info("No class is specified, cannot add perm [%s] \n",
+ p);
+ return false;
+ }
- perm = symtab_search(&cls->permissions, p);
- if (perm == NULL && cls->comdatum != NULL) {
- perm = symtab_search(&cls->comdatum->permissions, p);
- }
- if (perm == NULL) {
- pr_info("perm %s does not exist in class %s\n", p, c);
- return false;
- }
- }
- add_rule_raw(db, src, tgt, cls, perm, effect, invert);
- return true;
+ perm = symtab_search(&cls->permissions, p);
+ if (perm == NULL && cls->comdatum != NULL) {
+ perm = symtab_search(&cls->comdatum->permissions, p);
+ }
+ if (perm == NULL) {
+ pr_info("perm %s does not exist in class %s\n", p, c);
+ return false;
+ }
+ }
+ add_rule_raw(db, src, tgt, cls, perm, effect, invert);
+ return true;
}
static void add_rule_raw(struct policydb *db, struct type_datum *src,
- struct type_datum *tgt, struct class_datum *cls,
- struct perm_datum *perm, int effect, bool invert)
+ struct type_datum *tgt, struct class_datum *cls,
+ struct perm_datum *perm, int effect, bool invert)
{
- if (src == NULL) {
- struct hashtab_node *node;
- if (strip_av(effect, invert)) {
- ksu_hashtab_for_each(db->p_types.table, node)
- {
- add_rule_raw(db,
- (struct type_datum *)node->datum,
- tgt, cls, perm, effect, invert);
- };
- } else {
- ksu_hashtab_for_each(db->p_types.table, node)
- {
- struct type_datum *type =
- (struct type_datum *)(node->datum);
- if (type->attribute) {
- add_rule_raw(db, type, tgt, cls, perm,
- effect, invert);
- }
- };
- }
- } else if (tgt == NULL) {
- struct hashtab_node *node;
- if (strip_av(effect, invert)) {
- ksu_hashtab_for_each(db->p_types.table, node)
- {
- add_rule_raw(db, src,
- (struct type_datum *)node->datum,
- cls, perm, effect, invert);
- };
- } else {
- ksu_hashtab_for_each(db->p_types.table, node)
- {
- struct type_datum *type =
- (struct type_datum *)(node->datum);
- if (type->attribute) {
- add_rule_raw(db, src, type, cls, perm,
- effect, invert);
- }
- };
- }
- } else if (cls == NULL) {
- struct hashtab_node *node;
- ksu_hashtab_for_each(db->p_classes.table, node)
- {
- add_rule_raw(db, src, tgt,
- (struct class_datum *)node->datum, perm,
- effect, invert);
- }
- } else {
- struct avtab_key key;
- key.source_type = src->value;
- key.target_type = tgt->value;
- key.target_class = cls->value;
- key.specified = effect;
+ if (src == NULL) {
+ struct hashtab_node *node;
+ if (strip_av(effect, invert)) {
+ ksu_hashtab_for_each(db->p_types.table, node)
+ {
+ add_rule_raw(db,
+ (struct type_datum *)node->datum,
+ tgt, cls, perm, effect, invert);
+ };
+ } else {
+ ksu_hashtab_for_each(db->p_types.table, node)
+ {
+ struct type_datum *type =
+ (struct type_datum *)(node->datum);
+ if (type->attribute) {
+ add_rule_raw(db, type, tgt, cls, perm,
+ effect, invert);
+ }
+ };
+ }
+ } else if (tgt == NULL) {
+ struct hashtab_node *node;
+ if (strip_av(effect, invert)) {
+ ksu_hashtab_for_each(db->p_types.table, node)
+ {
+ add_rule_raw(db, src,
+ (struct type_datum *)node->datum,
+ cls, perm, effect, invert);
+ };
+ } else {
+ ksu_hashtab_for_each(db->p_types.table, node)
+ {
+ struct type_datum *type =
+ (struct type_datum *)(node->datum);
+ if (type->attribute) {
+ add_rule_raw(db, src, type, cls, perm,
+ effect, invert);
+ }
+ };
+ }
+ } else if (cls == NULL) {
+ struct hashtab_node *node;
+ ksu_hashtab_for_each(db->p_classes.table, node)
+ {
+ add_rule_raw(db, src, tgt,
+ (struct class_datum *)node->datum, perm,
+ effect, invert);
+ }
+ } else {
+ struct avtab_key key;
+ key.source_type = src->value;
+ key.target_type = tgt->value;
+ key.target_class = cls->value;
+ key.specified = effect;
- struct avtab_node *node = get_avtab_node(db, &key, NULL);
- if (invert) {
- if (perm)
- node->datum.u.data &=
- ~(1U << (perm->value - 1));
- else
- node->datum.u.data = 0U;
- } else {
- if (perm)
- node->datum.u.data |= 1U << (perm->value - 1);
- else
- node->datum.u.data = ~0U;
- }
- }
+ struct avtab_node *node = get_avtab_node(db, &key, NULL);
+ if (invert) {
+ if (perm)
+ node->datum.u.data &=
+ ~(1U << (perm->value - 1));
+ else
+ node->datum.u.data = 0U;
+ } else {
+ if (perm)
+ node->datum.u.data |= 1U << (perm->value - 1);
+ else
+ node->datum.u.data = ~0U;
+ }
+ }
}
#define ioctl_driver(x) (x >> 8 & 0xFF)
@@ -275,183 +274,183 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
#define xperm_clear(x, p) (p[x >> 5] &= ~(1 << (x & 0x1f)))
static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
- struct type_datum *tgt, struct class_datum *cls,
- uint16_t low, uint16_t high, int effect,
- bool invert)
+ struct type_datum *tgt, struct class_datum *cls,
+ uint16_t low, uint16_t high, int effect,
+ bool invert)
{
- if (src == NULL) {
- struct hashtab_node *node;
- ksu_hashtab_for_each(db->p_types.table, node)
- {
- struct type_datum *type =
- (struct type_datum *)(node->datum);
- if (type->attribute) {
- add_xperm_rule_raw(db, type, tgt, cls, low,
- high, effect, invert);
- }
- };
- } else if (tgt == NULL) {
- struct hashtab_node *node;
- ksu_hashtab_for_each(db->p_types.table, node)
- {
- struct type_datum *type =
- (struct type_datum *)(node->datum);
- if (type->attribute) {
- add_xperm_rule_raw(db, src, type, cls, low,
- high, effect, invert);
- }
- };
- } else if (cls == NULL) {
- struct hashtab_node *node;
- ksu_hashtab_for_each(db->p_classes.table, node)
- {
- add_xperm_rule_raw(db, src, tgt,
- (struct class_datum *)(node->datum),
- low, high, effect, invert);
- };
- } else {
- struct avtab_key key;
- key.source_type = src->value;
- key.target_type = tgt->value;
- key.target_class = cls->value;
- key.specified = effect;
+ if (src == NULL) {
+ struct hashtab_node *node;
+ ksu_hashtab_for_each(db->p_types.table, node)
+ {
+ struct type_datum *type =
+ (struct type_datum *)(node->datum);
+ if (type->attribute) {
+ add_xperm_rule_raw(db, type, tgt, cls, low,
+ high, effect, invert);
+ }
+ };
+ } else if (tgt == NULL) {
+ struct hashtab_node *node;
+ ksu_hashtab_for_each(db->p_types.table, node)
+ {
+ struct type_datum *type =
+ (struct type_datum *)(node->datum);
+ if (type->attribute) {
+ add_xperm_rule_raw(db, src, type, cls, low,
+ high, effect, invert);
+ }
+ };
+ } else if (cls == NULL) {
+ struct hashtab_node *node;
+ ksu_hashtab_for_each(db->p_classes.table, node)
+ {
+ add_xperm_rule_raw(db, src, tgt,
+ (struct class_datum *)(node->datum),
+ low, high, effect, invert);
+ };
+ } else {
+ struct avtab_key key;
+ key.source_type = src->value;
+ key.target_type = tgt->value;
+ key.target_class = cls->value;
+ key.specified = effect;
- struct avtab_datum *datum;
- struct avtab_node *node;
- struct avtab_extended_perms xperms;
+ struct avtab_datum *datum;
+ struct avtab_node *node;
+ struct avtab_extended_perms xperms;
- memset(&xperms, 0, sizeof(xperms));
- if (ioctl_driver(low) != ioctl_driver(high)) {
- xperms.specified = AVTAB_XPERMS_IOCTLDRIVER;
- xperms.driver = 0;
- } else {
- xperms.specified = AVTAB_XPERMS_IOCTLFUNCTION;
- xperms.driver = ioctl_driver(low);
- }
- int i;
- if (xperms.specified == AVTAB_XPERMS_IOCTLDRIVER) {
- for (i = ioctl_driver(low); i <= ioctl_driver(high);
- ++i) {
- if (invert)
- xperm_clear(i, xperms.perms.p);
- else
- xperm_set(i, xperms.perms.p);
- }
- } else {
- for (i = ioctl_func(low); i <= ioctl_func(high); ++i) {
- if (invert)
- xperm_clear(i, xperms.perms.p);
- else
- xperm_set(i, xperms.perms.p);
- }
- }
+ memset(&xperms, 0, sizeof(xperms));
+ if (ioctl_driver(low) != ioctl_driver(high)) {
+ xperms.specified = AVTAB_XPERMS_IOCTLDRIVER;
+ xperms.driver = 0;
+ } else {
+ xperms.specified = AVTAB_XPERMS_IOCTLFUNCTION;
+ xperms.driver = ioctl_driver(low);
+ }
+ int i;
+ if (xperms.specified == AVTAB_XPERMS_IOCTLDRIVER) {
+ for (i = ioctl_driver(low); i <= ioctl_driver(high);
+ ++i) {
+ if (invert)
+ xperm_clear(i, xperms.perms.p);
+ else
+ xperm_set(i, xperms.perms.p);
+ }
+ } else {
+ for (i = ioctl_func(low); i <= ioctl_func(high); ++i) {
+ if (invert)
+ xperm_clear(i, xperms.perms.p);
+ else
+ xperm_set(i, xperms.perms.p);
+ }
+ }
- node = get_avtab_node(db, &key, &xperms);
- if (!node) {
- pr_warn("add_xperm_rule_raw cannot found node!\n");
- return;
- }
- datum = &node->datum;
+ node = get_avtab_node(db, &key, &xperms);
+ if (!node) {
+ pr_warn("add_xperm_rule_raw cannot found node!\n");
+ return;
+ }
+ datum = &node->datum;
- if (datum->u.xperms == NULL) {
- datum->u.xperms =
- (struct avtab_extended_perms *)(kmalloc(
- sizeof(xperms), GFP_KERNEL));
- if (!datum->u.xperms) {
- pr_err("alloc xperms failed\n");
- return;
- }
- memcpy(datum->u.xperms, &xperms, sizeof(xperms));
- }
- }
+ if (datum->u.xperms == NULL) {
+ datum->u.xperms =
+ (struct avtab_extended_perms *)(kmalloc(
+ sizeof(xperms), GFP_KERNEL));
+ if (!datum->u.xperms) {
+ pr_err("alloc xperms failed\n");
+ return;
+ }
+ memcpy(datum->u.xperms, &xperms, sizeof(xperms));
+ }
+ }
}
static bool add_xperm_rule(struct policydb *db, const char *s, const char *t,
- const char *c, const char *range, int effect,
- bool invert)
+ const char *c, const char *range, int effect,
+ bool invert)
{
- struct type_datum *src = NULL, *tgt = NULL;
- struct class_datum *cls = NULL;
+ struct type_datum *src = NULL, *tgt = NULL;
+ struct class_datum *cls = NULL;
- if (s) {
- src = symtab_search(&db->p_types, s);
- if (src == NULL) {
- pr_info("source type %s does not exist\n", s);
- return false;
- }
- }
+ if (s) {
+ src = symtab_search(&db->p_types, s);
+ if (src == NULL) {
+ pr_info("source type %s does not exist\n", s);
+ return false;
+ }
+ }
- if (t) {
- tgt = symtab_search(&db->p_types, t);
- if (tgt == NULL) {
- pr_info("target type %s does not exist\n", t);
- return false;
- }
- }
+ if (t) {
+ tgt = symtab_search(&db->p_types, t);
+ if (tgt == NULL) {
+ pr_info("target type %s does not exist\n", t);
+ return false;
+ }
+ }
- if (c) {
- cls = symtab_search(&db->p_classes, c);
- if (cls == NULL) {
- pr_info("class %s does not exist\n", c);
- return false;
- }
- }
+ if (c) {
+ cls = symtab_search(&db->p_classes, c);
+ if (cls == NULL) {
+ pr_info("class %s does not exist\n", c);
+ return false;
+ }
+ }
- u16 low, high;
+ u16 low, high;
- if (range) {
- if (strchr(range, '-')) {
- sscanf(range, "%hx-%hx", &low, &high);
- } else {
- sscanf(range, "%hx", &low);
- high = low;
- }
- } else {
- low = 0;
- high = 0xFFFF;
- }
+ if (range) {
+ if (strchr(range, '-')) {
+ sscanf(range, "%hx-%hx", &low, &high);
+ } else {
+ sscanf(range, "%hx", &low);
+ high = low;
+ }
+ } else {
+ low = 0;
+ high = 0xFFFF;
+ }
- add_xperm_rule_raw(db, src, tgt, cls, low, high, effect, invert);
- return true;
+ add_xperm_rule_raw(db, src, tgt, cls, low, high, effect, invert);
+ return true;
}
static bool add_type_rule(struct policydb *db, const char *s, const char *t,
- const char *c, const char *d, int effect)
+ const char *c, const char *d, int effect)
{
- struct type_datum *src, *tgt, *def;
- struct class_datum *cls;
+ struct type_datum *src, *tgt, *def;
+ struct class_datum *cls;
- src = symtab_search(&db->p_types, s);
- if (src == NULL) {
- pr_info("source type %s does not exist\n", s);
- return false;
- }
- tgt = symtab_search(&db->p_types, t);
- if (tgt == NULL) {
- pr_info("target type %s does not exist\n", t);
- return false;
- }
- cls = symtab_search(&db->p_classes, c);
- if (cls == NULL) {
- pr_info("class %s does not exist\n", c);
- return false;
- }
- def = symtab_search(&db->p_types, d);
- if (def == NULL) {
- pr_info("default type %s does not exist\n", d);
- return false;
- }
+ src = symtab_search(&db->p_types, s);
+ if (src == NULL) {
+ pr_info("source type %s does not exist\n", s);
+ return false;
+ }
+ tgt = symtab_search(&db->p_types, t);
+ if (tgt == NULL) {
+ pr_info("target type %s does not exist\n", t);
+ return false;
+ }
+ cls = symtab_search(&db->p_classes, c);
+ if (cls == NULL) {
+ pr_info("class %s does not exist\n", c);
+ return false;
+ }
+ def = symtab_search(&db->p_types, d);
+ if (def == NULL) {
+ pr_info("default type %s does not exist\n", d);
+ return false;
+ }
- struct avtab_key key;
- key.source_type = src->value;
- key.target_type = tgt->value;
- key.target_class = cls->value;
- key.specified = effect;
+ struct avtab_key key;
+ key.source_type = src->value;
+ key.target_type = tgt->value;
+ key.target_class = cls->value;
+ key.specified = effect;
- struct avtab_node *node = get_avtab_node(db, &key, NULL);
- node->datum.u.data = def->value;
+ struct avtab_node *node = get_avtab_node(db, &key, NULL);
+ node->datum.u.data = def->value;
- return true;
+ return true;
}
// 5.9.0 : static inline int hashtab_insert(struct hashtab *h, void *key, void
@@ -460,287 +459,287 @@ static bool add_type_rule(struct policydb *db, const char *s, const char *t,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
static u32 filenametr_hash(const void *k)
{
- const struct filename_trans_key *ft = k;
- unsigned long hash;
- unsigned int byte_num;
- unsigned char focus;
+ const struct filename_trans_key *ft = k;
+ unsigned long hash;
+ unsigned int byte_num;
+ unsigned char focus;
- hash = ft->ttype ^ ft->tclass;
+ hash = ft->ttype ^ ft->tclass;
- byte_num = 0;
- while ((focus = ft->name[byte_num++]))
- hash = partial_name_hash(focus, hash);
- return hash;
+ byte_num = 0;
+ while ((focus = ft->name[byte_num++]))
+ hash = partial_name_hash(focus, hash);
+ return hash;
}
static int filenametr_cmp(const void *k1, const void *k2)
{
- const struct filename_trans_key *ft1 = k1;
- const struct filename_trans_key *ft2 = k2;
- int v;
+ const struct filename_trans_key *ft1 = k1;
+ const struct filename_trans_key *ft2 = k2;
+ int v;
- v = ft1->ttype - ft2->ttype;
- if (v)
- return v;
+ v = ft1->ttype - ft2->ttype;
+ if (v)
+ return v;
- v = ft1->tclass - ft2->tclass;
- if (v)
- return v;
+ v = ft1->tclass - ft2->tclass;
+ if (v)
+ return v;
- return strcmp(ft1->name, ft2->name);
+ return strcmp(ft1->name, ft2->name);
}
static const struct hashtab_key_params filenametr_key_params = {
- .hash = filenametr_hash,
- .cmp = filenametr_cmp,
+ .hash = filenametr_hash,
+ .cmp = filenametr_cmp,
};
#endif
static bool add_filename_trans(struct policydb *db, const char *s,
- const char *t, const char *c, const char *d,
- const char *o)
+ const char *t, const char *c, const char *d,
+ const char *o)
{
- struct type_datum *src, *tgt, *def;
- struct class_datum *cls;
+ struct type_datum *src, *tgt, *def;
+ struct class_datum *cls;
- src = symtab_search(&db->p_types, s);
- if (src == NULL) {
- pr_warn("source type %s does not exist\n", s);
- return false;
- }
- tgt = symtab_search(&db->p_types, t);
- if (tgt == NULL) {
- pr_warn("target type %s does not exist\n", t);
- return false;
- }
- cls = symtab_search(&db->p_classes, c);
- if (cls == NULL) {
- pr_warn("class %s does not exist\n", c);
- return false;
- }
- def = symtab_search(&db->p_types, d);
- if (def == NULL) {
- pr_warn("default type %s does not exist\n", d);
- return false;
- }
+ src = symtab_search(&db->p_types, s);
+ if (src == NULL) {
+ pr_warn("source type %s does not exist\n", s);
+ return false;
+ }
+ tgt = symtab_search(&db->p_types, t);
+ if (tgt == NULL) {
+ pr_warn("target type %s does not exist\n", t);
+ return false;
+ }
+ cls = symtab_search(&db->p_classes, c);
+ if (cls == NULL) {
+ pr_warn("class %s does not exist\n", c);
+ return false;
+ }
+ def = symtab_search(&db->p_types, d);
+ if (def == NULL) {
+ pr_warn("default type %s does not exist\n", d);
+ return false;
+ }
- struct filename_trans_key key;
- key.ttype = tgt->value;
- key.tclass = cls->value;
- key.name = (char *)o;
+ struct filename_trans_key key;
+ key.ttype = tgt->value;
+ key.tclass = cls->value;
+ key.name = (char *)o;
- struct filename_trans_datum *last = NULL;
+ struct filename_trans_datum *last = NULL;
- struct filename_trans_datum *trans =
- policydb_filenametr_search(db, &key);
- while (trans) {
- if (ebitmap_get_bit(&trans->stypes, src->value - 1)) {
- // Duplicate, overwrite existing data and return
- trans->otype = def->value;
- return true;
- }
- if (trans->otype == def->value)
- break;
- last = trans;
- trans = trans->next;
- }
+ struct filename_trans_datum *trans =
+ policydb_filenametr_search(db, &key);
+ while (trans) {
+ if (ebitmap_get_bit(&trans->stypes, src->value - 1)) {
+ // Duplicate, overwrite existing data and return
+ trans->otype = def->value;
+ return true;
+ }
+ if (trans->otype == def->value)
+ break;
+ last = trans;
+ trans = trans->next;
+ }
- if (trans == NULL) {
- trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
- 1, GFP_ATOMIC);
- struct filename_trans_key *new_key =
- (struct filename_trans_key *)kmalloc(sizeof(*new_key),
- GFP_ATOMIC);
- *new_key = key;
- new_key->name = kstrdup(key.name, GFP_ATOMIC);
- trans->next = last;
- trans->otype = def->value;
- hashtab_insert(&db->filename_trans, new_key, trans,
- filenametr_key_params);
- }
+ if (trans == NULL) {
+ trans = (struct filename_trans_datum *)kcalloc(1 ,sizeof(*trans),
+ GFP_ATOMIC);
+ struct filename_trans_key *new_key =
+ (struct filename_trans_key *)kmalloc(sizeof(*new_key),
+ GFP_ATOMIC);
+ *new_key = key;
+ new_key->name = kstrdup(key.name, GFP_ATOMIC);
+ trans->next = last;
+ trans->otype = def->value;
+ hashtab_insert(&db->filename_trans, new_key, trans,
+ filenametr_key_params);
+ }
- db->compat_filename_trans_count++;
- return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0;
+ db->compat_filename_trans_count++;
+ return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0;
}
static bool add_genfscon(struct policydb *db, const char *fs_name,
- const char *path, const char *context)
+ const char *path, const char *context)
{
- return false;
+ return false;
}
static void *ksu_realloc(void *old, size_t new_size, size_t old_size)
{
- // we can't use krealloc, because it may be read-only
- void *new = kzalloc(new_size, GFP_ATOMIC);
- if (!new) {
- return NULL;
- }
- if (old_size) {
- memcpy(new, old, old_size);
- }
- // we can't use kfree, because it may be read-only
- // there maybe some leaks, maybe we can check ptr_write, but it's not a big deal
- // kfree(old);
- return new;
+ // we can't use krealloc, because it may be read-only
+ void *new = kzalloc(new_size, GFP_ATOMIC);
+ if (!new) {
+ return NULL;
+ }
+ if (old_size) {
+ memcpy(new, old, old_size);
+ }
+ // we can't use kfree, because it may be read-only
+ // there maybe some leaks, maybe we can check ptr_write, but it's not a big deal
+ // kfree(old);
+ return new;
}
static bool add_type(struct policydb *db, const char *type_name, bool attr)
{
- struct type_datum *type = symtab_search(&db->p_types, type_name);
- if (type) {
- pr_warn("Type %s already exists\n", type_name);
- return true;
- }
+ struct type_datum *type = symtab_search(&db->p_types, type_name);
+ if (type) {
+ pr_warn("Type %s already exists\n", type_name);
+ return true;
+ }
- u32 value = ++db->p_types.nprim;
- type = (struct type_datum *)kzalloc(sizeof(struct type_datum),
- GFP_ATOMIC);
- if (!type) {
- pr_err("add_type: alloc type_datum failed.\n");
- return false;
- }
+ u32 value = ++db->p_types.nprim;
+ type = (struct type_datum *)kzalloc(sizeof(struct type_datum),
+ GFP_ATOMIC);
+ if (!type) {
+ pr_err("add_type: alloc type_datum failed.\n");
+ return false;
+ }
- type->primary = 1;
- type->value = value;
- type->attribute = attr;
+ type->primary = 1;
+ type->value = value;
+ type->attribute = attr;
- char *key = kstrdup(type_name, GFP_ATOMIC);
- if (!key) {
- pr_err("add_type: alloc key failed.\n");
- return false;
- }
+ char *key = kstrdup(type_name, GFP_ATOMIC);
+ if (!key) {
+ pr_err("add_type: alloc key failed.\n");
+ return false;
+ }
- if (symtab_insert(&db->p_types, key, type)) {
- pr_err("add_type: insert symtab failed.\n");
- return false;
- }
+ if (symtab_insert(&db->p_types, key, type)) {
+ pr_err("add_type: insert symtab failed.\n");
+ return false;
+ }
- struct ebitmap *new_type_attr_map_array =
- ksu_realloc(db->type_attr_map_array,
- value * sizeof(struct ebitmap),
- (value - 1) * sizeof(struct ebitmap));
+ struct ebitmap *new_type_attr_map_array =
+ ksu_realloc(db->type_attr_map_array,
+ value * sizeof(struct ebitmap),
+ (value - 1) * sizeof(struct ebitmap));
- if (!new_type_attr_map_array) {
- pr_err("add_type: alloc type_attr_map_array failed\n");
- return false;
- }
+ if (!new_type_attr_map_array) {
+ pr_err("add_type: alloc type_attr_map_array failed\n");
+ return false;
+ }
- struct type_datum **new_type_val_to_struct =
- ksu_realloc(db->type_val_to_struct,
- sizeof(*db->type_val_to_struct) * value,
- sizeof(*db->type_val_to_struct) * (value - 1));
+ struct type_datum **new_type_val_to_struct =
+ ksu_realloc(db->type_val_to_struct,
+ sizeof(*db->type_val_to_struct) * value,
+ sizeof(*db->type_val_to_struct) * (value - 1));
- if (!new_type_val_to_struct) {
- pr_err("add_type: alloc type_val_to_struct failed\n");
- return false;
- }
+ if (!new_type_val_to_struct) {
+ pr_err("add_type: alloc type_val_to_struct failed\n");
+ return false;
+ }
- char **new_val_to_name_types =
- ksu_realloc(db->sym_val_to_name[SYM_TYPES],
- sizeof(char *) * value,
- sizeof(char *) * (value - 1));
- if (!new_val_to_name_types) {
- pr_err("add_type: alloc val_to_name failed\n");
- return false;
- }
+ char **new_val_to_name_types =
+ ksu_realloc(db->sym_val_to_name[SYM_TYPES],
+ sizeof(char *) * value,
+ sizeof(char *) * (value - 1));
+ if (!new_val_to_name_types) {
+ pr_err("add_type: alloc val_to_name failed\n");
+ return false;
+ }
- db->type_attr_map_array = new_type_attr_map_array;
- ebitmap_init(&db->type_attr_map_array[value - 1]);
- ebitmap_set_bit(&db->type_attr_map_array[value - 1], value - 1, 1);
+ db->type_attr_map_array = new_type_attr_map_array;
+ ebitmap_init(&db->type_attr_map_array[value - 1]);
+ ebitmap_set_bit(&db->type_attr_map_array[value - 1], value - 1, 1);
- db->type_val_to_struct = new_type_val_to_struct;
- db->type_val_to_struct[value - 1] = type;
+ db->type_val_to_struct = new_type_val_to_struct;
+ db->type_val_to_struct[value - 1] = type;
- db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
- db->sym_val_to_name[SYM_TYPES][value - 1] = key;
+ db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types;
+ db->sym_val_to_name[SYM_TYPES][value - 1] = key;
- int i;
- for (i = 0; i < db->p_roles.nprim; ++i) {
- ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
- 1);
- }
+ int i;
+ for (i = 0; i < db->p_roles.nprim; ++i) {
+ ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1,
+ 1);
+ }
- return true;
+ return true;
}
static bool set_type_state(struct policydb *db, const char *type_name,
- bool permissive)
+ bool permissive)
{
- struct type_datum *type;
- if (type_name == NULL) {
- struct hashtab_node *node;
- ksu_hashtab_for_each(db->p_types.table, node)
- {
- type = (struct type_datum *)(node->datum);
- if (ebitmap_set_bit(&db->permissive_map, type->value,
- permissive))
- pr_info("Could not set bit in permissive map\n");
- };
- } else {
- type = (struct type_datum *)symtab_search(&db->p_types,
- type_name);
- if (type == NULL) {
- pr_info("type %s does not exist\n", type_name);
- return false;
- }
- if (ebitmap_set_bit(&db->permissive_map, type->value,
- permissive)) {
- pr_info("Could not set bit in permissive map\n");
- return false;
- }
- }
- return true;
+ struct type_datum *type;
+ if (type_name == NULL) {
+ struct hashtab_node *node;
+ ksu_hashtab_for_each(db->p_types.table, node)
+ {
+ type = (struct type_datum *)(node->datum);
+ if (ebitmap_set_bit(&db->permissive_map, type->value,
+ permissive))
+ pr_info("Could not set bit in permissive map\n");
+ };
+ } else {
+ type = (struct type_datum *)symtab_search(&db->p_types,
+ type_name);
+ if (type == NULL) {
+ pr_info("type %s does not exist\n", type_name);
+ return false;
+ }
+ if (ebitmap_set_bit(&db->permissive_map, type->value,
+ permissive)) {
+ pr_info("Could not set bit in permissive map\n");
+ return false;
+ }
+ }
+ return true;
}
static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
- struct type_datum *attr)
+ struct type_datum *attr)
{
- struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1];
- ebitmap_set_bit(sattr, attr->value - 1, 1);
+ struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1];
+ ebitmap_set_bit(sattr, attr->value - 1, 1);
- struct hashtab_node *node;
- struct constraint_node *n;
- struct constraint_expr *e;
- ksu_hashtab_for_each(db->p_classes.table, node)
- {
- struct class_datum *cls = (struct class_datum *)(node->datum);
- for (n = cls->constraints; n; n = n->next) {
- for (e = n->expr; e; e = e->next) {
- if (e->expr_type == CEXPR_NAMES &&
- ebitmap_get_bit(&e->type_names->types,
- attr->value - 1)) {
- ebitmap_set_bit(&e->names,
- type->value - 1, 1);
- }
- }
- }
- };
+ struct hashtab_node *node;
+ struct constraint_node *n;
+ struct constraint_expr *e;
+ ksu_hashtab_for_each(db->p_classes.table, node)
+ {
+ struct class_datum *cls = (struct class_datum *)(node->datum);
+ for (n = cls->constraints; n; n = n->next) {
+ for (e = n->expr; e; e = e->next) {
+ if (e->expr_type == CEXPR_NAMES &&
+ ebitmap_get_bit(&e->type_names->types,
+ attr->value - 1)) {
+ ebitmap_set_bit(&e->names,
+ type->value - 1, 1);
+ }
+ }
+ }
+ };
}
static bool add_typeattribute(struct policydb *db, const char *type,
- const char *attr)
+ const char *attr)
{
- struct type_datum *type_d = symtab_search(&db->p_types, type);
- if (type_d == NULL) {
- pr_info("type %s does not exist\n", type);
- return false;
- } else if (type_d->attribute) {
- pr_info("type %s is an attribute\n", attr);
- return false;
- }
+ struct type_datum *type_d = symtab_search(&db->p_types, type);
+ if (type_d == NULL) {
+ pr_info("type %s does not exist\n", type);
+ return false;
+ } else if (type_d->attribute) {
+ pr_info("type %s is an attribute\n", attr);
+ return false;
+ }
- struct type_datum *attr_d = symtab_search(&db->p_types, attr);
- if (attr_d == NULL) {
- pr_info("attribute %s does not exist\n", type);
- return false;
- } else if (!attr_d->attribute) {
- pr_info("type %s is not an attribute \n", attr);
- return false;
- }
+ struct type_datum *attr_d = symtab_search(&db->p_types, attr);
+ if (attr_d == NULL) {
+ pr_info("attribute %s does not exist\n", type);
+ return false;
+ } else if (!attr_d->attribute) {
+ pr_info("type %s is not an attribute \n", attr);
+ return false;
+ }
- add_typeattribute_raw(db, type_d, attr_d);
- return true;
+ add_typeattribute_raw(db, type_d, attr_d);
+ return true;
}
//////////////////////////////////////////////////////////////////////////
@@ -748,106 +747,106 @@ static bool add_typeattribute(struct policydb *db, const char *type,
// Operation on types
bool ksu_type(struct policydb *db, const char *name, const char *attr)
{
- return add_type(db, name, false) && add_typeattribute(db, name, attr);
+ return add_type(db, name, false) && add_typeattribute(db, name, attr);
}
bool ksu_attribute(struct policydb *db, const char *name)
{
- return add_type(db, name, true);
+ return add_type(db, name, true);
}
bool ksu_permissive(struct policydb *db, const char *type)
{
- return set_type_state(db, type, true);
+ return set_type_state(db, type, true);
}
bool ksu_enforce(struct policydb *db, const char *type)
{
- return set_type_state(db, type, false);
+ return set_type_state(db, type, false);
}
bool ksu_typeattribute(struct policydb *db, const char *type, const char *attr)
{
- return add_typeattribute(db, type, attr);
+ return add_typeattribute(db, type, attr);
}
bool ksu_exists(struct policydb *db, const char *type)
{
- return symtab_search(&db->p_types, type) != NULL;
+ return symtab_search(&db->p_types, type) != NULL;
}
// Access vector rules
bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
- const char *cls, const char *perm)
+ const char *cls, const char *perm)
{
- return add_rule(db, src, tgt, cls, perm, AVTAB_ALLOWED, false);
+ return add_rule(db, src, tgt, cls, perm, AVTAB_ALLOWED, false);
}
bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
- const char *cls, const char *perm)
+ const char *cls, const char *perm)
{
- return add_rule(db, src, tgt, cls, perm, AVTAB_ALLOWED, true);
+ return add_rule(db, src, tgt, cls, perm, AVTAB_ALLOWED, true);
}
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
- const char *cls, const char *perm)
+ const char *cls, const char *perm)
{
- return add_rule(db, src, tgt, cls, perm, AVTAB_AUDITALLOW, false);
+ return add_rule(db, src, tgt, cls, perm, AVTAB_AUDITALLOW, false);
}
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
- const char *cls, const char *perm)
+ const char *cls, const char *perm)
{
- return add_rule(db, src, tgt, cls, perm, AVTAB_AUDITDENY, true);
+ return add_rule(db, src, tgt, cls, perm, AVTAB_AUDITDENY, true);
}
// Extended permissions access vector rules
bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
- const char *cls, const char *range)
+ const char *cls, const char *range)
{
- return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_ALLOWED,
- false);
+ return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_ALLOWED,
+ false);
}
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
- const char *cls, const char *range)
+ const char *cls, const char *range)
{
- return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_AUDITALLOW,
- false);
+ return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_AUDITALLOW,
+ false);
}
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
- const char *cls, const char *range)
+ const char *cls, const char *range)
{
- return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_DONTAUDIT,
- false);
+ return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_DONTAUDIT,
+ false);
}
// Type rules
bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
- const char *cls, const char *def, const char *obj)
+ const char *cls, const char *def, const char *obj)
{
- if (obj) {
- return add_filename_trans(db, src, tgt, cls, def, obj);
- } else {
- return add_type_rule(db, src, tgt, cls, def, AVTAB_TRANSITION);
- }
+ if (obj) {
+ return add_filename_trans(db, src, tgt, cls, def, obj);
+ } else {
+ return add_type_rule(db, src, tgt, cls, def, AVTAB_TRANSITION);
+ }
}
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
- const char *cls, const char *def)
+ const char *cls, const char *def)
{
- return add_type_rule(db, src, tgt, cls, def, AVTAB_CHANGE);
+ return add_type_rule(db, src, tgt, cls, def, AVTAB_CHANGE);
}
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
- const char *cls, const char *def)
+ const char *cls, const char *def)
{
- return add_type_rule(db, src, tgt, cls, def, AVTAB_MEMBER);
+ return add_type_rule(db, src, tgt, cls, def, AVTAB_MEMBER);
}
// File system labeling
bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
- const char *ctx)
+ const char *ctx)
{
- return add_genfscon(db, fs_name, path, ctx);
+ return add_genfscon(db, fs_name, path, ctx);
}
diff --git a/kernel/selinux/sepolicy.h b/kernel/selinux/sepolicy.h
index 675d149..fd062ce 100644
--- a/kernel/selinux/sepolicy.h
+++ b/kernel/selinux/sepolicy.h
@@ -15,32 +15,32 @@ 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);
+ const char *cls, const char *perm);
bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
- const char *cls, const char *perm);
+ const char *cls, const char *perm);
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
- const char *cls, const char *perm);
+ const char *cls, const char *perm);
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
- const char *cls, const char *perm);
+ 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);
+ const char *cls, const char *range);
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
- const char *cls, const char *range);
+ const char *cls, const char *range);
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
- const char *cls, const char *range);
+ 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);
+ 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);
+ 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);
+ 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);
+ const char *ctx);
#endif
diff --git a/kernel/setuid_hook.c b/kernel/setuid_hook.c
new file mode 100644
index 0000000..44dffd9
--- /dev/null
+++ b/kernel/setuid_hook.c
@@ -0,0 +1,171 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "allowlist.h"
+#include "setuid_hook.h"
+#include "feature.h"
+#include "klog.h" // IWYU pragma: keep
+#include "manager.h"
+#include "selinux/selinux.h"
+#include "seccomp_cache.h"
+#include "supercalls.h"
+#include "syscall_hook_manager.h"
+#include "kernel_umount.h"
+#include "app_profile.h"
+
+static bool ksu_enhanced_security_enabled = false;
+
+static int enhanced_security_feature_get(u64 *value)
+{
+ *value = ksu_enhanced_security_enabled ? 1 : 0;
+ return 0;
+}
+
+static int enhanced_security_feature_set(u64 value)
+{
+ bool enable = value != 0;
+ ksu_enhanced_security_enabled = enable;
+ pr_info("enhanced_security: set to %d\n", enable);
+ return 0;
+}
+
+static const struct ksu_feature_handler enhanced_security_handler = {
+ .feature_id = KSU_FEATURE_ENHANCED_SECURITY,
+ .name = "enhanced_security",
+ .get_handler = enhanced_security_feature_get,
+ .set_handler = enhanced_security_feature_set,
+};
+
+static inline bool is_allow_su()
+{
+ if (is_manager()) {
+ // we are manager, allow!
+ return true;
+ }
+ return ksu_is_allow_uid_for_current(current_uid().val);
+}
+
+int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid)
+{
+ uid_t new_uid = ruid;
+ uid_t old_uid = current_uid().val;
+
+ pr_info("handle_setresuid from %d to %d\n", old_uid, new_uid);
+
+ // if old process is root, ignore it.
+ if (old_uid != 0 && ksu_enhanced_security_enabled) {
+ // disallow any non-ksu domain escalation from non-root to root!
+ // euid is what we care about here as it controls permission
+ if (unlikely(euid == 0)) {
+ if (!is_ksu_domain()) {
+ pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
+ current->pid, current->comm, old_uid, new_uid);
+ force_sig(SIGKILL);
+ return 0;
+ }
+ }
+ // disallow appuid decrease to any other uid if it is not allowed to su
+ if (is_appuid(old_uid)) {
+ if (euid < current_euid().val && !ksu_is_allow_uid_for_current(old_uid)) {
+ pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
+ current->pid, current->comm, old_uid, new_uid);
+ force_sig(SIGKILL);
+ return 0;
+ }
+ }
+ return 0;
+ }
+
+ // if on private space, see if its possibly the manager
+ if (new_uid > PER_USER_RANGE && new_uid % PER_USER_RANGE == ksu_get_manager_uid()) {
+ ksu_set_manager_uid(new_uid);
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
+ if (ksu_get_manager_uid() == new_uid) {
+ pr_info("install fd for manager: %d\n", new_uid);
+ ksu_install_fd();
+ spin_lock_irq(¤t->sighand->siglock);
+ ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot);
+ ksu_set_task_tracepoint_flag(current);
+ spin_unlock_irq(¤t->sighand->siglock);
+ return 0;
+ }
+
+ if (ksu_is_allow_uid_for_current(new_uid)) {
+ if (current->seccomp.mode == SECCOMP_MODE_FILTER &&
+ current->seccomp.filter) {
+ spin_lock_irq(¤t->sighand->siglock);
+ ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot);
+ spin_unlock_irq(¤t->sighand->siglock);
+ }
+ ksu_set_task_tracepoint_flag(current);
+ } else {
+ ksu_clear_task_tracepoint_flag_if_needed(current);
+ }
+#else
+ if (ksu_is_allow_uid_for_current(new_uid)) {
+ spin_lock_irq(¤t->sighand->siglock);
+ disable_seccomp();
+ spin_unlock_irq(¤t->sighand->siglock);
+
+ if (ksu_get_manager_uid() == new_uid) {
+ pr_info("install fd for ksu manager(uid=%d)\n",
+ new_uid);
+ ksu_install_fd();
+ }
+
+ return 0;
+ }
+#endif
+
+ // Handle kernel umount
+ ksu_handle_umount(old_uid, new_uid);
+
+ return 0;
+}
+
+void ksu_setuid_hook_init(void)
+{
+ ksu_kernel_umount_init();
+ if (ksu_register_feature_handler(&enhanced_security_handler)) {
+ pr_err("Failed to register enhanced security feature handler\n");
+ }
+}
+
+void ksu_setuid_hook_exit(void)
+{
+ pr_info("ksu_core_exit\n");
+ ksu_kernel_umount_exit();
+ ksu_unregister_feature_handler(KSU_FEATURE_ENHANCED_SECURITY);
+}
diff --git a/kernel/setuid_hook.h b/kernel/setuid_hook.h
new file mode 100644
index 0000000..fc5b93a
--- /dev/null
+++ b/kernel/setuid_hook.h
@@ -0,0 +1,14 @@
+#ifndef __KSU_H_KSU_CORE
+#define __KSU_H_KSU_CORE
+
+#include
+#include
+#include "apk_sign.h"
+#include
+
+void ksu_setuid_hook_init(void);
+void ksu_setuid_hook_exit(void);
+
+int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid);
+
+#endif
diff --git a/kernel/sucompat.c b/kernel/sucompat.c
index ca94a60..009d610 100644
--- a/kernel/sucompat.c
+++ b/kernel/sucompat.c
@@ -1,337 +1,188 @@
-#include
-#include
+#include "linux/compiler.h"
+#include "linux/printk.h"
#include
#include
-#include
#include
-#include
#include
#include
#include
#include
+#include
-#include "objsec.h"
#include "allowlist.h"
-#include "arch.h"
+#include "feature.h"
#include "klog.h" // IWYU pragma: keep
#include "ksud.h"
-#include "kernel_compat.h"
+#include "sucompat.h"
+#include "app_profile.h"
+#include "syscall_hook_manager.h"
+
+
+#include "sulog.h"
#define SU_PATH "/system/bin/su"
#define SH_PATH "/system/bin/sh"
-extern void escape_to_root();
+bool ksu_su_compat_enabled __read_mostly = true;
-#ifndef CONFIG_KSU_KPROBES_HOOK
-static bool ksu_sucompat_hook_state __read_mostly = true;
-#endif
+static int su_compat_feature_get(u64 *value)
+{
+ *value = ksu_su_compat_enabled ? 1 : 0;
+ return 0;
+}
+
+static int su_compat_feature_set(u64 value)
+{
+ bool enable = value != 0;
+ ksu_su_compat_enabled = enable;
+ pr_info("su_compat: set to %d\n", enable);
+ return 0;
+}
+
+static const struct ksu_feature_handler su_compat_handler = {
+ .feature_id = KSU_FEATURE_SU_COMPAT,
+ .name = "su_compat",
+ .get_handler = su_compat_feature_get,
+ .set_handler = su_compat_feature_set,
+};
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;
+ // 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;
+ return copy_to_user(p, d, len) ? NULL : p;
}
static char __user *sh_user_path(void)
{
- static const char sh_path[] = "/system/bin/sh";
+ static const char sh_path[] = "/system/bin/sh";
- return userspace_stack_buffer(sh_path, sizeof(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;
+ static const char ksud_path[] = KSUD_PATH;
- return userspace_stack_buffer(ksud_path, sizeof(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)
+ int *__unused_flags)
{
- const char su[] = SU_PATH;
+ const char su[] = SU_PATH;
-#ifndef CONFIG_KSU_KPROBES_HOOK
- if (!ksu_sucompat_hook_state) {
- return 0;
- }
+ if (!ksu_is_allow_uid_for_current(current_uid().val)) {
+ return 0;
+ }
+
+ char path[sizeof(su) + 1];
+ memset(path, 0, sizeof(path));
+ strncpy_from_user_nofault(path, *filename_user, sizeof(path));
+
+ if (unlikely(!memcmp(path, su, sizeof(su)))) {
+#if __SULOG_GATE
+ ksu_sulog_report_syscall(current_uid().val, NULL, "faccessat", path);
#endif
+ pr_info("faccessat su->sh!\n");
+ *filename_user = sh_user_path();
+ }
- 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;
+ return 0;
}
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
{
- // const char sh[] = SH_PATH;
- const char su[] = SU_PATH;
+ // const char sh[] = SH_PATH;
+ const char su[] = SU_PATH;
-#ifndef CONFIG_KSU_KPROBES_HOOK
- if (!ksu_sucompat_hook_state) {
- return 0;
- }
-#endif
- if (!ksu_is_allow_uid(current_uid().val)) {
- return 0;
- }
+ if (!ksu_is_allow_uid_for_current(current_uid().val)) {
+ return 0;
+ }
- if (unlikely(!filename_user)) {
- return 0;
- }
+ if (unlikely(!filename_user)) {
+ return 0;
+ }
- char path[sizeof(su) + 1];
- memset(path, 0, sizeof(path));
+ 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));
+ // 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));
+ 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();
- }
+ if (unlikely(!memcmp(path, su, sizeof(su)))) {
+#if __SULOG_GATE
+ ksu_sulog_report_syscall(current_uid().val, NULL, "newfstatat", path);
+#endif
+ pr_info("newfstatat su->sh!\n");
+ *filename_user = sh_user_path();
+ }
#endif
- return 0;
+ return 0;
}
-int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
- void *envp, int *flags)
+int ksu_handle_execve_sucompat(const char __user **filename_user,
+ void *__never_use_argv, void *__never_use_envp,
+ int *__never_use_flags)
{
- return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, flags);
-}
+ const char su[] = SU_PATH;
+ char path[sizeof(su) + 1];
-// the call from execve_handler_pre won't provided correct value for __never_use_argument, use them after fix execve_handler_pre, keeping them for consistence for manually patched code
-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 sh[] = KSUD_PATH;
- const char su[] = SU_PATH;
+ if (unlikely(!filename_user))
+ return 0;
-#ifndef CONFIG_KSU_KPROBES_HOOK
- if (!ksu_sucompat_hook_state) {
- return 0;
- }
-#endif
- if (unlikely(!filename_ptr))
- return 0;
+ memset(path, 0, sizeof(path));
+ strncpy_from_user_nofault(path, *filename_user, sizeof(path));
- filename = *filename_ptr;
- if (IS_ERR(filename)) {
- return 0;
- }
+ if (likely(memcmp(path, su, sizeof(su))))
+ return 0;
- if (likely(memcmp(filename->name, su, sizeof(su))))
- return 0;
+#if __SULOG_GATE
+ bool is_allowed = ksu_is_allow_uid_for_current(current_uid().val);
+ ksu_sulog_report_syscall(current_uid().val, NULL, "execve", path);
+
+ if (!is_allowed)
+ return 0;
- if (!ksu_is_allow_uid(current_uid().val))
- return 0;
-
- pr_info("do_execveat_common su found\n");
- memcpy((void *)filename->name, sh, sizeof(sh));
-
- 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 CONFIG_KSU_KPROBES_HOOK
- if (!ksu_sucompat_hook_state){
- 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();
-
- escape_to_root();
-
- return 0;
-}
-
-// dummified
-int ksu_handle_devpts(struct inode *inode)
-{
- return 0;
-}
-
-int __ksu_handle_devpts(struct inode *inode)
-{
-
-#ifndef CONFIG_KSU_KPROBES_HOOK
- if (!ksu_sucompat_hook_state)
- return 0;
-#endif
-
- if (!current->mm) {
- return 0;
- }
-
- uid_t uid = current_uid().val;
- if (uid % 100000 < 10000) {
- // not untrusted_app, ignore it
- return 0;
- }
-
- if (likely(!ksu_is_allow_uid(uid)))
- return 0;
-
- struct inode_security_struct *sec = selinux_inode(inode);
-
- if (ksu_devpts_sid && sec)
- sec->sid = ksu_devpts_sid;
-
- return 0;
-}
-
-#ifdef CONFIG_KSU_KPROBES_HOOK
-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);
-}
-
-static struct kprobe *su_kps[4];
-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;
+ ksu_sulog_report_su_attempt(current_uid().val, NULL, path, is_allowed);
#else
- inode = (struct inode *)PT_REGS_PARM2(regs);
+ if (!ksu_is_allow_uid_for_current(current_uid().val)) {
+ return 0;
+ }
#endif
- return ksu_handle_devpts(inode);
+ pr_info("sys_execve su found\n");
+ *filename_user = ksud_user_path();
+
+ escape_with_root_profile();
+
+ return 0;
}
-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;
-}
-
-#endif
-
-// sucompat: permited process can execute 'su' to gain root access.
+// sucompat: permitted process can execute 'su' to gain root access.
void ksu_sucompat_init()
{
-#ifdef CONFIG_KSU_KPROBES_HOOK
- 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_sucompat_hook_state = true;
- pr_info("ksu_sucompat_init: hooks enabled: execve/execveat_su, faccessat, stat\n");
-#endif
+ if (ksu_register_feature_handler(&su_compat_handler)) {
+ pr_err("Failed to register su_compat feature handler\n");
+ }
}
void ksu_sucompat_exit()
{
-#ifdef CONFIG_KSU_KPROBES_HOOK
- int i;
- for (i = 0; i < ARRAY_SIZE(su_kps); i++) {
- destroy_kprobe(&su_kps[i]);
- }
-#else
- ksu_sucompat_hook_state = false;
- pr_info("ksu_sucompat_exit: hooks disabled: execve/execveat_su, faccessat, stat\n");
-#endif
-}
+ ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT);
+}
\ No newline at end of file
diff --git a/kernel/sucompat.h b/kernel/sucompat.h
new file mode 100644
index 0000000..82161f7
--- /dev/null
+++ b/kernel/sucompat.h
@@ -0,0 +1,18 @@
+#ifndef __KSU_H_SUCOMPAT
+#define __KSU_H_SUCOMPAT
+#include
+
+extern bool ksu_su_compat_enabled;
+
+void ksu_sucompat_init(void);
+void ksu_sucompat_exit(void);
+
+// Handler functions exported for hook_manager
+int ksu_handle_faccessat(int *dfd, const char __user **filename_user,
+ int *mode, int *__unused_flags);
+int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
+int ksu_handle_execve_sucompat(const char __user **filename_user,
+ void *__never_use_argv, void *__never_use_envp,
+ int *__never_use_flags);
+
+#endif
\ No newline at end of file
diff --git a/kernel/sulog.c b/kernel/sulog.c
new file mode 100644
index 0000000..ef2263f
--- /dev/null
+++ b/kernel/sulog.c
@@ -0,0 +1,340 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "klog.h"
+#include "sulog.h"
+#include "ksu.h"
+
+#if __SULOG_GATE
+
+struct dedup_entry dedup_tbl[SULOG_COMM_LEN];
+static DEFINE_SPINLOCK(dedup_lock);
+static LIST_HEAD(sulog_queue);
+static struct workqueue_struct *sulog_workqueue;
+static struct work_struct sulog_work;
+static bool sulog_enabled = true;
+
+static void get_timestamp(char *buf, size_t len)
+{
+ struct timespec64 ts;
+ struct tm tm;
+
+ ktime_get_real_ts64(&ts);
+ time64_to_tm(ts.tv_sec - sys_tz.tz_minuteswest * 60, 0, &tm);
+
+ snprintf(buf, len, "%04ld-%02d-%02d %02d:%02d:%02d",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+}
+
+static void ksu_get_cmdline(char *full_comm, const char *comm, size_t buf_len)
+{
+ if (!full_comm || buf_len <= 0)
+ return;
+
+ if (comm && strlen(comm) > 0) {
+ KSU_STRSCPY(full_comm, comm, buf_len);
+ return;
+ }
+
+ if (in_atomic() || in_interrupt() || irqs_disabled()) {
+ KSU_STRSCPY(full_comm, current->comm, buf_len);
+ return;
+ }
+
+ if (!current->mm) {
+ KSU_STRSCPY(full_comm, current->comm, buf_len);
+ return;
+ }
+
+ int n = get_cmdline(current, full_comm, buf_len);
+ if (n <= 0) {
+ KSU_STRSCPY(full_comm, current->comm, buf_len);
+ return;
+ }
+
+ for (int i = 0; i < n && i < buf_len - 1; i++) {
+ if (full_comm[i] == '\0')
+ full_comm[i] = ' ';
+ }
+ full_comm[n < buf_len ? n : buf_len - 1] = '\0';
+}
+
+static void sanitize_string(char *str, size_t len)
+{
+ if (!str || len == 0)
+ return;
+
+ size_t read_pos = 0, write_pos = 0;
+
+ while (read_pos < len && str[read_pos] != '\0') {
+ char c = str[read_pos];
+
+ if (c == '\n' || c == '\r') {
+ read_pos++;
+ continue;
+ }
+
+ if (c == ' ' && write_pos > 0 && str[write_pos - 1] == ' ') {
+ read_pos++;
+ continue;
+ }
+
+ str[write_pos++] = c;
+ read_pos++;
+ }
+
+ str[write_pos] = '\0';
+}
+
+static bool dedup_should_print(uid_t uid, u8 type, const char *content, size_t len)
+{
+ struct dedup_key key = {
+ .crc = dedup_calc_hash(content, len),
+ .uid = uid,
+ .type = type,
+ };
+ u64 now = ktime_get_ns();
+ u64 delta_ns = DEDUP_SECS * NSEC_PER_SEC;
+
+ u32 idx = key.crc & (SULOG_COMM_LEN - 1);
+ spin_lock(&dedup_lock);
+
+ struct dedup_entry *e = &dedup_tbl[idx];
+ if (e->key.crc == key.crc &&
+ e->key.uid == key.uid &&
+ e->key.type == key.type &&
+ (now - e->ts_ns) < delta_ns) {
+ spin_unlock(&dedup_lock);
+ return false;
+ }
+
+ e->key = key;
+ e->ts_ns = now;
+ spin_unlock(&dedup_lock);
+ return true;
+}
+
+static void sulog_work_handler(struct work_struct *work)
+{
+ struct file *fp;
+ struct sulog_entry *entry, *tmp;
+ LIST_HEAD(local_queue);
+ loff_t pos = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dedup_lock, flags);
+ list_splice_init(&sulog_queue, &local_queue);
+ spin_unlock_irqrestore(&dedup_lock, flags);
+
+ if (list_empty(&local_queue))
+ return;
+
+ fp = filp_open(SULOG_PATH, O_WRONLY | O_CREAT | O_APPEND, 0640);
+ if (IS_ERR(fp)) {
+ pr_err("sulog: failed to open log file: %ld\n", PTR_ERR(fp));
+ goto cleanup;
+ }
+
+ if (fp->f_inode->i_size > SULOG_MAX_SIZE) {
+ if (vfs_truncate(&fp->f_path, 0))
+ pr_err("sulog: failed to truncate log file\n");
+ pos = 0;
+ } else {
+ pos = fp->f_inode->i_size;
+ }
+
+ list_for_each_entry(entry, &local_queue, list)
+ kernel_write(fp, entry->content, strlen(entry->content), &pos);
+
+ vfs_fsync(fp, 0);
+ filp_close(fp, 0);
+
+cleanup:
+ list_for_each_entry_safe(entry, tmp, &local_queue, list) {
+ list_del(&entry->list);
+ kfree(entry);
+ }
+}
+
+static void sulog_add_entry(char *log_buf, size_t len, uid_t uid, u8 dedup_type)
+{
+ struct sulog_entry *entry;
+ unsigned long flags;
+
+ if (!sulog_enabled || !log_buf || len == 0)
+ return;
+
+ if (!dedup_should_print(uid, dedup_type, log_buf, len))
+ return;
+
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return;
+
+ KSU_STRSCPY(entry->content, log_buf, SULOG_ENTRY_MAX_LEN);
+
+ spin_lock_irqsave(&dedup_lock, flags);
+ list_add_tail(&entry->list, &sulog_queue);
+ spin_unlock_irqrestore(&dedup_lock, flags);
+
+ if (sulog_workqueue)
+ queue_work(sulog_workqueue, &sulog_work);
+}
+
+void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method)
+{
+ char log_buf[SULOG_ENTRY_MAX_LEN];
+ char timestamp[32];
+ char full_comm[SULOG_COMM_LEN];
+
+ if (!sulog_enabled)
+ return;
+
+ get_timestamp(timestamp, sizeof(timestamp));
+ ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
+
+ sanitize_string(full_comm, sizeof(full_comm));
+
+ snprintf(log_buf, sizeof(log_buf),
+ "[%s] SU_GRANT: UID=%d COMM=%s METHOD=%s PID=%d\n",
+ timestamp, uid, full_comm, method ? method : "unknown", current->pid);
+
+ sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SU_GRANT);
+}
+
+void ksu_sulog_report_su_attempt(uid_t uid, const char *comm, const char *target_path, bool success)
+{
+ char log_buf[SULOG_ENTRY_MAX_LEN];
+ char timestamp[32];
+ char full_comm[SULOG_COMM_LEN];
+
+ if (!sulog_enabled)
+ return;
+
+ get_timestamp(timestamp, sizeof(timestamp));
+ ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
+
+ sanitize_string(full_comm, sizeof(full_comm));
+
+ snprintf(log_buf, sizeof(log_buf),
+ "[%s] SU_EXEC: UID=%d COMM=%s TARGET=%s RESULT=%s PID=%d\n",
+ timestamp, uid, full_comm, target_path ? target_path : "unknown",
+ success ? "SUCCESS" : "DENIED", current->pid);
+
+ sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SU_ATTEMPT);
+}
+
+void ksu_sulog_report_permission_check(uid_t uid, const char *comm, bool allowed)
+{
+ char log_buf[SULOG_ENTRY_MAX_LEN];
+ char timestamp[32];
+ char full_comm[SULOG_COMM_LEN];
+
+ if (!sulog_enabled)
+ return;
+
+ get_timestamp(timestamp, sizeof(timestamp));
+ ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
+
+ sanitize_string(full_comm, sizeof(full_comm));
+
+ snprintf(log_buf, sizeof(log_buf),
+ "[%s] PERM_CHECK: UID=%d COMM=%s RESULT=%s PID=%d\n",
+ timestamp, uid, full_comm, allowed ? "ALLOWED" : "DENIED", current->pid);
+
+ sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_PERM_CHECK);
+}
+
+void ksu_sulog_report_manager_operation(const char *operation, uid_t manager_uid, uid_t target_uid)
+{
+ char log_buf[SULOG_ENTRY_MAX_LEN];
+ char timestamp[32];
+ char full_comm[SULOG_COMM_LEN];
+
+ if (!sulog_enabled)
+ return;
+
+ get_timestamp(timestamp, sizeof(timestamp));
+ ksu_get_cmdline(full_comm, NULL, sizeof(full_comm));
+
+ sanitize_string(full_comm, sizeof(full_comm));
+
+ snprintf(log_buf, sizeof(log_buf),
+ "[%s] MANAGER_OP: OP=%s MANAGER_UID=%d TARGET_UID=%d COMM=%s PID=%d\n",
+ timestamp, operation ? operation : "unknown", manager_uid, target_uid, full_comm, current->pid);
+
+ sulog_add_entry(log_buf, strlen(log_buf), manager_uid, DEDUP_MANAGER_OP);
+}
+
+void ksu_sulog_report_syscall(uid_t uid, const char *comm, const char *syscall, const char *args)
+{
+ char log_buf[SULOG_ENTRY_MAX_LEN];
+ char timestamp[32];
+ char full_comm[SULOG_COMM_LEN];
+
+ if (!sulog_enabled)
+ return;
+
+ get_timestamp(timestamp, sizeof(timestamp));
+ ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
+
+ sanitize_string(full_comm, sizeof(full_comm));
+
+ snprintf(log_buf, sizeof(log_buf),
+ "[%s] SYSCALL: UID=%d COMM=%s SYSCALL=%s ARGS=%s PID=%d\n",
+ timestamp, uid, full_comm, syscall ? syscall : "unknown",
+ args ? args : "none", current->pid);
+
+ sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SYSCALL);
+}
+
+int ksu_sulog_init(void)
+{
+ sulog_workqueue = alloc_workqueue("ksu_sulog", WQ_UNBOUND | WQ_HIGHPRI, 1);
+ if (!sulog_workqueue) {
+ pr_err("sulog: failed to create workqueue\n");
+ return -ENOMEM;
+ }
+
+ INIT_WORK(&sulog_work, sulog_work_handler);
+ pr_info("sulog: initialized successfully\n");
+ return 0;
+}
+
+void ksu_sulog_exit(void)
+{
+ struct sulog_entry *entry, *tmp;
+ unsigned long flags;
+
+ sulog_enabled = false;
+
+ if (sulog_workqueue) {
+ flush_workqueue(sulog_workqueue);
+ destroy_workqueue(sulog_workqueue);
+ sulog_workqueue = NULL;
+ }
+
+ spin_lock_irqsave(&dedup_lock, flags);
+ list_for_each_entry_safe(entry, tmp, &sulog_queue, list) {
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ spin_unlock_irqrestore(&dedup_lock, flags);
+
+ pr_info("sulog: cleaned up successfully\n");
+}
+
+#endif // __SULOG_GATE
diff --git a/kernel/sulog.h b/kernel/sulog.h
new file mode 100644
index 0000000..1569a8a
--- /dev/null
+++ b/kernel/sulog.h
@@ -0,0 +1,93 @@
+#ifndef __KSU_SULOG_H
+#define __KSU_SULOG_H
+
+#include
+#include
+#include // needed for function dedup_calc_hash
+
+#define __SULOG_GATE 1
+
+#if __SULOG_GATE
+
+extern struct timezone sys_tz;
+
+#define SULOG_PATH "/data/adb/ksu/log/sulog.log"
+#define SULOG_MAX_SIZE (128 * 1024 * 1024) // 128MB
+#define SULOG_ENTRY_MAX_LEN 512
+#define SULOG_COMM_LEN 256
+#define DEDUP_SECS 10
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0)
+static inline size_t strlcpy(char *dest, const char *src, size_t size)
+{
+ return strscpy(dest, src, size);
+}
+#endif
+
+#define KSU_STRSCPY(dst, src, size) \
+ do { \
+ if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) { \
+ strscpy(dst, src, size); \
+ } else { \
+ strlcpy(dst, src, size); \
+ } \
+ } while (0)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
+#include
+
+static inline void time64_to_tm(time64_t totalsecs, int offset, struct tm *result)
+{
+ struct rtc_time rtc_tm;
+ rtc_time64_to_tm(totalsecs, &rtc_tm);
+
+ result->tm_sec = rtc_tm.tm_sec;
+ result->tm_min = rtc_tm.tm_min;
+ result->tm_hour = rtc_tm.tm_hour;
+ result->tm_mday = rtc_tm.tm_mday;
+ result->tm_mon = rtc_tm.tm_mon;
+ result->tm_year = rtc_tm.tm_year;
+}
+#endif
+
+struct dedup_key {
+ u32 crc;
+ uid_t uid;
+ u8 type;
+ u8 _pad[1];
+};
+
+struct dedup_entry {
+ struct dedup_key key;
+ u64 ts_ns;
+};
+
+enum {
+ DEDUP_SU_GRANT = 0,
+ DEDUP_SU_ATTEMPT,
+ DEDUP_PERM_CHECK,
+ DEDUP_MANAGER_OP,
+ DEDUP_SYSCALL,
+};
+
+static inline u32 dedup_calc_hash(const char *content, size_t len)
+{
+ return crc32(0, content, len);
+}
+
+struct sulog_entry {
+ struct list_head list;
+ char content[SULOG_ENTRY_MAX_LEN];
+};
+
+void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method);
+void ksu_sulog_report_su_attempt(uid_t uid, const char *comm, const char *target_path, bool success);
+void ksu_sulog_report_permission_check(uid_t uid, const char *comm, bool allowed);
+void ksu_sulog_report_manager_operation(const char *operation, uid_t manager_uid, uid_t target_uid);
+void ksu_sulog_report_syscall(uid_t uid, const char *comm, const char *syscall, const char *args);
+
+int ksu_sulog_init(void);
+void ksu_sulog_exit(void);
+#endif // __SULOG_GATE
+
+#endif /* __KSU_SULOG_H */
diff --git a/kernel/supercalls.c b/kernel/supercalls.c
new file mode 100644
index 0000000..9a91f14
--- /dev/null
+++ b/kernel/supercalls.c
@@ -0,0 +1,935 @@
+#include "supercalls.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "arch.h"
+#include "allowlist.h"
+#include "feature.h"
+#include "klog.h" // IWYU pragma: keep
+#include "ksud.h"
+#include "manager.h"
+#include "sulog.h"
+#include "selinux/selinux.h"
+#include "objsec.h"
+#include "file_wrapper.h"
+#include "syscall_hook_manager.h"
+#include "throne_comm.h"
+#include "dynamic_manager.h"
+#include "umount_manager.h"
+
+#ifdef CONFIG_KSU_MANUAL_SU
+#include "manual_su.h"
+#endif
+
+bool ksu_uid_scanner_enabled = false;
+
+// Permission check functions
+bool only_manager(void)
+{
+ return is_manager();
+}
+
+bool only_root(void)
+{
+ return current_uid().val == 0;
+}
+
+bool manager_or_root(void)
+{
+ return current_uid().val == 0 || is_manager();
+}
+
+bool always_allow(void)
+{
+ return true; // No permission check
+}
+
+bool allowed_for_su(void)
+{
+ bool is_allowed = is_manager() || ksu_is_allow_uid_for_current(current_uid().val);
+#if __SULOG_GATE
+ ksu_sulog_report_permission_check(current_uid().val, current->comm, is_allowed);
+#endif
+ return is_allowed;
+}
+
+static void init_uid_scanner(void)
+{
+ ksu_uid_init();
+ do_load_throne_state(NULL);
+
+ if (ksu_uid_scanner_enabled) {
+ int ret = ksu_throne_comm_init();
+ if (ret != 0) {
+ pr_err("Failed to initialize throne communication: %d\n", ret);
+ }
+ }
+}
+
+static int do_grant_root(void __user *arg)
+{
+ // we already check uid above on allowed_for_su()
+
+ pr_info("allow root for: %d\n", current_uid().val);
+ escape_with_root_profile();
+
+ return 0;
+}
+
+static int do_get_info(void __user *arg)
+{
+ struct ksu_get_info_cmd cmd = {.version = KERNEL_SU_VERSION, .flags = 0};
+
+#ifdef MODULE
+ cmd.flags |= 0x1;
+#endif
+ if (is_manager()) {
+ cmd.flags |= 0x2;
+ }
+ cmd.features = KSU_FEATURE_MAX;
+
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("get_version: copy_to_user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int do_report_event(void __user *arg)
+{
+ struct ksu_report_event_cmd cmd;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ switch (cmd.event) {
+ case EVENT_POST_FS_DATA: {
+ static bool post_fs_data_lock = false;
+ if (!post_fs_data_lock) {
+ post_fs_data_lock = true;
+ pr_info("post-fs-data triggered\n");
+ on_post_fs_data();
+ init_uid_scanner();
+#if __SULOG_GATE
+ ksu_sulog_init();
+#endif
+ ksu_dynamic_manager_init();
+ }
+ break;
+ }
+ case EVENT_BOOT_COMPLETED: {
+ static bool boot_complete_lock = false;
+ if (!boot_complete_lock) {
+ boot_complete_lock = true;
+ pr_info("boot_complete triggered\n");
+ on_boot_completed();
+ }
+ break;
+ }
+ case EVENT_MODULE_MOUNTED: {
+ pr_info("module mounted!\n");
+ on_module_mounted();
+ break;
+ }
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int do_set_sepolicy(void __user *arg)
+{
+ struct ksu_set_sepolicy_cmd cmd;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ return handle_sepolicy(cmd.cmd, (void __user *)cmd.arg);
+}
+
+static int do_check_safemode(void __user *arg)
+{
+ struct ksu_check_safemode_cmd cmd;
+
+ cmd.in_safe_mode = ksu_is_safe_mode();
+
+ if (cmd.in_safe_mode) {
+ pr_warn("safemode enabled!\n");
+ }
+
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("check_safemode: copy_to_user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int do_get_allow_list(void __user *arg)
+{
+ struct ksu_get_allow_list_cmd cmd;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ bool success = ksu_get_allow_list((int *)cmd.uids, (int *)&cmd.count, true);
+
+ if (!success) {
+ return -EFAULT;
+ }
+
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("get_allow_list: copy_to_user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int do_get_deny_list(void __user *arg)
+{
+ struct ksu_get_allow_list_cmd cmd;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ bool success = ksu_get_allow_list((int *)cmd.uids, (int *)&cmd.count, false);
+
+ if (!success) {
+ return -EFAULT;
+ }
+
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("get_deny_list: copy_to_user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int do_uid_granted_root(void __user *arg)
+{
+ struct ksu_uid_granted_root_cmd cmd;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ cmd.granted = ksu_is_allow_uid_for_current(cmd.uid);
+
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("uid_granted_root: copy_to_user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int do_uid_should_umount(void __user *arg)
+{
+ struct ksu_uid_should_umount_cmd cmd;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ return -EFAULT;
+ }
+
+ cmd.should_umount = ksu_uid_should_umount(cmd.uid);
+
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("uid_should_umount: copy_to_user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int do_get_manager_uid(void __user *arg)
+{
+ struct ksu_get_manager_uid_cmd cmd;
+
+ cmd.uid = ksu_get_manager_uid();
+
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("get_manager_uid: copy_to_user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int do_get_app_profile(void __user *arg)
+{
+ struct ksu_get_app_profile_cmd cmd;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ pr_err("get_app_profile: copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ if (!ksu_get_app_profile(&cmd.profile)) {
+ return -ENOENT;
+ }
+
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("get_app_profile: copy_to_user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int do_set_app_profile(void __user *arg)
+{
+ struct ksu_set_app_profile_cmd cmd;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ pr_err("set_app_profile: copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ if (!ksu_set_app_profile(&cmd.profile, true)) {
+#if __SULOG_GATE
+ ksu_sulog_report_manager_operation("SET_APP_PROFILE",
+ current_uid().val, cmd.profile.current_uid);
+#endif
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int do_get_feature(void __user *arg)
+{
+ struct ksu_get_feature_cmd cmd;
+ bool supported;
+ int ret;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ pr_err("get_feature: copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ ret = ksu_get_feature(cmd.feature_id, &cmd.value, &supported);
+ cmd.supported = supported ? 1 : 0;
+
+ if (ret && supported) {
+ pr_err("get_feature: failed for feature %u: %d\n", cmd.feature_id, ret);
+ return ret;
+ }
+
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("get_feature: copy_to_user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int do_set_feature(void __user *arg)
+{
+ struct ksu_set_feature_cmd cmd;
+ int ret;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ pr_err("set_feature: copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ ret = ksu_set_feature(cmd.feature_id, cmd.value);
+ if (ret) {
+ pr_err("set_feature: failed for feature %u: %d\n", cmd.feature_id, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int do_get_wrapper_fd(void __user *arg) {
+ if (!ksu_file_sid) {
+ return -EINVAL;
+ }
+
+ struct ksu_get_wrapper_fd_cmd cmd;
+ int ret;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ pr_err("get_wrapper_fd: copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ struct file* f = fget(cmd.fd);
+ if (!f) {
+ return -EBADF;
+ }
+
+ struct ksu_file_wrapper *data = ksu_create_file_wrapper(f);
+ if (data == NULL) {
+ ret = -ENOMEM;
+ goto put_orig_file;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
+#define getfd_secure anon_inode_create_getfd
+#else
+#define getfd_secure anon_inode_getfd_secure
+#endif
+ ret = getfd_secure("[ksu_fdwrapper]", &data->ops, data, f->f_flags, NULL);
+ if (ret < 0) {
+ pr_err("ksu_fdwrapper: getfd failed: %d\n", ret);
+ goto put_wrapper_data;
+ }
+ struct file* pf = fget(ret);
+
+ struct inode* wrapper_inode = file_inode(pf);
+ // copy original inode mode
+ wrapper_inode->i_mode = file_inode(f)->i_mode;
+ struct inode_security_struct *sec = selinux_inode(wrapper_inode);
+ if (sec) {
+ sec->sid = ksu_file_sid;
+ }
+
+ fput(pf);
+ goto put_orig_file;
+put_wrapper_data:
+ ksu_delete_file_wrapper(data);
+put_orig_file:
+ fput(f);
+
+ return ret;
+}
+
+static int do_manage_mark(void __user *arg)
+{
+ struct ksu_manage_mark_cmd cmd;
+ int ret = 0;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ pr_err("manage_mark: copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ switch (cmd.operation) {
+ case KSU_MARK_GET: {
+ // Get task mark status
+ ret = ksu_get_task_mark(cmd.pid);
+ if (ret < 0) {
+ pr_err("manage_mark: get failed for pid %d: %d\n", cmd.pid, ret);
+ return ret;
+ }
+ cmd.result = (u32)ret;
+ break;
+ }
+ case KSU_MARK_MARK: {
+ if (cmd.pid == 0) {
+ ksu_mark_all_process();
+ } else {
+ ret = ksu_set_task_mark(cmd.pid, true);
+ if (ret < 0) {
+ pr_err("manage_mark: set_mark failed for pid %d: %d\n", cmd.pid,
+ ret);
+ return ret;
+ }
+ }
+ break;
+ }
+ case KSU_MARK_UNMARK: {
+ if (cmd.pid == 0) {
+ ksu_unmark_all_process();
+ } else {
+ ret = ksu_set_task_mark(cmd.pid, false);
+ if (ret < 0) {
+ pr_err("manage_mark: set_unmark failed for pid %d: %d\n",
+ cmd.pid, ret);
+ return ret;
+ }
+ }
+ break;
+ }
+ case KSU_MARK_REFRESH: {
+ ksu_mark_running_process();
+ pr_info("manage_mark: refreshed running processes\n");
+ break;
+ }
+ default: {
+ pr_err("manage_mark: invalid operation %u\n", cmd.operation);
+ return -EINVAL;
+ }
+ }
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("manage_mark: copy_to_user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+// 100. GET_FULL_VERSION - Get full version string
+static int do_get_full_version(void __user *arg)
+{
+ struct ksu_get_full_version_cmd cmd = {0};
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
+ strscpy(cmd.version_full, KSU_VERSION_FULL, sizeof(cmd.version_full));
+#else
+ strlcpy(cmd.version_full, KSU_VERSION_FULL, sizeof(cmd.version_full));
+#endif
+
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("get_full_version: copy_to_user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+// 101. HOOK_TYPE - Get hook type
+static int do_get_hook_type(void __user *arg)
+{
+ struct ksu_hook_type_cmd cmd = {0};
+ const char *type = "Tracepoint";
+
+#if defined(KSU_MANUAL_HOOK)
+ type = "Manual";
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
+ strscpy(cmd.hook_type, type, sizeof(cmd.hook_type));
+#else
+ strlcpy(cmd.hook_type, type, sizeof(cmd.hook_type));
+#endif
+
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("get_hook_type: copy_to_user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+// 102. ENABLE_KPM - Check if KPM is enabled
+static int do_enable_kpm(void __user *arg)
+{
+ struct ksu_enable_kpm_cmd cmd;
+
+ cmd.enabled = IS_ENABLED(CONFIG_KPM);
+
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("enable_kpm: copy_to_user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int do_dynamic_manager(void __user *arg)
+{
+ struct ksu_dynamic_manager_cmd cmd;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ pr_err("dynamic_manager: copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ int ret = ksu_handle_dynamic_manager(&cmd.config);
+ if (ret)
+ return ret;
+
+ if (cmd.config.operation == DYNAMIC_MANAGER_OP_GET &&
+ copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("dynamic_manager: copy_to_user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int do_get_managers(void __user *arg)
+{
+ struct ksu_get_managers_cmd cmd;
+
+ int ret = ksu_get_active_managers(&cmd.manager_info);
+ if (ret)
+ return ret;
+
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("get_managers: copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int do_enable_uid_scanner(void __user *arg)
+{
+ struct ksu_enable_uid_scanner_cmd cmd;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ pr_err("enable_uid_scanner: copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ switch (cmd.operation) {
+ case UID_SCANNER_OP_GET_STATUS: {
+ bool status = ksu_uid_scanner_enabled;
+ if (copy_to_user((void __user *)cmd.status_ptr, &status, sizeof(status))) {
+ pr_err("enable_uid_scanner: copy status failed\n");
+ return -EFAULT;
+ }
+ break;
+ }
+ case UID_SCANNER_OP_TOGGLE: {
+ bool enabled = cmd.enabled;
+
+ if (enabled == ksu_uid_scanner_enabled) {
+ pr_info("enable_uid_scanner: no need to change, already %s\n",
+ enabled ? "enabled" : "disabled");
+ break;
+ }
+
+ if (enabled) {
+ // Enable UID scanner
+ int ret = ksu_throne_comm_init();
+ if (ret != 0) {
+ pr_err("enable_uid_scanner: failed to initialize: %d\n", ret);
+ return -EFAULT;
+ }
+ pr_info("enable_uid_scanner: enabled\n");
+ } else {
+ // Disable UID scanner
+ ksu_throne_comm_exit();
+ pr_info("enable_uid_scanner: disabled\n");
+ }
+
+ ksu_uid_scanner_enabled = enabled;
+ ksu_throne_comm_save_state();
+ break;
+ }
+ case UID_SCANNER_OP_CLEAR_ENV: {
+ // Clear environment (force exit)
+ ksu_throne_comm_exit();
+ ksu_uid_scanner_enabled = false;
+ ksu_throne_comm_save_state();
+ pr_info("enable_uid_scanner: environment cleared\n");
+ break;
+ }
+ default:
+ pr_err("enable_uid_scanner: invalid operation\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_KSU_MANUAL_SU
+static bool system_uid_check(void)
+{
+ return current_uid().val <= 2000;
+}
+
+static int do_manual_su(void __user *arg)
+{
+ struct ksu_manual_su_cmd cmd;
+ struct manual_su_request request;
+ int res;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ pr_err("manual_su: copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ pr_info("manual_su request, option=%d, uid=%d, pid=%d\n",
+ cmd.option, cmd.target_uid, cmd.target_pid);
+
+ memset(&request, 0, sizeof(request));
+ request.target_uid = cmd.target_uid;
+ request.target_pid = cmd.target_pid;
+
+ if (cmd.option == MANUAL_SU_OP_GENERATE_TOKEN ||
+ cmd.option == MANUAL_SU_OP_ESCALATE) {
+ memcpy(request.token_buffer, cmd.token_buffer, sizeof(request.token_buffer));
+ }
+
+ res = ksu_handle_manual_su_request(cmd.option, &request);
+
+ if (cmd.option == MANUAL_SU_OP_GENERATE_TOKEN && res == 0) {
+ memcpy(cmd.token_buffer, request.token_buffer, sizeof(cmd.token_buffer));
+ if (copy_to_user(arg, &cmd, sizeof(cmd))) {
+ pr_err("manual_su: copy_to_user failed\n");
+ return -EFAULT;
+ }
+ }
+
+ return res;
+}
+#endif
+
+static int do_umount_manager(void __user *arg)
+{
+ struct ksu_umount_manager_cmd cmd;
+
+ if (copy_from_user(&cmd, arg, sizeof(cmd))) {
+ pr_err("umount_manager: copy_from_user failed\n");
+ return -EFAULT;
+ }
+
+ switch (cmd.operation) {
+ case UMOUNT_OP_ADD: {
+ return ksu_umount_manager_add(cmd.path, cmd.check_mnt, cmd.flags, false);
+ }
+ case UMOUNT_OP_REMOVE: {
+ return ksu_umount_manager_remove(cmd.path);
+ }
+ case UMOUNT_OP_LIST: {
+ struct ksu_umount_entry_info __user *entries =
+ (struct ksu_umount_entry_info __user *)cmd.entries_ptr;
+ return ksu_umount_manager_get_entries(entries, &cmd.count);
+ }
+ case UMOUNT_OP_CLEAR_CUSTOM: {
+ return ksu_umount_manager_clear_custom();
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+// IOCTL handlers mapping table
+static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
+ { .cmd = KSU_IOCTL_GRANT_ROOT, .name = "GRANT_ROOT", .handler = do_grant_root, .perm_check = allowed_for_su },
+ { .cmd = KSU_IOCTL_GET_INFO, .name = "GET_INFO", .handler = do_get_info, .perm_check = always_allow },
+ { .cmd = KSU_IOCTL_REPORT_EVENT, .name = "REPORT_EVENT", .handler = do_report_event, .perm_check = only_root },
+ { .cmd = KSU_IOCTL_SET_SEPOLICY, .name = "SET_SEPOLICY", .handler = do_set_sepolicy, .perm_check = only_root },
+ { .cmd = KSU_IOCTL_CHECK_SAFEMODE, .name = "CHECK_SAFEMODE", .handler = do_check_safemode, .perm_check = always_allow },
+ { .cmd = KSU_IOCTL_GET_ALLOW_LIST, .name = "GET_ALLOW_LIST", .handler = do_get_allow_list, .perm_check = manager_or_root },
+ { .cmd = KSU_IOCTL_GET_DENY_LIST, .name = "GET_DENY_LIST", .handler = do_get_deny_list, .perm_check = manager_or_root },
+ { .cmd = KSU_IOCTL_UID_GRANTED_ROOT, .name = "UID_GRANTED_ROOT", .handler = do_uid_granted_root, .perm_check = manager_or_root },
+ { .cmd = KSU_IOCTL_UID_SHOULD_UMOUNT, .name = "UID_SHOULD_UMOUNT", .handler = do_uid_should_umount, .perm_check = manager_or_root },
+ { .cmd = KSU_IOCTL_GET_MANAGER_UID, .name = "GET_MANAGER_UID", .handler = do_get_manager_uid, .perm_check = manager_or_root },
+ { .cmd = KSU_IOCTL_GET_APP_PROFILE, .name = "GET_APP_PROFILE", .handler = do_get_app_profile, .perm_check = only_manager },
+ { .cmd = KSU_IOCTL_SET_APP_PROFILE, .name = "SET_APP_PROFILE", .handler = do_set_app_profile, .perm_check = only_manager },
+ { .cmd = KSU_IOCTL_GET_FEATURE, .name = "GET_FEATURE", .handler = do_get_feature, .perm_check = manager_or_root },
+ { .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root },
+ { .cmd = KSU_IOCTL_GET_WRAPPER_FD, .name = "GET_WRAPPER_FD", .handler = do_get_wrapper_fd, .perm_check = manager_or_root },
+ { .cmd = KSU_IOCTL_MANAGE_MARK, .name = "MANAGE_MARK", .handler = do_manage_mark, .perm_check = manager_or_root },
+ { .cmd = KSU_IOCTL_GET_FULL_VERSION,.name = "GET_FULL_VERSION", .handler = do_get_full_version, .perm_check = always_allow},
+ { .cmd = KSU_IOCTL_HOOK_TYPE,.name = "GET_HOOK_TYPE", .handler = do_get_hook_type, .perm_check = manager_or_root},
+ { .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .perm_check = manager_or_root},
+ { .cmd = KSU_IOCTL_DYNAMIC_MANAGER, .name = "SET_DYNAMIC_MANAGER", .handler = do_dynamic_manager, .perm_check = manager_or_root},
+ { .cmd = KSU_IOCTL_GET_MANAGERS, .name = "GET_MANAGERS", .handler = do_get_managers, .perm_check = manager_or_root},
+ { .cmd = KSU_IOCTL_ENABLE_UID_SCANNER, .name = "SET_ENABLE_UID_SCANNER", .handler = do_enable_uid_scanner, .perm_check = manager_or_root},
+#ifdef CONFIG_KSU_MANUAL_SU
+ { .cmd = KSU_IOCTL_MANUAL_SU, .name = "MANUAL_SU", .handler = do_manual_su, .perm_check = system_uid_check},
+#endif
+#ifdef CONFIG_KPM
+ { .cmd = KSU_IOCTL_KPM, .name = "KPM_OPERATION", .handler = do_kpm, .perm_check = manager_or_root},
+#endif
+ { .cmd = KSU_IOCTL_UMOUNT_MANAGER, .name = "UMOUNT_MANAGER", .handler = do_umount_manager, .perm_check = manager_or_root},
+ { .cmd = 0, .name = NULL, .handler = NULL, .perm_check = NULL} // Sentine
+};
+
+struct ksu_install_fd_tw {
+ struct callback_head cb;
+ int __user *outp;
+};
+
+static void ksu_install_fd_tw_func(struct callback_head *cb)
+{
+ struct ksu_install_fd_tw *tw = container_of(cb, struct ksu_install_fd_tw, cb);
+ int fd = ksu_install_fd();
+ pr_info("[%d] install ksu fd: %d\n", current->pid, fd);
+
+ if (copy_to_user(tw->outp, &fd, sizeof(fd))) {
+ pr_err("install ksu fd reply err\n");
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
+ close_fd(fd);
+#else
+ ksys_close(fd);
+#endif
+ }
+
+ kfree(tw);
+}
+
+// downstream: make sure to pass arg as reference, this can allow us to extend things.
+int ksu_handle_sys_reboot(int magic1, int magic2, unsigned int cmd, void __user **arg)
+{
+ struct ksu_install_fd_tw *tw;
+
+ if (magic1 != KSU_INSTALL_MAGIC1)
+ return 0;
+
+#ifdef CONFIG_KSU_DEBUG
+ pr_info("sys_reboot: intercepted call! magic: 0x%x id: %d\n", magic1, magic2);
+#endif
+
+ // Check if this is a request to install KSU fd
+ if (magic2 == KSU_INSTALL_MAGIC2) {
+ tw = kzalloc(sizeof(*tw), GFP_ATOMIC);
+ if (!tw)
+ return 0;
+
+ tw->outp = (int __user *)*arg;
+ tw->cb.func = ksu_install_fd_tw_func;
+
+ if (task_work_add(current, &tw->cb, TWA_RESUME)) {
+ kfree(tw);
+ pr_warn("install fd add task_work failed\n");
+ }
+
+ return 0;
+ }
+
+ // extensions
+
+ return 0;
+}
+
+#ifdef KSU_KPROBES_HOOK
+// Reboot hook for installing fd
+static int reboot_handler_pre(struct kprobe *p, struct pt_regs *regs)
+{
+ struct pt_regs *real_regs = PT_REAL_REGS(regs);
+ int magic1 = (int)PT_REGS_PARM1(real_regs);
+ int magic2 = (int)PT_REGS_PARM2(real_regs);
+ int cmd = (int)PT_REGS_PARM3(real_regs);
+ void __user **arg = (void __user **)&PT_REGS_SYSCALL_PARM4(real_regs);
+
+ return ksu_handle_sys_reboot(magic1, magic2, cmd, arg);
+}
+
+static struct kprobe reboot_kp = {
+ .symbol_name = REBOOT_SYMBOL,
+ .pre_handler = reboot_handler_pre,
+};
+#endif
+
+void ksu_supercalls_init(void)
+{
+ int i;
+
+ pr_info("KernelSU IOCTL Commands:\n");
+ for (i = 0; ksu_ioctl_handlers[i].handler; i++) {
+ pr_info(" %-18s = 0x%08x\n", ksu_ioctl_handlers[i].name, ksu_ioctl_handlers[i].cmd);
+ }
+#ifdef KSU_KPROBES_HOOK
+ int rc = register_kprobe(&reboot_kp);
+ if (rc) {
+ pr_err("reboot kprobe failed: %d\n", rc);
+ } else {
+ pr_info("reboot kprobe registered successfully\n");
+ }
+#endif
+}
+
+void ksu_supercalls_exit(void) {
+#ifdef KSU_KPROBES_HOOK
+ unregister_kprobe(&reboot_kp);
+#endif
+}
+
+static inline void ksu_ioctl_audit(unsigned int cmd, const char *cmd_name, uid_t uid, int ret)
+{
+#if __SULOG_GATE
+ const char *result = (ret == 0) ? "SUCCESS" :
+ (ret == -EPERM) ? "DENIED" : "FAILED";
+ ksu_sulog_report_syscall(uid, NULL, cmd_name, result);
+#endif
+}
+
+// IOCTL dispatcher
+static long anon_ksu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int i;
+
+#ifdef CONFIG_KSU_DEBUG
+ pr_info("ksu ioctl: cmd=0x%x from uid=%d\n", cmd, current_uid().val);
+#endif
+
+ for (i = 0; ksu_ioctl_handlers[i].handler; i++) {
+ if (cmd == ksu_ioctl_handlers[i].cmd) {
+ // Check permission first
+ if (ksu_ioctl_handlers[i].perm_check &&
+ !ksu_ioctl_handlers[i].perm_check()) {
+ pr_warn("ksu ioctl: permission denied for cmd=0x%x uid=%d\n",
+ cmd, current_uid().val);
+ ksu_ioctl_audit(cmd, ksu_ioctl_handlers[i].name,
+ current_uid().val, -EPERM);
+ return -EPERM;
+ }
+ // Execute handler
+ int ret = ksu_ioctl_handlers[i].handler(argp);
+ ksu_ioctl_audit(cmd, ksu_ioctl_handlers[i].name,
+ current_uid().val, ret);
+ return ret;
+ }
+ }
+
+ pr_warn("ksu ioctl: unsupported command 0x%x\n", cmd);
+ return -ENOTTY;
+}
+
+// File release handler
+static int anon_ksu_release(struct inode *inode, struct file *filp)
+{
+ pr_info("ksu fd released\n");
+ return 0;
+}
+
+// File operations structure
+static const struct file_operations anon_ksu_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = anon_ksu_ioctl,
+ .compat_ioctl = anon_ksu_ioctl,
+ .release = anon_ksu_release,
+};
+
+// Install KSU fd to current process
+int ksu_install_fd(void)
+{
+ struct file *filp;
+ int fd;
+
+ // Get unused fd
+ fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fd < 0) {
+ pr_err("ksu_install_fd: failed to get unused fd\n");
+ return fd;
+ }
+
+ // Create anonymous inode file
+ filp = anon_inode_getfile("[ksu_driver]", &anon_ksu_fops, NULL, O_RDWR | O_CLOEXEC);
+ if (IS_ERR(filp)) {
+ pr_err("ksu_install_fd: failed to create anon inode file\n");
+ put_unused_fd(fd);
+ return PTR_ERR(filp);
+ }
+
+ // Install fd
+ fd_install(fd, filp);
+
+#if __SULOG_GATE
+ ksu_sulog_report_permission_check(current_uid().val, current->comm, fd >= 0);
+#endif
+
+ pr_info("ksu fd installed: %d for pid %d\n", fd, current->pid);
+
+ return fd;
+}
\ No newline at end of file
diff --git a/kernel/supercalls.h b/kernel/supercalls.h
new file mode 100644
index 0000000..c54585e
--- /dev/null
+++ b/kernel/supercalls.h
@@ -0,0 +1,180 @@
+#ifndef __KSU_H_SUPERCALLS
+#define __KSU_H_SUPERCALLS
+
+#include
+#include