pax_global_header00006660000000000000000000000064113511212720014505gustar00rootroot0000000000000052 comment=1295e556f9c820bed6fdb39f6995dbd979081b07 reglookup-0.12.0/000077500000000000000000000000001135112127200135745ustar00rootroot00000000000000reglookup-0.12.0/INSTALL000066400000000000000000000025171135112127200146320ustar00rootroot00000000000000RegLookup Installation ====================== Prerequisites ------------- This package doesn't require much for installation. Just what typically comes with any free operating system. Be sure you have: - GNU Make - GCC Note that iconv support is required, as specified in IEEE Std 1003.1 (POSIX.1-2001). Some platforms still do not contain support for this natively, in which case you may need to install libiconv from: http://www.gnu.org/software/libiconv/ Survival Commands ----------------- make # and as root make install If GNU Make isn't your default, you may need to run `gmake' instead. Advanced Installation --------------------- To install in a custom directory, simply change one or more of the following make variables to suit your needs: PREFIX Top level install directory. Used as base for rest unless they too are overridden. (This defaults to /usr/local) BIN_PREFIX Location for executable programs to be installed. DOC_PREFIX Location for documentation. MAN_PREFIX A path in the MANPATH. Use the following syntax (with GNU Make) to override a variable: # as root make VAR=value install For more information, see the Makefiles. Primitive build features have been added to support cross-compiling to Windows binaries using MinGW. For more information on this, see: doc/mingw-build.txt reglookup-0.12.0/LICENSE000066400000000000000000001043741135112127200146120ustar00rootroot00000000000000 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 . reglookup-0.12.0/Makefile000066400000000000000000000030411135112127200152320ustar00rootroot00000000000000# $Id: Makefile 159 2009-12-06 20:09:01Z tim $ # Installation prefixes. Change to install elsewhere. PREFIX=/usr/local BIN_PREFIX=$(PREFIX)/bin DOC_PREFIX=$(PREFIX)/share/doc/reglookup MAN_PREFIX=$(PREFIX)/man ################################################################################ CC=gcc OPTS=-std=gnu99 -pedantic -Wall -ggdb #OPTS=-std=gnu99 -pedantic -Wall INC:=-I$(PWD)/include -I/usr/local/include LIB=-L/usr/local/lib -lm BIN_EXT= EXTRA_OBJ= UNAME := $(shell uname) ifneq ($(UNAME),Linux) LIB:=$(LIB) -liconv endif ################################################################################ # MinGW cross-compiling build settings ifdef BUILD_MINGW ## These may need to be changed CC=i586-mingw32msvc-cc LIBICONV_PATH=/usr/local/src/libiconv-1.9.2-1-lib ## These probably do not need to be changed BIN_EXT=.exe INC:=$(INC) -I$(LIBICONV_PATH)/include EXTRA_OBJ=$(LIBICONV_PATH)/lib/libiconv.dll.a endif ################################################################################ BUILD=$(CURDIR)/build BUILD_BIN=$(BUILD)/bin BUILD_DOC=$(BUILD)/doc BUILD_TREE=$(BUILD_BIN) $(BUILD_DOC) SUB_DIRS=lib src doc bin FILES=$(REGLOOKUP) .PHONY: $(SUB_DIRS) clean export all: $(BUILD_TREE) $(SUB_DIRS) #XXX: This should be more generalized. install: all mkdir -p $(BIN_PREFIX) mkdir -p $(DOC_PREFIX) mkdir -p $(MAN_PREFIX)/man1 $(MAKE) -C bin install $(MAKE) -C src install $(MAKE) -C doc install $(SUB_DIRS): $(MAKE) -C $@ $(BUILD_TREE): mkdir -p $@ clean: $(MAKE) -C src clean $(MAKE) -C lib clean rm -rf $(BUILD)/* reglookup-0.12.0/bin/000077500000000000000000000000001135112127200143445ustar00rootroot00000000000000reglookup-0.12.0/bin/Makefile000066400000000000000000000004671135112127200160130ustar00rootroot00000000000000# $Id: Makefile 90 2007-03-28 19:22:38Z tim $ ################################################################################ TIMELINE=$(BUILD_BIN)/reglookup-timeline FILES=$(TIMELINE) all: $(FILES) $(TIMELINE): cp reglookup-timeline $@ install: install -m 0755 $(FILES) $(BIN_PREFIX) clean: rm -f *~ reglookup-0.12.0/bin/reglookup-timeline000077500000000000000000000027731135112127200201160ustar00rootroot00000000000000#!/bin/sh # This script is a wrapper for reglookup, and reads one or more registry # files to produce an MTIME sorted output. This is helpful when building # timelines for investigations. # # Copyright (C) 2005-2007,2010 Timothy D. Morgan # # 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; version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # $Id: reglookup-timeline 170 2010-03-06 04:40:25Z tim $ usage() { echo "Usage: $0 [-H] [-V] [ ...]" 1>&2 echo " -H Omit header line" 1>&2 echo " -V Include values with parent timestamps" 1>&2 } if [ $# -eq 0 ]; then usage echo "ERROR: requires at least one parameter" 1>&2 exit 1 fi PRINT_HEADER=true if [ "$1" = "-H" ]; then PRINT_HEADER=false shift fi OPTS='-t KEY' if [ "$1" = "-V" ]; then OPTS='-i' shift fi if [ "$PRINT_HEADER" = "true" ]; then echo "MTIME,FILE,PATH" fi for F in $@; do reglookup $OPTS -H "$F" | awk -F',' '{ printf "%s,'"$F"',%s\n",$4,$1; }' done | sort reglookup-0.12.0/doc/000077500000000000000000000000001135112127200143415ustar00rootroot00000000000000reglookup-0.12.0/doc/Makefile000066400000000000000000000027021135112127200160020ustar00rootroot00000000000000# $Id: Makefile 155 2009-06-03 23:45:58Z tim $ BUILD_FILES=$(BUILD_DOC)/man/man1/reglookup.1.gz\ $(BUILD_DOC)/man/man1/reglookup-timeline.1.gz\ $(BUILD_DOC)/man/man1/reglookup-recover.1.gz default: $(BUILD_FILES) $(BUILD_DOC)/man/man1: mkdir -p $(BUILD_DOC)/man/man1 $(BUILD_DOC)/man/man1/reglookup.1.gz: man/man1/reglookup.1.gz $(BUILD_DOC)/man/man1 cp man/man1/reglookup.1.gz $@ $(BUILD_DOC)/man/man1/reglookup-timeline.1.gz: man/man1/reglookup-timeline.1.gz $(BUILD_DOC)/man/man1 cp man/man1/reglookup-timeline.1.gz $@ $(BUILD_DOC)/man/man1/reglookup-recover.1.gz: man/man1/reglookup-recover.1.gz $(BUILD_DOC)/man/man1 cp man/man1/reglookup-recover.1.gz $@ install: cp -r $(BUILD_DOC)/* $(DOC_PREFIX) ln -sf $(DOC_PREFIX)/man/man1/* $(MAN_PREFIX)/man1 #XXX: Used during release only release: #XXX: The sed filter is a temporary hack to work around a bug in docbook2x-man # See: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=516165 docbook2x-man --to-stdout reglookup.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup.1 docbook2x-man --to-stdout reglookup-timeline.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup-timeline.1 docbook2x-man --to-stdout reglookup-recover.1.docbook | sed 's/.SH DESCRIPTION/\n.SH DESCRIPTION/' > man/man1/reglookup-recover.1 cd man/man1 && gzip -9 -f reglookup.1 cd man/man1 && gzip -9 -f reglookup-timeline.1 cd man/man1 && gzip -9 -f reglookup-recover.1 reglookup-0.12.0/doc/devel/000077500000000000000000000000001135112127200154405ustar00rootroot00000000000000reglookup-0.12.0/doc/devel/README000066400000000000000000000003711135112127200163210ustar00rootroot00000000000000Developer's documentation previously stored here has been removed from the main software distribution trunk. It is still available in the Subversion repository, however, and may be obtained here: http://code.google.com/p/reglookup/source/browse/ reglookup-0.12.0/doc/man/000077500000000000000000000000001135112127200151145ustar00rootroot00000000000000reglookup-0.12.0/doc/man/man1/000077500000000000000000000000001135112127200157505ustar00rootroot00000000000000reglookup-0.12.0/doc/man/man1/reglookup-recover.1.gz000066400000000000000000000030611135112127200221230ustar00rootroot00000000000000+Kreglookup-recover.1Wao6Z0FͳA?! )d/C_ }atn 0(*g1'Dxgt2+6FXe vUMrmiyd.O=%}úB$=7@^`毻;1-z0 W滕Ò/hTF A'ZE )d:n*2^+wrfJxG}R|P:(B,JE*-)Vl4cT}^vӤ\Z -- +s̃kcAz^ıhxJZF`-1* `wVrcJэ |"?<`B`1˺@DHd&[*p⥕ˮ| )?h 6Y֘ sAK_v߾#\"fth KQ ), ޳l AdPk#5ٹp%('+ɬZ;+잟4N8k`|ɤVܨMD) { y$|%isAxmЃ?_މ:n赾˗ NÈҺ<dn;z_;M OBPfMc$ͷ_O}t(MłLïP_pj#q^mw#K$.cؖzԷV'ZKS,UwkgI]=;oWmuY G)IϢa\ W\`>>F*8!!GX-#.,C㟤6mC?Z#x8dF1K NI65RJW" Xw$Jɟ2 iP`< )TWc/={ ]ëQ| 5C IJ.U,yxt9DC֖lџh\tڇvveuX2"rCڂgK]!4J3여V{?op`0L?|z4a@ 6q$J.n /n |G`yGdEp}0]ƃpz Q >bZ'0 J 0Gw#8x KTB reglookup-0.12.0/doc/man/man1/reglookup-timeline.1.gz000066400000000000000000000026321135112127200222670ustar00rootroot00000000000000+Kreglookup-timeline.1}VmO9_1N*#rrN ! D"|0N֪׎l/ܯg!-= DǞew R*m7 CJyQdzM+{m(ju9~o^}Xهl-GUkZ;NćՉEoRdy1}ièeUtF?F?}REcgYZFmt*`o MG7]!i[.дnfB|lq?-X߸a·W{$D/wרEʲbYNEQ@z $u^ny:.=ΟU q^:0PtjKap>\m(%OvjeՆu!^ *tI72nf9/&I@oL\Q!1k S̯'2g*[kvu^m :0E]L&!t_e]PJ$أ5Je-QD$;AM۠ A%pwMFJ(`Op#C l*p[32@tG{#Zeak'{7 |yFKŔlk Ye-$kp)U_#UFuHVܲ.[#=TfH+V42l*gqܷNbGҠTU h n;Y)C#5T2c3Ujmْ4QJx4Vlь%3G덯R|L) Q6[kM,(4XO$RpKs1֑ 6|QiJ9H,9@"8Ly \ytM 0?PO-wˆ;Ds =;l/5C//ZO"./hzOw|4-La/RϪG7[.=Cf2_!~t_=+2/ł.o4Wќfv1h7.>O9=qf퓁W5y 1Ja:=dB@?TG`WCX^o reglookup-0.12.0/doc/man/man1/reglookup.1.gz000066400000000000000000000067641135112127200204750ustar00rootroot00000000000000+Kreglookup.1Ymo7pE^'|ʒ:nU.%]K3$%Q Kr8뿜Y,A\jŎcϤ< z+]}q`X;vh_zf2lk222F v[\: j.YM%R/<y2J%vv2-F`}W Y vӿvTn aܮ hM' fr}kTi#hƳ8823ETah61}l{*O鱑a{1+wWf<ՑdazTh.RC͠QP\0{Ϟ|j`ƣHp*(|<穀=cX⾶sz' }V~`qܞ`jn?yMIxR͊ W:cei(m*3<40x^r{ /TEck-<dtv ?;n4o;f:wN0M~x9nqZ$1+p+:HfzW/X ☐Ʊ$቗`dK* d怋{Ce*\-F a8mXhj;lDIqFk`ԙŹ׺T,OWBgQ""xX4;PKQԓ8 绂BI["gl:52#9/fh6"* CU K_-삹R櫾[a l|vwո4]-(-4  ;MZgC$ezH!S[NA>)PATnB~+=4xu"JRp>, MgF$STIB "\2Ð;z焓5=.z i ?o2]ߺI'?47O|7nmTk޼>o>/q [-_ɲn|:.]|=z=r>F`O'*.`S p﷋exMVQw=tlut {Z/nj#4T :! ԃVм"KVqD _)ϸtԚn~Eb`#Wm7ghO.j{/ !mYCd isSg罴 /nO% Sޞw=~滿W X@Mo>1"µTȳ8¯l>p9KHK@ٌAU*,ڰXEʠtDuª0%q{ .< ɑvDneInsՐ_,P2G%R*2rg@A U6[ZNaY${# 6T/SZKHA~Svdھojé-[BntX.m:}UZ4 T[6Dąp=z`q9kl Kl|7O8\Kߍ$3T=GKmA''a@ðUw=if~ԿM&~֕M$FkAl`OepɕvBX^b{~Йb$w/lZx^=/.šDz]V]AZ˘NսV C*P!"W?U}k)8$PtiWպ {] UUg$);d lT$`0*3IeUm?k{j)!cR9o_6φF)UvO!kt)(F)$ vv2B&,5MK wNB0 Q9mJ"Bze"sIƊǭ[ NLx6zPFH~[I h+aTls;Ε'ߊxCnq Št - reglookup 1 File Conversion Utilities reglookup-recover Windows NT+ registry deleted data recovery tool SYNOPSIS reglookup-recover [options] registry-file DESCRIPTION reglookup-recover attempts to scour a Windows registry hive for deleted data structures and outputs those found in a CSV-like format. OPTIONS reglookup-recover accepts the following parameters: Verbose output. Enables the printing of a column header row. (default) Disables the printing of a column header row. Display cells which could not be interpreted as valid registry structures at the end of the output. Do not display cells which could not be interpreted as valid registry structures. This is the default behavior. Display raw cell contents for cells which were interpreted as intact data structures. This additional output will appear on the same line as the interpreted data. Do not display raw cell contents for cells which were interpreted as intact data structures. This is the default behavior. Required argument. Specifies the location of the registry file to read. The system registry files should be found under: %SystemRoot%/system32/config. OUTPUT reglookup-recover generates a comma-separated values (CSV) like output and writes it to stdout. For more information on the syntax of the general format, see reglookup(1). This tool is new and the output format, particularly the included columns, may change in future revisions. When this format stablizes, additional documentation will be included here. EXAMPLES To dump the recoverable contents of a system registry hive: reglookup-recover /mnt/win/c/WINDOWS/system32/config/system Extract all available unallocated data, including unparsable unallocated space and the raw data associated with parsed cells in a user-specific registry: reglookup-recover -r -l '/mnt/win/c/Documents and Settings/user/NTUSER.DAT' BUGS This program has been smoke-tested against most current Windows target platforms, but a comprehensive test suite has not yet been developed. (Please report results to the development mailing list if you encounter any bugs. Sample registry files and/or patches are greatly appreciated.) This program is new as of RegLookup release 0.9.0 and should be considered unstable. For more information on registry format details and the recovery algorithm, see: http://sentinelchicken.com/research/registry_format/ http://sentinelchicken.com/research/registry_recovery/ CREDITS This program was written by Timothy D. Morgan. LICENSE Please see the file "LICENSE" included with this software distribution. 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 version 3 for more details. SEE ALSO reglookup-timeline(1) reglookup-recover(1) reglookup-0.12.0/doc/reglookup-timeline.1.docbook000066400000000000000000000100571135112127200216600ustar00rootroot00000000000000 reglookup-timeline 1 File Conversion Utilities reglookup-timeline Windows NT+ registry MTIME timeline generator SYNOPSIS reglookup-timeline [-H] registry-file [registry-file ...] DESCRIPTION This script is a wrapper for reglookup(1), and reads one or more registry files to produce an MTIME-sorted output. This is helpful when building timelines for forensic investigations. PARAMETERS reglookup-timeline accepts one or more registry file names. All of the provided registries will be parsed using reglookup(1). The -H option may be used to omit the header line. OUTPUT reglookup-timeline generates a comma-separated values (CSV) compatible format to stdout. While the output of reglookup-timeline and reglookup(1) differ in the columns returned, the base format is the same. Currently, reglookup-timeline returns three columns: MTIME, FILE, and PATH. Only rows representing registry keys are returned, since MTIMEs are not stored for values. The FILE column indicates which registry file (provided as an argument) the key came from. Finally, the PATH field contains the full registry path to the key. Records are returned sorted in ascending order based on the MTIME column. BUGS This script is new, and as such it's interface may change significantly over the next few revisions. In particular, additional command line options will likely be added, and the output of the script may be altered in minor ways. It is very difficult to find documentation on what precise operations cause the MTIMEs to be updated. Basic experimentation indicates that a key's stamp is updated anytime an immediate sub-value or sub-key is created, renamed, deleted, or it's value is modified. If this MTIME data is critical to an investigation, any conclusions should be validated through experimentation in a controlled lab environment. This software should be considered unstable at this time. CREDITS This script was written by Timothy D. Morgan based on suggestions from Uwe Danz. Please see source code for a full list of copyrights. LICENSE Please see the file "LICENSE" included with this software distribution. 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 version 3 for more details. SEE ALSO reglookup(1) reglookup-recover(1) reglookup-0.12.0/doc/reglookup.1.docbook000066400000000000000000000273031135112127200200560ustar00rootroot00000000000000 reglookup 1 File Conversion Utilities reglookup Windows NT+ registry reader/lookup tool SYNOPSIS reglookup [options] registry-file DESCRIPTION reglookup is designed to read windows registry elements and print them out to stdout in a CSV-like format. It has filtering options to narrow the focus of the output. This tool is designed to work with on Windows NT-based registries. OPTIONS reglookup accepts the following parameters: Specify a path prefix filter. Only keys/values under this registry path will be output. Specify a type filter. Only elements which match this registry data type will be printed. Acceptable values are: NONE, SZ, EXPAND_SZ, BINARY, DWORD, DWORD_BE, LINK, MULTI_SZ, RSRC_LIST, RSRC_DESC, RSRC_REQ_LIST, QWORD and KEY Enables the printing of a column header row. (default) Printed values inherit the timestamp of their parent key, which is printed along with them. Note that this timestamp is not necessarily meaningful for any given value values because timestamps are saved on keys only and you cannot tell which value has been modified since a change to any value of a given key would update the time stamp. Disables the printing of a column header row. Adds five additional columns to output containing information from key security descriptors and rarely used fields. The columns are: owner, group, sacl, dacl, class. (This feature's output has not been extensively tested.) Disables the printing of security descriptor information. (default) Verbose output. Required argument. Specifies the location of the registry file to read. The system registry files should be found under: %SystemRoot%/system32/config. OUTPUT reglookup generates comma-separated values (CSV) and writes them to stdout. The format is designed to simplify parsing algorithms of other tools by quoting CSV special characters using a common hexadecimal format. Specifically, special characters or non-ascii bytes are converted to "\xQQ" where QQ is the hexadecimal value for the byte. The number of columns or fields in each line is fixed for a given run of the program, but may vary based on the command line options provided. See the header line for information on which fields are available and what they contain. Some fields in some lines may contain sub-fields which require additional delimiters. If these sub-delimiters occur in these sub-fields, they are also encoded in the same way as commas or other special characters are. Currently, the second, third, and fourth level delimiters are "|", ":", and " ", respectively. These are particularly important to take note of when security attributes are printed. Please note that these delimiters may occur in fields that are not sub-delimited, and should not be interpreted as special. Security attributes of registry keys have a complex structure which is outlined here. Each key will generally have an associated ACL (Access Control List), which is made up of ACEs (Access Control Entries). Each ACE is delimited by the secondary delimiter mentioned above, "|". The fields within an ACE are delimited by the third-level delimiter, ":", and consist of a SID, the ACE type (ALLOW, DENY, etc), a list of access rights, and a list of flags. The last two fields are delimited by the fourth-level delimiter " ". These final lists are simply human-readable interpretations of bits. The access rights abbreviations are listed below along with their Microsoft-assigned names: QRY_VAL KEY_QUERY_VALUE SET_VAL KEY_SET_VALUE CREATE_KEY KEY_CREATE_SUB_KEY ENUM_KEYS KEY_ENUMERATE_SUB_KEYS NOTIFY KEY_NOTIFY CREATE_LNK KEY_CREATE_LINK WOW64_64 KEY_WOW64_64KEY WOW64_32 KEY_WOW64_32KEY DELETE DELETE R_CONT READ_CONTROL W_DAC WRITE_DAC W_OWNER WRITE_OWNER SYNC SYNCHRONIZE SYS_SEC ACCESS_SYSTEM_SECURITY MAX_ALLWD MAXIMUM_ALLOWED GEN_A GENERIC_ALL GEN_X GENERIC_EXECUTE GEN_W GENERIC_WRITE GEN_R GENERIC_READ And the meaning of each flag is: OI Object Inherit CI Container Inherit NP Non-Propagate IO Inherit Only IA Inherited ACE Please see the following references for more information: http://msdn2.microsoft.com/en-gb/library/ms724878.aspx http://msdn2.microsoft.com/en-gb/library/aa374892.aspx http://msdn2.microsoft.com/en-us/library/aa772242.aspx http://support.microsoft.com/kb/220167 Note that some of the bits listed above have either not been allocated by Microsoft, or simply aren't documented. If any bits are set in the above two fields that aren't recognized, a hexidecimal representation of all of these mystery bits will be included in the output. For instance, if the lowest bit and third lowest bit were not recognized while being set, the number "0x5" would be included as an element in the list. While the ACL/ACE output format is mostly stable at this point, minor changes may be introduced in future versions. EXAMPLES To read and print the contents of an entire system registry file: reglookup /mnt/win/c/WINNT/system32/config/system To limit the output to just those entries under the Services key: reglookup -p /ControlSet002/Services /mnt/win/c/WINNT/system32/config/system To limit the output to all registry values of type BINARY: reglookup -t BINARY /mnt/win/c/WINNT/system32/config/system And to limit the output to BINARY values under the Services key: reglookup -t BINARY -p /ControlSet002/Services /mnt/win/c/WINNT/system32/config/system BUGS This program has been smoke-tested against most current Windows target platforms, but a comprehensive test suite has not yet been developed. (Please report results to the development mailing list if you encounter any bugs. Sample registry files and/or patches are greatly appreciated.) The SID conversions haven't been carefully checked for accuracy. The MTIME conversions appear correctly produce the stored UTC timestamp. However, due to the periodicity of registry writes, and the complexity of the conversion, a small amount of error (on the order of seconds) may be possible. The documentation available online from Microsoft on this field is very poor. Backslashes are currently considered special characters, to make parsing easier for automated tools. However, this causes paths to be difficult to read by mere mortals. For more information on registry format details, see: http://sentinelchicken.com/research/registry_format/ CREDITS This program was initially based on editreg.c by Richard Sharpe. It has since been rewritten to use a modified version the regfio library written by Gerald Carter. Heavy modifications to the library and the original command line interface have been done by Timothy D. Morgan. Please see source code for a full list of copyrights. LICENSE Please see the file "LICENSE" included with this software distribution. 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 version 3 for more details. SEE ALSO reglookup-timeline(1) reglookup-recover(1) reglookup-0.12.0/include/000077500000000000000000000000001135112127200152175ustar00rootroot00000000000000reglookup-0.12.0/include/byteorder.h000066400000000000000000000161141135112127200173720ustar00rootroot00000000000000/* * Branched from Samba project Subversion repository, version #2: * http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/byteorder.h * * Unix SMB/CIFS implementation. * SMB Byte handling * * Copyright (C) 2005 Timothy D. Morgan * Copyright (C) 1992-1998 Andrew Tridgell * * 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; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: byteorder.h 168 2010-03-03 00:08:42Z tim $ */ #ifndef _BYTEORDER_H #define _BYTEORDER_H /** * @file * * This file implements macros for machine independent short and * int manipulation @verbatim Here is a description of this file that I emailed to the samba list once: > I am confused about the way that byteorder.h works in Samba. I have > looked at it, and I would have thought that you might make a distinction > between LE and BE machines, but you only seem to distinguish between 386 > and all other architectures. > > Can you give me a clue? sure. The distinction between 386 and other architectures is only there as an optimisation. You can take it out completely and it will make no difference. The routines (macros) in byteorder.h are totally byteorder independent. The 386 optimsation just takes advantage of the fact that the x86 processors don't care about alignment, so we don't have to align ints on int boundaries etc. If there are other processors out there that aren't alignment sensitive then you could also define CAREFUL_ALIGNMENT=0 on those processors as well. Ok, now to the macros themselves. I'll take a simple example, say we want to extract a 2 byte integer from a SMB packet and put it into a type called uint16_t that is in the local machines byte order, and you want to do it with only the assumption that uint16_t is _at_least_ 16 bits long (this last condition is very important for architectures that don't have any int types that are 2 bytes long) You do this: #define CVAL(buf,pos) (((unsigned char *)(buf))[pos]) #define PVAL(buf,pos) ((unsigned)CVAL(buf,pos)) #define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8) then to extract a uint16_t value at offset 25 in a buffer you do this: char *buffer = foo_bar(); uint16_t xx = SVAL(buffer,25); We are using the byteoder independence of the ANSI C bitshifts to do the work. A good optimising compiler should turn this into efficient code, especially if it happens to have the right byteorder :-) I know these macros can be made a bit tidier by removing some of the casts, but you need to look at byteorder.h as a whole to see the reasoning behind them. byteorder.h defines the following macros: SVAL(buf,pos) - extract a 2 byte SMB value IVAL(buf,pos) - extract a 4 byte SMB value SVALS(buf,pos) signed version of SVAL() IVALS(buf,pos) signed version of IVAL() SSVAL(buf,pos,val) - put a 2 byte SMB value into a buffer SIVAL(buf,pos,val) - put a 4 byte SMB value into a buffer SSVALS(buf,pos,val) - signed version of SSVAL() SIVALS(buf,pos,val) - signed version of SIVAL() RSVAL(buf,pos) - like SVAL() but for NMB byte ordering RSVALS(buf,pos) - like SVALS() but for NMB byte ordering RIVAL(buf,pos) - like IVAL() but for NMB byte ordering RIVALS(buf,pos) - like IVALS() but for NMB byte ordering RSSVAL(buf,pos,val) - like SSVAL() but for NMB ordering RSIVAL(buf,pos,val) - like SIVAL() but for NMB ordering RSIVALS(buf,pos,val) - like SIVALS() but for NMB ordering it also defines lots of intermediate macros, just ignore those :-) @endverbatim */ #undef CAREFUL_ALIGNMENT /* we know that the 386 can handle misalignment and has the "right" byteorder */ #ifdef __i386__ #define CAREFUL_ALIGNMENT 0 #endif #ifndef CAREFUL_ALIGNMENT #define CAREFUL_ALIGNMENT 1 #endif #define CVAL(buf,pos) ((unsigned)(((const unsigned char *)(buf))[pos])) #define CVAL_NC(buf,pos) (((unsigned char *)(buf))[pos]) /* Non-const version of CVAL */ #define PVAL(buf,pos) (CVAL(buf,pos)) #define SCVAL(buf,pos,val) (CVAL_NC(buf,pos) = (val)) #if CAREFUL_ALIGNMENT #define SVAL(buf,pos) (PVAL(buf,pos)|PVAL(buf,(pos)+1)<<8) #define IVAL(buf,pos) (SVAL(buf,pos)|SVAL(buf,(pos)+2)<<16) #define SSVALX(buf,pos,val) (CVAL_NC(buf,pos)=(unsigned char)((val)&0xFF),CVAL_NC(buf,pos+1)=(unsigned char)((val)>>8)) #define SIVALX(buf,pos,val) (SSVALX(buf,pos,val&0xFFFF),SSVALX(buf,pos+2,val>>16)) #define SVALS(buf,pos) ((int16_t)SVAL(buf,pos)) #define IVALS(buf,pos) ((int32_t)IVAL(buf,pos)) #define SSVAL(buf,pos,val) SSVALX((buf),(pos),((uint16_t)(val))) #define SIVAL(buf,pos,val) SIVALX((buf),(pos),((uint32_t)(val))) #define SSVALS(buf,pos,val) SSVALX((buf),(pos),((int16_t)(val))) #define SIVALS(buf,pos,val) SIVALX((buf),(pos),((int32_t)(val))) #else /* CAREFUL_ALIGNMENT */ /* this handles things for architectures like the 386 that can handle alignment errors */ /* WARNING: This section is dependent on the length of int16_t and int32_t being correct */ /* get single value from an SMB buffer */ #define SVAL(buf,pos) (*(const uint16_t *)((const char *)(buf) + (pos))) #define SVAL_NC(buf,pos) (*(uint16_t *)((char *)(buf) + (pos))) /* Non const version of above. */ #define IVAL(buf,pos) (*(const uint32_t *)((const char *)(buf) + (pos))) #define IVAL_NC(buf,pos) (*(uint32_t *)((char *)(buf) + (pos))) /* Non const version of above. */ #define SVALS(buf,pos) (*(const int16_t *)((const char *)(buf) + (pos))) #define SVALS_NC(buf,pos) (*(int16_t *)((char *)(buf) + (pos))) /* Non const version of above. */ #define IVALS(buf,pos) (*(const int32_t *)((const char *)(buf) + (pos))) #define IVALS_NC(buf,pos) (*(int32_t *)((char *)(buf) + (pos))) /* Non const version of above. */ /* store single value in an SMB buffer */ #define SSVAL(buf,pos,val) SVAL_NC(buf,pos)=((uint16_t)(val)) #define SIVAL(buf,pos,val) IVAL_NC(buf,pos)=((uint32_t)(val)) #define SSVALS(buf,pos,val) SVALS_NC(buf,pos)=((int16_t)(val)) #define SIVALS(buf,pos,val) IVALS_NC(buf,pos)=((int32_t)(val)) #endif /* CAREFUL_ALIGNMENT */ /* now the reverse routines - these are used in nmb packets (mostly) */ #define SREV(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) #define IREV(x) ((SREV(x)<<16) | (SREV((x)>>16))) #define RSVAL(buf,pos) SREV(SVAL(buf,pos)) #define RSVALS(buf,pos) SREV(SVALS(buf,pos)) #define RIVAL(buf,pos) IREV(IVAL(buf,pos)) #define RIVALS(buf,pos) IREV(IVALS(buf,pos)) #define RSSVAL(buf,pos,val) SSVAL(buf,pos,SREV(val)) #define RSSVALS(buf,pos,val) SSVALS(buf,pos,SREV(val)) #define RSIVAL(buf,pos,val) SIVAL(buf,pos,IREV(val)) #define RSIVALS(buf,pos,val) SIVALS(buf,pos,IREV(val)) /* Alignment macros. */ #define ALIGN4(p,base) ((p) + ((4 - (PTR_DIFF((p), (base)) & 3)) & 3)) #define ALIGN2(p,base) ((p) + ((2 - (PTR_DIFF((p), (base)) & 1)) & 1)) #endif /* _BYTEORDER_H */ reglookup-0.12.0/include/lru_cache.h000066400000000000000000000050161135112127200173170ustar00rootroot00000000000000/* * Copyright (C) 2008-2010 Timothy D. Morgan * * 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; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: lru_cache.h 169 2010-03-03 19:24:58Z tim $ */ /** * @file * * A data structure which approximates a least recently used (LRU) cache. * Implemented as a basic randomized hash table. */ #ifndef LRU_CACHE_H #define LRU_CACHE_H #include #include #include #include #include #include #include "talloc.h" struct lru_cache_element; typedef struct lru_cache_element lru_cache_element; struct lru_cache_element { void* index; uint32_t index_len; void* data; lru_cache_element* next; lru_cache_element* older; lru_cache_element* newer; }; /** XXX: document this. */ typedef struct _lru_cache { uint32_t secret; uint32_t num_keys; uint32_t num_buckets; uint32_t max_keys; lru_cache_element* oldest; lru_cache_element* newest; lru_cache_element** table; bool talloc_data; } lru_cache; /** * XXX: finish documenting. */ lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret); /** * XXX: finish documenting. */ lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys, uint32_t secret, bool talloc_data); /** * XXX: finish documenting. */ void lru_cache_destroy(lru_cache* ht); /** * XXX: finish documenting. */ bool lru_cache_update(lru_cache* ht, const void* index, uint32_t index_len, void* data); /** * XXX: finish documenting. * * @return A pointer to data previously stored at index. * If no data was found at index, NULL is returned. */ void* lru_cache_find(lru_cache* ht, const void* index, uint32_t index_len); /** * XXX: finish documenting. * * Removes entry from table at index. * * @return A pointer to data that was there previously or NULL if no entry is * at index. */ bool lru_cache_remove(lru_cache* ht, const void* index, uint32_t index_len); #endif reglookup-0.12.0/include/range_list.h000066400000000000000000000126051135112127200175230ustar00rootroot00000000000000/* * Copyright (C) 2008-2010 Timothy D. Morgan * * 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; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: range_list.h 169 2010-03-03 19:24:58Z tim $ */ /** * @file * * A data structure which stores a list of address ranges. * * range_lists support basic in-place modifications and maintain the address * space in sorted order. Looking up a range_list_element is implemented * through binary search. */ #ifndef _RANGE_LIST_H #define _RANGE_LIST_H #include #include #include #include #include #include "talloc.h" typedef struct _range_list_element { uint32_t offset; uint32_t length; void* data; } range_list_element; /** XXX: document this. */ typedef struct _range_list { range_list_element** elements; uint32_t elem_alloced; uint32_t size; } range_list; /** Allocates a new range_list. * * @return A newly allocated range_list, or NULL if an error occurred. */ range_list* range_list_new(); /** Frees the memory associated with a range_list, including the elements, but * not any data parameters referenced by those elements. * * If rl is NULL, does nothing. * * @param rl the range_list to be free()d. */ void range_list_free(range_list* rl); /** Query the current number of elements on a range_list * * @param rl the range_list to query * * @return The number of elements currently in the list. */ uint32_t range_list_size(const range_list* rl); /** Adds an element to the range_list. * * The new element must not overlap with others. * NOTE: this is a slow operation. * * @param rl the range list to update * @param offset the starting point for the range * @param length the length of the range * @param data misc data associated with this range element * * @return true on success, false on failure. * * Failures can occur due to memory limitations, max_size limitations, * or if the submitted range overlaps with an existing element. Other * errors may also be possible. */ bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data); /** Removes an element from the list. * * The element data structure will be freed, but the data property will not be. * * @param rl the range_list to modify * @param index the element index to remove * * @return true if the element was successfully removed, false otherwise. */ bool range_list_remove(range_list* rl, uint32_t index); /** Retrieves the element for a given index. * * @param rl the range_list being queried. * @param index the element index desired. * * @return The element for a given index, or NULL if the element is not * available. */ const range_list_element* range_list_get(const range_list* rl, uint32_t index); /** Attempts to find the unique element whose range encompasses offset. * * @param rl the range_list being queried. * @param offset the location for which an element is desired. * * @return A matching element index or a negative value if none could be found. */ int32_t range_list_find(const range_list* rl, uint32_t offset); /** Same as range_list_find(), but returns the data associated with an element. * * @param rl the range_list being queried. * @param offset the address to search for in the ranges * * @return The data element of the matching element index or NULL if none could * be found. * * NOTE: May also return NULL if an element matched but the data * element was never set. */ void* range_list_find_data(const range_list* rl, uint32_t offset); /** Splits an existing element into two elements in place. * * The resulting list will contain an additional element whose offset * is the one provided and whose length extends to the end of the old element * (the one identified by the index). The original element's offset will * remain the same while it's length is shortened such that it is contiguous * with the newly created element. The newly created element will have an index * of one more than the current element. * * Both the original element and the newly created element will reference the * original element's data. * * @param rl the range_list to modify * @param index the index of the element to be split * @param offset the at which the element will be split * * @return true if the element was successfully split, false otherwise. */ bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset); /** Determines whether or not a specified range exists contiguously within the * range_list. * * @param rl the range_list to search * @param start the offset at the beginning of the range * @param length the length of the range * * @return true if the specified range exists and is complete, false otherwise. */ bool range_list_has_range(range_list* rl, uint32_t start, uint32_t length); #endif reglookup-0.12.0/include/regfi.h000066400000000000000000001230541135112127200164710ustar00rootroot00000000000000/* * Copyright (C) 2005-2010 Timothy D. Morgan * Copyright (C) 2005 Gerald (Jerry) Carter * * 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; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: regfi.h 172 2010-03-08 03:04:34Z tim $ */ /** * @file * Windows NT (and later) read-only registry library * * This library is intended for use in digital forensics investigations, but * is likely useful in other applications. * * Branched from Samba project Subversion repository, version #6903: * http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/include/regfio.h?rev=6903&view=auto * * Since then, it has been heavily rewritten, simplified, and improved. */ /** * @mainpage Home * * The regfi library is a read-only NT registry library which serves as the main * engine behind the reglookup tool. It is designed with digital forensic * analysis in mind, but it should also be useful in other tools which need to * efficiently traverse and query registry data structures. * * The library is broken down into four main parts, the * @ref regfiBase "Base Layer", which any code dependent on the library will * likely need to rely on, as well as three main functional layers: * @li @ref regfiIteratorLayer * @li @ref regfiGlueLayer * @li @ref regfiParseLayer * * Most users will find that a combination of the Base Layer and the Iterator Layer * will be sufficient for accessing registry hive files. Those who are wiling * to dive deep into registry data structures, for instance to recover deleted * data structures or to research Windows registry behavior in detail, will * find the Parse Layer to be quite useful. */ #ifndef _REGFI_H #define _REGFI_H #include #include #include #include #include #include #include #include #include #include #include #include "byteorder.h" #include "talloc.h" #include "winsec.h" #include "void_stack.h" #include "range_list.h" #include "lru_cache.h" /******************************************************************************/ /* regfi library error message types */ #define REGFI_MSG_INFO 0x0001 #define REGFI_MSG_WARN 0x0004 #define REGFI_MSG_ERROR 0x0010 typedef uint8_t REGFI_ENCODING; /* regfi library supported character encodings */ #define REGFI_ENCODING_ASCII 0 #define REGFI_ENCODING_UTF8 1 #define REGFI_ENCODING_DEFAULT REGFI_ENCODING_ASCII /* UTF16LE is not supported for output */ #define REGFI_ENCODING_UTF16LE 2 #define REGFI_NUM_ENCODINGS 3 /* Windows is lame */ #ifdef O_BINARY #define REGFI_OPEN_FLAGS O_RDONLY|O_BINARY #else #define REGFI_OPEN_FLAGS O_RDONLY #endif /* Registry data types */ #define REG_NONE 0 #define REG_SZ 1 #define REG_EXPAND_SZ 2 #define REG_BINARY 3 #define REG_DWORD 4 #define REG_DWORD_LE 4 /* DWORD, little endian */ #define REG_DWORD_BE 5 /* DWORD, big endian */ #define REG_LINK 6 #define REG_MULTI_SZ 7 #define REG_RESOURCE_LIST 8 #define REG_FULL_RESOURCE_DESCRIPTOR 9 #define REG_RESOURCE_REQUIREMENTS_LIST 10 #define REG_QWORD 11 /* 64-bit little endian */ /* XXX: Has MS defined a REG_QWORD_BE? */ /* Not a real type in the registry */ #define REG_KEY 0x7FFFFFFF #define REGFI_OFFSET_NONE 0xffffffff /* This maximum depth is described here: * http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx */ #define REGFI_MAX_DEPTH 512 /* This limit defines the maximum number of levels deep that ri subkey list * trees can go. */ /* XXX: This is totally arbitrary right now. * The actual limit may need to be discovered by experimentation. */ #define REGFI_MAX_SUBKEY_DEPTH 255 /* Header sizes and magic number lengths for various records */ #define REGFI_HBIN_ALLOC 0x1000 /* Minimum allocation unit for HBINs */ #define REGFI_REGF_SIZE 0x1000 /* "regf" header block size */ #define REGFI_REGF_MAGIC_SIZE 4 #define REGFI_REGF_NAME_SIZE 64 #define REGFI_REGF_RESERVED1_SIZE 340 #define REGFI_REGF_RESERVED2_SIZE 3528 #define REGFI_HBIN_MAGIC_SIZE 4 #define REGFI_CELL_MAGIC_SIZE 2 #define REGFI_HBIN_HEADER_SIZE 0x20 #define REGFI_NK_MIN_LENGTH 0x4C #define REGFI_VK_MIN_LENGTH 0x14 #define REGFI_SK_MIN_LENGTH 0x14 #define REGFI_SUBKEY_LIST_MIN_LEN 0x4 #define REGFI_BIG_DATA_MIN_LENGTH 0xC /* Constants used for validation */ /* XXX: Can we add clock resolution validation as well as range? It has * been reported that Windows timestamps are never more than a * certain granularity (250ms?), which could be used to help * eliminate false positives. Would need to verify this and * perhaps conservatively implement a check. */ /* Minimum time is Jan 1, 1990 00:00:00 */ #define REGFI_MTIME_MIN_HIGH 0x01B41E6D #define REGFI_MTIME_MIN_LOW 0x26F98000 /* Maximum time is Jan 1, 2290 00:00:00 * (We hope no one is using Windows by then...) */ #define REGFI_MTIME_MAX_HIGH 0x03047543 #define REGFI_MTIME_MAX_LOW 0xC80A4000 /* Flags for the vk records */ #define REGFI_VK_FLAG_ASCIINAME 0x0001 #define REGFI_VK_DATA_IN_OFFSET 0x80000000 #define REGFI_VK_MAX_DATA_LENGTH 1024*1024 /* XXX: This is arbitrary */ /* Known key flags */ /*******************/ /* These next two show up on normal-seeming keys in Vista and W2K3 registries */ #define REGFI_NK_FLAG_UNKNOWN1 0x4000 #define REGFI_NK_FLAG_UNKNOWN2 0x1000 /* This next one shows up in some Vista "software" registries */ /* XXX: This shows up in the following two SOFTWARE keys in Vista: * /Wow6432Node/Microsoft * /Wow6432Node/Microsoft/Cryptography * * It comes along with UNKNOWN2 and ASCIINAME for a total flags value of 0x10A0 */ #define REGFI_NK_FLAG_UNKNOWN3 0x0080 /* Predefined handle. Rumor has it that the valuelist count for this key is * where the handle is stored. * http://msdn.microsoft.com/en-us/library/ms724836(VS.85).aspx */ #define REGFI_NK_FLAG_PREDEF_KEY 0x0040 /* The name will be in ASCII if this next bit is set, otherwise UTF-16LE */ #define REGFI_NK_FLAG_ASCIINAME 0x0020 /* Symlink key. * See: http://www.codeproject.com/KB/system/regsymlink.aspx */ #define REGFI_NK_FLAG_LINK 0x0010 /* This key cannot be deleted */ #define REGFI_NK_FLAG_NO_RM 0x0008 /* Root of a hive */ #define REGFI_NK_FLAG_ROOT 0x0004 /* Mount point of another hive. NULL/(default) value indicates which hive * and where in the hive it points to. */ #define REGFI_NK_FLAG_HIVE_LINK 0x0002 /* These keys shouldn't be stored on disk, according to: * http://geekswithblogs.net/sdorman/archive/2007/12/24/volatile-registry-keys.aspx */ #define REGFI_NK_FLAG_VOLATILE 0x0001 /* Useful for identifying unknown flag types */ #define REGFI_NK_KNOWN_FLAGS (REGFI_NK_FLAG_PREDEF_KEY\ | REGFI_NK_FLAG_ASCIINAME\ | REGFI_NK_FLAG_LINK\ | REGFI_NK_FLAG_NO_RM\ | REGFI_NK_FLAG_ROOT\ | REGFI_NK_FLAG_HIVE_LINK\ | REGFI_NK_FLAG_VOLATILE\ | REGFI_NK_FLAG_UNKNOWN1\ | REGFI_NK_FLAG_UNKNOWN2\ | REGFI_NK_FLAG_UNKNOWN3) #define CHAR_BIT 8 #define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \ : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1)) #define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN) #define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60)) typedef struct _regfi_nttime { uint32_t low; uint32_t high; } REGFI_NTTIME; /** HBIN block information * @ingroup regfiMiddleLayer */ typedef struct _regfi_hbin { /** Offset of this HBIN in the registry file */ uint32_t file_off; /** Number of active records pointing to this block (not used currently) */ uint32_t ref_count; /** Offset from first hbin block */ uint32_t first_hbin_off; /** Block size of this block Should be a multiple of 4096 (0x1000) */ uint32_t block_size; /** Relative offset to next block. * * @note This value may be unreliable! */ uint32_t next_block; /** Magic number for the HBIN (should be "hbin"). */ uint8_t magic[REGFI_HBIN_MAGIC_SIZE]; } REGFI_HBIN; /* Subkey List -- list of key offsets and hashed names for consistency */ typedef struct { /* Virtual offset of NK record or additional subkey list, * depending on this list's type. */ uint32_t offset; uint32_t hash; } REGFI_SUBKEY_LIST_ELEM; /** Subkey-list structure * @ingroup regfiMiddleLayer */ typedef struct _regfi_subkey_list { /* Real offset of this record's cell in the file */ uint32_t offset; uint32_t cell_size; /* Number of immediate children */ uint32_t num_children; /* Total number of keys referenced by this list and it's children */ uint32_t num_keys; REGFI_SUBKEY_LIST_ELEM* elements; uint8_t magic[REGFI_CELL_MAGIC_SIZE]; /* Set if the magic indicates this subkey list points to child subkey lists */ bool recursive_type; } REGFI_SUBKEY_LIST; typedef uint32_t REGFI_VALUE_LIST_ELEM; /** Value-list structure * @ingroup regfiMiddleLayer */ typedef struct _regfi_value_list { /* Actual number of values referenced by this list. * May differ from parent key's num_values if there were parsing errors. */ uint32_t num_values; REGFI_VALUE_LIST_ELEM* elements; } REGFI_VALUE_LIST; /** Class name structure (used in storing SysKeys) * @ingroup regfiBase */ typedef struct _regfi_classname { /** As converted to requested REGFI_ENCODING */ char* interpreted; /** Represents raw buffer read from classname cell. * * Length of this item is specified in the size field. */ uint8_t* raw; /** Length of the raw data. * * May be shorter than that indicated by parent key. */ uint16_t size; } REGFI_CLASSNAME; /** Data record structure * @ingroup regfiBase */ typedef struct _regfi_data { /** Data type of this data, as indicated by the referencing VK record. */ uint32_t type; /** Length of the raw data. */ uint32_t size; /** This is always present, representing the raw data cell contents. */ uint8_t* raw; /** Represents the length of the interpreted value. Meaning is type-specific. */ uint32_t interpreted_size; /** These items represent interpreted versions of the REGFI_DATA::raw field. * * Only use the appropriate member according to the REGFI_DATA::type field. * In the event of an unknown type, use only the REGFI_DATA::raw field. */ union _regfi_data_interpreted { /** REG_NONE * * Stored as a raw buffer. Use REGFI_DATA::interpreted_size to determine * length. */ uint8_t* none; /** REG_SZ * * Stored as a NUL terminated string. Converted to the specified * REGFI_ENCODING. */ uint8_t* string; /** REG_EXPAND_SZ * * Stored as a NUL terminated string. Converted to the specified * REGFI_ENCODING. */ uint8_t* expand_string; /** REG_BINARY * * Stored as a raw buffer. Use REGFI_DATA::interpreted_size to determine * length. */ uint8_t* binary; /** REG_DWORD */ uint32_t dword; /** REG_DWORD_BE */ uint32_t dword_be; /** REG_LINK * * Stored as a NUL terminated string. Converted to the specified * REGFI_ENCODING. */ uint8_t* link; /** REG_MULTI_SZ * * Stored as a list of uint8_t* pointers, terminated with a NULL pointer. * Each string element in the list is NUL terminated, and the character set * is determined by the specified REGFI_ENCODING. */ uint8_t** multiple_string; /** REG_QWORD */ uint64_t qword; /* The following are treated as binary currently, but this may change in * the future as the formats become better understood. */ /** REG_RESOURCE_LIST * * Stored as a raw buffer. Use REGFI_DATA::interpreted_size to determine * length. */ uint8_t* resource_list; /** REG_FULL_RESOURCE_DESCRIPTOR * * Stored as a raw buffer. Use REGFI_DATA::interpreted_size to determine * length. */ uint8_t* full_resource_descriptor; /** REG_RESOURCE_REQUIREMENTS_LIST * * Stored as a raw buffer. Use REGFI_DATA::interpreted_size to determine * length. */ uint8_t* resource_requirements_list; } interpreted; } REGFI_DATA; /** Value structure * @ingroup regfiBase */ typedef struct { /** Real offset of this record's cell in the file */ uint32_t offset; /** ((start_offset - end_offset) & 0xfffffff8) */ uint32_t cell_size; /* XXX: deprecated */ REGFI_DATA* data; /** The name of this value converted to desired REGFI_ENCODING. * * This conversion typically occurs automatically through REGFI_ITERATOR * settings. String is NUL terminated. */ char* valuename; /** The raw value name * * Length of the buffer is stored in name_length. */ uint8_t* valuename_raw; /** Length of valuename_raw */ uint16_t name_length; /** Offset from beginning of this hbin block */ uint32_t hbin_off; /** Size of the value's data as reported in the VK record. * * May be different than that obtained while parsing the data cell itself. */ uint32_t data_size; /** Virtual offset of data cell */ uint32_t data_off; /** Value's data type */ uint32_t type; /** VK record's magic number (should be "vk") */ uint8_t magic[REGFI_CELL_MAGIC_SIZE]; /** VK record flags */ uint16_t flags; /* XXX: A 2-byte field of unknown purpose stored in the VK record */ uint16_t unknown1; /** Whether or not the data record is stored in the VK record's data_off field. * * This information is derived from the high bit of the raw data size field. */ bool data_in_offset; } REGFI_VK_REC; /* Key Security */ struct _regfi_sk_rec; /** Security structure * @ingroup regfiBase */ typedef struct _regfi_sk_rec { /** Real file offset of this record */ uint32_t offset; /** ((start_offset - end_offset) & 0xfffffff8) */ uint32_t cell_size; /** The stored Windows security descriptor for this SK record */ WINSEC_DESC* sec_desc; /** Offset of this record from beginning of this hbin block */ uint32_t hbin_off; /** Offset of the previous SK record in the linked list of SK records */ uint32_t prev_sk_off; /** Offset of the next SK record in the linked list of SK records */ uint32_t next_sk_off; /** Number of keys referencing this SK record */ uint32_t ref_count; /** Size of security descriptor (sec_desc) */ uint32_t desc_size; /* XXX: A 2-byte field of unknown purpose */ uint16_t unknown_tag; /** The magic number for this record (should be "sk") */ uint8_t magic[REGFI_CELL_MAGIC_SIZE]; } REGFI_SK_REC; /** Key structure * @ingroup regfiBase */ typedef struct { /** Real offset of this record's cell in the file */ uint32_t offset; /** Actual or estimated length of the cell. * Always in multiples of 8. */ uint32_t cell_size; /** Preloaded value-list for this key. * This element is loaded automatically when using the iterator interface and * possibly some lower layer interfaces. */ REGFI_VALUE_LIST* values; /** Preloaded subkey-list for this key. * This element is loaded automatically when using the iterator interface and * possibly some lower layer interfaces. */ REGFI_SUBKEY_LIST* subkeys; /** Key flags */ uint16_t flags; /** Magic number of key (should be "nk") */ uint8_t magic[REGFI_CELL_MAGIC_SIZE]; /** Key's last modification time */ REGFI_NTTIME mtime; /** Length of keyname_raw */ uint16_t name_length; /** Length of referenced classname */ uint16_t classname_length; /** The name of this key converted to desired REGFI_ENCODING. * * This conversion typically occurs automatically through REGFI_ITERATOR * settings. String is NUL terminated. */ char* keyname; /** The raw key name * * Length of the buffer is stored in name_length. */ uint8_t* keyname_raw; /** Virutal offset of parent key */ uint32_t parent_off; /** Virutal offset of classname key */ uint32_t classname_off; /* XXX: max subkey name * 2 */ uint32_t max_bytes_subkeyname; /* XXX: max subkey classname length (as if) */ uint32_t max_bytes_subkeyclassname; /* XXX: max valuename * 2 */ uint32_t max_bytes_valuename; /* XXX: max value data size */ uint32_t max_bytes_value; /* XXX: Fields of unknown purpose */ uint32_t unknown1; uint32_t unknown2; uint32_t unknown3; uint32_t unk_index; /* nigel says run time index ? */ /** Number of subkeys */ uint32_t num_subkeys; /** Virtual offset of subkey-list */ uint32_t subkeys_off; /** Number of values for this key */ uint32_t num_values; /** Virtual offset of value-list */ uint32_t values_off; /** Virtual offset of SK record */ uint32_t sk_off; } REGFI_NK_REC; /** Registry hive file data structure * * This essential structure stores run-time information about a single open * registry hive as well as file header (REGF block) data. This structure * also stores a list of warnings and error messages generated while parsing * the registry hive. These can be tuned using @ref regfi_set_message_mask. * Messages may be retrieved using @ref regfi_get_messages. * * @note If the message mask is set to record any messages, dependent code * must use @ref regfi_get_messages periodically to clear the message * queue. Otherwise, this structure will grow in size over time as * messages queue up. * * @ingroup regfiBase */ typedef struct { /* Run-time information */ /************************/ /* file descriptor */ int fd; /* For sanity checking (not part of the registry header) */ uint32_t file_length; /* Metadata about hbins */ range_list* hbins; /* SK record cached since they're repeatedly reused */ lru_cache* sk_cache; /* Error/warning/info messages returned by lower layer functions */ char* last_message; /* Mask for error message types that will be stored. */ uint16_t msg_mask; /* Data parsed from file header */ /********************************/ uint8_t magic[REGFI_REGF_MAGIC_SIZE];/* "regf" */ /* These sequence numbers should match if * the hive was properly synced to disk. */ uint32_t sequence1; uint32_t sequence2; REGFI_NTTIME mtime; uint32_t major_version; /* Set to 1 in all known hives */ uint32_t minor_version; /* Set to 3 or 5 in all known hives */ uint32_t type; /* XXX: Unverified. Set to 0 in all known hives */ uint32_t format; /* XXX: Unverified. Set to 1 in all known hives */ uint32_t root_cell; /* Offset to root cell in the first (or any?) hbin block */ uint32_t last_block; /* Offset to last hbin block in file */ uint32_t cluster; /* XXX: Unverified. Set to 1 in all known hives */ /* Matches hive's base file name. Stored in UTF-16LE */ uint8_t file_name[REGFI_REGF_NAME_SIZE]; WINSEC_UUID* rm_id; /* XXX: Unverified. */ WINSEC_UUID* log_id; /* XXX: Unverified. */ WINSEC_UUID* tm_id; /* XXX: Unverified. */ uint32_t flags; /* XXX: Unverified. */ uint32_t guid_signature; /* XXX: Unverified. */ uint32_t checksum; /* Stored checksum from file */ uint32_t computed_checksum; /* Our own calculation of the checksum. * (XOR of bytes 0x0000 - 0x01FB) */ WINSEC_UUID* thaw_tm_id; /* XXX: Unverified. */ WINSEC_UUID* thaw_rm_id; /* XXX: Unverified. */ WINSEC_UUID* thaw_log_id; /* XXX: Unverified. */ uint32_t boot_type; /* XXX: Unverified. */ uint32_t boot_recover; /* XXX: Unverified. */ /* This seems to include random junk. Possibly unsanitized memory left over * from when header block was written. For instance, chunks of nk records * can be found, though often it's all 0s. */ uint8_t reserved1[REGFI_REGF_RESERVED1_SIZE]; /* This is likely reserved and unusued currently. (Should be all 0s.) * Included here for easier access in looking for hidden data * or doing research. */ uint8_t reserved2[REGFI_REGF_RESERVED2_SIZE]; } REGFI_FILE; /** Registry hive iterator * @ingroup regfiIteratorLayer */ typedef struct _regfi_iterator { /** The registry hive this iterator is associated with */ REGFI_FILE* f; /** All current parent keys and associated iterator positions */ void_stack* key_positions; /** The current key */ REGFI_NK_REC* cur_key; /** The encoding that all strings are converted to as set during iterator * creation. */ REGFI_ENCODING string_encoding; /** Index of the current subkey */ uint32_t cur_subkey; /** Index of the current value */ uint32_t cur_value; } REGFI_ITERATOR; typedef struct _regfi_iter_position { REGFI_NK_REC* nk; uint32_t cur_subkey; /* We could store a cur_value here as well, but didn't see * the use in it right now. */ } REGFI_ITER_POSITION; /** General purpose buffer with stored length * @ingroup regfiBottomLayer */ typedef struct _regfi_buffer { uint8_t* buf; uint32_t len; } REGFI_BUFFER; /******************************************************************************/ /** * @defgroup regfiBase Base Layer: Essential Functions and Data Structures * * These functions are either necessary for normal use of the regfi API or just * don't fit particularly well in any of the other layers. */ /******************************************************************************/ /** Attempts to open a registry hive and allocate related data structures. * * @param filename A string containing the relative or absolute path of the * registry hive to be opened. * * @return A reference to a newly allocated REGFI_FILE structure, * if successful; NULL on error. * * @ingroup regfiBase */ REGFI_FILE* regfi_open(const char* filename); /** Parses file headers of an already open registry hive file and * allocates related structures for further parsing. * * @param fd A file descriptor of an already open file. Must be seekable. * * @return A reference to a newly allocated REGFI_FILE structure, if successful; * NULL on error. * * @ingroup regfiBase */ REGFI_FILE* regfi_alloc(int fd); /** Closes and frees an open registry hive. * * @param file The registry structure to close. * * @return 0 on success, -1 on failure with errno set. * errno codes are similar to those of close(2). * * @ingroup regfiBase */ int regfi_close(REGFI_FILE* file); /** Frees a hive's data structures without closing the underlying file. * * @param file The registry structure to free. * * @ingroup regfiBase */ void regfi_free(REGFI_FILE* file); /** Get errors, warnings, and/or verbose information relating to processing of * the given registry file. * * @param file the structure for the registry file * * @return A newly allocated char* which must be free()d by the caller. * * @ingroup regfiBase */ char* regfi_get_messages(REGFI_FILE* file); /** Set the verbosity level of errors and warnings generated by the library * (as accessible via regfi_get_messages). * * This may be called at any time and will take effect immediately. * * @param file the structure for the registry file * * @param mask an integer representing the types of messages desired. * Acceptable values are created through bitwise ORs of * REGFI_MSG_* values. For instance, if only errors and * informational messages were desired (but not warnings), * then one would specify: REGFI_MSG_ERROR|REGFI_MSG_INFO * New REGFI_FILE structures are created with: * REGFI_MSG_ERROR|REGFI_MSG_WARN * Note that error and warning messages will continue to * accumulate in memory if they are not fetched using * regfi_get_messages and then freed by the caller. * To disable error messages entirely, supply 0, which * will prevent message accumulation. * * @ingroup regfiBase */ void regfi_set_message_mask(REGFI_FILE* file, uint16_t mask); /* Dispose of previously parsed records */ /** Frees a key structure previously returned by one of the API functions * * XXX: finish documenting * * @ingroup regfiBase */ void regfi_free_key(REGFI_NK_REC* nk); /** Frees a value structure previously returned by one of the API functions * * XXX: finish documenting * * @ingroup regfiBase */ void regfi_free_value(REGFI_VK_REC* vk); /******************************************************************************/ /** * @defgroup regfiIteratorLayer Iterator Layer: Primary regfi Library Interface * * This top layer of API functions provides an iterator interface which makes * traversing registry data structures easy in both single-threaded and * multi-threaded scenarios. */ /******************************************************************************/ /** Creates a new iterator for the provided registry file. * * @param file The opened registry file the iterator should be created for. * * @param output_encoding Character encoding that strings should be returned in. * Only supply the REGFI_ENCODING_* constants, as others * will be rejected. * The following values are currently accepted: * REGFI_ENCODING_DEFAULT (currently REGFI_ENCODING_ASCII) * REGFI_ENCODING_ASCII * REGFI_ENCODING_UTF8 * * @return A newly allocated REGFI_ITERATOR. * Must be free()d with regfi_iterator_free. * * @ingroup regfiIteratorLayer */ REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file, REGFI_ENCODING output_encoding); /** Frees a registry file iterator previously created by regfi_iterator_new. * * This does not affect the underlying registry file's allocation status. * * @param i the iterator to be freed * * @ingroup regfiIteratorLayer */ void regfi_iterator_free(REGFI_ITERATOR* i); /** Traverse deeper into the registry tree at the current subkey. * * @param i the iterator * * @return true on success, false on failure. * Note that subkey and value indexes are preserved. That is, if a * regfi_iterator_up call occurs later (reversing the effect of this * call) then the subkey and value referenced prior to the * regfi_iterator_down call will still be referenced. This makes * depth-first iteration particularly easy. * * @ingroup regfiIteratorLayer */ bool regfi_iterator_down(REGFI_ITERATOR* i); /** Traverse up to the current key's parent key. * * @param i the iterator * * @return true on success, false on failure. Any subkey or value state * associated with the current key is lost. * * @ingroup regfiIteratorLayer */ bool regfi_iterator_up(REGFI_ITERATOR* i); /** Traverse up to the root key of the hive. * * @param i the iterator * * @return true on success, false on failure. * * @ingroup regfiIteratorLayer */ bool regfi_iterator_to_root(REGFI_ITERATOR* i); /** Traverse down multiple levels in the registry hive. * * XXX: This currently only accepts ASCII key names. Need to look into * accepting other encodings. * * @param i the iterator * @param path a list of key names representing the path. This list must * contain NUL terminated strings. The list itself is * terminated with a NULL pointer. All path elements must be * keys; value names are not accepted (even as the last * element). * * @return true on success, false on failure. If any element of path is not * found, false will be returned and the iterator will remain * in its original position. * * @ingroup regfiIteratorLayer */ bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path); /** Returns the currently referenced key. * * @param i the iterator * * @return A read-only key structure for the current key, or NULL on failure. * * @ingroup regfiIteratorLayer */ const REGFI_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i); /** Returns the SK (security) record referenced by the current key. * * @param i the iterator * * @return A read-only SK structure, or NULL on failure. * * @ingroup regfiIteratorLayer */ const REGFI_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i); /** Sets the internal subkey index to the first subkey referenced by the current * key and returns that key. * * @param i the iterator * * @return A newly allocated key structure for the newly referenced first * subkey, or NULL on failure. Failure may be due to a lack of any * subkeys or other errors. Newly allocated keys must be freed with * regfi_free_key. * * @ingroup regfiIteratorLayer */ REGFI_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i); /** Returns the currently indexed subkey. * * @param i the iterator * * @return A newly allocated key structure for the currently referenced subkey, * or NULL on failure. Newly allocated keys must be freed with * regfi_free_key. * * @ingroup regfiIteratorLayer */ REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i); /** Increments the internal subkey index to the next key in the subkey-list and * returns the subkey for that index. * * @param i the iterator * * @return A newly allocated key structure for the next subkey or NULL on * failure. Newly allocated keys must be freed with regfi_free_key. * * @ingroup regfiIteratorLayer */ REGFI_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i); /** Searches for a subkey with a given name under the current key. * * @param i the iterator * @param subkey_name subkey name to search for * * @return True if such a subkey was found, false otherwise. If a subkey is * found, the current subkey index is set to that subkey. Otherwise, * the subkey index remains at the same location as before the call. * * @ingroup regfiIteratorLayer */ bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name); /** Sets the internal value index to the first value referenced by the current * key and returns that value. * * @param i the iterator * * @return A newly allocated value structure for the newly referenced first * value, or NULL on failure. Failure may be due to a lack of any * values or other errors. Newly allocated keys must be freed with * regfi_free_value. * * @ingroup regfiIteratorLayer */ REGFI_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i); /** Returns the currently indexed value. * * @param i the iterator * * @return A newly allocated value structure for the currently referenced value, * or NULL on failure. Newly allocated values must be freed with * regfi_free_value. * * @ingroup regfiIteratorLayer */ REGFI_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i); /** Increments the internal value index to the next value in the value-list and * returns the value for that index. * * @param i the iterator * * @return A newly allocated key structure for the next value or NULL on * failure. Newly allocated keys must be freed with regfi_free_value. * * @ingroup regfiIteratorLayer */ REGFI_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i); /** Searches for a value with a given name under the current key. * * @param i the iterator * @param value_name value name to search for * * @return True if such a value was found, false otherwise. If a value is * found, the current value index is set to that value. Otherwise, * the value index remains at the same location as before the call. * * @ingroup regfiIteratorLayer */ bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name); /** Retrieves classname for a given key. * * @param i the iterator * @param key the key whose classname is desired * * @return Returns a newly allocated classname structure, or NULL on failure. * Classname structures must be freed with regfi_free_classname. * * @ingroup regfiIteratorLayer */ REGFI_CLASSNAME* regfi_iterator_fetch_classname(REGFI_ITERATOR* i, const REGFI_NK_REC* key); /** Retrieves data for a given value. * * @param i the iterator * @param value the value whose data is desired * * @return Returns a newly allocated data structure, or NULL on failure. * Data structures must be freed with regfi_free_data. * * @ingroup regfiIteratorLayer */ REGFI_DATA* regfi_iterator_fetch_data(REGFI_ITERATOR* i, const REGFI_VK_REC* value); /******************************************************************************/ /** * @defgroup regfiGlueLayer Glue Layer: Logical Data Structure Loading */ /******************************************************************************/ /** Loads a key at a given file offset along with associated data structures. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset, REGFI_ENCODING output_encoding, bool strict); /** Loads a value at a given file offset alng with associated data structures. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32_t offset, REGFI_ENCODING output_encoding, bool strict); /** Loads a logical subkey list in its entirety which may span multiple records. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset, uint32_t num_keys, uint32_t max_size, bool strict); /** Loads a valuelist. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset, uint32_t num_values, uint32_t max_size, bool strict); /** Loads a data record which may be contained in the virtual offset, in a * single cell, or in multiple cells through big data records. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset, uint32_t length, bool data_in_offset, bool strict); /** Loads the data associated with a big data record at the specified offset. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, uint32_t offset, uint32_t data_length,uint32_t cell_length, range_list* used_ranges, bool strict); /** Given raw data, attempts to interpret the data based on a specified registry * data type. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ bool regfi_interpret_data(REGFI_FILE* file, REGFI_ENCODING string_encoding, uint32_t type, REGFI_DATA* data); /** Frees the memory associated with a REGFI_CLASSNAME data structure. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ void regfi_free_classname(REGFI_CLASSNAME* classname); /** Frees the memory associated with a REGFI_DATA data structure. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ void regfi_free_data(REGFI_DATA* data); /* These are cached so return values don't need to be freed. */ /** Loads an "sk" security record at the specified offset. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32_t offset, bool strict); /** Retrieves the HBIN data structure stored at the specified offset. * * XXX: finish documenting * * @ingroup regfiGlueLayer */ const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset); /******************************************************************************/ /** * @defgroup regfiParseLayer Parsing Layer: Direct Data Structure Access */ /******************************************************************************/ REGFI_FILE* regfi_parse_regf(int fd, bool strict); REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, bool strict); /** Parses an NK record at the specified offset * * @param file the registry file structure * @param offset the offset of the cell (not the record) to be parsed. * @param max_size the maximum size the NK cell could be. (for validation) * @param strict if true, rejects any malformed records. Otherwise, * tries to minimally validate integrity. * * @return A newly allocated NK record structure, or NULL on failure. * * @ingroup regfiParseLayer */ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict); /** Parses a single cell containing a subkey-list record. * * XXX: finish documenting * * @ingroup regfiParseLayer */ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict); /** Parses a VK (value) record at the specified offset * * XXX: finish documenting * * @ingroup regfiParseLayer */ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict); /** Parses an SK (security) record at the specified offset * * XXX: finish documenting * * @ingroup regfiParseLayer */ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict); /** Retrieves information on all cells in the registry hive which are * currently in the unallocated status. * * The unallocated status is determined based soley on the cell length sign. * * XXX: finish documenting * * @ingroup regfiParseLayer */ range_list* regfi_parse_unalloc_cells(REGFI_FILE* file); /** Helper function to parse a cell * * XXX: finish documenting * * @ingroup regfiParseLayer */ bool regfi_parse_cell(int fd, uint32_t offset, uint8_t* hdr, uint32_t hdr_len, uint32_t* cell_length, bool* unalloc); /** Parses a classname cell * * XXX: finish documenting * * @ingroup regfiParseLayer */ uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset, uint16_t* name_length, uint32_t max_size, bool strict); /** Parses a single-cell data record * * XXX: finish documenting * * @ingroup regfiParseLayer */ REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset, uint32_t length, bool strict); /** Parses a "little data" record which is stored entirely within the * provided virtual offset. * * XXX: finish documenting * * @ingroup regfiParseLayer */ REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset, uint32_t length, bool strict); /******************************************************************************/ /* Private Functions */ /******************************************************************************/ REGFI_NK_REC* regfi_rootkey(REGFI_FILE* file, REGFI_ENCODING output_encoding); void regfi_subkeylist_free(REGFI_SUBKEY_LIST* list); uint32_t regfi_read(int fd, uint8_t* buf, uint32_t* length); const char* regfi_type_val2str(unsigned int val); int regfi_type_str2val(const char* str); char* regfi_get_sacl(WINSEC_DESC* sec_desc); char* regfi_get_dacl(WINSEC_DESC* sec_desc); char* regfi_get_owner(WINSEC_DESC* sec_desc); char* regfi_get_group(WINSEC_DESC* sec_desc); REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16_t num_lists, REGFI_SUBKEY_LIST** lists, bool strict); REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict, uint8_t depth_left); void regfi_add_message(REGFI_FILE* file, uint16_t msg_type, const char* fmt, ...); REGFI_NK_REC* regfi_copy_nk(const REGFI_NK_REC* nk); REGFI_VK_REC* regfi_copy_vk(const REGFI_VK_REC* vk); int32_t regfi_calc_maxsize(REGFI_FILE* file, uint32_t offset); int32_t regfi_conv_charset(const char* input_charset, const char* output_charset, uint8_t* input, char* output, uint32_t input_len, uint32_t output_max); REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data); /* XXX: move to base API and document */ void regfi_unix2nt_time(REGFI_NTTIME* nt, time_t t); time_t regfi_nt2unix_time(const REGFI_NTTIME* nt); void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk, REGFI_ENCODING output_encoding, bool strict); void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk, REGFI_ENCODING output_encoding, bool strict); #endif /* _REGFI_H */ reglookup-0.12.0/include/talloc.h000066400000000000000000000166601135112127200166570ustar00rootroot00000000000000/* Unix SMB/CIFS implementation. Samba temporary memory allocation functions Copyright (C) Andrew Tridgell 2004-2005 Copyright (C) Stefan Metzmacher 2006 Copyright (C) Timothy D. Morgan 2009 ** NOTE! The following LGPL license applies to the talloc library. ** This does NOT imply that all of reglookup is released under the LGPL This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see . */ /* $Id: talloc.h 168 2010-03-03 00:08:42Z tim $ */ #ifndef _TALLOC_H_ #define _TALLOC_H_ #include #include #include #include #include #ifndef MAX #define MAX(a,b) ((a)>(b)?(a):(b)) #endif #ifndef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) #endif /* this uses a little trick to allow __LINE__ to be stringified */ #ifndef __location__ #define __TALLOC_STRING_LINE1__(s) #s #define __TALLOC_STRING_LINE2__(s) __TALLOC_STRING_LINE1__(s) #define __TALLOC_STRING_LINE3__ __TALLOC_STRING_LINE2__(__LINE__) #define __location__ __FILE__ ":" __TALLOC_STRING_LINE3__ #endif #ifndef TALLOC_DEPRECATED #define TALLOC_DEPRECATED 0 #endif #ifndef PRINTF_ATTRIBUTE #if (__GNUC__ >= 3) /** Use gcc attribute to check printf fns. a1 is the 1-based index of * the parameter containing the format, and a2 the index of the first * argument. Note that some gcc 2.x versions don't handle this * properly **/ #define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) #else #define PRINTF_ATTRIBUTE(a1, a2) #endif #endif #define talloc_set_destructor(ptr, function) \ _talloc_set_destructor((ptr), (int (*)(void *))(function)) #define _TALLOC_TYPEOF(ptr) void * #define talloc_steal(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_steal((ctx),(ptr)) #define talloc_reference(ctx, ptr) (_TALLOC_TYPEOF(ptr))_talloc_reference((ctx),(ptr)) #define talloc_move(ctx, ptr) (_TALLOC_TYPEOF(*(ptr)))_talloc_move((ctx),(void *)(ptr)) /* useful macros for creating type checked pointers */ #define talloc(ctx, type) (type *)talloc_named_const(ctx, sizeof(type), #type) #define talloc_size(ctx, size) talloc_named_const(ctx, size, __location__) #define talloc_ptrtype(ctx, ptr) (_TALLOC_TYPEOF(ptr))talloc_size(ctx, sizeof(*(ptr))) #define talloc_new(ctx) talloc_named_const(ctx, 0, "talloc_new: " __location__) #define talloc_zero(ctx, type) (type *)_talloc_zero(ctx, sizeof(type), #type) #define talloc_zero_size(ctx, size) _talloc_zero(ctx, size, __location__) #define talloc_zero_array(ctx, type, count) (type *)_talloc_zero_array(ctx, sizeof(type), count, #type) #define talloc_array(ctx, type, count) (type *)_talloc_array(ctx, sizeof(type), count, #type) #define talloc_array_size(ctx, size, count) _talloc_array(ctx, size, count, __location__) #define talloc_array_ptrtype(ctx, ptr, count) (_TALLOC_TYPEOF(ptr))talloc_array_size(ctx, sizeof(*(ptr)), count) #define talloc_realloc(ctx, p, type, count) (type *)_talloc_realloc_array(ctx, p, sizeof(type), count, #type) #define talloc_realloc_size(ctx, ptr, size) _talloc_realloc(ctx, ptr, size, __location__) #define talloc_memdup(t, p, size) _talloc_memdup(t, p, size, __location__) #define talloc_set_type(ptr, type) talloc_set_name_const(ptr, #type) #define talloc_get_type(ptr, type) (type *)talloc_check_name(ptr, #type) #define talloc_find_parent_bytype(ptr, type) (type *)talloc_find_parent_byname(ptr, #type) /* The following definitions come from talloc.c */ void *_talloc(const void *context, size_t size); void *talloc_pool(const void *context, size_t size); void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)); int talloc_increase_ref_count(const void *ptr); size_t talloc_reference_count(const void *ptr); void *_talloc_reference(const void *context, const void *ptr); int talloc_unlink(const void *context, void *ptr); const char *talloc_set_name(const void *ptr, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); void talloc_set_name_const(const void *ptr, const char *name); void *talloc_named(const void *context, size_t size, const char *fmt, ...) PRINTF_ATTRIBUTE(3,4); void *talloc_named_const(const void *context, size_t size, const char *name); const char *talloc_get_name(const void *ptr); void *talloc_check_name(const void *ptr, const char *name); void *talloc_parent(const void *ptr); const char *talloc_parent_name(const void *ptr); void *talloc_init(const char *fmt, ...) PRINTF_ATTRIBUTE(1,2); int talloc_free(void *ptr); void talloc_free_children(void *ptr); void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name); void *_talloc_steal(const void *new_ctx, const void *ptr); void *_talloc_move(const void *new_ctx, const void *pptr); size_t talloc_total_size(const void *ptr); size_t talloc_total_blocks(const void *ptr); void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, void (*callback)(const void *ptr, int depth, int max_depth, int is_ref, void *private_data), void *private_data); void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f); void talloc_report_full(const void *ptr, FILE *f); void talloc_report(const void *ptr, FILE *f); void talloc_enable_null_tracking(void); void talloc_disable_null_tracking(void); void talloc_enable_leak_report(void); void talloc_enable_leak_report_full(void); void *_talloc_zero(const void *ctx, size_t size, const char *name); void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name); void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name); void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name); void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name); void *talloc_realloc_fn(const void *context, void *ptr, size_t size); void *talloc_autofree_context(void); size_t talloc_get_size(const void *ctx); void *talloc_find_parent_byname(const void *ctx, const char *name); void talloc_show_parents(const void *context, FILE *file); int talloc_is_parent(const void *context, const void *ptr); char *talloc_strdup(const void *t, const char *p); char *talloc_strdup_append(char *s, const char *a); char *talloc_strdup_append_buffer(char *s, const char *a); char *talloc_strndup(const void *t, const char *p, size_t n); char *talloc_strndup_append(char *s, const char *a, size_t n); char *talloc_strndup_append_buffer(char *s, const char *a, size_t n); char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); char *talloc_asprintf(const void *t, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); char *talloc_asprintf_append(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) PRINTF_ATTRIBUTE(2,3); #endif reglookup-0.12.0/include/void_stack.h000066400000000000000000000111501135112127200175140ustar00rootroot00000000000000/* * Copyright (C) 2005,2007,2009-2010 Timothy D. Morgan * * 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; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: void_stack.h 169 2010-03-03 19:24:58Z tim $ */ /** *@file * * This is a very simple implementation of a stack which stores chunks of * memory of any type. */ #ifndef _VOID_STACK_H #define _VOID_STACK_H #include #include #include #include "talloc.h" /** XXX: document this. */ typedef struct _void_stack { void** elements; unsigned short max_size; unsigned short top; } void_stack; /** XXX: document this. */ typedef struct _void_stack_iterator { const void_stack* stack; unsigned short cur; } void_stack_iterator; /** Allocates a new void_stack. * * @param max_size the maxiumum number of elements * which may be pushed onto the stack. * * @return a pointer to the newly allocated void_stack, * or NULL if an error occurred. */ void_stack* void_stack_new(unsigned short max_size); /** Makes a shallow copy of void_stack. * * @param v the stack to make a copy of. * * @return a pointer to the duplicate void_stack, or NULL if an error occurred. */ void_stack* void_stack_copy(const void_stack* v); /** Makes a shallow copy of void_stack in reverse order. * * @param v the stack to make a copy of. * * @return a pointer to the duplicate void_stack * (which will be in reverse order), or NULL if an error occurred. */ void_stack* void_stack_copy_reverse(const void_stack* v); /** Frees the memory associated with a void_stack, but not the elements held * on the stack. * * @param stack the stack to be free()d. */ void void_stack_free(void_stack* stack); /** Frees the memory associated with a void_stack and the elements referenced * by the stack. * * Do not use this function if the elements of the stack * are also free()d elsewhere, or contain pointers to other memory which * cannot be otherwise free()d. * * @param stack the stack to be free()d. */ void void_stack_free_deep(void_stack* stack); /** Query the current number of elements on a void_stack() * * @param stack the void_stack to query * * @return the number of elements currently on the stack. */ unsigned short void_stack_size(const void_stack* stack); /** Removes the top element on a void_stack and returns a reference to it. * * @param stack the void_stack to pop * * @return a pointer to the popped stack element, or NULL if no elements exist * on the stack. */ void* void_stack_pop(void_stack* stack); /** Puts a new element on the top of a void_stack. * * @param stack the void_stack being modified. * @param e the element to be added * * @return true if the element was successfully added, false otherwise. */ bool void_stack_push(void_stack* stack, void* e); /** Returns a pointer to the current element on the top of the stack. * * @param stack the void_stack being queried. * * @return a pointer to the current element on the top of the stack, or NULL if * no elements exist in the stack. */ const void* void_stack_cur(const void_stack* stack); /** Creates a new iterator for the specified void_stack. * * @param stack the void_stack to be referenced by the new iterator * * @return a new void_stack_iterator, or NULL if an error occurred. */ void_stack_iterator* void_stack_iterator_new(const void_stack* stack); /** Frees a void_stack_iterator. * * Does not affect the void_stack referenced by the iterator. * * @param iter the void_stack_iterator to be free()d. */ void void_stack_iterator_free(void_stack_iterator* iter); /** Returns a pointer to the the next element in the stack. * * Iterates over elements starting in order from the oldest element (bottom of the stack). * * @param iter the void_stack_iterator used to lookup the next element. * * @return a pointer to the next element. */ const void* void_stack_iterator_next(void_stack_iterator* iter); /* XXX: for completeness, might want to add a void_stack_iterator_first() * function, to return iterator to first element */ #endif reglookup-0.12.0/include/winsec.h000066400000000000000000000154571135112127200166740ustar00rootroot00000000000000/* * Copyright (C) 2005,2009-2010 Timothy D. Morgan * Copyright (C) 1992-2005 Samba development team * * 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; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: winsec.h 169 2010-03-03 19:24:58Z tim $ */ /** * @file * * A small library for interpreting Windows Security Descriptors. * This library was originally based on Samba source from: * http://websvn.samba.org/cgi-bin/viewcvs.cgi/trunk/source/ * * The library has been heavily rewritten and improved based on information * provided by Microsoft at: * http://msdn.microsoft.com/en-us/library/cc230366%28PROT.10%29.aspx */ #ifndef _WINSEC_H #define _WINSEC_H #include #include #include #include #include #include #include #include #include #include #include "talloc.h" #include "byteorder.h" /* This is the maximum number of subauths in a SID, as defined here: * http://msdn.microsoft.com/en-us/library/cc230371(PROT.10).aspx */ #define WINSEC_MAX_SUBAUTHS 15 #define WINSEC_DESC_HEADER_SIZE (5 * sizeof(uint32_t)) #define WINSEC_ACL_HEADER_SIZE (2 * sizeof(uint32_t)) #define WINSEC_ACE_MIN_SIZE 16 /* XXX: Fill in definitions of other flags */ /* This self relative flag means offsets contained in the descriptor are relative * to the descriptor's offset. This had better be true in the registry. */ #define WINSEC_DESC_SELF_RELATIVE 0x8000 #define WINSEC_DESC_SACL_PRESENT 0x0010 #define WINSEC_DESC_DACL_PRESENT 0x0004 #define WINSEC_ACE_OBJECT_PRESENT 0x00000001 #define WINSEC_ACE_OBJECT_INHERITED_PRESENT 0x00000002 #define WINSEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT 0x5 #define WINSEC_ACE_TYPE_ACCESS_DENIED_OBJECT 0x6 #define WINSEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT 0x7 #define WINSEC_ACE_TYPE_SYSTEM_ALARM_OBJECT 0x8 /** XXX: document this. */ typedef struct _winsec_uuid { /** XXX: document this. */ uint32_t time_low; /** XXX: document this. */ uint16_t time_mid; /** XXX: document this. */ uint16_t time_hi_and_version; /** XXX: document this. */ uint8_t clock_seq[2]; /** XXX: document this. */ uint8_t node[6]; } WINSEC_UUID; /** XXX: document this. */ typedef struct _winsec_sid { /** SID revision number */ uint8_t sid_rev_num; /** Number of sub-authorities */ uint8_t num_auths; /** Identifier Authority */ uint8_t id_auth[6]; /** Pointer to sub-authorities. * * @note The values in these uint32_t's are in *native* byteorder, not * neccessarily little-endian...... JRA. */ uint32_t sub_auths[WINSEC_MAX_SUBAUTHS]; /* XXX: Make this dynamically allocated? */ } WINSEC_DOM_SID; /** XXX: document this. */ typedef struct _winsec_ace { /** xxxx_xxxx_ACE_TYPE - e.g allowed / denied etc */ uint8_t type; /** xxxx_INHERIT_xxxx - e.g OBJECT_INHERIT_ACE */ uint8_t flags; /** XXX: finish documenting */ uint16_t size; /** XXX: finish documenting */ uint32_t access_mask; /* This stuff may be present when type is XXXX_TYPE_XXXX_OBJECT */ /** xxxx_ACE_OBJECT_xxxx e.g present/inherited present etc */ uint32_t obj_flags; /** Object GUID */ WINSEC_UUID* obj_guid; /** Inherited object GUID */ WINSEC_UUID* inh_guid; /* eof object stuff */ /** XXX: finish documenting */ WINSEC_DOM_SID* trustee; } WINSEC_ACE; /** XXX: document this. */ typedef struct _winsec_acl { /** 0x0003 */ uint16_t revision; /** Size, in bytes, of the entire ACL structure */ uint16_t size; /** Number of Access Control Entries */ uint32_t num_aces; /** XXX: document this. */ WINSEC_ACE** aces; } WINSEC_ACL; /** XXX: document this. */ typedef struct _winsec_desc { /** 0x01 */ uint8_t revision; /** XXX: better explain this * * "If the Control field has the RM flag set, then this field contains the * resource manager (RM) control value. ... Otherwise, this field is reserved * and MUST be set to zero." -- Microsoft. * See: * http://msdn.microsoft.com/en-us/library/cc230371%28PROT.10%29.aspx */ uint8_t sbz1; /** WINSEC_DESC_* flags */ uint16_t control; /** Offset to owner sid */ uint32_t off_owner_sid; /** Offset to group sid */ uint32_t off_grp_sid; /** Offset to system list of permissions */ uint32_t off_sacl; /** Offset to list of permissions */ uint32_t off_dacl; /** XXX: document this */ WINSEC_DOM_SID* owner_sid; /** XXX: document this */ WINSEC_DOM_SID* grp_sid; /** System ACL */ WINSEC_ACL* sacl; /** User ACL */ WINSEC_ACL* dacl; } WINSEC_DESC; /** * * XXX: finish documenting */ WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len); /** * * XXX: finish documenting */ void winsec_free_descriptor(WINSEC_DESC* desc); /** * * XXX: finish documenting */ WINSEC_DESC* winsec_parse_desc(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len); /** * * XXX: finish documenting */ WINSEC_ACL* winsec_parse_acl(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len); /** * * XXX: finish documenting */ WINSEC_ACE* winsec_parse_ace(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len); /** * * XXX: finish documenting */ WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len); /** * * XXX: finish documenting */ WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len); /** * * XXX: finish documenting */ size_t winsec_sid_size(const WINSEC_DOM_SID* sid); /** * * XXX: finish documenting */ int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2); /** * * XXX: finish documenting */ int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2); /** * * XXX: finish documenting */ bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2); /** * * XXX: finish documenting */ bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2); /** * * XXX: finish documenting */ bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2); /** * * XXX: finish documenting */ bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2); /** * * XXX: finish documenting */ bool winsec_ace_object(uint8_t type); #endif /* _WINSEC_H */ reglookup-0.12.0/lib/000077500000000000000000000000001135112127200143425ustar00rootroot00000000000000reglookup-0.12.0/lib/Makefile000066400000000000000000000012601135112127200160010ustar00rootroot00000000000000# $Id: Makefile 30 2005-07-16 14:31:27Z tim $ ################################################################################ FILES=regfi.o winsec.o void_stack.o range_list.o lru_cache.o talloc.o all: $(FILES) regfi.o: regfi.c $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ regfi.c winsec.o: winsec.c $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ winsec.c void_stack.o: void_stack.c $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ void_stack.c range_list.o: range_list.c $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ range_list.c lru_cache.o: lru_cache.c $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ lru_cache.c talloc.o: talloc.c $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ talloc.c clean: rm -f $(FILES) reglookup-0.12.0/lib/lru_cache.c000066400000000000000000000176771135112127200164550ustar00rootroot00000000000000/* * Copyright (C) 2008-2009 Timothy D. Morgan * * 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; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: lru_cache.c 169 2010-03-03 19:24:58Z tim $ */ /** * @file */ #include "lru_cache.h" #define LRU_CACHE_DEBUG 0 /* XXX: really should replace this with a real universal hash or other * fast HMAC. */ static uint32_t lru_cache_compute_hash(uint32_t num_buckets, uint32_t secret, const void* buf, uint32_t buf_len) { uint32_t i; uint32_t ret_val = 0x243f6a88; unsigned char* s = (unsigned char*)&secret; const unsigned char* b = (unsigned char*)buf; for(i=0; i 1; ret_val--) if((n & (1 << ret_val)) != 0) return ret_val; return 0; } #if 0 static void lru_cache_print(lru_cache* ht) { uint32_t i; lru_cache_element* cur; printf("from newest to oldest:\n"); for(cur=ht->newest; cur != NULL; cur=cur->older) { /* write(STDOUT_FILENO, cur->index, cur->index_len);*/ printf("%p", (void*)cur); printf("\n"); if(cur->older == ht->newest) { printf("??? Loop in LRU list!!"); break; } } printf("\n"); printf("table:\n"); for(i=0; inum_buckets; i++) { printf("%.8X: ", i); for(cur=ht->table[i]; cur != NULL; cur=cur->next) { /* write(STDOUT_FILENO, cur->index, cur->index_len);*/ printf("%p", (void*)cur); printf("|"); if(cur->next == ht->table[i]) { printf("??? Loop in table chain!!"); break; } } printf("\n"); } } #endif lru_cache* lru_cache_create(uint32_t max_keys, uint32_t secret) { return lru_cache_create_ctx(NULL, max_keys, secret, false); } lru_cache* lru_cache_create_ctx(void* talloc_ctx, uint32_t max_keys, uint32_t secret, bool talloc_data) { lru_cache* ret_val; ret_val = talloc(talloc_ctx, lru_cache); if(ret_val == NULL) return NULL; if(max_keys == 0) ret_val->num_buckets = 1024; else { ret_val->num_buckets = max_keys/lru_cache_floor_log2(max_keys); if(ret_val->num_buckets < 1) ret_val->num_buckets = 1; } ret_val->table = talloc_array(ret_val, lru_cache_element*, ret_val->num_buckets); if(ret_val->table == NULL) { talloc_free(ret_val); return NULL; } ret_val->oldest = NULL; ret_val->newest = NULL; ret_val->max_keys = max_keys; ret_val->secret = secret; ret_val->talloc_data = talloc_data; ret_val->num_keys = 0; memset(ret_val->table, 0, ret_val->num_buckets*sizeof(lru_cache_element*)); return ret_val; } void lru_cache_destroy(lru_cache* ht) { ht->secret = 0; talloc_free(ht); } bool lru_cache_update(lru_cache* ht, const void* index, uint32_t index_len, void* data) { uint32_t hash, lru_hash; lru_cache_element* cur; lru_cache_element* last = NULL; lru_cache_element* e = NULL; void* tmp_index; hash = lru_cache_compute_hash(ht->num_buckets, ht->secret, index, index_len); for(cur = ht->table[hash]; cur != NULL && e == NULL; cur=cur->next) { if((index_len == cur->index_len) && memcmp(cur->index, index, index_len) == 0) { e = cur; } } if(e != NULL) { /* We found the index, so we're going to overwrite the data. * We also need to reposition the element to the newest position, * so remove it from the list for now. */ if(ht->talloc_data) talloc_free(e->data); if(e->newer == NULL) ht->newest = e->older; else e->newer->older = e->older; if(e->older == NULL) ht->oldest = e->newer; else e->older->newer = e->newer; } else { /* We didn't find an identical index. */ if((ht->max_keys != 0) && (ht->num_keys >= ht->max_keys)) { /* Eliminate the least recently used item, but reuse the element * structure to minimize reallocation. */ e = ht->oldest; if(ht->newest == ht->oldest) { ht->newest = NULL; ht->oldest = NULL; } else { ht->oldest = e->newer; e->newer->older = NULL; } e->newer = NULL; e->older = NULL; last = NULL; lru_hash = lru_cache_compute_hash(ht->num_buckets, ht->secret, e->index, e->index_len); for(cur = ht->table[lru_hash]; cur != e && cur != NULL; last=cur, cur=cur->next) { continue; } if(last == NULL) ht->table[lru_hash] = e->next; else last->next = e->next; e->next = NULL; if(ht->talloc_data) talloc_free(e->data); tmp_index = talloc_realloc_size(e, e->index, index_len); if(tmp_index == NULL) { talloc_free(e); return false; } else e->index = tmp_index; } else { /* Brand new element because we have room to spare. */ e = talloc(ht->table, lru_cache_element); if(e == NULL) return false; e->index = talloc_size(e, index_len); if(e->index == NULL) { talloc_free(e); return false; } /* New entry, increment counters. */ ht->num_keys++; } memcpy(e->index, index, index_len); e->index_len = index_len; /* Insert at beginning of chain, in a vaguely LRU style */ e->next = ht->table[hash]; ht->table[hash] = e; } e->data = data; if(ht->talloc_data) talloc_steal(e, e->data); /* Finally, let's insert the element to the newest position in the LRU list.*/ if(ht->newest != NULL) ht->newest->newer = e; e->newer = NULL; e->older = ht->newest; ht->newest = e; if(ht->oldest == NULL) ht->oldest = e; return true; } void* lru_cache_find(lru_cache* ht, const void* index, uint32_t index_len) { uint32_t hash; lru_cache_element* cur; hash = lru_cache_compute_hash(ht->num_buckets, ht->secret, index, index_len); for(cur = ht->table[hash]; (cur != NULL); cur = cur->next) { if((index_len == cur->index_len) && memcmp(cur->index, index, index_len) == 0) { break; } } if(cur != NULL && cur->newer != NULL) { /* Need to move this element up to the newest slot. */ cur->newer->older = cur->older; if(cur->older == NULL) ht->oldest = cur->newer; else cur->older->newer = cur->newer; cur->newer = NULL; cur->older = ht->newest; ht->newest->newer = cur; ht->newest = cur; } if(cur != NULL) return cur->data; else return NULL; } bool lru_cache_remove(lru_cache* ht, const void* index, uint32_t index_len) { uint32_t hash; lru_cache_element* cur; lru_cache_element* last = NULL; hash = lru_cache_compute_hash(ht->num_buckets, ht->secret, index, index_len); for(cur=ht->table[hash]; (cur != NULL); last=cur, cur=cur->next) { if((index_len == cur->index_len) && memcmp(cur->index, index, index_len) == 0) { break; } } if(cur == NULL) return false; /* Detach from list */ if(cur->newer == NULL) ht->newest = cur->older; else cur->newer->older = cur->older; if(cur->older == NULL) ht->oldest = cur->newer; else cur->older->newer = cur->newer; /* Detach from hash table */ if(last == NULL) ht->table[hash] = cur->next; else last->next = cur->next; talloc_free(cur); /* Removing entry, decrement counters. */ ht->num_keys--; return true; } reglookup-0.12.0/lib/range_list.c000066400000000000000000000174511135112127200166450ustar00rootroot00000000000000/* * Copyright (C) 2008-2010 Timothy D. Morgan * * 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; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: range_list.c 169 2010-03-03 19:24:58Z tim $ */ /** * @file */ #include "range_list.h" /*******************/ /* Private symbols */ /*******************/ #define RANGE_LIST_ALLOC_SIZE 256 #if 0 /* For debugging */ #include static void range_list_print(const range_list* rl) { uint32_t i; for(i=0; isize; i++) fprintf(stderr, " %d=%p,%d,%d,%p", i, (void*)rl->elements[i], rl->elements[i]->offset, rl->elements[i]->length, rl->elements[i]->data); fprintf(stderr, "\n"); } #endif /* * Inserts elem into rl at the specified index and updates rl->size. * Memory reallocation of rl->elements is handled when necessary, and * rl->elem_alloced is updated in this case.. Returns false if memory * could not be allocated. */ static bool range_list_insert(range_list* rl, range_list_element* elem, uint32_t index) { uint32_t i; range_list_element** tmp; if(rl->size == rl->elem_alloced) { tmp = talloc_realloc(rl, rl->elements, range_list_element*, (rl->elem_alloced+RANGE_LIST_ALLOC_SIZE)); if(tmp == NULL) return false; rl->elements = tmp; rl->elem_alloced += RANGE_LIST_ALLOC_SIZE; } /* Do the shuffle to the right. */ for(i=rl->size; i > index; i--) rl->elements[i] = rl->elements[i-1]; rl->elements[index] = elem; rl->size++; return true; } /* * Finds the element with the closest offset to that provided, such that * the element's offset <= the provided offset. If no such element * exists, this returns -1 which indicates that the provided offset * appears before all elements. */ static int32_t range_list_find_previous(const range_list* rl, uint32_t offset) { uint32_t h_idx, l_idx, cur_idx; uint32_t h_val, l_val; range_list_element* cur_elem; if((rl->size == 0) || (offset < rl->elements[0]->offset)) return -1; if(offset >= rl->elements[rl->size-1]->offset) return rl->size-1; h_idx = rl->size-1; l_idx = 0; while(h_idx != l_idx) { h_val = rl->elements[h_idx]->offset + rl->elements[h_idx]->length; l_val = rl->elements[l_idx]->offset; /* Make an educated guess as to the "middle" index based on the * ratios of the offset and high/low values. */ cur_idx = (uint32_t)ceil((((double)offset-l_val)/(h_val-l_val))*(h_idx-l_idx)); if(cur_idx > h_idx) cur_idx = h_idx; if(cur_idx < l_idx) cur_idx = l_idx; cur_elem = rl->elements[cur_idx]; if((offset >= cur_elem->offset) && (offset < rl->elements[cur_idx+1]->offset)) return cur_idx; if(offset < cur_elem->offset) h_idx = cur_idx-1; else l_idx = cur_idx+1; } return h_idx; } /******************/ /* Public symbols */ /******************/ range_list* range_list_new() { range_list* rl; rl = talloc(NULL, range_list); if(rl == NULL) return NULL; rl->elements = talloc_array(rl, range_list_element*, RANGE_LIST_ALLOC_SIZE); if(rl->elements == NULL) { talloc_free(rl); return NULL; } rl->elem_alloced = RANGE_LIST_ALLOC_SIZE; rl->size = 0; return rl; } void range_list_free(range_list* rl) { if(rl != NULL) talloc_free(rl); } uint32_t range_list_size(const range_list* rl) { return rl->size; } bool range_list_add(range_list* rl, uint32_t offset, uint32_t length, void* data) { uint32_t insert_index; range_list_element* elem; range_list_element* prev_elem; /*fprintf(stderr, "DEBUG: rl->size=%d\n", rl->size);*/ /* Sorry, limited to 2**31-1 elements. */ if(rl->size >= 0x7FFFFFFF) return false; /* 0-length ranges aren't allowed. */ if(length == 0) return false; /* Check for integer overflows */ if((uint32_t)(offset+length) < offset || (uint32_t)(offset+length) < length) return false; /* Find insertion point and validate there are no overlaps */ insert_index = range_list_find_previous(rl, offset)+1; /* Does the previous element overlap with this one? */ if(insert_index > 0) { prev_elem = rl->elements[insert_index-1]; if(offset < prev_elem->length + prev_elem->offset) return false; } /* Does this new element overlap with the next one? */ if((insert_index+1 < rl->size) && (offset+length > rl->elements[insert_index+1]->offset)) return false; elem = talloc(rl->elements, range_list_element); if(elem == NULL) return false; elem->offset = offset; elem->length = length; elem->data = data; if(!range_list_insert(rl, elem, insert_index)) { talloc_free(elem); return false; } return true; } bool range_list_remove(range_list* rl, uint32_t index) { uint32_t i; range_list_element** tmp; if(index >= rl->size) return false; talloc_free(rl->elements[index]); /* Do the shuffle to the left. */ for(i=index; i < (rl->size-1); i++) rl->elements[i] = rl->elements[i+1]; rl->elements[rl->size-1] = NULL; rl->size--; /* Try to keep memory usage down */ if(rl->size + 2 * RANGE_LIST_ALLOC_SIZE < rl->elem_alloced) { tmp = talloc_realloc(rl, rl->elements, range_list_element*, (rl->elem_alloced-2*RANGE_LIST_ALLOC_SIZE)); if(tmp != NULL) { rl->elements = tmp; rl->elem_alloced -= 2*RANGE_LIST_ALLOC_SIZE; } } return true; } const range_list_element* range_list_get(const range_list* rl, uint32_t index) { if(index >= rl->size) return NULL; return rl->elements[index]; } int32_t range_list_find(const range_list* rl, uint32_t offset) { uint32_t prev_idx; range_list_element* elem; if(rl->size == 0) return -1; if((offset < rl->elements[0]->offset) || (offset > rl->elements[rl->size-1]->offset + rl->elements[rl->size-1]->length)) return -2; prev_idx = range_list_find_previous(rl, offset); elem = rl->elements[prev_idx]; if(offset < elem->offset+elem->length) return prev_idx; return -3; } void* range_list_find_data(const range_list* rl, uint32_t offset) { int32_t index = range_list_find(rl, offset); if(index < 0) return NULL; return rl->elements[index]->data; } bool range_list_split_element(range_list* rl, uint32_t index, uint32_t offset) { range_list_element* cur_elem; range_list_element* new_elem; if(index >= rl->size) return false; cur_elem = rl->elements[index]; if((offset <= cur_elem->offset) || (offset >= cur_elem->offset+cur_elem->length)) return false; new_elem = talloc(rl->elements, range_list_element); if(new_elem == NULL) return false; new_elem->offset = offset; new_elem->length = cur_elem->offset + cur_elem->length - offset; new_elem->data = cur_elem->data; if(!range_list_insert(rl, new_elem, index+1)) { talloc_free(new_elem); return false; } cur_elem->length = new_elem->offset - cur_elem->offset; return true; } bool range_list_has_range(range_list* rl, uint32_t start, uint32_t length) { int32_t idx1, idx2; idx1 = range_list_find(rl, start); if(idx1 < 0) return false; idx2 = range_list_find(rl, start+length); if(idx2 < 0) return false; if(idx1 == idx2) return true; while(idx1 != idx2) { if(rl->elements[idx1]->offset + rl->elements[idx1]->length != rl->elements[idx1+1]->offset) return false; idx1++; } return true; } reglookup-0.12.0/lib/regfi.c000066400000000000000000002632601135112127200156130ustar00rootroot00000000000000/* * Copyright (C) 2005-2010 Timothy D. Morgan * Copyright (C) 2005 Gerald (Jerry) Carter * * 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; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: regfi.c 173 2010-03-08 03:39:09Z tim $ */ /** * @file * * Windows NT (and later) read-only registry library * * See @ref regfi.h for more information. * * Branched from Samba project Subversion repository, version #7470: * http://viewcvs.samba.org/cgi-bin/viewcvs.cgi/trunk/source/registry/regfio.c?rev=7470&view=auto * * Since then, it has been heavily rewritten, simplified, and improved. */ #include "regfi.h" /* Registry types mapping */ const unsigned int regfi_num_reg_types = 12; static const char* regfi_type_names[] = {"NONE", "SZ", "EXPAND_SZ", "BINARY", "DWORD", "DWORD_BE", "LINK", "MULTI_SZ", "RSRC_LIST", "RSRC_DESC", "RSRC_REQ_LIST", "QWORD"}; const char* regfi_encoding_names[] = {"US-ASCII//TRANSLIT", "UTF-8//TRANSLIT", "UTF-16LE//TRANSLIT"}; /****************************************************************************** ******************************************************************************/ void regfi_add_message(REGFI_FILE* file, uint16_t msg_type, const char* fmt, ...) { /* XXX: This function is not particularly efficient, * but then it is mostly used during errors. */ uint32_t buf_size, buf_used; char* new_msg; va_list args; if((file->msg_mask & msg_type) != 0) { if(file->last_message == NULL) buf_used = 0; else buf_used = strlen(file->last_message); buf_size = buf_used+strlen(fmt)+160; new_msg = realloc(file->last_message, buf_size); if(new_msg == NULL) /* XXX: should we report this? */ return; switch (msg_type) { case REGFI_MSG_INFO: strcpy(new_msg+buf_used, "INFO: "); buf_used += 6; break; case REGFI_MSG_WARN: strcpy(new_msg+buf_used, "WARN: "); buf_used += 6; break; case REGFI_MSG_ERROR: strcpy(new_msg+buf_used, "ERROR: "); buf_used += 7; break; } va_start(args, fmt); vsnprintf(new_msg+buf_used, buf_size-buf_used, fmt, args); va_end(args); strncat(new_msg, "\n", buf_size-1); file->last_message = new_msg; } } /****************************************************************************** ******************************************************************************/ char* regfi_get_messages(REGFI_FILE* file) { char* ret_val = file->last_message; file->last_message = NULL; return ret_val; } void regfi_set_message_mask(REGFI_FILE* file, uint16_t mask) { file->msg_mask = mask; } /****************************************************************************** * Returns NULL for an invalid e *****************************************************************************/ static const char* regfi_encoding_int2str(REGFI_ENCODING e) { if(e < REGFI_NUM_ENCODINGS) return regfi_encoding_names[e]; return NULL; } /****************************************************************************** * Returns NULL for an invalid val *****************************************************************************/ const char* regfi_type_val2str(unsigned int val) { if(val == REG_KEY) return "KEY"; if(val >= regfi_num_reg_types) return NULL; return regfi_type_names[val]; } /****************************************************************************** * Returns -1 on error *****************************************************************************/ int regfi_type_str2val(const char* str) { int i; if(strcmp("KEY", str) == 0) return REG_KEY; for(i=0; i < regfi_num_reg_types; i++) if (strcmp(regfi_type_names[i], str) == 0) return i; if(strcmp("DWORD_LE", str) == 0) return REG_DWORD_LE; return -1; } /* Security descriptor formatting functions */ const char* regfi_ace_type2str(uint8_t type) { static const char* map[7] = {"ALLOW", "DENY", "AUDIT", "ALARM", "ALLOW CPD", "OBJ ALLOW", "OBJ DENY"}; if(type < 7) return map[type]; else /* XXX: would be nice to return the unknown integer value. * However, as it is a const string, it can't be free()ed later on, * so that would need to change. */ return "UNKNOWN"; } /* XXX: need a better reference on the meaning of each flag. */ /* For more info, see: * http://msdn2.microsoft.com/en-us/library/aa772242.aspx */ char* regfi_ace_flags2str(uint8_t flags) { static const char* flag_map[32] = { "OI", /* Object Inherit */ "CI", /* Container Inherit */ "NP", /* Non-Propagate */ "IO", /* Inherit Only */ "IA", /* Inherited ACE */ NULL, NULL, NULL, }; char* ret_val = malloc(35*sizeof(char)); char* fo = ret_val; uint32_t i; uint8_t f; if(ret_val == NULL) return NULL; fo[0] = '\0'; if (!flags) return ret_val; for(i=0; i < 8; i++) { f = (1<num_auths; char* ret_val = malloc(size); if(ret_val == NULL) return NULL; if(comps > WINSEC_MAX_SUBAUTHS) comps = WINSEC_MAX_SUBAUTHS; left -= sprintf(ret_val, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]); for (i = 0; i < comps; i++) left -= snprintf(ret_val+(size-left), left, "-%u", sid->sub_auths[i]); return ret_val; } char* regfi_get_acl(WINSEC_ACL* acl) { uint32_t i, extra, size = 0; const char* type_str; char* flags_str; char* perms_str; char* sid_str; char* ace_delim = ""; char* ret_val = NULL; char* tmp_val = NULL; bool failed = false; char field_delim = ':'; for (i = 0; i < acl->num_aces && !failed; i++) { sid_str = regfi_sid2str(acl->aces[i]->trustee); type_str = regfi_ace_type2str(acl->aces[i]->type); perms_str = regfi_ace_perms2str(acl->aces[i]->access_mask); flags_str = regfi_ace_flags2str(acl->aces[i]->flags); if(flags_str != NULL && perms_str != NULL && type_str != NULL && sid_str != NULL) { /* XXX: this is slow */ extra = strlen(sid_str) + strlen(type_str) + strlen(perms_str) + strlen(flags_str) + 5; tmp_val = realloc(ret_val, size+extra); if(tmp_val == NULL) { free(ret_val); ret_val = NULL; failed = true; } else { ret_val = tmp_val; size += sprintf(ret_val+size, "%s%s%c%s%c%s%c%s", ace_delim,sid_str, field_delim,type_str, field_delim,perms_str, field_delim,flags_str); ace_delim = "|"; } } else failed = true; if(sid_str != NULL) free(sid_str); if(sid_str != NULL) free(perms_str); if(sid_str != NULL) free(flags_str); } return ret_val; } char* regfi_get_sacl(WINSEC_DESC *sec_desc) { if (sec_desc->sacl) return regfi_get_acl(sec_desc->sacl); else return NULL; } char* regfi_get_dacl(WINSEC_DESC *sec_desc) { if (sec_desc->dacl) return regfi_get_acl(sec_desc->dacl); else return NULL; } char* regfi_get_owner(WINSEC_DESC *sec_desc) { return regfi_sid2str(sec_desc->owner_sid); } char* regfi_get_group(WINSEC_DESC *sec_desc) { return regfi_sid2str(sec_desc->grp_sid); } /***************************************************************************** * This function is just like read(2), except that it continues to * re-try reading from the file descriptor if EINTR or EAGAIN is received. * regfi_read will attempt to read length bytes from fd and write them to buf. * * On success, 0 is returned. Upon failure, an errno code is returned. * * The number of bytes successfully read is returned through the length * parameter by reference. If both the return value and length parameter are * returned as 0, then EOF was encountered immediately *****************************************************************************/ uint32_t regfi_read(int fd, uint8_t* buf, uint32_t* length) { uint32_t rsize = 0; uint32_t rret = 0; do { rret = read(fd, buf + rsize, *length - rsize); if(rret > 0) rsize += rret; }while(*length - rsize > 0 && (rret > 0 || (rret == -1 && (errno == EAGAIN || errno == EINTR)))); *length = rsize; if (rret == -1 && errno != EINTR && errno != EAGAIN) return errno; return 0; } /***************************************************************************** * *****************************************************************************/ bool regfi_parse_cell(int fd, uint32_t offset, uint8_t* hdr, uint32_t hdr_len, uint32_t* cell_length, bool* unalloc) { uint32_t length; int32_t raw_length; uint8_t tmp[4]; if(lseek(fd, offset, SEEK_SET) == -1) return false; length = 4; if((regfi_read(fd, tmp, &length) != 0) || length != 4) return false; raw_length = IVALS(tmp, 0); if(raw_length < 0) { (*cell_length) = raw_length*(-1); (*unalloc) = false; } else { (*cell_length) = raw_length; (*unalloc) = true; } if(*cell_length - 4 < hdr_len) return false; if(hdr_len > 0) { length = hdr_len; if((regfi_read(fd, hdr, &length) != 0) || length != hdr_len) return false; } return true; } /****************************************************************************** * Given an offset and an hbin, is the offset within that hbin? * The offset is a virtual file offset. ******************************************************************************/ static bool regfi_offset_in_hbin(const REGFI_HBIN* hbin, uint32_t voffset) { if(!hbin) return false; if((voffset > hbin->first_hbin_off) && (voffset < (hbin->first_hbin_off + hbin->block_size))) return true; return false; } /****************************************************************************** * Provide a physical offset and receive the correpsonding HBIN * block for it. NULL if one doesn't exist. ******************************************************************************/ const REGFI_HBIN* regfi_lookup_hbin(REGFI_FILE* file, uint32_t offset) { return (const REGFI_HBIN*)range_list_find_data(file->hbins, offset); } /****************************************************************************** * Calculate the largest possible cell size given a physical offset. * Largest size is based on the HBIN the offset is currently a member of. * Returns negative values on error. * (Since cells can only be ~2^31 in size, this works out.) ******************************************************************************/ int32_t regfi_calc_maxsize(REGFI_FILE* file, uint32_t offset) { const REGFI_HBIN* hbin = regfi_lookup_hbin(file, offset); if(hbin == NULL) return -1; return (hbin->block_size + hbin->file_off) - offset; } /****************************************************************************** ******************************************************************************/ REGFI_SUBKEY_LIST* regfi_load_subkeylist(REGFI_FILE* file, uint32_t offset, uint32_t num_keys, uint32_t max_size, bool strict) { REGFI_SUBKEY_LIST* ret_val; ret_val = regfi_load_subkeylist_aux(file, offset, max_size, strict, REGFI_MAX_SUBKEY_DEPTH); if(ret_val == NULL) { regfi_add_message(file, REGFI_MSG_WARN, "Failed to load subkey list at" " offset 0x%.8X.", offset); return NULL; } if(num_keys != ret_val->num_keys) { /* Not sure which should be authoritative, the number from the * NK record, or the number in the subkey list. Just emit a warning for * now if they don't match. */ regfi_add_message(file, REGFI_MSG_WARN, "Number of subkeys listed in parent" " (%d) did not match number found in subkey list/tree (%d)" " while parsing subkey list/tree at offset 0x%.8X.", num_keys, ret_val->num_keys, offset); } return ret_val; } /****************************************************************************** ******************************************************************************/ REGFI_SUBKEY_LIST* regfi_load_subkeylist_aux(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict, uint8_t depth_left) { REGFI_SUBKEY_LIST* ret_val; REGFI_SUBKEY_LIST** sublists; uint32_t i, num_sublists, off; int32_t sublist_maxsize; if(depth_left == 0) { regfi_add_message(file, REGFI_MSG_WARN, "Maximum depth reached" " while parsing subkey list/tree at offset 0x%.8X.", offset); return NULL; } ret_val = regfi_parse_subkeylist(file, offset, max_size, strict); if(ret_val == NULL) return NULL; if(ret_val->recursive_type) { num_sublists = ret_val->num_children; sublists = (REGFI_SUBKEY_LIST**)malloc(num_sublists * sizeof(REGFI_SUBKEY_LIST*)); for(i=0; i < num_sublists; i++) { off = ret_val->elements[i].offset + REGFI_REGF_SIZE; sublist_maxsize = regfi_calc_maxsize(file, off); if(sublist_maxsize < 0) sublists[i] = NULL; else sublists[i] = regfi_load_subkeylist_aux(file, off, sublist_maxsize, strict, depth_left-1); } talloc_free(ret_val); return regfi_merge_subkeylists(num_sublists, sublists, strict); } return ret_val; } /****************************************************************************** ******************************************************************************/ REGFI_SUBKEY_LIST* regfi_parse_subkeylist(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict) { REGFI_SUBKEY_LIST* ret_val; uint32_t i, cell_length, length, elem_size, read_len; uint8_t* elements = NULL; uint8_t buf[REGFI_SUBKEY_LIST_MIN_LEN]; bool unalloc; bool recursive_type; if(!regfi_parse_cell(file->fd, offset, buf, REGFI_SUBKEY_LIST_MIN_LEN, &cell_length, &unalloc)) { regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while " "parsing subkey-list at offset 0x%.8X.", offset); return NULL; } if(cell_length > max_size) { regfi_add_message(file, REGFI_MSG_WARN, "Cell size longer than max_size" " while parsing subkey-list at offset 0x%.8X.", offset); if(strict) return NULL; cell_length = max_size & 0xFFFFFFF8; } recursive_type = false; if(buf[0] == 'r' && buf[1] == 'i') { recursive_type = true; elem_size = sizeof(uint32_t); } else if(buf[0] == 'l' && buf[1] == 'i') elem_size = sizeof(uint32_t); else if((buf[0] == 'l') && (buf[1] == 'f' || buf[1] == 'h')) elem_size = sizeof(REGFI_SUBKEY_LIST_ELEM); else { regfi_add_message(file, REGFI_MSG_ERROR, "Unknown magic number" " (0x%.2X, 0x%.2X) encountered while parsing" " subkey-list at offset 0x%.8X.", buf[0], buf[1], offset); return NULL; } ret_val = talloc(NULL, REGFI_SUBKEY_LIST); if(ret_val == NULL) return NULL; ret_val->offset = offset; ret_val->cell_size = cell_length; ret_val->magic[0] = buf[0]; ret_val->magic[1] = buf[1]; ret_val->recursive_type = recursive_type; ret_val->num_children = SVAL(buf, 0x2); if(!recursive_type) ret_val->num_keys = ret_val->num_children; length = elem_size*ret_val->num_children; if(cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t) < length) { regfi_add_message(file, REGFI_MSG_WARN, "Number of elements too large for" " cell while parsing subkey-list at offset 0x%.8X.", offset); if(strict) goto fail; length = cell_length - REGFI_SUBKEY_LIST_MIN_LEN - sizeof(uint32_t); } ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM, ret_val->num_children); if(ret_val->elements == NULL) goto fail; elements = (uint8_t*)malloc(length); if(elements == NULL) goto fail; read_len = length; if(regfi_read(file->fd, elements, &read_len) != 0 || read_len != length) goto fail; if(elem_size == sizeof(uint32_t)) { for (i=0; i < ret_val->num_children; i++) { ret_val->elements[i].offset = IVAL(elements, i*elem_size); ret_val->elements[i].hash = 0; } } else { for (i=0; i < ret_val->num_children; i++) { ret_val->elements[i].offset = IVAL(elements, i*elem_size); ret_val->elements[i].hash = IVAL(elements, i*elem_size+4); } } free(elements); return ret_val; fail: if(elements != NULL) free(elements); talloc_free(ret_val); return NULL; } /******************************************************************* *******************************************************************/ REGFI_SUBKEY_LIST* regfi_merge_subkeylists(uint16_t num_lists, REGFI_SUBKEY_LIST** lists, bool strict) { uint32_t i,j,k; REGFI_SUBKEY_LIST* ret_val; if(lists == NULL) return NULL; ret_val = talloc(NULL, REGFI_SUBKEY_LIST); if(ret_val == NULL) return NULL; /* Obtain total number of elements */ ret_val->num_keys = 0; for(i=0; i < num_lists; i++) { if(lists[i] != NULL) ret_val->num_keys += lists[i]->num_children; } ret_val->num_children = ret_val->num_keys; if(ret_val->num_keys > 0) { ret_val->elements = talloc_array(ret_val, REGFI_SUBKEY_LIST_ELEM, ret_val->num_keys); k=0; if(ret_val->elements != NULL) { for(i=0; i < num_lists; i++) { if(lists[i] != NULL) { for(j=0; j < lists[i]->num_keys; j++) { ret_val->elements[k].hash = lists[i]->elements[j].hash; ret_val->elements[k++].offset = lists[i]->elements[j].offset; } } } } } for(i=0; i < num_lists; i++) regfi_subkeylist_free(lists[i]); free(lists); return ret_val; } /****************************************************************************** * ******************************************************************************/ REGFI_SK_REC* regfi_parse_sk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict) { REGFI_SK_REC* ret_val; uint8_t* sec_desc_buf = NULL; uint32_t cell_length, length; uint8_t sk_header[REGFI_SK_MIN_LENGTH]; bool unalloc = false; if(!regfi_parse_cell(file->fd, offset, sk_header, REGFI_SK_MIN_LENGTH, &cell_length, &unalloc)) { regfi_add_message(file, REGFI_MSG_WARN, "Could not parse SK record cell" " at offset 0x%.8X.", offset); return NULL; } if(sk_header[0] != 's' || sk_header[1] != 'k') { regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing" " SK record at offset 0x%.8X.", offset); return NULL; } ret_val = talloc(NULL, REGFI_SK_REC); if(ret_val == NULL) return NULL; ret_val->offset = offset; /* XXX: Is there a way to be more conservative (shorter) with * cell length when cell is unallocated? */ ret_val->cell_size = cell_length; if(ret_val->cell_size > max_size) ret_val->cell_size = max_size & 0xFFFFFFF8; if((ret_val->cell_size < REGFI_SK_MIN_LENGTH) || (strict && (ret_val->cell_size & 0x00000007) != 0)) { regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size found while" " parsing SK record at offset 0x%.8X.", offset); goto fail; } ret_val->magic[0] = sk_header[0]; ret_val->magic[1] = sk_header[1]; ret_val->unknown_tag = SVAL(sk_header, 0x2); ret_val->prev_sk_off = IVAL(sk_header, 0x4); ret_val->next_sk_off = IVAL(sk_header, 0x8); ret_val->ref_count = IVAL(sk_header, 0xC); ret_val->desc_size = IVAL(sk_header, 0x10); if((ret_val->prev_sk_off & 0x00000007) != 0 || (ret_val->next_sk_off & 0x00000007) != 0) { regfi_add_message(file, REGFI_MSG_WARN, "SK record's next/previous offsets" " are not a multiple of 8 while parsing SK record at" " offset 0x%.8X.", offset); goto fail; } if(ret_val->desc_size + REGFI_SK_MIN_LENGTH > ret_val->cell_size) { regfi_add_message(file, REGFI_MSG_WARN, "Security descriptor too large for" " cell while parsing SK record at offset 0x%.8X.", offset); goto fail; } sec_desc_buf = (uint8_t*)malloc(ret_val->desc_size); if(sec_desc_buf == NULL) goto fail; length = ret_val->desc_size; if(regfi_read(file->fd, sec_desc_buf, &length) != 0 || length != ret_val->desc_size) { regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read security" " descriptor while parsing SK record at offset 0x%.8X.", offset); goto fail; } if(!(ret_val->sec_desc = winsec_parse_desc(ret_val, sec_desc_buf, ret_val->desc_size))) { regfi_add_message(file, REGFI_MSG_ERROR, "Failed to parse security" " descriptor while parsing SK record at offset 0x%.8X.", offset); goto fail; } free(sec_desc_buf); return ret_val; fail: if(sec_desc_buf != NULL) free(sec_desc_buf); talloc_free(ret_val); return NULL; } REGFI_VALUE_LIST* regfi_parse_valuelist(REGFI_FILE* file, uint32_t offset, uint32_t num_values, bool strict) { REGFI_VALUE_LIST* ret_val; uint32_t i, cell_length, length, read_len; bool unalloc; if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc)) { regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read cell header" " while parsing value list at offset 0x%.8X.", offset); return NULL; } if((cell_length & 0x00000007) != 0) { regfi_add_message(file, REGFI_MSG_WARN, "Cell length not a multiple of 8" " while parsing value list at offset 0x%.8X.", offset); if(strict) return NULL; cell_length = cell_length & 0xFFFFFFF8; } if((num_values * sizeof(uint32_t)) > cell_length-sizeof(uint32_t)) { regfi_add_message(file, REGFI_MSG_WARN, "Too many values found" " while parsing value list at offset 0x%.8X.", offset); if(strict) return NULL; num_values = cell_length/sizeof(uint32_t) - sizeof(uint32_t); } read_len = num_values*sizeof(uint32_t); ret_val = talloc(NULL, REGFI_VALUE_LIST); if(ret_val == NULL) return NULL; ret_val->elements = (REGFI_VALUE_LIST_ELEM*)talloc_size(ret_val, read_len); if(ret_val->elements == NULL) { talloc_free(ret_val); return NULL; } ret_val->num_values = num_values; length = read_len; if((regfi_read(file->fd, (uint8_t*)ret_val->elements, &length) != 0) || length != read_len) { regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read value pointers" " while parsing value list at offset 0x%.8X.", offset); talloc_free(ret_val); return NULL; } for(i=0; i < num_values; i++) { /* Fix endianness */ ret_val->elements[i] = IVAL(&ret_val->elements[i], 0); /* Validate the first num_values values to ensure they make sense */ if(strict) { /* XXX: Need to revisit this file length check when we start dealing * with partial files. */ if((ret_val->elements[i] + REGFI_REGF_SIZE > file->file_length) || ((ret_val->elements[i] & 0x00000007) != 0)) { regfi_add_message(file, REGFI_MSG_WARN, "Invalid value pointer" " (0x%.8X) found while parsing value list at offset" " 0x%.8X.", ret_val->elements[i], offset); talloc_free(ret_val); return NULL; } } } return ret_val; } void regfi_interpret_valuename(REGFI_FILE* file, REGFI_VK_REC* vk, REGFI_ENCODING output_encoding, bool strict) { /* XXX: Registry value names are supposedly limited to 16383 characters * according to: * http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx * Might want to emit a warning if this is exceeded. * It is expected that "characters" could be variable width. * Also, it may be useful to use this information to limit false positives * when recovering deleted VK records. */ int32_t tmp_size; REGFI_ENCODING from_encoding = (vk->flags & REGFI_VK_FLAG_ASCIINAME) ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE; if(from_encoding == output_encoding) { vk->valuename_raw = talloc_realloc(vk, vk->valuename_raw, uint8_t, vk->name_length+1); vk->valuename_raw[vk->name_length] = '\0'; vk->valuename = (char*)vk->valuename_raw; } else { vk->valuename = talloc_array(vk, char, vk->name_length+1); if(vk->valuename == NULL) { regfi_free_value(vk); return; } tmp_size = regfi_conv_charset(regfi_encoding_int2str(from_encoding), regfi_encoding_int2str(output_encoding), vk->valuename_raw, vk->valuename, vk->name_length, vk->name_length+1); if(tmp_size < 0) { regfi_add_message(file, REGFI_MSG_WARN, "Error occurred while converting" " valuename to encoding %s. Error message: %s", regfi_encoding_int2str(output_encoding), strerror(-tmp_size)); talloc_free(vk->valuename); vk->valuename = NULL; } } } /****************************************************************************** ******************************************************************************/ REGFI_VK_REC* regfi_load_value(REGFI_FILE* file, uint32_t offset, REGFI_ENCODING output_encoding, bool strict) { REGFI_VK_REC* ret_val = NULL; int32_t max_size; max_size = regfi_calc_maxsize(file, offset); if(max_size < 0) return NULL; ret_val = regfi_parse_vk(file, offset, max_size, strict); if(ret_val == NULL) return NULL; regfi_interpret_valuename(file, ret_val, output_encoding, strict); return ret_val; } /****************************************************************************** * If !strict, the list may contain NULLs, VK records may point to NULL. ******************************************************************************/ REGFI_VALUE_LIST* regfi_load_valuelist(REGFI_FILE* file, uint32_t offset, uint32_t num_values, uint32_t max_size, bool strict) { uint32_t usable_num_values; if((num_values+1) * sizeof(uint32_t) > max_size) { regfi_add_message(file, REGFI_MSG_WARN, "Number of values indicated by" " parent key (%d) would cause cell to straddle HBIN" " boundary while loading value list at offset" " 0x%.8X.", num_values, offset); if(strict) return NULL; usable_num_values = max_size/sizeof(uint32_t) - sizeof(uint32_t); } else usable_num_values = num_values; return regfi_parse_valuelist(file, offset, usable_num_values, strict); } void regfi_interpret_keyname(REGFI_FILE* file, REGFI_NK_REC* nk, REGFI_ENCODING output_encoding, bool strict) { /* XXX: Registry key names are supposedly limited to 255 characters according to: * http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx * Might want to emit a warning if this is exceeded. * It is expected that "characters" could be variable width. * Also, it may be useful to use this information to limit false positives * when recovering deleted NK records. */ int32_t tmp_size; REGFI_ENCODING from_encoding = (nk->flags & REGFI_NK_FLAG_ASCIINAME) ? REGFI_ENCODING_ASCII : REGFI_ENCODING_UTF16LE; if(from_encoding == output_encoding) { nk->keyname_raw = talloc_realloc(nk, nk->keyname_raw, uint8_t, nk->name_length+1); nk->keyname_raw[nk->name_length] = '\0'; nk->keyname = (char*)nk->keyname_raw; } else { nk->keyname = talloc_array(nk, char, nk->name_length+1); if(nk->keyname == NULL) { regfi_free_key(nk); return; } tmp_size = regfi_conv_charset(regfi_encoding_int2str(from_encoding), regfi_encoding_int2str(output_encoding), nk->keyname_raw, nk->keyname, nk->name_length, nk->name_length+1); if(tmp_size < 0) { regfi_add_message(file, REGFI_MSG_WARN, "Error occurred while converting" " keyname to encoding %s. Error message: %s", regfi_encoding_int2str(output_encoding), strerror(-tmp_size)); talloc_free(nk->keyname); nk->keyname = NULL; } } } /****************************************************************************** * ******************************************************************************/ REGFI_NK_REC* regfi_load_key(REGFI_FILE* file, uint32_t offset, REGFI_ENCODING output_encoding, bool strict) { REGFI_NK_REC* nk; uint32_t off; int32_t max_size; max_size = regfi_calc_maxsize(file, offset); if (max_size < 0) return NULL; /* get the initial nk record */ if((nk = regfi_parse_nk(file, offset, max_size, true)) == NULL) { regfi_add_message(file, REGFI_MSG_ERROR, "Could not load NK record at" " offset 0x%.8X.", offset); return NULL; } regfi_interpret_keyname(file, nk, output_encoding, strict); /* get value list */ if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE)) { off = nk->values_off + REGFI_REGF_SIZE; max_size = regfi_calc_maxsize(file, off); if(max_size < 0) { if(strict) { regfi_free_key(nk); return NULL; } else nk->values = NULL; } else { nk->values = regfi_load_valuelist(file, off, nk->num_values, max_size, true); if(nk->values == NULL) { regfi_add_message(file, REGFI_MSG_WARN, "Could not load value list" " for NK record at offset 0x%.8X.", offset); if(strict) { regfi_free_key(nk); return NULL; } } talloc_steal(nk, nk->values); } } /* now get subkey list */ if(nk->num_subkeys && (nk->subkeys_off != REGFI_OFFSET_NONE)) { off = nk->subkeys_off + REGFI_REGF_SIZE; max_size = regfi_calc_maxsize(file, off); if(max_size < 0) { if(strict) { regfi_free_key(nk); return NULL; } else nk->subkeys = NULL; } else { nk->subkeys = regfi_load_subkeylist(file, off, nk->num_subkeys, max_size, true); if(nk->subkeys == NULL) { regfi_add_message(file, REGFI_MSG_WARN, "Could not load subkey list" " while parsing NK record at offset 0x%.8X.", offset); nk->num_subkeys = 0; } talloc_steal(nk, nk->subkeys); } } return nk; } /****************************************************************************** ******************************************************************************/ const REGFI_SK_REC* regfi_load_sk(REGFI_FILE* file, uint32_t offset, bool strict) { REGFI_SK_REC* ret_val = NULL; int32_t max_size; void* failure_ptr = NULL; /* First look if we have already parsed it */ ret_val = (REGFI_SK_REC*)lru_cache_find(file->sk_cache, &offset, 4); /* Bail out if we have previously cached a parse failure at this offset. */ if(ret_val == (void*)REGFI_OFFSET_NONE) return NULL; if(ret_val == NULL) { max_size = regfi_calc_maxsize(file, offset); if(max_size < 0) return NULL; ret_val = regfi_parse_sk(file, offset, max_size, strict); if(ret_val == NULL) { /* Cache the parse failure and bail out. */ failure_ptr = talloc(NULL, uint32_t); if(failure_ptr == NULL) return NULL; *(uint32_t*)failure_ptr = REGFI_OFFSET_NONE; lru_cache_update(file->sk_cache, &offset, 4, failure_ptr); return NULL; } lru_cache_update(file->sk_cache, &offset, 4, ret_val); } return ret_val; } /****************************************************************************** ******************************************************************************/ REGFI_NK_REC* regfi_find_root_nk(REGFI_FILE* file, const REGFI_HBIN* hbin, REGFI_ENCODING output_encoding) { REGFI_NK_REC* nk = NULL; uint32_t cell_length; uint32_t cur_offset = hbin->file_off+REGFI_HBIN_HEADER_SIZE; uint32_t hbin_end = hbin->file_off+hbin->block_size; bool unalloc; while(cur_offset < hbin_end) { if(!regfi_parse_cell(file->fd, cur_offset, NULL, 0, &cell_length, &unalloc)) { regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell at offset" " 0x%.8X while searching for root key.", cur_offset); return NULL; } if(!unalloc) { nk = regfi_load_key(file, cur_offset, output_encoding, true); if(nk != NULL) { if(nk->flags & REGFI_NK_FLAG_ROOT) return nk; } } cur_offset += cell_length; } return NULL; } /****************************************************************************** ******************************************************************************/ REGFI_FILE* regfi_open(const char* filename) { REGFI_FILE* ret_val; int fd; /* open an existing file */ if ((fd = open(filename, REGFI_OPEN_FLAGS)) == -1) { /* fprintf(stderr, "regfi_open: failure to open %s (%s)\n", filename, strerror(errno));*/ return NULL; } ret_val = regfi_alloc(fd); if(ret_val == NULL) close(fd); return ret_val; } /****************************************************************************** ******************************************************************************/ REGFI_FILE* regfi_alloc(int fd) { struct stat sbuf; REGFI_FILE* rb; REGFI_HBIN* hbin = NULL; uint32_t hbin_off, file_length, cache_secret; bool rla; /* Determine file length. Must be at least big enough * for the header and one hbin. */ if (fstat(fd, &sbuf) == -1) return NULL; file_length = sbuf.st_size; if(file_length < REGFI_REGF_SIZE+REGFI_HBIN_ALLOC) return NULL; /* Read file header */ if ((rb = regfi_parse_regf(fd, true)) == NULL) { /* fprintf(stderr, "regfi_alloc: Failed to read initial REGF block\n"); */ return NULL; } rb->file_length = file_length; rb->hbins = range_list_new(); if(rb->hbins == NULL) { /* fprintf(stderr, "regfi_alloc: Failed to create HBIN list.\n"); */ talloc_free(rb); return NULL; } talloc_steal(rb, rb->hbins); rla = true; hbin_off = REGFI_REGF_SIZE; hbin = regfi_parse_hbin(rb, hbin_off, true); while(hbin && rla) { rla = range_list_add(rb->hbins, hbin->file_off, hbin->block_size, hbin); if(rla) talloc_steal(rb->hbins, hbin); hbin_off = hbin->file_off + hbin->block_size; hbin = regfi_parse_hbin(rb, hbin_off, true); } /* This secret isn't very secret, but we don't need a good one. This * secret is just designed to prevent someone from trying to blow our * caching and make things slow. */ cache_secret = 0x15DEAD05^time(NULL)^(getpid()<<16); /* Cache an unlimited number of SK records. Typically there are very few. */ rb->sk_cache = lru_cache_create_ctx(rb, 0, cache_secret, true); /* Default message mask */ rb->msg_mask = REGFI_MSG_ERROR|REGFI_MSG_WARN; /* success */ return rb; } /****************************************************************************** ******************************************************************************/ int regfi_close(REGFI_FILE* file) { int fd; /* nothing to do if there is no open file */ if ((file == NULL) || (file->fd == -1)) return 0; fd = file->fd; file->fd = -1; regfi_free(file); return close(fd); } /****************************************************************************** ******************************************************************************/ void regfi_free(REGFI_FILE *file) { if(file->last_message != NULL) free(file->last_message); talloc_free(file); } /****************************************************************************** * First checks the offset given by the file header, then checks the * rest of the file if that fails. ******************************************************************************/ REGFI_NK_REC* regfi_rootkey(REGFI_FILE* file, REGFI_ENCODING output_encoding) { REGFI_NK_REC* nk = NULL; REGFI_HBIN* hbin; uint32_t root_offset, i, num_hbins; if(!file) return NULL; root_offset = file->root_cell+REGFI_REGF_SIZE; nk = regfi_load_key(file, root_offset, output_encoding, true); if(nk != NULL) { if(nk->flags & REGFI_NK_FLAG_ROOT) return nk; } regfi_add_message(file, REGFI_MSG_WARN, "File header indicated root key at" " location 0x%.8X, but no root key found." " Searching rest of file...", root_offset); /* If the file header gives bad info, scan through the file one HBIN * block at a time looking for an NK record with a root key type. */ num_hbins = range_list_size(file->hbins); for(i=0; i < num_hbins && nk == NULL; i++) { hbin = (REGFI_HBIN*)range_list_get(file->hbins, i)->data; nk = regfi_find_root_nk(file, hbin, output_encoding); } return nk; } /****************************************************************************** *****************************************************************************/ void regfi_free_key(REGFI_NK_REC* nk) { regfi_subkeylist_free(nk->subkeys); talloc_free(nk); } /****************************************************************************** *****************************************************************************/ void regfi_free_value(REGFI_VK_REC* vk) { talloc_free(vk); } /****************************************************************************** *****************************************************************************/ void regfi_subkeylist_free(REGFI_SUBKEY_LIST* list) { if(list != NULL) { talloc_free(list); } } /****************************************************************************** *****************************************************************************/ REGFI_ITERATOR* regfi_iterator_new(REGFI_FILE* file, REGFI_ENCODING output_encoding) { REGFI_NK_REC* root; REGFI_ITERATOR* ret_val; if(output_encoding != REGFI_ENCODING_UTF8 && output_encoding != REGFI_ENCODING_ASCII) { regfi_add_message(file, REGFI_MSG_ERROR, "Invalid output_encoding supplied" " in creation of regfi iterator."); return NULL; } ret_val = talloc(NULL, REGFI_ITERATOR); if(ret_val == NULL) return NULL; root = regfi_rootkey(file, output_encoding); if(root == NULL) { talloc_free(ret_val); return NULL; } ret_val->key_positions = void_stack_new(REGFI_MAX_DEPTH); if(ret_val->key_positions == NULL) { talloc_free(ret_val); return NULL; } talloc_steal(ret_val, ret_val->key_positions); ret_val->f = file; ret_val->cur_key = root; ret_val->cur_subkey = 0; ret_val->cur_value = 0; ret_val->string_encoding = output_encoding; return ret_val; } /****************************************************************************** *****************************************************************************/ void regfi_iterator_free(REGFI_ITERATOR* i) { talloc_free(i); } /****************************************************************************** *****************************************************************************/ /* XXX: some way of indicating reason for failure should be added. */ bool regfi_iterator_down(REGFI_ITERATOR* i) { REGFI_NK_REC* subkey; REGFI_ITER_POSITION* pos; pos = talloc(i->key_positions, REGFI_ITER_POSITION); if(pos == NULL) return false; subkey = (REGFI_NK_REC*)regfi_iterator_cur_subkey(i); if(subkey == NULL) { talloc_free(pos); return false; } pos->nk = i->cur_key; pos->cur_subkey = i->cur_subkey; if(!void_stack_push(i->key_positions, pos)) { talloc_free(pos); regfi_free_key(subkey); return false; } talloc_steal(i, subkey); i->cur_key = subkey; i->cur_subkey = 0; i->cur_value = 0; return true; } /****************************************************************************** *****************************************************************************/ bool regfi_iterator_up(REGFI_ITERATOR* i) { REGFI_ITER_POSITION* pos; pos = (REGFI_ITER_POSITION*)void_stack_pop(i->key_positions); if(pos == NULL) return false; regfi_free_key(i->cur_key); i->cur_key = pos->nk; i->cur_subkey = pos->cur_subkey; i->cur_value = 0; talloc_free(pos); return true; } /****************************************************************************** *****************************************************************************/ bool regfi_iterator_to_root(REGFI_ITERATOR* i) { while(regfi_iterator_up(i)) continue; return true; } /****************************************************************************** *****************************************************************************/ bool regfi_iterator_find_subkey(REGFI_ITERATOR* i, const char* subkey_name) { REGFI_NK_REC* subkey; bool found = false; uint32_t old_subkey = i->cur_subkey; if(subkey_name == NULL) return false; /* XXX: this alloc/free of each sub key might be a bit excessive */ subkey = (REGFI_NK_REC*)regfi_iterator_first_subkey(i); while((subkey != NULL) && (found == false)) { if(subkey->keyname != NULL && strcasecmp(subkey->keyname, subkey_name) == 0) found = true; else { regfi_free_key(subkey); subkey = (REGFI_NK_REC*)regfi_iterator_next_subkey(i); } } if(found == false) { i->cur_subkey = old_subkey; return false; } regfi_free_key(subkey); return true; } /****************************************************************************** *****************************************************************************/ bool regfi_iterator_walk_path(REGFI_ITERATOR* i, const char** path) { uint32_t x; if(path == NULL) return false; for(x=0; ((path[x] != NULL) && regfi_iterator_find_subkey(i, path[x]) && regfi_iterator_down(i)); x++) { continue; } if(path[x] == NULL) return true; /* XXX: is this the right number of times? */ for(; x > 0; x--) regfi_iterator_up(i); return false; } /****************************************************************************** *****************************************************************************/ const REGFI_NK_REC* regfi_iterator_cur_key(REGFI_ITERATOR* i) { return i->cur_key; } /****************************************************************************** *****************************************************************************/ const REGFI_SK_REC* regfi_iterator_cur_sk(REGFI_ITERATOR* i) { if(i->cur_key == NULL || i->cur_key->sk_off == REGFI_OFFSET_NONE) return NULL; return regfi_load_sk(i->f, i->cur_key->sk_off + REGFI_REGF_SIZE, true); } /****************************************************************************** *****************************************************************************/ REGFI_NK_REC* regfi_iterator_first_subkey(REGFI_ITERATOR* i) { i->cur_subkey = 0; return regfi_iterator_cur_subkey(i); } /****************************************************************************** *****************************************************************************/ REGFI_NK_REC* regfi_iterator_cur_subkey(REGFI_ITERATOR* i) { uint32_t nk_offset; /* see if there is anything left to report */ if (!(i->cur_key) || (i->cur_key->subkeys_off==REGFI_OFFSET_NONE) || (i->cur_subkey >= i->cur_key->num_subkeys)) return NULL; nk_offset = i->cur_key->subkeys->elements[i->cur_subkey].offset; return regfi_load_key(i->f, nk_offset+REGFI_REGF_SIZE, i->string_encoding, true); } /****************************************************************************** *****************************************************************************/ /* XXX: some way of indicating reason for failure should be added. */ REGFI_NK_REC* regfi_iterator_next_subkey(REGFI_ITERATOR* i) { REGFI_NK_REC* subkey; i->cur_subkey++; subkey = regfi_iterator_cur_subkey(i); if(subkey == NULL) i->cur_subkey--; return subkey; } /****************************************************************************** *****************************************************************************/ bool regfi_iterator_find_value(REGFI_ITERATOR* i, const char* value_name) { REGFI_VK_REC* cur; bool found = false; uint32_t old_value = i->cur_value; /* XXX: cur->valuename can be NULL in the registry. * Should we allow for a way to search for that? */ if(value_name == NULL) return false; cur = regfi_iterator_first_value(i); while((cur != NULL) && (found == false)) { if((cur->valuename != NULL) && (strcasecmp(cur->valuename, value_name) == 0)) found = true; else { regfi_free_value(cur); cur = regfi_iterator_next_value(i); } } if(found == false) { i->cur_value = old_value; return false; } regfi_free_value(cur); return true; } /****************************************************************************** *****************************************************************************/ REGFI_VK_REC* regfi_iterator_first_value(REGFI_ITERATOR* i) { i->cur_value = 0; return regfi_iterator_cur_value(i); } /****************************************************************************** *****************************************************************************/ REGFI_VK_REC* regfi_iterator_cur_value(REGFI_ITERATOR* i) { REGFI_VK_REC* ret_val = NULL; uint32_t voffset; if(i->cur_key->values != NULL && i->cur_key->values->elements != NULL) { if(i->cur_value < i->cur_key->values->num_values) { voffset = i->cur_key->values->elements[i->cur_value]; ret_val = regfi_load_value(i->f, voffset+REGFI_REGF_SIZE, i->string_encoding, true); } } return ret_val; } /****************************************************************************** *****************************************************************************/ REGFI_VK_REC* regfi_iterator_next_value(REGFI_ITERATOR* i) { REGFI_VK_REC* ret_val; i->cur_value++; ret_val = regfi_iterator_cur_value(i); if(ret_val == NULL) i->cur_value--; return ret_val; } /****************************************************************************** *****************************************************************************/ REGFI_CLASSNAME* regfi_iterator_fetch_classname(REGFI_ITERATOR* i, const REGFI_NK_REC* key) { REGFI_CLASSNAME* ret_val; uint8_t* raw; char* interpreted; uint32_t offset; int32_t conv_size, max_size; uint16_t parse_length; if(key->classname_off == REGFI_OFFSET_NONE || key->classname_length == 0) return NULL; offset = key->classname_off + REGFI_REGF_SIZE; max_size = regfi_calc_maxsize(i->f, offset); if(max_size <= 0) return NULL; parse_length = key->classname_length; raw = regfi_parse_classname(i->f, offset, &parse_length, max_size, true); if(raw == NULL) { regfi_add_message(i->f, REGFI_MSG_WARN, "Could not parse class" " name at offset 0x%.8X for key record at offset 0x%.8X.", offset, key->offset); return NULL; } ret_val = talloc(NULL, REGFI_CLASSNAME); if(ret_val == NULL) return NULL; ret_val->raw = raw; ret_val->size = parse_length; talloc_steal(ret_val, raw); interpreted = talloc_array(NULL, char, parse_length); conv_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE), regfi_encoding_int2str(i->string_encoding), raw, interpreted, parse_length, parse_length); if(conv_size < 0) { regfi_add_message(i->f, REGFI_MSG_WARN, "Error occurred while" " converting classname to charset %s. Error message: %s", i->string_encoding, strerror(-conv_size)); talloc_free(interpreted); ret_val->interpreted = NULL; } else { interpreted = talloc_realloc(NULL, interpreted, char, conv_size); ret_val->interpreted = interpreted; talloc_steal(ret_val, interpreted); } return ret_val; } /****************************************************************************** *****************************************************************************/ REGFI_DATA* regfi_iterator_fetch_data(REGFI_ITERATOR* i, const REGFI_VK_REC* value) { REGFI_DATA* ret_val = NULL; REGFI_BUFFER raw_data; if(value->data_size != 0) { raw_data = regfi_load_data(i->f, value->data_off, value->data_size, value->data_in_offset, true); if(raw_data.buf == NULL) { regfi_add_message(i->f, REGFI_MSG_WARN, "Could not parse data record" " while parsing VK record at offset 0x%.8X.", value->offset); } else { ret_val = regfi_buffer_to_data(raw_data); if(ret_val == NULL) { regfi_add_message(i->f, REGFI_MSG_WARN, "Error occurred in converting" " data buffer to data structure while interpreting " "data for VK record at offset 0x%.8X.", value->offset); talloc_free(raw_data.buf); return NULL; } if(!regfi_interpret_data(i->f, i->string_encoding, value->type, ret_val)) { regfi_add_message(i->f, REGFI_MSG_INFO, "Error occurred while" " interpreting data for VK record at offset 0x%.8X.", value->offset); } } } return ret_val; } /****************************************************************************** *****************************************************************************/ void regfi_free_classname(REGFI_CLASSNAME* classname) { talloc_free(classname); } /****************************************************************************** *****************************************************************************/ void regfi_free_data(REGFI_DATA* data) { talloc_free(data); } /****************************************************************************** *****************************************************************************/ REGFI_DATA* regfi_buffer_to_data(REGFI_BUFFER raw_data) { REGFI_DATA* ret_val; if(raw_data.buf == NULL) return NULL; ret_val = talloc(NULL, REGFI_DATA); if(ret_val == NULL) return NULL; talloc_steal(ret_val, raw_data.buf); ret_val->raw = raw_data.buf; ret_val->size = raw_data.len; ret_val->interpreted_size = 0; ret_val->interpreted.qword = 0; return ret_val; } /****************************************************************************** *****************************************************************************/ bool regfi_interpret_data(REGFI_FILE* file, REGFI_ENCODING string_encoding, uint32_t type, REGFI_DATA* data) { uint8_t** tmp_array; uint8_t* tmp_str; int32_t tmp_size; uint32_t i, j, array_size; if(data == NULL) return false; switch (type) { case REG_SZ: case REG_EXPAND_SZ: /* REG_LINK is a symbolic link, stored as a unicode string. */ case REG_LINK: tmp_str = talloc_array(NULL, uint8_t, data->size); if(tmp_str == NULL) { data->interpreted.string = NULL; data->interpreted_size = 0; return false; } tmp_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE), regfi_encoding_int2str(string_encoding), data->raw, (char*)tmp_str, data->size, data->size); if(tmp_size < 0) { regfi_add_message(file, REGFI_MSG_INFO, "Error occurred while" " converting data of type %d to %s. Error message: %s", type, string_encoding, strerror(-tmp_size)); talloc_free(tmp_str); data->interpreted.string = NULL; data->interpreted_size = 0; return false; } tmp_str = talloc_realloc(NULL, tmp_str, uint8_t, tmp_size); data->interpreted.string = tmp_str; data->interpreted_size = tmp_size; talloc_steal(data, tmp_str); break; case REG_DWORD: if(data->size < 4) { data->interpreted.dword = 0; data->interpreted_size = 0; return false; } data->interpreted.dword = IVAL(data->raw, 0); data->interpreted_size = 4; break; case REG_DWORD_BE: if(data->size < 4) { data->interpreted.dword_be = 0; data->interpreted_size = 0; return false; } data->interpreted.dword_be = RIVAL(data->raw, 0); data->interpreted_size = 4; break; case REG_QWORD: if(data->size < 8) { data->interpreted.qword = 0; data->interpreted_size = 0; return false; } data->interpreted.qword = (uint64_t)IVAL(data->raw, 0) + (((uint64_t)IVAL(data->raw, 4))<<32); data->interpreted_size = 8; break; case REG_MULTI_SZ: tmp_str = talloc_array(NULL, uint8_t, data->size); if(tmp_str == NULL) { data->interpreted.multiple_string = NULL; data->interpreted_size = 0; return false; } /* Attempt to convert entire string from UTF-16LE to output encoding, * then parse and quote fields individually. */ tmp_size = regfi_conv_charset(regfi_encoding_int2str(REGFI_ENCODING_UTF16LE), regfi_encoding_int2str(string_encoding), data->raw, (char*)tmp_str, data->size, data->size); if(tmp_size < 0) { regfi_add_message(file, REGFI_MSG_INFO, "Error occurred while" " converting data of type %d to %s. Error message: %s", type, string_encoding, strerror(-tmp_size)); talloc_free(tmp_str); data->interpreted.multiple_string = NULL; data->interpreted_size = 0; return false; } array_size = tmp_size+1; tmp_array = talloc_array(NULL, uint8_t*, array_size); if(tmp_array == NULL) { talloc_free(tmp_str); data->interpreted.string = NULL; data->interpreted_size = 0; return false; } tmp_array[0] = tmp_str; for(i=0,j=1; i < tmp_size && j < array_size-1; i++) { if(tmp_str[i] == '\0' && (i+1 < tmp_size)) tmp_array[j++] = tmp_str+i+1; } tmp_array[j] = NULL; tmp_array = talloc_realloc(NULL, tmp_array, uint8_t*, j+1); data->interpreted.multiple_string = tmp_array; /* XXX: how meaningful is this? should we store number of strings instead? */ data->interpreted_size = tmp_size; talloc_steal(tmp_array, tmp_str); talloc_steal(data, tmp_array); break; /* XXX: Dont know how to interpret these yet, just treat as binary */ case REG_NONE: data->interpreted.none = data->raw; data->interpreted_size = data->size; break; case REG_RESOURCE_LIST: data->interpreted.resource_list = data->raw; data->interpreted_size = data->size; break; case REG_FULL_RESOURCE_DESCRIPTOR: data->interpreted.full_resource_descriptor = data->raw; data->interpreted_size = data->size; break; case REG_RESOURCE_REQUIREMENTS_LIST: data->interpreted.resource_requirements_list = data->raw; data->interpreted_size = data->size; break; case REG_BINARY: data->interpreted.binary = data->raw; data->interpreted_size = data->size; break; default: data->interpreted.qword = 0; data->interpreted_size = 0; return false; } data->type = type; return true; } /****************************************************************************** * Convert from UTF-16LE to specified character set. * On error, returns a negative errno code. *****************************************************************************/ int32_t regfi_conv_charset(const char* input_charset, const char* output_charset, uint8_t* input, char* output, uint32_t input_len, uint32_t output_max) { iconv_t conv_desc; char* inbuf = (char*)input; char* outbuf = output; size_t in_len = (size_t)input_len; size_t out_len = (size_t)(output_max-1); int ret; /* XXX: Consider creating a couple of conversion descriptors earlier, * storing them on an iterator so they don't have to be recreated * each time. */ /* Set up conversion descriptor. */ conv_desc = iconv_open(output_charset, input_charset); ret = iconv(conv_desc, &inbuf, &in_len, &outbuf, &out_len); if(ret == -1) { iconv_close(conv_desc); return -errno; } *outbuf = '\0'; iconv_close(conv_desc); return output_max-out_len-1; } /******************************************************************* * Computes the checksum of the registry file header. * buffer must be at least the size of a regf header (4096 bytes). *******************************************************************/ static uint32_t regfi_compute_header_checksum(uint8_t* buffer) { uint32_t checksum, x; int i; /* XOR of all bytes 0x0000 - 0x01FB */ checksum = x = 0; for ( i=0; i<0x01FB; i+=4 ) { x = IVAL(buffer, i ); checksum ^= x; } return checksum; } /******************************************************************* * XXX: Add way to return more detailed error information. *******************************************************************/ REGFI_FILE* regfi_parse_regf(int fd, bool strict) { uint8_t file_header[REGFI_REGF_SIZE]; uint32_t length; REGFI_FILE* ret_val; ret_val = talloc(NULL, REGFI_FILE); if(ret_val == NULL) return NULL; ret_val->fd = fd; ret_val->sk_cache = NULL; ret_val->last_message = NULL; ret_val->hbins = NULL; length = REGFI_REGF_SIZE; if((regfi_read(fd, file_header, &length)) != 0 || length != REGFI_REGF_SIZE) goto fail; ret_val->checksum = IVAL(file_header, 0x1FC); ret_val->computed_checksum = regfi_compute_header_checksum(file_header); if (strict && (ret_val->checksum != ret_val->computed_checksum)) goto fail; memcpy(ret_val->magic, file_header, REGFI_REGF_MAGIC_SIZE); if(memcmp(ret_val->magic, "regf", REGFI_REGF_MAGIC_SIZE) != 0) { if(strict) goto fail; regfi_add_message(ret_val, REGFI_MSG_WARN, "Magic number mismatch " "(%.2X %.2X %.2X %.2X) while parsing hive header", ret_val->magic[0], ret_val->magic[1], ret_val->magic[2], ret_val->magic[3]); } ret_val->sequence1 = IVAL(file_header, 0x4); ret_val->sequence2 = IVAL(file_header, 0x8); ret_val->mtime.low = IVAL(file_header, 0xC); ret_val->mtime.high = IVAL(file_header, 0x10); ret_val->major_version = IVAL(file_header, 0x14); ret_val->minor_version = IVAL(file_header, 0x18); ret_val->type = IVAL(file_header, 0x1C); ret_val->format = IVAL(file_header, 0x20); ret_val->root_cell = IVAL(file_header, 0x24); ret_val->last_block = IVAL(file_header, 0x28); ret_val->cluster = IVAL(file_header, 0x2C); memcpy(ret_val->file_name, file_header+0x30, REGFI_REGF_NAME_SIZE); /* XXX: Should we add a warning if these uuid parsers fail? Can they? */ ret_val->rm_id = winsec_parse_uuid(ret_val, file_header+0x70, 16); ret_val->log_id = winsec_parse_uuid(ret_val, file_header+0x80, 16); ret_val->flags = IVAL(file_header, 0x90); ret_val->tm_id = winsec_parse_uuid(ret_val, file_header+0x94, 16); ret_val->guid_signature = IVAL(file_header, 0xa4); memcpy(ret_val->reserved1, file_header+0xa8, REGFI_REGF_RESERVED1_SIZE); memcpy(ret_val->reserved2, file_header+0x200, REGFI_REGF_RESERVED2_SIZE); ret_val->thaw_tm_id = winsec_parse_uuid(ret_val, file_header+0xFC8, 16); ret_val->thaw_rm_id = winsec_parse_uuid(ret_val, file_header+0xFD8, 16); ret_val->thaw_log_id = winsec_parse_uuid(ret_val, file_header+0xFE8, 16); ret_val->boot_type = IVAL(file_header, 0xFF8); ret_val->boot_recover = IVAL(file_header, 0xFFC); return ret_val; fail: talloc_free(ret_val); return NULL; } /****************************************************************************** * Given real file offset, read and parse the hbin at that location * along with it's associated cells. ******************************************************************************/ REGFI_HBIN* regfi_parse_hbin(REGFI_FILE* file, uint32_t offset, bool strict) { REGFI_HBIN *hbin; uint8_t hbin_header[REGFI_HBIN_HEADER_SIZE]; uint32_t length; if(offset >= file->file_length) return NULL; if(lseek(file->fd, offset, SEEK_SET) == -1) { regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed" " while parsing hbin at offset 0x%.8X.", offset); return NULL; } length = REGFI_HBIN_HEADER_SIZE; if((regfi_read(file->fd, hbin_header, &length) != 0) || length != REGFI_HBIN_HEADER_SIZE) return NULL; if(lseek(file->fd, offset, SEEK_SET) == -1) { regfi_add_message(file, REGFI_MSG_ERROR, "Seek failed" " while parsing hbin at offset 0x%.8X.", offset); return NULL; } hbin = talloc(NULL, REGFI_HBIN); if(hbin == NULL) return NULL; hbin->file_off = offset; memcpy(hbin->magic, hbin_header, 4); if(strict && (memcmp(hbin->magic, "hbin", 4) != 0)) { regfi_add_message(file, REGFI_MSG_INFO, "Magic number mismatch " "(%.2X %.2X %.2X %.2X) while parsing hbin at offset" " 0x%.8X.", hbin->magic[0], hbin->magic[1], hbin->magic[2], hbin->magic[3], offset); talloc_free(hbin); return NULL; } hbin->first_hbin_off = IVAL(hbin_header, 0x4); hbin->block_size = IVAL(hbin_header, 0x8); /* this should be the same thing as hbin->block_size but just in case */ hbin->next_block = IVAL(hbin_header, 0x1C); /* Ensure the block size is a multiple of 0x1000 and doesn't run off * the end of the file. */ /* XXX: This may need to be relaxed for dealing with * partial or corrupt files. */ if((offset + hbin->block_size > file->file_length) || (hbin->block_size & 0xFFFFF000) != hbin->block_size) { regfi_add_message(file, REGFI_MSG_ERROR, "The hbin offset is not aligned" " or runs off the end of the file" " while parsing hbin at offset 0x%.8X.", offset); talloc_free(hbin); return NULL; } return hbin; } /******************************************************************* *******************************************************************/ REGFI_NK_REC* regfi_parse_nk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict) { uint8_t nk_header[REGFI_NK_MIN_LENGTH]; REGFI_NK_REC* ret_val; uint32_t length,cell_length; bool unalloc = false; if(!regfi_parse_cell(file->fd, offset, nk_header, REGFI_NK_MIN_LENGTH, &cell_length, &unalloc)) { regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header" " while parsing NK record at offset 0x%.8X.", offset); return NULL; } /* A bit of validation before bothering to allocate memory */ if((nk_header[0x0] != 'n') || (nk_header[0x1] != 'k')) { regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch in parsing" " NK record at offset 0x%.8X.", offset); return NULL; } ret_val = talloc(NULL, REGFI_NK_REC); if(ret_val == NULL) { regfi_add_message(file, REGFI_MSG_ERROR, "Failed to allocate memory while" " parsing NK record at offset 0x%.8X.", offset); return NULL; } ret_val->values = NULL; ret_val->subkeys = NULL; ret_val->offset = offset; ret_val->cell_size = cell_length; if(ret_val->cell_size > max_size) ret_val->cell_size = max_size & 0xFFFFFFF8; if((ret_val->cell_size < REGFI_NK_MIN_LENGTH) || (strict && (ret_val->cell_size & 0x00000007) != 0)) { regfi_add_message(file, REGFI_MSG_WARN, "A length check failed while" " parsing NK record at offset 0x%.8X.", offset); talloc_free(ret_val); return NULL; } ret_val->magic[0] = nk_header[0x0]; ret_val->magic[1] = nk_header[0x1]; ret_val->flags = SVAL(nk_header, 0x2); if((ret_val->flags & ~REGFI_NK_KNOWN_FLAGS) != 0) { regfi_add_message(file, REGFI_MSG_WARN, "Unknown key flags (0x%.4X) while" " parsing NK record at offset 0x%.8X.", (ret_val->flags & ~REGFI_NK_KNOWN_FLAGS), offset); } ret_val->mtime.low = IVAL(nk_header, 0x4); ret_val->mtime.high = IVAL(nk_header, 0x8); /* If the key is unallocated and the MTIME is earlier than Jan 1, 1990 * or later than Jan 1, 2290, we consider this a bad key. This helps * weed out some false positives during deleted data recovery. */ if(unalloc && ((ret_val->mtime.high < REGFI_MTIME_MIN_HIGH && ret_val->mtime.low < REGFI_MTIME_MIN_LOW) || (ret_val->mtime.high > REGFI_MTIME_MAX_HIGH && ret_val->mtime.low > REGFI_MTIME_MAX_LOW))) return NULL; ret_val->unknown1 = IVAL(nk_header, 0xC); ret_val->parent_off = IVAL(nk_header, 0x10); ret_val->num_subkeys = IVAL(nk_header, 0x14); ret_val->unknown2 = IVAL(nk_header, 0x18); ret_val->subkeys_off = IVAL(nk_header, 0x1C); ret_val->unknown3 = IVAL(nk_header, 0x20); ret_val->num_values = IVAL(nk_header, 0x24); ret_val->values_off = IVAL(nk_header, 0x28); ret_val->sk_off = IVAL(nk_header, 0x2C); ret_val->classname_off = IVAL(nk_header, 0x30); ret_val->max_bytes_subkeyname = IVAL(nk_header, 0x34); ret_val->max_bytes_subkeyclassname = IVAL(nk_header, 0x38); ret_val->max_bytes_valuename = IVAL(nk_header, 0x3C); ret_val->max_bytes_value = IVAL(nk_header, 0x40); ret_val->unk_index = IVAL(nk_header, 0x44); ret_val->name_length = SVAL(nk_header, 0x48); ret_val->classname_length = SVAL(nk_header, 0x4A); ret_val->keyname = NULL; if(ret_val->name_length + REGFI_NK_MIN_LENGTH > ret_val->cell_size) { if(strict) { regfi_add_message(file, REGFI_MSG_ERROR, "Contents too large for cell" " while parsing NK record at offset 0x%.8X.", offset); talloc_free(ret_val); return NULL; } else ret_val->name_length = ret_val->cell_size - REGFI_NK_MIN_LENGTH; } else if (unalloc) { /* Truncate cell_size if it's much larger than the apparent total record length. */ /* Round up to the next multiple of 8 */ length = (ret_val->name_length + REGFI_NK_MIN_LENGTH) & 0xFFFFFFF8; if(length < ret_val->name_length + REGFI_NK_MIN_LENGTH) length+=8; /* If cell_size is still greater, truncate. */ if(length < ret_val->cell_size) ret_val->cell_size = length; } ret_val->keyname_raw = talloc_array(ret_val, uint8_t, ret_val->name_length); if(ret_val->keyname_raw == NULL) { talloc_free(ret_val); return NULL; } /* Don't need to seek, should be at the right offset */ length = ret_val->name_length; if((regfi_read(file->fd, (uint8_t*)ret_val->keyname_raw, &length) != 0) || length != ret_val->name_length) { regfi_add_message(file, REGFI_MSG_ERROR, "Failed to read key name" " while parsing NK record at offset 0x%.8X.", offset); talloc_free(ret_val); return NULL; } return ret_val; } uint8_t* regfi_parse_classname(REGFI_FILE* file, uint32_t offset, uint16_t* name_length, uint32_t max_size, bool strict) { uint8_t* ret_val = NULL; uint32_t length; uint32_t cell_length; bool unalloc = false; if(*name_length > 0 && offset != REGFI_OFFSET_NONE && (offset & 0x00000007) == 0) { if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc)) { regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header" " while parsing class name at offset 0x%.8X.", offset); return NULL; } if((cell_length & 0x0000007) != 0) { regfi_add_message(file, REGFI_MSG_ERROR, "Cell length not a multiple of 8" " while parsing class name at offset 0x%.8X.", offset); return NULL; } if(cell_length > max_size) { regfi_add_message(file, REGFI_MSG_WARN, "Cell stretches past hbin " "boundary while parsing class name at offset 0x%.8X.", offset); if(strict) return NULL; cell_length = max_size; } if((cell_length - 4) < *name_length) { regfi_add_message(file, REGFI_MSG_WARN, "Class name is larger than" " cell_length while parsing class name at offset" " 0x%.8X.", offset); if(strict) return NULL; *name_length = cell_length - 4; } ret_val = talloc_array(NULL, uint8_t, *name_length); if(ret_val != NULL) { length = *name_length; if((regfi_read(file->fd, ret_val, &length) != 0) || length != *name_length) { regfi_add_message(file, REGFI_MSG_ERROR, "Could not read class name" " while parsing class name at offset 0x%.8X.", offset); talloc_free(ret_val); return NULL; } } } return ret_val; } /****************************************************************************** *******************************************************************************/ REGFI_VK_REC* regfi_parse_vk(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict) { REGFI_VK_REC* ret_val; uint8_t vk_header[REGFI_VK_MIN_LENGTH]; uint32_t raw_data_size, length, cell_length; bool unalloc = false; if(!regfi_parse_cell(file->fd, offset, vk_header, REGFI_VK_MIN_LENGTH, &cell_length, &unalloc)) { regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell header" " while parsing VK record at offset 0x%.8X.", offset); return NULL; } ret_val = talloc(NULL, REGFI_VK_REC); if(ret_val == NULL) return NULL; ret_val->offset = offset; ret_val->cell_size = cell_length; ret_val->valuename = NULL; ret_val->valuename_raw = NULL; if(ret_val->cell_size > max_size) ret_val->cell_size = max_size & 0xFFFFFFF8; if((ret_val->cell_size < REGFI_VK_MIN_LENGTH) || (ret_val->cell_size & 0x00000007) != 0) { regfi_add_message(file, REGFI_MSG_WARN, "Invalid cell size encountered" " while parsing VK record at offset 0x%.8X.", offset); talloc_free(ret_val); return NULL; } ret_val->magic[0] = vk_header[0x0]; ret_val->magic[1] = vk_header[0x1]; if((ret_val->magic[0] != 'v') || (ret_val->magic[1] != 'k')) { /* XXX: This does not account for deleted keys under Win2K which * often have this (and the name length) overwritten with * 0xFFFF. */ regfi_add_message(file, REGFI_MSG_WARN, "Magic number mismatch" " while parsing VK record at offset 0x%.8X.", offset); talloc_free(ret_val); return NULL; } ret_val->name_length = SVAL(vk_header, 0x2); raw_data_size = IVAL(vk_header, 0x4); ret_val->data_size = raw_data_size & ~REGFI_VK_DATA_IN_OFFSET; /* The data is typically stored in the offset if the size <= 4, * in which case this flag is set. */ ret_val->data_in_offset = (bool)(raw_data_size & REGFI_VK_DATA_IN_OFFSET); ret_val->data_off = IVAL(vk_header, 0x8); ret_val->type = IVAL(vk_header, 0xC); ret_val->flags = SVAL(vk_header, 0x10); ret_val->unknown1 = SVAL(vk_header, 0x12); if(ret_val->name_length > 0) { if(ret_val->name_length + REGFI_VK_MIN_LENGTH + 4 > ret_val->cell_size) { regfi_add_message(file, REGFI_MSG_WARN, "Name too long for remaining cell" " space while parsing VK record at offset 0x%.8X.", offset); if(strict) { talloc_free(ret_val); return NULL; } else ret_val->name_length = ret_val->cell_size - REGFI_VK_MIN_LENGTH - 4; } /* Round up to the next multiple of 8 */ cell_length = (ret_val->name_length + REGFI_VK_MIN_LENGTH + 4) & 0xFFFFFFF8; if(cell_length < ret_val->name_length + REGFI_VK_MIN_LENGTH + 4) cell_length+=8; ret_val->valuename_raw = talloc_array(ret_val, uint8_t, ret_val->name_length); if(ret_val->valuename_raw == NULL) { talloc_free(ret_val); return NULL; } length = ret_val->name_length; if((regfi_read(file->fd, (uint8_t*)ret_val->valuename_raw, &length) != 0) || length != ret_val->name_length) { regfi_add_message(file, REGFI_MSG_ERROR, "Could not read value name" " while parsing VK record at offset 0x%.8X.", offset); talloc_free(ret_val); return NULL; } } else cell_length = REGFI_VK_MIN_LENGTH + 4; if(unalloc) { /* If cell_size is still greater, truncate. */ if(cell_length < ret_val->cell_size) ret_val->cell_size = cell_length; } return ret_val; } /****************************************************************************** * ******************************************************************************/ REGFI_BUFFER regfi_load_data(REGFI_FILE* file, uint32_t voffset, uint32_t length, bool data_in_offset, bool strict) { REGFI_BUFFER ret_val; uint32_t cell_length, offset; int32_t max_size; bool unalloc; /* Microsoft's documentation indicates that "available memory" is * the limit on value sizes for the more recent registry format version. * This is not only annoying, but it's probably also incorrect, since clearly * value data sizes are limited to 2^31 (high bit used as a flag) and even * with big data records, the apparent max size is: * 16344 * 2^16 = 1071104040 (~1GB). * * We choose to limit it to 1M which was the limit in older versions and * should rarely be exceeded unless the file is corrupt or malicious. * For more info, see: * http://msdn.microsoft.com/en-us/library/ms724872%28VS.85%29.aspx */ /* XXX: add way to skip this check at user discression. */ if(length > REGFI_VK_MAX_DATA_LENGTH) { regfi_add_message(file, REGFI_MSG_WARN, "Value data size %d larger than " "%d, truncating...", length, REGFI_VK_MAX_DATA_LENGTH); length = REGFI_VK_MAX_DATA_LENGTH; } if(data_in_offset) return regfi_parse_little_data(file, voffset, length, strict); else { offset = voffset + REGFI_REGF_SIZE; max_size = regfi_calc_maxsize(file, offset); if(max_size < 0) { regfi_add_message(file, REGFI_MSG_WARN, "Could not find HBIN for data" " at offset 0x%.8X.", offset); goto fail; } if(!regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc)) { regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while" " parsing data record at offset 0x%.8X.", offset); goto fail; } if((cell_length & 0x00000007) != 0) { regfi_add_message(file, REGFI_MSG_WARN, "Cell length not multiple of 8" " while parsing data record at offset 0x%.8X.", offset); goto fail; } if(cell_length > max_size) { regfi_add_message(file, REGFI_MSG_WARN, "Cell extends past HBIN boundary" " while parsing data record at offset 0x%.8X.", offset); goto fail; } if(cell_length - 4 < length) { /* XXX: All big data records thus far have been 16 bytes long. * Should we check for this precise size instead of just * relying upon the above check? */ if (file->major_version >= 1 && file->minor_version >= 5) { /* Attempt to parse a big data record */ return regfi_load_big_data(file, offset, length, cell_length, NULL, strict); } else { regfi_add_message(file, REGFI_MSG_WARN, "Data length (0x%.8X) larger than" " remaining cell length (0x%.8X)" " while parsing data record at offset 0x%.8X.", length, cell_length - 4, offset); if(strict) goto fail; else length = cell_length - 4; } } ret_val = regfi_parse_data(file, offset, length, strict); } return ret_val; fail: ret_val.buf = NULL; ret_val.len = 0; return ret_val; } /****************************************************************************** * Parses the common case data records stored in a single cell. ******************************************************************************/ REGFI_BUFFER regfi_parse_data(REGFI_FILE* file, uint32_t offset, uint32_t length, bool strict) { REGFI_BUFFER ret_val; uint32_t read_length; ret_val.buf = NULL; ret_val.len = 0; if(lseek(file->fd, offset+4, SEEK_SET) == -1) { regfi_add_message(file, REGFI_MSG_WARN, "Could not seek while " "reading data at offset 0x%.8X.", offset); return ret_val; } if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL) return ret_val; ret_val.len = length; read_length = length; if((regfi_read(file->fd, ret_val.buf, &read_length) != 0) || read_length != length) { regfi_add_message(file, REGFI_MSG_ERROR, "Could not read data block while" " parsing data record at offset 0x%.8X.", offset); talloc_free(ret_val.buf); ret_val.buf = NULL; ret_val.buf = 0; } return ret_val; } /****************************************************************************** * ******************************************************************************/ REGFI_BUFFER regfi_parse_little_data(REGFI_FILE* file, uint32_t voffset, uint32_t length, bool strict) { uint8_t i; REGFI_BUFFER ret_val; ret_val.buf = NULL; ret_val.len = 0; if(length > 4) { regfi_add_message(file, REGFI_MSG_ERROR, "Data in offset but length > 4" " while parsing data record. (voffset=0x%.8X, length=%d)", voffset, length); return ret_val; } if((ret_val.buf = talloc_array(NULL, uint8_t, length)) == NULL) return ret_val; ret_val.len = length; for(i = 0; i < length; i++) ret_val.buf[i] = (uint8_t)((voffset >> i*8) & 0xFF); return ret_val; } /****************************************************************************** *******************************************************************************/ REGFI_BUFFER regfi_parse_big_data_header(REGFI_FILE* file, uint32_t offset, uint32_t max_size, bool strict) { REGFI_BUFFER ret_val; uint32_t cell_length; bool unalloc; /* XXX: do something with unalloc? */ ret_val.buf = (uint8_t*)talloc_array(NULL, uint8_t, REGFI_BIG_DATA_MIN_LENGTH); if(ret_val.buf == NULL) goto fail; if(REGFI_BIG_DATA_MIN_LENGTH > max_size) { regfi_add_message(file, REGFI_MSG_WARN, "Big data header exceeded max_size " "while parsing big data header at offset 0x%.8X.",offset); goto fail; } if(!regfi_parse_cell(file->fd, offset, ret_val.buf, REGFI_BIG_DATA_MIN_LENGTH, &cell_length, &unalloc)) { regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while" " parsing big data header at offset 0x%.8X.", offset); goto fail; } if((ret_val.buf[0] != 'd') || (ret_val.buf[1] != 'b')) { regfi_add_message(file, REGFI_MSG_WARN, "Unknown magic number" " (0x%.2X, 0x%.2X) encountered while parsing" " big data header at offset 0x%.8X.", ret_val.buf[0], ret_val.buf[1], offset); goto fail; } ret_val.len = REGFI_BIG_DATA_MIN_LENGTH; return ret_val; fail: if(ret_val.buf != NULL) { talloc_free(ret_val.buf); ret_val.buf = NULL; } ret_val.len = 0; return ret_val; } /****************************************************************************** * ******************************************************************************/ uint32_t* regfi_parse_big_data_indirect(REGFI_FILE* file, uint32_t offset, uint16_t num_chunks, bool strict) { uint32_t* ret_val; uint32_t indirect_length; int32_t max_size; uint16_t i; bool unalloc; /* XXX: do something with unalloc? */ max_size = regfi_calc_maxsize(file, offset); if((max_size < 0) || (num_chunks*sizeof(uint32_t) + 4 > max_size)) return NULL; ret_val = (uint32_t*)talloc_array(NULL, uint32_t, num_chunks); if(ret_val == NULL) goto fail; if(!regfi_parse_cell(file->fd, offset, (uint8_t*)ret_val, num_chunks*sizeof(uint32_t), &indirect_length, &unalloc)) { regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while" " parsing big data indirect record at offset 0x%.8X.", offset); goto fail; } /* Convert pointers to proper endianess, verify they are aligned. */ for(i=0; ifd, chunk_offset, NULL, 0, &cell_length, &unalloc)) { regfi_add_message(file, REGFI_MSG_WARN, "Could not parse cell while" " parsing big data chunk at offset 0x%.8X.", chunk_offset); goto fail; } if(!range_list_add(ret_val, chunk_offset, cell_length, NULL)) goto fail; } return ret_val; fail: if(ret_val != NULL) range_list_free(ret_val); return NULL; } /****************************************************************************** *******************************************************************************/ REGFI_BUFFER regfi_load_big_data(REGFI_FILE* file, uint32_t offset, uint32_t data_length, uint32_t cell_length, range_list* used_ranges, bool strict) { REGFI_BUFFER ret_val; uint16_t num_chunks, i; uint32_t read_length, data_left, tmp_len, indirect_offset; uint32_t* indirect_ptrs = NULL; REGFI_BUFFER bd_header; range_list* bd_cells = NULL; const range_list_element* cell_info; ret_val.buf = NULL; /* XXX: Add better error/warning messages */ bd_header = regfi_parse_big_data_header(file, offset, cell_length, strict); if(bd_header.buf == NULL) goto fail; /* Keep track of used space for use by reglookup-recover */ if(used_ranges != NULL) if(!range_list_add(used_ranges, offset, cell_length, NULL)) goto fail; num_chunks = SVAL(bd_header.buf, 0x2); indirect_offset = IVAL(bd_header.buf, 0x4) + REGFI_REGF_SIZE; talloc_free(bd_header.buf); indirect_ptrs = regfi_parse_big_data_indirect(file, indirect_offset, num_chunks, strict); if(indirect_ptrs == NULL) goto fail; if(used_ranges != NULL) if(!range_list_add(used_ranges, indirect_offset, num_chunks*4+4, NULL)) goto fail; if((ret_val.buf = talloc_array(NULL, uint8_t, data_length)) == NULL) goto fail; data_left = data_length; bd_cells = regfi_parse_big_data_cells(file, indirect_ptrs, num_chunks, strict); if(bd_cells == NULL) goto fail; talloc_free(indirect_ptrs); indirect_ptrs = NULL; for(i=0; (i0); i++) { cell_info = range_list_get(bd_cells, i); if(cell_info == NULL) goto fail; /* XXX: This should be "cell_info->length-4" to account for the 4 byte cell * length. However, it has been observed that some (all?) chunks * have an additional 4 bytes of 0 at the end of their cells that * isn't part of the data, so we're trimming that off too. * Perhaps it's just an 8 byte alignment requirement... */ if(cell_info->length - 8 >= data_left) { if(i+1 != num_chunks) { regfi_add_message(file, REGFI_MSG_WARN, "Left over chunks detected " "while constructing big data at offset 0x%.8X " "(chunk offset 0x%.8X).", offset, cell_info->offset); } read_length = data_left; } else read_length = cell_info->length - 8; if(read_length > regfi_calc_maxsize(file, cell_info->offset)) { regfi_add_message(file, REGFI_MSG_WARN, "A chunk exceeded the maxsize " "while constructing big data at offset 0x%.8X " "(chunk offset 0x%.8X).", offset, cell_info->offset); goto fail; } if(lseek(file->fd, cell_info->offset+sizeof(uint32_t), SEEK_SET) == -1) { regfi_add_message(file, REGFI_MSG_WARN, "Could not seek to chunk while " "constructing big data at offset 0x%.8X " "(chunk offset 0x%.8X).", offset, cell_info->offset); goto fail; } tmp_len = read_length; if(regfi_read(file->fd, ret_val.buf+(data_length-data_left), &read_length) != 0 || (read_length != tmp_len)) { regfi_add_message(file, REGFI_MSG_WARN, "Could not read data chunk while" " constructing big data at offset 0x%.8X" " (chunk offset 0x%.8X).", offset, cell_info->offset); goto fail; } if(used_ranges != NULL) if(!range_list_add(used_ranges, cell_info->offset,cell_info->length,NULL)) goto fail; data_left -= read_length; } range_list_free(bd_cells); ret_val.len = data_length-data_left; return ret_val; fail: if(ret_val.buf != NULL) talloc_free(ret_val.buf); if(indirect_ptrs != NULL) talloc_free(indirect_ptrs); if(bd_cells != NULL) range_list_free(bd_cells); ret_val.buf = NULL; ret_val.len = 0; return ret_val; } range_list* regfi_parse_unalloc_cells(REGFI_FILE* file) { range_list* ret_val; REGFI_HBIN* hbin; const range_list_element* hbins_elem; uint32_t i, num_hbins, curr_off, cell_len; bool is_unalloc; ret_val = range_list_new(); if(ret_val == NULL) return NULL; num_hbins = range_list_size(file->hbins); for(i=0; ihbins, i); if(hbins_elem == NULL) break; hbin = (REGFI_HBIN*)hbins_elem->data; curr_off = REGFI_HBIN_HEADER_SIZE; while(curr_off < hbin->block_size) { if(!regfi_parse_cell(file->fd, hbin->file_off+curr_off, NULL, 0, &cell_len, &is_unalloc)) break; if((cell_len == 0) || ((cell_len & 0x00000007) != 0)) { regfi_add_message(file, REGFI_MSG_ERROR, "Bad cell length encountered" " while parsing unallocated cells at offset 0x%.8X.", hbin->file_off+curr_off); break; } /* for some reason the record_size of the last record in an hbin block can extend past the end of the block even though the record fits within the remaining space....aaarrrgggghhhhhh */ if(curr_off + cell_len >= hbin->block_size) cell_len = hbin->block_size - curr_off; if(is_unalloc) range_list_add(ret_val, hbin->file_off+curr_off, cell_len, NULL); curr_off = curr_off+cell_len; } } return ret_val; } /* From lib/time.c */ /**************************************************************************** Put a 8 byte filetime from a time_t This takes real GMT as input and converts to kludge-GMT ****************************************************************************/ void regfi_unix2nt_time(REGFI_NTTIME *nt, time_t t) { double d; if (t==0) { nt->low = 0; nt->high = 0; return; } if (t == TIME_T_MAX) { nt->low = 0xffffffff; nt->high = 0x7fffffff; return; } if (t == -1) { nt->low = 0xffffffff; nt->high = 0xffffffff; return; } /* this converts GMT to kludge-GMT */ /* XXX: This was removed due to difficult dependency requirements. * So far, times appear to be correct without this adjustment, but * that may be proven wrong with adequate testing. */ /* t -= TimeDiff(t) - get_serverzone(); */ d = (double)(t); d += TIME_FIXUP_CONSTANT; d *= 1.0e7; nt->high = (uint32_t)(d * (1.0/(4.0*(double)(1<<30)))); nt->low = (uint32_t)(d - ((double)nt->high)*4.0*(double)(1<<30)); } /**************************************************************************** Interpret an 8 byte "filetime" structure to a time_t It's originally in "100ns units since jan 1st 1601" An 8 byte value of 0xffffffffffffffff will be returned as (time_t)0. It appears to be kludge-GMT (at least for file listings). This means its the GMT you get by taking a localtime and adding the serverzone. This is NOT the same as GMT in some cases. This routine converts this to real GMT. ****************************************************************************/ time_t regfi_nt2unix_time(const REGFI_NTTIME* nt) { double d; time_t ret; /* The next two lines are a fix needed for the broken SCO compiler. JRA. */ time_t l_time_min = TIME_T_MIN; time_t l_time_max = TIME_T_MAX; if (nt->high == 0 || (nt->high == 0xffffffff && nt->low == 0xffffffff)) return(0); d = ((double)nt->high)*4.0*(double)(1<<30); d += (nt->low&0xFFF00000); d *= 1.0e-7; /* now adjust by 369 years to make the secs since 1970 */ d -= TIME_FIXUP_CONSTANT; if (d <= l_time_min) return (l_time_min); if (d >= l_time_max) return (l_time_max); ret = (time_t)(d+0.5); /* this takes us from kludge-GMT to real GMT */ /* XXX: This was removed due to difficult dependency requirements. * So far, times appear to be correct without this adjustment, but * that may be proven wrong with adequate testing. */ /* ret -= get_serverzone(); ret += LocTimeDiff(ret); */ return(ret); } /* End of stuff from lib/time.c */ reglookup-0.12.0/lib/talloc.c000066400000000000000000001121731135112127200157710ustar00rootroot00000000000000/* Samba Unix SMB/CIFS implementation. Samba trivial allocation library - new interface inspired by http://swapped.cc/halloc/ NOTE: Please read talloc_guide.txt for full documentation Copyright (C) Andrew Tridgell 2004 Copyright (C) Stefan Metzmacher 2006 Copyright (C) Timothy D. Morgan 2009 ** NOTE! The following LGPL license applies to the talloc library. ** This does NOT imply that all of reglookup is released under the LGPL This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see . */ /* $Id: talloc.c 147 2009-02-22 19:31:52Z tim $ */ #include "talloc.h" /* use this to force every realloc to change the pointer, to stress test code that might not cope */ #define ALWAYS_REALLOC 0 #define MAX_TALLOC_SIZE 0x10000000 #define TALLOC_MAGIC 0xe814ec70 #define TALLOC_FLAG_FREE 0x01 #define TALLOC_FLAG_LOOP 0x02 #define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */ #define TALLOC_FLAG_POOLMEM 0x08 /* This is allocated in a pool */ #define TALLOC_MAGIC_REFERENCE ((const char *)1) /* by default we abort when given a bad pointer (such as when talloc_free() is called on a pointer that came from malloc() */ #ifndef TALLOC_ABORT #define TALLOC_ABORT(reason) abort() #endif #ifndef discard_const_p #if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) # define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr))) #else # define discard_const_p(type, ptr) ((type *)(ptr)) #endif #endif /* these macros gain us a few percent of speed on gcc */ #if (__GNUC__ >= 3) /* the strange !! is to ensure that __builtin_expect() takes either 0 or 1 as its first argument */ #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #else #define likely(x) x #define unlikely(x) x #endif /* this null_context is only used if talloc_enable_leak_report() or talloc_enable_leak_report_full() is called, otherwise it remains NULL */ static void *null_context; static void *autofree_context; struct talloc_reference_handle { struct talloc_reference_handle *next, *prev; void *ptr; }; typedef int (*talloc_destructor_t)(void *); struct talloc_chunk { struct talloc_chunk *next, *prev; struct talloc_chunk *parent, *child; struct talloc_reference_handle *refs; talloc_destructor_t destructor; const char *name; size_t size; unsigned flags; /* * "pool" has dual use: * * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool" * marks the end of the currently allocated area. * * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool" * is a pointer to the struct talloc_chunk of the pool that it was * allocated from. This way children can quickly find the pool to chew * from. */ void *pool; }; /* 16 byte alignment seems to keep everyone happy */ #define TC_HDR_SIZE ((sizeof(struct talloc_chunk)+15)&~15) #define TC_PTR_FROM_CHUNK(tc) ((void *)(TC_HDR_SIZE + (char*)tc)) static void talloc_abort_double_free(void) { TALLOC_ABORT("Bad talloc magic value - double free"); } static void talloc_abort_unknown_value(void) { TALLOC_ABORT("Bad talloc magic value - unknown value"); } /* panic if we get a bad magic value */ static inline struct talloc_chunk *talloc_chunk_from_ptr(const void *ptr) { const char *pp = (const char *)ptr; struct talloc_chunk *tc = discard_const_p(struct talloc_chunk, pp - TC_HDR_SIZE); if (unlikely((tc->flags & (TALLOC_FLAG_FREE | ~0xF)) != TALLOC_MAGIC)) { if (tc->flags & TALLOC_FLAG_FREE) { talloc_abort_double_free(); } else { talloc_abort_unknown_value(); } } return tc; } /* hook into the front of the list */ #define _TLIST_ADD(list, p) \ do { \ if (!(list)) { \ (list) = (p); \ (p)->next = (p)->prev = NULL; \ } else { \ (list)->prev = (p); \ (p)->next = (list); \ (p)->prev = NULL; \ (list) = (p); \ }\ } while (0) /* remove an element from a list - element doesn't have to be in list. */ #define _TLIST_REMOVE(list, p) \ do { \ if ((p) == (list)) { \ (list) = (p)->next; \ if (list) (list)->prev = NULL; \ } else { \ if ((p)->prev) (p)->prev->next = (p)->next; \ if ((p)->next) (p)->next->prev = (p)->prev; \ } \ if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \ } while (0) /* return the parent chunk of a pointer */ static inline struct talloc_chunk *talloc_parent_chunk(const void *ptr) { struct talloc_chunk *tc; if (unlikely(ptr == NULL)) { return NULL; } tc = talloc_chunk_from_ptr(ptr); while (tc->prev) tc=tc->prev; return tc->parent; } void *talloc_parent(const void *ptr) { struct talloc_chunk *tc = talloc_parent_chunk(ptr); return tc? TC_PTR_FROM_CHUNK(tc) : NULL; } /* find parents name */ const char *talloc_parent_name(const void *ptr) { struct talloc_chunk *tc = talloc_parent_chunk(ptr); return tc? tc->name : NULL; } /* A pool carries an in-pool object count count in the first 16 bytes. bytes. This is done to support talloc_steal() to a parent outside of the pool. The count includes the pool itself, so a talloc_free() on a pool will only destroy the pool if the count has dropped to zero. A talloc_free() of a pool member will reduce the count, and eventually also call free(3) on the pool memory. The object count is not put into "struct talloc_chunk" because it is only relevant for talloc pools and the alignment to 16 bytes would increase the memory footprint of each talloc chunk by those 16 bytes. */ #define TALLOC_POOL_HDR_SIZE 16 static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc) { return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk)); } /* Allocate from a pool */ static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent, size_t size) { struct talloc_chunk *pool_ctx = NULL; size_t space_left; struct talloc_chunk *result; size_t chunk_size; if (parent == NULL) { return NULL; } if (parent->flags & TALLOC_FLAG_POOL) { pool_ctx = parent; } else if (parent->flags & TALLOC_FLAG_POOLMEM) { pool_ctx = (struct talloc_chunk *)parent->pool; } if (pool_ctx == NULL) { return NULL; } space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size) - ((char *)pool_ctx->pool); /* * Align size to 16 bytes */ chunk_size = ((size + 15) & ~15); if (space_left < chunk_size) { return NULL; } result = (struct talloc_chunk *)pool_ctx->pool; #if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED) VALGRIND_MAKE_MEM_UNDEFINED(result, size); #endif pool_ctx->pool = (void *)((char *)result + chunk_size); result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM; result->pool = pool_ctx; *talloc_pool_objectcount(pool_ctx) += 1; return result; } /* Allocate a bit of memory as a child of an existing pointer */ static inline void *__talloc(const void *context, size_t size) { struct talloc_chunk *tc = NULL; if (unlikely(context == NULL)) { context = null_context; } if (unlikely(size >= MAX_TALLOC_SIZE)) { return NULL; } if (context != NULL) { tc = talloc_alloc_pool(talloc_chunk_from_ptr(context), TC_HDR_SIZE+size); } if (tc == NULL) { tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size); if (unlikely(tc == NULL)) return NULL; tc->flags = TALLOC_MAGIC; tc->pool = NULL; } tc->size = size; tc->destructor = NULL; tc->child = NULL; tc->name = NULL; tc->refs = NULL; if (likely(context)) { struct talloc_chunk *parent = talloc_chunk_from_ptr(context); if (parent->child) { parent->child->parent = NULL; tc->next = parent->child; tc->next->prev = tc; } else { tc->next = NULL; } tc->parent = parent; tc->prev = NULL; parent->child = tc; } else { tc->next = tc->prev = tc->parent = NULL; } return TC_PTR_FROM_CHUNK(tc); } /* * Create a talloc pool */ void *talloc_pool(const void *context, size_t size) { void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE); struct talloc_chunk *tc; if (unlikely(result == NULL)) { return NULL; } tc = talloc_chunk_from_ptr(result); tc->flags |= TALLOC_FLAG_POOL; tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE; *talloc_pool_objectcount(tc) = 1; #if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size); #endif return result; } /* setup a destructor to be called on free of a pointer the destructor should return 0 on success, or -1 on failure. if the destructor fails then the free is failed, and the memory can be continued to be used */ void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); tc->destructor = destructor; } /* increase the reference count on a piece of memory. */ int talloc_increase_ref_count(const void *ptr) { if (unlikely(!talloc_reference(null_context, ptr))) { return -1; } return 0; } /* helper for talloc_reference() this is referenced by a function pointer and should not be inline */ static int talloc_reference_destructor(struct talloc_reference_handle *handle) { struct talloc_chunk *ptr_tc = talloc_chunk_from_ptr(handle->ptr); _TLIST_REMOVE(ptr_tc->refs, handle); return 0; } /* more efficient way to add a name to a pointer - the name must point to a true string constant */ static inline void _talloc_set_name_const(const void *ptr, const char *name) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); tc->name = name; } /* internal talloc_named_const() */ static inline void *_talloc_named_const(const void *context, size_t size, const char *name) { void *ptr; ptr = __talloc(context, size); if (unlikely(ptr == NULL)) { return NULL; } _talloc_set_name_const(ptr, name); return ptr; } /* make a secondary reference to a pointer, hanging off the given context. the pointer remains valid until both the original caller and this given context are freed. the major use for this is when two different structures need to reference the same underlying data, and you want to be able to free the two instances separately, and in either order */ void *_talloc_reference(const void *context, const void *ptr) { struct talloc_chunk *tc; struct talloc_reference_handle *handle; if (unlikely(ptr == NULL)) return NULL; tc = talloc_chunk_from_ptr(ptr); handle = (struct talloc_reference_handle *)_talloc_named_const(context, sizeof(struct talloc_reference_handle), TALLOC_MAGIC_REFERENCE); if (unlikely(handle == NULL)) return NULL; /* note that we hang the destructor off the handle, not the main context as that allows the caller to still setup their own destructor on the context if they want to */ talloc_set_destructor(handle, talloc_reference_destructor); handle->ptr = discard_const_p(void, ptr); _TLIST_ADD(tc->refs, handle); return handle->ptr; } /* internal talloc_free call */ static inline int _talloc_free(void *ptr) { struct talloc_chunk *tc; if (unlikely(ptr == NULL)) { return -1; } tc = talloc_chunk_from_ptr(ptr); if (unlikely(tc->refs)) { int is_child; /* check this is a reference from a child or grantchild * back to it's parent or grantparent * * in that case we need to remove the reference and * call another instance of talloc_free() on the current * pointer. */ is_child = talloc_is_parent(tc->refs, ptr); _talloc_free(tc->refs); if (is_child) { return _talloc_free(ptr); } return -1; } if (unlikely(tc->flags & TALLOC_FLAG_LOOP)) { /* we have a free loop - stop looping */ return 0; } if (unlikely(tc->destructor)) { talloc_destructor_t d = tc->destructor; if (d == (talloc_destructor_t)-1) { return -1; } tc->destructor = (talloc_destructor_t)-1; if (d(ptr) == -1) { tc->destructor = d; return -1; } tc->destructor = NULL; } if (tc->parent) { _TLIST_REMOVE(tc->parent->child, tc); if (tc->parent->child) { tc->parent->child->parent = tc->parent; } } else { if (tc->prev) tc->prev->next = tc->next; if (tc->next) tc->next->prev = tc->prev; } tc->flags |= TALLOC_FLAG_LOOP; while (tc->child) { /* we need to work out who will own an abandoned child if it cannot be freed. In priority order, the first choice is owner of any remaining reference to this pointer, the second choice is our parent, and the final choice is the null context. */ void *child = TC_PTR_FROM_CHUNK(tc->child); const void *new_parent = null_context; if (unlikely(tc->child->refs)) { struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); if (p) new_parent = TC_PTR_FROM_CHUNK(p); } if (unlikely(_talloc_free(child) == -1)) { if (new_parent == null_context) { struct talloc_chunk *p = talloc_parent_chunk(ptr); if (p) new_parent = TC_PTR_FROM_CHUNK(p); } talloc_steal(new_parent, child); } } tc->flags |= TALLOC_FLAG_FREE; if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) { struct talloc_chunk *pool; unsigned int *pool_object_count; pool = (tc->flags & TALLOC_FLAG_POOL) ? tc : (struct talloc_chunk *)tc->pool; pool_object_count = talloc_pool_objectcount(pool); if (*pool_object_count == 0) { TALLOC_ABORT("Pool object count zero!"); } *pool_object_count -= 1; if (*pool_object_count == 0) { free(pool); } } else { free(tc); } return 0; } /* move a lump of memory from one talloc context to another return the ptr on success, or NULL if it could not be transferred. passing NULL as ptr will always return NULL with no side effects. */ void *_talloc_steal(const void *new_ctx, const void *ptr) { struct talloc_chunk *tc, *new_tc; if (unlikely(!ptr)) { return NULL; } if (unlikely(new_ctx == NULL)) { new_ctx = null_context; } tc = talloc_chunk_from_ptr(ptr); if (unlikely(new_ctx == NULL)) { if (tc->parent) { _TLIST_REMOVE(tc->parent->child, tc); if (tc->parent->child) { tc->parent->child->parent = tc->parent; } } else { if (tc->prev) tc->prev->next = tc->next; if (tc->next) tc->next->prev = tc->prev; } tc->parent = tc->next = tc->prev = NULL; return discard_const_p(void, ptr); } new_tc = talloc_chunk_from_ptr(new_ctx); if (unlikely(tc == new_tc || tc->parent == new_tc)) { return discard_const_p(void, ptr); } if (tc->parent) { _TLIST_REMOVE(tc->parent->child, tc); if (tc->parent->child) { tc->parent->child->parent = tc->parent; } } else { if (tc->prev) tc->prev->next = tc->next; if (tc->next) tc->next->prev = tc->prev; } tc->parent = new_tc; if (new_tc->child) new_tc->child->parent = NULL; _TLIST_ADD(new_tc->child, tc); return discard_const_p(void, ptr); } /* remove a secondary reference to a pointer. This undo's what talloc_reference() has done. The context and pointer arguments must match those given to a talloc_reference() */ static inline int talloc_unreference(const void *context, const void *ptr) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); struct talloc_reference_handle *h; if (unlikely(context == NULL)) { context = null_context; } for (h=tc->refs;h;h=h->next) { struct talloc_chunk *p = talloc_parent_chunk(h); if (p == NULL) { if (context == NULL) break; } else if (TC_PTR_FROM_CHUNK(p) == context) { break; } } if (h == NULL) { return -1; } return _talloc_free(h); } /* remove a specific parent context from a pointer. This is a more controlled varient of talloc_free() */ int talloc_unlink(const void *context, void *ptr) { struct talloc_chunk *tc_p, *new_p; void *new_parent; if (ptr == NULL) { return -1; } if (context == NULL) { context = null_context; } if (talloc_unreference(context, ptr) == 0) { return 0; } if (context == NULL) { if (talloc_parent_chunk(ptr) != NULL) { return -1; } } else { if (talloc_chunk_from_ptr(context) != talloc_parent_chunk(ptr)) { return -1; } } tc_p = talloc_chunk_from_ptr(ptr); if (tc_p->refs == NULL) { return _talloc_free(ptr); } new_p = talloc_parent_chunk(tc_p->refs); if (new_p) { new_parent = TC_PTR_FROM_CHUNK(new_p); } else { new_parent = NULL; } if (talloc_unreference(new_parent, ptr) != 0) { return -1; } talloc_steal(new_parent, ptr); return 0; } /* add a name to an existing pointer - va_list version */ static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(2,0); static inline const char *talloc_set_name_v(const void *ptr, const char *fmt, va_list ap) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); tc->name = talloc_vasprintf(ptr, fmt, ap); if (likely(tc->name)) { _talloc_set_name_const(tc->name, ".name"); } return tc->name; } /* add a name to an existing pointer */ const char *talloc_set_name(const void *ptr, const char *fmt, ...) { const char *name; va_list ap; va_start(ap, fmt); name = talloc_set_name_v(ptr, fmt, ap); va_end(ap); return name; } /* create a named talloc pointer. Any talloc pointer can be named, and talloc_named() operates just like talloc() except that it allows you to name the pointer. */ void *talloc_named(const void *context, size_t size, const char *fmt, ...) { va_list ap; void *ptr; const char *name; ptr = __talloc(context, size); if (unlikely(ptr == NULL)) return NULL; va_start(ap, fmt); name = talloc_set_name_v(ptr, fmt, ap); va_end(ap); if (unlikely(name == NULL)) { _talloc_free(ptr); return NULL; } return ptr; } /* return the name of a talloc ptr, or "UNNAMED" */ const char *talloc_get_name(const void *ptr) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); if (unlikely(tc->name == TALLOC_MAGIC_REFERENCE)) { return ".reference"; } if (likely(tc->name)) { return tc->name; } return "UNNAMED"; } /* check if a pointer has the given name. If it does, return the pointer, otherwise return NULL */ void *talloc_check_name(const void *ptr, const char *name) { const char *pname; if (unlikely(ptr == NULL)) return NULL; pname = talloc_get_name(ptr); if (likely(pname == name || strcmp(pname, name) == 0)) { return discard_const_p(void, ptr); } return NULL; } /* this is for compatibility with older versions of talloc */ void *talloc_init(const char *fmt, ...) { va_list ap; void *ptr; const char *name; /* * samba3 expects talloc_report_depth_cb(NULL, ...) * reports all talloc'ed memory, so we need to enable * null_tracking */ talloc_enable_null_tracking(); ptr = __talloc(NULL, 0); if (unlikely(ptr == NULL)) return NULL; va_start(ap, fmt); name = talloc_set_name_v(ptr, fmt, ap); va_end(ap); if (unlikely(name == NULL)) { _talloc_free(ptr); return NULL; } return ptr; } /* this is a replacement for the Samba3 talloc_destroy_pool functionality. It should probably not be used in new code. It's in here to keep the talloc code consistent across Samba 3 and 4. */ void talloc_free_children(void *ptr) { struct talloc_chunk *tc; if (unlikely(ptr == NULL)) { return; } tc = talloc_chunk_from_ptr(ptr); while (tc->child) { /* we need to work out who will own an abandoned child if it cannot be freed. In priority order, the first choice is owner of any remaining reference to this pointer, the second choice is our parent, and the final choice is the null context. */ void *child = TC_PTR_FROM_CHUNK(tc->child); const void *new_parent = null_context; if (unlikely(tc->child->refs)) { struct talloc_chunk *p = talloc_parent_chunk(tc->child->refs); if (p) new_parent = TC_PTR_FROM_CHUNK(p); } if (unlikely(_talloc_free(child) == -1)) { if (new_parent == null_context) { struct talloc_chunk *p = talloc_parent_chunk(ptr); if (p) new_parent = TC_PTR_FROM_CHUNK(p); } talloc_steal(new_parent, child); } } if ((tc->flags & TALLOC_FLAG_POOL) && (*talloc_pool_objectcount(tc) == 1)) { tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE); #if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) VALGRIND_MAKE_MEM_NOACCESS( tc->pool, tc->size - TALLOC_POOL_HDR_SIZE); #endif } } /* Allocate a bit of memory as a child of an existing pointer */ void *_talloc(const void *context, size_t size) { return __talloc(context, size); } /* externally callable talloc_set_name_const() */ void talloc_set_name_const(const void *ptr, const char *name) { _talloc_set_name_const(ptr, name); } /* create a named talloc pointer. Any talloc pointer can be named, and talloc_named() operates just like talloc() except that it allows you to name the pointer. */ void *talloc_named_const(const void *context, size_t size, const char *name) { return _talloc_named_const(context, size, name); } /* free a talloc pointer. This also frees all child pointers of this pointer recursively return 0 if the memory is actually freed, otherwise -1. The memory will not be freed if the ref_count is > 1 or the destructor (if any) returns non-zero */ int talloc_free(void *ptr) { return _talloc_free(ptr); } /* A talloc version of realloc. The context argument is only used if ptr is NULL */ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *name) { struct talloc_chunk *tc; void *new_ptr; bool malloced = false; /* size zero is equivalent to free() */ if (unlikely(size == 0)) { _talloc_free(ptr); return NULL; } if (unlikely(size >= MAX_TALLOC_SIZE)) { return NULL; } /* realloc(NULL) is equivalent to malloc() */ if (ptr == NULL) { return _talloc_named_const(context, size, name); } tc = talloc_chunk_from_ptr(ptr); /* don't allow realloc on referenced pointers */ if (unlikely(tc->refs)) { return NULL; } /* don't shrink if we have less than 1k to gain */ if ((size < tc->size) && ((tc->size - size) < 1024)) { tc->size = size; return ptr; } /* by resetting magic we catch users of the old memory */ tc->flags |= TALLOC_FLAG_FREE; #if ALWAYS_REALLOC new_ptr = malloc(size + TC_HDR_SIZE); if (new_ptr) { memcpy(new_ptr, tc, tc->size + TC_HDR_SIZE); free(tc); } #else if (tc->flags & TALLOC_FLAG_POOLMEM) { new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE); *talloc_pool_objectcount((struct talloc_chunk *) (tc->pool)) -= 1; if (new_ptr == NULL) { new_ptr = malloc(TC_HDR_SIZE+size); malloced = true; } if (new_ptr) { memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE); } } else { new_ptr = realloc(tc, size + TC_HDR_SIZE); } #endif if (unlikely(!new_ptr)) { tc->flags &= ~TALLOC_FLAG_FREE; return NULL; } tc = (struct talloc_chunk *)new_ptr; tc->flags &= ~TALLOC_FLAG_FREE; if (malloced) { tc->flags &= ~TALLOC_FLAG_POOLMEM; } if (tc->parent) { tc->parent->child = tc; } if (tc->child) { tc->child->parent = tc; } if (tc->prev) { tc->prev->next = tc; } if (tc->next) { tc->next->prev = tc; } tc->size = size; _talloc_set_name_const(TC_PTR_FROM_CHUNK(tc), name); return TC_PTR_FROM_CHUNK(tc); } /* a wrapper around talloc_steal() for situations where you are moving a pointer between two structures, and want the old pointer to be set to NULL */ void *_talloc_move(const void *new_ctx, const void *_pptr) { const void **pptr = discard_const_p(const void *,_pptr); void *ret = _talloc_steal(new_ctx, *pptr); (*pptr) = NULL; return ret; } /* return the total size of a talloc pool (subtree) */ size_t talloc_total_size(const void *ptr) { size_t total = 0; struct talloc_chunk *c, *tc; if (ptr == NULL) { ptr = null_context; } if (ptr == NULL) { return 0; } tc = talloc_chunk_from_ptr(ptr); if (tc->flags & TALLOC_FLAG_LOOP) { return 0; } tc->flags |= TALLOC_FLAG_LOOP; total = tc->size; for (c=tc->child;c;c=c->next) { total += talloc_total_size(TC_PTR_FROM_CHUNK(c)); } tc->flags &= ~TALLOC_FLAG_LOOP; return total; } /* return the total number of blocks in a talloc pool (subtree) */ size_t talloc_total_blocks(const void *ptr) { size_t total = 0; struct talloc_chunk *c, *tc = talloc_chunk_from_ptr(ptr); if (tc->flags & TALLOC_FLAG_LOOP) { return 0; } tc->flags |= TALLOC_FLAG_LOOP; total++; for (c=tc->child;c;c=c->next) { total += talloc_total_blocks(TC_PTR_FROM_CHUNK(c)); } tc->flags &= ~TALLOC_FLAG_LOOP; return total; } /* return the number of external references to a pointer */ size_t talloc_reference_count(const void *ptr) { struct talloc_chunk *tc = talloc_chunk_from_ptr(ptr); struct talloc_reference_handle *h; size_t ret = 0; for (h=tc->refs;h;h=h->next) { ret++; } return ret; } /* report on memory usage by all children of a pointer, giving a full tree view */ void talloc_report_depth_cb(const void *ptr, int depth, int max_depth, void (*callback)(const void *ptr, int depth, int max_depth, int is_ref, void *private_data), void *private_data) { struct talloc_chunk *c, *tc; if (ptr == NULL) { ptr = null_context; } if (ptr == NULL) return; tc = talloc_chunk_from_ptr(ptr); if (tc->flags & TALLOC_FLAG_LOOP) { return; } callback(ptr, depth, max_depth, 0, private_data); if (max_depth >= 0 && depth >= max_depth) { return; } tc->flags |= TALLOC_FLAG_LOOP; for (c=tc->child;c;c=c->next) { if (c->name == TALLOC_MAGIC_REFERENCE) { struct talloc_reference_handle *h = (struct talloc_reference_handle *)TC_PTR_FROM_CHUNK(c); callback(h->ptr, depth + 1, max_depth, 1, private_data); } else { talloc_report_depth_cb(TC_PTR_FROM_CHUNK(c), depth + 1, max_depth, callback, private_data); } } tc->flags &= ~TALLOC_FLAG_LOOP; } static void talloc_report_depth_FILE_helper(const void *ptr, int depth, int max_depth, int is_ref, void *_f) { const char *name = talloc_get_name(ptr); FILE *f = (FILE *)_f; if (is_ref) { fprintf(f, "%*sreference to: %s\n", depth*4, "", name); return; } if (depth == 0) { fprintf(f,"%stalloc report on '%s' (total %6lu bytes in %3lu blocks)\n", (max_depth < 0 ? "full " :""), name, (unsigned long)talloc_total_size(ptr), (unsigned long)talloc_total_blocks(ptr)); return; } fprintf(f, "%*s%-30s contains %6lu bytes in %3lu blocks (ref %d) %p\n", depth*4, "", name, (unsigned long)talloc_total_size(ptr), (unsigned long)talloc_total_blocks(ptr), (int)talloc_reference_count(ptr), ptr); #if 0 fprintf(f, "content: "); if (talloc_total_size(ptr)) { int tot = talloc_total_size(ptr); int i; for (i = 0; i < tot; i++) { if ((((char *)ptr)[i] > 31) && (((char *)ptr)[i] < 126)) { fprintf(f, "%c", ((char *)ptr)[i]); } else { fprintf(f, "~%02x", ((char *)ptr)[i]); } } } fprintf(f, "\n"); #endif } /* report on memory usage by all children of a pointer, giving a full tree view */ void talloc_report_depth_file(const void *ptr, int depth, int max_depth, FILE *f) { talloc_report_depth_cb(ptr, depth, max_depth, talloc_report_depth_FILE_helper, f); fflush(f); } /* report on memory usage by all children of a pointer, giving a full tree view */ void talloc_report_full(const void *ptr, FILE *f) { talloc_report_depth_file(ptr, 0, -1, f); } /* report on memory usage by all children of a pointer */ void talloc_report(const void *ptr, FILE *f) { talloc_report_depth_file(ptr, 0, 1, f); } /* report on any memory hanging off the null context */ static void talloc_report_null(void) { if (talloc_total_size(null_context) != 0) { talloc_report(null_context, stderr); } } /* report on any memory hanging off the null context */ static void talloc_report_null_full(void) { if (talloc_total_size(null_context) != 0) { talloc_report_full(null_context, stderr); } } /* enable tracking of the NULL context */ void talloc_enable_null_tracking(void) { if (null_context == NULL) { null_context = _talloc_named_const(NULL, 0, "null_context"); } } /* disable tracking of the NULL context */ void talloc_disable_null_tracking(void) { _talloc_free(null_context); null_context = NULL; } /* enable leak reporting on exit */ void talloc_enable_leak_report(void) { talloc_enable_null_tracking(); atexit(talloc_report_null); } /* enable full leak reporting on exit */ void talloc_enable_leak_report_full(void) { talloc_enable_null_tracking(); atexit(talloc_report_null_full); } /* talloc and zero memory. */ void *_talloc_zero(const void *ctx, size_t size, const char *name) { void *p = _talloc_named_const(ctx, size, name); if (p) { memset(p, '\0', size); } return p; } /* memdup with a talloc. */ void *_talloc_memdup(const void *t, const void *p, size_t size, const char *name) { void *newp = _talloc_named_const(t, size, name); if (likely(newp)) { memcpy(newp, p, size); } return newp; } static inline char *__talloc_strlendup(const void *t, const char *p, size_t len) { char *ret; ret = (char *)__talloc(t, len + 1); if (unlikely(!ret)) return NULL; memcpy(ret, p, len); ret[len] = 0; _talloc_set_name_const(ret, ret); return ret; } /* strdup with a talloc */ char *talloc_strdup(const void *t, const char *p) { if (unlikely(!p)) return NULL; return __talloc_strlendup(t, p, strlen(p)); } /* strndup with a talloc */ char *talloc_strndup(const void *t, const char *p, size_t n) { if (unlikely(!p)) return NULL; return __talloc_strlendup(t, p, n); } static inline char *__talloc_strlendup_append(char *s, size_t slen, const char *a, size_t alen) { char *ret; ret = talloc_realloc(NULL, s, char, slen + alen + 1); if (unlikely(!ret)) return NULL; /* append the string and the trailing \0 */ memcpy(&ret[slen], a, alen); ret[slen+alen] = 0; _talloc_set_name_const(ret, ret); return ret; } /* * Appends at the end of the string. */ char *talloc_strdup_append(char *s, const char *a) { if (unlikely(!s)) { return talloc_strdup(NULL, a); } if (unlikely(!a)) { return s; } return __talloc_strlendup_append(s, strlen(s), a, strlen(a)); } /* * Appends at the end of the talloc'ed buffer, * not the end of the string. */ char *talloc_strdup_append_buffer(char *s, const char *a) { size_t slen; if (unlikely(!s)) { return talloc_strdup(NULL, a); } if (unlikely(!a)) { return s; } slen = talloc_get_size(s); if (likely(slen > 0)) { slen--; } return __talloc_strlendup_append(s, slen, a, strlen(a)); } /* * Appends at the end of the string. */ char *talloc_strndup_append(char *s, const char *a, size_t n) { if (unlikely(!s)) { return talloc_strdup(NULL, a); } if (unlikely(!a)) { return s; } return __talloc_strlendup_append(s, strlen(s), a, n); } /* * Appends at the end of the talloc'ed buffer, * not the end of the string. */ char *talloc_strndup_append_buffer(char *s, const char *a, size_t n) { size_t slen; if (unlikely(!s)) { return talloc_strdup(NULL, a); } if (unlikely(!a)) { return s; } slen = talloc_get_size(s); if (likely(slen > 0)) { slen--; } return __talloc_strlendup_append(s, slen, a, n); } char *talloc_vasprintf(const void *t, const char *fmt, va_list ap) { int len; char *ret; va_list ap2; char c; /* this call looks strange, but it makes it work on older solaris boxes */ va_copy(ap2, ap); len = vsnprintf(&c, 1, fmt, ap2); va_end(ap2); if (unlikely(len < 0)) { return NULL; } ret = (char *)__talloc(t, len+1); if (unlikely(!ret)) return NULL; va_copy(ap2, ap); vsnprintf(ret, len+1, fmt, ap2); va_end(ap2); _talloc_set_name_const(ret, ret); return ret; } /* Perform string formatting, and return a pointer to newly allocated memory holding the result, inside a memory pool. */ char *talloc_asprintf(const void *t, const char *fmt, ...) { va_list ap; char *ret; va_start(ap, fmt); ret = talloc_vasprintf(t, fmt, ap); va_end(ap); return ret; } static inline char *__talloc_vaslenprintf_append(char *s, size_t slen, const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0); static inline char *__talloc_vaslenprintf_append(char *s, size_t slen, const char *fmt, va_list ap) { ssize_t alen; va_list ap2; char c; va_copy(ap2, ap); alen = vsnprintf(&c, 1, fmt, ap2); va_end(ap2); if (alen <= 0) { /* Either the vsnprintf failed or the format resulted in * no characters being formatted. In the former case, we * ought to return NULL, in the latter we ought to return * the original string. Most current callers of this * function expect it to never return NULL. */ return s; } s = talloc_realloc(NULL, s, char, slen + alen + 1); if (!s) return NULL; va_copy(ap2, ap); vsnprintf(s + slen, alen + 1, fmt, ap2); va_end(ap2); _talloc_set_name_const(s, s); return s; } /** * Realloc @p s to append the formatted result of @p fmt and @p ap, * and return @p s, which may have moved. Good for gradually * accumulating output into a string buffer. Appends at the end * of the string. **/ char *talloc_vasprintf_append(char *s, const char *fmt, va_list ap) { if (unlikely(!s)) { return talloc_vasprintf(NULL, fmt, ap); } return __talloc_vaslenprintf_append(s, strlen(s), fmt, ap); } /** * Realloc @p s to append the formatted result of @p fmt and @p ap, * and return @p s, which may have moved. Always appends at the * end of the talloc'ed buffer, not the end of the string. **/ char *talloc_vasprintf_append_buffer(char *s, const char *fmt, va_list ap) { size_t slen; if (unlikely(!s)) { return talloc_vasprintf(NULL, fmt, ap); } slen = talloc_get_size(s); if (likely(slen > 0)) { slen--; } return __talloc_vaslenprintf_append(s, slen, fmt, ap); } /* Realloc @p s to append the formatted result of @p fmt and return @p s, which may have moved. Good for gradually accumulating output into a string buffer. */ char *talloc_asprintf_append(char *s, const char *fmt, ...) { va_list ap; va_start(ap, fmt); s = talloc_vasprintf_append(s, fmt, ap); va_end(ap); return s; } /* Realloc @p s to append the formatted result of @p fmt and return @p s, which may have moved. Good for gradually accumulating output into a buffer. */ char *talloc_asprintf_append_buffer(char *s, const char *fmt, ...) { va_list ap; va_start(ap, fmt); s = talloc_vasprintf_append_buffer(s, fmt, ap); va_end(ap); return s; } /* alloc an array, checking for integer overflow in the array size */ void *_talloc_array(const void *ctx, size_t el_size, unsigned count, const char *name) { if (count >= MAX_TALLOC_SIZE/el_size) { return NULL; } return _talloc_named_const(ctx, el_size * count, name); } /* alloc an zero array, checking for integer overflow in the array size */ void *_talloc_zero_array(const void *ctx, size_t el_size, unsigned count, const char *name) { if (count >= MAX_TALLOC_SIZE/el_size) { return NULL; } return _talloc_zero(ctx, el_size * count, name); } /* realloc an array, checking for integer overflow in the array size */ void *_talloc_realloc_array(const void *ctx, void *ptr, size_t el_size, unsigned count, const char *name) { if (count >= MAX_TALLOC_SIZE/el_size) { return NULL; } return _talloc_realloc(ctx, ptr, el_size * count, name); } /* a function version of talloc_realloc(), so it can be passed as a function pointer to libraries that want a realloc function (a realloc function encapsulates all the basic capabilities of an allocation library, which is why this is useful) */ void *talloc_realloc_fn(const void *context, void *ptr, size_t size) { return _talloc_realloc(context, ptr, size, NULL); } static int talloc_autofree_destructor(void *ptr) { autofree_context = NULL; return 0; } static void talloc_autofree(void) { _talloc_free(autofree_context); } /* return a context which will be auto-freed on exit this is useful for reducing the noise in leak reports */ void *talloc_autofree_context(void) { if (autofree_context == NULL) { autofree_context = _talloc_named_const(NULL, 0, "autofree_context"); talloc_set_destructor(autofree_context, talloc_autofree_destructor); atexit(talloc_autofree); } return autofree_context; } size_t talloc_get_size(const void *context) { struct talloc_chunk *tc; if (context == NULL) return 0; tc = talloc_chunk_from_ptr(context); return tc->size; } /* find a parent of this context that has the given name, if any */ void *talloc_find_parent_byname(const void *context, const char *name) { struct talloc_chunk *tc; if (context == NULL) { return NULL; } tc = talloc_chunk_from_ptr(context); while (tc) { if (tc->name && strcmp(tc->name, name) == 0) { return TC_PTR_FROM_CHUNK(tc); } while (tc && tc->prev) tc = tc->prev; if (tc) { tc = tc->parent; } } return NULL; } /* show the parentage of a context */ void talloc_show_parents(const void *context, FILE *file) { struct talloc_chunk *tc; if (context == NULL) { fprintf(file, "talloc no parents for NULL\n"); return; } tc = talloc_chunk_from_ptr(context); fprintf(file, "talloc parents of '%s'\n", talloc_get_name(context)); while (tc) { fprintf(file, "\t'%s'\n", talloc_get_name(TC_PTR_FROM_CHUNK(tc))); while (tc && tc->prev) tc = tc->prev; if (tc) { tc = tc->parent; } } fflush(file); } /* return 1 if ptr is a parent of context */ int talloc_is_parent(const void *context, const void *ptr) { struct talloc_chunk *tc; if (context == NULL) { return 0; } tc = talloc_chunk_from_ptr(context); while (tc) { if (TC_PTR_FROM_CHUNK(tc) == ptr) return 1; while (tc && tc->prev) tc = tc->prev; if (tc) { tc = tc->parent; } } return 0; } reglookup-0.12.0/lib/void_stack.c000066400000000000000000000065371135112127200166470ustar00rootroot00000000000000/* * Copyright (C) 2005,2007,2009-2010 Timothy D. Morgan * * 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; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: void_stack.c 169 2010-03-03 19:24:58Z tim $ */ /** * @file */ #include "void_stack.h" void_stack* void_stack_new(unsigned short max_size) { void_stack* ret_val = talloc(NULL, void_stack); if (ret_val != NULL) { memset(ret_val, 0, sizeof(*ret_val)); ret_val->elements = talloc_array(ret_val, void*, max_size); if (ret_val->elements == NULL) { talloc_free(ret_val); ret_val = NULL; } else { memset(ret_val->elements, 0, max_size*sizeof(void*)); ret_val->max_size = max_size; ret_val->top = 0; } } return ret_val; } void_stack* void_stack_copy(const void_stack* v) { unsigned int i; void_stack* ret_val; if(v == NULL) return NULL; ret_val = void_stack_new(v->max_size); if(ret_val == NULL) return NULL; for(i = 0; i < v->top; i++) ret_val->elements[i] = v->elements[i]; ret_val->top = v->top; return ret_val; } void_stack* void_stack_copy_reverse(const void_stack* v) { unsigned int i; void_stack* ret_val; if(v == NULL) return NULL; ret_val = void_stack_new(v->max_size); if(ret_val == NULL) return NULL; for(i = 0; i < v->top; i++) ret_val->elements[i] = v->elements[v->top-i-1]; ret_val->top = v->top; return ret_val; } void void_stack_free(void_stack* stack) { talloc_free(stack); } void void_stack_free_deep(void_stack* stack) { unsigned short i; for(i=0; i < stack->top; i++) free(stack->elements[i]); talloc_free(stack); } unsigned short void_stack_size(const void_stack* stack) { return stack->top; } void* void_stack_pop(void_stack* stack) { void* ret_val = NULL; if(stack->top > 0) { ret_val = stack->elements[--(stack->top)]; stack->elements[stack->top] = NULL; } return ret_val; } bool void_stack_push(void_stack* stack, void* e) { if(stack->top < stack->max_size) { stack->elements[stack->top++] = e; return true; } else return false; } const void* void_stack_cur(const void_stack* stack) { void* ret_val = NULL; if(stack->top > 0) ret_val = stack->elements[stack->top-1]; return ret_val; } void_stack_iterator* void_stack_iterator_new(const void_stack* stack) { void_stack_iterator* ret_val = NULL; if(stack != NULL) { ret_val = talloc(stack, void_stack_iterator); if (ret_val != NULL) { ret_val->stack = stack; ret_val->cur = 0; } } return ret_val; } void void_stack_iterator_free(void_stack_iterator* iter) { talloc_free(iter); } const void* void_stack_iterator_next(void_stack_iterator* iter) { if(iter->cur < iter->stack->top) return iter->stack->elements[iter->cur++]; else return NULL; } reglookup-0.12.0/lib/winsec.c000066400000000000000000000336401135112127200160040ustar00rootroot00000000000000/* * * Copyright (C) 2005-2006,2009-2010 Timothy D. Morgan * Copyright (C) 1992-2005 Samba development team * (see individual files under Subversion for details.) * * 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; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: winsec.c 169 2010-03-03 19:24:58Z tim $ */ /** @file */ #include "winsec.h" /****************************************************************************** * Non-talloc() interface for parsing a descriptor. ******************************************************************************/ WINSEC_DESC* winsec_parse_descriptor(const uint8_t* buf, uint32_t buf_len) { return winsec_parse_desc(NULL, buf, buf_len); } /****************************************************************************** * Free a descriptor. Not needed if using talloc and a parent context is freed. ******************************************************************************/ void winsec_free_descriptor(WINSEC_DESC* desc) { talloc_free(desc); } /****************************************************************************** * Parses a WINSEC_DESC structure and substructures. ******************************************************************************/ WINSEC_DESC* winsec_parse_desc(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len) { WINSEC_DESC* ret_val; if (buf == NULL || buf_len < WINSEC_DESC_HEADER_SIZE) return NULL; if((ret_val = talloc(talloc_ctx, WINSEC_DESC)) == NULL) return NULL; ret_val->revision = buf[0]; ret_val->sbz1 = buf[1]; ret_val->control = SVAL(buf, 0x2); if(!(ret_val->control & WINSEC_DESC_SELF_RELATIVE)) fprintf(stderr, "DEBUG: NOT self-relative!\n"); ret_val->off_owner_sid = IVAL(buf, 0x4); ret_val->off_grp_sid = IVAL(buf, 0x8); ret_val->off_sacl = IVAL(buf, 0xC); ret_val->off_dacl = IVAL(buf, 0x10); /* A basic sanity check to ensure our offsets are within our buffer. * Additional length checking is done in secondary parsing functions. */ if((ret_val->off_owner_sid >= buf_len) || (ret_val->off_grp_sid >= buf_len) || (ret_val->off_sacl >= buf_len) || (ret_val->off_dacl >= buf_len)) { talloc_free(ret_val); return NULL; } if(ret_val->off_owner_sid == 0) ret_val->owner_sid = NULL; else { ret_val->owner_sid = winsec_parse_dom_sid(ret_val, buf + ret_val->off_owner_sid, buf_len - ret_val->off_owner_sid); if(ret_val->owner_sid == NULL) { talloc_free(ret_val); return NULL; } } if(ret_val->off_grp_sid == 0) ret_val->grp_sid = NULL; else { ret_val->grp_sid = winsec_parse_dom_sid(ret_val, buf + ret_val->off_grp_sid, buf_len - ret_val->off_grp_sid); if(ret_val->grp_sid == NULL) { talloc_free(ret_val); return NULL; } } if((ret_val->control & WINSEC_DESC_SACL_PRESENT) && ret_val->off_sacl) { ret_val->sacl = winsec_parse_acl(ret_val, buf + ret_val->off_sacl, buf_len - ret_val->off_sacl); if(ret_val->sacl == NULL) { talloc_free(ret_val); return NULL; } } else ret_val->sacl = NULL; if((ret_val->control & WINSEC_DESC_DACL_PRESENT) && ret_val->off_dacl != 0) { ret_val->dacl = winsec_parse_acl(ret_val, buf + ret_val->off_dacl, buf_len - ret_val->off_dacl); if(ret_val->dacl == NULL) { talloc_free(ret_val); return NULL; } } else ret_val->dacl = NULL; return ret_val; } /****************************************************************************** * Parses a WINSEC_ACL structure and all substructures. ******************************************************************************/ WINSEC_ACL* winsec_parse_acl(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len) { uint32_t i, offset; WINSEC_ACL* ret_val; /* * Note that the size is always a multiple of 4 bytes due to the * nature of the data structure. */ if (buf == NULL || buf_len < 8) return NULL; if((ret_val = talloc(talloc_ctx, WINSEC_ACL)) == NULL) return NULL; ret_val->revision = SVAL(buf, 0x0); ret_val->size = SVAL(buf, 0x2); ret_val->num_aces = IVAL(buf, 0x4); /* The num_aces can be at most around 4k because anything greater * wouldn't fit in the 16 bit size even if every ace was as small as * possible. */ if((ret_val->size > buf_len) || (ret_val->num_aces > 4095)) { talloc_free(ret_val); return NULL; } /* Even if the num_aces is zero, allocate memory as there's a difference * between a non-present DACL (allow all access) and a DACL with no ACE's * (allow no access). */ if((ret_val->aces = talloc_array(ret_val, WINSEC_ACE*, ret_val->num_aces+1)) == NULL) { talloc_free(ret_val); return NULL; } offset = 8; for(i=0; i < ret_val->num_aces; i++) { ret_val->aces[i] = winsec_parse_ace(ret_val->aces, buf+offset, buf_len-offset); if(ret_val->aces[i] == NULL) { talloc_free(ret_val); return NULL; } offset += ret_val->aces[i]->size; if(offset > buf_len) { talloc_free(ret_val); return NULL; } } ret_val->aces[ret_val->num_aces] = NULL; return ret_val; } /****************************************************************************** * Parses a WINSEC_ACE structure and all substructures. ******************************************************************************/ WINSEC_ACE* winsec_parse_ace(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len) { uint32_t offset; WINSEC_ACE* ret_val; if(buf == NULL || buf_len < WINSEC_ACE_MIN_SIZE) return NULL; if((ret_val = talloc(talloc_ctx, WINSEC_ACE)) == NULL) return NULL; ret_val->type = buf[0]; ret_val->flags = buf[1]; ret_val->size = SVAL(buf, 0x2); ret_val->access_mask = IVAL(buf, 0x4); offset = 0x8; /* check whether object access is present */ if (winsec_ace_object(ret_val->type)) { ret_val->obj_flags = IVAL(buf, offset); offset += 4; if(ret_val->obj_flags & WINSEC_ACE_OBJECT_PRESENT) { ret_val->obj_guid = winsec_parse_uuid(ret_val, buf+offset, buf_len-offset); if(ret_val->obj_guid == NULL) { talloc_free(ret_val); return NULL; } offset += sizeof(WINSEC_UUID); } else ret_val->obj_guid = NULL; if(ret_val->obj_flags & WINSEC_ACE_OBJECT_INHERITED_PRESENT) { ret_val->inh_guid = winsec_parse_uuid(ret_val, buf+offset, buf_len-offset); if(ret_val->inh_guid == NULL) { talloc_free(ret_val); return NULL; } offset += sizeof(WINSEC_UUID); } else ret_val->inh_guid = NULL; } ret_val->trustee = winsec_parse_dom_sid(ret_val, buf+offset, buf_len-offset); if(ret_val->trustee == NULL) { talloc_free(ret_val); return NULL; } return ret_val; } /****************************************************************************** * Parses a WINSEC_DOM_SID structure. ******************************************************************************/ WINSEC_DOM_SID* winsec_parse_dom_sid(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len) { uint32_t i; WINSEC_DOM_SID* ret_val; if(buf == NULL || buf_len < 8) return NULL; /* if((ret_val = (WINSEC_DOM_SID*)zalloc(sizeof(WINSEC_DOM_SID))) == NULL)*/ if((ret_val = talloc(talloc_ctx, WINSEC_DOM_SID)) == NULL) return NULL; ret_val->sid_rev_num = buf[0]; ret_val->num_auths = buf[1]; memcpy(ret_val->id_auth, buf+2, 6); /* XXX: should really issue a warning here... */ if (ret_val->num_auths > WINSEC_MAX_SUBAUTHS) ret_val->num_auths = WINSEC_MAX_SUBAUTHS; if(buf_len < ret_val->num_auths*sizeof(uint32_t)+8) { talloc_free(ret_val); return NULL; } for(i=0; i < ret_val->num_auths; i++) ret_val->sub_auths[i] = IVAL(buf, 8+i*sizeof(uint32_t)); return ret_val; } /****************************************************************************** * Parses a WINSEC_UUID struct. ******************************************************************************/ WINSEC_UUID* winsec_parse_uuid(void* talloc_ctx, const uint8_t* buf, uint32_t buf_len) { WINSEC_UUID* ret_val; if(buf == NULL || buf_len < sizeof(WINSEC_UUID)) return false; if((ret_val = talloc(talloc_ctx, WINSEC_UUID)) == NULL) return NULL; ret_val->time_low = IVAL(buf, 0x0); ret_val->time_mid = SVAL(buf, 0x4); ret_val->time_hi_and_version = SVAL(buf, 0x6); memcpy(ret_val->clock_seq, buf+0x8, 2); memcpy(ret_val->node, buf+0xB, 6); return ret_val; } /****************************************************************************** * Calculates the size of a SID. ******************************************************************************/ size_t winsec_sid_size(const WINSEC_DOM_SID* sid) { if (sid == NULL) return 0; return sid->num_auths * sizeof(uint32_t) + 8; } /****************************************************************************** * Compare the auth portion of two SIDs. ******************************************************************************/ int winsec_sid_compare_auth(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2) { int i; if (sid1 == sid2) return 0; if (!sid1) return -1; if (!sid2) return 1; if (sid1->sid_rev_num != sid2->sid_rev_num) return sid1->sid_rev_num - sid2->sid_rev_num; for (i = 0; i < 6; i++) if (sid1->id_auth[i] != sid2->id_auth[i]) return sid1->id_auth[i] - sid2->id_auth[i]; return 0; } /****************************************************************************** * Compare two SIDs. ******************************************************************************/ int winsec_sid_compare(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2) { int i; if (sid1 == sid2) return 0; if (!sid1) return -1; if (!sid2) return 1; /* Compare most likely different rids, first: i.e start at end */ if (sid1->num_auths != sid2->num_auths) return sid1->num_auths - sid2->num_auths; for (i = sid1->num_auths-1; i >= 0; --i) if (sid1->sub_auths[i] != sid2->sub_auths[i]) return sid1->sub_auths[i] - sid2->sub_auths[i]; return winsec_sid_compare_auth(sid1, sid2); } /****************************************************************************** * Compare two SIDs. ******************************************************************************/ bool winsec_sid_equal(const WINSEC_DOM_SID* sid1, const WINSEC_DOM_SID* sid2) { return winsec_sid_compare(sid1, sid2) == 0; } /****************************************************************************** * Compares two WINSEC_DESC structures. ******************************************************************************/ bool winsec_desc_equal(WINSEC_DESC* s1, WINSEC_DESC* s2) { /* Trivial cases */ if (!s1 && !s2) return true; if (!s1 || !s2) return false; /* Check top level stuff */ if (s1->revision != s2->revision) return false; if (s1->control != s2->control) return false; /* Check owner and group */ if (!winsec_sid_equal(s1->owner_sid, s2->owner_sid)) return false; if (!winsec_sid_equal(s1->grp_sid, s2->grp_sid)) return false; /* Check ACLs present in one but not the other */ if ((s1->dacl && !s2->dacl) || (!s1->dacl && s2->dacl) || (s1->sacl && !s2->sacl) || (!s1->sacl && s2->sacl)) { return false; } /* Sigh - we have to do it the hard way by iterating over all the ACEs in the ACLs */ if(!winsec_acl_equal(s1->dacl, s2->dacl) || !winsec_acl_equal(s1->sacl, s2->sacl)) return false; return true; } /****************************************************************************** * Compares two WINSEC_ACL structures. ******************************************************************************/ bool winsec_acl_equal(WINSEC_ACL* s1, WINSEC_ACL* s2) { unsigned int i, j; /* Trivial cases */ if (!s1 && !s2) return true; if (!s1 || !s2) return false; /* Check top level stuff */ if (s1->revision != s2->revision) return false; if (s1->num_aces != s2->num_aces) return false; /* The ACEs could be in any order so check each ACE in s1 against each ACE in s2. */ for (i = 0; i < s1->num_aces; i++) { bool found = false; for (j = 0; j < s2->num_aces; j++) { if (winsec_ace_equal(s1->aces[i], s2->aces[j])) { found = true; break; } } if (!found) return false; } return true; } /****************************************************************************** * Compares two WINSEC_ACE structures. ******************************************************************************/ bool winsec_ace_equal(WINSEC_ACE* s1, WINSEC_ACE* s2) { /* Trivial cases */ if (!s1 && !s2) return true; if (!s1 || !s2) return false; /* Check top level stuff */ if (s1->type != s2->type || s1->flags != s2->flags || s1->access_mask != s2->access_mask) { return false; } /* Check SID */ if (!winsec_sid_equal(s1->trustee, s2->trustee)) return false; return true; } /****************************************************************************** * Check if ACE has OBJECT type. ******************************************************************************/ bool winsec_ace_object(uint8_t type) { if (type == WINSEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT || type == WINSEC_ACE_TYPE_ACCESS_DENIED_OBJECT || type == WINSEC_ACE_TYPE_SYSTEM_AUDIT_OBJECT || type == WINSEC_ACE_TYPE_SYSTEM_ALARM_OBJECT) { return true; } return false; } reglookup-0.12.0/src/000077500000000000000000000000001135112127200143635ustar00rootroot00000000000000reglookup-0.12.0/src/Makefile000066400000000000000000000014351135112127200160260ustar00rootroot00000000000000# $Id: Makefile 143 2009-02-13 03:24:27Z tim $ ################################################################################ REGLOOKUP=$(BUILD_BIN)/reglookup$(BIN_EXT) REGLOOKUP_RECOVER=$(BUILD_BIN)/reglookup-recover$(BIN_EXT) OBJ=$(wildcard ../lib/*.o) FILES=$(REGLOOKUP) $(REGLOOKUP_RECOVER) all: $(FILES) $(REGLOOKUP): reglookup.o $(OBJ) $(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup.o $(OBJ) $(EXTRA_OBJ) $(REGLOOKUP_RECOVER): reglookup-recover.o $(OBJ) $(CC) $(CFLAGS) $(OPTS) $(LIB) -o $@ reglookup-recover.o $(OBJ) $(EXTRA_OBJ) reglookup.o: reglookup.c $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ reglookup.c reglookup-recover.o: reglookup-recover.c $(CC) $(CFLAGS) $(OPTS) $(INC) -c -o $@ reglookup-recover.c install: install -m 0755 $(FILES) $(BIN_PREFIX) clean: rm -f *.o reglookup-0.12.0/src/common.c000066400000000000000000000213731135112127200160250ustar00rootroot00000000000000/* * This file stores code common to the command line tools. * XXX: This should be converted to a proper library. * * Copyright (C) 2005-2008 Timothy D. Morgan * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com * * 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; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: common.c 172 2010-03-08 03:04:34Z tim $ */ #include iconv_t conv_desc; const char* key_special_chars = ",\"\\/"; const char* subfield_special_chars = ",\"\\|"; const char* common_special_chars = ",\"\\"; #define REGLOOKUP_VERSION "0.12.0" #define REGLOOKUP_EXIT_OK 0 #define REGLOOKUP_EXIT_OSERR 71 #define REGLOOKUP_EXIT_USAGE 64 #define REGLOOKUP_EXIT_DATAERR 65 #define REGLOOKUP_EXIT_NOINPUT 66 void bailOut(int code, char* message) { fprintf(stderr, message); exit(code); } void printMsgs(REGFI_FILE* f) { char* msgs = regfi_get_messages(f); if(msgs != NULL) { fprintf(stderr, "%s", msgs); free(msgs); } } void clearMsgs(REGFI_FILE* f) { char* msgs = regfi_get_messages(f); if(msgs != NULL) free(msgs); } /* Returns a newly malloc()ed string which contains original buffer, * except for non-printable or special characters are quoted in hex * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted * character. A null terminator is added, since only ascii, not binary, * is returned. */ static char* quote_buffer(const unsigned char* str, unsigned int len, const char* special) { unsigned int i, added_len; unsigned int num_written = 0; unsigned int buf_len = sizeof(char)*(len+1); char* ret_val = NULL; char* tmp_buf; if(buf_len > 0) ret_val = malloc(buf_len); if(ret_val == NULL) return NULL; for(i=0; i (len*4+1)) buf_len = len*4+1; else { if (added_len < 5) buf_len += 5; else buf_len += added_len; } tmp_buf = realloc(ret_val, buf_len); if(tmp_buf == NULL) { free(ret_val); return NULL; } ret_val = tmp_buf; } if(str[i] < 32 || str[i] > 126 || strchr(special, str[i]) != NULL) { num_written += snprintf(ret_val + num_written, buf_len - num_written, "\\x%.2X", str[i]); } else ret_val[num_written++] = str[i]; } ret_val[num_written] = '\0'; return ret_val; } /* Returns a newly malloc()ed string which contains original string, * except for non-printable or special characters are quoted in hex * with the syntax '\xQQ' where QQ is the hex ascii value of the quoted * character. */ static char* quote_string(const char* str, const char* special) { unsigned int len; if(str == NULL) return NULL; len = strlen(str); return quote_buffer((const unsigned char*)str, len, special); } /* * Convert a data value to a string for display. Returns NULL on error, * and the string to display if there is no error, or a non-fatal * error. On any error (fatal or non-fatal) occurs, (*error_msg) will * be set to a newly allocated string, containing an error message. If * a memory allocation failure occurs while generating the error * message, both the return value and (*error_msg) will be NULL. It * is the responsibility of the caller to free both a non-NULL return * value, and a non-NULL (*error_msg). */ static char* data_to_ascii(REGFI_DATA* data, char** error_msg) { char* ret_val; char* cur_quoted; char* tmp_ptr; char* delim; uint32_t ret_val_left, i, tmp_len; if(data == NULL || data->size == 0) { *error_msg = (char*)malloc(37); if(*error_msg == NULL) return NULL; strcpy(*error_msg, "Data pointer was NULL or size was 0."); return NULL; } *error_msg = NULL; if(data->interpreted_size == 0) { *error_msg = (char*)malloc(51); if(*error_msg == NULL) return NULL; strcpy(*error_msg, "Data could not be interpreted, quoting raw buffer."); return quote_buffer(data->raw, data->size, subfield_special_chars); } switch (data->type) { case REG_SZ: ret_val = quote_string((char*)data->interpreted.string, common_special_chars); if(ret_val == NULL && (*error_msg = (char*)malloc(49)) != NULL) strcpy(*error_msg, "Buffer could not be quoted due to unknown error."); return ret_val; break; case REG_EXPAND_SZ: ret_val = quote_string((char*)data->interpreted.expand_string, common_special_chars); if(ret_val == NULL && (*error_msg = (char*)malloc(49)) != NULL) strcpy(*error_msg, "Buffer could not be quoted due to unknown error."); return ret_val; break; case REG_LINK: ret_val = quote_string((char*)data->interpreted.link, common_special_chars); if(ret_val == NULL && (*error_msg = (char*)malloc(49)) != NULL) strcpy(*error_msg, "Buffer could not be quoted due to unknown error."); return ret_val; break; case REG_DWORD: ret_val = malloc(sizeof(char)*(8+2+1)); if(ret_val == NULL) return NULL; sprintf(ret_val, "0x%.8X", data->interpreted.dword); return ret_val; break; case REG_DWORD_BE: ret_val = malloc(sizeof(char)*(8+2+1)); if(ret_val == NULL) return NULL; sprintf(ret_val, "0x%.8X", data->interpreted.dword_be); return ret_val; break; case REG_QWORD: ret_val = malloc(sizeof(char)*(16+2+1)); if(ret_val == NULL) return NULL; sprintf(ret_val, "0x%.16llX", (long long unsigned int)data->interpreted.qword); return ret_val; break; case REG_MULTI_SZ: ret_val_left = data->interpreted_size*4+1; ret_val = malloc(ret_val_left); if(ret_val == NULL) return NULL; tmp_ptr = ret_val; tmp_ptr[0] = '\0'; delim = ""; for(i=0; data->interpreted.multiple_string[i] != NULL; i++) { cur_quoted = quote_string((char*)data->interpreted.multiple_string[i], subfield_special_chars); if(cur_quoted != NULL && cur_quoted[0] != '\0') { tmp_len = snprintf(tmp_ptr, ret_val_left, "%s%s",delim, cur_quoted); tmp_ptr += tmp_len; ret_val_left -= tmp_len; free(cur_quoted); } delim = "|"; } return ret_val; break; case REG_NONE: return quote_buffer(data->interpreted.none, data->interpreted_size, common_special_chars); break; case REG_RESOURCE_LIST: return quote_buffer(data->interpreted.resource_list, data->interpreted_size, common_special_chars); break; case REG_FULL_RESOURCE_DESCRIPTOR: return quote_buffer(data->interpreted.full_resource_descriptor, data->interpreted_size, common_special_chars); break; case REG_RESOURCE_REQUIREMENTS_LIST: return quote_buffer(data->interpreted.resource_requirements_list, data->interpreted_size, common_special_chars); break; case REG_BINARY: return quote_buffer(data->interpreted.binary, data->interpreted_size, common_special_chars); break; default: /* This shouldn't happen, since the regfi routines won't interpret * unknown types, but just as a safety measure against library changes... */ *error_msg = (char*)malloc(65); if(*error_msg == NULL) return NULL; sprintf(*error_msg, "Unrecognized registry data type (0x%.8X); quoting as binary.", data->type); return quote_buffer(data->raw, data->size, common_special_chars); } return NULL; } static char* get_quoted_keyname(const REGFI_NK_REC* nk) { char* ret_val; if(nk->keyname == NULL) ret_val = quote_buffer(nk->keyname_raw, nk->name_length, key_special_chars); else ret_val = quote_string(nk->keyname, key_special_chars); return ret_val; } static char* get_quoted_valuename(const REGFI_VK_REC* vk) { char* ret_val; if(vk->valuename == NULL) ret_val = quote_buffer(vk->valuename_raw, vk->name_length, key_special_chars); else ret_val = quote_string(vk->valuename, key_special_chars); return ret_val; } reglookup-0.12.0/src/reglookup-recover.c000066400000000000000000000631231135112127200202060ustar00rootroot00000000000000/* * This program attempts to recover deleted data structures in a registry hive. * * Copyright (C) 2008-2010 Timothy D. Morgan * * 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; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: reglookup-recover.c 173 2010-03-08 03:39:09Z tim $ */ #include #include #include "talloc.h" #include "regfi.h" #include "range_list.h" #include "lru_cache.h" /* Globals, influenced by command line parameters */ bool print_verbose = false; bool print_security = false; bool print_header = true; bool print_leftover = false; bool print_parsedraw = false; char* registry_file = NULL; #include "common.c" char* getQuotedData(int fd, uint32_t offset, uint32_t length) { uint8_t* buf; char* quoted_buf; uint32_t len; if((lseek(fd, offset, SEEK_SET)) == -1) return NULL; buf = (uint8_t*)malloc(length); if(buf == NULL) return NULL; len = length; if((regfi_read(fd, buf, &length) != 0) || length != len) { free(buf); return NULL; } quoted_buf = quote_buffer(buf, length, common_special_chars); free(buf); return quoted_buf; } /* XXX: Somewhere in here, need to start looking for and handling classnames */ void printKey(REGFI_FILE* f, REGFI_NK_REC* nk, const char* prefix) { char mtime[20]; time_t tmp_time[1]; struct tm* tmp_time_s = NULL; char* quoted_name = NULL; char* quoted_raw = ""; *tmp_time = regfi_nt2unix_time(&nk->mtime); tmp_time_s = gmtime(tmp_time); strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s); /* XXX: Add command line option to choose output encoding */ regfi_interpret_keyname(f, nk, REGFI_ENCODING_ASCII, true); quoted_name = get_quoted_keyname(nk); if (quoted_name == NULL) { quoted_name = malloc(1*sizeof(char)); if(quoted_name == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n"); quoted_name[0] = '\0'; fprintf(stderr, "WARN: NULL key name in NK record at offset %.8X.\n", nk->offset); } if(print_parsedraw) quoted_raw = getQuotedData(f->fd, nk->offset, nk->cell_size); printf("%.8X,%.8X,KEY,%s,%s,%s,%d,,,,,,,,%s\n", nk->offset, nk->cell_size, prefix, quoted_name, mtime, nk->num_values, quoted_raw); if(print_parsedraw) free(quoted_raw); free(quoted_name); } void printValue(REGFI_FILE* f, REGFI_VK_REC* vk, const char* prefix) { char* quoted_value = NULL; char* quoted_name = NULL; char* quoted_raw = ""; char* conv_error = NULL; const char* str_type = NULL; /* XXX: Add command line option to choose output encoding */ regfi_interpret_valuename(f, vk, REGFI_ENCODING_ASCII, true); quoted_name = get_quoted_valuename(vk); if (quoted_name == NULL) { /* Value names are NULL when we're looking at the "(default)" value. * Currently we just return a 0-length string to try an eliminate * ambiguity with a literal "(default)" value. The data type of a line * in the output allows one to differentiate between the parent key and * this value. */ quoted_name = strdup(""); if(quoted_name == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n"); } /* XXX: Add command line option to choose output encoding */ if(vk->data != NULL && !regfi_interpret_data(f, REGFI_ENCODING_ASCII, vk->type, vk->data)) { fprintf(stderr, "WARN: Error occurred while interpreting data for VK record" " at offset 0x%.8X.\n", vk->offset); } printMsgs(f); quoted_value = data_to_ascii(vk->data, &conv_error); if(quoted_value == NULL) { quoted_value = malloc(1*sizeof(char)); if(quoted_value == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n"); quoted_value[0] = '\0'; if(conv_error == NULL) fprintf(stderr, "WARN: Could not quote value for '%s/%s'. " "Memory allocation failure likely.\n", prefix, quoted_name); else if(print_verbose) fprintf(stderr, "WARN: Could not quote value for '%s/%s'. " "Returned error: %s\n", prefix, quoted_name, conv_error); } /* XXX: should these always be printed? */ else if(conv_error != NULL && print_verbose) fprintf(stderr, "INFO: While quoting value for '%s/%s', " "warning returned: %s\n", prefix, quoted_name, conv_error); if(print_parsedraw) quoted_raw = getQuotedData(f->fd, vk->offset, vk->cell_size); str_type = regfi_type_val2str(vk->type); if(str_type == NULL) printf("%.8X,%.8X,VALUE,%s,%s,,,0x%.8X,%s,%d,,,,,%s\n", vk->offset, vk->cell_size, prefix, quoted_name, vk->type, quoted_value, vk->data_size, quoted_raw); else printf("%.8X,%.8X,VALUE,%s,%s,,,%s,%s,%d,,,,,%s\n", vk->offset, vk->cell_size, prefix, quoted_name, str_type, quoted_value, vk->data_size, quoted_raw); if(print_parsedraw) free(quoted_raw); if(quoted_value != NULL) free(quoted_value); if(quoted_name != NULL) free(quoted_name); if(conv_error != NULL) free(conv_error); } void printSK(REGFI_FILE* f, REGFI_SK_REC* sk) { char* quoted_raw = NULL; char* empty_str = ""; char* owner = regfi_get_owner(sk->sec_desc); char* group = regfi_get_group(sk->sec_desc); char* sacl = regfi_get_sacl(sk->sec_desc); char* dacl = regfi_get_dacl(sk->sec_desc); if(print_parsedraw) quoted_raw = getQuotedData(f->fd, sk->offset, sk->cell_size); if(owner == NULL) owner = empty_str; if(group == NULL) group = empty_str; if(sacl == NULL) sacl = empty_str; if(dacl == NULL) dacl = empty_str; printf("%.8X,%.8X,SK,,,,,,,,%s,%s,%s,%s,%s\n", sk->offset, sk->cell_size, owner, group, sacl, dacl, quoted_raw); if(owner != empty_str) free(owner); if(group != empty_str) free(group); if(sacl != empty_str) free(sacl); if(dacl != empty_str) free(dacl); if(print_parsedraw) free(quoted_raw); } int printCell(REGFI_FILE* f, uint32_t offset) { char* quoted_buf; uint32_t cell_length; bool unalloc; if(!regfi_parse_cell(f->fd, offset, NULL, 0, &cell_length, &unalloc)) return 1; quoted_buf = getQuotedData(f->fd, offset, cell_length); if(quoted_buf == NULL) return 2; printf("%.8X,%.8X,RAW,,,,,,,,,,,,%s\n", offset, cell_length, quoted_buf); free(quoted_buf); return 0; } /* This function returns a properly quoted parent path or partial parent * path for a given key. Returns NULL on error, "" if no path was available. * Paths returned must be free()d. */ /* XXX: This is not terribly efficient, as it may reparse many keys * repeatedly. Should try to add caching. */ char* getParentPath(REGFI_FILE* f, REGFI_NK_REC* nk) { void_stack* path_stack = void_stack_new(REGFI_MAX_DEPTH); REGFI_NK_REC* cur_ancestor; char* ret_val; uint32_t virt_offset, i, stack_size, ret_val_size, ret_val_used, offset; int32_t max_size; REGFI_BUFFER* path_element; /* The path_stack size limit should guarantee that we don't recurse forever. */ virt_offset = nk->parent_off; ret_val_size = 1; /* NUL */ while(virt_offset != REGFI_OFFSET_NONE) { offset = virt_offset+REGFI_REGF_SIZE; max_size = regfi_calc_maxsize(f, offset); if(max_size < 0) virt_offset = REGFI_OFFSET_NONE; else { cur_ancestor = regfi_parse_nk(f, offset, max_size, true); printMsgs(f); if(cur_ancestor == NULL) virt_offset = REGFI_OFFSET_NONE; else { if(cur_ancestor->flags & REGFI_NK_FLAG_ROOT) virt_offset = REGFI_OFFSET_NONE; else virt_offset = cur_ancestor->parent_off; path_element = talloc(path_stack, REGFI_BUFFER); if(path_element != NULL) { /* XXX: Add command line option to choose output encoding */ regfi_interpret_keyname(f, cur_ancestor, REGFI_ENCODING_ASCII, true); path_element->buf = (uint8_t*)get_quoted_keyname(cur_ancestor); } if(path_element == NULL || path_element->buf == NULL || !void_stack_push(path_stack, path_element)) { /* XXX: Need to add a warning here */ regfi_free_key(cur_ancestor); void_stack_free(path_stack); return NULL; } /* Path element and preceeding delimiter * Note that this integer can't overflow since key name lengths are * 16 bits and the max depth is 512. */ path_element->len = strlen((char*)path_element->buf); ret_val_size += path_element->len + 1; regfi_free_key(cur_ancestor); } } } stack_size = void_stack_size(path_stack); ret_val_used = 0; ret_val = malloc(ret_val_size); if(ret_val == NULL) { void_stack_free(path_stack); return NULL; } ret_val[0] = '\0'; for(i=0; ibuf); ret_val_used += path_element->len + 1; free(path_element->buf); talloc_free(path_element); } void_stack_free(path_stack); return ret_val; } static void usage(void) { fprintf(stderr, "Usage: reglookup-recover [options] \n"); fprintf(stderr, "Version: %s\n", REGLOOKUP_VERSION); fprintf(stderr, "Options:\n"); fprintf(stderr, "\t-v\t sets verbose mode.\n"); fprintf(stderr, "\t-h\t enables header row. (default)\n"); fprintf(stderr, "\t-H\t disables header row.\n"); fprintf(stderr, "\t-l\t enables leftover(raw) cell output.\n"); fprintf(stderr, "\t-L\t disables leftover(raw) cell output. (default)\n"); fprintf(stderr, "\t-r\t enables raw cell output for parsed cells.\n"); fprintf(stderr, "\t-R\t disables raw cell output for parsed cells. (default)\n"); fprintf(stderr, "\n"); } bool removeRange(range_list* rl, uint32_t offset, uint32_t length) { int32_t rm_idx; const range_list_element* cur_elem; rm_idx = range_list_find(rl, offset); if(rm_idx < 0) { fprintf(stderr, "DEBUG: removeRange: rm_idx < 0; (%d)\n", rm_idx); return false; } cur_elem = range_list_get(rl, rm_idx); if(cur_elem == NULL) { fprintf(stderr, "DEBUG: removeRange: cur_elem == NULL. rm_idx=%d\n", rm_idx); return false; } if(offset > cur_elem->offset) { if(!range_list_split_element(rl, rm_idx, offset)) { fprintf(stderr, "DEBUG: removeRange: first split failed\n"); return false; } rm_idx++; cur_elem = range_list_get(rl, rm_idx); if(cur_elem == NULL) { fprintf(stderr, "DEBUG: removeRange: cur_elem == NULL after first split. rm_idx=%d\n", rm_idx); return false; } } if(offset+length < cur_elem->offset+cur_elem->length) { if(!range_list_split_element(rl, rm_idx, offset+length)) { fprintf(stderr, "DEBUG: removeRange: second split failed\n"); return false; } } if(!range_list_remove(rl, rm_idx)) { fprintf(stderr, "DEBUG: removeRange: remove failed\n"); return false; } return true; } int extractVKs(REGFI_FILE* f, range_list* unalloc_cells, range_list* unalloc_values) { const range_list_element* cur_elem; REGFI_VK_REC* vk; uint32_t i, j; for(i=0; i < range_list_size(unalloc_cells); i++) { printMsgs(f); cur_elem = range_list_get(unalloc_cells, i); for(j=0; j <= cur_elem->length; j+=8) { vk = regfi_parse_vk(f, cur_elem->offset+j, cur_elem->length-j, false); printMsgs(f); if(vk != NULL) { if(!range_list_add(unalloc_values, vk->offset, vk->cell_size, vk)) { fprintf(stderr, "ERROR: Couldn't add value to unalloc_values.\n"); return 20; } j+=vk->cell_size-8; } } } /* Remove value ranges from the unalloc_cells before we continue. */ for(i=0; ioffset, cur_elem->length)) return 30; } return 0; } int extractDataCells(REGFI_FILE* file, range_list* unalloc_cells, range_list* unalloc_values) { const range_list_element* cur_elem; REGFI_VK_REC* vk; range_list* bd_cells; REGFI_BUFFER data; uint32_t i, j, offset, cell_length, length; int32_t max_size; bool unalloc; bd_cells = range_list_new(); if(bd_cells == NULL) return 10; data.buf = NULL; data.len = 0; for(i=0; idata; if(vk == NULL) return 11; length = vk->data_size; vk->data = NULL; if(vk->data_size != 0) { offset = vk->data_off+REGFI_REGF_SIZE; if(vk->data_in_offset) data = regfi_parse_little_data(file, vk->data_off, length, false); else { max_size = regfi_calc_maxsize(file, offset); if(max_size >= 0 && regfi_parse_cell(file->fd, offset, NULL, 0, &cell_length, &unalloc) && (cell_length & 0x00000007) == 0 && cell_length <= max_size) { if(cell_length - 4 < length) { /* Multi-cell "big data" */ /* XXX: All big data records thus far have been 16 bytes long. * Should we check for this precise size instead of just * relying upon the above check? */ if (file->major_version >= 1 && file->minor_version >= 5) { /* Attempt to parse a big data record */ data = regfi_load_big_data(file, offset, length, cell_length, bd_cells, false); /* XXX: if this turns out NULL, should fall back to truncating cell */ if(data.buf != NULL) { for(j=0; joffset, cur_elem->length)) { fprintf(stderr, "WARN: Successfully parsed big data at offset" " 0x%.8X was rejected because some substructure" " (offset=0x%.8X) is allocated or used in other" " recovered structures.\n", offset, cur_elem->offset); talloc_free(data.buf); data.buf = NULL; data.len = 0; break; } } if(data.buf != NULL) { for(j=0; joffset, cur_elem->length)) { return 22; } } } } } else { fprintf(stderr, "WARN: Data length (0x%.8X)" " larger than remaining cell length (0x%.8X)" " while parsing data record at offset 0x%.8X." " Truncating...\n", length, cell_length - 4, offset); length = cell_length - 4; } } /* Typical 1-cell data */ if(range_list_has_range(unalloc_cells, offset, length)) { data = regfi_parse_data(file, offset, length, false); if(data.buf != NULL) if(!removeRange(unalloc_cells, offset, length)) return 30; } } } /* XXX: Need to come up with a different way to link these so the * vk->data item can be removed from the structure. */ vk->data = regfi_buffer_to_data(data); talloc_steal(vk, vk->data); } } range_list_free(bd_cells); return 0; } /* NOTE: unalloc_keys should be an empty range_list. */ int extractKeys(REGFI_FILE* f, range_list* unalloc_cells, range_list* unalloc_keys) { const range_list_element* cur_elem; REGFI_NK_REC* key; uint32_t i, j; int error_code = 0; for(i=0; i < range_list_size(unalloc_cells); i++) { printMsgs(f); cur_elem = range_list_get(unalloc_cells, i); for(j=0; cur_elem->length > REGFI_NK_MIN_LENGTH && j <= cur_elem->length-REGFI_NK_MIN_LENGTH; j+=8) { key = regfi_parse_nk(f, cur_elem->offset+j, cur_elem->length-j, false); printMsgs(f); if(key != NULL) { if(!range_list_add(unalloc_keys, key->offset, key->cell_size, key)) { fprintf(stderr, "ERROR: Couldn't add key to unalloc_keys.\n"); error_code = 20; goto fail; } talloc_steal(unalloc_keys, key); j+=key->cell_size-8; } } } for(i=0; ioffset, cur_elem->length)) { error_code = 30; goto fail; } } return 0; fail: regfi_free_key(key); return error_code; } int extractValueLists(REGFI_FILE* f, range_list* unalloc_cells, range_list* unalloc_keys, range_list* unalloc_linked_values) { REGFI_NK_REC* nk; REGFI_VK_REC* vk; const range_list_element* cur_elem; uint32_t i, j, num_keys, off, values_length; int32_t max_size; num_keys=range_list_size(unalloc_keys); for(i=0; idata; if(nk->num_values && (nk->values_off!=REGFI_OFFSET_NONE)) { off = nk->values_off + REGFI_REGF_SIZE; max_size = regfi_calc_maxsize(f, off); if(max_size >= 0) { nk->values = regfi_load_valuelist(f, off, nk->num_values, max_size, false); if(nk->values != NULL && nk->values->elements != NULL) { /* Number of elements in the value list may be shorter than advertised * by NK record due to cell truncation. We'll consider this valid and * only throw out the whole value list if it bleeds into an already * parsed structure. */ values_length = (nk->values->num_values+1)*sizeof(uint32_t); if(values_length != (values_length & 0xFFFFFFF8)) values_length = (values_length & 0xFFFFFFF8) + 8; if(!range_list_has_range(unalloc_cells, off, values_length)) { /* We've parsed a values-list which isn't in the unallocated list, * so prune it. */ talloc_free(nk->values); nk->values = NULL; } else { /* Values-list was recovered. Remove from unalloc_cells and * inspect values. */ if(!removeRange(unalloc_cells, off, values_length)) return 20; for(j=0; j < nk->values->num_values; j++) { /* Don't bother to restrict cell length here, since we'll * check our unalloc_cells range_list later. */ vk = regfi_parse_vk(f, nk->values->elements[j]+REGFI_REGF_SIZE, 0x7FFFFFFF, false); printMsgs(f); if(vk != NULL) { if(range_list_has_range(unalloc_cells, vk->offset, vk->cell_size)) { if(!range_list_add(unalloc_linked_values, vk->offset, vk->cell_size, vk)) { talloc_free(vk); return 30; } if(!removeRange(unalloc_cells, vk->offset, vk->cell_size)) return 40; } else talloc_free(vk); } } } } } } } return 0; } /* NOTE: unalloc_sks should be an empty range_list. */ int extractSKs(REGFI_FILE* f, range_list* unalloc_cells, range_list* unalloc_sks) { const range_list_element* cur_elem; REGFI_SK_REC* sk; uint32_t i, j; for(i=0; i < range_list_size(unalloc_cells); i++) { printMsgs(f); cur_elem = range_list_get(unalloc_cells, i); for(j=0; j <= cur_elem->length; j+=8) { sk = regfi_parse_sk(f, cur_elem->offset+j, cur_elem->length-j, false); printMsgs(f); if(sk != NULL) { if(!range_list_add(unalloc_sks, sk->offset, sk->cell_size, sk)) { fprintf(stderr, "ERROR: Couldn't add sk to unalloc_sks.\n"); return 20; } talloc_steal(unalloc_sks, sk); j+=sk->cell_size-8; } } } for(i=0; ioffset, cur_elem->length)) return 30; } return 0; } int main(int argc, char** argv) { REGFI_FILE* f; const range_list_element* cur_elem; range_list* unalloc_cells; range_list* unalloc_keys; range_list* unalloc_linked_values; range_list* unalloc_values; range_list* unalloc_sks; char** parent_paths; char* tmp_name; char* tmp_path; REGFI_NK_REC* tmp_key; REGFI_VK_REC* tmp_value; uint32_t argi, arge, i, j, ret, num_unalloc_keys; /* Process command line arguments */ if(argc < 2) { usage(); bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: Requires at least one argument.\n"); } arge = argc-1; for(argi = 1; argi < arge; argi++) { if (strcmp("-v", argv[argi]) == 0) print_verbose = true; else if (strcmp("-h", argv[argi]) == 0) print_header = true; else if (strcmp("-H", argv[argi]) == 0) print_header = false; else if (strcmp("-l", argv[argi]) == 0) print_leftover = true; else if (strcmp("-L", argv[argi]) == 0) print_leftover = false; else if (strcmp("-r", argv[argi]) == 0) print_parsedraw = true; else if (strcmp("-R", argv[argi]) == 0) print_parsedraw = false; else { usage(); fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]); bailOut(REGLOOKUP_EXIT_USAGE, ""); } } /*test_offset = strtol(argv[argi++], NULL, 16);*/ if((registry_file = strdup(argv[argi])) == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n"); f = regfi_open(registry_file); if(f == NULL) { fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file); bailOut(REGLOOKUP_EXIT_NOINPUT, ""); } if(print_verbose) regfi_set_message_mask(f, REGFI_MSG_ERROR|REGFI_MSG_WARN|REGFI_MSG_INFO); else regfi_set_message_mask(f, REGFI_MSG_ERROR); if(print_header) printf("OFFSET,REC_LENGTH,REC_TYPE,PATH,NAME," "NK_MTIME,NK_NVAL,VK_TYPE,VK_VALUE,VK_DATA_LEN," "SK_OWNER,SK_GROUP,SK_SACL,SK_DACL,RAW_CELL\n"); unalloc_cells = regfi_parse_unalloc_cells(f); if(unalloc_cells == NULL) { fprintf(stderr, "ERROR: Could not obtain list of unallocated cells.\n"); return 1; } unalloc_keys = range_list_new(); if(unalloc_keys == NULL) return 10; unalloc_linked_values = range_list_new(); if(unalloc_linked_values == NULL) return 10; unalloc_values = range_list_new(); if(unalloc_values == NULL) return 10; unalloc_sks = range_list_new(); if(unalloc_sks == NULL) return 10; ret = extractKeys(f, unalloc_cells, unalloc_keys); if(ret != 0) { fprintf(stderr, "ERROR: extractKeys() failed with %d.\n", ret); return ret; } ret = extractValueLists(f, unalloc_cells, unalloc_keys,unalloc_linked_values); if(ret != 0) { fprintf(stderr, "ERROR: extractValueLists() failed with %d.\n", ret); return ret; } /* Carve any orphan values */ ret = extractVKs(f, unalloc_cells, unalloc_values); if(ret != 0) { fprintf(stderr, "ERROR: extractVKs() failed with %d.\n", ret); return ret; } /* Carve any data associated with VK records */ ret = extractDataCells(f, unalloc_cells, unalloc_linked_values); if(ret != 0) { fprintf(stderr, "ERROR: extractDataCells() failed with %d.\n", ret); return ret; } ret = extractDataCells(f, unalloc_cells, unalloc_values); if(ret != 0) { fprintf(stderr, "ERROR: extractDataCells() failed with %d.\n", ret); return ret; } /* Carve any SK records */ ret = extractSKs(f, unalloc_cells, unalloc_sks); if(ret != 0) { fprintf(stderr, "ERROR: extractSKs() failed with %d.\n", ret); return ret; } /* Now that we're done carving, associate recovered keys with parents, * if at all possible. */ num_unalloc_keys = range_list_size(unalloc_keys); parent_paths = (char**)malloc(sizeof(char*)*num_unalloc_keys); if(parent_paths == NULL) return 10; for(i=0; i < num_unalloc_keys; i++) { cur_elem = range_list_get(unalloc_keys, i); tmp_key = (REGFI_NK_REC*)cur_elem->data; if(tmp_key == NULL) return 20; parent_paths[i] = getParentPath(f, tmp_key); if(parent_paths[i] == NULL) return 20; } /* Now start the output */ for(i=0; i < num_unalloc_keys; i++) { cur_elem = range_list_get(unalloc_keys, i); tmp_key = (REGFI_NK_REC*)cur_elem->data; printKey(f, tmp_key, parent_paths[i]); if(tmp_key->num_values > 0 && tmp_key->values != NULL) { /* XXX: Add command line option to choose output encoding */ regfi_interpret_keyname(f, tmp_key, REGFI_ENCODING_ASCII, true); tmp_name = get_quoted_keyname(tmp_key); tmp_path = (char*)malloc(strlen(parent_paths[i])+strlen(tmp_name)+2); if(tmp_path == NULL) { free(tmp_name); return 10; } sprintf(tmp_path, "%s/%s", parent_paths[i], tmp_name); for(j=0; j < tmp_key->values->num_values; j++) { tmp_value = (REGFI_VK_REC*)range_list_find_data(unalloc_linked_values, tmp_key->values->elements[j] + REGFI_REGF_SIZE); if(tmp_value != NULL) printValue(f, tmp_value, tmp_path); } free(tmp_path); free(tmp_name); free(parent_paths[i]); } } free(parent_paths); /* Print out orphaned values */ for(i=0; i < range_list_size(unalloc_values); i++) { cur_elem = range_list_get(unalloc_values, i); tmp_value = (REGFI_VK_REC*)cur_elem->data; printValue(f, tmp_value, ""); } if(print_leftover) { for(i=0; i < range_list_size(unalloc_cells); i++) { cur_elem = range_list_get(unalloc_cells, i); printCell(f, cur_elem->offset); } } range_list_free(unalloc_cells); range_list_free(unalloc_keys); range_list_free(unalloc_linked_values); range_list_free(unalloc_values); range_list_free(unalloc_sks); return 0; } reglookup-0.12.0/src/reglookup.c000066400000000000000000000423561135112127200165500ustar00rootroot00000000000000/* * A utility to read a Windows NT and later registry files. * * Copyright (C) 2005-2010 Timothy D. Morgan * Copyright (C) 2010 Tobias Mueller (portions of '-i' code) * Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com * * 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; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: reglookup.c 172 2010-03-08 03:04:34Z tim $ */ #include #include #include #include #include #include "regfi.h" #include "void_stack.h" /* Globals, influenced by command line parameters */ bool print_value_mtime = false; bool print_verbose = false; bool print_security = false; bool print_header = true; bool path_filter_enabled = false; bool type_filter_enabled = false; char* path_filter = NULL; int type_filter; char* registry_file = NULL; /* Other globals */ REGFI_FILE* f; /* XXX: A hack to share some functions with reglookup-recover.c. * Should move these into a proper library at some point. */ #include "common.c" void printValue(REGFI_ITERATOR* iter, const REGFI_VK_REC* vk, char* prefix) { REGFI_DATA* data; char* quoted_value = NULL; char* quoted_name = NULL; char* conv_error = NULL; const char* str_type = NULL; char mtime[20]; time_t tmp_time[1]; struct tm* tmp_time_s = NULL; quoted_name = get_quoted_valuename(vk); if (quoted_name == NULL) { /* Value names are NULL when we're looking at the "(default)" value. * Currently we just return a 0-length string to try an eliminate * ambiguity with a literal "(default)" value. The data type of a line * in the output allows one to differentiate between the parent key and * this value. */ quoted_name = malloc(1*sizeof(char)); if(quoted_name == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not allocate sufficient memory.\n"); quoted_name[0] = '\0'; } data = regfi_iterator_fetch_data(iter, vk); printMsgs(iter->f); if(data != NULL) { quoted_value = data_to_ascii(data, &conv_error); if(quoted_value == NULL) { if(conv_error == NULL) fprintf(stderr, "WARN: Could not quote value for '%s/%s'. " "Memory allocation failure likely.\n", prefix, quoted_name); else fprintf(stderr, "WARN: Could not quote value for '%s/%s'. " "Returned error: %s\n", prefix, quoted_name, conv_error); } else if(conv_error != NULL) fprintf(stderr, "WARN: While quoting value for '%s/%s', " "warning returned: %s\n", prefix, quoted_name, conv_error); regfi_free_data(data); } if(print_value_mtime) { *tmp_time = regfi_nt2unix_time(&iter->cur_key->mtime); tmp_time_s = gmtime(tmp_time); strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s); } else mtime[0] = '\0'; str_type = regfi_type_val2str(vk->type); if(print_security) { if(str_type == NULL) printf("%s/%s,0x%.8X,%s,%s,,,,\n", prefix, quoted_name, vk->type, quoted_value, mtime); else printf("%s/%s,%s,%s,%s,,,,\n", prefix, quoted_name, str_type, quoted_value, mtime); } else { if(str_type == NULL) printf("%s/%s,0x%.8X,%s,%s\n", prefix, quoted_name, vk->type, quoted_value, mtime); else printf("%s/%s,%s,%s,%s\n", prefix, quoted_name, str_type, quoted_value, mtime); } if(quoted_value != NULL) free(quoted_value); if(quoted_name != NULL) free(quoted_name); if(conv_error != NULL) free(conv_error); } char** splitPath(const char* s) { char** ret_val; const char* cur = s; char* next = NULL; char* copy; uint32_t ret_cur = 0; ret_val = (char**)malloc((REGFI_MAX_DEPTH+1+1)*sizeof(char**)); if (ret_val == NULL) return NULL; ret_val[0] = NULL; /* We return a well-formed, 0-length, path even when input is icky. */ if (s == NULL) return ret_val; while((next = strchr(cur, '/')) != NULL) { if ((next-cur) > 0) { copy = (char*)malloc((next-cur+1)*sizeof(char)); if(copy == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n"); memcpy(copy, cur, next-cur); copy[next-cur] = '\0'; ret_val[ret_cur++] = copy; if(ret_cur < (REGFI_MAX_DEPTH+1+1)) ret_val[ret_cur] = NULL; else bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Registry maximum depth exceeded.\n"); } cur = next+1; } /* Grab last element, if path doesn't end in '/'. */ if(strlen(cur) > 0) { copy = strdup(cur); ret_val[ret_cur++] = copy; if(ret_cur < (REGFI_MAX_DEPTH+1+1)) ret_val[ret_cur] = NULL; else bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Registry maximum depth exceeded.\n"); } return ret_val; } void freePath(char** path) { uint32_t i; if(path == NULL) return; for(i=0; path[i] != NULL; i++) free(path[i]); free(path); } /* Returns a quoted path from an iterator's stack */ char* iter2Path(REGFI_ITERATOR* i) { const REGFI_ITER_POSITION* cur; const REGFI_NK_REC* tmp_key; uint32_t buf_left = 127; uint32_t buf_len = buf_left+1; uint32_t name_len = 0; uint32_t grow_amt; char* buf; char* new_buf; char* name; void_stack_iterator* iter; buf = (char*)malloc((buf_len)*sizeof(char)); if (buf == NULL) return NULL; buf[0] = '\0'; iter = void_stack_iterator_new(i->key_positions); if (iter == NULL) { free(buf); return NULL; } /* skip root element */ if(void_stack_size(i->key_positions) < 1) { buf[0] = '/'; buf[1] = '\0'; return buf; } cur = void_stack_iterator_next(iter); do { cur = void_stack_iterator_next(iter); if (cur == NULL) tmp_key = i->cur_key; else tmp_key = cur->nk; name = get_quoted_keyname(tmp_key); buf[buf_len-buf_left-1] = '/'; buf_left -= 1; name_len = strlen(name); if(name_len+1 > buf_left) { grow_amt = (uint32_t)(buf_len/2); buf_len += name_len+1+grow_amt-buf_left; if((new_buf = realloc(buf, buf_len)) == NULL) { free(name); free(buf); free(iter); return NULL; } buf = new_buf; buf_left = grow_amt + name_len + 1; } strncpy(buf+(buf_len-buf_left-1), name, name_len); buf_left -= name_len; buf[buf_len-buf_left-1] = '\0'; free(name); } while(cur != NULL); return buf; } void printValueList(REGFI_ITERATOR* iter, char* prefix) { REGFI_VK_REC* value; value = regfi_iterator_first_value(iter); while(value != NULL) { if(!type_filter_enabled || (value->type == type_filter)) printValue(iter, value, prefix); regfi_free_value(value); value = regfi_iterator_next_value(iter); printMsgs(iter->f); } } void printKey(REGFI_ITERATOR* iter, char* full_path) { static char empty_str[1] = ""; char* owner = NULL; char* group = NULL; char* sacl = NULL; char* dacl = NULL; char* quoted_classname; char mtime[20]; time_t tmp_time[1]; struct tm* tmp_time_s = NULL; const REGFI_SK_REC* sk; const REGFI_NK_REC* k = regfi_iterator_cur_key(iter); REGFI_CLASSNAME* classname; *tmp_time = regfi_nt2unix_time(&k->mtime); tmp_time_s = gmtime(tmp_time); strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S", tmp_time_s); if(print_security && (sk=regfi_iterator_cur_sk(iter))) { owner = regfi_get_owner(sk->sec_desc); group = regfi_get_group(sk->sec_desc); sacl = regfi_get_sacl(sk->sec_desc); dacl = regfi_get_dacl(sk->sec_desc); if(owner == NULL) owner = empty_str; if(group == NULL) group = empty_str; if(sacl == NULL) sacl = empty_str; if(dacl == NULL) dacl = empty_str; classname = regfi_iterator_fetch_classname(iter, k); printMsgs(iter->f); if(classname != NULL) { if(classname->interpreted == NULL) { fprintf(stderr, "WARN: Could not convert class name" " charset for key '%s'. Quoting raw...\n", full_path); quoted_classname = quote_buffer(classname->raw, classname->size, key_special_chars); } else quoted_classname = quote_string(classname->interpreted, key_special_chars); if(quoted_classname == NULL) { fprintf(stderr, "ERROR: Could not quote classname" " for key '%s' due to unknown error.\n", full_path); quoted_classname = empty_str; } } else quoted_classname = empty_str; regfi_free_classname(classname); printMsgs(iter->f); printf("%s,KEY,,%s,%s,%s,%s,%s,%s\n", full_path, mtime, owner, group, sacl, dacl, quoted_classname); if(owner != empty_str) free(owner); if(group != empty_str) free(group); if(sacl != empty_str) free(sacl); if(dacl != empty_str) free(dacl); if(quoted_classname != empty_str) free(quoted_classname); } else printf("%s,KEY,,%s\n", full_path, mtime); } void printKeyTree(REGFI_ITERATOR* iter) { const REGFI_NK_REC* root = NULL; const REGFI_NK_REC* cur = NULL; REGFI_NK_REC* sub = NULL; char* path = NULL; int key_type = regfi_type_str2val("KEY"); bool print_this = true; root = cur = regfi_iterator_cur_key(iter); sub = regfi_iterator_first_subkey(iter); printMsgs(iter->f); if(root == NULL) bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: root cannot be NULL.\n"); do { if(print_this) { path = iter2Path(iter); if(path == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Could not construct iterator's path.\n"); if(!type_filter_enabled || (key_type == type_filter)) printKey(iter, path); if(!type_filter_enabled || (key_type != type_filter)) printValueList(iter, path); free(path); } if(sub == NULL) { if(cur != root) { /* We're done with this sub-tree, going up and hitting other branches. */ if(!regfi_iterator_up(iter)) { printMsgs(iter->f); bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator upward.\n"); } cur = regfi_iterator_cur_key(iter); if(cur == NULL) { printMsgs(iter->f); bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: unexpected NULL for key.\n"); } sub = regfi_iterator_next_subkey(iter); } print_this = false; } else { /* We have unexplored sub-keys. * Let's move down and print this first sub-tree out. */ if(!regfi_iterator_down(iter)) { printMsgs(iter->f); bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: could not traverse iterator downward.\n"); } cur = regfi_iterator_cur_key(iter); regfi_free_key(sub); sub = regfi_iterator_first_subkey(iter); print_this = true; } printMsgs(iter->f); } while(!((cur == root) && (sub == NULL))); if(print_verbose) fprintf(stderr, "INFO: Finished printing key tree.\n"); } /* XXX: What if there is BOTH a value AND a key with that name?? * What if there are multiple keys/values with the same name?? */ /* * Returns 0 if path was not found. * Returns 1 if path was found as value. * Returns 2 if path was found as key. * Returns less than 0 on other error. */ int retrievePath(REGFI_ITERATOR* iter, char** path) { REGFI_VK_REC* value; char* tmp_path_joined; const char** tmp_path; uint32_t i; if(path == NULL) return -1; /* One extra for any value at the end, and one more for NULL */ tmp_path = (const char**)malloc(sizeof(const char**)*(REGFI_MAX_DEPTH+1+1)); if(tmp_path == NULL) return -2; /* Strip any potential value name at end of path */ for(i=0; (path[i] != NULL) && (path[i+1] != NULL) && (i < REGFI_MAX_DEPTH+1); i++) { tmp_path[i] = path[i]; } tmp_path[i] = NULL; if(print_verbose) fprintf(stderr, "INFO: Attempting to retrieve specified path: %s\n", path_filter); /* Special check for '/' path filter */ if(path[0] == NULL) { if(print_verbose) fprintf(stderr, "INFO: Found final path element as root key.\n"); free(tmp_path); return 2; } if(!regfi_iterator_walk_path(iter, tmp_path)) { printMsgs(iter->f); free(tmp_path); return 0; } if(regfi_iterator_find_value(iter, path[i])) { if(print_verbose) fprintf(stderr, "INFO: Found final path element as value.\n"); value = regfi_iterator_cur_value(iter); printMsgs(iter->f); tmp_path_joined = iter2Path(iter); if((value == NULL) || (tmp_path_joined == NULL)) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Unexpected error before printValue.\n"); if(!type_filter_enabled || (value->type == type_filter)) printValue(iter, value, tmp_path_joined); regfi_free_value(value); free(tmp_path); free(tmp_path_joined); return 1; } else if(regfi_iterator_find_subkey(iter, path[i])) { printMsgs(iter->f); if(print_verbose) fprintf(stderr, "INFO: Found final path element as key.\n"); if(!regfi_iterator_down(iter)) { printMsgs(iter->f); bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Unexpected error on traversing path filter key.\n"); } return 2; } printMsgs(iter->f); if(print_verbose) fprintf(stderr, "INFO: Could not find last element of path.\n"); return 0; } static void usage(void) { fprintf(stderr, "Usage: reglookup [-v] [-s]" " [-p ] [-t ]" " \n"); fprintf(stderr, "Version: %s\n", REGLOOKUP_VERSION); fprintf(stderr, "Options:\n"); fprintf(stderr, "\t-v\t sets verbose mode.\n"); fprintf(stderr, "\t-h\t enables header row. (default)\n"); fprintf(stderr, "\t-H\t disables header row.\n"); fprintf(stderr, "\t-s\t enables security descriptor output.\n"); fprintf(stderr, "\t-S\t disables security descriptor output. (default)\n"); fprintf(stderr, "\t-p\t restrict output to elements below this path.\n"); fprintf(stderr, "\t-t\t restrict results to this specific data type.\n"); fprintf(stderr, "\t-i\t includes parent key modification times with child values.\n"); fprintf(stderr, "\n"); } int main(int argc, char** argv) { char** path = NULL; REGFI_ITERATOR* iter; int retr_path_ret; uint32_t argi, arge; /* Process command line arguments */ if(argc < 2) { usage(); bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: Requires at least one argument.\n"); } arge = argc-1; for(argi = 1; argi < arge; argi++) { if (strcmp("-p", argv[argi]) == 0) { if(++argi >= arge) { usage(); bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: '-p' option requires parameter.\n"); } if((path_filter = strdup(argv[argi])) == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n"); path_filter_enabled = true; } else if (strcmp("-t", argv[argi]) == 0) { if(++argi >= arge) { usage(); bailOut(REGLOOKUP_EXIT_USAGE, "ERROR: '-t' option requires parameter.\n"); } if((type_filter = regfi_type_str2val(argv[argi])) < 0) { fprintf(stderr, "ERROR: Invalid type specified: %s.\n", argv[argi]); bailOut(REGLOOKUP_EXIT_USAGE, ""); } type_filter_enabled = true; } else if (strcmp("-h", argv[argi]) == 0) print_header = true; else if (strcmp("-H", argv[argi]) == 0) print_header = false; else if (strcmp("-s", argv[argi]) == 0) print_security = true; else if (strcmp("-S", argv[argi]) == 0) print_security = false; else if (strcmp("-v", argv[argi]) == 0) print_verbose = true; else if (strcmp("-i", argv[argi]) == 0) print_value_mtime = true; else { usage(); fprintf(stderr, "ERROR: Unrecognized option: %s\n", argv[argi]); bailOut(REGLOOKUP_EXIT_USAGE, ""); } } if((registry_file = strdup(argv[argi])) == NULL) bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Memory allocation problem.\n"); f = regfi_open(registry_file); if(f == NULL) { fprintf(stderr, "ERROR: Couldn't open registry file: %s\n", registry_file); bailOut(REGLOOKUP_EXIT_NOINPUT, ""); } if(print_verbose) regfi_set_message_mask(f, REGFI_MSG_INFO|REGFI_MSG_WARN|REGFI_MSG_ERROR); /* XXX: add command line option to choose output encoding */ iter = regfi_iterator_new(f, REGFI_ENCODING_ASCII); if(iter == NULL) { printMsgs(f); bailOut(REGLOOKUP_EXIT_OSERR, "ERROR: Couldn't create registry iterator.\n"); } if(print_header) { if(print_security) printf("PATH,TYPE,VALUE,MTIME,OWNER,GROUP,SACL,DACL,CLASS\n"); else printf("PATH,TYPE,VALUE,MTIME\n"); } if(path_filter_enabled && path_filter != NULL) path = splitPath(path_filter); if(path != NULL) { retr_path_ret = retrievePath(iter, path); printMsgs(iter->f); freePath(path); if(retr_path_ret == 0) fprintf(stderr, "WARN: Specified path '%s' not found.\n", path_filter); else if (retr_path_ret == 2) printKeyTree(iter); else if(retr_path_ret < 0) { fprintf(stderr, "ERROR: retrievePath() returned %d.\n", retr_path_ret); bailOut(REGLOOKUP_EXIT_DATAERR, "ERROR: Unknown error occurred in retrieving path.\n"); } } else printKeyTree(iter); regfi_iterator_free(iter); regfi_close(f); return 0; }