work/0000775000000000000000000000000015063477206006742 5ustar work/.dir-locals.el0000664000000000000000000000034215063477206011372 0ustar ;;; Directory Local Variables ;;; See Info node `(emacs) Directory Variables' for more information. ((c-mode (c-basic-offset . 4)) (python-mode (indent-tabs-mode . t) (python-indent-offset . 8) (python-indent . 8))) work/.gitignore0000664000000000000000000000134015063477206010730 0ustar *.o .*.d *.pyc conffile.tab.[ch] conffile.yy.[ch] pubkeys.fl pubkeys.yy.[ch] /version.c /secnet /eax-*-test /eax-*-test.confirm /ipaddrset-test.new /ipaddrset.confirm /config.log /config.h /config.status /config.stamp /config.stamp.in Makefile /common.make /test-common.make msgcode-test msgcode-test.confirm autom4te.cache *~ *.tmp TAGS .makefiles.stamp Dir.mk /main.mk /Final.mk debian/files debian/secnet.debhelper.log debian/*.debhelper debian/secnet debian/secnet.substvars *.xcodeproj /build test-example/*.key test-example/*.privkeys test-example/pubkeys test-example/sites.conf test-example/sites-nonego.conf test-example/bogus-setup-request build-stamp [sm]test/d-* stest/udp-preload.so base91s/*.[ch] base91s/base91s work/COPYING0000664000000000000000000010451315063477206010001 0ustar 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 . work/CREDITS0000664000000000000000000000307515063477206007767 0ustar Stephen Early - original author Ian Jackson - current maintainer Mark Wooding - much useful stuff Ross Anderson, Eli Biham, Lars Knudsen - serpent Colin Plumb, Ian Jackson - MD5 implementation Steve Reid, James H. Brown, Saul Kravitz - SHA1 implementation Vincent Rijmen, Antoon Bosselaers, Paulo Barreto - Rijndael (AES) implementation Guido Draheim - ac_prog_cc_no_writeable_strings.m4 Free Software Foundation and Scott G. Miller - SHA-512 implementation Free Software Foundation and Paul Eggert - u64.h Massachusetts Institute of Technology - install-sh Omnifarious, btel (StackOverflow uers) - Python argparse --[no-]foo options Joachim Henkea - base91 specificaation and C implementation Adrien Béraud, Guillaume Jacquenot, SunDwarf aka eyesismine - base91 python Simon Tatham, Jonathan Amery, Ian Jackson - testing and debugging Simon Tatham - RSA signatures using Chinese Remainder Theorem Simon Tatham - endianness cleanups in transform.c Richard Kettlewell, Matthew Vernon, Peter Benie - assorted bugfixes "Omnifarious" and "btel" on Stackoverflow - python yes/no arg parsing Joachim Henke - basE91 encoding format and the corresponding C library Adrien Béraud, Guillaume Jacquenot, SunDwarf - python basE91 library Joey Hess - some elements of the Debian packaging Miquel van Smoorenburg, Ian Murdock - original init script example -- This file is part of secnet. See LICENCE and this file CREDITS for full list of copyright holders. SPDX-License-Identifier: GPL-3.0-or-later There is NO WARRANTY. work/DEVELOPER-CERTIFICATE0000777000000000000000000000000015063477206016535 2subdirmk/DEVELOPER-CERTIFICATEustar work/Dir.sd.mk0000664000000000000000000001767715063477206010440 0ustar # Makefile for secnet # # This file is part of secnet. # See README for full list of copyright holders. # # secnet 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. # # secnet 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 # version 3 along with secnet; if not, see # https://www.gnu.org/licenses/gpl.html. .PHONY: all clean realclean distclean dist install PACKAGE:=secnet VERSION=0.6.7 VPATH:=@srcdir@ srcdir:=@srcdir@ include common.make INSTALL:=@INSTALL@ INSTALL_PROGRAM:=@INSTALL_PROGRAM@ INSTALL_SCRIPT:=@INSTALL_SCRIPT@ INSTALL_DATA:=@INSTALL_DATA@ prefix:=$(DESTDIR)@prefix@ exec_prefix:=@exec_prefix@ sbindir:=@sbindir@ sysconfdir:=$(DESTDIR)@sysconfdir@ datarootdir:=@datarootdir@ transform:=@program_transform_name@ mandir:=@mandir@ ALL_CFLAGS:=@DEFS@ -I$(srcdir) -I. $(CFLAGS) $(EXTRA_CFLAGS) CPPFLAGS:=@CPPFLAGS@ -DDATAROOTDIR='"$(datarootdir)"' $(EXTRA_CPPFLAGS) LDFLAGS:=@LDFLAGS@ $(EXTRA_LDFLAGS) LDLIBS:=@LIBS@ $(EXTRA_LDLIBS) &:local+global OBJECTS TARGETS TARGETS:=secnet OBJECTS:=secnet.o util.o conffile.yy.o conffile.tab.o conffile.o modules.o \ resolver.o random.o udp.o site.o transform-cbcmac.o transform-eax.o \ comm-common.o polypath.o privcache.o pubkeys.o pubkeys.yy.o \ netlink.o rsa.o dh.o serpent.o serpentbe.o \ md5.o sha512.o tun.o slip.o sha1.o ipaddr.o log.o \ process.o osdep.o @LIBOBJS@ \ hackypar.o base91s/base91.o # version.o is handled specially below and in the link rule for secnet. PYMODULES := ipaddrset.py argparseactionnoyes.py base91.py TEST_OBJECTS:=eax-aes-test.o eax-serpent-test.o eax-serpentbe-test.o \ eax-test.o aes.o ifeq (version.o,$(MAKECMDGOALS)) OBJECTS:= TEST_OBJECTS:= endif &OBJECTS += $(OBJECTS) $(TEST_OBJECTS) STALE_PYTHON_FILES= $(foreach e, py pyc, \ $(foreach p, /usr /usr/local, \ $(foreach l, ipaddr, \ $(DESTDIR)$p/share/secnet/$l.$e \ ))) %.yy.c %.yy.h: %.fl flex --header=$*.yy.h -o$*.yy.c $< %.c: %.y # make has a builtin rule to run yacc, but we don't want that because # our conffile.c is handwritten, and we use conffile.yy.c for the # bison output. There is no need to do likewise for flex because # our flex input files are end in .fl, not .l. %.tab.c %.tab.h: %.y bison -d -o $@ $< %.o: %.c $(CC) $(CPPFLAGS) $(ALL_CFLAGS) $(CDEPS_CFLAGS) -c $< -o $@ $(OBJECTS): conffile.yy.h pubkeys.yy.h base91s/base91.h # ^ we can't write this as a dependency on the %.o %.c rule # because (say) conffile.yy.c isn't mentioned so doesn't "ought # to exist" in make's mind. But specifying it explicitly like this # works. all:: $(TARGETS) ${srcdir}/config.h.in: configure.ac cd ${srcdir} && autoheader touch $@ MAKEFILE_TEMPLATES += config.h.in CONFIG_STATUS_OUTPUTS += config.h # Manual dependencies section conffile.yy.c: conffile.tab.c %.tab.c: %.y # End of manual dependencies section %.yy.o: ALL_CFLAGS += -Wno-sign-compare -Wno-unused-function secnet: $(OBJECTS) $(MAKE) -f main.mk version.o # *.o $(filter-out %.o, $^) $(CC) $(LDFLAGS) $(ALL_CFLAGS) -o $@ $(OBJECTS) version.o $(LDLIBS) # We (always) regenerate the version, but only if we regenerate the # binary. (This is necessary as the version string is can depend on # any of the source files, eg to see whether "+" is needed.) ifneq (,$(wildcard .git/HEAD)) # If we have (eg) committed, relink and thus regenerate the version # with the new info from git describe. secnet: Makefile .git/HEAD $(wildcard $(shell sed -n 's#^ref: #.git/#p' .git/HEAD)) secnet: $(wildcard .git/packed-refs) endif TESTDIRS=stest mtest &TARGETS_check = eax-aes-test.confirm eax-serpent-test.confirm \ eax-serpentbe-test.confirm ipaddrset.confirm &TARGETS_fullcheck += $(&TARGETS_check) &TARGETS_fullcheck += msgcode-test.confirm RECHECK_RM += $(&TARGETS_check) recheck: check .PHONY: FORCE version.c: FORCE echo "#include \"secnet.h\"" >$@.new @set -ex; if test -e .git && type -p git >/dev/null; then \ v=$$(git describe --match 'v*'); v=$${v#v}; \ if ! git diff --quiet HEAD; then v="$$v+"; fi; \ else \ v="$(VERSION)"; \ fi; \ echo "char version[]=\"secnet $$v\";" >>$@.new mv -f $@.new $@ eax-%-test: eax-%-test.o eax-test.o %.o $(CC) $(LDFLAGS) $(ALL_CFLAGS) -o $@ $^ eax-%-test.confirm: eax-%-test eax-%-test.vectors ./$< <$(srcdir)/eax-$*-test.vectors >$@.new mv -f $@.new $@ &CDEPS_OBJECTS += msgcode-test.o msgcode-test: msgcode-test.o $(CC) $(LDFLAGS) $(ALL_CFLAGS) -o $@ $^ msgcode-test.confirm: msgcode-test ./msgcode-test touch $@ ipaddrset.confirm: ipaddrset-test.py ipaddrset.py ipaddrset-test.expected $(srcdir)/ipaddrset-test.py >ipaddrset-test.new diff -u $(srcdir)/ipaddrset-test.expected ipaddrset-test.new touch $@ &CLEAN += & pubkeys.fl pubkeys.fl: ${srcdir}/pubkeys.fl.pl ${srcdir}/pubkeys.fl.pl >$@.tmp && mv -f $@.tmp $@ .PRECIOUS: eax-%-test installdirs: $(INSTALL) -d $(prefix)/share/secnet $(sbindir) $(INSTALL) -d $(mandir)/man8 $(INSTALL) -d $(datarootdir)/secnet install: installdirs set -e; ok=true; for f in $(STALE_PYTHON_FILES); do \ if test -e $$f; then \ echo >&\&2 "ERROR: $$f still exists "\ "- try \`make install-force'"; \ ok=false; \ fi; \ done; \ $$ok $(INSTALL_PROGRAM) secnet $(sbindir)/`echo secnet|sed '$(transform)'` $(INSTALL_PROGRAM) ${srcdir}/make-secnet-sites $(sbindir)/`echo make-secnet-sites|sed '$(transform)'` set -e; for m in $(PYMODULES); do \ $(INSTALL_DATA) ${srcdir}/$$m $(prefix)/share/secnet/$$m; \ done $(INSTALL_SCRIPT) ${srcdir}/polypath-interface-monitor-linux \ $(datarootdir)/secnet/. $(INSTALL_DATA) ${srcdir}/secnet.8 $(mandir)/man8/secnet.8 install-force: rm -f $(STALE_PYTHON_FILES) $(MAKE) -f main.mk install &CLEAN += .version.d &CLEAN += $(TARGETS) $(&TARGETS_check) $(&TARGETS_fullcheck) clean:: $(RM) -f *.o *.yy.[ch] *.tab.[ch] core version.c $(RM) -f *.pyc *~ eax-*-test.confirm eax-*-test $(RM) -rf __pycache__ $(RM) -f msgcode-test.confirm msgcode-test realclean:: clean $(RM) -f *~ Makefile config.h \ config.log config.status config.cache \ config.stamp Makefile.bak distclean:: realclean include subdirmk/regen.mk &:warn !single-char-var # Release checklist: # # 0. Use this checklist from Dir.sd.mk # # 1. Check that the tree has what you want # # 2. Update changelog: # gbp dch --since= # and then edit debian/changelog. # # 3. Update VERSION (in this file, above) and # finalise debian/changelog (removing ~ from version) and commit. # # 4. Build source and binaries: # dgit -wgf sbuild -A -c stretch -j8 # # 5. dpkg -i on zealot just to check # dpkg -i ~ian/things/Fvpn/bpd/secnet_${VERSION}_amd64.deb # # 6. run it on chiark # check we can still ping davenant and chiark # # 7. Make git tag and source tarball signature: # git-tag -u general -m "secnet $VERSION" -s v${VERSION//\~/_} # gpg -u general --detach-sign ../bpd/secnet_$VERSION.tar.gz # # 8. Publish the branch and distriubtion files: # git-push origin v${VERSION//\~/_} v${VERSION//\~/_}~0:master # dcmd rsync -v ../bpd/secnet_${VERSION}_multi.changes chiark:/home/ianmdlvl/public-html/secnet/download/ # # 8a. Upload to Debian: # dgit push-source # # 9. Sort out html. On chiark as user secnet: # cd ~secnet/public-html/release/ # mkdir $VERSION # cd $VERSION # ln -s /home/ianmdlvl/public-html/secnet/download/secnet?$VERSION* . # ln -sfn $VERSION ../current # # 10. write and post a release announcement # cd ../bpd # dcmd sha256sum secnet_${VERSION}_multi.changes # ... # gpg --clearsign ../release-announcement # rsync -vP ../release-announcement.asc c:mail/d/ # # 11. bump changelog version in master, to new version with ~ work/Final.sd.mk0000664000000000000000000000210115063477206010723 0ustar # Final.sd.mk for secnet # # This file is part of secnet. # See README for full list of copyright holders. # # secnet 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. # # secnet 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 # version 3 along with secnet; if not, see # https://www.gnu.org/licenses/gpl.html. # This contrives to delete things before make starts, if the user # said "recheck". The alternative is having recheck be a target # which contains the rm's and then runs $(MAKE) again but then # we recursively re-enter make in parallel, which is Bad. $(eval $(if $(filter recheck,$(MAKECMDGOALS)), \ $(shell set -x; rm -rf $(RECHECK_RM) ))) work/INSTALL0000664000000000000000000001615415063477206010002 0ustar INSTALLATION INSTRUCTIONS for SECNET USE AT YOUR OWN RISK. THIS IS ALPHA TEST SOFTWARE. I DO NOT GUARANTEE THAT THERE WILL BE PROTOCOL COMPATIBILITY BETWEEN DIFFERENT VERSIONS. * Preparation ** System software support Ensure that you have libgmp3-dev and adns installed (and bison and flex, and for that matter gcc...). [On BSD install /usr/ports/devel/bison] If you intend to configure secnet to obtain packets from the kernel through userv-ipif, install and configure userv-ipif. It is part of userv-utils, available from ftp.chiark.greenend.org.uk in /users/ian/userv If you intend to configure secnet to obtain packets from the kernel using the universal TUN/TAP driver, make sure it's configured in your kernel (it's under "network device support" in Linux-2.4) and that you've created the appropriate device files; see linux/Documentation/networking/tuntap.txt If you're using TUN/TAP on a platform other than Linux-2.4, see http://vtun.sourceforge.net/tun/ You will probably be using the supplied `make-secnet-sites' program to generate your VPN's list of sites as a secnet configuration from a more-human-writeable form. ** System and network configuration If you intend to start secnet as root, I suggest you create a userid for it to run as once it's ready to drop its privileges. Example (on Debian): # adduser --system --no-create-home secnet If you're using the 'soft routes' feature (for some classes of mobile device) you'll have to run as root all the time, to enable secnet to add and remove routes from your kernel's routing table. (This restriction may be relaxed later if someone writes a userv service to modify the routing table.) If you are joining an existing VPN, read that VPN's documentation now. It may supersede the next paragraph. In most configurations, you will need to allocate two IP addresses for use by secnet. One will be for the tunnel interface on your tunnel endpoint machine (i.e. the address you see in 'ifconfig' when you look at the tunnel interface). The other will be for secnet itself. These addresses should probably be allocated from the range used by your internal network: if you do this, you should provide appropriate proxy-ARP on the internal network interface of the machine running secnet (eg. add an entry net/ipv4/conf/eth_whatever/proxy_arp = 1 to /etc/sysctl.conf on Debian systems and run sysctl -p). Alternatively the addresses could be from some other range - this works well if the machine running secnet is the default route out of your network - but this requires more thought. http://www.ucam.org/cam-grin/ may be useful. * Installation If you installed the Debian package of secnet, skip to "If installing for the first time", below, and note that example.conf can be found in /usr/share/doc/secnet/examples. To install secnet do $ ./configure $ make # make install # mkdir /etc/secnet (Note: you may see the following warning while compiling conffile.tab.c; this is a bug in bison-1.28: /usr/share/bison/bison.simple: In function `yyparse': /usr/share/bison/bison.simple:285: warning: `yyval' might be used uninitialized in this function You may if you wish apply the following patch to bison.simple: diff -pu -r1.28.0.1 -r1.28.0.3 --- bison.s1 1999/08/30 19:23:24 1.28.0.1 +++ bison.s1 1999/08/30 21:15:18 1.28.0.3 @@ -523,8 +523,14 @@ yydefault: /* Do a reduction. yyn is the number of a rule to reduce with. */ yyreduce: yylen = yyr2[yyn]; - if (yylen > 0) - yyval = yyvsp[1-yylen]; /* implement default value of the action */ + + /* If yylen is nonzero, implement the default value of the action. + Otherwise, the following line sets yyval to the semantic value of + the lookahead token. This behavior is undocumented and bison + users should not rely upon it. Assigning to yyval + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that yyval may be used uninitialized. */ + yyval = yyvsp[1-yylen]; #if YYDEBUG != 0 if (yydebug) ) Any other warnings or errors should be reported to steve@greenend.org.uk. If installing for the first time, do # cp example.conf /etc/secnet/secnet.conf # cd /etc/secnet # ssh-keygen -f key -t rsa1 -N "" (You may need ssh-keygen1, instead, which might be found in openssh-client-ssh1.) [On BSD use $ LDFLAGS="-L/usr/local/lib" ./configure $ gmake CFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib" XXX this should eventually be worked out automatically by 'configure'.] Generate a site file fragment for your site (see your VPN's documentation, or see below), and submit it for inclusion in your VPN's 'sites' file. Download the vpn-sites file to /etc/secnet/sites - MAKE SURE YOU GET AN AUTHENTIC COPY because the sites file contains public keys for all the sites in the VPN. Use the make-secnet-sites program provided with the secnet distribution to convert the distributed sites file into one that can be included in a secnet configuration file: # make-secnet-sites /etc/secnet/sites /etc/secnet/sites.conf * Configuration Should be reasonably obvious - edit /etc/secnet/secnet.conf as prompted by the comments in example.conf. XXX Fuller documentation of the configuration file format should be forthcoming in time. Its syntax is described in the README file at the moment. * Constructing your site file fragment You need the following information: 1. the name of your VPN. 2. the name of your location(s). 3. a short name for your site, eg. "sinister". This is used to identify your site in the vpn-sites file, and should probably be the same as its hostname. 4. the DNS name of the machine that will be the "front-end" for your secnet installation. This will typically be the name of the gateway machine for your network, eg. sinister.dynamic.greenend.org.uk secnet does not actually have to run on this machine, as long as the machine can be configured to forward UDP packets to the machine that is running secnet. 5. the port number used to contact secnet at your site. This is the port number on the front-end machine, and does not necessarily have to match the port number on the machine running secnet. If you want to use a privileged port number we suggest 410. An appropriate unprivileged port number is 51396. 6. the list of networks accessible at your site over the VPN. 7. the public part of the RSA key you generated during installation (in /etc/secnet/key.pub if you followed the installation instructions). This file contains three numbers and a comment on one line. If you are running secnet on a particularly slow machine, you may like to specify a larger value for the key setup retry timeout than the default, to prevent unnecessary retransmissions of key setup packets. See the notes in the example configuration file for more on this. The site file fragment should look something like this: vpn sgo location greenend contact steve@greenend.org.uk site sinister networks 192.168.73.0/24 192.168.1.0/24 172.19.71.0/24 address sinister.dynamic.greenend.org.uk 51396 pubkey 1024 35 142982503......[lots more].....0611 steve@sinister -- This file is part of secnet. See LICENCE and CREDITS for full list of copyright holders. SPDX-License-Identifier: GPL-3.0-or-later There is NO WARRANTY. work/LICENCE0000664000000000000000000003467615063477206007747 0ustar secnet - flexible VPN software ============================== secnet is Copyright 1995-2003 Stephen Early Copyright 2002-2023 Ian Jackson Copyright 1991 Massachusetts Institute of Technology Copyright 1998 Ross Anderson, Eli Biham, Lars Knudsen Copyright 1993 Colin Plumb Copyright 1998 James H. Brown, Steve Reid Copyright 1998 Miquel van Smoorenburg, Ian Murdock Copyright 2000 Vincent Rijmen, Antoon Bosselaers, Paulo Barreto Copyright 2001 Joey Hess Copyright 2000-2006 Joachim Henkea Copyright 2012-2016 Adrien Béraud Copyright 2015 Guillaume Jacquenot Copyright 2016 SunDwarf aka eyesismine Copyright 2001 Saul Kravitz Copyright 2004 Fabrice Bellard Copyright 2002 Guido Draheim Copyright 2005-2010 Free Software Foundation, Inc. Copyright 1995-2001 Jonathan Amery Copyright 1995-2003 Peter Benie Copyright 2011 Richard Kettlewell Copyright 2012 Matthew Vernon Copyright 2013-2019 Mark Wooding Copyright 1995-2013 Simon Tatham Copyright 2012,2013 "Omnifarious" and "btel" on Stackoverflow GPLv3+ ====== secnet is distributed under the terms of the GNU General Public License, version 3 or later. secnet 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. The file COPYING in the source tree contains a copy of the GNU GPL v3. A copy of this licence can be found on Debian systems (and derivatives) in /usr/share/common-licenses/GPL-3. Some files contain a notice with the abbreviated form: SPDX-License-Identifier: GPL-3.0-or-later As is conventional, this should be read as a licence grant. Some included libraries contain CC-BY-SA 4.0 code, which is not necessarily compatibile with future GPL versions, so the effective licence for a built and distributed secnet package is GPLv3 only. Contributing ============ Contributions to secnet are accepted based on the git commit Signed-off-by convention, by which the contributors' certify their contributions according to the Developer Certificate of Origin version 1.1 - see the file DEVELOPER-CERTIFICATE. In accordance with the Developer Certificate of Origin, if you make a contribution to one of the incorporated works with a more liberal licence, you will be taken to have made your contribution under that more liberal licence. If you create a new file please be sure to add an appropriate licence header. In the main parts of secnet this should be something like: * This file is part of secnet. * See LICENCE and CREDITS for full list of copyright holders. * SPDX-License-Identifier: GPL-3.0-or-later * There is NO WARRANTY. Incorporated works ================== Some of the source code has more liberal licences, all of which are GPLv3-compatible, and potentially GPLv3+-compatible. These portions can be used, distributed, and modified, separately, under the more liberal licence. But when they are used or distributed as part of secnet, the GPL applies to the combination. ac_prog_cc_no_writeable_strings.m4 ----------------------------------- This is a version of ax_cflags_no_writable_strings.m4 which is now in the Autoconf Archive, where it has this permission notice: Copyright (c) 2008 Guido U. Draheim 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 . As a special exception, the respective Autoconf Macro's copyright owner gives unlimited permission to copy, distribute and modify the configure scripts that are the output of Autoconf when processing the Macro. You need not follow the terms of the GNU General Public License when using or distributing such scripts, even though portions of the text of the Macro appear in them. The GNU General Public License (GPL) does govern all other use of the material that constitutes the Autoconf Macro. This special exception to the GPL applies to versions of the Autoconf Macro released by the Autoconf Archive. When you make and distribute a modified version of the Autoconf Macro, you may extend this special exception to the GPL to apply to your modified version as well. argparseactionnoyes.py ---------------------- Copyright 2012 "Omnifarious" (a user on StackOverFlow) Copyright 2013 "btel" (a user on StackOverFlow) https://stackoverflow.com/questions/9234258/in-python-argparse-is-it-possible-to-have-paired-no-something-something-arg/20422915#20422915 CC-BY-SA 4.0 https://creativecommons.org/licenses/by-sa/4.0/legalcode by virtue of https://stackoverflow.com/legal/terms-of-service#licensing which says everything is CC-BY-SA and has a link to v4.0 (And which is therefore compatible with secnet's GPLv3) all retrieved 4.11.2019 and rechecked 12.11.2022 aes.[ch] -------- Copyright 2000 Vincent Rijmen, Antoon Bosselaers, Paulo Barreto Copyright 2004 Fabrice Bellard Copyright 2013 Ian Jackson Original licence: This code is hereby placed in the public domain. THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. base91-c/ --------- Copyright (c) 2000-2006 Joachim Henke Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Joachim Henke nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. base91-python/ --------------- Copyright (c) 2012 Adrien Beraud Copyright (c) 2015 Guillaume Jacquenot Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Adrien Beraud, Wisdom Vibes Pte. Ltd., nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. configure --------- This is autogenerated from various other inputs, including FSF code in utotools, regarding which the FSF say this: Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. However, in fact, the script is GPLv3+ because it contains pieces derived from secnet's aclocal.m4, etc. debian/ ------- debian/init is from an original init script example by Miquel van Smoorenburg and Ian Murdock. Modified by Richard Kettlewell. debian/rules was once from an example from Joey Hess and is now (after most of it was deleted) probably entirely the work of Ian Jackson. The remainder of debian/ is by the authors of Secnet (with possible future changes made by Debian contributors). install-sh ---------- Copyright 1991 by the Massachusetts Institute of Technology Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of M.I.T. not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. M.I.T. makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. md5.[ch] -------- Authored by Colin Plumb in 1993. Public domain statement: This code implements the MD5 message-digest algorithm. The algorithm is due to Ron Rivest. This code was written by Colin Plumb in 1993, no copyright is claimed. This code is in the public domain; do with it what you wish. I interpet this as a blanket permision. serpent.c, serpentsboxes.h --------------------------- Authored by Ross Anderson, Eli Biham, Lars Knudsen. Quoting the Serpent web page: Serpent is now completely in the public domain, and we impose no restrictions on its use. This was announced on the 21st August at the First AES Candidate Conference. The optimised implementations in the submission package are now under the General Public License (GPL), although some comments in the code still say otherwise. You are welcome to use Serpent for any application. If you do use it, we would appreciate it if you would let us know! https://www.cl.cam.ac.uk/~rja14/serpent.html I have indeed corresponded with Ross Anderson about the use of Serpent in secnet, and have updated the source comments to reflect the licence upgrade. sha1.c ------ Authored by Steve Reid and modified by James H. Brown, Saul Kravitz, and Ian Jackson. 100% Public Domain I interpet this as a blanket permision. sha512.[ch] ----------- Copyright (C) 2005, 2006, 2008, 2009, 2010 Free Software Foundation, Inc. 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. Written by David Madore, considerably copypasting from Scott G. Miller's sha1.c subdirmk/ --------- Copyright 2019-2020 Ian Jackson Copyright 2019 Mark Wooding subdirmk and its example is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library as the file LGPL-2. If not, see https://www.gnu.org/. A copy of this licence can be found on Debian systems (and derivatives) in /usr/share/common-licenses/LGPL-3. u64.h ----- uint64_t-like operations that work even on hosts lacking uint64_t Copyright (C) 2006, 2009, 2010 Free Software Foundation, Inc. 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. Written by Paul Eggert. work/LICENSE0000777000000000000000000000000015063477206010727 2LICENCEustar work/NOTES0000664000000000000000000004525115063477206007564 0ustar * Design of new, multi-subnet secnet protocol Like the first (1995/6) version, we're tunnelling IP packets inside UDP packets. To defeat various restrictions which may be imposed on us by network providers (like the prohibition of incoming TCP connections) we're sticking with UDP for everything this time, including key setup. This means we have to handle retries, etc. Other new features include being able to deal with subnets hidden behind changing 'real' IP addresses, and the ability to choose algorithms and keys per pair of communicating sites. ** Configuration and structure [The original plan] The network is made up from a number of 'sites'. These are collections of machines with private IP addresses. The new secnet code runs on machines which have interfaces on the private site network and some way of accessing the 'real' internet. Each end of a tunnel is identified by a name. Often it will be convenient for every gateway machine to use the same name for each tunnel endpoint, but this is not vital. Individual tunnels are identified by their two endpoint names. [The new plan] It appears that people want to be able to use secnet on mobile machines like laptops as well as to interconnect sites. In particular, they want to be able to use their laptop in three situations: 1) connected to their internal LAN by a cable; no tunnel involved 2) connected via wireless, using a tunnel to protect traffic 3) connected to some other network, using a tunnel to access the internal LAN. They want the laptop to keep the same IP address all the time. Case (1) is simple. Case (2) requires that the laptop run a copy of secnet, and have a tunnel configured between it and the main internal LAN default gateway. secnet must support the concept of a 'soft' tunnel where it adds a route and causes the gateway to do proxy-ARP when the tunnel is up, and removes the route again when the tunnel is down. The usual prohibition of packets coming in from one tunnel and going out another must be relaxed in this case (in particular, the destination address of packets from these 'mobile station' tunnels may be another tunnel as well as the host). (Quick sanity check: if chiark's secnet address was in 192.168.73.0/24, would this work properly? Yes, because there will be an explicit route to it, and proxy ARP will be done for it. Do we want packets from the chiark tunnel to be able to go out along other routes? No. So, spotting a 'local' address in a remote site's list of networks isn't sufficient to switch on routing for a site. We need an explicit option. NB packets may be routed if the source OR the destination is marked as allowing routing [otherwise packets couldn't get back from eg. chiark to a laptop at greenend]). [the even newer plan] secnet sites are configured to grant access to particular IP address ranges to the holder of a particular public key. The key can certify other keys, which will then be permitted to use a subrange of the IP address range of the certifying key. This means that secnet won't know in advance (i.e. at configuration time) how many tunnels it might be required to support, so we have to be able to create them (and routes, and so on) on the fly. ** VPN-level configuration At a high level we just want to be able to indicate which groups of users can claim ownership of which ranges of IP addresses. Assuming these users (or their representatives) all have accounts on a single machine, we can automate the submission of keys and other information to make up a 'sites' file for the entire VPN. The distributed 'sites' file should be in a more restricted format than the secnet configuration file, to prevent attackers who manage to distribute bogus sites files from taking over their victim's machines. The distributed 'sites' file is read one line at a time. Each line consists of a keyword followed by other information. It defines a number of VPNs; within each VPN it defines a number of locations; within each location it defines a number of sites. These VPNs, locations and sites are turned into a secnet.conf file fragment using a script. Some keywords are valid at any 'level' of the distributed 'sites' file, indicating defaults. The keywords are: vpn n: we are now declaring information to do with VPN 'n'. Must come first. location n: we are now declaring information for location 'n'. site n: we are now declaring information for site 'n'. endsite: we're finished declaring information for the current site restrict-nets a b c ...: restrict the allowable 'networks' for the current level to those in this list. end-definitions: prevent definition of further vpns and locations, and modification of defaults at VPN level dh x y: the current VPN uses the specified group; x=modulus, y=generator hash x: which hash function to use. Valid options are 'md5' and 'sha1'. admin n: administrator email address for current level key-lifetime n setup-retries n setup-timeout n wait-time n renegotiate-time n address a b: a=dnsname, b=port networks a b c ... pubkey x y z: x=keylen, y=encryption key, z=modulus mobile: declare this to be a 'mobile' site ** Logging etc. There are several possible ways of running secnet: 'reporting' only: --version, --help, etc. command line options and the --just-check-config mode. 'normal' run: perform setup in the foreground, and then background. 'failed' run: setup in the foreground, and terminate with an error before going to background. 'reporting' modes should never output anything except to stdout/stderr. 'normal' and 'failed' runs output to stdout/stderr before backgrounding, then thereafter output only to log destinations. ** Site long-term keys We use authenticated DH. Sites identify themselves to each other using long-term signing keys. These signing keys may be for a variety of algorithms. (An algorithm specifies completely how to do a signature and verification.) Each site may have several keys. This helps support key rollover and algorithm agility. Several keys of different algorithms can form a key group. Usually a key group consists of keys generated at the same time. A key is identified by a 4-byte group id (invented by its publisher and opaque) plus a 1-byte algorithm id (defined by the protocol spec for each algorithm). Keys are published in key sets. A key set is a collection of key groups (including older keys as well as newer ones) published at a particular time. Key sets have their own 4-byte ids; these are invented by the publisher but are ordered using sequence number arithmetic. This allows reliers to favour new sets over old ones. Within each key set, some groups may be marked as `fallback'. This means a group that should be tolerated by a relier only if the relier doesn't support any non-fallback keys. Keys within groups, and groups within sets, are ordered (by the publisher of the set), from most to least preferred. When deciding which public keys to accept, a relier should: Process each group within the key set. Discard unknown algorithms. Choose a preferred algorithm: Earliest in the group (or local config could have algorithm prefererence). Discard empty groups. Discard unneeded fallback groups: If any (non-empty) non-fallback groups found, discard all fallback groups. Otherwise there are only fallback groups; discard all but first group in the set. Discard any keys exceeding limit on number of keys honoured: Limit is at least 4 Discard keys later in the set In wire protocol, offer the resulting subset of keyids to the peer and a allow the signer to select which key to use from that subset. In configuration and key management, long-term private and public keys are octet strings. Private keys are generally stored in disk files, one key per file. The octet string for a private key should identify the algorithm so that passing the private key to the code for the wrong algorithm does not produce results which would leak or weaken the key. The octet string for a public key need not identify the algorithm; when it's loaded the algorithm will be known from context. The group id 00000000 is special. It should contain only one key, algorithm 00. Key 0000000000 refers to the rsa1 key promulgated before the key rollover/advertisement protocols, or the key which should be used by sites running old software. The key set id 00000000 is special and is considered older than all othere key sets (ie this is an exception to the sequence number arithmetic). It is the implied key set id of the rsa1 key promulgated before the key rollover/advertisement protocols. The algorithm 00 is special and refers to the old rsa1 signature protocol but unusually does not identify the hash function. The hash function is conventional and must be specified out of band. In known existing installations it is SHA-1. ** Protocols *** Protocol environment: Each gateway machine serves a particular, well-known set of private IP addresses (i.e. the agreement over which addresses it serves is outside the scope of this discussion). Each gateway machine has an IP address on the interconnecting network (usually the Internet), which may be dynamically allocated and may change at any point. Each gateway knows the RSA public keys of the other gateways with which it wishes to communicate. The mechanism by which this happens is outside the scope of this discussion. There exists a means by which each gateway can look up the probable IP address of any other. *** Protocol goals: The ultimate goal of the protocol is for the originating gateway machine to be able to forward packets from its section of the private network to the appropriate gateway machine for the destination machine, in such a way that it can be sure that the packets are being sent to the correct destination machine, the destination machine can be sure that the source of the packets is the originating gateway machine, and the contents of the packets cannot be understood other than by the two communicating gateways. XXX not sure about the address-change stuff; leave it out of the first version of the protocol. From experience, IP addresses seem to be quite stable so the feature doesn't gain us much. **** Protocol sub-goal 1: establish a shared key Definitions: A is the originating gateway machine name B is the destination gateway machine name A+ and B+ are the names with optional additional data, see below PK_A is the public RSA key of A PK_B is the public RSA key of B PK_A^-1 is the private RSA key of A PK_B^-1 is the private RSA key of B x is the fresh private DH key of A y is the fresh private DH key of B k is g^xy mod m g and m are generator and modulus for Diffie-Hellman nA is a nonce generated by A nB is a nonce generated by B iA is an index generated by A, to be used in packets sent from B to A iB is an index generated by B, to be used in packets sent from A to B i? is appropriate index for receiver Note that 'i' may be re-used from one session to the next, whereas 'n' is always fresh. The optional additional data after the sender's name consists of some initial subset of the following list of items: * A 32-bit integer with a set of capability flags, representing the abilities of the sender. * In MSG3/MSG4: a 16-bit integer being the sender's MTU, or zero. (In other messages: nothing.) See below. * In MSG2/MSG3: a list of the peer's public keys that the sender will accept: (i) a 1-byte integer count (ii) that many 5-byte key ids. If not present, implicitly only the special key id 0000000000. * In MSG3/MSG4: an 8-bit integer being an index into the receiver's public key acceptance list, with which the message is signed. If not present, implicitly the key id 00000000000. * More data which is yet to be defined and which must be ignored by receivers. The optional additional data after the receiver's name is not currently used. If any is seen, it must be ignored. Capability flag bits must be in one the following two categories: 1. Early capability flags must be advertised in MSG1 or MSG2, as applicable. If MSG3 or MSG4 advertise any "early" capability bits, MSG1 or MSG3 (as applicable) must have advertised them too. 2. Late capability flags may be advertised only in MSG2 or MSG3, as applicable. They are only in MSG1 with newer secnets; older versions omit them. MSG4 must advertise the same set as MSG2. Currently, the low 16 bits are allocated for negotiating bulk-crypto transforms. Bits 8 to 15 are used by Secnet as default capability numbers for the various kinds of transform closures: bit 8 is for the original CBCMAC-based transform, and bit 9 for the new EAX transform; bits 10 to 15 are reserved for future expansion. The the low eight bits are reserved for local use, e.g., to allow migration from one set of parameters for a particular transform to a different, incompatible set of parameters for the same transform. Bit 31, if advertised by both ends, indicates that a mobile end gets priority in case of crossed MSG1. The remaining bits have not yet been assigned a purpose. Whether a capability number is early depends on its meaning, rather than being a static property of its number. That said, the mobile-end-gets priority bit (31) is always sent as an `early' capability bit. MTU handling In older versions of secnet, secnet was not capable of fragmentation or sending ICMP Frag Needed. Administrators were expected to configure consistent MTUs across the network. It is still the case in the current version that the MTUs need to be configured reasonably coherently across the network: the allocated buffer sizes must be sufficient to cope with packets from all other peers. However, provided the buffers are sufficient, all packets will be processed properly: a secnet receiving a packet larger than the applicable MTU for its delivery will either fragment it, or reject it with ICMP Frag Needed. The MTU additional data field allows secnet to advertise an MTU to the peer. This allows the sending end to handle overlarge packets, before they are transmitted across the underlying public network. This can therefore be used to work around underlying network braindamage affecting large packets. If the MTU additional data field is zero or not present, then the peer should use locally-configured MTU information (normally, its local netlink MTU) instead. If it is nonzero, the peer may send packets up to the advertised size (and if that size is bigger than the peer's administratively configured size, the advertiser promises that its buffers can handle such a large packet). A secnet instance should not assume that just because it has advertised an mtu which is lower than usual for the vpn, the peer will honour it, unless the administrator knows that the peers are sufficiently modern to understand the mtu advertisement option. So secnet will still accept packets which exceed the link MTU (whether negotiated or assumed). Messages: 1) A->B: i*,iA,msg1,A+,B+,nA i* must be encoded as 0. (However, it is permitted for a site to use zero as its "index" for another site.) 2) B->A: iA,iB,msg2,B+,A+,nB,nA (The order of B and A reverses in alternate messages so that the same code can be used to construct them...) 3) A->B: {iB,iA,msg3,A+,B+,[chosen-transform],nA,nB,g^x mod m}_PK_A^-1 If message 1 was a replay then A will not generate message 3, because it doesn't recognise nA. If message 2 was from an attacker then B will not generate message 4, because it doesn't recognise nB. 4) B->A: {iA,iB,msg4,B+,A+,nB,nA,g^y mod m}_PK_B^-1 At this point, A and B share a key, k. B must keep retransmitting message 4 until it receives a packet encrypted using key k. 5) A: iB,iA,msg5,(ping/msg5)_k 6) B: iA,iB,msg6,(pong/msg6)_k (Note that these are encrypted using the same transform that's used for normal traffic, so they include sequence number, MAC, etc.) The ping and pong messages can be used by either end of the tunnel at any time, but using msg0 as the unencrypted message type indicator. **** Protocol sub-goal 2: end the use of a shared key 7) i?,i?,msg0,(end-session/msg7,A,B)_k This message can be sent by either party. Once sent, k can be forgotten. Once received and checked, k can be forgotten. No need to retransmit or confirm reception. It is suggested that this message be sent when a key times out, or the tunnel is forcibly terminated for some reason. **** Protocol sub-goal 3: send a packet 8) i?,i?,msg0,(send-packet/msg9,packet)_k **** Other messages 9) i?,i?,NAK (NAK is encoded as zero) If the link-layer can't work out what to do with a packet (session has gone away, etc.) it can transmit a NAK back to the sender. This can alert the sender to the situation where the sender has a key but the receiver doesn't (eg because it has been restarted). The sender, on receiving the NAK, will try to initiate a key exchange. Forged (or overly delayed) NAKs can cause wasted resources due to spurious key exchange initiation, but there is a limit on this because of the key exchange retry timeout. 10) i?,i?,msg8,A,B,nA,nB,msg? This is an obsolete form of NAK packet which is not sent by any even vaguely recent version of secnet. (In fact, there is no evidence in the git history of it ever being sent.) This message number is reserved. 11) *,*,PROD,A,B Sent in response to a NAK from B to A. Requests that B initiates a key exchange with A, if B is willing and lacks a transport key for A. (If B doesn't have A's address configured, implicitly supplies A's public address.) This is necessary because if one end of a link (B) is restarted while a key exchange is in progress, the following bad state can persist: the non-restarted end (A) thinks that the key is still valid and keeps sending packets, but B either doesn't realise that a key exchange with A is necessary or (if A is a mobile site) doesn't know A's public IP address. Normally in these circumstances B would send NAKs to A, causing A to initiate a key exchange. However if A and B were already in the middle of a key exchange then A will not want to try another one until the first one has timed out ("setup-time" x "setup-retries") and then the key exchange retry timeout ("wait-time") has elapsed. However if B's setup has timed out, B would be willing to participate in a key exchange initiated by A, if A could be induced to do so. This is the purpose of the PROD packet. We send no more PRODs than we would want to send data packets, to avoid a traffic amplification attack. We also send them only in state WAIT, as in other states we wouldn't respond favourably. And we only honour them if we don't already have a key. With PROD, the period of broken communication due to a key exchange interrupted by a restart is limited to the key exchange total retransmission timeout, rather than also including the key exchange retry timeout. -- This file is part of secnet. See LICENCE and CREDITS for full list of copyright holders. SPDX-License-Identifier: GPL-3.0-or-later There is NO WARRANTY. work/NOTES.peer-keys0000664000000000000000000001146215063477206011464 0ustar peerkeys files -------------- live file, loaded on startup, updated by secnet (only). * in-memory peerkeys_current is kept synced with this file ~update update file from config manager, checked before every key exchange. config manager must rename this file into place; it will be renamed and then removed by secnet. ~proc update file being processed by secnet. only secnet may write or remove. ~incoming update file from peer, being received by secnet may be incomplete, unverified, or even malicious only secnet may write or remove. ~tmp update file from config manager, only mss may write or rename secnet discards updates that are not more recent than (by serial) the live file. But it may not process updates immediately. The implied keyset to be used is MAX(live, proc, update). secnet does: check live vs proc, either mv proc live or rm proc if proc doesn't exist, mv update proc make-secnet-sites does: write: rename something onto update read: read update,proc,live in that order and take max We support only one concurrent secnet, one concurrent writing make-secnet-sites, and any number of readers. We want to maintain a live file at all times as that is what secnet actually reads at startup and uses. Proof that this is sound: Let us regard update,proc,live as i=0,1,2 Files contain public key sets and are manipulated as a whole, and we may regard key sets with the same serial as equivalent. We talk below about reading as if it were atomic. Actually the atomic operation is open(2); the reading gets whatever that name refers to. So we can model this as an atomic read. secnet eventually moves all data into the live file or deletes it, so there should be no indefinitely stale data; informally this means we can disregard the possibility of very old serials and regard serials as fully ordered. (We don't bother with a formal proof of this property.) Consequently we will only think about the serial and not the contents. We treat absent files as minimal (we will write -1 for convenience although we don't mean a numerical value). We write S(i). Invariant 1 for secnet's transformations is as follows: Each file S(i) is only reduced (to S'(i)) if for some j S'(j) >= S(i), with S'(j) either being >= S(i) beforehand, or updated atomically together with S(i). Proof of invariant 1 for the secnet operations: (a) check live vs proc, proc>live, mv: j=2, i=1; S'(i)=-1, so S(i) is being reduced. S'(j) is equal to S(i), and the rename is atomic [1], so S'(j) and S'(i) are updated simultaneously. S(j) is being increased. (There are no hazards from concurrent writers; only we ourselves (secnet) write to live or proc.) (b) check live vs proc, proc<=live, rm: j=2, i=1; S'(i)=-1, so S(i) is being reduced. But S(j) is >= $(i) throughout. (Again, no concurrent writer hazards.) (c) mv update proc (when proc does not exist): j=1, i=0; S(i) is being reduced to -1. But simultaneously S(j) is being increased to the old S(i). Our precondition (proc not existing) is not subject to a concurrent writer hazards because only we write to proc; our action is atomic and takes whatever update is available (if any). Proof of soundness for the mss reading operation: Let M be MAX(\forall S) at the point where mss reads update. Invariant 2: when mss reads S(k), MAX(K, S(k)..S(2)) >= M, where K is the max S it has seen so far. Clearly this is true for k=0 (with K==-1). secnet's operations never break this invariant because if any S() is reduced, another one counted must be increased. mss's step operation updates K with S(k), so MAX(K', S(k+1)..)=MAX(K, S(k)..), and updates k to k+1, preserving the invariant. At the end we have k=3 and K=>M. Since secnet never invents serials, K=M in the absence of an mss update with a bigger S. Consideration of the mss update operation: Successive serials from sites file updates etc. are supposed to be increasing. When this is true, M is increased. A concurrent reading mss which makes its first read after the update will get the new data (by the proofs above). This seems to be the required property. QED. [1] From "Base Specifications issue 7", 2.9.7 Thread Interactions with Regular File Operations All of the following functions shall be atomic with respect to each other in the effects specified in POSIX.1-2017 when they operate on regular files or symbolic links: ... rename ... open ... -- This file is part of secnet. See LICENCE and CREDITS for full list of copyright holders. SPDX-License-Identifier: GPL-3.0-or-later There is NO WARRANTY. work/OLD-BUGS0000664000000000000000000000103315063477206010036 0ustar Known bugs in secnet (Complaints from Ian:) Your init.d script makes it hard to start secnet as non-root, too. secnet -jv has printed a large routing table full of stuff I wasn't interested in. Make explicit in the documentation that -n causes all log output to go to stderr. Provide an option that is _really_ just "don't fork()" for people who want to run secnet from init. -- This file is part of secnet. See LICENCE and CREDITS for full list of copyright holders. SPDX-License-Identifier: GPL-3.0-or-later There is NO WARRANTY. work/OLD-NEWS0000664000000000000000000003003015063477206010051 0ustar * Planned for the future Please note that the 0.1 series of secnet releases is now 'maintenance only'; further development continues in secnet-0.2. Debconf support - if you are using the Debian packaged version and your secnet configuration is autogenerated using debconf then the upgrade to version 0.2.0 should just involve installing the package; an appropriate 0.2-style configuration file will be generated automatically. * New in version 0.1.18 ipaddr.py now declares its character encoding; required by recent versions of Python * New in version 0.1.17 autoconf updates for cross-compilation / more modern autoconf from Ross Younger MacOS X support from Richard Kettlewell Makefile fix: Update bison pattern rule to indicate that both the .tab.c and .tab.h files are generated by the same command. i386 ip_csum implementation updated to work with modern gcc Rename global 'log' to 'slilog' to avoid conflict with gcc built-in log() function. * New in version 0.1.16 XXX XXX PROTOCOL COMPATIBILITY IS BROKEN BETWEEN VERSION 0.1.16 AND XXX XXX ALL PREVIOUS VERSIONS. Bugfix: rsa.c private-key now works properly when you choose not to verify it. Bugfix: serpent key setup was only using the first 8 bytes of the key material. (Oops!) Ian Jackson contributed a fix so the full 32 bytes are used, in big-endian mode. Debatable-bugfix: RSA operations now use PKCS1 v1.5-style padding "Hacky parallelism" contributed by Ian Jackson; this permits public-key operations to be performed in a subprocess during key exchange, to make secnet more usable on very slow machines. This is not compiled in by default; if you find you need it (because key exchanges are taking more than a second or two) then add -DHACKY_PARALLEL to FLAGS in the Makefile.in and recompile. udp module updates from Peter Benie: 1) Handle the case where authbind-helper terminates with a signal 2) Cope with signals being delivered during waitpid 3) Add 'address' (optional) to the udp settings. This is an IP address that the socket will be bound to. 4) Change the endianess of the arguments to authbind-helper. sprintf("%04X") already translates from machine repesentation to most significant octet first so htons reversed it again. All uses of alloca() expunged by Peter Benie. make-secnet-sites now supports configurations where each tunnel gets its own interface on the host, and the IP router code in secnet is disabled. make-secnet-sites has been rewritten for clarity. For information on how to configure secnet for one-interface-per-tunnel, see the example.conf file. * New in version 0.1.15 Now terminates with an error when an "include" filename is not specified in the configuration file (thanks to RJK). RSA private key operations optimised using CRT. Thanks to SGT. Now compiles cleanly with -Wwrite-strings turned on in gcc. Anything sent to stderr once secnet has started running in the background is now redirected to the system/log facility. * New in version 0.1.14 The --help and --version options now send their output to stdout. Bugfix: TUN flavour "BSD" no longer implies a BSD-style ifconfig and route command invocation. Instead "ioctl"-style is used, which should work on both BSD and linux-2.2 systems. If no "networks" parameter is specified for a netlink device then it is assumed to be 0.0.0.0/0 rather than the empty set. So, by default there is a default route from each netlink device to the host machine. The "networks" parameter can be used to implement a primitive firewall, restricting the destination addresses of packets received through tunnels; if a more complex firewall is required then implement it on the host. * New in version 0.1.13 site.c code cleaned up; no externally visible changes secnet now calls setsid() after becoming a daemon. secnet now supports TUN on Solaris 2.5 and above (and possibly other STREAMS-based systems as well). The TUN code now tries to auto-detect the type of "TUN" in use (BSD-style, Linux-style or STREAMS-style). If your configuration file specifies "tun-old" then it defaults to BSD-style; however, since "tun-old" will be removed in a future release, you should change your configuration file to specify "tun" and if there's a problem also specify the flavour in use. Example: netlink tun-old { ... }; should be rewritten as netlink tun { flavour "bsd"; ... }; The flavours currently defined are "bsd", "linux" and "streams". The TUN code can now be configured to configure interfaces and add/delete routes using one of several methods: invoking a "linux"-style ifconfig/route command, a "bsd"-style ifconfig/route command, "solaris-2.5"-style ifconfig/route command or calling ioctl() directly. These methods can be selected using the "ifconfig-type" and "route-type" options. Example: netlink tun { ifconfig-type "ioctl"; route-type "ioctl"; ... }; The ioctl-based method is now the default for Linux systems. Magic numbers used within secnet are now collected in the header file "magic.h". netlink now uses ICMP type=0 code=13 for 'administratively prohibited' instead of code 9. See RFC1812 section 5.2.7.1. The UDP comm module now supports a proxy server, "udpforward". This runs on a machine which is directly accessible by secnet and which can send packets to appropriate destinations. It's useful when the proxy machine doesn't support source- and destination-NAT. The proxy server is specified using the "proxy" key in the UDP module configuration; parameters are IP address (string) and port number. Bugfix: ipset_to_subnet_list() in ipaddr.c now believed to work in all cases, including 0.0.0.0/0 * New in version 0.1.12 IMPORTANT: fix calculation of 'now' in secnet.c; necessary for correct operation. (Only interesting for people building and modifying secnet by hand: the Makefile now works out most dependencies automatically.) The netlink code no longer produces an internal routing table sorted by netmask length. Instead, netlink instances have a 'priority'; the table of routes is sorted by priority. Devices like laptops that have tunnels that must sometimes 'mask' parts of other tunnels should be given higher priorities. If a priority is not specified it is assumed to be zero. Example usage: site laptop { ... link netlink { route "192.168.73.74/31"; priority 10; }; }; * New in version 0.1.11 Lists of IP addresses in the configuration file can now include exclusions as well as inclusions. For example, you can specify all the hosts on a subnet except one as follows: networks "192.168.73.0/24","!192.168.73.70"; (If you were only allowed inclusions, you'd have to specify that like this: networks "192.168.73.71/32","192.168.73.68/31","192.168.73.64/30", "192.168.73.72/29","192.168.73.80/28","192.168.73.96/27", "192.168.73.0/26","192.168.73.128/25"; ) secnet now ensures that it invokes userv-ipif with a non-overlapping list of subnets. There is a new command-line option, --sites-key or -s, that enables the configuration file key that's checked to determine the list of active sites (default "sites") to be changed. This enables a single configuration file to contain multiple cofigurations conveniently. NAKs are now sent when packets arrive that are not understood. The tunnel code initiates a key setup if it sees a NAK. Future developments should include configuration options that control this. The tunnel code notifies its peer when secnet is terminating, so the peer can close the session. The netlink "exclude-remote-networks" option has now been replaced by a "remote-networks" option; instead of specifying networks that no site may access, you specify the set of networks that remote sites are allowed to access. A sensible example: "192.168.0.0/16", "172.16.0.0/12", "10.0.0.0/8", "!your-local-network" * New in version 0.1.10 WARNING: THIS VERSION MAKES A CHANGE TO THE CONFIGURATION FILE FORMAT THAT IS NOT BACKWARD COMPATIBLE. However, in most configurations the change only affects the sites.conf file, which is generated by the make-secnet-sites script; after you regenerate your sites.conf using version 0.1.10, everything should continue to work. Netlink devices now interact slightly differently with the 'site' code. When you invoke a netlink closure like 'tun' or 'userv-ipif', you get another closure back. You then invoke this closure (usually in the site definitions) to specify things like routes and options. The result of this invocation should be used as the 'link' option in site configurations. All this really means is that instead of site configurations looking like this: foo { name "foo"; networks "a", "b", "c"; etc. }; ...they look like this: foo { name "foo"; link netlink { routes "a", "b", "c"; }; etc. }; This change was made to enable the 'site' code to be completely free of any knowledge of the contents of the packets it transmits. It should now be possible in the future to tunnel other protocols like IPv6, IPX, raw Ethernet frames, etc. without changing the 'site' code at all. Point-to-point netlink devices work slightly differently; when you apply the 'tun', 'userv-ipif', etc. closure and specify the ptp-address option, you must also specify the 'routes' option. The result of this invocation should be passed directly to the 'link' option of the site configuration. You can do things like this: sites site { name "foo"; link tun { networks "192.168.73.76/32"; local-address "192.168.73.76"; # IP address of interface ptp-address "192.168.73.75"; # IP address of other end of link routes "192.168.73.74/32"; mtu 1400; buffer sysbuffer(); }; etc. }; The route dump obtained by sending SIGUSR1 to secnet now includes packet counts. Point-to-point mode has now been tested. tun-old has now been tested, and the annoying 'untested' message has been removed. Thanks to SGT and JDA. secnet now closes its stdin, stdout and stderr just after backgrounding. Bugfix: specifying network "0.0.0.0/0" (or "default") now works correctly. * New in version 0.1.9 The netlink code may now generate ICMP responses to ICMP messages that are not errors, eg. ICMP echo-request. This makes Windows NT traceroute output look a little less strange. configure.in and config.h.bot now define uint32_t etc. even on systems without stdint.h and inttypes.h (needed for Solaris 2.5.1) GNU getopt is included for systems that lack it. We check for LOG_AUTHPRIV before trying to use it in log.c (Solaris 2.5.1 doesn't have it.) Portable snprintf.c from http://www.ijs.si/software/snprintf/ is included for systems that lack snprintf/vsnprintf. make-secnet-sites.py renamed to make-secnet-sites and now installed in $prefix/sbin/make-secnet-sites; ipaddr.py library installed in $prefix/share/secnet/ipaddr.py. make-secnet-sites searches /usr/local/share/secnet and /usr/share/secnet for ipaddr.py * New in version 0.1.8 Netlink devices now support a 'point-to-point' mode. In this mode the netlink device does not require an IP address; instead, the IP address of the other end of the tunnel is specified using the 'ptp-address' option. Precisely one site must be configured to use the netlink device. (I haven't had a chance to test this because 0.1.8 turned into a 'quick' release to enable secnet to cope with the network problems affecting connections going via LINX on 2001-10-16.) The tunnel code in site.c now initiates a key setup if the reverse-transform function fails (wrong key, bad MAC, too much skew, etc.) - this should make secnet more reliable on dodgy links, which are much more common than links with active attackers... (an attacker can now force a new key setup by replaying an old packet, but apart from minor denial of service on slow links or machines this won't achieve them much). This should eventually be made configurable. The sequence number skew detection code in transform.c now only complains about 'reverse skew' - replays of packets that are too old. 'Forward skew' (gaps in the sequence numbers of received packets) is now tolerated silently, to cope with large amounts of packet loss. -- This file is part of secnet. See LICENCE and CREDITS for full list of copyright holders. SPDX-License-Identifier: GPL-3.0-or-later There is NO WARRANTY. work/README0000664000000000000000000005633615063477206007637 0ustar secnet - flexible VPN software See LICENCE for legal information and CREDITS for a list of contributors. * Introduction secnet allows large virtual private networks to be constructed spanning multiple separate sites. It is designed for the case where a private network connecting many hosts is 'hidden' behind a single globally-routable IP address, but can also be applied in other circumstances. It communicates entirely using UDP, and works well with gateways that implement network address translation. If you are installing secnet to join an existing VPN, you should read the 'INSTALL' file and your particular VPN's documentation now. You may need to refer back to this file for information on the netlink and comm sections of the configuration file. If you are thinking about setting up a new VPN of any size (from one providing complete links between multiple sites to a simple laptop-to-host link), read the section in this file on 'Creating a VPN'. * Mailing lists and bug reporting There are two mailing lists associated with secnet: an 'announce' list and a 'discuss' list. Their addresses are: http://www.chiark.greenend.org.uk/mailman/listinfo/secnet-announce http://www.chiark.greenend.org.uk/mailman/listinfo/secnet-discuss The -announce list receives one message per secnet release. The -discuss list is for general discussion, including help with configuration, bug reports, feature requests, etc. Bug reports should be sent to ; they will be forwarded to the -discuss list by me. * Creating a VPN XXX TODO * secnet configuration file format By default secnet on linux reads /etc/secnet/secnet.conf. The default may be different on other platforms. This file defines a dictionary (a mapping from keys to values) full of configuration information for secnet. Two keys must be defined in this file for secnet to start. One is "system", a dictionary containing systemwide control parameters. The other is "sites", a list of all the sites that you intend to communicate with. The configuration file has a very simple syntax; keys are defined as follows: key definition; or key = definition; (the "=" is optional) Keys must match the following regular expression: [[:alpha:]_][[:alnum:]\-_]* i.e. the first character must be an alpha or an underscore, and the remaining characters may be alphanumeric, '-' or '_'. Keys can be defined to be a comma-separated list of any of the following types: a boolean a string, in quotes a number, in decimal a dictionary of definitions, enclosed in { } a "closure", followed by arguments a path to a key that already exists, to reference that definition Note that dictionaries can be nested: a key in one dictionary can refer to another dictionary. When secnet looks for a key in a particular directory and can't find it, it looks in the dictionary's lexical 'parents' in turn until it finds it (or fails to find it at all and stops with an error). Definitions can refer to previous definitions by naming them with a path. Paths are key1/key2/key3... (starting from wherever we find key1, i.e. in the current dictionary or any of its parents), or alternatively /key1/key2/key3... (to start from the root). Definitions cannot refer to future definitions. Example: a=1; b=2; c={ d=3; e=a; }; f={ a=4; g=c; }; The following paths are valid: a is 1 b is 2 c is a dictionary: c/d is 3 c/e is 1 f is a dictionary: f/a is 4 f/g is a dictionary: f/g/d is 3 f/g/e is 1 Note that f/g/e is NOT 4. Elements that are lists are inserted into lists in definitions, not referenced by them (i.e. you can't have lists of lists). Some closures may be followed by an argument list in ( ), and may return any number of whatever type they like (including other closures). Some types of closure (typically those returned from invokations of other closures) cannot be invoked. closure { definitions } is short for closure({definitions}). The main body of secnet, and all the additional modules, predefine some keys in the root dictionary. The main ones are: yes, true, True, TRUE, on: the boolean value True no, false, False, FALSE, off: the boolean value False makelist: turns a dictionary (arg1) into a list of definitions (ignoring the keys) readfile: reads a file (arg1) and returns it as a string map: applies the closure specified as arg1 to each of the remaining elements in the list in turn. Returns a list made up of the outputs of the closure. Keys defined by modules are described below, in the module documentation. Other configuration files can be included inline by writing "include filename" at the start of a line. After the configuration file is read, secnet looks for particular keys in configuration space to tell it what to do: system: a dictionary which can contain the following keys: log (log closure): a destination for system messages userid (string): the userid for secnet to run as once it drops privileges pidfile (string): where to store its PID sites: a list of closures of type 'site', which define other tunnel endpoints that secnet will attempt to communicate with * secnet command line options Usage: secnet [OPTION]... -f, --silent, --quiet suppress error messages -w, --nowarnings suppress warnings -v, --verbose output extra diagnostics -c, --config=filename specify a configuration file -j, --just-check-config stop after reading configfile -n, --nodetach do not run in background -d, --debug=item,... set debug options --help display this help and exit --version output version information and exit * base91s secnet defines a variant of the base91 encoding `basE91', from http://base91.sourceforge.net/ base91s is the same as baseE91 except that: - in the encoded charset, `"' is replaced with `-' - spaces, newlines etc. and other characters outside the charset are not permitted (although in some places they may be ignored, this is not guaranteed). * secnet builtin modules ** resolver Defines: adns (closure => resolver closure) adns: dict argument config (string): optional, a resolv.conf for ADNS to use ** random Defines: randomsrc (closure => randomsrc closure) randomsrc: string[,bool] arg1: filename of random source arg2: if True then source is blocking ** udp Defines: udp (closure => comm closure) udp: dict argument address (string list): IPv6 or IPv4 addresses to listen and send on; default is all local addresses port (integer): UDP port to listen and send on; optional if you don't need to have a stable address for your peers to talk to (in which case your site ought probably to have `local-mobile true'). buffer (buffer closure): buffer for incoming packets authbind (string): optional, path to authbind-helper program ** polypath Defines: polypath (closure => comm closure) polypath: dict argument port (integer): UDP port to listen and send on buffer (buffer closure): buffer for incoming packets authbind (string): optional, path to authbind-helper program max-interfaces (number): optional, max number of different interfaces to use (also, maximum steady-state amount of packet multiplication); interfaces marked with `@' do not count. interfaces (string list): which interfaces to process; each entry is optionally `!' or `+' or `@' followed by a glob pattern (which is applied to a prospective interface using fnmatch with no flags). `+' or nothing means to process normally. `!' means to ignore; `@' means to use only in conjunction with dedicated-interface-addr. If no list is specified, or the list ends with a `!' entry, a default list is used/appended: "!tun*","!tap*","!sl*","!userv*","!lo","@hippo*","*". Patterns which do not start with `*' or an alphanumeric need to be preceded by `!' or `+' or `@'. monitor-command (string list): Program to use to monitor appearance and disappearance of addresses on local network interfaces. Should produce lines of the form `+|- 4|6 ' where is an address literal. Each - line should relate to a previously printed + line. On startup, should produce a + line for each currently existing address. secnet does filtering so there is no need to strip out tun interfaces, multicast addresses, and so on. The command is run as the user secnet is started as (not the one which secnet may drop privilege to due to the configured `userid'). The default depends on the operating system. permit-loopback (boolean): Normally, loopback IPv6 and IPv4 addresses on local interfaces are disregarded, because such interfaces are not interesting for communicating with distant hosts. Setting this option will ignore that check, which can be useful for testing. Setting this option also removes "!lo*" from the default interface pattern list. When using this comm, packets are sent out of every active interface on the host (where possible). It is important that interfaces created by secnet itself are not included! secnet's default filter list tries to do this. This comm only makes sense for sites which are mobile. That is, the site closures used with this comm should all have the `local-mobile' parameter set to `true'. When the local site site is not marked mobile the address selection machinery might fixate on an unsuitable address. polypath takes site-specific informtion as passed to the `comm-info' site closure parameter. The entries understood in the dictionary are: dedicated-interface-addr (string): IPv4 or IPv6 address literal. Interfaces specified with `@' in `interfaces' will be used for the corresponding site iff the interface local address is this address. For an interface to work with polypath, it must either have a suitable default route, or be a point-to-point interface. In the general case this might mean that the host would have to have multiple default routes. However in practice the most useful configuration is two interfaces being (1) wifi (2) mobile internet. I have had success on Linux by using network-manager for wifi and invoking ppp directly for mobile internet. ppp sets up a point-to-point link, and does not add a default route if there already is one. network-manager always sets up a default route. The result is that the wifi always has a default route (so is useable); ppp (being a point-to-point link) does not need one. The use of polypath requires that secnet be started with root privilege, to make the setsockopt(,,SO_BINDTODEVICE,) calls. If the configuration specifies that secnet should drop privilege (see `userid' above), secnet will keep a special process around for this purpose; that process will handle local network interface changes but does not deal with any packets, key exchange, etc. polypath support is only available when secnet is built against an IPv6-capable version of adns (because it wants features in the newer adns). ** log Defines: logfile (closure => log closure) syslog (closure => log closure) logfile: dict argument filename (string): where to log to; default is stderr prefix (string): added to messages [""] class (string list): what type of messages to log { "debug-config", M_DEBUG_CONFIG }, { "debug-phase", M_DEBUG_PHASE }, { "debug", M_DEBUG }, { "all-debug", M_DEBUG|M_DEBUG_PHASE|M_DEBUG_CONFIG }, { "info", M_INFO }, { "notice", M_NOTICE }, { "warning", M_WARNING }, { "error", M_ERROR }, { "security", M_SECURITY }, { "fatal", M_FATAL }, { "default", M_WARNING|M_ERROR|M_SECURITY|M_FATAL }, { "verbose", M_INFO|M_NOTICE|M_WARNING|M_ERROR|M_SECURITY|M_FATAL }, { "quiet", M_FATAL } logfile will close and reopen its file upon receipt of SIGHUP. syslog: dict argument ident (string): include this string in every log message facility (string): facility to log as { "authpriv", LOG_AUTHPRIV }, { "cron", LOG_CRON }, { "daemon", LOG_DAEMON }, { "kern", LOG_KERN }, { "local0", LOG_LOCAL0 }, { "local1", LOG_LOCAL1 }, { "local2", LOG_LOCAL2 }, { "local3", LOG_LOCAL3 }, { "local4", LOG_LOCAL4 }, { "local5", LOG_LOCAL5 }, { "local6", LOG_LOCAL6 }, { "local7", LOG_LOCAL7 }, { "lpr", LOG_LPR }, { "mail", LOG_MAIL }, { "news", LOG_NEWS }, { "syslog", LOG_SYSLOG }, { "user", LOG_USER }, { "uucp", LOG_UUCP } ** util Defines: sysbuffer (closure => buffer closure) sysbuffer: integer[,dict] arg1: buffer length arg2: options: lockdown (boolean): if True, mlock() the buffer ** site Defines: site (closure => site closure) site: dict argument local-name (string): this site's name for itself name (string): the name of the site's peer link (netlink closure) comm (one or more comm closures): if there is more than one, the first one will be used for any key setups initiated by us using the configured address. Others are only used if our peer talks to them. resolver (resolver closure) random (randomsrc closure) key-cache (privcache closure) local-key (sigprivkey closure): Deprecated; use key-cache instead. address (string list): optional, DNS name(s) used to find our peer; address literals are supported too if enclosed in `[' `]'. port (integer): mandatory if 'address' is specified: the port used to contact our peer peer-keys (string): path (prefix) for peer public key set file(s); see README.make-secnet-sites re `pub' etc. and NOTES.peer-keys. key (sigpubkey closure): our peer's public key (obsolete) transform (transform closure): how to mangle packets sent between sites dh (dh closure) key-lifetime (integer): max lifetime of a session key, in ms [one hour; mobile: 2 days] setup-retries (integer): max number of times to transmit a key negotiation packet [5; mobile: 30] setup-timeout (integer): time between retransmissions of key negotiation packets, in ms [2000; mobile: 1000] wait-time (integer): after failed key setup, wait roughly this long (in ms) before allowing another attempt [20000; mobile: 10000] Actual wait time is randomly chosen between ~0.5x and ~1.5x this. renegotiate-time (integer): if we see traffic on the link after this time then renegotiate another session key immediately (in ms) [half key-lifetime, or key-lifetime minus 5 mins (mobile: 12 hours), whichever is longer]. keepalive (bool): if True then attempt always to keep a valid session key. [false] log-events (string list): types of events to log for this site unexpected: unexpected key setup packets (may be late retransmissions) setup-init: start of attempt to setup a session key setup-timeout: failure of attempt to setup a session key, through timeout activate-key: activation of a new session key timeout-key: deletion of current session key through age security: anything potentially suspicious state-change: steps in the key setup protocol packet-drop: whenever we throw away an outgoing packet dump-packets: every key setup packet we see errors: failure of name resolution, internal errors peer-addrs: changes to sets of peer addresses (interesting for mobile peers) all: everything (too much!) mobile (bool): if True then peer is "mobile" ie we assume it may change its apparent IP address and port number without either it or us being aware of the change; so, we remember the last several port/addr pairs we've seen and send packets to all of them (subject to a timeout). We maintain one set of addresses for key setup exchanges, and another for data traffic. Two communicating peers must not each regard the other as mobile, or all the traffic in each direction will be triplicated (strictly, transmitted mobile-peers-max times) and anyway two peers whose public contact address may suddenly change couldn't communicate reliably because their contact addresses might both change at once. [false] mobile-peers-max (integer): Maximum number of peer port/addr pairs we remember and send to. Must be at least 1 and no more than 5. [4 if any address is configured, otherwise 3] static-peers-max (integer): Maximum number of peer port/addr pairs we can try for a static site. Must be at least 1 and no more than 5. [4 or 3, as above] mobile-peer-expiry (integer): For "mobile" peers only, the length of time (in seconds) for which we will keep sending to multiple address/ports from which we have not seen incoming traffic. [120] local-mobile (bool): if True then other peers have been told we are "mobile". This should be True iff the peers' site configurations for us have "mobile True" (and if we find a site configuration for ourselves in the config, we insist on this). The effect is to check that there are no links both ends of which are allegedly mobile (which is not supported, so those links are ignored) and to change some of the tuning parameter defaults. [false] mtu-target (integer): Desired value of the inter-site MTU for this peering. This value will be advertised to the peer (which ought to affect incoming packets), and if the peer advertises an MTU its value will be combined with this setting to compute the inter-site MTU. (secnet will still accept packets which exceed the (negotiated or assumed) inter-site MTU.) Setting a lower inter-site MTU can be used to try to restrict the sizes of the packets sent over the underlying public network (e.g. to work around network braindamage). It is not normally useful to set a larger value for mtu-target than the VPN's general MTU (which should be reflected in the local private interface MTU, ie the mtu parameter to netlink). If this parameter is not set, or is set to 0, the default is to use the local private link mtu. comm-info (dict): Information for the comm, used when this site wants to transmit. If the comm does not support this, it is ignored. Links involving mobile peers have some different tuning parameter default values, which are generally more aggressive about retrying key setup but more relaxed about using old keys. These are noted with "mobile:", above, and apply whether the mobile peer is local or remote. ** transform-eax Defines: eax-serpent (closure => transform closure) ** transform-cbcmac Defines: serpent256-cbc (closure => transform closure) ** netlink Defines: null-netlink (closure => closure or netlink closure) null-netlink: dict argument name (string): name for netlink device, used in log messages networks (string list): networks on the host side of the netlink device remote-networks (string list): networks that may be claimed by the remote site using this netlink device local-address (string): IP address of host's tunnel interface secnet-address (string): IP address of this netlink device ptp-address (string): IP address of the other end of a point-to-point link mtu (integer): MTU of host's tunnel interface Only one of secnet-address or ptp-address may be specified. If point-to-point mode is in use then the "routes" option must also be specified, and netlink returns a netlink closure that should be used directly with the "link" option to the site closure. If point-to-point mode is not in use then netlink returns a closure that may be invoked using a dict argument with the following keys to yield a netlink closure: routes (string list): networks reachable down the tunnel attached to this instance of netlink options (string list): allow-route: allow packets coming from this tunnel to be routed to other tunnels as well as the host (used for mobile devices like laptops) soft: remove these routes from the host's routing table when the tunnel link quality is zero mtu (integer): MTU of host's tunnel interface Netlink will dump its current routing table to the system/log on receipt of SIGUSR1. ** slip Defines: userv-ipif (closure => netlink closure) userv-ipif: dict argument userv-path (string): optional, where to find userv ["userv"] service-user (string): optional, username for userv-ipif service ["root"] service-name (string): optional, name of userv-ipif service ["ipif"] buffer (buffer closure): buffer for assembly of host->secnet packets plus generic netlink options, as for 'null-netlink' ** tun Defines: tun (closure => netlink closure) [only on linux-2.4] tun-old (closure => netlink closure) tun: dict argument flavour (string): optional, type of TUN interface to use ("guess","linux","bsd","streams") device (string): optional, path of TUN/TAP device file ["/dev/net/tun"] interface (string): optional, name of tunnel network interface ifconfig-path (string): optional, path to ifconfig command route-path (string): optional, path to route command ifconfig-type (string): optional, how to perform ifconfig route-type (string): optional, how to add and remove routes types are: "guess", "ioctl", "bsd", "linux", "solaris-2.5" buffer (buffer closure): buffer for host->secnet packets plus generic netlink options, as for 'null-netlink' I recommend you don't specify the 'interface' option unless you're doing something that requires the interface name to be constant. ** privcache Cache of dynamically loaded private keys. Defines: priv-cache (closure => privcache closure) priv-cache: dict argument privkeys (string): path prefix for private keys. Each key is looked for at this path prefix followed by the 10-character hex key id. privcache-size (integer): optional, maximum number of private keys to retain at once. [5] privkey-max (integer): optional, maximum size of private key file in bytes. [4095] ** pubkeys Defines: make-public (closure => sigpubkey closure) make-public: ( arg1: sigscheme name arg2: base91s encoded public key data, according to algorithm ** rsa Defines: sigscheme algorithm 00 "rsa1" rsa-private (closure => sigprivkey closure) rsa-public (closure => sigpubkey closure) rsa1 sigscheme algorithm: private key: SSH private key file, version 1, no password public key: SSH public key file, version 1 (length, restrictions, email, etc., ignored) rsa-private: string[,bool] arg1: filename of SSH private key file (version 1, no password) arg2: whether to check that the key is usable [default True] rsa-public: string,string arg1: encryption key (decimal) arg2: modulus (decimal) The sigscheme is hardcoded to use sha1. Both rsa-private and rsa-public look for the following config key in their context: hash (hash closure): hash function [sha1] ** dh Defines: diffie-hellman (closure => dh closure) diffie-hellman: string,string[,bool] arg1: modulus (hex) arg2: generator (hex) arg3: whether to check that the modulus is prime [default True] ** md5 Defines: md5 (hash closure) ** sha1 Defines: sha1 (hash closure) ** conffile Defines: makelist (dictionary => list of definitions) readfile (string => string) map (closure,list => list) makelist: dictionary returns a list consisting of the definitions in the dictionary. The keys are discarded. readfile: string reads the named file and returns its contents as a string map: applies the closure specified as arg1 to each of the elements in the list. Returns a list made up of the outputs of the closure. * Legal This file is part of secnet. See LICENCE and CREDITS for full list of copyright holders. SPDX-License-Identifier: GPL-3.0-or-later There is NO WARRANTY. work/README.mac0000664000000000000000000000600615063477206010363 0ustar How to install secnet on a Fink-equipped OS X system: - Install GMP: fink install gmp - Download and install ADNS: ./configure --disable-dynamic make sudo make install - Build secnet: ./configure make sudo make install - Install tuntap for OSX from http://tuntaposx.sourceforge.net/ - Create /etc/secnet/{key,secnet.conf,sites.conf} as usual - If you don't want secnet 'always on', edit uk.org.greenend.secnet.plist and remove *both* these two lines: RunAtLoad - Create the 'secnet' user and install the job configuration: ./setup.mac. To start secnet: sudo launchctl start uk.org.greenend.secnet To stop secnet: sudo launchctl stop uk.org.greenend.secnet To uninstall: sudo launchctl unload /Library/LaunchDaemons/uk.org.greenend.secnet.plist sudo rm -f /Library/LaunchDaemons/uk.org.greenend.secnet.plist If you need to enable IP forwarding: sudo sysctl -w net.inet.ip.forwarding=1 (Note that on a Mac, you need to enable IP forwarding if you want to route to addresses on one interface via another; i.e. if you expect to be able to reach an address on en0 with a packet delivered through tun0, IP forwarding must be turned on.) How to import secnet into XCode 3.2: - Set up build directories as follows: $ mkdir build/Debug build/Release $ cd build/Debug $ ~/src/secnet/configure CFLAGS="-g -O0" $ cd ../Release $ ~/src/secnet/configure $ cd ../.. (Replace ~/src/secnet with the *absolute* path to your secnet tree - XCode cannot map the relative paths in errors to the source files otherwise.) - Start XCode - Menubar -> File -> New Project - Choose the Mac OS X -> Other -> External Build System template - Choose the *parent* of the secnet directory and call the project secnet - OK the overwrite (it won't overwrite anything that matters) - This creates 'build' and 'secnet.xcodeproj' directories in your secnet tree. - Right-click Groups & Files -> secnet -> Add -> Existing files and select all the *.c, *.h, *.y and *.fl files. - Omit the following files: - *.yy.[ch] \ - *.tab.[ch] | generated during build - version.c | - config.h / - snprintf.[ch] - unnecessary on OSX - Sort by 'kind' may make this easier - Leave 'Copy items...' unchecked - Add To Targets should have 'secnet' checked - For conffile.fl, right click Get Info -> General, and set File Type to sourcecode.lex. - Under Groups & Files -> secnet, select all source files and right click Get Info -> General, and set: - Tab Width to 8 - Indent Width to 4 - Check Editor uses tabs - Double click click Groups & Files -> Targets secnet - Add '-C $TARGET_BUILD_DIR' to the start of the arguments. You should now be able to build both debug and release configurations using ⌘B. Richard Kettlewell 2011-07-23 -- This file is part of secnet. See LICENCE and this file CREDITS for full list of copyright holders. SPDX-License-Identifier: GPL-3.0-or-later There is NO WARRANTY. work/README.make-secnet-sites0000664000000000000000000002503515063477206013147 0ustar USAGE make-secnet-sites [-P PREFIX] [--conf] [IN [OUTCONF]] make-secnet-sites --filter [IN [OUT]] make-secnet-sites -u|--userv HEADER GRPDIR SITESFILE GROUP The `-P' option sets the PREFIX string, mentioned below in `OUTPUT STRUCTURE'; the default is empty. In --conf mode, `make-secnet-sites' reads a single input file from IN (defaulting to standard input), and writes a Secnet configuration fragment to OUTCONF (defaulting to standard output). In --filter mode, `make-secnet-sites' reads a single input file from IN (defaulting to standard input), and writes a version of that sites file to OUT (defaulting to standard output). The output is filtered according to --output-version. In --userv mode, `make-secnet-sites' expects to have been invoked via GNU Userv. It verifies that GROUP is listed in the `USERV_GROUP' environment variable. It then processes the HEADER input, which should say `end-defintions' somewhere, to enable restrictions, and then user input on standard input. If the combination of the two is acceptable, it writes a copy of the user input to the file `GRPDIR/RGROUP' (the `R' is literal) preceded by a comment logging the time and the value of the `USERV_USER' environment variable, and writes a file named SITESFILE consisting of the concatenation of: * a header comment logging the time and the value of the `USERV_USER' environment variable, and a reminder that this is `make-secnet-sites' input; * the HEADER, with any `include' lines replaced by the files they include; and * each of the `GRPDIR/R*' files, in some arbitrary order. This SITESFILE can later be processed in the former mode to produce Secnet configuration. OPTIONS --output-version NUMBER Write backward-compatible sites file output, targeting a particular sites format. Values of NUMBER that are understood are: 1 The original format, pre signing key negotiation. 2 Signing key algorithm agility and negotiation. If NUMBER is higher than make-secnet-sites supports, it writes out what it can. --pubkeys-install Specifies that public keys are to be installed in the live pubkeys area (and not hardcoded in secnet conf files). With this option, generated site configs refer to keys in PUBKEYS; also, the generated secnet configuration enables live peer public update. --pubkeys-single Specifies that one public key per site is to be written directly into the sites.conf output. If --output-version=1, this is the rsa1 key 0000000000. Otherwise it is an error if there are multiple public keys defined for any site, in the input. --pubkeys-single is the default. --pubkeys-elide In the sites.conf output, just write the peer-keys entry referring to keys in PUBKEYS. But do not write public keys anywhere. --pubkeys-dir PUBKEYS Specifies the live pubkeys area pathname. The default is /var/lib/secnet/pubkeys. Key files are named PUBKEYS/peer.[~...] mangled-peer-name is chosen by make-secnet-sites / => , --debug | -D Increase amount of debugging output. INPUT SYNTAX The input files have a simple line-based syntax. Blank lines, and lines beginning with a `#' character, are ignored. Other lines consist of a keyword followed by arguments, and separated by horizontal whitespace. There is no quoting, and it is not possible to include horizontal whitespace in an argument. An input file describes a number of virtual private networks (`VPNs'). Each VPN consists of a number of locations, and each location consists of a number of sites, thus forming (together with the root) a fixed four-level hierarchy. The root, VPNs, locations, and sites can each have a number of properties attached to them: each level in the hierarchy has a different set of permissable properties. Most keywords define properties on a `current' item in the hierarchy. Some change which item is current, possibly creating a new item. A few are special. First, the navigation keywords. vpn NAME Switch to the VPN called NAME, which is a direct child of the root, creating it if necessary. Subsequent properties, up until the next navigation keyword, are attached directly to the VPN. A VPN item becomes a dictionary named `NAME' within the `PREFIXvpn-data' dictionary in the generated output. location NAME [GROUP] Switch to the location called NAME, which is a direct child of the most recently mentioned VPN, creating it if necessary. The GROUP name may be omitted (and is anyway ignored) if the location already exists. It is an error if there is no current VPN. Subsequent properties, up until the next navigation keyword, are attached directly to the location. A location item becomes a dictionary named `NAME' within its parent VPN's dictionary in the generated output. site NAME Switch to the site called NAME, which is a direct child of the most recently mentioned location, creating it if necessary. It is an error if there is no current location. Subsequent properties, up until the next navigation keyword, are attached directly to the site. A location item becomes a dictionary named `NAME' within its parent location's dictionary in the generated output. Now, the special keywords. include FILE Read lines from FILE, as if they'd appeared at this point in the input. If the FILE name is relative, it is interpreted relative to the directory containing the most recently opened file. (This seems to be a bug.) The `include' keyword is only permitted before the `end-defintions' marker in a HEADER file processed using the `-u' option. end-definitions After this keyword, the following restrictions apply. * The `include' keyword can no longer be used. * It is not permitted to define new VPNs and locations. * It is not permitted to append new items to root, VPN, and location properties which are already defined. (Assigning new properties is permitted.) * It is not permitted to define new VPN-level properties. Finally, the properties. Usually, if a property has already been defined on an item, then it is an error to try to redefine it. But some properties are list-like: the values are accumulated into a single list. Mostly, properties are written to corresponding assignments in the generated Secnet configuration file, . The entries below describe how properties are translated into assignments. contact EMAIL Becomes a `Contact address' comment in the output. Acceptable at all levels; required separately at VPN and location levels. dh P G Assigns a Diffie--Hellman closure to the `dh' key, constructed as `diffie-hellman(P, G)'. Acceptable at all levels; required at site level. hash HASH-NAME Assigns the HASH-NAME to the `hash' key. The HASH-NAME must be one of `md5' or `sha1', and the corresponding hash closure is used. Acceptable at all levels; required at site level. key-lifetime INT setup-timeout INT setup-retries INT wait-time INT renegotiate-time INT Assign integers to the like-named key. Acceptable at all levels. restrict-nets NETWORK NETWORK ... This item and its descendents may only define `networks' and `peer' properties with addresses within the listed NETWORKs, each of which has the form IPADDR/MASK, where the IPADDR is an IPv4 address in dotted-quad form, and the MASK is either a netmask in dotted-quad form or a prefix length. Becomes a comment n the output. Acceptable at all levels. networks NETWORK NETWORK ... Assigns a list of NETWORKs to the `routes' key in a netlink application (see below). See `restrict-nets' for the syntax of a NETWORK. Acceptable only at site level; required at site level. address HOSTNAME PORT Assigns HOSTNAME to the `address' key and PORT (an integer) to the `port' key. Acceptable only at site level. May be omitted for mobile sites. peer IPADDR Assigns IPADDR to the `ptp-address' key in a netlink application (see below). IPADDR must be an IPv4 address in dotted-quad form. Acceptable only at site level; required at site level. pubkey HUNOZ E N Assigns a public-key closure to the `key' key, constructed as `rsa-public(E, N)'. The argument HUNOZ must be an integer, but is otherwise ignored; it's conventionally the length of N in bits. Acceptable only at site level. See `pub'. mobile BOOL Assigns BOOL to the `mobile' key. Acceptable only at site level, but optional. Properties which can also appear in public key files. (named by `peer-keys' key to secnet sites closure.) These are acceptable to make-secnet-sites only at site level. See also `Site long-term keys' in NOTES. pub ALG DATAB91S Defines a public key. ALG is an algorithm name and DATA91S is the public key data, encoded according to secnet-base91 (see below). Gives make-public("ALG","DATAB91S") in sites.conf; at least one `pub' or `pubkey' must be specified. serial SETIDHEX Specifies the key set id (8 hex digits representing 4 bytes: each pair is the value of the next byte). May appear at most once. If not present, 00000000. pkg GROUPIDHEX pkgf GROUPIDHEX Specifies the key group id for subsequent keys. pkgf indicates a fallback group. May be repeated (with different id values). If not specified, 00000000. OUTPUT STRUCTURE The program produces a Secnet configuration fragment with the structure described below, suitable for inclusion using the `include' keyword. PREFIXvpn-data { VPN { # Contact email address: EMAIL [ # restrict-nets: NETWORKS ] [ VPN-PROPERTIES ] LOCATION { # Contact email address: EMAIL [ # restrict-nets: NETWORKS ] [ LOCATION-PROPERTIES ] SITE { [ # Contact email address: EMAIL ] [ # restrict-nets: NETWORKS ] name "VPN/LOCATION/NAME"; SITE-PROPERTIES link netlink { routes NETWORK ...; ptp-address IPADDR; }; }; [ MORE SITES ... ] }; [ MORE LOCATIONS ... ] }; [ MORE VPNS ... ] }; PREFIXvpn { VPN { LOCATION PREFIXvpn-data/VPN/LOCATION/SITE, ...; [ MORE LOCATIONS ] all-sites LOCATION, ...; }; }; PREFIXall-sites PREFIXvpn/VPN/all-sites, ...; Note in particular the implicit dependency on a pure closure named `netlink' used to set the `link' key in each site definition. Usually, this will be constructed by a partial application of the built-in `userv-ipif' or `tun' closures. -- This file is part of secnet. See LICENCE and this file CREDITS for full list of copyright holders. SPDX-License-Identifier: GPL-3.0-or-later There is NO WARRANTY. work/Suffix.sd.mk0000664000000000000000000000042315063477206011143 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. &TARGETS_check += &TARGETS_fullcheck += &:include subdirmk/cdeps.sd.mk &:include subdirmk/clean.sd.mk work/TODO0000664000000000000000000000261415063477206007435 0ustar dh.c: change format to binary from decimal string (without introducing endianness problems) netlink.c: test the 'allow_route' option properly. Add fragmentation code. Check that we comply with RFC1812. random.c: test properly resolver.c: ought to return a list of addresses for each address; the site code ought to remember them and try contacting them in turn. rsa.c: check padding type, change format to binary from decimal string (without introducing endianness problems) site.c: Abandon key exchanges when a bad packet is received. Modify protocol to include version fields, as described in the NOTES file. Implement keepalive mode. Make policy about when to initiate key exchanges more configurable (how many NAKs / bad reverse-transforms does it take to prompt a key exchange?) slip.c: restart userv-ipif to cope with soft routes? Restart it if it fails in use? transform.c: separate the transforms into multiple parts, which can then be combined in the configuration file. Will allow the user to plug in different block ciphers, invent an authenticity-only mode, etc. (similar to udptunnel) udp.c: option for path-MTU discovery (once fragmentation support is implemented in netlink) global: consider using liboop for the event loop -- This file is part of secnet. See LICENCE and this file CREDITS for full list of copyright holders. SPDX-License-Identifier: GPL-3.0-or-later There is NO WARRANTY. work/ac_prog_cc_no_writeable_strings.m40000664000000000000000000001133315063477206015567 0ustar dnl @synopsis AC_PROG_CC_NO_WRITEABLE_STRINGS(substvar [,hard]) dnl dnl Try to find a compiler option that warns when a stringliteral is dnl used in a place that could potentially modify the address. This dnl should warn on giving an stringliteral to a function that asks of dnl a non-const-modified char-pointer. dnl dnl The sanity check is done by looking at string.h which has a set dnl of strcpy definitions that should be defined with const-modifiers dnl to not emit a warning in all so many places. dnl dnl Currently this macro knows about GCC. dnl hopefully will evolve to use: Solaris C compiler, dnl Digital Unix C compiler, C for AIX Compiler, HP-UX C compiler, dnl and IRIX C compiler. dnl dnl @version $Id: ac_prog_cc_no_writeable_strings.m4,v 1.1 2002/02/20 16:18:18 steve Exp $ dnl @author Guido Draheim dnl This is an older version of ax_cflags_no_writable_strings.m4 dnl which is nowadays to be found in the Autoconf Archive. Nowadays, dnl this file has this permission notice there:: dnl dnl Copyright (c) 2008 Guido U. Draheim dnl dnl This program is free software; you can redistribute it and/or modify it dnl under the terms of the GNU General Public License as published by the dnl Free Software Foundation; either version 3 of the License, or (at your dnl option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General dnl Public License for more details. dnl dnl You should have received a copy of the GNU General Public License along dnl with this program. If not, see . dnl dnl As a special exception, the respective Autoconf Macro's copyright owner dnl gives unlimited permission to copy, distribute and modify the configure dnl scripts that are the output of Autoconf when processing the Macro. You dnl need not follow the terms of the GNU General Public License when using dnl or distributing such scripts, even though portions of the text of the dnl Macro appear in them. The GNU General Public License (GPL) does govern dnl all other use of the material that constitutes the Autoconf Macro. dnl dnl This special exception to the GPL applies to versions of the Autoconf dnl Macro released by the Autoconf Archive. When you make and distribute a dnl modified version of the Autoconf Macro, you may extend this special dnl exception to the GPL to apply to your modified version as well. AC_DEFUN([AC_PROG_CC_NO_WRITEABLE_STRINGS], [ pushdef([CV],ac_cv_prog_cc_no_writeable_strings)dnl hard=$2 if test -z "$hard"; then msg="C to warn about writing to stringliterals" else msg="C to prohibit any write to stringliterals" fi AC_CACHE_CHECK($msg, CV, [ cat > conftest.c < int main (void) { char test[[16]]; if (strcpy (test, "test")) return 0; return 1; } EOF dnl GCC if test "$GCC" = "yes"; then if test -z "$hard"; then CV="-Wwrite-strings" else CV="-fno-writable-strings -Wwrite-strings" fi if test -n "`${CC-cc} -c $CV conftest.c 2>&1`" ; then CV="suppressed: string.h" fi dnl Solaris C compiler elif $CC -flags 2>&1 | grep "Xc.*strict ANSI C" > /dev/null 2>&1 && $CC -c -xstrconst conftest.c > /dev/null 2>&1 && test -f conftest.o then # strings go into readonly segment CV="-xstrconst" rm conftest.o if test -n "`${CC-cc} -c $CV conftest.c 2>&1`" ; then CV="suppressed: string.h" fi dnl HP-UX C compiler elif $CC > /dev/null 2>&1 && $CC -c +ESlit conftest.c > /dev/null 2>&1 && test -f conftest.o then # strings go into readonly segment CV="+ESlit" rm conftest.o if test -n "`${CC-cc} -c $CV conftest.c 2>&1`" ; then CV="suppressed: string.h" fi dnl Digital Unix C compiler elif ! $CC > /dev/null 2>&1 && $CC -c -readonly_strings conftest.c > /dev/null 2>&1 && test -f conftest.o then # strings go into readonly segment CV="-readonly_strings" rm conftest.o if test -n "`${CC-cc} -c $CV conftest.c 2>&1`" ; then CV="suppressed: string.h" fi dnl C for AIX Compiler dnl IRIX C compiler # -use_readonly_const is the default for IRIX C, # puts them into .rodata, but they are copied later. # need to be "-G0 -rdatashared" for strictmode but # I am not sure what effect that has really. fi rm -f conftest.* ]) if test -z "[$]$1" ; then if test -n "$CV" ; then case "$CV" in suppressed*) $1="" ;; # known but suppressed *) $1="$CV" ;; esac fi fi AC_SUBST($1) popdef([CV])dnl ]) work/aclocal.m40000664000000000000000000000222115063477206010577 0ustar # aclocal.m4 - package-specific macros for autoconf dnl This file is part of secnet. dnl See README for full list of copyright holders. dnl dnl secnet is free software; you can redistribute it and/or modify it dnl under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 3 of the License, or dnl (at your option) any later version. dnl dnl secnet is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl version 3 along with secnet; if not, see dnl https://www.gnu.org/licenses/gpl.html. dnl This next macro came from adns.git, dnl (d8fa191ed7774818862febd6ade774cb7e149ab9). define(ADNS_C_GETFUNC,[ AC_CHECK_FUNC([$1],,[ AC_CHECK_LIB([$2],[$1],[$3],[ AC_MSG_ERROR([cannot find library function $1]) ]) ]) ]) define(SECNET_C_GETFUNC,[ ADNS_C_GETFUNC($1,$2,[ LIBS="-l$2 $LIBS"; AC_MSG_WARN([$1 is in lib$2, urgh. Must use -l$2.]) ]) ]) work/aes.c0000664000000000000000000017226715063477206007675 0ustar /* * aes.c - implementation of Rijndael */ /* * This file is Free Software. It has been modified to as part of its * incorporation into secnet. * * Copyright 2000 Vincent Rijmen, Antoon Bosselaers, Paulo Barreto * Copyright 2004 Fabrice Bellard * Copyright 2013 Ian Jackson * * You may redistribute this file and/or modify it under the terms of * the permissive licence shown below. * * You may redistribute secnet as a whole and/or modify it under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 3, 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 * https://www.gnu.org/licenses/gpl.html. */ /* * Integrated in QEMU by Fabrice Bellard from the OpenSSL project. * * Copied to the secnet tree by Ian Jackson from the upstream qemu git * tree revision 55616505876d6683130076b810a27c7889321560 * and modified only to remove the include of qemu-common.h. * * (The changes by various qemu contributors between * e4d4fe3c34cdd6e26f9b9975efec7d1e81ad00b6, where this file appeared * in qemu in a commit by Fabrice Bellard, and 55616505 are too * trivial to attract copyright, which is just as well because some of * the commits are lacking a S-o-b.) */ /* * rijndael-alg-fst.c * * @version 3.0 (December 2000) * * Optimised ANSI C code for the Rijndael cipher (now AES) * * @author Vincent Rijmen * @author Antoon Bosselaers * @author Paulo Barreto * * This code is hereby placed in the public domain. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "aes.h" #ifndef NDEBUG #define NDEBUG #endif typedef uint32_t u32; typedef uint16_t u16; typedef uint8_t u8; /* This controls loop-unrolling in aes_core.c */ #undef FULL_UNROLL # define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) # define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } /* Te0[x] = S [x].[02, 01, 01, 03]; Te1[x] = S [x].[03, 02, 01, 01]; Te2[x] = S [x].[01, 03, 02, 01]; Te3[x] = S [x].[01, 01, 03, 02]; Te4[x] = S [x].[01, 01, 01, 01]; Td0[x] = Si[x].[0e, 09, 0d, 0b]; Td1[x] = Si[x].[0b, 0e, 09, 0d]; Td2[x] = Si[x].[0d, 0b, 0e, 09]; Td3[x] = Si[x].[09, 0d, 0b, 0e]; Td4[x] = Si[x].[01, 01, 01, 01]; */ static const u32 Te0[256] = { 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU, 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U, 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU, 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU, 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU, 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU, 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU, 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U, 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU, 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU, 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U, 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU, 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU, 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU, 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU, 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU, 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U, 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU, 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU, 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU, 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU, 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U, 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U, 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U, 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U, 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU, 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U, 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U, 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU, 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU, 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U, 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U, 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U, 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU, 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U, 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU, 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U, 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU, 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U, 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U, 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU, 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U, 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U, 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U, 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U, 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U, 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U, 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U, 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U, 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU, 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U, 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U, 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U, 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U, 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U, 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U, 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU, 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U, 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U, 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, }; static const u32 Te1[256] = { 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U, 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU, 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U, 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU, 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U, 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U, 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU, 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U, 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U, 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U, 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU, 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U, 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U, 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU, 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U, 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U, 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U, 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU, 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU, 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U, 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU, 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU, 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U, 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU, 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U, 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU, 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U, 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U, 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U, 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU, 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U, 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU, 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U, 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU, 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U, 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U, 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU, 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU, 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU, 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U, 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U, 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU, 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U, 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU, 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U, 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU, 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U, 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU, 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU, 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U, 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU, 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U, 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU, 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U, 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U, 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U, 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU, 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU, 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U, 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, }; static const u32 Te2[256] = { 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U, 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU, 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U, 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU, 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U, 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U, 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU, 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U, 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U, 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U, 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU, 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U, 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U, 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU, 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U, 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U, 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U, 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU, 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU, 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U, 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU, 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU, 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U, 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU, 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U, 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU, 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U, 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U, 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U, 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU, 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U, 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU, 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U, 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU, 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U, 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U, 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU, 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU, 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU, 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U, 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U, 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU, 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U, 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU, 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U, 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU, 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U, 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU, 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU, 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U, 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU, 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U, 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU, 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U, 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U, 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U, 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU, 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU, 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U, 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, }; static const u32 Te3[256] = { 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U, 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU, 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU, 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU, 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U, 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU, 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU, 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U, 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U, 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU, 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU, 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU, 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU, 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU, 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U, 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU, 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU, 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U, 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U, 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U, 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U, 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U, 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU, 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U, 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU, 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU, 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U, 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U, 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U, 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU, 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U, 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU, 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU, 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U, 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U, 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU, 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U, 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU, 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U, 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U, 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U, 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U, 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU, 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U, 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU, 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U, 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU, 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U, 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU, 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU, 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU, 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU, 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U, 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U, 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U, 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U, 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U, 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U, 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU, 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U, 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, }; static const u32 Te4[256] = { 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U, 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU, 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U, 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU, 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U, 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U, 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU, 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U, 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U, 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U, 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU, 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U, 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U, 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU, 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U, 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U, 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U, 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU, 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU, 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U, 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU, 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU, 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U, 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU, 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U, 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU, 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U, 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U, 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U, 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU, 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U, 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU, 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U, 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU, 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U, 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U, 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU, 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU, 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU, 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U, 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U, 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU, 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U, 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU, 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U, 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU, 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U, 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU, 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU, 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U, 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU, 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U, 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU, 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U, 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U, 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U, 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU, 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU, 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U, 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, }; static const u32 Td0[256] = { 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU, 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U, 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U, 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU, 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U, 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU, 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U, 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U, 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U, 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U, 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU, 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U, 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU, 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U, 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU, 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U, 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U, 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U, 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU, 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U, 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU, 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U, 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU, 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U, 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU, 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU, 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U, 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU, 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U, 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU, 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U, 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U, 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U, 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU, 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U, 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U, 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU, 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U, 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U, 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U, 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U, 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U, 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU, 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U, 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U, 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U, 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U, 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U, 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU, 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU, 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU, 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU, 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U, 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U, 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU, 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU, 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U, 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU, 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U, 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, }; static const u32 Td1[256] = { 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U, 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U, 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U, 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U, 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U, 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U, 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU, 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU, 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU, 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U, 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU, 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U, 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U, 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U, 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU, 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU, 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U, 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU, 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U, 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU, 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU, 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U, 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U, 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U, 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU, 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U, 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU, 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U, 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U, 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U, 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU, 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U, 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U, 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U, 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U, 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U, 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U, 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU, 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU, 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U, 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU, 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U, 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU, 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU, 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U, 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU, 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U, 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U, 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U, 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U, 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U, 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U, 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U, 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU, 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U, 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U, 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU, 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U, 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U, 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, }; static const u32 Td2[256] = { 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U, 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU, 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U, 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U, 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U, 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U, 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU, 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U, 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U, 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU, 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U, 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U, 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U, 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U, 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U, 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U, 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU, 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U, 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U, 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U, 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U, 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U, 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU, 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU, 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U, 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU, 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U, 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU, 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU, 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU, 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU, 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U, 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U, 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U, 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U, 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U, 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U, 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U, 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU, 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU, 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U, 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U, 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU, 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU, 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U, 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U, 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U, 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U, 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U, 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U, 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U, 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU, 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U, 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U, 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U, 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U, 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U, 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U, 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU, 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, }; static const u32 Td3[256] = { 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U, 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU, 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU, 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U, 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU, 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U, 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU, 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U, 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U, 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U, 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U, 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U, 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU, 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU, 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U, 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U, 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU, 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU, 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U, 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U, 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U, 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U, 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU, 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U, 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U, 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU, 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU, 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U, 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U, 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U, 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU, 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U, 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U, 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U, 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U, 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U, 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U, 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U, 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU, 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U, 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U, 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU, 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU, 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U, 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU, 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U, 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U, 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U, 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U, 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U, 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U, 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU, 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU, 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU, 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU, 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U, 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U, 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U, 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU, 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, }; static const u32 Td4[256] = { 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU, 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U, 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U, 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U, 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU, 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U, 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU, 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU, 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU, 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U, 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U, 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U, 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U, 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U, 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U, 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU, 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U, 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U, 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU, 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U, 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U, 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U, 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU, 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U, 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U, 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU, 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U, 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U, 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU, 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U, 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU, 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU, 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U, 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U, 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U, 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U, 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU, 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U, 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U, 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU, 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU, 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU, 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U, 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU, 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U, 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U, 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U, 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U, 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU, 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U, 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU, 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU, 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU, 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU, 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U, 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU, 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U, 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU, 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U, 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, }; static const u32 rcon[] = { 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ }; /** * Expand the cipher key into the encryption key schedule. */ int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key) { u32 *rk; int i = 0; u32 temp; if (!userKey || !key) return -1; if (bits != 128 && bits != 192 && bits != 256) return -2; rk = key->rd_key; if (bits==128) key->rounds = 10; else if (bits==192) key->rounds = 12; else key->rounds = 14; rk[0] = GETU32(userKey ); rk[1] = GETU32(userKey + 4); rk[2] = GETU32(userKey + 8); rk[3] = GETU32(userKey + 12); if (bits == 128) { while (1) { temp = rk[3]; rk[4] = rk[0] ^ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ (Te4[(temp ) & 0xff] & 0x0000ff00) ^ (Te4[(temp >> 24) ] & 0x000000ff) ^ rcon[i]; rk[5] = rk[1] ^ rk[4]; rk[6] = rk[2] ^ rk[5]; rk[7] = rk[3] ^ rk[6]; if (++i == 10) { return 0; } rk += 4; } } rk[4] = GETU32(userKey + 16); rk[5] = GETU32(userKey + 20); if (bits == 192) { while (1) { temp = rk[ 5]; rk[ 6] = rk[ 0] ^ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ (Te4[(temp ) & 0xff] & 0x0000ff00) ^ (Te4[(temp >> 24) ] & 0x000000ff) ^ rcon[i]; rk[ 7] = rk[ 1] ^ rk[ 6]; rk[ 8] = rk[ 2] ^ rk[ 7]; rk[ 9] = rk[ 3] ^ rk[ 8]; if (++i == 8) { return 0; } rk[10] = rk[ 4] ^ rk[ 9]; rk[11] = rk[ 5] ^ rk[10]; rk += 6; } } rk[6] = GETU32(userKey + 24); rk[7] = GETU32(userKey + 28); if (bits == 256) { while (1) { temp = rk[ 7]; rk[ 8] = rk[ 0] ^ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ (Te4[(temp ) & 0xff] & 0x0000ff00) ^ (Te4[(temp >> 24) ] & 0x000000ff) ^ rcon[i]; rk[ 9] = rk[ 1] ^ rk[ 8]; rk[10] = rk[ 2] ^ rk[ 9]; rk[11] = rk[ 3] ^ rk[10]; if (++i == 7) { return 0; } temp = rk[11]; rk[12] = rk[ 4] ^ (Te4[(temp >> 24) ] & 0xff000000) ^ (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(temp ) & 0xff] & 0x000000ff); rk[13] = rk[ 5] ^ rk[12]; rk[14] = rk[ 6] ^ rk[13]; rk[15] = rk[ 7] ^ rk[14]; rk += 8; } } return 0; } /** * Expand the cipher key into the decryption key schedule. */ int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key) { u32 *rk; int i, j, status; u32 temp; /* first, start with an encryption schedule */ status = AES_set_encrypt_key(userKey, bits, key); if (status < 0) return status; rk = key->rd_key; /* invert the order of the round keys: */ for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) { temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; } /* apply the inverse MixColumn transform to all round keys but the first and the last: */ for (i = 1; i < (key->rounds); i++) { rk += 4; rk[0] = Td0[Te4[(rk[0] >> 24) ] & 0xff] ^ Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ Td3[Te4[(rk[0] ) & 0xff] & 0xff]; rk[1] = Td0[Te4[(rk[1] >> 24) ] & 0xff] ^ Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ Td3[Te4[(rk[1] ) & 0xff] & 0xff]; rk[2] = Td0[Te4[(rk[2] >> 24) ] & 0xff] ^ Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ Td3[Te4[(rk[2] ) & 0xff] & 0xff]; rk[3] = Td0[Te4[(rk[3] >> 24) ] & 0xff] ^ Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ Td3[Te4[(rk[3] ) & 0xff] & 0xff]; } return 0; } #ifndef AES_ASM /* * Encrypt a single block * in and out can overlap */ void AES_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key) { const u32 *rk; u32 s0, s1, s2, s3, t0, t1, t2, t3; #ifndef FULL_UNROLL int r; #endif /* ?FULL_UNROLL */ assert(in && out && key); rk = key->rd_key; /* * map byte array block to cipher state * and add initial round key: */ s0 = GETU32(in ) ^ rk[0]; s1 = GETU32(in + 4) ^ rk[1]; s2 = GETU32(in + 8) ^ rk[2]; s3 = GETU32(in + 12) ^ rk[3]; #ifdef FULL_UNROLL /* round 1: */ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; /* round 2: */ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; /* round 3: */ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; /* round 4: */ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; /* round 5: */ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; /* round 6: */ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; /* round 7: */ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; /* round 8: */ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; /* round 9: */ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; if (key->rounds > 10) { /* round 10: */ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40]; s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41]; s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42]; s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43]; /* round 11: */ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44]; t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45]; t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46]; t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47]; if (key->rounds > 12) { /* round 12: */ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48]; s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49]; s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50]; s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51]; /* round 13: */ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52]; t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53]; t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54]; t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55]; } } rk += key->rounds << 2; #else /* !FULL_UNROLL */ /* * Nr - 1 full rounds: */ r = key->rounds >> 1; for (;;) { t0 = Te0[(s0 >> 24) ] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[(s3 ) & 0xff] ^ rk[4]; t1 = Te0[(s1 >> 24) ] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[(s0 ) & 0xff] ^ rk[5]; t2 = Te0[(s2 >> 24) ] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[(s1 ) & 0xff] ^ rk[6]; t3 = Te0[(s3 >> 24) ] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[(s2 ) & 0xff] ^ rk[7]; rk += 8; if (--r == 0) { break; } s0 = Te0[(t0 >> 24) ] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[(t3 ) & 0xff] ^ rk[0]; s1 = Te0[(t1 >> 24) ] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[(t0 ) & 0xff] ^ rk[1]; s2 = Te0[(t2 >> 24) ] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[(t1 ) & 0xff] ^ rk[2]; s3 = Te0[(t3 >> 24) ] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[(t2 ) & 0xff] ^ rk[3]; } #endif /* ?FULL_UNROLL */ /* * apply last round and * map cipher state to byte array block: */ s0 = (Te4[(t0 >> 24) ] & 0xff000000) ^ (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t3 ) & 0xff] & 0x000000ff) ^ rk[0]; PUTU32(out , s0); s1 = (Te4[(t1 >> 24) ] & 0xff000000) ^ (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t0 ) & 0xff] & 0x000000ff) ^ rk[1]; PUTU32(out + 4, s1); s2 = (Te4[(t2 >> 24) ] & 0xff000000) ^ (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t1 ) & 0xff] & 0x000000ff) ^ rk[2]; PUTU32(out + 8, s2); s3 = (Te4[(t3 >> 24) ] & 0xff000000) ^ (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ (Te4[(t2 ) & 0xff] & 0x000000ff) ^ rk[3]; PUTU32(out + 12, s3); } /* * Decrypt a single block * in and out can overlap */ void AES_decrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key) { const u32 *rk; u32 s0, s1, s2, s3, t0, t1, t2, t3; #ifndef FULL_UNROLL int r; #endif /* ?FULL_UNROLL */ assert(in && out && key); rk = key->rd_key; /* * map byte array block to cipher state * and add initial round key: */ s0 = GETU32(in ) ^ rk[0]; s1 = GETU32(in + 4) ^ rk[1]; s2 = GETU32(in + 8) ^ rk[2]; s3 = GETU32(in + 12) ^ rk[3]; #ifdef FULL_UNROLL /* round 1: */ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4]; t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5]; t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6]; t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7]; /* round 2: */ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8]; s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9]; s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; /* round 3: */ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; /* round 4: */ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; /* round 5: */ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; /* round 6: */ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; /* round 7: */ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; /* round 8: */ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; /* round 9: */ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; if (key->rounds > 10) { /* round 10: */ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40]; s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41]; s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42]; s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43]; /* round 11: */ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44]; t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45]; t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46]; t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47]; if (key->rounds > 12) { /* round 12: */ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48]; s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49]; s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50]; s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51]; /* round 13: */ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52]; t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53]; t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54]; t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55]; } } rk += key->rounds << 2; #else /* !FULL_UNROLL */ /* * Nr - 1 full rounds: */ r = key->rounds >> 1; for (;;) { t0 = Td0[(s0 >> 24) ] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[(s1 ) & 0xff] ^ rk[4]; t1 = Td0[(s1 >> 24) ] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[(s2 ) & 0xff] ^ rk[5]; t2 = Td0[(s2 >> 24) ] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[(s3 ) & 0xff] ^ rk[6]; t3 = Td0[(s3 >> 24) ] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[(s0 ) & 0xff] ^ rk[7]; rk += 8; if (--r == 0) { break; } s0 = Td0[(t0 >> 24) ] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[(t1 ) & 0xff] ^ rk[0]; s1 = Td0[(t1 >> 24) ] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[(t2 ) & 0xff] ^ rk[1]; s2 = Td0[(t2 >> 24) ] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[(t3 ) & 0xff] ^ rk[2]; s3 = Td0[(t3 >> 24) ] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[(t0 ) & 0xff] ^ rk[3]; } #endif /* ?FULL_UNROLL */ /* * apply last round and * map cipher state to byte array block: */ s0 = (Td4[(t0 >> 24) ] & 0xff000000) ^ (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ (Td4[(t1 ) & 0xff] & 0x000000ff) ^ rk[0]; PUTU32(out , s0); s1 = (Td4[(t1 >> 24) ] & 0xff000000) ^ (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ (Td4[(t2 ) & 0xff] & 0x000000ff) ^ rk[1]; PUTU32(out + 4, s1); s2 = (Td4[(t2 >> 24) ] & 0xff000000) ^ (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ (Td4[(t3 ) & 0xff] & 0x000000ff) ^ rk[2]; PUTU32(out + 8, s2); s3 = (Td4[(t3 >> 24) ] & 0xff000000) ^ (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ (Td4[(t0 ) & 0xff] & 0x000000ff) ^ rk[3]; PUTU32(out + 12, s3); } #endif /* AES_ASM */ void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, const unsigned long length, const AES_KEY *key, unsigned char *ivec, const int enc) { unsigned long n; unsigned long len = length; unsigned char tmp[AES_BLOCK_SIZE]; assert(in && out && key && ivec); if (enc) { while (len >= AES_BLOCK_SIZE) { for(n=0; n < AES_BLOCK_SIZE; ++n) tmp[n] = in[n] ^ ivec[n]; AES_encrypt(tmp, out, key); memcpy(ivec, out, AES_BLOCK_SIZE); len -= AES_BLOCK_SIZE; in += AES_BLOCK_SIZE; out += AES_BLOCK_SIZE; } if (len) { for(n=0; n < len; ++n) tmp[n] = in[n] ^ ivec[n]; for(n=len; n < AES_BLOCK_SIZE; ++n) tmp[n] = ivec[n]; AES_encrypt(tmp, tmp, key); memcpy(out, tmp, AES_BLOCK_SIZE); memcpy(ivec, tmp, AES_BLOCK_SIZE); } } else { while (len >= AES_BLOCK_SIZE) { memcpy(tmp, in, AES_BLOCK_SIZE); AES_decrypt(in, out, key); for(n=0; n < AES_BLOCK_SIZE; ++n) out[n] ^= ivec[n]; memcpy(ivec, tmp, AES_BLOCK_SIZE); len -= AES_BLOCK_SIZE; in += AES_BLOCK_SIZE; out += AES_BLOCK_SIZE; } if (len) { memcpy(tmp, in, AES_BLOCK_SIZE); AES_decrypt(tmp, tmp, key); for(n=0; n < len; ++n) out[n] = tmp[n] ^ ivec[n]; memcpy(ivec, tmp, AES_BLOCK_SIZE); } } } work/aes.h0000664000000000000000000000725315063477206007672 0ustar /* * aes.h - Header file declaring AES functions. */ /* * This file is Free Software. It has been modified to as part of its * incorporation into secnet. * * Copyright 2000 Vincent Rijmen, Antoon Bosselaers, Paulo Barreto * Copyright 2004 Fabrice Bellard * Copyright 2013 Ian Jackson * * You may redistribute this file and/or modify it under the terms of * the permissive licence shown below. * * You may redistribute secnet as a whole and/or modify it under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 3, 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 * https://www.gnu.org/licenses/gpl.html. */ /* * Copied from the upstream qemu git tree revision * 55616505876d6683130076b810a27c7889321560 * but was introduced there by Fabrice Bellard in * e4d4fe3c34cdd6e26f9b9975efec7d1e81ad00b6 * AES crypto support * git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1036 \ * c046a42c-6fe2-441c-8c8c-71466251a162 * * Modified by Ian Jackson to change the guard #define from * QEMU_AES_H to AES_H and to add some needed system #include's. * * The header file doesn't appear to have a separate copyright notice * but is clearly a lightly edited (by Bellard) version of code from * Rijmen, Bosselaers and Barreto. * * The original is from rijndael-alg-fst.c, with this copyright * notice: * * rijndael-alg-fst.c * * @version 3.0 (December 2000) * * Optimised ANSI C code for the Rijndael cipher (now AES) * * @author Vincent Rijmen * @author Antoon Bosselaers * @author Paulo Barreto * * This code is hereby placed in the public domain. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef AES_H #define AES_H #include #include #include #define AES_MAXNR 14 #define AES_BLOCK_SIZE 16 struct aes_key_st { uint32_t rd_key[4 *(AES_MAXNR + 1)]; int rounds; }; typedef struct aes_key_st AES_KEY; int AES_set_encrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key); int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key); void AES_encrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key); void AES_decrypt(const unsigned char *in, unsigned char *out, const AES_KEY *key); void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, const unsigned long length, const AES_KEY *key, unsigned char *ivec, const int enc); #endif /* AES_H */ work/argparseactionnoyes.py0000664000000000000000000000273015063477206013376 0ustar # Python argparse --[no-]foo options # # Copyright 2012 "Omnifarious" (a user on StackOverFlow) # Copyright 2013 "btel" (a user on StackOverFlow) # # https://stackoverflow.com/questions/9234258/in-python-argparse-is-it-possible-to-have-paired-no-something-something-arg/20422915#20422915 # # CC-BY-SA 4.0 # by virtue of # https://stackoverflow.com/legal/terms-of-service#licensing # which says everything is CC-BY-SA and has a link to v4.0 # (And which is therefore compatible with secnet's GPLv3+) # # all retrieved 4.11.2019 import argparse class ActionNoYes(argparse.Action): def __init__(self, option_strings, dest, default=None, required=False, help=None): if default is None: raise ValueError('You must provide a default with Yes/No action') if len(option_strings)!=1: raise ValueError('Only single argument is allowed with YesNo action') opt = option_strings[0] if not opt.startswith('--'): raise ValueError('Yes/No arguments must be prefixed with --') opt = opt[2:] opts = ['--' + opt, '--no-' + opt] super(ActionNoYes, self).__init__(opts, dest, nargs=0, const=None, default=default, required=required, help=help) def __call__(self, parser, namespace, values, option_strings=None): if option_strings.startswith('--no-'): setattr(namespace, self.dest, False) else: setattr(namespace, self.dest, True) work/autogen.sh0000775000000000000000000000171215063477206010744 0ustar #!/bin/sh # # This file is part of secnet. # See README for full list of copyright holders. # # secnet 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. # # secnet 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 # version 3 along with secnet; if not, see # https://www.gnu.org/licenses/gpl.html. set -e autoconf autoheader # ^ although configure will run autoheader if we didn't, our # objective is for users not to have to have recent autoconf # installed, so we commit config.h. ./autogen.sh regenerates # the committed files.work/base91-c/0000775000000000000000000000000015063477206010246 5ustar work/base91-c/.gitignore0000664000000000000000000000012515063477206012234 0ustar /base91 /lentest *.o /test/b91dec /test/b91enc /test/lentest /test/*.dat /test/*.b91 work/base91-c/AWK/0000775000000000000000000000000015063477206010670 5ustar work/base91-c/AWK/README0000664000000000000000000000107715063477206011555 0ustar This is a (slow) AWK implementation of the basE91 decoder. It decodes from standard input to standard output. Example usage: awk -f b91dec.awk < file.b91 > file.bin or ./b91dec.awk < file.b91 > file.bin Be careful on non-Unix systems! - During output, some ported versions of awk automatically convert byte values of 0x0A to the native line break sequence of the host system (e.g. 0x0D 0x0A under DOS/Windows). This can result in corrupt binary files. You should test on some examples and compare the output of b91dec.awk with the original data before relying on it. work/base91-c/AWK/b91dec.awk0000775000000000000000000000132715063477206012451 0ustar #!/usr/bin/awk -f # basE91 decoder # Copyright (c) 2000-2006 Joachim Henke # http://base91.sourceforge.net/ BEGIN { b = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~\"" for (i = 0; i < 256; ++i) { --d[sprintf("%c", i)] } for (i = 0; i < 91; ++i) { d[substr(b, i + 1, 1)] = i } b = 0 n = 0 v = -1 } { l = length($0) for (i = 1; i <= l; ++i) { c = d[substr($0, i, 1)] if (c < 0) { continue } if (v < 0) { v = c } else { v += c * 91 b += v * 2 ^ n n += v % 8192 > 88 ? 13 : 14 do { b -= c = b % 256 printf "%c", c b /= 256 n -= 8 } while (n > 7) v = -1 } } } END { if (v + 1) { printf "%c", b + v * 2 ^ n } } work/base91-c/DOS-asm/0000775000000000000000000000000015063477206011451 5ustar work/base91-c/DOS-asm/b91enc.asm0000664000000000000000000000663715063477206013250 0ustar ; basE91 encoder for DOS ; ; Copyright (c) 2005-2006 Joachim Henke ; All rights reserved. ; ; Redistribution and use in source and binary forms, with or without ; modification, are permitted provided that the following conditions are met: ; ; - Redistributions of source code must retain the above copyright notice, ; this list of conditions and the following disclaimer. ; - Redistributions in binary form must reproduce the above copyright notice, ; this list of conditions and the following disclaimer in the documentation ; and/or other materials provided with the distribution. ; - Neither the name of Joachim Henke nor the names of his contributors may ; be used to endorse or promote products derived from this software without ; specific prior written permission. ; ; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE ; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ; INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ; CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ; ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ; POSSIBILITY OF SUCH DAMAGE. bits 16 cpu 8086 org 256 xor sp, sp mov si, ld_0 ; create lookup table mov bp, 90 lc_0: mov bx, 90 mov ah, [bp + si] lc_1: mov al, [bx + si] push ax dec bx jns lc_1 dec bp jns lc_0 inc bx mov sp, a_stck lc_2: push bx push bx jmp short lc_5 lc_3: mov ax, [si] cmp cl, 6 ; bits in queue + 8 < 14? sbb dx, dx inc si mov ch, ah add bp, dx sbb dx, dx xor ch, al and ah, dl and ch, dl sub si, dx xor ch, al shl ax, cl add cl, 8 or bx, ax test bp, bp js lc_4 and bh, 0x1F ; keep 13 bits and dl, 8 and ah, 0x3F cmp bx, byte 89 ; value in bit queue < 89? sbb al, al add dl, cl and ah, al mov cl, 13 or bh, ah ; take 13 or 14 bits sub cl, al add bx, bx mov ax, [bx + a_ltab] mov bx, cx add cl, 16 sub cl, dl sub dl, bl shr bx, cl ; restore bit queue mov cl, dl stosw dec bp jns lc_3 lc_4: push bx mov ah, 0x40 push cx mov bx, 1 lea cx, [di - a_obuf] mov dx, a_obuf int 0x21 ; write to standard output dec bx lc_5: mov ah, 0x3F mov cx, s_ibuf mov dx, a_ibuf int 0x21 ; read from standard input cld pop cx mov si, dx mov di, a_obuf pop bx add bp, ax ; ax = 0 -> EOF jc lc_3 push ax test cl, cl jz lc_6 cmp bx, byte 91 ; value in bit queue < 91? sbb dx, dx cmp cl, 8 ; less than 8 bits in queue? sbb cx, cx add bx, bx and cx, dx mov dx, a_obuf mov ax, [bx + a_ltab] inc cx mov bx, 1 inc cx stosw mov ah, 0x40 int 0x21 ; write out 1 or 2 bytes lc_6: retn ; exit program ld_0: db 'ABCDEFGHIJKLM' db 'NOPQRSTUVWXYZ' db 'abcdefghijklm' db 'nopqrstuvwxyz' db '0123456789!#$' db '%&()*+,./:;<=' db '>?@[]^_`{|}~"' a_stck equ ((lc_2 - $$) + 256) & 510 a_ltab equ 48974 a_obuf equ ((ld_0 - $$) + 257) & 510 s_ibuf equ ((a_ltab - a_obuf - 2) << 4) / 29 a_ibuf equ a_ltab - s_ibuf work/base91-c/DOS-asm/readme.txt0000664000000000000000000000051415063477206013447 0ustar This is a compact 16-bit assembly implementation of the basE91 encoder for DOS. It encodes from standard input to standard output. Minimum system requirements: DOS 2.0, 8086 processor Example usage: b91enc < file.bin > file.b91 Assemble with NASM [http://nasm.sourceforge.net/]: nasm -O2 -o b91enc.com b91enc.asm work/base91-c/Java/0000775000000000000000000000000015063477206011127 5ustar work/base91-c/Java/b91cli.java0000664000000000000000000001165215063477206013062 0ustar /* * basE91 command line front-end * * Copyright (c) 2000-2006 Joachim Henke * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Joachim Henke nor the names of his contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ import java.io.*; public class b91cli { private static void encode(InputStream is, OutputStream os) { int s; byte[] ibuf = new byte[53248]; byte[] obuf = new byte[65536]; basE91 b91 = new basE91(); try { while ((s = is.read(ibuf)) > 0) { s = b91.encode(ibuf, s, obuf); os.write(obuf, 0, s); } s = b91.encEnd(obuf); os.write(obuf, 0, s); } catch (Exception e) { System.err.println(e); } } private static void encodeWrap(InputStream is, OutputStream os) { int i, s; int n = 0; byte[] ibuf = new byte[53248]; byte[] obuf = new byte[65536]; char[] line = new char[76]; basE91 b91 = new basE91(); try { PrintStream ps = new PrintStream(os, false, "US-ASCII"); while ((s = is.read(ibuf)) > 0) { s = b91.encode(ibuf, s, obuf); for (i = 0; i < s; ++i) { line[n++] = (char) obuf[i]; if (n == 76) { ps.println(line); n = 0; } } } s = b91.encEnd(obuf); for (i = 0; i < s; ++i) { line[n++] = (char) obuf[i]; if (n == 76) { ps.println(line); n = 0; } } if (n > 0) ps.println(new String(line, 0, n)); } catch (Exception e) { System.err.println(e); } } private static void decode(InputStream is, OutputStream os) { int s; byte[] ibuf = new byte[65536]; byte[] obuf = new byte[57344]; basE91 b91 = new basE91(); try { while ((s = is.read(ibuf)) > 0) { s = b91.decode(ibuf, s, obuf); os.write(obuf, 0, s); } s = b91.decEnd(obuf); os.write(obuf, 0, s); } catch (Exception e) { System.err.println(e); } } private static void errExit(String msg) { System.err.println("syntax error - " + msg + "\nTry `-h' option for more information."); System.exit(3); } public static void main(String[] args) { int i; boolean enc = true; boolean lbr = true; String ifn = null; String ofn = null; for (i = 0; i < args.length; ++i) if (args[i].length() == 2 && args[i].charAt(0) == '-') switch (args[i].charAt(1)) { case 'd': enc = false; break; case 'u': lbr = false; break; case 'h': System.out.println("Usage: base91 [OPTION] infile [outfile]\n\n -d\tdecode a basE91 encoded file\n -u\tleave encoder output unformatted (disable line wrapping)\n -h\tdisplay this help and exit\n -V\toutput version information and exit"); return; case 'V': System.out.println("base91 0.6.0\nCopyright (c) 2000-2006 Joachim Henke"); return; default: errExit("invalid option: " + args[i]); } else if (ifn == null) ifn = args[i]; else if (ofn == null) ofn = args[i]; else errExit("too many arguments: " + args[i]); if (ifn == null) errExit("file name missing"); if (ofn == null) if (enc) ofn = ifn + (lbr ? "_b91.txt" : ".b91"); else { String lifn = ifn.toLowerCase(); if (ifn.length() > 4 && lifn.endsWith(".b91")) ofn = ifn.substring(0, ifn.length() - 4); else if (ifn.length() > 8 && lifn.endsWith("_b91.txt")) ofn = ifn.substring(0, ifn.length() - 8); else ofn = ifn + ".bin"; } try { FileInputStream ifs = new FileInputStream(ifn); FileOutputStream ofs = new FileOutputStream(ofn); if (enc) if (lbr) encodeWrap(ifs, ofs); else encode(ifs, ofs); else decode(ifs, ofs); ifs.close(); ofs.close(); } catch (Exception e) { System.err.println(e); } } } work/base91-c/Java/basE91.java0000664000000000000000000000634115063477206013022 0ustar /* * basE91 encoding/decoding routines * * Copyright (c) 2000-2006 Joachim Henke * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Joachim Henke nor the names of his contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ public class basE91 { private int ebq, en, dbq, dn, dv; public final byte[] enctab; private final byte[] dectab; public int encode(byte[] ib, int n, byte[] ob) { int i, c = 0; for (i = 0; i < n; ++i) { ebq |= (ib[i] & 255) << en; en += 8; if (en > 13) { int ev = ebq & 8191; if (ev > 88) { ebq >>= 13; en -= 13; } else { ev = ebq & 16383; ebq >>= 14; en -= 14; } ob[c++] = enctab[ev % 91]; ob[c++] = enctab[ev / 91]; } } return c; } public int encEnd(byte[] ob) { int c = 0; if (en > 0) { ob[c++] = enctab[ebq % 91]; if (en > 7 || ebq > 90) ob[c++] = enctab[ebq / 91]; } encReset(); return c; } public void encReset() { ebq = 0; en = 0; } public int decode(byte[] ib, int n, byte[] ob) { int i, c = 0; for (i = 0; i < n; ++i) { if (dectab[ib[i]] == -1) continue; if (dv == -1) dv = dectab[ib[i]]; else { dv += dectab[ib[i]] * 91; dbq |= dv << dn; dn += (dv & 8191) > 88 ? 13 : 14; do { ob[c++] = (byte) dbq; dbq >>= 8; dn -= 8; } while (dn > 7); dv = -1; } } return c; } public int decEnd(byte[] ob) { int c = 0; if (dv != -1) ob[c++] = (byte) (dbq | dv << dn); decReset(); return c; } public void decReset() { dbq = 0; dn = 0; dv = -1; } public basE91() { int i; String ts = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_`{|}~\""; enctab = ts.getBytes(); dectab = new byte[256]; for (i = 0; i < 256; ++i) dectab[i] = -1; for (i = 0; i < 91; ++i) dectab[enctab[i]] = (byte) i; encReset(); decReset(); } } work/base91-c/Java/build_jar.sh0000775000000000000000000000026215063477206013421 0ustar #!/bin/sh javac -encoding US-ASCII -g:none -source 1.3 -target 1.2 basE91.java b91cli.java && \ jar cvfm base91.jar manifest.mf b91cli.class basE91.class license.txt readme.txt work/base91-c/Java/license.txt0000664000000000000000000000276615063477206013325 0ustar Copyright (c) 2000-2006 Joachim Henke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Joachim Henke nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. work/base91-c/Java/manifest.mf0000664000000000000000000000016015063477206013256 0ustar Main-Class: b91cli Package-Title: basE91 command line tool Package-Version: 0.6.0 Package-Vendor: Joachim Henke work/base91-c/Java/readme.txt0000664000000000000000000000143115063477206013124 0ustar This is an implementation of the basE91 encoder and decoder in Java. Syntax: java -jar base91.jar [OPTION] infile [outfile] Options: -d decode a basE91 encoded file; all non-alphabet characters (such as newlines) are ignored -u leave encoder output unformatted; i. e., disable line wrapping after 76 characters -h display short help and exit -V output version information and exit If no outfile is given for encoding, it defaults to `infile_b91.txt' (or to `infile.b91' with the `-u' switch). On decoding, the added file extension is removed to generate the name for outfile; otherwise, if infile hasn't a default extension, the decoded data is written to `infile.bin'. For further information visit the basE91 home page at http://base91.sourceforge.net/ work/base91-c/LICENSE0000664000000000000000000000273515063477206011262 0ustar Copyright (c) 2000-2006 Joachim Henke All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - Neither the name of Joachim Henke nor the names of his contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. work/base91-c/Makefile0000664000000000000000000000165415063477206011714 0ustar CFLAGS = -Wall -W -O2 LDFLAGS = -s CC = gcc INSTALL = install INSTALL_DATA = $(INSTALL) -m 444 INSTALL_PROGRAM = $(INSTALL) -m 555 prefix = /usr/local exec_prefix = $(prefix) bindir = $(exec_prefix)/bin mandir = $(prefix)/share/man man1dir = $(mandir)/man1 manext = .1 BIN = base91 lentest .PHONY: all install check clean all: $(BIN) %.o: %.c $(CC) $(CFLAGS) -c $< base91: cli.o base91.o $(CC) $(LDFLAGS) -o $@ $^ lentest: lentest.o base91.o install: all mkdir -p $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) base91 $(DESTDIR)$(bindir)/base91 ln -sf base91 $(DESTDIR)$(bindir)/b91dec ln -sf base91 $(DESTDIR)$(bindir)/b91enc mkdir -p $(DESTDIR)$(man1dir) $(INSTALL_DATA) base91.1 $(DESTDIR)$(man1dir)/base91$(manext) ln -sf base91$(manext) $(DESTDIR)$(man1dir)/b91dec$(manext) ln -sf base91$(manext) $(DESTDIR)$(man1dir)/b91enc$(manext) check: all cd test && $(MAKE) clean: -rm -f *.o $(BIN) core cd test && $(MAKE) clean work/base91-c/NEWS0000664000000000000000000000443215063477206010750 0ustar [0.6.0] 2006-11-01 * basE91 encoding/decoding routines restructured to be thread-safe * lots of type fixes * new core utility is `base91', with a behaviour similar to GNU base64 * introduce `-w' switch for wrapping encoded output lines after given length * long option handling * use standard I/O functions for better portability * MinGW compatibility code added * minor extensions to `make check' * Java-tool wraps output lines by default; can be avoided with the `-u' switch * license changed to BSD [0.5.2] 2006-08-25 * code cleanup * encoder for DOS rewritten to be faster and compatible down to Intel 8086 [0.5.1] 2005-10-05 * Java-b91enc now handles file extensions case insensitively * native DOS version of basE91 encoder added [0.5.0] 2005-06-24 * ATTENTION: this version breaks backward compatibility because the basE91 alphabet was changed to reduce the occurrence of double quotes - sorry, I should have done this long before * b91dec is installed as a link to b91enc * `-e' option added (complement to `-d') * build system should be more portable now [0.4.2] 2005-05-16 * AWK basE91 decoder no longer depends on GNU extensions * Java byte code removed (distributed separately in a jar file) [0.4.1] 2005-05-07 * some code cleanup * Java-b91enc can break encoded output to lines of 76 characters (`-b' switch) [0.4.0] 2005-04-26 * improved encoder behaviour on stream ends (can save one byte sometimes) * allocate buffer memory dynamically; use overlapping buffers * new `-m' switch can be used for testing * verbose mode extended: `-vv' shows memory statistics * `make check' implemented - runs some basic tests [0.3.1] 2005-04-19 * b91enc has a verbose mode now (`-v' switch) * Java-b91enc accepts command line syntax with only one FILE argument again [0.3.0] 2005-04-17 * the code was restructured to allow a more universal use of the basE91 backend * version switch changed to `-V' which is more common - sorry for that * `make install' is possible now * changed Java-b91enc to be a bit more similar to the C version * implementation in PHP added [0.2.3] 2005-04-11 * man page included (thanks to Kei!) * version (-v) switch added [0.2.2] 2005-04-10 * fixed a bug in decoder that could result in corrupt output on 64-bit systems * Java class files included [0.2.1] 2005-04-09 * first public release work/base91-c/PHP4/0000775000000000000000000000000015063477206010761 5ustar work/base91-c/PHP4/README0000664000000000000000000000164415063477206011646 0ustar base91_encode -- Encodes data with basE91 string base91_encode ( string data ) base91_encode() returns data encoded with basE91. This encoding is designed to make binary data survive transport through transport layers that are not 8-bit clean, such as mail bodies. basE91-encoded data takes at most 23% more space than the original data. Example: This example will produce: nX,<:WRT%yV%!5:maref3+1RrUb64^M ----- base91_decode -- Decodes data encoded with basE91 string base91_decode ( string encoded_data ) base91_decode() decodes encoded_data ignoring non-alphabet characters and returns the original data. The returned data may be binary. Example: This example will produce: This is an encoded string work/base91-c/PHP4/base91.php0000664000000000000000000000275115063477206012563 0ustar ', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '"' ); $b91_dectab = array_flip($b91_enctab); function base91_decode($d) { global $b91_dectab; $l = strlen($d); $v = -1; for ($i = 0; $i < $l; ++$i) { $c = $b91_dectab[$d{$i}]; if (!isset($c)) continue; if ($v < 0) $v = $c; else { $v += $c * 91; $b |= $v << $n; $n += ($v & 8191) > 88 ? 13 : 14; do { $o .= chr($b & 255); $b >>= 8; $n -= 8; } while ($n > 7); $v = -1; } } if ($v + 1) $o .= chr(($b | $v << $n) & 255); return $o; } function base91_encode($d) { global $b91_enctab; $l = strlen($d); for ($i = 0; $i < $l; ++$i) { $b |= ord($d{$i}) << $n; $n += 8; if ($n > 13) { $v = $b & 8191; if ($v > 88) { $b >>= 13; $n -= 13; } else { $v = $b & 16383; $b >>= 14; $n -= 14; } $o .= $b91_enctab[$v % 91] . $b91_enctab[$v / 91]; } } if ($n) { $o .= $b91_enctab[$b % 91]; if ($n > 7 || $b > 90) $o .= $b91_enctab[$b / 91]; } return $o; } ?> work/base91-c/README0000664000000000000000000000447015063477206011133 0ustar basE91 - converting binary data to ASCII text ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Copyright (c) 2000-2006 Joachim Henke basE91 is an advanced method for encoding binary data as ASCII characters. It is similar to UUencode or base64, but is more efficient. The overhead produced by basE91 depends on the input data. It amounts at most to 23% (versus 33% for base64) and can range down to 14%, which typically occurs on 0-byte blocks. This makes basE91 very useful for transferring larger files over binary insecure connections like e-mail or terminal lines. The current algorithm has been written with portability and simplicity in mind an is therefore not necessarily optimised for speed. * Alphabet As the name suggests, basE91 needs 91 characters to represent the encoded binary data in ASCII. From the 94 printable ASCII characters (0x21-0x7E), the following three ones have been omitted to build the basE91 alphabet: - (dash, 0x2D) ' (apostrophe, 0x27) \ (backslash, 0x5C) The translation table is composed of the remaining characters as shown below. 0 A 13 N 26 a 39 n 52 0 65 % 78 > 1 B 14 O 27 b 40 o 53 1 66 & 79 ? 2 C 15 P 28 c 41 p 54 2 67 ( 80 @ 3 D 16 Q 29 d 42 q 55 3 68 ) 81 [ 4 E 17 R 30 e 43 r 56 4 69 * 82 ] 5 F 18 S 31 f 44 s 57 5 70 + 83 ^ 6 G 19 T 32 g 45 t 58 6 71 , 84 _ 7 H 20 U 33 h 46 u 59 7 72 . 85 ` 8 I 21 V 34 i 47 v 60 8 73 / 86 { 9 J 22 W 35 j 48 w 61 9 74 : 87 | 10 K 23 X 36 k 49 x 62 ! 75 ; 88 } 11 L 24 Y 37 l 50 y 63 # 76 < 89 ~ 12 M 25 Z 38 m 51 z 64 $ 77 = 90 " * Building 1. `cd' to the directory containing the package's source code and type `make' to compile the package 2. optionally, type `make check' to run any self-tests that come with the package 3. type `make install' to install the program and documentation in `/usr/local' (to specify another installation prefix than `/usr/local', type `make prefix=PATH install' instead) 4. you can remove the program binaries and object files from the source code directory by typing `make clean' * Developer Joachim Henke * Copying All source code in this package is released under the terms of the BSD license. See the file LICENSE for copying permission. * See also Please visit the basE91 home page [http://base91.sourceforge.net/] for the latest version and pre-compiled binaries. work/base91-c/base91.10000664000000000000000000000334215063477206011416 0ustar .TH BASE91 1 "November 2006" "base91 0.6.0" basE91 .SH NAME base91, b91enc, b91dec \- basE91 encode/decode data .SH SYNOPSIS .B base91 .RI [ OPTION "]... [" FILE ] .SH DESCRIPTION Convert binary data in FILE to plain ASCII text (or vice versa), writing to standard output. With no FILE, or when FILE is \-, read standard input. .TP .BR \-d ", " \-\-decode decode data (default for .BR b91dec ); all non\-alphabet characters (such as newlines) are ignored .TP .BI "\-m " SIZE use maximum SIZE bytes of main memory for buffers (default 64K); SIZE may be followed by a multiplicative suffix: .I K 1024, .I M 1024*1024 .TP .BR \-o ", " \-\-output =\fIFILE\fR write result to FILE instead of standard output .TP .BR \-v ", " \-\-verbose run in verbose mode and write some statistics to standard error; use it twice to increase verbosity .TP .BR \-w ", " \-\-wrap =\fICOLS\fR wrap encoded lines after COLS characters (default 76); use 0 to disable line wrapping (default for .BR b91enc ) .TP .B \-\-help prints out the available program options .TP .B \-\-version output version information and exit .PP basE91 is an advanced method for encoding binary data as ASCII characters. It is similar to UUencode or base64, but is more efficient. The overhead produced by basE91 depends on the input data. It amounts at most to 23% (versus 33% for base64) and can range down to 14%, which typically occurs on 0\-byte blocks. This makes basE91 very useful for transferring larger files over binary insecure connections like e\-mail or terminal lines. .SH AUTHOR Written by Joachim Henke. .SH "REPORTING BUGS" Report bugs to . .SH COPYRIGHT Copyright (c) 2000\-2006 Joachim Henke .SH "SEE ALSO" base64(1), uuencode(1) http://base91.sourceforge.net/ work/base91-c/base91.c0000664000000000000000000001327215063477206011503 0ustar /* * basE91 encoding/decoding routines * * Copyright (c) 2000-2006 Joachim Henke * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Joachim Henke nor the names of his contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "base91.h" const unsigned char enctab[91] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '#', '$', '%', '&', '(', ')', '*', '+', ',', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '"' }; const unsigned char dectab[256] = { 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 62, 90, 63, 64, 65, 66, 91, 67, 68, 69, 70, 71, 91, 72, 73, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 74, 75, 76, 77, 78, 79, 80, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 81, 91, 82, 83, 84, 85, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 86, 87, 88, 89, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91 }; void basE91_init(struct basE91 *b) { b->queue = 0; b->nbits = 0; b->val = -1; } size_t basE91_encode(struct basE91 *b, const void *i, size_t len, void *o) { const unsigned char *ib = i; unsigned char *ob = o; size_t n = 0; while (len--) { b->queue |= *ib++ << b->nbits; b->nbits += 8; if (b->nbits > 13) { /* enough bits in queue */ unsigned int val = b->queue & 8191; if (val > 88) { b->queue >>= 13; b->nbits -= 13; } else { /* we can take 14 bits */ val = b->queue & 16383; b->queue >>= 14; b->nbits -= 14; } ob[n++] = enctab[val % 91]; ob[n++] = enctab[val / 91]; } } return n; } /* process remaining bits from bit queue; write up to 2 bytes */ size_t basE91_encode_end(struct basE91 *b, void *o) { unsigned char *ob = o; size_t n = 0; if (b->nbits) { ob[n++] = enctab[b->queue % 91]; if (b->nbits > 7 || b->queue > 90) ob[n++] = enctab[b->queue / 91]; } b->queue = 0; b->nbits = 0; b->val = -1; return n; } /* return maximum length that an input of length i could encode to * (this is a maximum, not a precise figure, because the actual * size depends on the precise data */ size_t basE91_encode_maxlen(size_t i /* must be < SIZE_T_MAX/8 */) { size_t bits = i*8; size_t pairs = bits / 13; size_t leftover = bits % 13; return 2*pairs + (leftover==0 ? 0 : leftover<=6 ? 1 : 2); } size_t basE91_decode(struct basE91 *b, const void *i, size_t len, void *o) { const unsigned char *ib = i; unsigned char *ob = o; size_t n = 0; unsigned int d; while (len--) { d = dectab[*ib++]; if (d == 91) continue; /* ignore non-alphabet chars */ if (b->val == -1) b->val = d; /* start next value */ else { b->val += d * 91; b->queue |= b->val << b->nbits; b->nbits += (b->val & 8191) > 88 ? 13 : 14; do { ob[n++] = b->queue; b->queue >>= 8; b->nbits -= 8; } while (b->nbits > 7); b->val = -1; /* mark value complete */ } } return n; } /* process remaining bits; write at most 1 byte */ size_t basE91_decode_end(struct basE91 *b, void *o) { unsigned char *ob = o; size_t n = 0; if (b->val != -1) ob[n++] = b->queue | b->val << b->nbits; b->queue = 0; b->nbits = 0; b->val = -1; return n; } /* return maximum length that an input of length i could decode to * (this is a maximum, not a precise figure, because the actual * size depends on the precise data */ size_t basE91_decode_maxlen(size_t i /* must be < SIZE_T_MAX/7 */) { size_t pairs = i / 2; size_t bits = pairs * 14; size_t bytes = bits / 8; size_t leftover = i % 2; return bytes + !!leftover; } work/base91-c/base91.h0000664000000000000000000000126715063477206011511 0ustar /* * Copyright (c) 2000-2006 Joachim Henke * * For conditions of distribution and use, see copyright notice in base91.c */ #ifndef BASE91_H #define BASE91_H 1 #include struct basE91 { unsigned long queue; unsigned int nbits; int val; }; void basE91_init(struct basE91 *); size_t basE91_encode(struct basE91 *, const void *, size_t, void *); size_t basE91_encode_end(struct basE91 *, void *); size_t basE91_encode_maxlen(size_t /* must be < SIZE_T_MAX/8 */); size_t basE91_decode(struct basE91 *, const void *, size_t, void *); size_t basE91_decode_end(struct basE91 *, void *); size_t basE91_decode_maxlen(size_t /* must be < SIZE_T_MAX/7 */); #endif /* base91.h */ work/base91-c/cli.c0000664000000000000000000001653215063477206011170 0ustar /* * basE91 command line front-end * * Copyright (c) 2000-2006 Joachim Henke * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Joachim Henke nor the names of his contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #ifdef _WIN32 #include #include #endif #include #include "base91.h" #define FLG_D 1 #define FLG_V 2 #define FLG_VV 4 static char status[32]; static const char *progname; static char *ibuf, *obuf; static size_t ibuf_size, llen; static struct basE91 b91; static void stream_b91enc_p(void) { size_t itotal = 0; size_t ototal = 0; size_t s; while ((s = fread(ibuf, 1, ibuf_size, stdin)) > 0) { itotal += s; s = basE91_encode(&b91, ibuf, s, obuf); ototal += s; fwrite(obuf, 1, s, stdout); } s = basE91_encode_end(&b91, obuf); /* empty bit queue */ ototal += s; fwrite(obuf, 1, s, stdout); sprintf(status, "\t%.2f%%\n", itotal ? (float) ototal / itotal * 100.0 : 1.0); } static void stream_b91enc_w(void) { size_t l = llen; size_t ltotal = 0; size_t i, s; char x; while ((s = fread(ibuf, 1, ibuf_size, stdin)) > 0) { s = basE91_encode(&b91, ibuf, s, obuf); for (i = 0; l <= s; l += llen) { x = obuf[l]; obuf[l] = '\0'; puts(obuf + i); ++ltotal; obuf[l] = x; i = l; } fwrite(obuf + i, 1, s - i, stdout); l -= s; } s = basE91_encode_end(&b91, obuf); if (s || l < llen) { obuf[s] = '\0'; if (s > l) { x = obuf[1]; obuf[1] = '\0'; puts(obuf); ++ltotal; obuf[0] = x; } puts(obuf); ++ltotal; } sprintf(status, "\t%lu lines\n", (unsigned long) ltotal); } static void stream_b91dec(void) { size_t s; while ((s = fread(ibuf, 1, ibuf_size, stdin)) > 0) { s = basE91_decode(&b91, ibuf, s, obuf); fwrite(obuf, 1, s, stdout); } s = basE91_decode_end(&b91, obuf); /* empty bit queue */ fwrite(obuf, 1, s, stdout); sprintf(status, "done\n"); } static int init_flags(const char *p) { size_t l = strlen(p); if (l > 5) { progname = p + l - 6; if (!strcmp(progname, "b91enc")) return 0; if (!strcmp(progname, "b91dec")) return FLG_D; } llen = 76; progname = "base91"; return 0; } int main(int argc, char **argv) { size_t buf_size = 65536; /* buffer memory defaults to 64 KiB */ int flags = init_flags(*argv); const char *ifile = "from standard input"; const char *ofile = NULL; int opt; struct option longopts[8] = { {"decode", no_argument, NULL, 'd'}, {"output", required_argument, NULL, 'o'}, {"verbose", no_argument, NULL, 'v'}, {"wrap", required_argument, NULL, 'w'}, {"help", no_argument, NULL, 'h'}, {"version", no_argument, NULL, 'V'}, {NULL, 0, NULL, 0} }; while ((opt = getopt_long(argc, argv, "dem:o:vw:hV", longopts, NULL)) != -1) switch (opt) { case 'd': flags |= FLG_D; break; case 'e': flags &= ~FLG_D; break; case 'm': { char *t; long l = strtol(optarg, &t, 0); if (t == optarg || strlen(t) > 1 || l < 0) { fprintf(stderr, "invalid SIZE argument: `%s'\n", optarg); return EXIT_FAILURE; } buf_size = l; switch (*t | 32) { case ' ': case 'b': break; case 'k': buf_size <<= 10; break; case 'm': buf_size <<= 20; break; default: fprintf(stderr, "invalid SIZE suffix: `%s'\n", t); return EXIT_FAILURE; } } break; case 'o': if (strcmp(optarg, "-")) ofile = optarg; break; case 'v': flags |= (flags & FLG_V) ? FLG_VV : FLG_V; break; case 'w': { char *t; long l = strtol(optarg, &t, 0); if (*t || l < 0) { fprintf(stderr, "invalid number of columns: `%s'\n", optarg); return EXIT_FAILURE; } llen = l; } break; case 'h': printf("Usage: %s [OPTION]... [FILE]\n" "basE91 encode or decode FILE, or standard input, to standard output.\n", progname); puts("\n -d, --decode\t\tdecode data\n" " -m SIZE\t\tuse SIZE bytes of memory for buffers (suffixes b, K, M)\n" " -o, --output=FILE\twrite to FILE instead of standard output\n" " -v, --verbose\t\tverbose mode\n" " -w, --wrap=COLS\twrap encoded lines after COLS characters (default 76)\n" " --help\t\tdisplay this help and exit\n" " --version\t\toutput version information and exit\n\n" "With no FILE, or when FILE is -, read standard input."); return EXIT_SUCCESS; case 'V': printf("%s 0.6.0\nCopyright (c) 2000-2006 Joachim Henke\n", progname); return EXIT_SUCCESS; default: fprintf(stderr, "Try `%s --help' for more information.\n", *argv); return EXIT_FAILURE; } if (flags & FLG_D) { ibuf_size = (buf_size - 1) << 3; if (ibuf_size < 15) { fputs("SIZE must be >= 3 for decoding\n", stderr); return EXIT_FAILURE; } ibuf_size /= 15; } else { ibuf_size = (buf_size - 2) << 4; if (ibuf_size < 29) { fputs("SIZE must be >= 4 for encoding\n", stderr); return EXIT_FAILURE; } ibuf_size /= 29; } if (optind < argc && strcmp(argv[optind], "-")) { ifile = argv[optind]; if (freopen(ifile, "r", stdin) != stdin) { perror(ifile); return EXIT_FAILURE; } } if (ofile) if (freopen(ofile, "w", stdout) != stdout) { perror(ofile); return EXIT_FAILURE; } if (flags & FLG_VV) fprintf(stderr, "using %lu bytes for buffers; input buffer: %lu bytes\n", (unsigned long) buf_size, (unsigned long) ibuf_size); obuf = malloc(buf_size); if (!obuf) { fputs("failed to allocate buffer memory\n", stderr); return EXIT_FAILURE; } basE91_init(&b91); #ifdef _WIN32 _setmode(_fileno(stdin), _O_BINARY); #endif if (flags & FLG_D) { #ifdef _WIN32 _setmode(_fileno(stdout), _O_BINARY); #endif ibuf = obuf + 1; /* create overlapping buffers to use memory efficiently */ if (flags & FLG_V) fprintf(stderr, "decoding %s ...", ifile); stream_b91dec(); } else { ibuf = obuf + buf_size - ibuf_size; /* partial overlap */ if (flags & FLG_V) fprintf(stderr, "encoding %s ...", ifile); if (llen) stream_b91enc_w(); else stream_b91enc_p(); } free(obuf); if (flags & FLG_V) fputs(status, stderr); return EXIT_SUCCESS; } work/base91-c/lentest.c0000664000000000000000000000547115063477206012077 0ustar /* * basE91 length calculation test * * Copyright (c) 2019 Ian Jackson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of Joachim Henke nor the names of his contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include "base91.h" static size_t upto = (14*16 + 14 + 16)*2; static int do_test(int do_do, int fill, const char *what, size_t f(struct basE91 *, const void *, size_t, void *), size_t f_end(struct basE91 *, void *), size_t f_maxlen(size_t) ) { struct basE91 b; size_t i, o, exp; int bad = 0; char ibuf[upto]; char obuf[upto*2+100]; /* in case we have bugs */ memset(ibuf,fill,upto); if (!do_do) { printf("%s: skipping\n",what); return 0; } for (i=0; i=2) { do_encode = !!strchr(argv[1],'e'); do_decode = !!strchr(argv[1],'d'); } if (argc>=3) { upto = atoi(argv[2]); } #define MAYBE_DO_TEST(ed, fill) \ (bad |= do_test(do_##ed, (fill), #ed, \ basE91_##ed, basE91_##ed##_end, basE91_##ed##_maxlen)) MAYBE_DO_TEST(encode, 0xff); MAYBE_DO_TEST(decode, 'A'); if (bad) exit(8); printf("ok\n"); exit(0); } work/base91-c/test/0000775000000000000000000000000015063477206011225 5ustar work/base91-c/test/Makefile0000664000000000000000000000025215063477206012664 0ustar SHELL = /bin/sh .PHONY: all clean all: ln -sf ../base91 b91enc ln -sf ../base91 b91dec ln -sf ../lentest . $(SHELL) test.sh clean: -rm -f b91??c *.b91 *.dat core work/base91-c/test/test.sh0000664000000000000000000001035115063477206012540 0ustar fail_exit() { echo 'FAILED!' exit 1 } T='--------------------------------------+' ./b91enc --version || fail_exit echo -n ' extracting test files...' echo '?/9f4iG0)z4jY;<$:B$AvWAAoB:CgALvDCeR+t bit0.dat && \ echo '?/9f4iG0)z4jZgv|?AUYRAgA%AAAUA:y5L9LcoHE8HJI_yGIJ>T' | ./b91dec | bunzip2 > bit1.dat && \ echo '71kS$5F/VZC0+Zw,c%df~>T1XkEvl{weS1NQ@@3Gd*`P2p~)^4>yP{Y9MFo.Q2=Mmsk:"lhR%87Ey_GRP 7hn_L73QVn>5CK0{6eHmBBtzP05[`PG3*"+n(bE$S";_b^gU5D87O,QjqW3wVc(_T8_:O{8P$9~{n7"SiU;Ca{w_;HB6L&u2W8M>ScI aP#E.:novN/#Mk$STQeM^dSp5~JKkb3[t5_*3q&V/Z7aVdFPJ#P&TR3#`W+;&GpWk9CHq%R|fV~(K:g6_Du~m j7S?;hnI$xFkGTDRJSJ#NNu_;(]ud#f3{1Q|;jH4l{Ql^mi]*jH>mCqmSW,Bx4^MBrnOtoa4gbM7m3S:OMQ4nt.un+)Zelc,Eoa|c!yN"3dV3b9YY2opW= VS&}g7RI#0g?^mT#@#;Q%kmT2gGX8.*H;^Dywb$(o(t;/_#j8Vi*djlobF!Xw)B2lLV_sio/`z1zu^+^m9D~At(UH|,6o=j6kT(=Cz8q6(FBOm~&+F*,IgYH;XtV+,ejB".y`K9!2VxGi?YeQs+q9Ses]Qt RzDX;Oyx]$Tcm;[GT4Auh}yN+%:}Q_xv5_B[/q30b!qFIn>w{^w`Qa99`=r0eo&uZFyq*` dg.h"YJiW)t5BaO;?liP7Y|)o|]9KwM`rZO1p#}zYnX|.C(+P.RXy>T0XG^58"DO8Su/5MElg)J>eU,BjzB5}et|m+4>$GdR<(_[PUxc^o|c2L{w~mE@SZ.FH ;(=m1zJIMyYbz8z0=gf*;)WDk:3@7^YN&i1|MVarqV)qe]x9kJ)OPi&F``3?|P:(Fg$1(P2{xf0yf$WBU!.YmwYIT F)^pdagK0(P~/96zGoD,QZlX]"`h/U`^ZWZeJ.c:XQ!WAz(Z|&2/E3)*V?vAb>?MUV5*T8i3u)apx`5<8J`bopXm> *J,^8m.Ldfyj~KR#mt*&Z3a]N9WUT+^c2*@07TM:ATg(D)YMiSR[RTll_7DB3.HhUzKL:N0.:%J,<(_$0Ab{``OaN 43R:29/1$KXzs2%58B]G,h*r$Z/PqQqwh*sY.o(;Kkqw`1N^;JFk!;^?V+6/7irU+h._wN},b?.O.8t#:prfx9ZEE t2AWg56N]g6MF0Bs7X^07[RwvgwxT@4RLk#SXzXj74XT93xa=$c0Sgi6z{apd:[5sDmwG;&jKSGGR.=c9qS$u[S:y wM|buns:@)g|V@|QGD9wipn#u*HCFK7W}iD}f|E{h@PR@2+m%nN>u+"+>x&3iF+0`2GE03Gg;K`[.E[X4Swjchxf6 LusH!p1)[}$SyFI"w|fztPfp*<~6Bu&1(.0XH68BX?a9mFcGoT>~qd7qtf)+v.o9(IAL7|jz32K"9d1Nh*1},xKnvFBHnD&1fv9+h,+nww+PRv7I2wU)B`nty%~eJ2OvRIa^k@T2 *Y:8fa@pLgwFy,[Ea$di~YWC]4)j&3=B0nZn())A|p,70Y02g3ArYWjs+U' | ./b91enc -do rnd0.dat && \ ./b91enc --decode -o rnd1.dat rnd0.dat && echo ' OK' || fail_exit echo " basE91 encode files: +-- best case --$T" ./b91enc -m 5k -vv --output=bit0.b91 bit0.dat echo "+-- worst case -$T" ./b91enc -m 3k -vv --output=bit1.b91 bit1.dat echo "+-- random data $T" ./b91enc -m 96 -vv --output=rnd0.b91 rnd0.dat ./b91enc -m 72 -vv --output=rnd1.b91 rnd1.dat echo ' comparing check sums...' cksum rnd?.dat *.b91 | while read C S F do echo -n "$F " case $F in rnd0.dat) V='15559944992141';; rnd1.dat) V='3514104192626';; bit0.b91) V='33531953171198370';; bit1.b91) V='5394938771290552';; rnd0.b91) V='32051515602633';; rnd1.b91) V='2018291165770';; esac test $V = "$C$S" && echo OK || exit 1 done || fail_exit echo " basE91 decode and compare: +-- best case --$T" fold -w 83 bit0.b91 | ./b91dec -vvm 2K | cmp bit0.dat && echo PASSED || fail_exit echo "+-- worst case -$T" fold -w 79 bit1.b91 | ./b91dec -vvm 1K | cmp bit1.dat && echo PASSED || fail_exit echo "+-- random data $T" fold -w 73 rnd0.b91 | ./b91dec -vvm 89 | cmp rnd0.dat && echo PASSED || fail_exit fold -w 71 rnd1.b91 | ./b91dec -vvm 73 | cmp rnd1.dat && echo PASSED || fail_exit echo " maxlength tests:" ./lentest && echo PASSED || fail_exit echo ' ================ all tests passed ================ ' work/base91-python/0000775000000000000000000000000015063477206011345 5ustar work/base91-python/.gitignore0000664000000000000000000000035015063477206013333 0ustar *.py[co] # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox #Translations *.mo #Mr Developer .mr.developer.cfg work/base91-python/README.md0000664000000000000000000000060115063477206012621 0ustar base91-python ============= A python implementation of Base91 as described on http://base91.sourceforge.net/ , licenced under the New BSD License. Usage ----- import base91 base91.encode('test') #result: 'fPNKd' base91.encode(b'\xfe\x03\x00W\xa9\xbc') #result: 'VLv(GdNE' base91.decode('8D9Kc)=/2$WzeFui#G9Km+<{VT2u9MZil}[A') # result: 'May a moody baby doom a yam?\n' work/base91-python/base91/0000775000000000000000000000000015063477206012431 5ustar work/base91-python/base91/__init__.py0000664000000000000000000000714115063477206014545 0ustar # Base91 encode/decode for Python 2 and Python 3 # # Copyright (c) 2012 Adrien Beraud # Copyright (c) 2015 Guillaume Jacquenot # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of Adrien Beraud, Wisdom Vibes Pte. Ltd., nor the names # of its contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # import struct base91_alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '#', '$', '%', '&', '(', ')', '*', '+', ',', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '"'] decode_table = dict((v, k) for k, v in enumerate(base91_alphabet)) def decode(encoded_str): ''' Decode Base91 string to a bytearray ''' v = -1 b = 0 n = 0 out = bytearray() for strletter in encoded_str: if not strletter in decode_table: continue c = decode_table[strletter] if (v < 0): v = c else: v += c * 91 b |= v << n n += 13 if (v & 8191) > 88 else 14 while True: out += struct.pack('B', b & 255) b >>= 8 n -= 8 if not n > 7: break v = -1 if v + 1: out += struct.pack('B', (b | v << n) & 255) return out def encode(bindata): ''' Encode a bytearray to a Base91 string ''' b = 0 n = 0 out = '' for count in range(len(bindata)): byte = bindata[count:count + 1] b |= struct.unpack('B', byte)[0] << n n += 8 if n > 13: v = b & 8191 if v > 88: b >>= 13 n -= 13 else: v = b & 16383 b >>= 14 n -= 14 out += base91_alphabet[v % 91] + base91_alphabet[v // 91] if n: out += base91_alphabet[b % 91] if n > 7 or b > 90: out += base91_alphabet[b // 91] return out work/base91-python/setup.py0000664000000000000000000000040415063477206013055 0ustar from setuptools import setup setup( name='base91', version='1.0.1', packages=['base91'], url='https://github.com/SunDwarf/base91-python', license='New BSD', author='', author_email='', description='Base91 encoding in Python' ) work/base91.py0000777000000000000000000000000015063477206016173 2base91-python/base91/__init__.pyustar work/base91s/0000775000000000000000000000000015063477206010211 5ustar work/base91s/Dir.sd.mk0000664000000000000000000000072315063477206011667 0ustar &TARGETS += & base91s base91.o &OBJECTS += & base91.o cli.o &CFILES += & base91.c base91.h cli.c &CLEAN += $(&CFILES) &base91.c: &^base91.c.patch $(&CFILES): &/%: &~/base91-c/% &/Dir.mk perl -pe <$< >$@.tmp \ 'next if m{^\#include}; s/basE91/base91s/g; s/base91\b/base91s/g' patch <$(or $(filter %.patch,$^),/dev/null) $@.tmp mv -f $@.tmp $@ $(&OBJECTS): &base91.h &:local+global &LDFLAGS &LDLIBS &base91s: $(&OBJECTS) $(CC) -o$@ $(&LDFLAGS) $^ $(&LDLIBS) work/base91s/base91.c.patch0000664000000000000000000000173015063477206012540 0ustar diff --git a/base91.c b/base91.c index 3d9d7ea..7a5958a 100644 --- a/base91.c +++ b/base91.c @@ -38,12 +38,12 @@ const unsigned char enctab[91] = { 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '!', '#', '$', '%', '&', '(', ')', '*', '+', ',', '.', '/', ':', ';', '<', '=', - '>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '"' + '>', '?', '@', '[', ']', '^', '_', '`', '{', '|', '}', '~', '-' }; const unsigned char dectab[256] = { 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 62, 90, 63, 64, 65, 66, 91, 67, 68, 69, 70, 71, 91, 72, 73, + 91, 62, 91, 63, 64, 65, 66, 91, 67, 68, 69, 70, 71, 90, 72, 73, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 74, 75, 76, 77, 78, 79, 80, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 81, 91, 82, 83, 84, work/comm-common.c0000664000000000000000000000421315063477206011327 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include "secnet.h" #include "comm-common.h" struct comm_clientinfo *comm_clientinfo_ignore(void *state, dict_t *dict, struct cloc cloc) { return 0; } void comm_request_notify(void *commst, void *nst, comm_notify_fn *fn) { struct commcommon *st=commst; struct comm_notify_entry *n; NEW(n); n->fn=fn; n->state=nst; LIST_INSERT_HEAD(&st->notify, n, entry); } void comm_release_notify(void *commst, void *nst, comm_notify_fn *fn) { struct commcommon *st=commst; struct comm_notify_entry *n, *t; /* XXX untested */ LIST_FOREACH_SAFE(n, &st->notify, entry, t) { if (n->state==nst && n->fn==fn) { LIST_REMOVE(n, entry); free(n); } } } bool_t comm_notify(struct commcommon *cc, struct buffer_if *buf, const struct comm_addr *ca) { struct comm_notify_list *notify = &cc->notify; struct comm_notify_entry *n; priomsg_reset(&cc->why_unwanted); LIST_FOREACH(n, notify, entry) { if (n->fn(n->state, buf, ca, &cc->why_unwanted)) { return True; } } return False; } void comm_apply(struct commcommon *cc, void *st) { assert(cc==st); cc->cl.type=CL_COMM; cc->cl.apply=NULL; cc->cl.interface=&cc->ops; cc->ops.st=cc; cc->ops.request_notify=comm_request_notify; cc->ops.release_notify=comm_release_notify; LIST_INIT(&cc->notify); cc->rbuf=NULL; priomsg_new(&cc->why_unwanted, MAX_NAK_MSG); } work/comm-common.h0000664000000000000000000001170315063477206011336 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #ifndef COMM_COMMON_H #define COMM_COMMON_H #include "secnet.h" #include "util.h" /*----- for all comms -----*/ struct comm_notify_entry { comm_notify_fn *fn; void *state; LIST_ENTRY(comm_notify_entry) entry; }; LIST_HEAD(comm_notify_list, comm_notify_entry); struct commcommon { /* must be first so that void* is comm_common* */ closure_t cl; struct comm_if ops; struct cloc loc; struct comm_notify_list notify; struct buffer_if *rbuf; struct priomsg why_unwanted; }; struct comm_clientinfo *comm_clientinfo_ignore(void *state, dict_t*, struct cloc cloc); void comm_request_notify(void *commst, void *nst, comm_notify_fn *fn); void comm_release_notify(void *commst, void *nst, comm_notify_fn *fn); bool_t comm_notify(struct commcommon*, struct buffer_if *buf, const struct comm_addr *ca); /* Either: returns True, with message delivered and buffer freed. * Or: False, if no-one wanted it - buffer still allocd'd; * in that case, cc->why_unwanted has info * Ie, roughly like comm_notify_fn. */ void comm_apply(struct commcommon *cc, void *st); #define COMM_APPLY(st,cc,prefix,desc,loc) \ NEW(st); \ (cc)->loc=loc; \ (cc)->cl.description=desc; \ (cc)->ops.clientinfo=comm_clientinfo_ignore; \ (cc)->ops.sendmsg=prefix##sendmsg; \ (cc)->ops.addr_to_string=prefix##addr_to_string; \ comm_apply((cc),(st)) /* void COMM_APPLY(SOMETHING *st, struct commcommon *FUNCTIONOF(st), * prefix, "DESC", struct cloc loc); * // Expects in scope: prefix##sendmsg, prefix##addr_to_string. */ #define COMM_APPLY_STANDARD(st,cc,desc,args) \ item_t *item=list_elem(args,0); \ if (!item || item->type!=t_dict) { \ cfgfatal((cc)->loc,desc,"first argument must be a dictionary\n"); \ } \ dict_t *d=item->data.dict; \ (cc)->rbuf=find_cl_if(d,"buffer",CL_BUFFER,True,desc,(cc)->loc) /* void COMM_APPLY_STANDARD(SOMETHING *st, struct commcommon *cc, * const char *desc, list_t *args); * // Declares: * // item_t *item = ; * // dict_t *dict = ; */ /*----- for udp-based comms -----*/ #define UDP_MAX_SOCKETS 3 /* 2 ought to do really */ #define MAX_AF MAX_RAW(AF_INET6,AF_INET) struct udpsock { union iaddr addr; int fd; bool_t experienced[/*0=recv,1=send*/2][MAX_AF+1][/*success?*/2]; }; struct udpsocks { int n_socks; struct udpsock socks[UDP_MAX_SOCKETS]; /* private for udp_socks_* */ struct udpcommon *uc; /* link to parent, for cfg, notify list, etc. */ struct poll_interest *interest; const char *desc; }; struct udpcommon { struct commcommon cc; int port; string_t authbind; bool_t use_proxy; union iaddr proxy; }; bool_t udp_make_socket(struct udpcommon *uc, struct udpsock *us, int failmsgclass); /* Caller should have filled in ->addr. Fills in us->fd, ->experienced; updates ->addr. Logs any errors with lg_[v]perror. */ bool_t udp_import_socket(struct udpcommon *uc, struct udpsock *us, int failmsgclass, int fd); /* Like udp_make_socket, but caller provides fd. fd is not closed on error */ void udp_destroy_socket(struct udpcommon *uc, struct udpsock *us); /* Idempotent. No errors are possible. */ const char *af_name(int af); void udp_sock_experienced(struct log_if *lg, struct udpcommon *uc, struct udpsocks *socks, struct udpsock *us, const union iaddr *dest, int af /* 0 means any */, int r, int errnoval); void udp_socks_register(struct udpcommon *uc, struct udpsocks *socks, const char *desc); void udp_socks_deregister(struct udpcommon *uc, struct udpsocks *socks); void udp_socks_childpersist(struct udpcommon *uc, struct udpsocks *socks); #define UDP_APPLY_STANDARD(st,uc,desc) \ (uc)->use_proxy=False; \ (uc)->authbind=dict_read_string(d,"authbind",False,"udp",(uc)->cc.loc); \ (uc)->port=dict_read_number(d,"port",False,"udp",(uc)->cc.loc,0) /* void UDP_APPLY_STANDARD(SOMETHING *st, struct udpcommon *uc, * const char *desc); * // Expects in scope: dict_t *d=...; as from COMM_APPLY_STANDARD */ #endif /*COMM_COMMON_H*/ work/common.make.in0000664000000000000000000000236015063477206011477 0ustar # common makefile settings for secnet # # This file is part of secnet. # See README for full list of copyright holders. # # secnet 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. # # secnet 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 # version 3 along with secnet; if not, see # https://www.gnu.org/licenses/gpl.html. @SET_MAKE@ topbuilddir:=@abs_top_builddir@ src:=@top_srcdir@ SHELL:=/bin/sh CC:=@CC@ CFLAGS:=-Wall @WRITESTRINGS@ @CFLAGS@ -Werror \ -W -Wno-unused -Wno-unused-parameter \ -Wno-pointer-sign -Wstrict-prototypes -Wmissing-prototypes \ -Wmissing-declarations -Wnested-externs -Wredundant-decls \ -Wpointer-arith -Wformat=2 -Winit-self \ -Wswitch-enum -Wunused-variable -Wunused-function -Wbad-function-cast \ -Wno-strict-aliasing -fno-strict-aliasing \ -Wno-bool-operation -Wno-stringop-truncation work/comprehensive-test0000775000000000000000000000323615063477206012520 0ustar #!/bin/bash # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. set -e set -o pipefail oot_rel=oot-rel.tmp~ oot_abs=$(cd .. && pwd)/oot-comprehensive-test.tmp~ nproc=$(nproc || echo 1) mflags=-j$nproc for arg in "$@"; do case "$arg" in --oot-abs=*) oot_abs=${arg%*=} ;; *) echo >&2 "unknown arg/option $1"; exit 1;; esac done case "${OLD_SECNET_DIR-:?must be set, perhaps to the empty string}" in ''|/*) ;; ../*) OLD_SECNET_DIR="${PWD%/*}/${OLD_SECNET_DIR#../}" echo >&2 "x OLD_SECNET_DIR=$OLD_SECNET_DIR" ;; *) echo >&2 "relative non-.. OLD_SECNET_DIR $OLD_SECNET_DIR !"; exit 1 ;; esac x () { echo >&2 "x $*"; "$@"; } srcdir=$(pwd) build_and_test () { cd "$srcdir" x git clean -xdff if [ "x$1" != x. ]; then rm -rf "$1" mkdir "$1" fi x ./autogen.sh x cd "$1" x "$srcdir/configure" CFLAGS='-O0 -g' x make $mflags all check for t in mtest/check stest/check; do x make $mflags clean x make $mflags $t done x make $mflags clean if [ "x$1" != x. ]; then find -type f else git-ls-files -o fi | perl -ne ' s{^\./}{}; s{^}{/}; next if m{^/ct-files$}; next if m{^/autom4te\.cache/}; next if m{/Makefile$}; next if m{\.mk$}; next if m{^/common\.make$}; next if m{^/(?:config|\.makefiles)\.stamp$}; next if m{^/config\.(?:log|status|h)$}; warn "clean in '"$1"' missed $_"; $bad=1; END { exit $bad; } ' cd "$srcdir" } build_and_test . build_and_test "$oot_rel" build_and_test "$oot_abs" echo "----- $0 ok -----" work/conffile.c0000664000000000000000000004700515063477206010701 0ustar /* conffile.c - process the configuration file */ /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ /* #define DUMP_PARSE_TREE */ #include "secnet.h" #include #include #include #include #include "conffile.h" #include "conffile_internal.h" #include "conffile.yy.h" #include "util.h" #include "ipaddr.h" static struct cloc no_loc={"none",0}; struct atomlist { struct atomlist *next; atom_t a; }; struct entry { struct entry *next; atom_t key; list_t *val; }; struct searchlist { struct dict *d; struct searchlist *next; }; struct dict { struct dict *parent; struct searchlist *search; struct entry *entries; int32_t size; }; static struct atomlist *atoms=NULL; static void process_alist(dict_t *context, struct p_node *c); static list_t *process_invocation(dict_t *context, struct p_node *i); static list_t *dict_ilookup_primitive(dict_t *dict, atom_t key) { struct entry *i; for (i=dict->entries; i; i=i->next) { if (key==i->key) return i->val; } return NULL; } static list_t *dict_ilookup(dict_t *dict, atom_t key) { dict_t *d; list_t *v; v=dict_ilookup_primitive(dict, key); if (v) return v; /* Check dictionaries in search path */ /* XXX */ /* Check lexical parents */ for (d=dict; d; d=d->parent) { v=dict_ilookup_primitive(d, key); if (v) return v; } return NULL; } static void dict_iadd(dict_t *dict, atom_t key, list_t *val) { struct entry *e; if (dict_ilookup_primitive(dict, key)) { fatal("duplicate key \"%s\" in dictionary",key); } NEW(e); e->next=dict->entries; e->key=key; e->val=val; dict->entries=e; dict->size++; } /***** Functions beyond this point are private to the config system *****/ static dict_t *dict_new(dict_t *parent) { dict_t *d; NEW(d); d->parent=parent; d->search=NULL; d->entries=NULL; d->size=0; return d; } static struct p_node *node_copy(struct p_node *n) { struct p_node *r; NEW(r); *r=*n; return r; } static struct p_node *list_reverse(struct p_node *list) { struct p_node *rl=NULL, *i, *n; for (i=list; i; i=i->r) { n=node_copy(i); n->r=rl; rl=n; } return rl; } /* Since we use left-recursion in the parser for efficiency, sequences end up "backwards" in the parse tree. Rather than have complicated code for, eg. processing assignments in the right order, we reverse these sequences here. */ static void ptree_mangle(struct p_node *t) { if (!t) return; ptree_mangle(t->l); ptree_mangle(t->r); switch (t->type) { case T_DICT: ASSERT(!t->l || t->l->type==T_ALIST); ASSERT(!t->r || t->r->type==T_LISTITEM); t->l=list_reverse(t->l); t->r=list_reverse(t->r); break; case T_ASSIGNMENT: ASSERT(t->l->type==T_KEY); ASSERT(t->r->type==T_LISTITEM); t->r=list_reverse(t->r); break; case T_ABSPATH: case T_RELPATH: ASSERT(t->l==NULL); ASSERT(t->r->type==T_PATHELEM); t->r=list_reverse(t->r); break; case T_EXEC: ASSERT(t->l); ASSERT(t->r==NULL || t->r->type==T_LISTITEM); t->r=list_reverse(t->r); break; } } #ifdef DUMP_PARSE_TREE /* Convert a node type to a string, for parse tree dump */ static const char *ntype(uint32_t type) { switch(type) { case T_STRING: return "T_STRING"; case T_NUMBER: return "T_NUMBER"; case T_KEY: return "T_KEY"; case T_ASSIGNMENT: return "T_ASSIGNMENT"; case T_LISTITEM: return "T_LISTITEM"; case T_EXEC: return "T_EXEC"; case T_PATHELEM: return "T_PATHELEM"; case T_ABSPATH: return "T_ABSPATH"; case T_RELPATH: return "T_RELPATH"; case T_DICT: return "T_DICT"; case T_ALIST: return "T_ALIST"; case T_ERROR: return "T_ERROR"; } return "**unknown**"; } static void ptree_indent(int amount) { int i; for (i=0; itype)) { switch(n->type) { case T_STRING: printf("T_STRING: \"%s\" (%s line %d)\n", n->data.string,n->loc.file,n->loc.line); break; case T_NUMBER: printf("T_NUMBER: %d (%s line %d)\n", n->data.number, n->loc.file,n->loc.line); break; case T_KEY: printf("T_KEY: %s (%s line %d)\n", n->data.key, n->loc.file,n->loc.line); break; default: printf("**unknown primitive type**\n"); break; } } else { assert(d<10000); printf("%s: (%s line %d)\n",ntype(n->type),n->loc.file,n->loc.line); ptree_indent(d); printf(" |-"); ptree_dump(n->l, d+1); ptree_indent(d); printf(" +-"); ptree_dump(n->r, d+1); } } #endif /* DUMP_PARSE_TREE */ static dict_t *dict_find_root(dict_t *d) { dict_t *i; for (i=d; i->parent; i=i->parent); return i; } static list_t *dict_lookup_path(dict_t *context, struct p_node *p) { dict_t *i; list_t *l; ASSERT(p->type==T_PATHELEM); ASSERT(p->l->type==T_KEY); l=dict_ilookup(context, p->l->data.key); if (!l) { cfgfatal(p->loc,"conffile","can't find key %s\n", p->l->data.key); } while (p->r) { if (l->item->type != t_dict) { cfgfatal(p->loc,"conffile","path element \"%s\" " "is not a dictionary\n",p->l->data.key); } i=l->item->data.dict; /* First thing in list */ p=p->r; l=dict_ilookup_primitive(i, p->l->data.key); if (!l) { cfgfatal(p->loc,"conffile","can't find key %s\n", p->l->data.key); } } return l; } static item_t *new_item(enum types type, struct cloc loc) { item_t *i; NEW(i); i->type=type; i->loc=loc; return i; } static list_t *process_item(dict_t *context, struct p_node *i) { item_t *item=NULL; switch (i->type) { case T_STRING: item=new_item(t_string, i->loc); item->data.string=i->data.string; /* XXX maybe strcpy */ break; case T_NUMBER: item=new_item(t_number, i->loc); item->data.number=i->data.number; break; case T_ABSPATH: context=dict_find_root(context); /* falls through */ case T_RELPATH: return dict_lookup_path(context, i->r); /* returns immediately */ break; case T_DICT: item=new_item(t_dict, i->loc); item->data.dict=dict_new(context); /* XXX dict_add_searchpath(context,process_ilist(context, i->r)); */ process_alist(item->data.dict, i->l); break; case T_EXEC: return process_invocation(context, i); /* returns immediately */ break; default: #ifdef DUMP_PARSE_TREE ptree_dump(i,0); fatal("process_item: invalid node type for a list item (%s)", ntype(i->type)); #else fatal("process_item: list item has invalid node type %d - recompile " "with DUMP_PARSE_TREE defined in conffile.c for more " "detailed debug output",i->type); #endif /* DUMP_PARSE_TREE */ break; } return list_append(NULL,item); } static list_t *process_ilist(dict_t *context, struct p_node *l) { struct p_node *i; list_t *r; ASSERT(!l || l->type==T_LISTITEM); r=list_new(); for (i=l; i; i=i->r) { r=list_append_list(r,process_item(context,i->l)); } return r; } static list_t *process_invocation(dict_t *context, struct p_node *i) { list_t *cll; item_t *cl; list_t *args; ASSERT(i->type==T_EXEC); ASSERT(i->r==NULL || i->r->type==T_LISTITEM); cll=process_item(context,i->l); cl=cll->item; if (cl->type != t_closure) { cfgfatal(i->l->loc,"conffile","only closures can be invoked\n"); } if (!cl->data.closure->apply) { cfgfatal(i->l->loc,"conffile","this closure cannot be invoked\n"); } args=process_ilist(context, i->r); return cl->data.closure->apply(cl->data.closure, i->loc, context, args); } static void process_alist(dict_t *context, struct p_node *c) { struct p_node *i; atom_t k; list_t *l; if (!c) return; /* NULL assignment lists are valid (empty dictionary) */ ASSERT(c->type==T_ALIST); if (c->type!=T_ALIST) { fatal("invalid node type in assignment list"); } for (i=c; i; i=i->r) { ASSERT(i->l && i->l->type==T_ASSIGNMENT); ASSERT(i->l->l->type==T_KEY); ASSERT(i->l->r->type==T_LISTITEM); k=i->l->l->data.key; l=process_ilist(context, i->l->r); dict_iadd(context, k, l); } } /* Take a list of items; turn any dictionaries in this list into lists */ static list_t *makelist(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { list_t *r=NULL, *i; struct entry *e; for (i=args; i; i=i->next) { if (i->item->type==t_dict) { /* Convert */ for (e=i->item->data.dict->entries; e; e=e->next) { r=list_append_list(r, e->val); } } else { r=list_append_list(r, list_append(NULL,i->item)); } } return r; } /* Take a list consisting of a closure and some other things. Apply the closure to the other things, and return the resulting list */ static list_t *map(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { list_t *r=NULL, *al; item_t *ci; closure_t *cl; list_t se; ci=list_elem(args,0); if (ci && ci->type==t_closure) { cl=ci->data.closure; if (!cl->apply) { cfgfatal(loc,"map","closure cannot be applied\n"); } for (al=args->next; al; al=al->next) { /* Construct a single-element list */ se.next=NULL; se.item=al->item; /* Invoke the closure, append its result to the output */ r=list_append_list(r,cl->apply(cl,loc,context,&se)); } } else { cfgfatal(loc,"map","you must supply a closure as the " "first argument\n"); } return r; } /* Read a file and turn it into a string */ static list_t *readfile(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { FILE *f; string_t filename; long length; item_t *r; r=list_elem(args,0); if (!r) { cfgfatal(loc,"readfile","you must supply a filename\n"); } if (r->type!=t_string) { cfgfatal(loc,"readfile","filename must be a string\n"); } filename=r->data.string; f=fopen(filename,"rb"); if (!f) { fatal_perror("readfile (%s:%d): cannot open file \"%s\"", loc.file,loc.line, filename); } if (fseek(f, 0, SEEK_END)!=0) { fatal_perror("readfile (%s:%d): fseek(SEEK_END)",loc.file,loc.line); } length=ftell(f); if (length<0) { fatal_perror("readfile (%s:%d): ftell()",loc.file,loc.line); } if (fseek(f, 0, SEEK_SET)!=0) { fatal_perror("readfile (%s:%d): fseek(SEEK_SET)",loc.file,loc.line); } r=new_item(t_string,loc); r->data.string=safe_malloc(length+1,"readfile"); if (fread(r->data.string,length,1,f)!=1) { (ferror(f) ? fatal_perror : fatal) ("readfile (%s:%d): fread: could not read all of file", loc.file,loc.line); } r->data.string[length]=0; if (fclose(f)!=0) { fatal_perror("readfile (%s:%d): fclose",loc.file,loc.line); } return list_append(NULL,r); } static dict_t *process_config(struct p_node *c) { dict_t *root; dict_t *context; item_t *i; list_t *false_; list_t *true_; root=dict_new(NULL); context=root; /* Predefined keys for boolean values */ /* "nowise" and "verily" have the advantage of being the same length, so they line up nicely... thanks VKC and SGT (who also point out that "mayhap" is a good "maybe" value as well) */ i=new_item(t_bool,no_loc); i->data.bool_=False; false_=list_append(NULL,i); i=new_item(t_bool,no_loc); i->data.bool_=True; true_=list_append(NULL,i); dict_add(root,"false",false_); dict_add(root,"False",false_); dict_add(root,"FALSE",false_); dict_add(root,"no",false_); dict_add(root,"No",false_); dict_add(root,"NO",false_); dict_add(root,"nowise",false_); dict_add(root,"Nowise",false_); dict_add(root,"NOWISE",false_); dict_add(root,"true",true_); dict_add(root,"True",true_); dict_add(root,"TRUE",true_); dict_add(root,"yes",true_); dict_add(root,"Yes",true_); dict_add(root,"YES",true_); dict_add(root,"verily",true_); dict_add(root,"Verily",true_); dict_add(root,"VERILY",true_); add_closure(root,"makelist",makelist); add_closure(root,"readfile",readfile); add_closure(root,"map",map); init_builtin_modules(root); process_alist(context, c); return root; } /***** Externally accessible functions */ atom_t intern(cstring_t s) { struct atomlist *i; for (i=atoms; i; i=i->next) { if (strcmp(i->a, s)==0) break; } if (!i) { /* Did't find it; create a new one */ NEW(i); i->a=safe_strdup(s,"intern: alloc string"); i->next=atoms; atoms=i; } return i->a; } list_t *dict_lookup(dict_t *dict, cstring_t key) { return dict_ilookup(dict, intern(key)); } list_t *dict_lookup_primitive(dict_t *dict, cstring_t key) { return dict_ilookup_primitive(dict, intern(key)); } void dict_add(dict_t *dict, cstring_t key, list_t *val) { dict_iadd(dict,intern(key),val); } cstring_t *dict_keys(dict_t *dict) { atom_t *r, *j; struct entry *i; NEW_ARY(r,dict->size+1); for (i=dict->entries, j=r; i; i=i->next, j++) { *j=i->key; } *j=NULL; return r; } /* List-related functions */ list_t *list_new(void) { return NULL; } int32_t list_length(const list_t *a) { int32_t l=0; const list_t *i; for (i=a; i; i=i->next) { assert(l < INT_MAX); l++; } return l; } static list_t *list_copy(list_t *a) { list_t *r, *i, *b, *l; if (!a) return NULL; l=NULL; r=NULL; for (i=a; i; i=i->next) { NEW(b); if (l) l->next=b; else r=b; l=b; b->item=i->item; b->next=NULL; } return r; } list_t *list_append_list(list_t *a, list_t *b) { list_t *i; b=list_copy(b); if (!a) return b; for (i=a; i->next; i=i->next); i->next=b; return a; } list_t *list_append(list_t *list, item_t *item) { list_t *l; NEW(l); l->item=item; l->next=NULL; return list_append_list(list,l); } item_t *list_elem(list_t *l, int32_t index) { if (!l) return NULL; if (index==0) return l->item; return list_elem(l->next, index-1); } list_t *new_closure(closure_t *cl) { item_t *i; i=new_item(t_closure,no_loc); i->data.closure=cl; return list_append(NULL,i); } void add_closure(dict_t *dict, cstring_t name, apply_fn apply) { closure_t *c; NEW(c); c->description=name; c->type=CL_PURE; c->apply=apply; c->interface=NULL; dict_add(dict,name,new_closure(c)); } void *find_cl_if(dict_t *dict, cstring_t name, uint32_t type, bool_t required, cstring_t desc, struct cloc loc) { item_t *i; closure_t *cl; i = dict_find_item(dict,name,required,desc,loc); if (!i) return NULL; if (i->type!=t_closure) { cfgfatal(loc,desc,"\"%s\" must be a closure\n",name); } cl=i->data.closure; if (cl->type!=type) { cfgfatal_cl_type(loc,desc,cl,type,name); } return cl->interface; } /* Convenience functions for modules reading configuration dictionaries */ item_t *dict_find_item(dict_t *dict, cstring_t key, bool_t required, cstring_t desc, struct cloc loc) { list_t *l; item_t *i; l=dict_lookup(dict,key); if (!l) { if (!required) return NULL; cfgfatal(loc,desc,"required parameter \"%s\" not found\n",key); } if(list_length(l) != 1) cfgfatal(loc,desc,"parameter \"%s\" has wrong number of values",key); i=list_elem(l,0); return i; } string_t dict_read_string(dict_t *dict, cstring_t key, bool_t required, cstring_t desc, struct cloc loc) { item_t *i; string_t r; i=dict_find_item(dict,key,required,desc,loc); if (!i) return NULL; if (i->type!=t_string) { cfgfatal(loc,desc,"\"%s\" must be a string\n",key); } if (strlen(i->data.string) > INT_MAX/10) { cfgfatal(loc,desc,"\"%s\" is unreasonably long\n",key); } r=i->data.string; return r; } const char **dict_read_string_array(dict_t *dict, cstring_t key, bool_t required, cstring_t desc, struct cloc loc, const char *const *def) { list_t *l; const char **ra, **rap; l=dict_lookup(dict,key); if (!l) { if (!required) return (const char**)def; cfgfatal(loc,desc,"required string list \"%s\" not found\n",key); } int32_t ll=list_length(l); NEW_ARY(ra, ll+1); for (rap=ra; l; l=l->next,rap++) { item_t *it=l->item; if (it->type!=t_string) cfgfatal(it->loc,desc,"\"%s\" entry must be a string\n",key); *rap=it->data.string; } *rap=0; return ra; } uint32_t dict_read_number(dict_t *dict, cstring_t key, bool_t required, cstring_t desc, struct cloc loc, uint32_t def) { item_t *i; uint32_t r; i=dict_find_item(dict,key,required,desc,loc); if (!i) return def; if (i->type!=t_number) { cfgfatal(loc,desc,"\"%s\" must be a number\n",key); } if (i->data.number >= 0x80000000) { cfgfatal(loc,desc,"\"%s\" must fit into a 32-bit signed integer\n",key); } r=i->data.number; return r; } bool_t dict_read_bool(dict_t *dict, cstring_t key, bool_t required, cstring_t desc, struct cloc loc, bool_t def) { item_t *i; bool_t r; i=dict_find_item(dict,key,required,desc,loc); if (!i) return def; if (i->type!=t_bool) { cfgfatal(loc,desc,"\"%s\" must be a boolean\n",key); } r=i->data.bool_; return r; } dict_t *dict_read_dict(dict_t *dict, cstring_t key, bool_t required, cstring_t desc, struct cloc loc) { item_t *i; dict_t *r; i=dict_find_item(dict,key,required,desc,loc); if (!i) return NULL; if (i->type!=t_dict) { cfgfatal(loc,desc,"\"%s\" must be a dictionary\n",key); } r=i->data.dict; return r; } uint32_t string_to_word(cstring_t s, struct cloc loc, struct flagstr *f, cstring_t desc) { struct flagstr *j; for (j=f; j->name; j++) if (strcmp(s,j->name)==0) return j->value; cfgfatal(loc,desc,"option \"%s\" not known\n",s); return 0; } uint32_t string_list_to_word(list_t *l, struct flagstr *f, cstring_t desc) { list_t *i; uint32_t r=0; struct flagstr *j; for (i=l; i; i=i->next) { if (i->item->type!=t_string) { cfgfatal(i->item->loc,desc,"all elements of list must be " "strings\n"); } for (j=f; j->name; j++) r|=string_to_word(i->item->data.string,i->item->loc,f,desc); } return r; } dict_t *read_conffile(const char *name) { FILE *conffile; struct p_node *config; if (strcmp(name,"-")==0) { conffile=stdin; } else { conffile=fopen(name,"r"); if (!conffile) fatal_perror("Cannot open configuration file \"%s\"",name); } config_lineno=1; config_file=name; config=parse_conffile(conffile); fclose(conffile); #ifdef DUMP_PARSE_TREE printf("*** config file parse tree BEFORE MANGLE\n"); ptree_dump(config,0); #endif /* DUMP_PARSE_TREE */ /* The root of the configuration is a T_ALIST, which needs reversing before we mangle because it isn't the child of a T_DICT. */ config=list_reverse(config); ptree_mangle(config); #ifdef DUMP_PARSE_TREE printf("\n\n*** config file parse tree AFTER MANGLE\n"); ptree_dump(config,0); #endif /* DUMP_PARSE_TREE */ return process_config(config); } work/conffile.fl0000664000000000000000000001041315063477206011051 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ /* the "incl" state is used for picking up the name of an include file */ %x incl %option nounput %option noinput %option never-interactive %option noyywrap %{ #include #include #include #include #include #include "conffile_internal.h" #include "conffile.tab.h" #include "util.h" #define YY_NO_UNPUT #define YY_INPUT(buf,result,max_size) \ do{ \ (result)= fread((buf),1,(max_size),yyin); \ if (ferror(yyin)) \ fatal_perror("Error reading configuration file (%s)", \ config_file); \ }while(0) #define MAX_INCLUDE_DEPTH 10 struct include_stack_item { YY_BUFFER_STATE bst; int lineno; cstring_t file; }; struct include_stack_item include_stack[MAX_INCLUDE_DEPTH]; int include_stack_ptr=0; int config_lineno=0; cstring_t config_file="xxx"; static struct p_node *leafnode(uint32_t type) { struct p_node *r; NEW(r); r->type=type; r->loc.file=config_file; r->loc.line=config_lineno; r->l=NULL; r->r=NULL; return r; } static struct p_node *keynode(atom_t key) { struct p_node *r; r=leafnode(T_KEY); r->data.key=intern(key); return r; } static struct p_node *stringnode(string_t string) { struct p_node *r; r=leafnode(T_STRING); string++; string[strlen(string)-1]=0; r->data.string=safe_strdup(string,"stringnode"); return r; } static struct p_node *numnode(string_t number) { struct p_node *r; unsigned long n; r=leafnode(T_NUMBER); errno = 0; n = strtoul(number, NULL, 10); /* The caller is expected to only give us [0-9]+, * so we skip some of the usual syntax checking. */ r->data.number=n; /* Give a consistent error message for any kind of * out-of-range condition */ if(errno == ERANGE || n != r->data.number) { Message(M_FATAL,"config file %s line %d: '%s' is too big\n", config_file, config_lineno, number); exit(1); } if(errno) { Message(M_FATAL,"config file %s line %d: '%s': %s\n", config_file, config_lineno, number, strerror(errno)); exit(1); } return r; } %} %% include BEGIN(incl); [ \t]* /* eat the whitespace */ [^ \t\n]+ { /* got the include filename */ if (include_stack_ptr >= MAX_INCLUDE_DEPTH) { fatal("Configuration file includes nested too deeply"); } include_stack[include_stack_ptr].bst=YY_CURRENT_BUFFER; include_stack[include_stack_ptr].lineno=config_lineno; include_stack[include_stack_ptr].file=config_file; include_stack_ptr++; yyin=fopen(yytext,"r"); if (!yyin) { fatal("Can't open included file %s",yytext); } config_lineno=1; config_file=safe_strdup(yytext,"conffile.fl/include"); yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE)); BEGIN(INITIAL); } \n { /* include with no filename */ Message(M_FATAL,"config file %s line %d: %s\n",config_file, config_lineno,"``include'' requires a filename"); BEGIN(INITIAL); assert(config_lineno < INT_MAX); ++config_lineno; ++yynerrs; } <> { if (--include_stack_ptr < 0) { yyterminate(); } else { fclose(yyin); yy_delete_buffer(YY_CURRENT_BUFFER); yy_switch_to_buffer(include_stack[include_stack_ptr].bst); config_lineno=include_stack[include_stack_ptr].lineno; config_file=include_stack[include_stack_ptr].file; } } \"[^\"]*\" yylval=stringnode(yytext); return TOK_STRING; [[:alpha:]_][[:alnum:]\-_]* yylval=keynode(yytext); return TOK_KEY; [[:digit:]]+ yylval=numnode(yytext); return TOK_NUMBER; /* Eat comments */ \#.*\n config_lineno++; /* Count lines */ \n config_lineno++; /* Eat whitespace */ [[:blank:]\j] /* Return all unclaimed single characters to the parser */ . return *yytext; work/conffile.h0000664000000000000000000000155715063477206010710 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #ifndef conffile_h #define conffile_h #include "secnet.h" extern dict_t *read_conffile(const char *conffile); #endif /* conffile_h */ work/conffile.y0000664000000000000000000000512015063477206010717 0ustar /* * This file is part of secnet. * See LICENCE and this file CREDITS for full list of copyright holders. * SPDX-License-Identifier: GPL-3.0-or-later * There is NO WARRANTY. */ %token TOK_STRING %token TOK_NUMBER %token TOK_KEY %start input %{ #include #include #include /* Bison stupidly redeclares malloc/free unless they are #defined * (or a bunch of madder conditions) */ #ifndef malloc # define malloc malloc # define free free #endif #include "secnet.h" #include "conffile_internal.h" #include "conffile.yy.h" #include "util.h" #define YYERROR_VERBOSE static struct p_node *node(uint32_t type, struct p_node *l, struct p_node *r); static struct p_node *result; static void yyerror(const char *s); %} %% input: assignments { result = $1; $$=result; } ; assignments: assignments assignment { $$=node(T_ALIST, $2, $1); } | assignment { $$=node(T_ALIST, $1, NULL); } ; searchpath: /* empty */ { $$ = NULL; } | '<' list '>' { $$ = $2; } ; dict: searchpath '{' assignments '}' { $$ = node(T_DICT, $3, $1); } | searchpath '{' '}' { $$ = node(T_DICT, NULL, $1); } ; path: '/' pathelements { $$ = node(T_ABSPATH, NULL, $2); } | pathelements { $$ = node(T_RELPATH, NULL, $1); } ; pathelements: pathelements '/' TOK_KEY { $$ = node(T_PATHELEM, $3, $1); } | TOK_KEY { $$ = node(T_PATHELEM, $1, NULL); } ; exec: item '(' list ')' { $$ = node(T_EXEC, $1, $3); } | item '(' ')' { $$ = node(T_EXEC, $1, NULL); } | item dict { $$ = node(T_EXEC, $1, node(T_LISTITEM, $2, NULL)); } ; list: list ',' item { $$ = node(T_LISTITEM, $3, $1); } | item { $$ = node(T_LISTITEM, $1, NULL); } ; assignment: TOK_KEY '=' list ';' { $$ = node(T_ASSIGNMENT, $1, $3); } | TOK_KEY list ';' { $$ = node(T_ASSIGNMENT, $1, $2); } | error ';' { $$ = node(T_ERROR, NULL, NULL); } | error '}' { $$ = node(T_ERROR, NULL, NULL); } | error ')' { $$ = node(T_ERROR, NULL, NULL); } ; item: TOK_STRING | TOK_NUMBER | path | dict | exec ; %% static void yyerror(const char *s) { Message(M_FATAL,"config file %s line %d: %s\n",config_file, config_lineno,s); } struct p_node *parse_conffile(FILE *conffile) { yyin=conffile; if (yyparse()!=0) { fatal("Configuration file parsing failed\n"); } if (yynerrs>0) { fatal("%d error%s encountered in configuration file\n", yynerrs,yynerrs==1?"":"s"); } return result; } static struct p_node *node(uint32_t type, struct p_node *l, struct p_node *r) { struct p_node *rv; NEW(rv); rv->type=type; rv->loc.file=config_file; rv->loc.line=config_lineno; rv->l=l; rv->r=r; return rv; } work/conffile_internal.h0000664000000000000000000000336615063477206012604 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #ifndef conffile_internal_h #define conffile_internal_h #include #include "secnet.h" typedef cstring_t atom_t; /* Parse tree for configuration file */ #define YYSTYPE struct p_node * #define T_STRING 1 #define T_NUMBER 2 #define T_KEY 3 #define T_ASSIGNMENT 10 #define T_LISTITEM 11 #define T_EXEC 12 #define T_PATHELEM 13 #define T_ABSPATH 14 #define T_RELPATH 15 #define T_DICT 16 #define T_ALIST 17 #define T_ERROR 20 #define T_IS_PRIMITIVE(NTYPE) ((NTYPE) < T_ASSIGNMENT) struct p_node { uint32_t type; struct cloc loc; union { atom_t key; string_t string; uint32_t number; } data; struct p_node *l; struct p_node *r; }; extern cstring_t config_file; extern int config_lineno; extern int yynerrs; /* Keys in dictionaries are 'atoms', which are constructed from strings using this call. Atoms may be compared using '=='. */ extern atom_t intern(cstring_t string); extern struct p_node *parse_conffile(FILE *conffile); #endif /* conffile_internal_h */ work/config.h.in0000664000000000000000000000473415063477206010775 0ustar /* config.h.in. Generated from configure.ac by autoheader. */ #ifndef _CONFIG_H #define _CONFIG_H /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* Define to 1 to use IPv6 support in system and adns */ #undef CONFIG_IPV6 /* Define to 1 if you have the 'fmemopen' function. */ #undef HAVE_FMEMOPEN /* Define to 1 if you have the 'funopen' function. */ #undef HAVE_FUNOPEN /* Define to 1 if you have the 'adns' library (-ladns). */ #undef HAVE_LIBADNS /* Define to 1 if you have the 'gmp' library (-lgmp). */ #undef HAVE_LIBGMP /* Define to 1 if you have the 'gmp2' library (-lgmp2). */ #undef HAVE_LIBGMP2 /* Define to 1 if you have the 'socket' library (-lsocket). */ #undef HAVE_LIBSOCKET /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_IF_TUN_H /* Define to 1 if you have the header file. */ #undef HAVE_NET_IF_H /* Define to 1 if you have the header file. */ #undef HAVE_NET_IF_TUN_H /* Define to 1 if you have the header file. */ #undef HAVE_NET_ROUTE_H /* Define to 1 if you have the header file. */ #undef HAVE_STROPTS_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKET_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKIO_H /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Use CLOCK_MONOTONIC and adns_if_monotonic */ #undef USE_MONOTONIC /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif /* -*- c -*- */ /* These used to be in config.h.bot, but are now in configure.in. */ #ifdef __GNUC__ #define NORETURN(_x) void _x __attribute__ ((noreturn)) #define FORMAT(_a,_b,_c) __attribute__ ((format (_a,_b,_c))) #else #define NORETURN(_x) _x #define FORMAT(_a,_b,_c) #endif #endif /* _CONFIG_H */ work/configure0000775000000000000000000047407515063477206010672 0ustar #! /bin/sh # From configure.ac Id: configure.in. # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for secnet 0.1.18+. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: secnet-discuss@chiark.greenend.org.uk about your $0: system, including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='secnet' PACKAGE_TARNAME='secnet' PACKAGE_VERSION='0.1.18+' PACKAGE_STRING='secnet 0.1.18+' PACKAGE_BUGREPORT='secnet-discuss@chiark.greenend.org.uk' PACKAGE_URL='' ac_unique_file="secnet.c" ac_subst_vars='LTLIBOBJS LIBOBJS WRITESTRINGS CPP INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC SET_MAKE FINK target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL _SUBDIRMK_MAKEFILES' ac_subst_files='' ac_user_opts=' enable_option_checking enable_hacky_parallel ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures secnet 0.1.18+ to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/secnet] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of secnet 0.1.18+:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-hacky-parallel parallelise slow cryptography (default is no) Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF secnet configure 0.1.18+ generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ( $as_echo "## ---------------------------------------------------- ## ## Report this to secnet-discuss@chiark.greenend.org.uk ## ## ---------------------------------------------------- ##" ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES # --------------------------------------------- # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. ac_fn_c_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 $as_echo_n "checking whether $as_decl_name is declared... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_decl cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by secnet $as_me 0.1.18+, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers config.h" ac_config_files="$ac_config_files main.mk:main.mk.tmp Dir.mk:Dir.mk.tmp Final.mk:Final.mk.tmp" _SUBDIRMK_MAKEFILES="$_SUBDIRMK_MAKEFILES subdirmk/regen.mk" ac_config_files="$ac_config_files subdirmk/regen.mk:subdirmk/regen.mk.in" _SUBDIRMK_MAKEFILES="$_SUBDIRMK_MAKEFILES subdirmk/usual.mk" ac_config_files="$ac_config_files subdirmk/usual.mk:subdirmk/usual.mk.in" subdirmk_subdirs="$subdirmk_subdirs 'test-example/'" ac_config_files="$ac_config_files test-example/Dir.mk:test-example/Dir.mk.tmp" subdirmk_subdirs="$subdirmk_subdirs 'mtest/'" ac_config_files="$ac_config_files mtest/Dir.mk:mtest/Dir.mk.tmp" subdirmk_subdirs="$subdirmk_subdirs 'stest/'" ac_config_files="$ac_config_files stest/Dir.mk:stest/Dir.mk.tmp" subdirmk_subdirs="$subdirmk_subdirs 'base91s/'" ac_config_files="$ac_config_files base91s/Dir.mk:base91s/Dir.mk.tmp" ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu # If fink is on the path then it is assumed we should use it. # Extract the first word of "fink", so it can be a program name with args. set dummy fink; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_FINK+:} false; then : $as_echo_n "(cached) " >&6 else case $FINK in [\\/]* | ?:[\\/]*) ac_cv_path_FINK="$FINK" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_FINK="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi FINK=$ac_cv_path_FINK if test -n "$FINK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $FINK" >&5 $as_echo "$FINK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$FINK" != x; then finkdir=`echo $FINK|sed 's,/[^/]*/[^/]*$,,'` CPPFLAGS="-I$finkdir/include ${CPPFLAGS}" LDFLAGS="-L$finkdir/lib ${LDFLAGS}" fi # This is quite unpleasant. It turns out that most header checking # macros call AC_INCLUDES_DEFAULT. By default AC_INCLUDES_DEFAULT # implies AC_HEADER_STDC and a bunch of conditional includes. But # these header checks are obsolete as the documentation for # AC_HEADER_STDC says. Instead, define AC_INCLUDES_DEFAULT ourselves. # The list of headers below is the list from `(autoconf) Default # Includes' (filtered by hand for the modern ones rather than the # fallbacks). We must include $1 because AC_INCLUDES_DEFAULT is # called with an argument giving the check-specific haders. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu for ac_header in net/if.h net/route.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" " # include # include # include # include # include # include # include # include " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in sys/socket.h do : ac_fn_c_check_header_mongrel "$LINENO" "sys/socket.h" "ac_cv_header_sys_socket_h" " # include # include # include # include # include # include # include # include " if test "x$ac_cv_header_sys_socket_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SYS_SOCKET_H 1 _ACEOF fi done for ac_header in linux/if_tun.h do : ac_fn_c_check_header_compile "$LINENO" "linux/if_tun.h" "ac_cv_header_linux_if_tun_h" " # include # include # include # include # include # include # include # include #if HAVE_SYS_SOCKET_H # include #endif " if test "x$ac_cv_header_linux_if_tun_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LINUX_IF_TUN_H 1 _ACEOF fi done for ac_header in stropts.h sys/sockio.h net/if_tun.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" " # include # include # include # include # include # include # include # include " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 $as_echo_n "checking whether byte ordering is bigendian... " >&6; } if ${ac_cv_c_bigendian+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_c_bigendian=unknown # See if we're dealing with a universal compiler. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __APPLE_CC__ not a universal capable compiler #endif typedef int dummy; _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # Check for potential -arch flags. It is not universal unless # there are at least two -arch flags with different values. ac_arch= ac_prev= for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do if test -n "$ac_prev"; then case $ac_word in i?86 | x86_64 | ppc | ppc64) if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then ac_arch=$ac_word else ac_cv_c_bigendian=universal break fi ;; esac ac_prev= elif test "x$ac_word" = "x-arch"; then ac_prev=arch fi done fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_c_bigendian = unknown; then # See if sys/param.h defines the BYTE_ORDER macro. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ && LITTLE_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { #if BYTE_ORDER != BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) bogus endian macros #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : # It does; now see whether it defined to _BIG_ENDIAN or not. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { #ifndef _BIG_ENDIAN not big endian #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_bigendian=yes else ac_cv_c_bigendian=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi if test $ac_cv_c_bigendian = unknown; then # Compile a test program. if test "$cross_compiling" = yes; then : # Try to guess by grepping values from an object file. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; int use_ascii (int i) { return ascii_mm[i] + ascii_ii[i]; } short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; short int ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; int use_ebcdic (int i) { return ebcdic_mm[i] + ebcdic_ii[i]; } extern int foo; int main () { return use_ascii (foo) == use_ebcdic (foo); ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then ac_cv_c_bigendian=yes fi if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then if test "$ac_cv_c_bigendian" = unknown; then ac_cv_c_bigendian=no else # finding both strings is unlikely to happen, but who knows? ac_cv_c_bigendian=unknown fi fi fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ # include # include # include # include # include # include # include # include int main () { /* Are we little or big endian? From Harbison&Steele. */ union { long int l; char c[sizeof (long int)]; } u; u.l = 1; return u.c[sizeof (long int) - 1] == 1; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_c_bigendian=no else ac_cv_c_bigendian=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 $as_echo "$ac_cv_c_bigendian" >&6; } case $ac_cv_c_bigendian in #( yes) $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h ;; #( no) ;; #( universal) $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h ;; #( *) as_fn_error $? "unknown endianness presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; esac hard= if test -z "$hard"; then msg="C to warn about writing to stringliterals" else msg="C to prohibit any write to stringliterals" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking $msg" >&5 $as_echo_n "checking $msg... " >&6; } if ${ac_cv_prog_cc_no_writeable_strings+:} false; then : $as_echo_n "(cached) " >&6 else cat > conftest.c < int main (void) { char test[16]; if (strcpy (test, "test")) return 0; return 1; } EOF if test "$GCC" = "yes"; then if test -z "$hard"; then ac_cv_prog_cc_no_writeable_strings="-Wwrite-strings" else ac_cv_prog_cc_no_writeable_strings="-fno-writable-strings -Wwrite-strings" fi if test -n "`${CC-cc} -c $ac_cv_prog_cc_no_writeable_strings conftest.c 2>&1`" ; then ac_cv_prog_cc_no_writeable_strings="suppressed: string.h" fi elif $CC -flags 2>&1 | grep "Xc.*strict ANSI C" > /dev/null 2>&1 && $CC -c -xstrconst conftest.c > /dev/null 2>&1 && test -f conftest.o then # strings go into readonly segment ac_cv_prog_cc_no_writeable_strings="-xstrconst" rm conftest.o if test -n "`${CC-cc} -c $ac_cv_prog_cc_no_writeable_strings conftest.c 2>&1`" ; then ac_cv_prog_cc_no_writeable_strings="suppressed: string.h" fi elif $CC > /dev/null 2>&1 && $CC -c +ESlit conftest.c > /dev/null 2>&1 && test -f conftest.o then # strings go into readonly segment ac_cv_prog_cc_no_writeable_strings="+ESlit" rm conftest.o if test -n "`${CC-cc} -c $ac_cv_prog_cc_no_writeable_strings conftest.c 2>&1`" ; then ac_cv_prog_cc_no_writeable_strings="suppressed: string.h" fi elif ! $CC > /dev/null 2>&1 && $CC -c -readonly_strings conftest.c > /dev/null 2>&1 && test -f conftest.o then # strings go into readonly segment ac_cv_prog_cc_no_writeable_strings="-readonly_strings" rm conftest.o if test -n "`${CC-cc} -c $ac_cv_prog_cc_no_writeable_strings conftest.c 2>&1`" ; then ac_cv_prog_cc_no_writeable_strings="suppressed: string.h" fi # -use_readonly_const is the default for IRIX C, # puts them into .rodata, but they are copied later. # need to be "-G0 -rdatashared" for strictmode but # I am not sure what effect that has really. fi rm -f conftest.* fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_no_writeable_strings" >&5 $as_echo "$ac_cv_prog_cc_no_writeable_strings" >&6; } if test -z "$WRITESTRINGS" ; then if test -n "$ac_cv_prog_cc_no_writeable_strings" ; then case "$ac_cv_prog_cc_no_writeable_strings" in suppressed*) WRITESTRINGS="" ;; # known but suppressed *) WRITESTRINGS="$ac_cv_prog_cc_no_writeable_strings" ;; esac fi fi # Check whether --enable-hacky-parallel was given. if test "${enable_hacky_parallel+set}" = set; then : enableval=$enable_hacky_parallel; case "$enableval" in n|0|no) ;; y|1|yes) CFLAGS="$CFLAGS -DHACKY_PARALLEL" ;; *) ;; esac fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mpz_init_set_str in -lgmp" >&5 $as_echo_n "checking for mpz_init_set_str in -lgmp... " >&6; } if ${ac_cv_lib_gmp_mpz_init_set_str+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lgmp $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char mpz_init_set_str (); int main () { return mpz_init_set_str (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_gmp_mpz_init_set_str=yes else ac_cv_lib_gmp_mpz_init_set_str=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gmp_mpz_init_set_str" >&5 $as_echo "$ac_cv_lib_gmp_mpz_init_set_str" >&6; } if test "x$ac_cv_lib_gmp_mpz_init_set_str" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBGMP 1 _ACEOF LIBS="-lgmp $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mpz_init_set_str in -lgmp2" >&5 $as_echo_n "checking for mpz_init_set_str in -lgmp2... " >&6; } if ${ac_cv_lib_gmp2_mpz_init_set_str+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lgmp2 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char mpz_init_set_str (); int main () { return mpz_init_set_str (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_gmp2_mpz_init_set_str=yes else ac_cv_lib_gmp2_mpz_init_set_str=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gmp2_mpz_init_set_str" >&5 $as_echo "$ac_cv_lib_gmp2_mpz_init_set_str" >&6; } if test "x$ac_cv_lib_gmp2_mpz_init_set_str" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBGMP2 1 _ACEOF LIBS="-lgmp2 $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __gmpz_init_set_str in -lgmp" >&5 $as_echo_n "checking for __gmpz_init_set_str in -lgmp... " >&6; } if ${ac_cv_lib_gmp___gmpz_init_set_str+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lgmp $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char __gmpz_init_set_str (); int main () { return __gmpz_init_set_str (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_gmp___gmpz_init_set_str=yes else ac_cv_lib_gmp___gmpz_init_set_str=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gmp___gmpz_init_set_str" >&5 $as_echo "$ac_cv_lib_gmp___gmpz_init_set_str" >&6; } if test "x$ac_cv_lib_gmp___gmpz_init_set_str" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBGMP 1 _ACEOF LIBS="-lgmp $LIBS" fi ac_fn_c_check_header_mongrel "$LINENO" "gmp.h" "ac_cv_header_gmp_h" " # include # include # include # include # include # include # include # include " if test "x$ac_cv_header_gmp_h" = xyes; then : else as_fn_error $? "gmp.h not found" "$LINENO" 5 fi ac_fn_c_check_func "$LINENO" "inet_ntoa" "ac_cv_func_inet_ntoa" if test "x$ac_cv_func_inet_ntoa" = xyes; then : else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inet_ntoa in -lnsl" >&5 $as_echo_n "checking for inet_ntoa in -lnsl... " >&6; } if ${ac_cv_lib_nsl_inet_ntoa+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lnsl $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char inet_ntoa (); int main () { return inet_ntoa (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_nsl_inet_ntoa=yes else ac_cv_lib_nsl_inet_ntoa=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_nsl_inet_ntoa" >&5 $as_echo "$ac_cv_lib_nsl_inet_ntoa" >&6; } if test "x$ac_cv_lib_nsl_inet_ntoa" = xyes; then : LIBS="-lnsl $LIBS"; { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: inet_ntoa is in libnsl, urgh. Must use -lnsl." >&5 $as_echo "$as_me: WARNING: inet_ntoa is in libnsl, urgh. Must use -lnsl." >&2;} else as_fn_error $? "cannot find library function inet_ntoa" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -lsocket" >&5 $as_echo_n "checking for socket in -lsocket... " >&6; } if ${ac_cv_lib_socket_socket+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lsocket $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char socket (); int main () { return socket (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_socket_socket=yes else ac_cv_lib_socket_socket=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_socket" >&5 $as_echo "$ac_cv_lib_socket_socket" >&6; } if test "x$ac_cv_lib_socket_socket" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBSOCKET 1 _ACEOF LIBS="-lsocket $LIBS" fi ac_fn_c_check_func "$LINENO" "inet_aton" "ac_cv_func_inet_aton" if test "x$ac_cv_func_inet_aton" = xyes; then : else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inet_aton in -lresolv" >&5 $as_echo_n "checking for inet_aton in -lresolv... " >&6; } if ${ac_cv_lib_resolv_inet_aton+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lresolv $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char inet_aton (); int main () { return inet_aton (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_resolv_inet_aton=yes else ac_cv_lib_resolv_inet_aton=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_resolv_inet_aton" >&5 $as_echo "$ac_cv_lib_resolv_inet_aton" >&6; } if test "x$ac_cv_lib_resolv_inet_aton" = xyes; then : LIBS="-lresolv $LIBS"; { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: inet_aton is in libresolv, urgh. Must use -lresolv." >&5 $as_echo "$as_me: WARNING: inet_aton is in libresolv, urgh. Must use -lresolv." >&2;} else as_fn_error $? "cannot find library function inet_aton" "$LINENO" 5 fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for adns_init in -ladns" >&5 $as_echo_n "checking for adns_init in -ladns... " >&6; } if ${ac_cv_lib_adns_adns_init+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-ladns $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char adns_init (); int main () { return adns_init (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_adns_adns_init=yes else ac_cv_lib_adns_adns_init=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_adns_adns_init" >&5 $as_echo "$ac_cv_lib_adns_adns_init" >&6; } if test "x$ac_cv_lib_adns_adns_init" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBADNS 1 _ACEOF LIBS="-ladns $LIBS" fi ac_fn_c_check_header_mongrel "$LINENO" "adns.h" "ac_cv_header_adns_h" " # include # include # include # include # include # include # include # include " if test "x$ac_cv_header_adns_h" = xyes; then : else as_fn_error $? "adns.h not found" "$LINENO" 5 fi for ac_func in fmemopen funopen do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking required gcc -std flag" >&5 $as_echo_n "checking required gcc -std flag... " >&6; } if ${secnet_cv_gcc_std_flag+:} false; then : $as_echo_n "(cached) " >&6 else secnet_cv_gcc_std_flag="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ void x(void) { for (int i=0; i<1; i++) { } } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else old_cflags="$CFLAGS" CFLAGS="$CFLAGS -std=gnu11" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ void x(void) { for (int i=0; i<1; i++) { } } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : secnet_cv_gcc_std_flag=" -std=gnu11" else { $as_echo "$as_me:${as_lineno-$LINENO}: result: failure!" >&5 $as_echo "failure!" >&6; } as_fn_error 1 "cannot get test program to compile" "$LINENO" 5 fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$old_cflags" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $secnet_cv_gcc_std_flag" >&5 $as_echo "$secnet_cv_gcc_std_flag" >&6; } CFLAGS="$CFLAGS$secnet_cv_gcc_std_flag" { $as_echo "$as_me:${as_lineno-$LINENO}: Checking requirements for IPv6 support..." >&5 $as_echo "$as_me: Checking requirements for IPv6 support..." >&6;} enable_ipv6=true ac_fn_c_check_decl "$LINENO" "AF_INET6" "ac_cv_have_decl_AF_INET6" " # include # include # include # include # include # include # include # include #include " if test "x$ac_cv_have_decl_AF_INET6" = xyes; then : else enable_ipv6=false fi ac_fn_c_check_func "$LINENO" "adns_addr2text" "ac_cv_func_adns_addr2text" if test "x$ac_cv_func_adns_addr2text" = xyes; then : else enable_ipv6=false fi if $enable_ipv6; then { $as_echo "$as_me:${as_lineno-$LINENO}: Enabling IPv6 support" >&5 $as_echo "$as_me: Enabling IPv6 support" >&6;} $as_echo "#define CONFIG_IPV6 1" >>confdefs.h else { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Disabling IPv6 support" >&5 $as_echo "$as_me: WARNING: Disabling IPv6 support" >&2;} fi ac_fn_c_check_decl "$LINENO" "adns_if_monotonic" "ac_cv_have_decl_adns_if_monotonic" " # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include # include #include " if test "x$ac_cv_have_decl_adns_if_monotonic" = xyes; then : $as_echo "#define USE_MONOTONIC 1" >>confdefs.h else : fi _SUBDIRMK_MAKEFILES="$_SUBDIRMK_MAKEFILES common.make" ac_config_files="$ac_config_files common.make:common.make.in" ac_config_commands="$ac_config_commands default" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by secnet $as_me 0.1.18+, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Configuration commands: $config_commands Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ secnet config.status 0.1.18+ configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # '$srcdir'/subdirmk/generate --srcdir='$srcdir' $subdirmk_subdirs _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; "main.mk") CONFIG_FILES="$CONFIG_FILES main.mk:main.mk.tmp" ;; "Dir.mk") CONFIG_FILES="$CONFIG_FILES Dir.mk:Dir.mk.tmp" ;; "Final.mk") CONFIG_FILES="$CONFIG_FILES Final.mk:Final.mk.tmp" ;; "subdirmk/regen.mk") CONFIG_FILES="$CONFIG_FILES subdirmk/regen.mk:subdirmk/regen.mk.in" ;; "subdirmk/usual.mk") CONFIG_FILES="$CONFIG_FILES subdirmk/usual.mk:subdirmk/usual.mk.in" ;; "test-example/Dir.mk") CONFIG_FILES="$CONFIG_FILES test-example/Dir.mk:test-example/Dir.mk.tmp" ;; "mtest/Dir.mk") CONFIG_FILES="$CONFIG_FILES mtest/Dir.mk:mtest/Dir.mk.tmp" ;; "stest/Dir.mk") CONFIG_FILES="$CONFIG_FILES stest/Dir.mk:stest/Dir.mk.tmp" ;; "base91s/Dir.mk") CONFIG_FILES="$CONFIG_FILES base91s/Dir.mk:base91s/Dir.mk.tmp" ;; "common.make") CONFIG_FILES="$CONFIG_FILES common.make:common.make.in" ;; "default") CONFIG_COMMANDS="$CONFIG_COMMANDS default" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 $as_echo "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "default":C) echo timestamp >config.stamp ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi work/configure.ac0000664000000000000000000001243615063477206011236 0ustar dnl Process this file with autoconf to produce a configure script. dnl This file is part of secnet. dnl See README for full list of copyright holders. dnl dnl secnet is free software; you can redistribute it and/or modify it dnl under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 3 of the License, or dnl (at your option) any later version. dnl dnl secnet is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl version 3 along with secnet; if not, see dnl https://www.gnu.org/licenses/gpl.html. sinclude(ac_prog_cc_no_writeable_strings.m4) m4_include(subdirmk/subdirmk.ac) AC_INIT(secnet,0.1.18+,secnet-discuss@chiark.greenend.org.uk) AC_CONFIG_SRCDIR(secnet.c) AC_CONFIG_HEADER(config.h) SUBDIRMK_SUBDIRS([test-example mtest stest base91s]) AC_PREREQ(2.50) AC_REVISION($Id: configure.in,v 1.4 2002/09/09 22:05:02 steve Exp $) AC_LANG_C # If fink is on the path then it is assumed we should use it. AC_PATH_PROG([FINK],[fink]) if test "x$FINK" != x; then finkdir=`echo $FINK|sed 's,/[[^/]]*/[[^/]]*$,,'` CPPFLAGS="-I$finkdir/include ${CPPFLAGS}" LDFLAGS="-L$finkdir/lib ${LDFLAGS}" fi # This is quite unpleasant. It turns out that most header checking # macros call AC_INCLUDES_DEFAULT. By default AC_INCLUDES_DEFAULT # implies AC_HEADER_STDC and a bunch of conditional includes. But # these header checks are obsolete as the documentation for # AC_HEADER_STDC says. Instead, define AC_INCLUDES_DEFAULT ourselves. # The list of headers below is the list from `(autoconf) Default # Includes' (filtered by hand for the modern ones rather than the # fallbacks). We must include $1 because AC_INCLUDES_DEFAULT is # called with an argument giving the check-specific haders. m4_define([AC_INCLUDES_DEFAULT],[ # include # include # include # include # include # include # include # include $1 ]) AC_PROG_MAKE_SET AC_PROG_CC AC_PROG_INSTALL AC_CHECK_HEADERS([net/if.h net/route.h]) AC_CHECK_HEADERS([sys/socket.h]) AC_CHECK_HEADERS([linux/if_tun.h], [], [], [#if HAVE_SYS_SOCKET_H # include #endif ]) AC_CHECK_HEADERS([stropts.h sys/sockio.h net/if_tun.h]) AC_C_BIGENDIAN AC_PROG_CC_NO_WRITEABLE_STRINGS(WRITESTRINGS) AC_ARG_ENABLE(hacky-parallel, [AS_HELP_STRING([--enable-hacky-parallel], [parallelise slow cryptography (default is no)])], [ case "$enableval" in n|0|no) ;; y|1|yes) CFLAGS="$CFLAGS -DHACKY_PARALLEL" ;; *) ;; esac ]) AC_DEFUN([REQUIRE_HEADER],[AC_CHECK_HEADER($1,,[AC_MSG_ERROR($1 not found)])]) dnl the order in which libraries is checked is important dnl eg. adns on Solaris 2.5.1 depends on -lnsl and -lsocket AC_CHECK_LIB(gmp,mpz_init_set_str) AC_CHECK_LIB(gmp2,mpz_init_set_str) AC_CHECK_LIB(gmp,__gmpz_init_set_str) REQUIRE_HEADER([gmp.h]) dnl Would love to barf if no gmp was found, but how to test? Requiring the header will do for now. SECNET_C_GETFUNC(inet_ntoa,nsl) AC_CHECK_LIB(socket,socket) SECNET_C_GETFUNC(inet_aton,resolv) AC_CHECK_LIB(adns,adns_init) REQUIRE_HEADER([adns.h]) AC_CHECK_FUNCS([fmemopen funopen]) dnl gcc 4.9.2 (jessie) requires -std=gnu11 to cope with for (int i=... dnl but we do not want to pass that everywhere because we don't want dnl to nail down the C dialect this way. Why oh why oh why. m4_define([for_gcc_std],[ void x(void) { for (int i=0; i<1; i++) { } } ]) AC_CACHE_CHECK([required gcc -std flag],[secnet_cv_gcc_std_flag],[ secnet_cv_gcc_std_flag="" AC_COMPILE_IFELSE([AC_LANG_SOURCE(for_gcc_std)],[],[ old_cflags="$CFLAGS" CFLAGS="$CFLAGS -std=gnu11" AC_COMPILE_IFELSE([AC_LANG_SOURCE(for_gcc_std)],[ secnet_cv_gcc_std_flag=" -std=gnu11" ],[ AC_MSG_RESULT([failure!]) AC_MSG_ERROR([cannot get test program to compile],1) ]) CFLAGS="$old_cflags" ]) ]) CFLAGS="$CFLAGS$secnet_cv_gcc_std_flag" AC_MSG_NOTICE([Checking requirements for IPv6 support...]) enable_ipv6=true m4_define(NO_IPV6,[enable_ipv6=false]) AC_CHECK_DECL(AF_INET6, [],[NO_IPV6],[#include ]) AC_CHECK_FUNC(adns_addr2text, [],[NO_IPV6]) if $enable_ipv6; then AC_MSG_NOTICE([Enabling IPv6 support]) AC_DEFINE(CONFIG_IPV6, 1, [Define to 1 to use IPv6 support in system and adns]) else AC_MSG_WARN([Disabling IPv6 support]) fi AC_CHECK_DECL([adns_if_monotonic],[ AC_DEFINE([USE_MONOTONIC],[1],[Use CLOCK_MONOTONIC and adns_if_monotonic]) ],[ : dnl AC_MSG_WARN([Using non-monotonic clock, due to lack of adns_if_monotonic]) ],[AC_INCLUDES_DEFAULT #include ]) SUBDIRMK_MAKEFILES(common.make) AC_OUTPUT(, echo timestamp >config.stamp) AH_TOP([ #ifndef _CONFIG_H #define _CONFIG_H ]) AH_BOTTOM([ /* -*- c -*- */ /* These used to be in config.h.bot, but are now in configure.in. */ #ifdef __GNUC__ #define NORETURN(_x) void _x __attribute__ ((noreturn)) #define FORMAT(_a,_b,_c) __attribute__ ((format (_a,_b,_c))) #else #define NORETURN(_x) _x #define FORMAT(_a,_b,_c) #endif #endif /* _CONFIG_H */ ]) work/debian/0000775000000000000000000000000015063477206010164 5ustar work/debian/.gitignore0000664000000000000000000000007115063477206012152 0ustar autoreconf.after autoreconf.before debhelper-build-stamp work/debian/README.source0000664000000000000000000000214515063477206012345 0ustar The source code for this package is maintained in git. The primary upstream branch is here: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git/secnet.git/ Upstream releases are announced here: https://www.chiark.greenend.org.uk/mailman/listinfo/sgo-software-announce There is no distinction between the upstream and Debian branches at this time. We don't maintain a Debian delta (patches), nor Debian revision numbers. Some items are included using git-subtree, notably subdirmk: This designed to be "git-subtree"'d into one's package. That is the way the upstream package uses it. It would be possible to make it a separate package and build-depend on it, at the cost of some additional work. The upside would be a very small amount of disk space saving, and largely theoretical saving of work in case of a need to do a security update for subdirmk (which seems unlikely to be critical since it's build system software which is designed to execute its input) - and that all only in the case where a second package in Debian uses subdirmk. We are deferring this work until subdirmk becomes more widely used. work/debian/changelog0000664000000000000000000006213215063477206012042 0ustar secnet (0.6.8) unstable; urgency=medium * Avoid using `bool`, `false` and `true` as identifiers. Fixes conflicts with modern C and GCC-15. Closes: #1097866. * Remove now-false caveat that hippotat isn't packaged. It is, now. [Report from Tomas Pospisek] Closes: #1066029. -- Ian Jackson Sat, 20 Sep 2025 11:14:30 +0100 secnet (0.6.7) unstable; urgency=medium * Upstream release of changes since 0.6.2. (Versions 0.6.5 and 0.6.6 were Debian-only; 0.6.3 and 0.6.4 were not properly released at all and should be disregarded.) Packaging bugfix: * Update VERSION in Makefile. -- Ian Jackson Mon, 09 Jan 2023 00:15:21 +0000 secnet (0.6.6) unstable; urgency=medium Bugfix: * polypath: Do not separately log xmit failures. Tests: * stest: Fix debugging output about spawning secnet Packaging: * Add missing build-dependency on netbase. Closes: #1028102. * Copyright notices and LICENCE (aka debian/copyright) fixes and improvements. [Requested by Debian ftpmaster] * Add Debian upload step to release checklist. -- Ian Jackson Sun, 08 Jan 2023 18:20:09 +0000 secnet (0.6.5) unstable; urgency=medium Copyright and source code notices: * Include notices and licences of all included elements in LICENCE, not just in the files themselvves. * Tidy up a handful of notices to the most recent facts. * Do not claim CC-BY-SA 4.0 is GPL3+ compatible - only GPL3. * Add a README.source. * Update my own copyright year. * Versions 0.6.3 and 0.6.4 burned due to failed uploads. (See dgit bug #944855.) -- Ian Jackson Sat, 12 Nov 2022 18:58:02 +0000 secnet (0.6.2) unstable; urgency=medium Bugfixes to code and build system: * site: promote creation of st->scratch, allowing new pubkey scheme to work even when secnet is restarting. * comm-common.h: Remove unused `notify' variable. [Colin Watson] * Dir.sd.mk: Prevent builtin make rule overwriting conffile.c. Documentation: * configure: Correct bug reporting address. Report from Colin Watson. * example.conf improvements: - Comment out some tuning overrides - Improve syntax by deleting one of the two map examples - Add a commented-out mobile site setting - Add mtu-target * OLD-NEWS, OLD-BUGS: Rename from NEWS and BUGS, and don't ship OLD-BUGS. Debian packaging changes: * debian/rules: Use dh sequencer. Resulting changes: - We now provide debug symbols in the new Debian way. - The way the prerm stops secnet accords with modern practice. * init script: source /lib/lsb/init-functions. * init script: make "restart" work if secnet is not running. * Expand Description. * debhelper compat: bump to 12 (stretch-backports). * debian/rules: Use dh_prep instead of deprecated dh_clean -k. * Adjust Priority (as per modern policy and lintian). * Add some lintian overrides. * Add missing Pre-Depends on init-system-helpers. Credits and legal documentation improvements: * Add missing credits for base91 and python argparse extension. ensure some packaging credits exist in all the places they should be. * Add copyright/credits notices to many files which were missing them. * Move and centralise legal information into its own file, LICENCE. Testing improvements: * comprehensive-test: Actually allow OLD_SECNET_DIR set to '' * test-example/common.conf: Fix a reference to the sites file. * test-example/null-fake-userv: New test utility script. -- Ian Jackson Thu, 30 Dec 2021 15:10:12 +0000 secnet (0.6.1) unstable; urgency=medium Logging bugfixes: * Fix completely broken startup logging. Config errors would not be repoorted other than via the exit status! (Broken in 0.6.0.) * Suppress various uninteresting messages during startup, to avoid noise during successful startup. * Move "starting" message earlier. make-secnet-sites bugfixes: * Fix error handling if caller is in wrong group. * Fix regressions in handling of delegated sites file fragments (especially wrt the optional group parameter to `location'). Broken since security fixes in 0.5.0. Fixes to example config file: * Use new name-prefixed format for map(site...) runes. Old runes were desupported by make-secnet-sites security fix (in 0.5.0). * Fix "transform" syntax (broken since 0.3.0). Other bugfixes and improvements: * rsa: Do not crash if private key file is not accessible when running --just-check-config. (Broken since 0.6.0.) * rsa: Print errno value if we fail to open key file. * config parsing: When closure is of wrong type, give details. * Use CLOCK_MONOTONIC for all our timing needs (but only when built against an as-yet-unpublished adns feature). Supporting changes: * Tests for some of the changes. * Minor internal refactorings and improvements. * Merge subdirmk 1.0. -- Ian Jackson Sun, 24 May 2020 22:14:26 +0100 secnet (0.6.0) unstable; urgency=medium Bugfixes: * mobile sites: Do not ever expire peer addresses. In practice this fixes transitions between IPv6-only and IPv4-only networks. * make-secnet-sites: Tainted: Fix a lot of bad return values (which would result in assertions rather than nice error messages). * Fix hash algo confusion in mixed sha1/md5 configurations (see below). Incompatible changes: * site: Always advertise all capabilities, even in MSG1. This is incompatible with secnets earlier than 0.3.0 (September 2013), which are all quite badly broken and should have been upgraded long ago. * Drop support for using the same loaded rsa key with multiple different hash algorithms (which was broken in 0.5.0). Right now we support only `sha1' and `md5' so everyone should be using `sha1'. Installations which specified `md5' anywhere may need config changes. Major new featureset (use of which is not adviseable yet): * New facilities for negotiating about the signing keys to use for authentication during key setup, and selecting and using the appropriate keys. (``key-cache'/`privcache' and `peer-keys'). Using these new facilities for keyrollover now is in principle possible but rather complex. Further machinery is planned; for now, retain your existing config which should keep working. In summary: - secnet: new `privcache' closure; - secnet: `key-cache' and `peer-keys' keys on site closures; - secnet: new file format for peer public keysets; - secnet: new `make-public' config operator; - make-secnet-sites `pub', `pkg', `serial', etc. keywords; - make-secnet-sites --filter, --pubkeys-*, --output-version. More minor (logging) improvements: * Make stderr line buffered and log to it by default. * Do not log differently with --nodetach. * New `prefix' option to `logfile' closure. * Tidy and simplify some messages. Supporting changes: * Many substantial internal refactorings in secnet. * Many substantial internal refactorings in make-secnet-sites. * make-secnet-sites option parsing totally replaced. * Imported subtrees for base91-c and base91-python. * New portablity code, etc.: osdep.[ch], fmemopen reimplementation. * Explicitly define oddly-rotated dh padding arrangement (write_mpbin). Build system and packaging: * Do not fail to build from git when HEAD refers to a packed ref. * Update to subdirmk 0.3. * Many makefile fixes (esp. to clean and cdeps). * configure.ac: Drop or suppress some very obsolete checks. * autogen.sh: Write a comment about need for autoheader. * dir-locals: Provide python-indent-offset too. Test suite bugfixes: * stest: Use stderr, not tty, for logging. * stest/udp-preload.c: Fix error handling of sun_prep. * stest: Fix breakage if nproc is not installed. Test suite improvements: * New tests, including tests for new features. * Existing tests (especially stest) generally made more thorough. * New comprehensive-test and pretest-to-tested convenience scripts. * Arrangements for testing with (user-provided) old secnet. * parallel-test.*: scripts to help with parallelised bisection. * stest: Print a lot more output about what we are doing. * stest: Better support for cwd with longish pathname. * stest: More flexibility, env var hooks, etc. -- Ian Jackson Sun, 16 Feb 2020 12:48:13 +0000 secnet (0.5.1) unstable; urgency=medium POTENTIALLY INCOMPATIBLE CHANGE. Some security implications. * make-secnet-sites: Prefix names when writing secnet sites.conf file. make-secnet-sites must copy names (vpn, location and site names) from the input sites file (which is not wholly trusted) to the secnet config file. Prior to this release, naming a location or site the same as a secnet predefined name could generate a broken sites.conf which secnet would reject. (With the existing featureset, malfunctions other than rejection, eg privilege escalation, are not possible.) make-secnet-sites now adds a prefix to these names when writing sites.conf. This will not affect configurations which use the make-secnet-sites-provided `all-sites' key, as is usual. Other configurations will break unless the references in the static part of the config are adjusted. Previous behaviour can be restored with the --no-conf-key-prefix option. (Planned future enhancements to secnet are likely to make use of that option, with untrusted input, dangerously insecure.) other changes to make-secnet-sites: * Fix argument parsing. Fixes a regression affecting -P in 0.5.0, and also fixes new facilities introduced in 0.5.0. * Sort the properties on output (and adjust the test case expected outputs). Tests now pass on (at least) Python 2.7.13, 3.5.3, 3.7.5. * Delete some unused code. secnet: * Change one idiom to avoid a warning from GCC9. No functional change. build system - MAJOR CHANGES: * Fix out-of-tree builds. (Broken in 0.5.0) * Replace recursive make with use of the new subdirmk system. This represents a fairly comprehensive overhaul of the makefiles. Several bugs (esp. involving dependencies between files in different directories) are fixed. * Drop `make check' from `make all'. (Otherwise there is no way to ask for `all' without `check'.) * Suppress two unhelpful new compiler warnings from GCC9. * Release checklist update. documentation: * Credit Mark Wooding properly in CREDITS. * Include DEVELOPER-CERTIFICATE. tests: * Locations now have different names to sites. * Somewhat better debugging output from mtest. * Do not run msgcode-test except with `make fullcheck'. * Other minor bugfixes and improvments. * stest: Suppress unhelpful -Wno-unused-result (needed for stretch). -- Ian Jackson Fri, 22 Nov 2019 23:13:14 +0000 secnet (0.5.0) unstable; urgency=medium make-secnet-sites SECURITY FIX: * Do not blindly trust inputs; instead, check the syntax for sanity. Previous releases can be induced to run arbitrary code as the user invoking secnet (which might be root), if a secnet sites.conf is used that was generated from an untrustworthy sites file. * The userv invocation mode of make-secnet-sites seems to have been safe in itself, but it previously allowed hazardous data to be propagated into the master sites file. This is now prevented too. make-secnet-sites overhaul work: * make-secnet-sites is now in the common subset of Python2 and Python3. The #! is python3 now, but it works with Python2.7 too. It will probably *not* work with old versions of Python2. * We no longer depend on the obsolete `ipaddr' library. We use `ipaddress' now. And this is onlo a Recommends in the .deb. * Ad-hoc argument parser been replaced with `argparse'. There should be no change to existing working invocations. * Bad address syntax error does not wrongly mention IPv6 scopes. * Minor refactoring to support forthcoming work. [Mark Wooding] other bugfixes, improvements and changes to secnet itself: * Better logging of why we are sending NAK messages. * Correctly use the verified copy of the peer remote capabilities from MSG3. (Bug is not a vulnerability.) [Mark Wooding] * Significant internal rearrangements and refactorings, to support forthcoming key management work. [Mark Wooding and Ian Jackson] build system etc.: * Completely overhaul release checklist; drop dist target. * Remove dependency on `libfl.a'. [Mark Wooding] * polypath.c: Fix missing include of . [Mark Wooding] * Add a Wireshark dissector `secnet-wireshark.lua'. It is not installed anywhere right now. [Mark Wooding] documentation: * Improve documentation of capability negotiation in NOTES, secnet(8) and magic.h. [Mark Wooding] -- Ian Jackson Thu, 24 Oct 2019 19:11:54 +0100 secnet (0.4.5) unstable; urgency=medium * INSTALL: Mention that rsa key generation might need ssh-keygen1. * mobile: Fix negotiation bug with mixed old/new secnets and simultaneous key setup attempts by each end. [Mark Wooding] * Makefile.in: Support installation from a `VPATH' build. [Mark Wooding] * Portability fixes for clang. [Mark Wooding] -- Ian Jackson Sat, 21 Sep 2019 12:04:31 +0100 secnet (0.4.4) unstable; urgency=medium Security fix: * make-secnet-sites: Don't allow setting new VPN-level properties when restricted. This could allow denial of service by users with delegated authorisation. [Mark Wooding] Bugfixes for poor network environments: * polypath: cope properly with asymmetric routing, by correcting the handling of late duplicated packets etc. Protocol is now incompatible with secnet prior to 0.3.0 when either end is mobile. * Randomise key setup retry time. Other bugfixes: * rsa and cbcmac: Fix configuration error messages. [Mark Wooding] * Handle IPv4 addresses properly (ie, not foolishly byte-swapped), when IPv6 is not available. [Mark Wooding] * Better logging (and less foolish debug), especially about whether key is set up, and about crossed key setup attempts. * Internal refactoring and fixes. [Ian Jackson and Mark Wooding] Build system and portability: * configure: rerun autogen.sh with autoconf 2.69-10 * Avoid memset(0,0,0) wrt st->sharedsecret. (Fixes compiler warning; in theory might cause miscompilation.) [Mark Wooding] Documentation: * README.make-secnet-sites: new documentation file. [Mark Wooding] * NOTES: Describe current allocation of capability bits. [Mark Wooding] * NOTES: tiny fix tot protocol description. * secnet(8): Delete wrong information about dh groups. [Mark Wooding] Administrivia: * Fix erroneous GPL3+ licence notices "version d or later" (!) * .dir-locals.el: Settings for Python code. [Mark Wooding] -- Ian Jackson Sun, 08 Sep 2019 22:53:14 +0100 secnet (0.4.3) unstable; urgency=low Security improvement: * Use `mpz_powm_sec' for modexps. Enhancements: * Implement comm-info and dedicated-interface-addr feature, for benefit of hippotat. * Implement `keepalive' site option, to try to keep link always up. Build etc. fixes: * #include (fixes the build on jessie). * Tolerate building from a git checkout, but with git not installed. (This can happen in chroots.) * Turn off -Wsign-compare for bison output. * Makefile.in: Fix `check-ipaddrset' rule to get reference from $(srcdir). (Makes out-of-tree builds work properly.) * Release checklist fixes. * Burn version numbers 0.4.1 and 0.4.2 due to errors in release prep. Bugfixes: * When printing messages about dropping IPv6, do not print anything about ihl. (Check the IP version field first!) * When turning on debug, turn on verbose too. -- Ian Jackson Sat, 25 Nov 2017 13:36:41 +0000 secnet (0.4.0) unstable; urgency=low Debugging improvements: * Packet-level debugging from site notes errors from transmit. * Report when transport peers updated as a result of transmit. -- Ian Jackson Sat, 28 Feb 2015 15:03:00 +0000 secnet (0.4.0~beta2) unstable; urgency=low Polypath bugfixes: * Ignore IPv6 Unique Local unicast addresses. * Skip "tentative" IPv6 local addresses. * Improve logging and debug output. Portability fix: * Build where size_t is not compatible with int. Build system and packaging fixes: * Makefile: support DESTDIR. * debian/rules: set DESTDIR (not prefix). * debian/rules: Support dpkg-buildflags. * Install ipaddrset.py and secnet.8 with correct permissions. * Fix check for and git rid of our copy. * Use -lresolv only if inet_aton is not found otherwise. * Use -lnsl only if inet_ntoa is not found otherwise. * debian/rules: Provide build-arch and build-indep targets. * debian/rules: Do not run build for *-indep (!) * Makefile.in: Putative dual (backport and not) release build process doc. Copyright updates: * Update to GPLv3. Add missing copyright notices and credits. * Get rid of old FSF street address; use URL instead. * Remove obsolete LICENCE.txt (which was for snprintf reimplementation). * Remove obsolete references to Cendio (for old ipaddr.py). -- Ian Jackson Sun, 28 Dec 2014 17:14:10 +0000 secnet (0.4.0~beta1) unstable; urgency=low New features: * Support transport over IPv6. (We do not yet carry IPv6 in the private network.) IPv6 support depends on IPv6-capable adns (adns 1.5.x). * New polypath comm, which can duplicate packets so as to send them via multiple routes over the public network, for increased reliability/performance (but increased cost). Currently Linux-only but should be fairly easy to port. * Support multiple public addresses for peers. * Discard previously-received packets (by default). Logging improvements: * Report (each first) transmission and reception success and failure. * Log reason for DNS reolution failure. * Log unexpected kinds of death from userv. * Log authbind exit status as errno value (if appropriate). Configuration adjustments: * Adjust default number of mobile peer addresses to store when a peer public address is also configured. * Make specifying peer public port optional. This avoids making special arrangements to bind to a port for in mobile sites with no public stable address. Bugfixes: * Hackypar children will die if they get a terminating signal. * Fix signal dispositions inherited by secnet's child processes. * Fix off-by-one error which prevented setting transport-peers-max to 5. Test, build and internal improvements: * Use conventional IP address handling library ipaddr.py. * Provide a fuzzer for the slip decoder. * Build system improvements. * Many source code cleanups. -- Ian Jackson Sun, 26 Oct 2014 15:28:31 +0000 secnet (0.3.4) unstable; urgency=low SECURITY FIX: * The previous security fix to buffer handling was entirely wrong. This one is better. Thanks to Simon Tatham for the report and the patch. -- Ian Jackson Mon, 22 Sep 2014 16:16:11 +0100 secnet (0.3.3) unstable; urgency=high SECURITY FIXES: * Pass correct size argument to recvfrom. This is a serious security problem which may be exploitable from outside the VPN. * Fix a memory leak in some error logging. Other related fixes: * Two other latent bugs in buffer length handling found and fixed. * Non-critical stylistic improvements to buffer length handling, to make the code clearer and to assist audit. -- Ian Jackson Fri, 19 Sep 2014 23:50:45 +0100 secnet (0.3.3~beta1) unstable; urgency=low Installation compatibility fix: * In make-secnet-sites, always use our own ipaddr.py even if the incompatible modern ipaddr.py is installed (eg via python-ipaddr.deb). (Future versions of secnet are going to need that Python module to be installed.) For links involving mobile sites: * Use source of NAK packets as hint for peer transport address. * When initiating rekey, make use of data transport peer addresses. Build fix: * Provide clean target in test-example/Makefile. -- Ian Jackson Fri, 19 Sep 2014 00:11:44 +0100 secnet (0.3.2) unstable; urgency=low * Release of 0.3.2. No code changes since 0.3.1~beta1. -- Ian Jackson Thu, 26 Jun 2014 20:27:58 +0100 secnet (0.3.2~beta1) unstable; urgency=low For links involving mobile sites: * SECURITY: Properly update peer address array when it is full. * Do name-resolution on peer-initiated key setup too, when we are mobile (and other name-resolution improvements). Other minor improvements: * Log peer addresses on key exchange timeout. * When printing version (eg during startup), use value from git-describe and thus include git commit id where applicable. * Updates to release checklist in Makefile.in. * Use C99 _Bool for bool_t. -- Ian Jackson Fri, 06 Jun 2014 01:17:54 +0100 secnet (0.3.1) unstable; urgency=low * Release of 0.3.1. No code changes since 0.3.1~beta3. -- Ian Jackson Thu, 15 May 2014 01:08:30 +0100 secnet (0.3.1~beta3) unstable; urgency=low * Build fixes for non-i386 architectures and gcc 4.8.2. -- Ian Jackson Thu, 08 May 2014 19:53:43 +0100 secnet (0.3.1~beta2) unstable; urgency=low Fix relating to new fragmentation / ICMP functionality: * Generate ICMP packets correctly in point-to-point configurations. -- Ian Jackson Sat, 03 May 2014 18:58:09 +0100 secnet (0.3.1~beta1) unstable; urgency=low Security fixes (vulnerabilities are to inside attackers only): * SECURITY: Fixes to MTU and fragmentation handling. * SECURITY: Correctly set "unused" ICMP header field. * SECURITY: Fix IP length check not to crash on very short packets. New feature: * Make the inter-site MTU configurable, and negotiate it with the peer. Bugfixes etc.: * Fix netlink SEGV on clientless netlinks (i.e. configuration error). * Fix formatting error in p-t-p startup message. * Do not send ICMP errors in response to unknown incoming ICMP. * Fix formatting error in secnet.8 manpage. * Internal code rearrangements and improvements. Packaging improvements: * Updates to release checklist in Makefile.in. * Additions to the test-example suite. -- Ian Jackson Thu, 01 May 2014 19:02:56 +0100 secnet (0.3.0) unstable; urgency=low * Release of 0.3.0. No code changes since 0.3.0~beta3. * Update release checklist. -- Ian Jackson Sun, 01 Sep 2013 20:27:48 +0100 secnet (0.3.0~beta3) unstable; urgency=low * New upstream version. - Stability bugfix: properly initialise site's scratch buffer. -- Ian Jackson Mon, 05 Aug 2013 11:54:09 +0100 secnet (0.3.0~beta2) unstable; urgency=low * New upstream version. - SECURITY FIX: RSA public modulus and exponent buffer overflow. - SECURITY FIX: Use constant-time memcmp for message authentication. - SECURITY FIX: Provide a new transform, eax-serpent, to replace cbcmac. - SECURITY FIX: No longer send NAKs for NAKs, avoiding NAK storm. - SECURITY FIX: Fix site name checking when site name A is prefix of B. - SECURITY FIX: Safely reject too-short IP packets. - Better robustness for mobile sites (proper user of NAKs, new PROD msg). - Better robustness against SLIP decoding errors. - Fix bugs which caused routes to sometimes not be advertised. - Protocol capability negotiation mechanism. - Improvements and fixes to protocol and usage documentation. - Other bugfixes and code tidying up. -- Ian Jackson Thu, 25 Jul 2013 18:26:01 +0100 secnet (0.3.0~beta1) unstable; urgency=low * New upstream version. - SECURITY FIX: avoid crashes (or buffer overrun) on short packets. - Bugfixes relating to packet loss during key exchange. - Bugfixes relating to link up/down status. - Bugfixes relating to logging. - make-secnet-sites made more sophisticated to support two vpns on chiark. - Documentation improvements. - Build system improvements. * Debian packaging improvements: - Native package. - Maintainer / uploaders. - init script requires $remove_fs since we're in /usr. -- Ian Jackson Thu, 12 Jul 2012 20:18:16 +0100 secnet (0.2.1-1) unstable; urgency=low * New upstream version. (authbind endianness fix) -- Ian Jackson Sun, 11 Dec 2011 13:14:57 +0000 secnet (0.2.0-1) unstable; urgency=low * New upstream version. -- Ian Jackson Sat, 10 Dec 2011 22:44:41 +0000 secnet (0.1.18-1) unstable; urgency=low * New upstream version. -- Stephen Early Tue, 18 Mar 2008 17:45:00 +0000 work/debian/compat0000664000000000000000000000000315063477206011363 0ustar 12 work/debian/control0000664000000000000000000000217315063477206011572 0ustar Source: secnet Section: net Priority: optional Maintainer: Ian Jackson Uploaders: Stephen Early , Richard Kettlewell Build-Depends: debhelper (>= 12), libgmp3-dev, libadns1-dev, bison, flex, libbsd-dev, python3, tclx, tcl, libtcl-chiark-1, netbase Standards-Version: 4.6.0 Package: secnet Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base Pre-Depends: ${misc:Pre-Depends} Recommends: python3 Description: VPN software for distributed networks secnet allows multiple private networks, each 'hidden' behind a single globally-routable IP address, to be bridged together. It also supports individual client systems which may connect to multiple upstreams. . Compared to many other systems, secnet has good handling of mobile endpoints (mobile client nodes), and extremely flexible configuration. However, it is not easy to configure. . secnet works well with userv-ipif (allowing it to run without needing root privilege) and hippotat. . secnet is the main VPN program used by greenend.org.uk. work/debian/copyright0000777000000000000000000000000015063477206013312 2../LICENCEustar work/debian/default0000664000000000000000000000001615063477206011530 0ustar RUN_SECNET=no work/debian/init0000664000000000000000000000407015063477206011053 0ustar #! /bin/sh # /etc/init.d/secnet # # This file is part of secnet. # See LICENCE and CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. ### BEGIN INIT INFO # Provides: secnet # Required-Start: $network $local_fs $remote_fs # Required-Stop: $network $local_fs $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start and stop secnet # Description: secnet is a VPN server. ### END INIT INFO set -e PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/usr/sbin/secnet NAME=secnet DESC="VPN server" test -f $DAEMON || exit 0 . /lib/lsb/init-functions test -f /etc/secnet/secnet.conf || exit 0 test -f /etc/default/secnet && . /etc/default/secnet [ "X$RUN_SECNET" = "Xyes" ] || exit 0 case "$1" in start) echo -n "Starting $DESC: " start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \ --exec $DAEMON echo "$NAME." ;; stop) echo -n "Stopping $DESC: " start-stop-daemon --stop --quiet --oknodo --pidfile \ /var/run/$NAME.pid --exec $DAEMON echo "$NAME." ;; #reload) # # If the daemon can reload its config files on the fly # for example by sending it SIGHUP, do it here. # # If the daemon responds to changes in its config file # directly anyway, make this a do-nothing entry. # # echo "Reloading $DESC configuration files." # start-stop-daemon --stop --signal 1 --quiet --pidfile \ # /var/run/$NAME.pid --exec $DAEMON #;; restart|force-reload) # # If the "reload" option is implemented, move the "force-reload" # option to the "reload" entry above. If not, "force-reload" is # just the same as "restart". # echo -n "Restarting $DESC: " start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \ --oknodo --exec $DAEMON sleep 1 start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \ --exec $DAEMON echo "$NAME." ;; *) N=/etc/init.d/$NAME # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2 echo "Usage: $N {start|stop|restart|force-reload}" >&2 exit 1 ;; esac exit 0 work/debian/lintian-overrides0000664000000000000000000000012215063477206013540 0ustar missing-debian-source-format package-contains-upstream-installation-documentation work/debian/rules0000775000000000000000000000263315063477206011250 0ustar #!/usr/bin/make -f # Sample debian/rules that uses debhelper. # This file is public domain software, originally written by Joey Hess. # Modified for secnet by Stephen Early # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # This file is Free Software. It has been incorporated into, and # extensively modified, for secnet. # # Copyright 2001 Joey Hess # Copyright 2011-2014 Ian Jackson # # You may redistribute this file (and the other source files in the # debian/ subdirectory) freely - the copyrightholders declare that # they wish these files to be in the public domain. # # You may redistribute secnet as a whole and/or modify it under the # terms of the GNU General Public License as published by the Free # Software Foundation; either version 3, or (at your option) any # later version. # # This software 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 software; if not, see # https://www.gnu.org/licenses/gpl.html. export EXTRA_CFLAGS= $(shell dpkg-buildflags --get CPPFLAGS) \ $(shell dpkg-buildflags --get CFLAGS) export EXTRA_LDFLAGS=$(shell dpkg-buildflags --get LDFLAGS) %: dh $@ work/debian/secnet.docs0000664000000000000000000000005315063477206012315 0ustar INSTALL README NOTES TODO OLD-NEWS CREDITS work/dh.c0000664000000000000000000001111615063477206007501 0ustar /* * dh.c */ /* * This file is Free Software. It was originally written for secnet. * * Copyright 1995-2003 Stephen Early * Copyright 2002-2014 Ian Jackson * * You may redistribute secnet as a whole and/or modify it under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 3, or (at your option) any * later version. * * You may redistribute this file and/or modify it under the terms of * the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later * version. * * This software 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 software; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include #include #include #include "secnet.h" #include "util.h" struct dh { closure_t cl; struct dh_if ops; struct cloc loc; MP_INT p,g; /* prime modulus and generator */ }; static string_t dh_makepublic(void *sst, uint8_t *secret, int32_t secretlen) { struct dh *st=sst; string_t r; MP_INT a, b; /* a is secret key, b is public key */ mpz_init(&a); mpz_init(&b); read_mpbin(&a, secret, secretlen); mpz_powm_sec(&b, &st->g, &a, &st->p); r=write_mpstring(&b); mpz_clear(&a); mpz_clear(&b); return r; } static void write_mpbin_anomalous(MP_INT *a, uint8_t *buffer, int32_t buflen) /* If the BN is smaller than buflen, pads it *at the wrong end* */ { char *hb = write_mpstring(a); int32_t len; hex_decode(buffer, buflen, &len, hb, True); if (lenp); write_mpbin_anomalous(&c,sharedsecret,buflen); mpz_clear(&a); mpz_clear(&b); mpz_clear(&c); } static list_t *dh_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct dh *st; string_t p,g; item_t *i; NEW(st); st->cl.description="dh"; st->cl.type=CL_DH; st->cl.apply=NULL; st->cl.interface=&st->ops; st->ops.st=st; st->ops.makepublic=dh_makepublic; st->ops.makeshared=dh_makeshared; st->loc=loc; /* We have two string arguments: the first is the modulus, and the second is the generator. Both are in hex. */ i=list_elem(args,0); if (i) { if (i->type!=t_string) { cfgfatal(i->loc,"diffie-hellman","first argument must be a " "string\n"); } p=i->data.string; if (mpz_init_set_str(&st->p,p,16)!=0) { cfgfatal(i->loc,"diffie-hellman","\"%s\" is not a hex number " "string\n",p); } } else { cfgfatal(loc,"diffie-hellman","you must provide a prime modulus\n"); } i=list_elem(args,1); if (i) { if (i->type!=t_string) { cfgfatal(i->loc,"diffie-hellman","second argument must be a " "string\n"); } g=i->data.string; if (mpz_init_set_str(&st->g,g,16)!=0) { cfgfatal(i->loc,"diffie-hellman","\"%s\" is not a hex number " "string\n",g); } } else { cfgfatal(loc,"diffie-hellman","you must provide a generator\n"); } i=list_elem(args,2); if (i && i->type==t_bool && i->data.bool_==False) { Message(M_INFO,"diffie-hellman (%s:%d): skipping modulus " "primality check\n",loc.file,loc.line); } else { /* Test that the modulus is really prime */ if (mpz_probab_prime_p(&st->p,5)==0) { cfgfatal(loc,"diffie-hellman","modulus must be a prime\n"); } } size_t sz=mpz_sizeinbase(&st->p,2)/8; if (sz>INT_MAX) { cfgfatal(loc,"diffie-hellman","modulus far too large\n"); } if (mpz_cmp(&st->g,&st->p) >= 0) { cfgfatal(loc,"diffie-hellman","generator must be less than modulus\n"); } st->ops.len=sz; st->ops.ceil_len=(mpz_sizeinbase(&st->p,2)+7)/8; /* According to the docs, mpz_sizeinbase(,256) is allowed to return * an answer which is 1 too large. But mpz_sizeinbase(,2) isn't. */ return new_closure(&st->cl); } void dh_module(dict_t *dict) { add_closure(dict,"diffie-hellman",dh_apply); } work/eax-aes-test.c0000664000000000000000000000312715063477206011411 0ustar /* * eax-aes-test.c: test harness glue for EAX-AES (EAX-Rijndael) */ /* * This file is Free Software. It was originally written for secnet. * * Copyright 2013 Ian Jackson * * You may redistribute secnet as a whole and/or modify it under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 3, or (at your option) any * later version. * * You may redistribute this file and/or modify it under the terms of * the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later * version. * * This software 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 software; if not, see * https://www.gnu.org/licenses/gpl.html. */ /* * The corresponding test vector file is eax-aes-test.vectors. It was * copied out of the AES (Rijndael) paper. I don't believe it is a * creative work that attracts copyright. -iwj. */ #include "eax-test.h" #include "aes.h" #define BLOCK_SIZE AES_BLOCK_SIZE static AES_KEY key; EAX_SOME_TEST; void eaxtest_blockcipher_key_setup(const uint8_t *keydata, uint8_t bytes) { AES_set_encrypt_key(keydata, bytes*8, &key); } static void BLOCK_ENCRYPT(uint8_t dst[BLOCK_SIZE], const uint8_t src[BLOCK_SIZE]) { AES_encrypt((const void*)src, (void*)dst, &key); } #include "eax.c" work/eax-aes-test.vectors0000664000000000000000000000370715063477206012660 0ustar MSG: KEY: 233952DEE4D5ED5F9B9C6D6FF80FF478 NONCE: 62EC67F9C3A4A407FCB2A8C49031A8B3 HEADER: 6BFB914FD07EAE6B CIPHER: E037830E8389F27B025A2D6527E79D01 MSG: F7FB KEY: 91945D3F4DCBEE0BF45EF52255F095A4 NONCE: BECAF043B0A23D843194BA972C66DEBD HEADER: FA3BFD4806EB53FA CIPHER: 19DD5C4C9331049D0BDAB0277408F67967E5 MSG: 1A47CB4933 KEY: 01F74AD64077F2E704C0F60ADA3DD523 NONCE: 70C3DB4F0D26368400A10ED05D2BFF5E HEADER: 234A3463C1264AC6 CIPHER: D851D5BAE03A59F238A23E39199DC9266626C40F80 MSG: 481C9E39B1 KEY: D07CF6CBB7F313BDDE66B727AFD3C5E8 NONCE: 8408DFFF3C1A2B1292DC199E46B7D617 HEADER: 33CCE2EABFF5A79D CIPHER: 632A9D131AD4C168A4225D8E1FF755939974A7BEDE MSG: 40D0C07DA5E4 KEY: 35B6D0580005BBC12B0587124557D2C2 NONCE: FDB6B06676EEDC5C61D74276E1F8E816 HEADER: AEB96EAEBE2970E9 CIPHER: 071DFE16C675CB0677E536F73AFE6A14B74EE49844DD MSG: 4DE3B35C3FC039245BD1FB7D KEY: BD8E6E11475E60B268784C38C62FEB22 NONCE: 6EAC5C93072D8E8513F750935E46DA1B HEADER: D4482D1CA78DCE0F CIPHER: 835BB4F15D743E350E728414ABB8644FD6CCB86947C5E10590210A4F MSG: 8B0A79306C9CE7ED99DAE4F87F8DD61636 KEY: 7C77D6E813BED5AC98BAA417477A2E7D NONCE: 1A8C98DCD73D38393B2BF1569DEEFC19 HEADER: 65D2017990D62528 CIPHER: 02083E3979DA014812F59F11D52630DA30137327D10649B0AA6E1C181DB617D7F2 MSG: 1BDA122BCE8A8DBAF1877D962B8592DD2D56 KEY: 5FFF20CAFAB119CA2FC73549E20F5B0D NONCE: DDE59B97D722156D4D9AFF2BC7559826 HEADER: 54B9F04E6A09189A CIPHER: 2EC47B2C4954A489AFC7BA4897EDCDAE8CC33B60450599BD02C96382902AEF7F832A MSG: 6CF36720872B8513F6EAB1A8A44438D5EF11 KEY: A4A4782BCFFD3EC5E7EF6D8C34A56123 NONCE: B781FCF2F75FA5A8DE97A9CA48E522EC HEADER: 899A175897561D7E CIPHER: 0DE18FD0FDD91E7AF19F1D8EE8733938B1E8E7F6D2231618102FDB7FE55FF1991700 MSG: CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7 KEY: 8395FCF1E95BEBD697BD010BC766AAC3 NONCE: 22E7ADD93CFC6393C57EC0B3C17D6B44 HEADER: 126735FCC320D25A CIPHER: CB8920F87A6C75CFF39627B56E3ED197C552D295A7CFC46AFC253B4652B1AF3795B124AB6E work/eax-serpent-test.c0000664000000000000000000000334415063477206012322 0ustar /* * eax-serpent-test.c: test harness glue for EAX-Serpent */ /* * This file is Free Software. It was originally written for secnet. * * Copyright 2013 Ian Jackson * * You may redistribute secnet as a whole and/or modify it under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 3, or (at your option) any * later version. * * You may redistribute this file and/or modify it under the terms of * the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later * version. * * This software 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 software; if not, see * https://www.gnu.org/licenses/gpl.html. */ /* * The corresponding test vector files are eax-serpent-test.vectors * and eax-serpentbe-test.vectors. eax-serpent-test.vectors was * provided by Mark Wooding and eax-serpentbe-test.vectors was * generated by this file (in its guise as eax-serpentbe-test). I * don't believe these test vecctors are creative works that attract * copyright. -iwj. */ #include "eax-test.h" #include "serpent.h" #define BLOCK_SIZE 16 static struct keyInstance key; EAX_SOME_TEST; void eaxtest_blockcipher_key_setup(const uint8_t *keydata, uint8_t bytes) { serpent_makekey(&key, bytes*8, keydata); } static void BLOCK_ENCRYPT(uint8_t dst[BLOCK_SIZE], const uint8_t src[BLOCK_SIZE]) { serpent_encrypt(&key, src, dst); } #include "eax.c" work/eax-serpent-test.vectors0000664000000000000000000000360515063477206013565 0ustar MSG: KEY: 233952DEE4D5ED5F9B9C6D6FF80FF478 NONCE: 62EC67F9C3A4A407FCB2A8C49031A8B3 HEADER: 6BFB914FD07EAE6B CIPHER: 1271EC1E68330EB461A96D3A3A7A2707 MSG: F7FB KEY: 91945D3F4DCBEE0BF45EF52255F095A4 NONCE: BECAF043B0A23D843194BA972C66DEBD HEADER: FA3BFD4806EB53FA CIPHER: 1C7367D3DB493A1F7B054ECECA2A2CF37EE6 MSG: 1A47CB4933 KEY: 01F74AD64077F2E704C0F60ADA3DD523 NONCE: 70C3DB4F0D26368400A10ED05D2BFF5E HEADER: 234A3463C1264AC6 CIPHER: 2439712B59B13982351BA05B25BB2BD3B95DF62D73 MSG: 481C9E39B1 KEY: D07CF6CBB7F313BDDE66B727AFD3C5E8 NONCE: 8408DFFF3C1A2B1292DC199E46B7D617 HEADER: 33CCE2EABFF5A79D CIPHER: F1D718884BE94B29E143A264B54E283CA9E439C90D MSG: 40D0C07DA5E4 KEY: 35B6D0580005BBC12B0587124557D2C2 NONCE: FDB6B06676EEDC5C61D74276E1F8E816 HEADER: AEB96EAEBE2970E9 CIPHER: 5936DB85DF31199BA3556A5D5EFF1964A6BEFEA0D950 MSG: 4DE3B35C3FC039245BD1FB7D KEY: BD8E6E11475E60B268784C38C62FEB22 NONCE: 6EAC5C93072D8E8513F750935E46DA1B HEADER: D4482D1CA78DCE0F CIPHER: 7A3A7997EE349B57152CC43F723903A85B09D86456315AC0D9180724 MSG: 8B0A79306C9CE7ED99DAE4F87F8DD61636 KEY: 7C77D6E813BED5AC98BAA417477A2E7D NONCE: 1A8C98DCD73D38393B2BF1569DEEFC19 HEADER: 65D2017990D62528 CIPHER: 73548FFAF45D2617EB25AD1DFFA18420836D48394D5EF2CD2E0E30CDD2F4C52D96 MSG: 1BDA122BCE8A8DBAF1877D962B8592DD2D56 KEY: 5FFF20CAFAB119CA2FC73549E20F5B0D NONCE: DDE59B97D722156D4D9AFF2BC7559826 HEADER: 54B9F04E6A09189A CIPHER: E8BD1C6FE47DF149A141CE813B0C1239542EC4CBF7B3968388D631E6F4FFE86E14E7 MSG: 6CF36720872B8513F6EAB1A8A44438D5EF11 KEY: A4A4782BCFFD3EC5E7EF6D8C34A56123 NONCE: B781FCF2F75FA5A8DE97A9CA48E522EC HEADER: 899A175897561D7E CIPHER: E4A9D72847D437B85F10B7DAA46F1E00E3509AF0B97961C39DFBB70170B6C4CADBC1 MSG: CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7 KEY: 8395FCF1E95BEBD697BD010BC766AAC3 NONCE: 22E7ADD93CFC6393C57EC0B3C17D6B44 HEADER: 126735FCC320D25A CIPHER: 83D69403EAE9386B679DAEAAD2951465F8DDF9BE1AFFAD1C5FEF072F8B48BD58C07FEE3D83 work/eax-serpentbe-test.c0000664000000000000000000000103715063477206012626 0ustar /* * This file is part of secnet. * See LICENCE and this file CREDITS for full list of copyright holders. * SPDX-License-Identifier: GPL-3.0-or-later * There is NO WARRANTY. */ #include "eax-test.h" #include "serpent.h" /* multiple-inclusion protection means that serpent.h's inclusion * by eax-serpent-test.c is suppressed, so we don't get useless * duplicate declarations of serpentbe_makekey and serpentbe_encrypt */ #define serpent_makekey serpentbe_makekey #define serpent_encrypt serpentbe_encrypt #include "eax-serpent-test.c" work/eax-serpentbe-test.vectors0000664000000000000000000000370715063477206014077 0ustar MSG: KEY: 233952DEE4D5ED5F9B9C6D6FF80FF478 NONCE: 62EC67F9C3A4A407FCB2A8C49031A8B3 HEADER: 6BFB914FD07EAE6B CIPHER: E667316E3FC40DF234575E203EA06EA0 MSG: F7FB KEY: 91945D3F4DCBEE0BF45EF52255F095A4 NONCE: BECAF043B0A23D843194BA972C66DEBD HEADER: FA3BFD4806EB53FA CIPHER: D6D0A45C2A76EEB6AD20C3DB5CE100A2AEC4 MSG: 1A47CB4933 KEY: 01F74AD64077F2E704C0F60ADA3DD523 NONCE: 70C3DB4F0D26368400A10ED05D2BFF5E HEADER: 234A3463C1264AC6 CIPHER: FD8218F8987B7CCEBE4D521F4374D40B2F85794B31 MSG: 481C9E39B1 KEY: D07CF6CBB7F313BDDE66B727AFD3C5E8 NONCE: 8408DFFF3C1A2B1292DC199E46B7D617 HEADER: 33CCE2EABFF5A79D CIPHER: 529951EDB28B9557667E88ED360EB51256DEC0F056 MSG: 40D0C07DA5E4 KEY: 35B6D0580005BBC12B0587124557D2C2 NONCE: FDB6B06676EEDC5C61D74276E1F8E816 HEADER: AEB96EAEBE2970E9 CIPHER: F46A8BFED3B22A6E4388659FF1C39B3D49AAD8ADEA74 MSG: 4DE3B35C3FC039245BD1FB7D KEY: BD8E6E11475E60B268784C38C62FEB22 NONCE: 6EAC5C93072D8E8513F750935E46DA1B HEADER: D4482D1CA78DCE0F CIPHER: C3C7281CE5790F14D4CD666E8494D4911D528548F200014C32B86719 MSG: 8B0A79306C9CE7ED99DAE4F87F8DD61636 KEY: 7C77D6E813BED5AC98BAA417477A2E7D NONCE: 1A8C98DCD73D38393B2BF1569DEEFC19 HEADER: 65D2017990D62528 CIPHER: 1DD9A93ACD19F0C3FB7A3B431DFCFB96D2B899FA2285AEC7DCA504AF75B97A58A3 MSG: 1BDA122BCE8A8DBAF1877D962B8592DD2D56 KEY: 5FFF20CAFAB119CA2FC73549E20F5B0D NONCE: DDE59B97D722156D4D9AFF2BC7559826 HEADER: 54B9F04E6A09189A CIPHER: 2BA4C477F2927210ADCA26DF72E2BEF81BF3D6B03160E7BFE7FD3EB57D255D66713F MSG: 6CF36720872B8513F6EAB1A8A44438D5EF11 KEY: A4A4782BCFFD3EC5E7EF6D8C34A56123 NONCE: B781FCF2F75FA5A8DE97A9CA48E522EC HEADER: 899A175897561D7E CIPHER: A17D854BA33FDBDF0BA86ADC9152D40B4EA01E1A8FAB1E0A80B19E73784219B29446 MSG: CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7 KEY: 8395FCF1E95BEBD697BD010BC766AAC3 NONCE: 22E7ADD93CFC6393C57EC0B3C17D6B44 HEADER: 126735FCC320D25A CIPHER: 9F30626B590A0A6E2F6CE0ED8835031654B3FCCF311BD7A6C6089AF1C7373D22CB80D4AFEE work/eax-test.c0000664000000000000000000001017015063477206010637 0ustar /* * eax-test.c: test harness for EAX, implementation */ /* * This file is Free Software. It was originally written for secnet. * * Copyright 2013 Ian Jackson * Copyright 2013 Mark Wooding * * You may redistribute secnet as a whole and/or modify it under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 3, or (at your option) any * later version. * * You may redistribute this file and/or modify it under the terms of * the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later * version. * * This software 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 software; if not, see * https://www.gnu.org/licenses/gpl.html. */ /* * usages: * ./eax-foo-test len; i++) { perturb->v[i] ^= delta; trydecrypt(0); perturb->v[i] ^= delta; } } static void something(void) { if (!msg.got) return; assert(key.got); assert(nonce.got); assert(header.got); eaxtest_blockcipher_key_setup(V(key)); eax_setup(-1); if (cipher.got) { assert(cipher.len > msg.len); tau = cipher.len - msg.len; assert(tau <= blocksize); } else { assert(msg.len + blocksize < sizeof(ourcipher.v)); tau = blocksize; } ourcipher.len = msg.len + tau; eax_encrypt(-1, V(nonce), V(header), V(msg), tau, ourcipher.v); if (cipher.got) { assert(ourcipher.len == cipher.len); assert(!memcmp(ourcipher.v, cipher.v, cipher.len)); trydecrypt(1); negtest(&ourcipher); negtest(&header); } else { size_t i; printf("CIPHER: "); for (i=0; igot = 1; cv->len = 0; for (;;) { c = getputchar(); if (c == ':') break; assert(isalpha(c)); } for (;;) { char hbuf[3], *ep; c = getputchar(); if (c == '\n') break; if (isspace(c)) continue; assert(isprint(c)); hbuf[0] = c; c = getputchar(); assert(isprint(c)); hbuf[1] = c; hbuf[2] = 0; assert(cv->len < sizeof(cv->v)); cv->v[cv->len++] = strtoul(hbuf,&ep,16); assert(!*ep); } } assert(!ferror(stdin)); assert(feof(stdin)); assert(!ferror(stdout)); assert(!fflush(stdout)); return 0; } work/eax-test.h0000664000000000000000000000347115063477206010652 0ustar /* * eax-test.c: test harness for EAX, common declarations */ /* * This file is Free Software. It was originally written for secnet. * See README for full list of copyright holders. * * Copyright 2013 Ian Jackson * Copyright 2013 Mark Wooding * * You may redistribute secnet as a whole and/or modify it under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 3, or (at your option) any * later version. * * You may redistribute this file and/or modify it under the terms of * the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later * version. * * This software 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 software; if not, see * https://www.gnu.org/licenses/gpl.html. */ #ifndef EAX_TEST_H #define EAX_TEST_H #include #include #include #include #include #include #include #define INFO int dummy_info #define I dummy_info #define EAX_ENTRYPOINT_DECL /* empty */ #define EAX_DECLARATIONS_ONLY #include "eax.c" #undef EAX_DECLARATIONS_ONLY void eaxtest_blockcipher_key_setup(const uint8_t *keydata, uint8_t bytes); #define consttime_memeq(s1,s2,sz) (!memcmp((s1),(s2),(sz))) /* fine for running test vectors */ extern const size_t blocksize; #define EAX_SOME_TEST \ const size_t blocksize = BLOCK_SIZE; \ static uint8_t INFO_B[BLOCK_SIZE], INFO_P[BLOCK_SIZE] #endif /* EAX_TEST_H */ work/eax.c0000664000000000000000000002726515063477206007677 0ustar /* * eax.c: implementation of the EAX authenticated encryption block cipher mode */ /* * This file is Free Software. It was originally written for secnet. * * Copyright 2013 Ian Jackson * Copyright 2013 Mark Wooding * * You may redistribute secnet as a whole and/or modify it under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 3, or (at your option) any * later version. * * You may redistribute this file and/or modify it under the terms of * the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later * version. * * This software 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 software; if not, see * https://www.gnu.org/licenses/gpl.html. */ /* * This file is designed to be #included into another .c file which * sets up the environment. It will declare or define three * functions, eax_setup, eax_encrypt and eax_decrypt. * * Manifest constants which are expected to be defined: * * INFO One or more formal parameter definitions. * Used in all relevant function declarations. Typically * the application will use this for its context pointer, * key schedule structure, etc. * * I Corresponding actual parameters for chaining onto * sub-functions declared to take INFO parameters * * EAX_ENTRYPOINT_DECL * Declarator decoration for the three entry points. * Typically either "static" or the empty string; * * EAX_DECLARATIONS_ONLY * Tested with #ifdef, so should be #defined to 1, or left * undefined. If defined, #including eax.c will produces * only the function prototypes for the three entrypoints. * Otherwise it will produce the implementation. * * BLOCK_SIZE * Constant expresion of integer type. * * void BLOCK_ENCRYPT(uint8_t dst[n], const uint8_t src[n]); * * Function to encrypt with the selected key. The block * cipher's key schedule (ie, the key) to be used is * implicit; uses of BLOCK_ENCRYPT always occur in a * context where the parameters defined by INFO are * available. * * So in a real application which wants to use more than * one key at a time, BLOCK_ENCRYPT must be a macro which * accesses the block cipher's key schedule via one of the * INFO parameters. * * BLOCK_ENCRYPT must tolerate dst==src. * * EAX does not need to use the block cipher's decryption * function. * * uint8_t INFO_B[n], INFO_P[n]; * * Buffers used by the algorithm; they are written by * eax_setup and used by eax_encrypt and eax_decrypt. * * That is, effectively they are the part of the key * schedule specific to EAX. * * An application which wants to use more than one key at * a time must define these as macros which refer to * key-specific variables via one of the INFO parameters. * * int consttime_memeq(const void *s1, const void *s2, size_t n); * * Like !memcmp(s1,s2,n) but takes the same amount of time * no matter where the discrepancy is. Result must be * a canonicalised boolean. * * The entrypoints which are then defined are: * * void eax_setup(INFO) * * Does the EAX-specific part of the key setup. The block * cipher key schedule must already have been done, as * eax_setup uses BLOCK_ENCRYPT. * * void eax_encrypt(INFO, const uint8_t nonce[nonce_len], size_t nonce_len, * const uint8_t h[h_len], size_t h_len, * const uint8_t m[m_len], size_t m_len, * uint8_t tau, * uint8_t ct[m_len+tau]) * * Does a single EAX authenticated encryption, providing * confidentiality and integrity to the message m[0..m_len-1]. * * The output message is longer than m by tau bytes and is stored * in the array ct which must be big enough. * * nonce must never be repeated with the same key or the security * of the system is destroyed, but it does not need to be secret. * It is OK to transmit the nonce with the message along with the * ciphertext and have the receiver recover it. * * h is the "header" - it is some extra data which should be * covered by the authentication, but not the encryption. The * output message does not contain a representation of h: it is * expected to be transmitted separately (perhaps even in a * different format). (If h_len==0, h may be a NULL pointer.) * * tau is the tag length - that is, the length of the message * authentication code. It should be chosen for the right * tradeoff between message expansion and security (resistence to * forgery). It must be no longer than the block cipher block * size. * * For any particular key. the tag length should be fixed. (The * tag length should NOT be encoded into the packet along with * the ciphertext and extracted by the receiver! Rather, the * receiver must know what tag length to expect.) * * It is permissible for ct==m, or for the arrays to be disjoint. * They must not overlap more subtly. * * _Bool eax_decrypt(INFO, const uint8_t nonce[nonce_len], size_t nonce_len, * const uint8_t h[h_len], size_t h_len, * const uint8_t ct[ct_len], size_t ct_len, * uint8_t tau, * uint8_t m[ct_len-tau]) * * Does a single EAX authenticated decryption. * * On successful return, the plaintext message is written to m * and eax_decrypt returns true. The length of the plaintext * message is always ct_len-tau. * * If the message did not decrypt correctly, returns false. * (There is no further indication of the nature of the error.) * In this case the buffer m may contain arbitrary contents which * should not be revealed to attackers. * * nonce, h, tau are as above. * * It is permissible to call eax_decrypt with an input message * which is too short (i.e. shorter than tau) (notwithstanding * the notation m[ct_len-tau] in the faux prototype above). * In this case it will return false without touching m. * * As with eax_decrypt, ct==m is permissible. */ /***** IMPLEMENTATION *****/ /* * We use the notation from the EAX paper, mostly. * (We write xscr for "x in fancy mathsy curly script".) * * See: * Mihir Bellare, Phillip Rogaway, and David Wagner * * _The EAX Mode of Operation * (A Two-Pass Authenticated Encryption Scheme * Optimized for Simplicity and Efficiency)_ * * Preliminary version in: * Fast Software Encryption (FSE) 2004. Lecture Notes in Computer Science, * vol. ??, pp. ??--??. * * Full version at: * http://www.cs.ucdavis.edu/~rogaway/papers/eax.html */ /* * In general, all functions tolerate their destination arrays being * the same pointer to their source arrays, or totally distinct. * (Just like BLOCK_ENCRYPT and the public eax entrypoints.) * They must not overlap in more subtle ways. */ #define n ((size_t)BLOCK_SIZE) #ifndef EAX_DECLARATIONS_ONLY static void xor_block(uint8_t *dst, const uint8_t *a, const uint8_t *b, size_t l) /* simple block xor */ { while (l--) *dst++ = *a++ ^ *b++; } static void increment(uint8_t *value) /* value is a single block; incremented (BE) mod 256^n */ { uint8_t *p; for (p=value+n; p>value; ) if ((*--p)++) break; } static void alg_ctr(INFO, uint8_t *c, const uint8_t *nscr, const uint8_t *m, size_t m_len) { uint8_t blocknonce[n], cipher[n]; size_t in; memcpy(blocknonce, nscr, n); for (in=0; in> 7) - 1u) & POLY; unsigned i, mm; for (i = n - 1; i < n; i--) { mm = (v[i] >> 7) & 1u; o[i] = (v[i] << 1) ^ m; m = mm; } #undef POLY } #endif /* not EAX_DECLARATIONS_ONLY */ EAX_ENTRYPOINT_DECL void eax_setup(INFO) #ifndef EAX_DECLARATIONS_ONLY { uint8_t work[n]; memset(work,0,n); BLOCK_ENCRYPT(work,work); consttime_curious_multiply(I, INFO_B, work); consttime_curious_multiply(I, INFO_P, INFO_B); } #endif /* not EAX_DECLARATIONS_ONLY */ ; EAX_ENTRYPOINT_DECL void eax_encrypt(INFO, const uint8_t *nonce, size_t nonce_len, const uint8_t *h, size_t h_len, const uint8_t *m, size_t m_len, uint8_t tau, uint8_t *ct) #ifndef EAX_DECLARATIONS_ONLY { assert(tau <= n); uint8_t nscr[n], hscr[n], cscr[n]; alg_omac_t_k(I, nscr, 0, nonce,nonce_len); alg_omac_t_k(I, hscr, 1, h,h_len); alg_ctr(I, ct, nscr, m, m_len); alg_omac_t_k(I, cscr, 2, ct, m_len); uint8_t *t = ct + m_len; xor_block(t, nscr, cscr, tau); xor_block(t, t, hscr, tau); } #endif /* not EAX_DECLARATIONS_ONLY */ ; EAX_ENTRYPOINT_DECL _Bool eax_decrypt(INFO, const uint8_t *nonce, size_t nonce_len, const uint8_t *h, size_t h_len, const uint8_t *ct, size_t ct_len, uint8_t tau, uint8_t *m) #ifndef EAX_DECLARATIONS_ONLY { assert(tau <= n); const uint8_t *t; uint8_t nscr[n], hscr[n], cscr[n], tprime[tau]; if (ct_len < tau) return 0; size_t m_len = ct_len - tau; t = ct + m_len; alg_omac_t_k(I, nscr, 0, nonce,nonce_len); alg_omac_t_k(I, hscr, 1, h,h_len); alg_omac_t_k(I, cscr, 2, ct,m_len); xor_block(tprime, nscr, cscr, tau); xor_block(tprime, tprime, hscr, tau); if (!consttime_memeq(tprime, t, tau)) return 0; alg_ctr(I, m, nscr, ct, m_len); return 1; } #endif /* not EAX_DECLARATIONS_ONLY */ ; #undef n work/example.conf0000664000000000000000000001613415063477206011251 0ustar # secnet example configuration file # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. # Log facility # If you use this unaltered you should consider providing automatic log # rotation for /var/log/secnet. secnet will close and re-open its logfiles # when it receives SIGHUP. log logfile { filename "/var/log/secnet"; class "info","notice","warning","error","security","fatal"; # There are some useful message classes that could replace # this list: # 'default' -> warning,error,security,fatal # 'verbose' -> info,notice,default # 'quiet' -> fatal }; # Alternatively you could log through syslog: # log syslog { # ident "secnet"; # facility "local0"; # }; # Systemwide configuration (all other configuration is per-site): # log a log facility for program messages # userid who we try to run as after setup # pidfile system { # Note that you should not specify 'userid' here unless secnet # is being invoked as root. userid "secnet"; pidfile "/var/run/secnet.pid"; }; # Parameters for each remote site (arguments to the site() closure): # things we configure locally # buffer buffer for constructing/sending/receiving packets # netlink user/kernel netlink device for this tunnel # comm UDP communication # resolver resolver to use for name lookups # log a log destination for this connection # log-events string list: which events we log # random a source of randomness # our local configuration visible to the outside world # local-name string: how we identify ourselves to them # local-key our own private RSA key # local-port port number we listen on # their configuration visible to us # name string: how they identify themselves # address string: use with resolver to find their IP address # networks string list: their networks for us # key the remote site's RSA public key # port port we send to to contact remote site # things both ends must agree on # transform routine for bulk encryption # dh Diffie-Hellman parameters # hash secure hash function # things both ends ought to agree on, but don't have to # key-lifetime max session key lifetime, in milliseconds # setup-retries max retransmits of a key setup packet # setup-timeout wait between retransmits of key setup packets, in ms # wait-time wait between unsuccessful key setup attempts, in ms # renegotiate-time set up a new key if we see any traffic after this time # Defaults that may be overridden on a per-site basis: #setup-retries 10; #setup-timeout 2000; # Use the universal TUN/TAP driver to get packets to and from the kernel, # through a single interface. secnet will act as a router; it requires # its own IP address which is specified below (you'll see it on traceroute, # etc. for routes that go via tunnels). If you don't want secnet to act # as a router, and instead want a separate kernel network interface per # tunnel, then see the alternative configuration below # If you want to use userv-ipif to manage interfaces then replace the # word "tun" with "userv-ipif". netlink tun { name "netlink-tun"; # Printed in log messages from this netlink # interface "tun0"; # You may set your own interface name if you wish; # if you don't one will be chosen for you. # device "/dev/net/tun"; local-address "192.168.x.x"; # IP address of host's tunnel interface secnet-address "192.168.x.x"; # IP address of this secnet # Tunnels are only allowed to use these networks; attempts to # claim IP addresses in any other ranges is a configuration error remote-networks "192.168.0.0/16", "172.16.0.0/12", "10.0.0.0/8"; # MTU of the tunnel interface. Should be kept under the path-MTU # (by at least 60 bytes) between this secnet and its peers for # optimum performance. mtu 1400; # This buffer is used to pass incoming packets onto the 'site' # module. It should be at least as big as the MTU plus 60 bytes. # Buffers can sometimes be shared between netlink devices - see # full documentation for more details. (XXX TODO) buffer sysbuffer(2048); }; # This alternative configuration allows you to create one kernel network # interface per tunnel. IT WILL ONLY WORK WITH "tun" - IT WILL NOT # WORK WITH "userv-ipif". This is because "tun" can share a single # buffer between multiple network interfaces, but userv-ipif can't. # To use userv-ipif in this style, process the sites.conf file so that # each "netlink" section contains a "buffer sysbuffer(2048);" line. #netlink tun; #local-address "192.168.x.x"; # Address of local interfaces - all the same #mtu 1400; #buffer sysbuffer(2048); # This is small enough that it fits without fragmentation into # the foolish wifi on Greater Anglia's now-retired Class 379s. # This is good because they mishandle fragmentation. mtu-target 1260; # This defines the port that this instance of secnet will listen on, and # originate packets on. It does not _have_ to correspond to the advertised # port for your site: you may be doing network address translation, for # example. You need to arrange that any UDP packets sent to the advertised # host and port for your site end up on this machine at the port you # specify here. comm udp { port 410; buffer sysbuffer(4096); }; # The resolver is used to look up IP addresses from the DNS names provided # in the sites file. You may specify an alternative resolv.conf for # ADNS here if you wish. resolver adns { # config=readfile("/etc/secnet/adns.conf"); }; # log is defined earlier - we share it with the system log-events "setup-init","setup-timeout","activate-key","timeout-key","errors", "security"; # A source of random bits for nonces and session keys. The 'no' specifies # that it's non-blocking. XXX 'yes' isn't implemented yet. random randomfile("/dev/urandom",no); # If you're using the make-secnet-sites script then your local-name # will be of the form "vpnname/location/site" eg. "sgo/greenend/sinister" local-name "your-site-name"; local-key rsa-private("/etc/secnet/key"); # Are we a mobile site? #local-mobile true; # On dodgy links you may want to specify a higher maximum sequence number skew transform eax-serpent { }, serpent256-cbc { }; include /etc/secnet/sites.conf # The /etc/secnet/sites file contains information on all reachable sites; # if the site you want to communicate with isn't listed, you should get # a newer version. MAKE SURE YOU GET AN AUTHENTIC COPY OF THE FILE - it # contains public keys for all sites. # If you want to communicate with all the VPN sites, you can use something # like the following: sites map(site,all-sites); # Or with a particular VPN #sites map(site,vpn/Vexample/all-sites); # If you only want to communicate with a subset of the VPN sites, list # them explicitly: # sites map(site, # vpn-data/example/location1/site1, # vpn-data/example/location2/site1, # vpn-data/example/location2/site2); # If you want to communicate with a subset of locations, try the following: # sites map(site,vpn/example/location1,vpn/example/location2); # This file is placed in the public domain (insofar as possible.) # Authors: Stephen Early, Ian Jackson work/hackypar.c0000664000000000000000000000616615063477206010721 0ustar /* Hacky parallelism */ /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #define _GNU_SOURCE #include "secnet.h" #include "util.h" #include "hackypar.h" #ifdef HACKY_PARALLEL #include #include #include #include #include #include #include #define HASHSIZE 16 #define CACHESIZE 16 typedef enum { hp_idle, hp_compute, hp_deferring, hp_fail } HPState; static HPState state; static pid_t child; static void checkchild(void) { int r, status; if (!child) return; r= waitpid(child,&status,WNOHANG); if (!r) return; if (r==-1) { Message(M_ERR,"hacky_par: waitpid: %s\n",strerror(errno)); return; } child= 0; if (WIFSIGNALED(status)) { Message(M_ERR,"hacky_par: signaled! %s\n",strsignal(WTERMSIG(status))); } else if (!WIFEXITED(status)) { Message(M_ERR,"hacky_par: unexpected status! %d\n", r); } } static HPState start(void) { assert(!child); child= fork(); if (child == -1) { Message(M_ERR,"hacky_par: fork failed: %s\n",strerror(errno)); return hp_fail; } if (!child) { /* we are the child */ afterfork(); return hp_compute; } Message(M_INFO,"hacky_par: started, punting\n"); return hp_deferring; } int hacky_par_start_failnow(void) { state= hp_idle; checkchild(); if (child) { state= hp_deferring; Message(M_INFO,"hacky_par: busy, punting\n"); return 1; } return 0; } int hacky_par_mid_failnow(void) { state= start(); return state != hp_compute; } bool_t (*packy_par_gen)(struct site *st); void hacky_par_end(int *ok, int32_t retries, int32_t timeout, bool_t (*send_msg)(struct site *st), struct site *st) { int i; switch (state) { case hp_deferring: assert(!*ok); *ok= 1; return; case hp_fail: assert(!*ok); return; case hp_idle: return; case hp_compute: if (!ok) { Message(M_ERR,"hacky_par: compute failed\n"); _exit(2); } Message(M_INFO,"hacky_par: got result, sending\n"); for (i=1; i #include static inline void hexdebug(FILE *file, const void *buffer, size_t len) { const uint8_t *array=buffer; size_t i; for (i=0; i #include #include #include #include "ipaddr.h" #include "util.h" #define DEFAULT_ALLOC 2 #define EXTEND_ALLOC_BY 4 struct subnet_list *subnet_list_new(void) { struct subnet_list *r; NEW(r); r->entries=0; r->alloc=DEFAULT_ALLOC; NEW_ARY(r->list,r->alloc); return r; } void subnet_list_free(struct subnet_list *a) { if (a->list) free(a->list); free(a); } static void subnet_list_set_len(struct subnet_list *a, int32_t l) { int32_t na; if (l>a->alloc) { assert(a->alloc < INT_MAX-EXTEND_ALLOC_BY); na=a->alloc+EXTEND_ALLOC_BY; REALLOC_ARY(a->list,na); a->alloc=na; } a->entries=l; } void subnet_list_append(struct subnet_list *a, uint32_t prefix, int len) { struct subnet *sn; assert(a->entries < INT_MAX); subnet_list_set_len(a,a->entries+1); sn=&a->list[a->entries-1]; sn->prefix=prefix; sn->len=len; sn->mask=len?(0xffffffff << (32-len)):0; } struct ipset *ipset_new(void) { struct ipset *r; NEW(r); r->l=0; r->a=DEFAULT_ALLOC; NEW_ARY(r->d,r->a); return r; } void ipset_free(struct ipset *a) { if (a->d) free(a->d); free(a); } #ifdef DEBUG static void ipset_dump(struct ipset *a, string_t name) { int32_t i; printf("%s: ",name); for (i=0; il; i++) { printf("[%08x-%08x] ",a->d[i].a,a->d[i].b); } printf("\n"); } #endif struct ipset *ipset_from_subnet(struct subnet s) { struct ipset *r; r=ipset_new(); r->l=1; r->d[0].a=s.prefix; r->d[0].b=s.prefix | (~s.mask); return r; } struct ipset *ipset_from_subnet_list(struct subnet_list *l) { struct ipset *r, *a, *b; int32_t i; r=ipset_new(); for (i=0; ientries; i++) { a=ipset_from_subnet(l->list[i]); b=ipset_union(r,a); ipset_free(a); ipset_free(r); r=b; } return r; } static void ipset_set_len(struct ipset *a, int32_t l) { int32_t na; if (l>a->a) { assert(a->a < INT_MAX-EXTEND_ALLOC_BY); na=a->a+EXTEND_ALLOC_BY; REALLOC_ARY(a->d,na); a->a=na; } a->l=l; } static void ipset_append_range(struct ipset *a, struct iprange r) { ipset_set_len(a,a->l+1); a->d[a->l-1]=r; } struct ipset *ipset_union(struct ipset *a, struct ipset *b) { struct ipset *c; struct iprange r; int32_t ia,ib; c=ipset_new(); ia=0; ib=0; while (ial || ibl) { if (ial) if (ibl) if (a->d[ia].a < b->d[ib].a) r=a->d[ia++]; else r=b->d[ib++]; else r=a->d[ia++]; else r=b->d[ib++]; if (c->l==0) ipset_append_range(c,r); else if (r.a <= c->d[c->l-1].b+1) /* Extends (or is consumed by) the last range */ c->d[c->l-1].b=MAX(c->d[c->l-1].b, r.b); else ipset_append_range(c,r); } return c; } struct ipset *ipset_intersection(struct ipset *a, struct ipset *b) { struct ipset *r; struct iprange ra, rb; int32_t ia,ib; r=ipset_new(); ia=0; ib=0; while (ial && ibl) { ra=a->d[ia]; rb=b->d[ib]; if (ra.b < rb.a) /* The first entry of a doesn't overlap with any part of b */ ia++; else if (ra.a > rb.b) /* The first entry of b doesn't overlap with any part of a */ ib++; else { /* Trim away any leading edges */ if (ra.a < rb.a) /* a starts before b */ ra.a=rb.a; else if (ra.a > rb.a) /* b starts before a */ rb.a=ra.a; /* Now the ranges start at the same point */ if (ra.b == rb.b) { /* The ranges are equal */ ipset_append_range(r,ra); ia++; ib++; } else if (ra.b < rb.b) { /* a is the smaller range */ ipset_append_range(r,ra); ia++; } else { /* y is the smaller range */ ipset_append_range(r,rb); ib++; } } } return r; } struct ipset *ipset_complement(struct ipset *a) { struct ipset *r; struct iprange n; int64_t pre; int32_t i; uint32_t lo,hi; r=ipset_new(); pre=-1; for (i=0; il; i++) { lo=a->d[i].a; hi=a->d[i].b; if (lo!=0) { n.a=pre+1; n.b=lo-1; ipset_append_range(r,n); } pre=hi; } if (pre!=0xffffffff) { n.a=pre+1; n.b=0xffffffff; ipset_append_range(r,n); } return r; } /* Return a-b */ struct ipset *ipset_subtract(struct ipset *a, struct ipset *b) { struct ipset *c, *r; c=ipset_complement(b); r=ipset_intersection(a,c); ipset_free(c); return r; } bool_t ipset_is_empty(struct ipset *a) { return (a->l==0); } bool_t ipset_contains_addr(struct ipset *a, uint32_t addr) { int32_t i; struct iprange r; for (i=0; il; i++) { r=a->d[i]; if (addr>=r.a && addr<=r.b) return True; if (addrl; i++) { a=is->d[i].a; b=is->d[i].b; lomask=1; lobit=1; himask=0xfffffffe; bits=32; while (a<=b) { if ((a & lomask) != 0) { subnet_list_append(r,a,bits); a=a+lobit; } else if ((b & lomask) != lomask) { subnet_list_append(r,b&himask,bits); b=b-lobit; } else { lomask = (lomask << 1) | 1; lobit = (lobit << 1); himask = himask ^ lobit; bits = bits - 1; ASSERT(bits>=0); } } } /* Sort the list? */ return r; } #define IPADDR_BUFLEN 20 static char *ipaddr_getbuf(void) { SBUF_DEFINE(16, IPADDR_BUFLEN); return SBUF; } /* The string buffer must be at least 16 bytes long */ string_t ipaddr_to_string(uint32_t addr) { uint8_t a,b,c,d; string_t s; s=ipaddr_getbuf(); a=addr>>24; b=addr>>16; c=addr>>8; d=addr; snprintf(s, 16, "%d.%d.%d.%d", a, b, c, d); return s; } string_t subnet_to_string(struct subnet sn) { uint32_t addr=sn.prefix; uint8_t a,b,c,d; string_t s; s=ipaddr_getbuf(); a=addr>>24; b=addr>>16; c=addr>>8; d=addr; snprintf(s, 19, "%d.%d.%d.%d/%d", a, b, c, d, sn.len); return s; } static struct subnet string_item_to_subnet(item_t *i, cstring_t desc, bool_t *invert) { struct subnet s; uint32_t a, b, c, d, n; int match; cstring_t in; *invert=False; /* i is not guaranteed to be a string */ if (i->type!=t_string) { cfgfatal(i->loc,desc,"expecting a string (subnet specification)\n"); } in=i->data.string; if (strcmp(in,"default")==0) { s.prefix=0; s.mask=0; s.len=0; return s; } if (*in=='!') { *invert=True; in++; } /* We expect strings of the form "a.b.c.d[/n]", i.e. the dots are NOT optional. The subnet mask is optional; if missing it is assumed to be /32. */ match=sscanf(in,"%u.%u.%u.%u/%u", &a, &b, &c, &d, &n); if (match<4) { cfgfatal(i->loc,desc,"\"%s\" is not a valid " "subnet specification\n",in); } if (match<5) { n=32; } if (a>255 || b>255 || c>255 || d>255 || n>32) { cfgfatal(i->loc,desc,"\"%s\": range error\n",in); } s.prefix=(a<<24)|(b<<16)|(c<<8)|(d); s.mask=n?(~0UL << (32-n)):0; s.len=n; if (s.prefix & ~s.mask) { cfgfatal(i->loc,desc,"\"%s\": prefix not fully contained " "in mask\n",in); } return s; } uint32_t string_item_to_ipaddr(const item_t *i, cstring_t desc) { uint32_t a, b, c, d; int match; /* i is not guaranteed to be a string */ if (i->type!=t_string) { cfgfatal(i->loc,desc,"expecting a string (IP address)\n"); } match=sscanf(i->data.string,"%u.%u.%u.%u", &a, &b, &c, &d); if (match<4) { cfgfatal(i->loc,desc,"\"%s\" is not a valid " "IP address\n",i->data.string); } if (a>255 || b>255 || c>255 || d>255) { cfgfatal(i->loc,desc,"\"%s\": range error\n",i->data.string); } return (a<<24)|(b<<16)|(c<<8)|(d); } struct ipset *string_list_to_ipset(list_t *l, struct cloc loc, cstring_t module, cstring_t param) { struct ipset *r, *n, *isn; uint32_t e,i; item_t *item; bool_t inv; r=ipset_new(); e=list_length(l); for (i=0; i=other: return +1 if self<=other: return -1 return NotImplemented def __iter__(self): "Iterates over minimal list of distinct IPNetworks in this set" for v in _vsns: for i in self._v[v]: yield i def networks(self): "Returns miminal list of distinct IPNetworks in this set" return [i for i in self] # set operations def intersection(self,other): "Returns the intersection; does not modify self" r = IPAddressSet() for v in _vsns: for i in self._v[v]: for j in other._v[v]: if i.overlaps(j): if i.prefixlen > j.prefixlen: r._append([i]) else: r._append([j]) return r def union(self,other): "Returns the union; does not modify self" r = IPAddressSet() r._append(self.networks()) r._append(other.networks()) r._compact() return r def _contains_net(self,n): """True iff self completely contains IPNetwork n""" for i in self: if i.overlaps(n) and n.prefixlen >= i.prefixlen: return True return False def contains(self,thing): """Returns True iff self completely contains thing. thing may be an IPNetwork or an IPAddressSet""" try: v = [thing.version] except KeyError: v = None if v: return self._contains_net(ipaddress.ip_network(thing)) else: return self.__ge__(thing) def complete_set(): "Returns a set containing all addresses" s=IPAddressSet() for v in _vsns: if v==6: a=ipaddress.IPv6Address(0) elif v==4: a=ipaddress.IPv4Address(0) else: raise "internal error" n=ipaddress.ip_network("%s/0" % a) s.append([n]) return s work/log.c0000664000000000000000000004116615063477206007677 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include "secnet.h" #include #include #include #include #include #include #include #include #include "process.h" #include "util.h" bool_t secnet_is_daemon=False; uint32_t message_level=M_WARNING|M_ERR|M_SECURITY|M_FATAL; struct log_if *system_log=NULL; FORMAT(printf,2,0) static void vMessageFallback(uint32_t class, const char *message, va_list args) { FILE *dest=stdout; /* Messages go to stdout/stderr */ if (class & message_level) { if (class&M_FATAL || class&M_ERR || class&M_WARNING) { dest=stderr; } vfprintf(dest,message,args); } } FORMAT(printf,2,0) static void vMessage(uint32_t class, const char *message, va_list args) { vslilog_part(system_log, class, message, args); } void Message(uint32_t class, const char *message, ...) { va_list ap; va_start(ap,message); vMessage(class,message,ap); va_end(ap); } FORMAT(printf,2,3) static void MessageFallback(uint32_t class, const char *message, ...) { va_list ap; va_start(ap,message); vMessageFallback(class,message,ap); va_end(ap); } static NORETURN(vfatal(int status, bool_t perror, const char *message, va_list args)); FORMAT(printf,3,0) static void vfatal(int status, bool_t perror, const char *message, va_list args) { int err; err=errno; enter_phase(PHASE_SHUTDOWN); Message(M_FATAL, "secnet fatal error: "); vMessage(M_FATAL, message, args); if (perror) Message(M_FATAL, ": %s\n",strerror(err)); else Message(M_FATAL, "\n"); exit(status); } void fatal(const char *message, ...) { va_list args; va_start(args,message); vfatal(current_phase,False,message,args); va_end(args); } void fatal_status(int status, const char *message, ...) { va_list args; va_start(args,message); vfatal(status,False,message,args); va_end(args); } void fatal_perror(const char *message, ...) { va_list args; va_start(args,message); vfatal(current_phase,True,message,args); va_end(args); } void fatal_perror_status(int status, const char *message, ...) { va_list args; va_start(args,message); vfatal(status,True,message,args); va_end(args); } void vcfgfatal_maybefile(FILE *maybe_f /* or 0 */, struct cloc loc, cstring_t facility, const char *message, va_list args, const char *suffix) { enter_phase(PHASE_SHUTDOWN); if (maybe_f && ferror(maybe_f)) { assert(loc.file); Message(M_FATAL, "error reading config file (%s, %s): %s", facility, loc.file, strerror(errno)); } else if (maybe_f && feof(maybe_f)) { assert(loc.file); Message(M_FATAL, "unexpected end of config file (%s, %s)", facility, loc.file); } else if (loc.file && loc.line) { Message(M_FATAL, "config error (%s, %s:%d): ",facility,loc.file, loc.line); } else if (!loc.file && loc.line) { Message(M_FATAL, "config error (%s, line %d): ",facility,loc.line); } else { Message(M_FATAL, "config error (%s): ",facility); } vMessage(M_FATAL,message,args); Message(M_FATAL,"%s",suffix); exit(current_phase); } void cfgfatal_maybefile(FILE *maybe_f, struct cloc loc, cstring_t facility, const char *message, ...) { va_list args; va_start(args,message); vcfgfatal_maybefile(maybe_f,loc,facility,message,args,0); va_end(args); } void cfgfatal_cl_type(struct cloc loc, const char *facility, closure_t *cl, uint32_t exp_type, const char *name) { char expbuf[10], gotbuf[10]; assert(cl->type != exp_type); const char *exp = closure_type_name(exp_type, expbuf); const char *got = closure_type_name(cl->type, gotbuf); cfgfatal(loc,facility, "\"%s\" is the wrong type of closure (expected %s, got %s)\n", name, exp, got); } void cfgfatal(struct cloc loc, cstring_t facility, const char *message, ...) { va_list args; va_start(args,message); vcfgfatal_maybefile(0,loc,facility,message,args,""); va_end(args); } void cfgfile_log__vmsg(void *sst, int class, const char *message, va_list args) { struct cfgfile_log *st=sst; vcfgfatal_maybefile(0,st->loc,st->facility,message,args,"\n"); } void cfgfile_postreadcheck(struct cloc loc, FILE *f) { assert(loc.file); if (ferror(f)) { Message(M_FATAL, "error reading config file (%s): %s\n", loc.file, strerror(errno)); exit(current_phase); } else if (feof(f)) { Message(M_FATAL, "unexpected end of config file (%s)\n", loc.file); exit(current_phase); } } /* Take a list of log closures and merge them */ struct loglist { struct log_if *l; struct loglist *next; }; FORMAT(printf, 3, 0) static void log_vmulti(void *sst, int class, const char *message, va_list args) { struct loglist *st=sst, *i; if (secnet_is_daemon) { for (i=st; i; i=i->next) { vslilog(i->l,class,message,args); } } else { vMessage(class,message,args); Message(class,"\n"); } } FORMAT(printf, 6, 0) void lg_vperror(struct log_if *lg, const char *desc, struct cloc *loc, int class, int errnoval, const char *fmt, va_list al) { int status=current_phase; int esave=errno; if (!lg) lg=system_log; if (class & M_FATAL) enter_phase(PHASE_SHUTDOWN); slilog_part(lg,class,"%s",desc); if (loc) slilog_part(lg,class," (%s:%d)",loc->file,loc->line); slilog_part(lg,class,": "); vslilog_part(lg,class,fmt,al); if (errnoval) slilog_part(lg,class,": %s",strerror(errnoval)); slilog_part(lg,class,"\n"); if (class & M_FATAL) exit(status); errno=esave; } void lg_perror(struct log_if *lg, const char *desc, struct cloc *loc, int class, int errnoval, const char *fmt, ...) { va_list al; va_start(al,fmt); lg_vperror(lg,desc,loc,class,errnoval,fmt,al); va_end(al); } void lg_exitstatus(struct log_if *lg, const char *desc, struct cloc *loc, int class, int status, const char *progname) { if (!status) lg_perror(lg,desc,loc,class,0,"%s exited",progname); else if (WIFEXITED(status)) lg_perror(lg,desc,loc,class,0,"%s exited with error exit status %d", progname,WEXITSTATUS(status)); else if (WIFSIGNALED(status)) lg_perror(lg,desc,loc,class,0,"%s died due to fatal signal %s (%d)%s", progname,strsignal(WTERMSIG(status)),WTERMSIG(status), WCOREDUMP(status)?" (core dumped)":""); else lg_perror(lg,desc,loc,class,0,"%s died with unknown wait status %d", progname,status); } struct log_if *init_log(list_t *ll) { int i=0; item_t *item; closure_t *cl; struct loglist *l=NULL, *n; struct log_if *r; if (list_length(ll)==1) { item=list_elem(ll,0); cl=item->data.closure; if (cl->type!=CL_LOG) { cfgfatal(item->loc,"init_log","closure is not a logger"); } return cl->interface; } while ((item=list_elem(ll,i++))) { if (item->type!=t_closure) { cfgfatal(item->loc,"init_log","item is not a closure"); } cl=item->data.closure; if (cl->type!=CL_LOG) { cfgfatal(item->loc,"init_log","closure is not a logger"); } NEW(n); n->l=cl->interface; n->next=l; l=n; } if (!l) { fatal("init_log: no log"); } NEW(r); r->st=l; r->vlogfn=log_vmulti; r->buff[0]=0; return r; } struct logfile { closure_t cl; struct log_if ops; struct cloc loc; string_t logfile; uint32_t level; FILE *f; const char *prefix; bool_t forked; }; static cstring_t months[]={ "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; FORMAT(printf, 3, 0) static void logfile_vlog(void *sst, int class, const char *message, va_list args) { struct logfile *st=sst; time_t t; struct tm *tm; char pidbuf[20]; if (st->forked) { pid_t us=getpid(); snprintf(pidbuf,sizeof(pidbuf),"[%ld] ",(long)us); } else { pidbuf[0]=0; } if (class&st->level) { t=time(NULL); tm=localtime(&t); fprintf(st->f,"%s %2d %02d:%02d:%02d %s%s%s", months[tm->tm_mon],tm->tm_mday,tm->tm_hour,tm->tm_min, tm->tm_sec, st->prefix, st->prefix[0] ? " " : "", pidbuf); vfprintf(st->f,message,args); fprintf(st->f,"\n"); fflush(st->f); } } FORMAT(printf,3,4) static void logfile_log(void *state, int class, const char *message, ...) { va_list ap; va_start(ap,message); logfile_vlog(state,class,message,ap); va_end(ap); } static void logfile_hup_notify(void *sst, int signum) { struct logfile *st=sst; FILE *f; if (!st->logfile) return; f=fopen(st->logfile,"a"); if (!f) { logfile_log(st,M_FATAL,"received SIGHUP, but could not reopen " "logfile: %s",strerror(errno)); } else { fclose(st->f); st->f=f; logfile_log(st,M_INFO,"received SIGHUP"); } } static void logfile_phase_hook(void *sst, uint32_t new_phase) { struct logfile *st=sst; FILE *f; if (background && st->logfile) { f=fopen(st->logfile,"a"); if (!f) fatal_perror("logfile (%s:%d): cannot open \"%s\"", st->loc.file,st->loc.line,st->logfile); st->f=f; request_signal_notification(SIGHUP, logfile_hup_notify,st); } } static void logfile_childpersist_hook(void *sst, uint32_t new_phase) { struct logfile *st=sst; st->forked=1; } static struct flagstr message_class_table[]={ { "debug-config", M_DEBUG_CONFIG }, { "debug-phase", M_DEBUG_PHASE }, { "debug", M_DEBUG }, { "all-debug", M_DEBUG|M_DEBUG_PHASE|M_DEBUG_CONFIG }, { "info", M_INFO }, { "notice", M_NOTICE }, { "warning", M_WARNING }, { "error", M_ERR }, { "security", M_SECURITY }, { "fatal", M_FATAL }, { "default", M_WARNING|M_ERR|M_SECURITY|M_FATAL }, { "verbose", M_INFO|M_NOTICE|M_WARNING|M_ERR|M_SECURITY|M_FATAL }, { "quiet", M_FATAL }, { NULL, 0 } }; static void logfile_file_init(struct logfile *st, FILE *f, const char *desc) { st->cl.description=desc; st->cl.type=CL_LOG; st->cl.apply=NULL; st->cl.interface=&st->ops; st->ops.st=st; st->ops.vlogfn=logfile_vlog; st->ops.buff[0]=0; st->f=f; st->logfile=0; st->prefix=""; st->forked=0; st->loc.file=0; st->loc.line=-1; } static list_t *logfile_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct logfile *st; item_t *item; dict_t *dict; /* We should defer opening the logfile until the getresources phase. We should defer writing into the logfile until after we become a daemon. */ NEW(st); st->loc=loc; logfile_file_init(st,stderr,"logfile"); item=list_elem(args,0); if (!item || item->type!=t_dict) { cfgfatal(loc,"logfile","argument must be a dictionary\n"); } dict=item->data.dict; st->logfile=dict_read_string(dict,"filename",False,"logfile",loc); st->prefix=dict_read_string(dict,"prefix",False,"logfile",loc); if (!st->prefix) st->prefix=""; st->level=string_list_to_word(dict_lookup(dict,"class"), message_class_table,"logfile"); add_hook(PHASE_DAEMONIZE,logfile_phase_hook,st); add_hook(PHASE_CHILDPERSIST,logfile_childpersist_hook,st); return new_closure(&st->cl); } struct syslog { closure_t cl; struct log_if ops; string_t ident; int facility; bool_t open; }; static int msgclass_to_syslogpriority(uint32_t m) { switch (m) { case M_DEBUG_CONFIG: return LOG_DEBUG; case M_DEBUG_PHASE: return LOG_DEBUG; case M_DEBUG: return LOG_DEBUG; case M_INFO: return LOG_INFO; case M_NOTICE: return LOG_NOTICE; case M_WARNING: return LOG_WARNING; case M_ERR: return LOG_ERR; case M_SECURITY: return LOG_CRIT; case M_FATAL: return LOG_EMERG; default: return LOG_NOTICE; } } static void syslog_vlog(void *sst, int class, const char *message, va_list args) FORMAT(printf,3,0); static void syslog_vlog(void *sst, int class, const char *message, va_list args) { struct syslog *st=sst; if (st->open) vsyslog(msgclass_to_syslogpriority(class),message,args); else { vMessageFallback(class,message,args); MessageFallback(class,"\n"); } } static struct flagstr syslog_facility_table[]={ #ifdef LOG_AUTH { "auth", LOG_AUTH }, #endif #ifdef LOG_AUTHPRIV { "authpriv", LOG_AUTHPRIV }, #endif { "cron", LOG_CRON }, { "daemon", LOG_DAEMON }, { "kern", LOG_KERN }, { "local0", LOG_LOCAL0 }, { "local1", LOG_LOCAL1 }, { "local2", LOG_LOCAL2 }, { "local3", LOG_LOCAL3 }, { "local4", LOG_LOCAL4 }, { "local5", LOG_LOCAL5 }, { "local6", LOG_LOCAL6 }, { "local7", LOG_LOCAL7 }, { "lpr", LOG_LPR }, { "mail", LOG_MAIL }, { "news", LOG_NEWS }, { "syslog", LOG_SYSLOG }, { "user", LOG_USER }, { "uucp", LOG_UUCP }, { NULL, 0 } }; static void syslog_phase_hook(void *sst, uint32_t newphase) { struct syslog *st=sst; if (background) { openlog(st->ident, newphase==PHASE_CHILDPERSIST ? LOG_PID : 0, st->facility); st->open=True; } } static list_t *syslog_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct syslog *st; dict_t *d; item_t *item; string_t facstr; NEW(st); st->cl.description="syslog"; st->cl.type=CL_LOG; st->cl.apply=NULL; st->cl.interface=&st->ops; st->ops.st=st; st->ops.vlogfn=syslog_vlog; st->ops.buff[0]=0; item=list_elem(args,0); if (!item || item->type!=t_dict) cfgfatal(loc,"syslog","parameter must be a dictionary\n"); d=item->data.dict; st->ident=dict_read_string(d, "ident", False, "syslog", loc); facstr=dict_read_string(d, "facility", True, "syslog", loc); st->facility=string_to_word(facstr,loc, syslog_facility_table,"syslog"); st->open=False; add_hook(PHASE_DAEMONIZE,syslog_phase_hook,st); add_hook(PHASE_CHILDPERSIST,syslog_phase_hook,st); return new_closure(&st->cl); } /* Read from a fd and output to a log. This is a quick hack to support logging stderr, and needs code adding to tidy up before it can be used for anything else. */ #define FDLOG_BUFSIZE 1024 struct fdlog { struct log_if *log; int fd; cstring_t prefix; string_t buffer; int i; bool_t finished; }; static int log_from_fd_beforepoll(void *sst, struct pollfd *fds, int *nfds_io, int *timeout_io) { struct fdlog *st=sst; if (!st->finished) { BEFOREPOLL_WANT_FDS(1); fds[0].fd=st->fd; fds[0].events=POLLIN; } else { BEFOREPOLL_WANT_FDS(0); } return 0; } static void log_from_fd_afterpoll(void *sst, struct pollfd *fds, int nfds) { struct fdlog *st=sst; int r,remain,i; if (nfds==0) return; if (fds[0].revents&POLLERR) { st->finished=True; } if (fds[0].revents&POLLIN) { remain=FDLOG_BUFSIZE-st->i-1; if (remain<=0) { st->buffer[FDLOG_BUFSIZE-1]=0; slilog(st->log,M_WARNING,"%s: overlong line: %s", st->prefix,st->buffer); st->i=0; remain=FDLOG_BUFSIZE-1; } r=read(st->fd,st->buffer+st->i,remain); if (r>0) { st->i+=r; for (i=0; ii; i++) { if (st->buffer[i]=='\n') { st->buffer[i]=0; slilog(st->log,M_INFO,"%s: %s", st->prefix,st->buffer); i++; memmove(st->buffer,st->buffer+i,st->i-i); st->i-=i; i=-1; } } } else if (errno==EINTR || iswouldblock(errno)) { } else { Message(M_WARNING,"log_from_fd: %s\n",strerror(errno)); st->finished=True; } } } void log_from_fd(int fd, cstring_t prefix, struct log_if *log) { struct fdlog *st; NEW(st); st->log=log; st->fd=fd; st->prefix=prefix; st->buffer=safe_malloc(FDLOG_BUFSIZE,"log_from_fd"); st->i=0; st->finished=False; setnonblock(st->fd); register_for_poll(st,log_from_fd_beforepoll,log_from_fd_afterpoll, prefix); } static struct logfile startup_log; void log_early_setlevel(void) { startup_log.level=message_level; } void log_early_init(void) { logfile_file_init(&startup_log,stderr,"startup"); log_early_setlevel(); system_log=&startup_log.ops;; } /* for the benefit of main, really */ void logfile_init_file(struct logfile *st, FILE *f); void log_module(dict_t *dict) { setlinebuf(stderr); add_closure(dict,"logfile",logfile_apply); add_closure(dict,"syslog",syslog_apply); } work/magic.h0000664000000000000000000001067215063477206010201 0ustar /* Magic numbers used within secnet */ /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #ifndef magic_h #define magic_h /* Encode a pair of 16 bit major and minor codes as a single 32-bit label. * The encoding is strange for historical reasons. Suppose that the nibbles * of the major number are (from high to low) a, b, c, d, and the minor * number has nibbles w, x, y, z. (Here, a, b, c, d are variables, not hex * digits.) We scramble them to form a message label as follows. * * 0 d 0 d 0 d 0 d * 0 0 0 a b c 0 0 * z 0 0 0 0 0 z 0 * w x y 0 0 0 0 0 * --------------- * f g h i j k l m * * and calculate the nibbles f, g, ..., m of the message label (higher * significance on the left) by XORing the columns. It can be shown that * this is invertible using linear algebra in GF(16), but but it's easier to * notice that d = m, z = l, c = k XOR d, b = j, a = i XOR d, y = h, * x = g XOR d, and w = f XOR z. * * Encoding in the forward direction, from a major/minor pair to a label, is * (almost?) always done on constants, so its performance is fairly * unimportant. There is a compatibility constraint on the patterns produced * with a = b = c = w = x = y = 0. Subject to that, I wanted to find an * invertible GF(16)-linear transformation which would let me recover the * major and minor numbers with relatively little calculation. */ #define MSGCODE(major, minor) \ ((((uint32_t)(major)&0x0000000fu) << 0) ^ \ (((uint32_t)(major)&0x0000000fu) << 8) ^ \ (((uint32_t)(major)&0x0000000fu) << 16) ^ \ (((uint32_t)(major)&0x0000000fu) << 24) ^ \ (((uint32_t)(major)&0x0000fff0u) << 4) ^ \ (((uint32_t)(minor)&0x0000000fu) << 4) ^ \ (((uint32_t)(minor)&0x0000000fu) << 28) ^ \ (((uint32_t)(minor)&0x0000fff0u) << 16)) /* Extract major and minor codes from a 32-bit message label. */ #define MSGMAJOR(label) \ ((((uint32_t)(label)&0x0000000fu) << 0) ^ \ (((uint32_t)(label)&0x0000000fu) << 4) ^ \ (((uint32_t)(label)&0x0000000fu) << 12) ^ \ (((uint32_t)(label)&0x000fff00u) >> 4)) #define MSGMINOR(label) \ ((((uint32_t)(label)&0x000000ffu) << 8) ^ \ (((uint32_t)(label)&0x000000f0u) >> 4) ^ \ (((uint32_t)(label)&0xfff00000u) >> 16)) #define LABEL_NAK MSGCODE( 0, 0) #define LABEL_MSG0 MSGCODE(0x2020, 0) /* ! */ #define LABEL_MSG1 MSGCODE( 1, 0) #define LABEL_MSG2 MSGCODE( 2, 0) #define LABEL_MSG3 MSGCODE( 3, 0) #define LABEL_MSG3BIS MSGCODE( 3, 1) #define LABEL_MSG4 MSGCODE( 4, 0) #define LABEL_MSG5 MSGCODE( 5, 0) #define LABEL_MSG6 MSGCODE( 6, 0) #define LABEL_MSG7 MSGCODE( 7, 0) #define LABEL_MSG8 MSGCODE( 8, 0) #define LABEL_MSG9 MSGCODE( 9, 0) #define LABEL_PROD MSGCODE( 10, 0) /* * The capability mask is a set of bits, one for each optional feature * supported. The capability numbers for transforms are set in the * configuration (and should correspond between the two sites), although * there are sensible defaults. * * Advertising a nonzero capability mask promises that the receiver * understands LABEL_MSG3BIS messages, which contain an additional byte * specifying the transform capability number actually chosen by the MSG3 * sender. * * Aside from that, an empty bitmask is treated the same as * 1u< 0: print(repr(args), file=sys.stderr) def base91s_encode(bindata): return base91.encode(bindata).replace('"',"-") def base91s_decode(string): return base91.decode(string.replace("-",'"')) class Tainted: def __init__(self,s,tline=None,tfile=None): self._s=s self._ok=None self._line=line if tline is None else tline self._file=file if tfile is None else tfile def __eq__(self,e): return self._s==e def __ne__(self,e): # for Python2 return not self.__eq__(e) def __str__(self): raise RuntimeError('direct use of Tainted value') def __repr__(self): return 'Tainted(%s)' % repr(self._s) def _bad(self,what,why): assert(self._ok is not True) self._ok=False complain('bad parameter: %s: %s' % (what, why)) return False def _max_ok(self,what,maxlen): if len(self._s) > maxlen: return self._bad(what,'too long (max %d)' % maxlen) return True def _re_ok(self,bad,what,maxlen=None): if maxlen is None: maxlen=max[what] self._max_ok(what,maxlen) if self._ok is False: return False if bad.search(self._s): #print(repr(self), file=sys.stderr) return self._bad(what,'bad syntax') return True def _rtnval(self, is_ok, ifgood, ifbad=''): if is_ok: assert(self._ok is not False) self._ok=True return ifgood else: assert(self._ok is not True) self._ok=False return ifbad def _rtn(self, is_ok, ifbad=''): return self._rtnval(is_ok, self._s, ifbad) def raw(self): return self._s def raw_mark_ok(self): # caller promises to throw if syntax was dangeorus return self._rtn(True) def output(self): if self._ok is False: return '' if self._ok is True: return self._s print('%s:%d: unchecked/unknown additional data "%s"' % (self._file,self._line,self._s), file=sys.stderr) sys.exit(1) bad_name=re.compile(r'^[^a-zA-Z]|[^-_0-9a-zA-Z]') # secnet accepts _ at start of names, but we reserve that bad_name_counter=0 def name(self,what='name'): ok=self._re_ok(Tainted.bad_name,what) return self._rtn(ok, '_line%d_%s' % (self._line, id(self))) def keyword(self): ok=self._s in keywords or self._s in levels if not ok: complain('unknown keyword %s' % self._s) return self._rtn(ok) bad_hex=re.compile(r'[^0-9a-fA-F]') def bignum_16(self,kind,what): maxlen=(max[kind+'_bits']+3)/4 ok=self._re_ok(Tainted.bad_hex,what,maxlen) return self._rtn(ok) bad_num=re.compile(r'[^0-9]') def bignum_10(self,kind,what): maxlen=math.ceil(max[kind+'_bits'] / math.log10(2)) ok=self._re_ok(Tainted.bad_num,what,maxlen) return self._rtn(ok) def number(self,minn,maxx,what='number'): # not for bignums ok=self._re_ok(Tainted.bad_num,what,10) if ok: v=int(self._s) if vmaxx: ok=self._bad(what,'out of range %d..%d' % (minn,maxx)) return self._rtnval(ok,v,minn) def hexid(self,byteslen,what): ok=self._re_ok(Tainted.bad_hex,what,byteslen*2) if ok: if len(self._s) < byteslen*2: ok=self._bad(what,'too short') return self._rtn(ok,ifbad='00'*byteslen) bad_host=re.compile(r'[^-\][_.:0-9a-zA-Z]') # We permit _ so we can refer to special non-host domains # which have A and AAAA RRs. This is a crude check and we may # still produce config files with syntactically invalid # domains or addresses, but that is OK. def host(self): ok=self._re_ok(Tainted.bad_host,'host/address',255) return self._rtn(ok) bad_email=re.compile(r'[^-._0-9a-z@!$%^&*=+~/]') # ^ This does not accept all valid email addresses. That's # not really possible with this input syntax. It accepts # all ones that don't require quoting anywhere in email # protocols (and also accepts some invalid ones). def email(self): ok=self._re_ok(Tainted.bad_email,'email address',1023) return self._rtn(ok) bad_groupname=re.compile(r'^[^_A-Za-z]|[^-+_0-9A-Za-z]') def groupname(self): ok=self._re_ok(Tainted.bad_groupname,'group name',64) return self._rtn(ok) bad_base91=re.compile(r'[^!-~]|[\'\"\\]') def base91(self,what='base91'): ok=self._re_ok(Tainted.bad_base91,what,4096) return self._rtn(ok) class ArgActionLambda(argparse.Action): def __init__(self, fn, **kwargs): self.fn=fn argparse.Action.__init__(self,**kwargs) def __call__(self,ap,ns,values,option_string): self.fn(values,ns,ap,option_string) class PkmBase(): def site_start(self,pubkeys_path): self._pa=pubkeys_path self._fs = FilterState() def site_serial(self,serial): pass def write_key(self,k): pass def site_finish(self,confw): pass class PkmSingle(PkmBase): opt = 'single' help = 'write one public key per site to sites.conf' def site_start(self,pubkeys_path): PkmBase.site_start(self,pubkeys_path) self._outk = [] def write_key(self,k): if k.okforonlykey(output_version,self._fs): self._outk.append(k) def site_finish(self,confw): if len(self._outk) == 0: complain("site with no public key"); elif len(self._outk) != 1: debugrepr('outk ', self._outk) complain( "site with multiple public keys, without --pubkeys-install (maybe --output-version=1 would help" ) else: confw.write("key %s;\n"%str(self._outk[0])) class PkmInstall(PkmBase): opt = 'install' help = 'install public keys in public key directory' def site_start(self,pubkeys_path): PkmBase.site_start(self,pubkeys_path) self._pw=open(self._pa+'~tmp','w') def site_serial(self,serial): self._pw.write('serial %s\n' % serial) def write_key(self,k): wout=k.forpub(output_version,self._fs) self._pw.write(' '.join(wout)) self._pw.write('\n') def site_finish(self,confw): self._pw.close() os.rename(self._pa+'~tmp',self._pa+'~update') PkmElide.site_finish(self,confw) class PkmElide(PkmBase): opt = 'elide' help = 'no public keys in sites.conf output nor in directory' def site_finish(self,confw): confw.write("peer-keys \"%s\";\n"%self._pa); class OpBase(): # Base case is reading a sites file from self.inputfilee. # And writing a sites file to self.sitesfile. def check_group(self,group,w): if len(w) >= 3: w[2].groupname() def positional_args(self, av): if len(av.arg)>3: print("Too many arguments") sys.exit(1) (self.inputfile, self.outputfile) = (av.arg + [None]*2)[0:2] def read_in(self): if self.inputfile is None: self.inputlines = pfile("stdin",sys.stdin.readlines()) else: self.inputlines = pfilepath(self.inputfile) def write_out(self): if self.outputfile is None: f=sys.stdout else: f=open(self.outputfile+"-tmp",'w') f.write("# sites file autogenerated by make-secnet-sites\n") self.write_out_heading(f) f.write("# use make-secnet-sites to turn this file into a\n") f.write("# valid /etc/secnet/sites.conf file\n\n") self.write_out_contents(f) f.write("# end of sites file\n") if self.outputfile is not None: f.close() os.rename(self.outputfile+"-tmp",self.outputfile) class OpConf(OpBase): opts = ['--conf'] help = 'sites.conf generation mode (default)' def write_out(self): if self.outputfile is None: of=sys.stdout else: tmp_outputfile=self.outputfile+'~tmp~' of=open(tmp_outputfile,'w') outputsites(of) if self.outputfile is not None: os.rename(tmp_outputfile,self.outputfile) class OpFilter(OpBase): opts = ['--filter'] help = 'sites file filtering mode' def positional_arXgs(self, av): if len(av.arg)!=1: print("Too many arguments") (self.inputfile,) = (av.arg + [None])[0:1] self.outputfile = None def write_out_heading(self,f): f.write("# --filter --output-version=%d\n"%output_version) def write_out_contents(self,f): for i in self.inputlines: f.write(i) class OpUserv(OpBase): opts = ['--userv','-u'] help = 'userv service fragment update mode' def positional_args(self, av): if len(av.arg)!=4: print("Wrong number of arguments") sys.exit(1) (self.header, self.groupfiledir, self.outputfile, self.group) = av.arg self.group = Tainted(self.group,0,'command line') # untrusted argument from caller if "USERV_USER" not in os.environ: print("Environment variable USERV_USER not found") sys.exit(1) self.user=os.environ["USERV_USER"] # Check that group is in USERV_GROUP if "USERV_GROUP" not in os.environ: print("Environment variable USERV_GROUP not found") sys.exit(1) ugs=os.environ["USERV_GROUP"] ok=0 for i in ugs.split(): if self.group==i: ok=1 if not ok: print("caller not in group %s"%self.group.groupname()) sys.exit(1) def check_group(self,group,w): if group!=self.group: complain("Incorrect group!") OpBase.check_group(self,group,w) def read_in(self): self.headerinput=pfilepath(self.header,allow_include=True) self.userinput=sys.stdin.readlines() pfile("user input",self.userinput) def write_out(self): # Put the user's input into their group file, and # rebuild the main sites file f=open(self.groupfiledir+"/T"+self.group.groupname(),'w') f.write("# Section submitted by user %s, %s\n"% (self.user,time.asctime(time.localtime(time.time())))) f.write("# Checked by make-secnet-sites version %s\n\n" %VERSION) for i in self.userinput: f.write(i) f.write("\n") f.close() os.rename(self.groupfiledir+"/T"+self.group.groupname(), self.groupfiledir+"/R"+self.group.groupname()) OpBase.write_out(self) def write_out_heading(self,f): f.write("# generated %s, invoked by %s\n"% (time.asctime(time.localtime(time.time())), self.user)) def write_out_contents(self,f): for i in self.headerinput: f.write(i) files=os.listdir(self.groupfiledir) for i in files: if i[0]=='R': j=open(self.groupfiledir+"/"+i) f.write(j.read()) j.close() def parse_args(): global opmode global prefix global key_prefix global debug_level global output_version global pubkeys_dir global pubkeys_mode ap = argparse.ArgumentParser(description='process secnet sites files') def add_opmode(how): ap.add_argument(*how().opts, action=ArgActionLambda, nargs=0, fn=(lambda v,ns,*x: setattr(ns,'opmode',how)), help=how().help) add_opmode(OpConf) add_opmode(OpFilter) add_opmode(OpUserv) ap.add_argument('--conf-key-prefix', action=ActionNoYes, default=True, help='prefix conf file key names derived from sites data') def add_pkm(how): ap.add_argument('--pubkeys-'+how().opt, action=ArgActionLambda, nargs=0, fn=(lambda v,ns,*x: setattr(ns,'pkm',how)), help=how().help) add_pkm(PkmInstall) add_pkm(PkmSingle) add_pkm(PkmElide) ap.add_argument('--pubkeys-dir', nargs=1, help='public key directory', default=['/var/lib/secnet/pubkeys']) ap.add_argument('--output-version', nargs=1, type=int, help='sites file output version', default=[max_version]) ap.add_argument('--prefix', '-P', nargs=1, help='set prefix') ap.add_argument('--debug', '-D', action='count', default=0) ap.add_argument('arg',nargs=argparse.REMAINDER) av = ap.parse_args() debug_level = av.debug debugrepr('av',av) opmode = getattr(av,'opmode',OpConf)() prefix = '' if av.prefix is None else av.prefix[0] key_prefix = av.conf_key_prefix output_version = av.output_version[0] pubkeys_dir = av.pubkeys_dir[0] pubkeys_mode = getattr(av,'pkm',PkmSingle) opmode.positional_args(av) parse_args() # Classes describing possible datatypes in the configuration file class basetype: "Common protocol for configuration types." def add(self,obj,w): complain("%s %s already has property %s defined"% (obj.type,obj.name,w[0].raw())) def forsites(self,version,copy,fs): return copy class conflist: "A list of some kind of configuration type." def __init__(self,subtype,w): self.subtype=subtype self.list=[subtype(w)] def add(self,obj,w): self.list.append(self.subtype(w)) def __str__(self): return ', '.join(map(str, self.list)) def forsites(self,version,copy,fs): most_recent=self.list[len(self.list)-1] return most_recent.forsites(version,copy,fs) def listof(subtype): return lambda w: conflist(subtype, w) class single_ipaddr (basetype): "An IP address" def __init__(self,w): self.addr=ipaddress.ip_address(w[1].raw_mark_ok()) def __str__(self): return '"%s"'%self.addr class networks (basetype): "A set of IP addresses specified as a list of networks" def __init__(self,w): self.set=ipaddrset.IPAddressSet() for i in w[1:]: x=ipaddress.ip_network(i.raw_mark_ok(),strict=True) self.set.append([x]) def __str__(self): return ",".join(map((lambda n: '"%s"'%n), self.set.networks())) class dhgroup (basetype): "A Diffie-Hellman group" def __init__(self,w): self.mod=w[1].bignum_16('dh','dh mod') self.gen=w[2].bignum_16('dh','dh gen') def __str__(self): return 'diffie-hellman("%s","%s")'%(self.mod,self.gen) class hash (basetype): "A choice of hash function" def __init__(self,w): hname=w[1] self.ht=hname.raw() if (self.ht!='md5' and self.ht!='sha1'): complain("unknown hash type %s"%(self.ht)) self.ht=None else: hname.raw_mark_ok() def __str__(self): return '%s'%(self.ht) class email (basetype): "An email address" def __init__(self,w): self.addr=w[1].email() def __str__(self): return '<%s>'%(self.addr) class boolean (basetype): "A boolean" def __init__(self,w): v=w[1] if re.match('[TtYy1]',v.raw()): self.b=True v.raw_mark_ok() elif re.match('[FfNn0]',v.raw()): self.b=False v.raw_mark_ok() else: complain("invalid boolean value"); def __str__(self): return ['False','True'][self.b] class num (basetype): "A decimal number" def __init__(self,w): self.n=w[1].number(0,0x7fffffff) def __str__(self): return '%d'%(self.n) class serial (basetype): def __init__(self,w): self.i=w[1].hexid(4,'serial') def __str__(self): return self.i def forsites(self,version,copy,fs): if version < 2: return [] return copy class address (basetype): "A DNS name and UDP port number" def __init__(self,w): self.adr=w[1].host() self.port=w[2].number(1,65536,'port') def __str__(self): return '"%s"; port %d'%(self.adr,self.port) class inpub (basetype): def forsites(self,version,xcopy,fs): return self.forpub(version,fs) class pubkey (inpub): "Some kind of publie key" def __init__(self,w): self.a=w[1].name('algname') self.d=w[2].base91(); def __str__(self): return 'make-public("%s","%s")'%(self.a,self.d) def forpub(self,version,fs): if version < 2: return [] return ['pub', self.a, self.d] def okforonlykey(self,version,fs): return len(self.forpub(version,fs)) != 0 class rsakey (pubkey): "An old-style RSA public key" def __init__(self,w): self.l=w[1].number(0,max['rsa_bits'],'rsa len') self.e=w[2].bignum_10('rsa','rsa e') self.n=w[3].bignum_10('rsa','rsa n') if len(w) >= 5: w[4].email() self.a='rsa1' self.d=base91s_encode(('%d %s %s' % (self.l, self.e, self.n)).encode('ascii')) # ^ this allows us to use the pubkey.forsites() # method for output in versions>=2 def __str__(self): return 'rsa-public("%s","%s")'%(self.e,self.n) # this specialisation means we can generate files # compatible with old secnet executables def forpub(self,version,fs): if version < 2: if fs.pkg != '00000000': return [] return ['pubkey', str(self.l), self.e, self.n] return pubkey.forpub(self,version,fs) class rsakey_newfmt(rsakey): "An old-style RSA public key in new-style sites format" # This is its own class simply to have its own constructor. def __init__(self,w): self.a=w[1].name() assert(self.a == 'rsa1') self.d=w[2].base91() try: w_inner=list(map(Tainted, ['X-PUB-RSA1'] + base91s_decode(self.d) .decode('ascii') .split(' '))) except UnicodeDecodeError: complain('rsa1 key in new format has bad base91') #print(repr(w_inner), file=sys.stderr) rsakey.__init__(self,w_inner) class pubkey_group(inpub): "Public key group introducer" # appears in the site's list of keys mixed in with the keys def __init__(self,w,fallback): self.i=w[1].hexid(4,'pkg-id') self.fallback=fallback def forpub(self,version,fs): fs.pkg=self.i if version < 2: return [] return ['pkgf' if self.fallback else 'pkg', self.i] def okforonlykey(self,version,fs): self.forpub(version,fs) return False def somepubkey(w): #print(repr(w), file=sys.stderr) if w[0]=='pubkey': return rsakey(w) elif w[0]=='pub' and w[1]=='rsa1': return rsakey_newfmt(w) elif w[0]=='pub': return pubkey(w) elif w[0]=='pkg': return pubkey_group(w,False) elif w[0]=='pkgf': return pubkey_group(w,True) else: assert(False) # Possible properties of configuration nodes keywords={ 'contact':(email,"Contact address"), 'dh':(dhgroup,"Diffie-Hellman group"), 'hash':(hash,"Hash function"), 'key-lifetime':(num,"Maximum key lifetime (ms)"), 'setup-timeout':(num,"Key setup timeout (ms)"), 'setup-retries':(num,"Maximum key setup packet retries"), 'wait-time':(num,"Time to wait after unsuccessful key setup (ms)"), 'renegotiate-time':(num,"Time after key setup to begin renegotiation (ms)"), 'restrict-nets':(networks,"Allowable networks"), 'networks':(networks,"Claimed networks"), 'serial':(serial,"public key set serial"), 'pkg':(listof(somepubkey),"start of public key group",'pub'), 'pkgf':(listof(somepubkey),"start of fallback public key group",'pub'), 'pub':(listof(somepubkey),"new style public site key"), 'pubkey':(listof(somepubkey),"Old-style RSA public site key",'pub'), 'peer':(single_ipaddr,"Tunnel peer IP address"), 'address':(address,"External contact address and port"), 'mobile':(boolean,"Site is mobile"), } def sp(name,value): "Simply output a property - the default case" return "%s %s;\n"%(name,value) # All levels support these properties global_properties={ 'contact':(lambda name,value:"# Contact email address: %s\n"%(value)), 'dh':sp, 'hash':sp, 'key-lifetime':sp, 'setup-timeout':sp, 'setup-retries':sp, 'wait-time':sp, 'renegotiate-time':sp, 'restrict-nets':(lambda name,value:"# restrict-nets %s\n"%value), } class level: "A level in the configuration hierarchy" depth=0 leaf=0 allow_properties={} require_properties={} def __init__(self,w): self.type=w[0].keyword() self.name=w[1].name() self.properties={} self.children={} def indent(self,w,t): w.write(" "[:t]) def prop_out(self,n): return self.allow_properties[n](n,str(self.properties[n])) def output_props(self,w,ind): for i in sorted(self.properties.keys()): if self.allow_properties[i]: self.indent(w,ind) w.write("%s"%self.prop_out(i)) def kname(self): return ((self.type[0].upper() if key_prefix else '') + self.name) def output_data(self,w,path): ind = 2*len(path) self.indent(w,ind) w.write("%s {\n"%(self.kname())) self.output_props(w,ind+2) if self.depth==1: w.write("\n"); for k in sorted(self.children.keys()): c=self.children[k] c.output_data(w,path+(c,)) self.indent(w,ind) w.write("};\n") class vpnlevel(level): "VPN level in the configuration hierarchy" depth=1 leaf=0 type="vpn" allow_properties=global_properties.copy() require_properties={ 'contact':"VPN admin contact address" } def __init__(self,w): level.__init__(self,w) def output_vpnflat(self,w,path): "Output flattened list of site names for this VPN" ind=2*(len(path)+1) self.indent(w,ind) w.write("%s {\n"%(self.kname())) for i in self.children.keys(): self.children[i].output_vpnflat(w,path+(self,)) w.write("\n") self.indent(w,ind+2) w.write("all-sites %s;\n"% ','.join(map(lambda i: i.kname(), self.children.values()))) self.indent(w,ind) w.write("};\n") class locationlevel(level): "Location level in the configuration hierarchy" depth=2 leaf=0 type="location" allow_properties=global_properties.copy() require_properties={ 'contact':"Location admin contact address", } def __init__(self,w): level.__init__(self,w) self.group=w[2].groupname() def output_vpnflat(self,w,path): ind=2*(len(path)+1) self.indent(w,ind) # The "path=path,self=self" abomination below exists because # Python didn't support nested_scopes until version 2.1 # #"/"+self.name+"/"+i w.write("%s %s;\n"%(self.kname(),','.join( map(lambda x,path=path,self=self: '/'.join([prefix+"vpn-data"] + list(map( lambda i: i.kname(), path+(self,x)))), self.children.values())))) class sitelevel(level): "Site level (i.e. a leafnode) in the configuration hierarchy" depth=3 leaf=1 type="site" allow_properties=global_properties.copy() allow_properties.update({ 'address':sp, 'networks':None, 'peer':None, 'serial':None, 'pkg':None, 'pkgf':None, 'pub':None, 'pubkey':None, 'mobile':sp, }) require_properties={ 'dh':"Diffie-Hellman group", 'contact':"Site admin contact address", 'networks':"Networks claimed by the site", 'hash':"hash function", 'peer':"Gateway address of the site", } def mangle_name(self): return self.name.replace('/',',') def pubkeys_path(self): return pubkeys_dir + '/peer.' + self.mangle_name() def __init__(self,w): level.__init__(self,w) def output_data(self,w,path): ind=2*len(path) np='/'.join(map(lambda i: i.name, path)) self.indent(w,ind) w.write("%s {\n"%(self.kname())) self.indent(w,ind+2) w.write("name \"%s\";\n"%(np,)) self.indent(w,ind+2) pkm = pubkeys_mode() debugrepr('pkm ',pkm) pkm.site_start(self.pubkeys_path()) if 'serial' in self.properties: pkm.site_serial(self.properties['serial']) for k in self.properties["pub"].list: debugrepr('pubkeys ', k) pkm.write_key(k) pkm.site_finish(w) self.output_props(w,ind+2) self.indent(w,ind+2) w.write("link netlink {\n"); self.indent(w,ind+4) w.write("routes %s;\n"%str(self.properties["networks"])) self.indent(w,ind+4) w.write("ptp-address %s;\n"%str(self.properties["peer"])) self.indent(w,ind+2) w.write("};\n") self.indent(w,ind) w.write("};\n") # Levels in the configuration file # (depth,properties) levels={'vpn':vpnlevel, 'location':locationlevel, 'site':sitelevel} def complain(msg): "Complain about a particular input line" moan(("%s line %d: "%(file,line))+msg) def moan(msg): "Complain about something in general" global complaints print(msg); if complaints is None: sys.exit(1) complaints=complaints+1 class UntaintedRoot(): def __init__(self,s): self._s=s def name(self): return self._s def keyword(self): return self._s root=level([UntaintedRoot(x) for x in ['root','root']]) # All vpns are children of this node obstack=[root] allow_defs=0 # Level above which new definitions are permitted def set_property(obj,w): "Set a property on a configuration node" prop=w[0] propname=prop.raw_mark_ok() kw=keywords[propname] if len(kw) >= 3: propname=kw[2] # for aliases if propname in obj.properties: obj.properties[propname].add(obj,w) else: obj.properties[propname]=kw[0](w) return obj.properties[propname] class FilterState: def __init__(self): self.reset() def reset(self): # called when we enter a new node, # in particular, at the start of each site self.pkg = '00000000' def pline(il,filterstate,allow_include=False): "Process a configuration file line" global allow_defs, obstack, root w=il.rstrip('\n').split() if len(w)==0: return [''] w=list([Tainted(x) for x in w]) keyword=w[0] current=obstack[len(obstack)-1] copyout_core=lambda: ' '.join([ww.output() for ww in w]) indent=' '*len(obstack) copyout=lambda: [indent + copyout_core() + '\n'] if keyword=='end-definitions': keyword.raw_mark_ok() allow_defs=sitelevel.depth obstack=[root] return copyout() if keyword=='include': if not allow_include: complain("include not permitted here") return [] if len(w) != 2: complain("include requires one argument") return [] newfile=os.path.join(os.path.dirname(file),w[1].raw_mark_ok()) # ^ user of "include" is trusted so raw_mark_ok is good return pfilepath(newfile,allow_include=allow_include) if keyword.raw() in levels: # We may go up any number of levels, but only down by one newdepth=levels[keyword.raw_mark_ok()].depth currentdepth=len(obstack) # actually +1... if newdepth<=currentdepth: obstack=obstack[:newdepth] if newdepth>currentdepth: complain("May not go from level %d to level %d"% (currentdepth-1,newdepth)) # See if it's a new one (and whether that's permitted) # or an existing one current=obstack[len(obstack)-1] tname=w[1].name() if tname in current.children: # Not new current=current.children[tname] if current.depth==2: opmode.check_group(current.group, w) else: # New # Ignore depth check for now nl=levels[keyword.raw()](w) if nl.depth0: if complaints==1: print("There was 1 problem.") else: print("There were %d problems."%(complaints)) sys.exit(1) complaints=None # arranges to crash if we complain later opmode.write_out() work/md5.c0000664000000000000000000002071415063477206007577 0ustar /* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * [I interpet this as a blanket permision -iwj.] * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. * * Changed so as no longer to depend on Colin Plumb's `usual.h' header * definitions; now uses stuff from dpkg's config.h. * - Ian Jackson . * Still in the public domain. */ #include "secnet.h" #include "util.h" #include /* for memcpy() */ #include "md5.h" #ifdef WORDS_BIGENDIAN static void byteSwap(uint32_t *buf, int words) { md5byte *p = (md5byte *)buf; do { *buf++ = (uint32_t)((unsigned)p[3] << 8 | p[2]) << 16 | ((unsigned)p[1] << 8 | p[0]); p += 4; } while (--words); } #else #define byteSwap(buf,words) #endif /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ static void MD5Init(struct MD5Context *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; ctx->buf[2] = 0x98badcfe; ctx->buf[3] = 0x10325476; ctx->bytes[0] = 0; ctx->bytes[1] = 0; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ static void MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len) { uint32_t t; /* Update byte count */ t = ctx->bytes[0]; if ((ctx->bytes[0] = t + len) < t) ctx->bytes[1]++; /* Carry from low to high */ t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ if (t > len) { memcpy((md5byte *)ctx->in + 64 - t, buf, len); return; } /* First chunk is an odd size */ memcpy((md5byte *)ctx->in + 64 - t, buf, t); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); buf += t; len -= t; /* Process data in 64-byte chunks */ while (len >= 64) { memcpy(ctx->in, buf, 64); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ memcpy(ctx->in, buf, len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ static void MD5Final(md5byte digest[16], struct MD5Context *ctx) { int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ md5byte *p = (md5byte *)ctx->in + count; /* Set the first char of padding to 0x80. There is always room. */ *p++ = 0x80; /* Bytes of padding needed to make 56 bytes (-8..55) */ count = 56 - 1 - count; if (count < 0) { /* Padding forces an extra block */ memset(p, 0, count + 8); byteSwap(ctx->in, 16); MD5Transform(ctx->buf, ctx->in); p = (md5byte *)ctx->in; count = 56; } memset(p, 0, count); byteSwap(ctx->in, 14); /* Append length in bits and transform */ ctx->in[14] = ctx->bytes[0] << 3; ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; MD5Transform(ctx->buf, ctx->in); byteSwap(ctx->buf, 4); memcpy(digest, ctx->buf, 16); memset(ctx, 0, sizeof *ctx); /* In case it's sensitive */ } #ifndef ASM_MD5 /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f,w,x,y,z,in,s) \ (w += f(x,y,z) + in, w = (w<>(32-s)) + x) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) { register uint32_t a, b, c, d; a = buf[0]; b = buf[1]; c = buf[2]; d = buf[3]; MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } #endif static void md5_init(void *sst) { struct MD5Context *ctx=sst; MD5Init(ctx); } static void md5_update(void *sst, const void *buf, int32_t len) { struct MD5Context *ctx=sst; MD5Update(ctx,buf,len); } static void md5_final(void *sst, uint8_t *digest) { struct MD5Context *ctx=sst; MD5Final(digest,ctx); } struct md5 { closure_t cl; struct hash_if ops; }; void md5_module(dict_t *dict) { struct md5 *st; cstring_t testinput="12345\n"; uint8_t expected[16]= {0xd5,0x77,0x27,0x3f,0xf8,0x85,0xc3,0xf8, 0x4d,0xad,0xb8,0x57,0x8b,0xb4,0x13,0x99}; uint8_t digest[16]; int i; NEW(st); st->cl.description="md5"; st->cl.type=CL_HASH; st->cl.apply=NULL; st->cl.interface=&st->ops; st->ops.hlen=16; st->ops.slen=sizeof(struct MD5Context); st->ops.init=md5_init; st->ops.update=md5_update; st->ops.final=md5_final; dict_add(dict,"md5",new_closure(&st->cl)); hash_hash(&st->ops,testinput,strlen(testinput),digest); for (i=0; i<16; i++) { if (digest[i]!=expected[i]) { fatal("md5 module failed self-test"); } } } work/md5.h0000664000000000000000000000260015063477206007576 0ustar /* * This is the header file for the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * [I interpet this as a blanket permision -iwj.] * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. * * Changed so as no longer to depend on Colin Plumb's `usual.h' * header definitions; now uses stuff from dpkg's config.h * - Ian Jackson . * Still in the public domain. */ #ifndef MD5_H #define MD5_H #define md5byte unsigned char struct MD5Context { uint32_t buf[4]; uint32_t bytes[2]; uint32_t in[16]; }; static void MD5Init(struct MD5Context *context); static void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len); static void MD5Final(unsigned char digest[16], struct MD5Context *context); static void MD5Transform(uint32_t buf[4], uint32_t const in[16]); #endif /* !MD5_H */ work/modules.c0000664000000000000000000000252215063477206010557 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include "secnet.h" void init_builtin_modules(dict_t *dict) { pubkeys_init(dict); resolver_module(dict); random_module(dict); udp_module(dict); polypath_module(dict); util_module(dict); site_module(dict); transform_eax_module(dict); transform_cbcmac_module(dict); netlink_module(dict); rsa_module(dict); dh_module(dict); md5_module(dict); slip_module(dict); tun_module(dict); sha1_module(dict); log_module(dict); privcache_module(dict); } const struct sigscheme_info sigschemes[]={ { "rsa1", 0x00, rsa1_loadpub, rsa1_loadpriv }, { 0 } }; work/msgcode-test.c0000664000000000000000000000472315063477206011512 0ustar /* * msgcode-test.c: check that the new message encoding is correct */ /* * This file is Free Software. It was originally written for secnet. * * Copyright 2017 Mark Wooding * * You may redistribute secnet as a whole and/or modify it under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 3, or (at your option) any * later version. * * You may redistribute this file and/or modify it under the terms of * the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later * version. * * This software 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 software; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include #include #include #include #include "magic.h" #define OLD_LABEL_NAK 0x00000000 #define OLD_LABEL_MSG0 0x00020200 #define OLD_LABEL_MSG1 0x01010101 #define OLD_LABEL_MSG2 0x02020202 #define OLD_LABEL_MSG3 0x03030303 #define OLD_LABEL_MSG3BIS 0x13030313 #define OLD_LABEL_MSG4 0x04040404 #define OLD_LABEL_MSG5 0x05050505 #define OLD_LABEL_MSG6 0x06060606 #define OLD_LABEL_MSG7 0x07070707 #define OLD_LABEL_MSG8 0x08080808 #define OLD_LABEL_MSG9 0x09090909 #define OLD_LABEL_PROD 0x0a0a0a0a static void check_labels(const char *what, uint32_t new, uint32_t old) { if (old != new) { printf("mismatch for %s: %08"PRIx32" (new) /= %08"PRIx32" (old)\n", what, new, old); exit(2); } } int main(void) { unsigned i, j; uint32_t m, r, s; #define CHECK(label) check_labels(#label, LABEL_##label, OLD_LABEL_##label) CHECK(NAK); CHECK(MSG0); CHECK(MSG1); CHECK(MSG2); CHECK(MSG3); CHECK(MSG3BIS); CHECK(MSG4); CHECK(MSG5); CHECK(MSG6); CHECK(MSG7); CHECK(MSG8); CHECK(MSG9); CHECK(PROD); #undef CHECK for (i = 0; i < 65536; i++) { for (j = 0; j < 65536; j++) { m = MSGCODE(i, j); r = MSGMAJOR(m); s = MSGMINOR(m); if (r != i || s != j) { printf("roundtrip fail: %04x %04x -> %08"PRIx32" " "-> %08"PRIx32" %08"PRIx32"\n", i, j, m, r, s); exit(2); } } } return (0); } work/mtest/0000775000000000000000000000000015063477206010076 5ustar work/mtest/Dir.sd.mk0000664000000000000000000000045515063477206011556 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. &DEPS += &~/make-secnet-sites &DEPS += &~/ipaddrset.py &DEPS += &^/common.tcl &:include test-common.sd.mk &check:: &check-real work/mtest/Ginside.sites0000664000000000000000000000122415063477206012530 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. vpn test-example location inside root site inside networks 172.18.232.8/30 peer 172.18.232.9 address [127.0.0.1] 16910 mobile True pubkey 1024 65537 130064631890186713927887504218626486455931306300999583387009075747001546036643522074275473238061323169592347601185592753550279410171535737146240085267000508853176463710554801101055212967131924064664249613912656320653505750073021702169423354903540699008756137338575553686987244488914481168225136440872431691669 inside@example.com work/mtest/Goutside.sites0000664000000000000000000000120315063477206012726 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. vpn test-example location outside root site outside networks 172.18.232.0/30 peer 172.18.232.1 address [::1] 16900 pubkey 1024 65537 129251483458784900555621175262818292872587807329014927540074484804119474262261383244074013537736576331652560727149001626325243856012659665194546933097292703586821422085819615124517093786704646988649444946154384037948502112302285511195679291084694375811092516151263088200304199780052361048758446082354317801941 outside@example.com work/mtest/common.tcl0000664000000000000000000000227715063477206012102 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. source test-common.tcl proc mss-program {} { global env set l ./make-secnet-sites if {![catch { set py $env(MTEST_PYTHON) }]} { set l [concat $py $l] } return $l } proc run-mss-userv {user group args} { eval [list prexec env USERV_USER=$user USERV_GROUP=$group] \ [mss-program] \ $args } proc run-mss {args} { eval [list prexec] [mss-program] $args } proc diff {a b seddery {sedderyb X}} { if {![string compare $sedderyb X]} { set sedderyb $seddery } puts "$a $b $seddery $sedderyb" exec bash -c " diff -u <( <$a $seddery ) \\ <( <$b $sedderyb ) " } proc diff-output {expected got suffix} { global seddery global tmp diff mtest/$expected$suffix $tmp/$got$suffix $seddery } proc diff-mss-sites-conf {expected got} { set seddery { sed -n 's/^[ \t]*//; /^[^#]/p' } diff $expected $got $seddery } file mkdir $tmp/groupfiles set env(PYTHONHASHSEED) 0 set env(PYTHONBYTECODEBASE) 0 set seddery { sed -n 's/^[ \t]*//; /^[^#]/p' } prefix_some_path PYTHONPATH . work/mtest/delegations.sites0000664000000000000000000000043315063477206013445 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. location outside Goutside restrict-nets 172.18.232.0/29 location inside Ginside restrict-nets 172.18.232.8/29 work/mtest/e-basic.conf0000664000000000000000000000272715063477206012260 0ustar # secnet sites file autogenerated by make-secnet-sites version 0.1.18 # Sat Dec 7 17:16:13 2019 # Command line: ./make-secnet-sites --no-conf-key-prefix test-example/sites /home/ian/things/Fvpn/secnet/mtest/d-basic/out.conf vpn-data { test-example { # Contact email address: dh diffie-hellman("8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3","2"); hash sha1; key-lifetime 72000000; # restrict-nets "172.18.232.0/28" setup-retries 5; setup-timeout 2000; in { inside { name "test-example/in/inside"; peer-keys "/var/lib/secnet/pubkeys/peer.inside"; address "[127.0.0.1]"; port 16910; mobile True; link netlink { routes "172.18.232.8/29"; ptp-address "172.18.232.9"; }; }; }; out { outside { name "test-example/out/outside"; peer-keys "/var/lib/secnet/pubkeys/peer.outside"; address "[::1]"; port 16900; link netlink { routes "172.18.232.0/29"; ptp-address "172.18.232.1"; }; }; }; }; }; vpn { test-example { out vpn-data/test-example/out/outside; in vpn-data/test-example/in/inside; all-sites out,in; }; }; all-sites vpn/test-example/all-sites; work/mtest/e-filter.sites0000664000000000000000000000511415063477206012657 0ustar # sites file autogenerated by make-secnet-sites # --filter --output-version=1 # use make-secnet-sites to turn this file into a # valid /etc/secnet/sites.conf file vpn test-example contact devnull@example.com dh 8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3 2 hash sha1 key-lifetime 72000000 restrict-nets 172.18.232.0/28 setup-timeout 2000 setup-retries 5 location out root site outside networks 172.18.232.0/29 peer 172.18.232.1 address [::1] 16900 #serial 5dcfe8e9 #pkg 5dc36a47 #pub rsa1 zt1E8Wddh26=^XgSyt2x2i)G7I5=Z[*T:4(aml1Xs1UxvnCS81>:tN$Ss!9E;mSuAJq@)YAS=4CF^heMR2=:_@7RVz>x2iooQJq@^jKUB29ECk5X7ID?N[*Tu!Yb:msMQJ9=EkgS37ux;m]i<1::rvKURztEbjkMQJD?OCmTb%tE;miM;Is@KC7RJ)gbWoiM$JD?iCFTQz`E4iVB$Jq@PvlT[+gb2i0o@JG?eChSpwQbllESQJ::xN+Tb%1ElluMs16+26FT-1;aLm[zAJm/26{Q[+),;m+GR2S*&6{Q-1_,N #pub unknown-algo TPwJh>A #pkgf 00000000 pubkey 1024 65537 129251483458784900555621175262818292872587807329014927540074484804119474262261383244074013537736576331652560727149001626325243856012659665194546933097292703586821422085819615124517093786704646988649444946154384037948502112302285511195679291084694375811092516151263088200304199780052361048758446082354317801941 location in root site inside networks 172.18.232.8/29 peer 172.18.232.9 address [127.0.0.1] 16910 mobile True pubkey 1024 65537 130064631890186713927887504218626486455931306300999583387009075747001546036643522074275473238061323169592347601185592753550279410171535737146240085267000508853176463710554801101055212967131924064664249613912656320653505750073021702169423354903540699008756137338575553686987244488914481168225136440872431691669 # end of sites file work/mtest/e-userv.conf0000664000000000000000000000265715063477206012345 0ustar # secnet sites file autogenerated by make-secnet-sites version 0.1.18 # Tue Mar 10 17:51:56 2020 # Command line: ./make-secnet-sites vpn-data { Vtest-example { # Contact email address: dh diffie-hellman("8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3","2"); hash sha1; key-lifetime 72000000; # restrict-nets "10.0.0.0/8","172.16.0.0/12","192.168.0.0/16" setup-retries 5; setup-timeout 2000; Linside { # restrict-nets "172.18.232.8/29" Sinside { name "test-example/inside/inside"; key rsa-public("65537","130064631890186713927887504218626486455931306300999583387009075747001546036643522074275473238061323169592347601185592753550279410171535737146240085267000508853176463710554801101055212967131924064664249613912656320653505750073021702169423354903540699008756137338575553686987244488914481168225136440872431691669"); address "[127.0.0.1]"; port 16910; mobile True; link netlink { routes "172.18.232.8/30"; ptp-address "172.18.232.9"; }; }; }; }; }; vpn { Vtest-example { Linside vpn-data/Vtest-example/Linside/Sinside; all-sites Linside; }; }; all-sites vpn/Vtest-example/all-sites; work/mtest/e-userv.sites0000664000000000000000000000252215063477206012536 0ustar # sites file autogenerated by make-secnet-sites # generated Sun Oct 20 13:21:06 2019, invoked by Uuser # use make-secnet-sites to turn this file into a # valid /etc/secnet/sites.conf file vpn test-example contact header@example.com dh 8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3 2 hash sha1 key-lifetime 72000000 restrict-nets 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12 setup-timeout 2000 setup-retries 5 location outside Goutside restrict-nets 172.18.232.0/29 location inside Ginside restrict-nets 172.18.232.8/29 end-definitions # Section submitted by user Uuser, Sun Oct 20 13:21:06 2019 # Checked by make-secnet-sites version 0.1.18 vpn test-example location inside root site inside networks 172.18.232.8/30 peer 172.18.232.9 address [127.0.0.1] 16910 mobile True pubkey 1024 65537 130064631890186713927887504218626486455931306300999583387009075747001546036643522074275473238061323169592347601185592753550279410171535737146240085267000508853176463710554801101055212967131924064664249613912656320653505750073021702169423354903540699008756137338575553686987244488914481168225136440872431691669 inside@example.com # end of sites file work/mtest/header.sites0000664000000000000000000000120415063477206012374 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. vpn test-example contact header@example.com dh 8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3 2 hash sha1 key-lifetime 72000000 restrict-nets 192.168.0.0/16 10.0.0.0/8 172.16.0.0/12 setup-timeout 2000 setup-retries 5 include delegations.sites end-definitions work/mtest/t-basic0000775000000000000000000000053415063477206011350 0ustar #! /usr/bin/tclsh # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. source mtest/common.tcl run-mss --no-conf-key-prefix --pubkeys-elide test-example/sites $tmp/out.conf diff-mss-sites-conf mtest/e-basic.conf $tmp/out.conf work/mtest/t-filter0000775000000000000000000000052115063477206011550 0ustar #! /usr/bin/tclsh # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. source mtest/common.tcl run-mss --filter --output-version=1 test-example/sites $tmp/out.sites diff mtest/e-filter.sites $tmp/out.sites $seddery work/mtest/t-prefix0000775000000000000000000000065515063477206011570 0ustar #! /usr/bin/tclsh # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. source mtest/common.tcl run-mss -Ppprefix --no-conf-key-prefix --pubkeys-elide test-example/sites $tmp/out.conf diff mtest/e-basic.conf $tmp/out.conf \ "sed -e 's/vpn/pprefixvpn/g; s/^all-sites/pprefix&/' | $seddery" \ $seddery work/mtest/t-userv0000775000000000000000000000274615063477206011442 0ustar #! /usr/bin/tclsh # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. source mtest/common.tcl #----- success test ----- set good [list Uuser Ginside -u \ mtest/header.sites $tmp/groupfiles $tmp/out.sites Ginside \ < mtest/Ginside.sites] eval run-mss-userv $good diff-output e-userv out .sites #----- argument parser does not look for args beyond header ----- set env(LC_MESSAGES) C set try [lreplace $good 4 4 --misparse-test] if {![catch { eval run-mss-userv $try } emsg]} { error "should have failed" } else { switch -glob $emsg { {*unrecognized arguments: --misparse-test*} { error "misparsed!" } {*No such file or directory: '--misparse-test/TGinside'*} { } * { error "huh ? $emsg" } } } #----- wrong group is rejected ----- set bad [lreplace $good 1 1 Gwrong] if {![catch { eval run-mss-userv $bad } emsg]} { error "should have failed" } else { switch -glob $emsg { {*caller not in group Ginside*} { } * { error "huh ? $emsg" } } } #----- resulting sites file is processable ----- eval run-mss $tmp/out.sites >$tmp/out.conf diff-mss-sites-conf mtest/e-userv.conf $tmp/out.conf #----- works without group in `location' line ----- set testgf $tmp/Ginside.no-group exec sed < mtest/Ginside.sites > $testgf {/^location / s/ root$//} set testargs [lreplace $good end end $testgf] eval run-mss-userv $testargs work/netlink.c0000664000000000000000000011653515063477206010565 0ustar /* User-kernel network link */ /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ /* See RFCs 791, 792, 1123 and 1812 */ /* The netlink device is actually a router. Tunnels are unnumbered point-to-point lines (RFC1812 section 2.2.7); the router has a single address (the 'router-id'). */ /* This is where we currently have the anti-spoofing paranoia - before sending a packet to the kernel we check that the tunnel it came over could reasonably have produced it. */ /* Points to note from RFC1812 (which may require changes in this file): 3.3.4 Maximum Transmission Unit - MTU The MTU of each logical interface MUST be configurable within the range of legal MTUs for the interface. Many Link Layer protocols define a maximum frame size that may be sent. In such cases, a router MUST NOT allow an MTU to be set which would allow sending of frames larger than those allowed by the Link Layer protocol. However, a router SHOULD be willing to receive a packet as large as the maximum frame size even if that is larger than the MTU. 4.2.1 A router SHOULD count datagrams discarded. 4.2.2.1 Source route options - we probably should implement processing of source routes, even though mostly the security policy will prevent their use. 5.3.13.4 Source Route Options A router MUST implement support for source route options in forwarded packets. A router MAY implement a configuration option that, when enabled, causes all source-routed packets to be discarded. However, such an option MUST NOT be enabled by default. 5.3.13.5 Record Route Option Routers MUST support the Record Route option in forwarded packets. A router MAY provide a configuration option that, if enabled, will cause the router to ignore (i.e., pass through unchanged) Record Route options in forwarded packets. If provided, such an option MUST default to enabling the record-route. This option should not affect the processing of Record Route options in datagrams received by the router itself (in particular, Record Route options in ICMP echo requests will still be processed according to Section [4.3.3.6]). 5.3.13.6 Timestamp Option Routers MUST support the timestamp option in forwarded packets. A timestamp value MUST follow the rules given [INTRO:2]. If the flags field = 3 (timestamp and prespecified address), the router MUST add its timestamp if the next prespecified address matches any of the router's IP addresses. It is not necessary that the prespecified address be either the address of the interface on which the packet arrived or the address of the interface over which it will be sent. 4.2.2.7 Fragmentation: RFC 791 Section 3.2 Fragmentation, as described in [INTERNET:1], MUST be supported by a router. 4.2.2.8 Reassembly: RFC 791 Section 3.2 As specified in the corresponding section of [INTRO:2], a router MUST support reassembly of datagrams that it delivers to itself. 4.2.2.9 Time to Live: RFC 791 Section 3.2 Note in particular that a router MUST NOT check the TTL of a packet except when forwarding it. A router MUST NOT discard a datagram just because it was received with TTL equal to zero or one; if it is to the router and otherwise valid, the router MUST attempt to receive it. On messages the router originates, the IP layer MUST provide a means for the transport layer to set the TTL field of every datagram that is sent. When a fixed TTL value is used, it MUST be configurable. 8.1 The Simple Network Management Protocol - SNMP 8.1.1 SNMP Protocol Elements Routers MUST be manageable by SNMP [MGT:3]. The SNMP MUST operate using UDP/IP as its transport and network protocols. */ #include #include #include #include "secnet.h" #include "util.h" #include "ipaddr.h" #include "netlink.h" #include "process.h" #ifdef NETLINK_DEBUG #define MDEBUG(...) Message(M_DEBUG, __VA_ARGS__) #else /* !NETLINK_DEBUG */ #define MDEBUG(...) ((void)0) #endif /* !NETLINK_DEBUG */ #define ICMP_TYPE_ECHO_REPLY 0 #define ICMP_TYPE_UNREACHABLE 3 #define ICMP_CODE_NET_UNREACHABLE 0 #define ICMP_CODE_PROTOCOL_UNREACHABLE 2 #define ICMP_CODE_FRAGMENTATION_REQUIRED 4 #define ICMP_CODE_NET_PROHIBITED 13 #define ICMP_TYPE_ECHO_REQUEST 8 #define ICMP_TYPE_TIME_EXCEEDED 11 #define ICMP_CODE_TTL_EXCEEDED 0 /* Generic IP checksum routine */ static inline uint16_t ip_csum(const uint8_t *iph,int32_t count) { register uint32_t sum=0; while (count>1) { sum+=ntohs(*(uint16_t *)iph); iph+=2; count-=2; } if(count>0) sum+=*(uint8_t *)iph; while (sum>>16) sum=(sum&0xffff)+(sum>>16); return htons(~sum); } #ifdef i386 /* * This is a version of ip_compute_csum() optimized for IP headers, * which always checksum on 4 octet boundaries. * * By Jorge Cwik , adapted for linux by * Arnt Gulbrandsen. */ static inline uint16_t ip_fast_csum(const uint8_t *iph, int32_t ihl) { uint32_t sum; __asm__ __volatile__( "movl (%1), %0 ;\n" "subl $4, %2 ;\n" "jbe 2f ;\n" "addl 4(%1), %0 ;\n" "adcl 8(%1), %0 ;\n" "adcl 12(%1), %0 ;\n" "1: adcl 16(%1), %0 ;\n" "lea 4(%1), %1 ;\n" "decl %2 ;\n" "jne 1b ;\n" "adcl $0, %0 ;\n" "movl %0, %2 ;\n" "shrl $16, %0 ;\n" "addw %w2, %w0 ;\n" "adcl $0, %0 ;\n" "notl %0 ;\n" "2: ;\n" /* Since the input registers which are loaded with iph and ipl are modified, we must also specify them as outputs, or gcc will assume they contain their original values. */ : "=r" (sum), "=r" (iph), "=r" (ihl) : "1" (iph), "2" (ihl) : "memory"); return sum; } #else static inline uint16_t ip_fast_csum(const uint8_t *iph, int32_t ihl) { assert(ihl < INT_MAX/4); return ip_csum(iph,ihl*4); } #endif struct iphdr { #if defined (WORDS_BIGENDIAN) uint8_t version:4, ihl:4; #else uint8_t ihl:4, version:4; #endif uint8_t tos; uint16_t tot_len; uint16_t id; uint16_t frag; #define IPHDR_FRAG_OFF ((uint16_t)0x1fff) #define IPHDR_FRAG_MORE ((uint16_t)0x2000) #define IPHDR_FRAG_DONT ((uint16_t)0x4000) /* reserved 0x8000 */ uint8_t ttl; uint8_t protocol; uint16_t check; uint32_t saddr; uint32_t daddr; /* The options start here. */ }; struct icmphdr { struct iphdr iph; uint8_t type; uint8_t code; uint16_t check; union icmpinfofield { uint32_t unused; struct { uint8_t pointer; uint8_t unused1; uint16_t unused2; } pprob; uint32_t gwaddr; struct { uint16_t id; uint16_t seq; } echo; struct { uint16_t unused; uint16_t mtu; } fragneeded; } d; }; static const union icmpinfofield icmp_noinfo; static void netlink_client_deliver(struct netlink *st, struct netlink_client *client, uint32_t source, uint32_t dest, struct buffer_if *buf); static void netlink_host_deliver(struct netlink *st, struct netlink_client *sender, uint32_t source, uint32_t dest, struct buffer_if *buf); static const char *sender_name(struct netlink_client *sender /* or NULL */) { return sender?sender->name:"(local)"; } static void netlink_packet_deliver(struct netlink *st, struct netlink_client *client, struct buffer_if *buf); /* XXX RFC1812 4.3.2.5: All other ICMP error messages (Destination Unreachable, Redirect, Time Exceeded, and Parameter Problem) SHOULD have their precedence value set to 6 (INTERNETWORK CONTROL) or 7 (NETWORK CONTROL). The IP Precedence value for these error messages MAY be settable. */ static struct icmphdr *netlink_icmp_tmpl(struct netlink *st, uint32_t source, uint32_t dest, uint16_t len) { struct icmphdr *h; BUF_ALLOC(&st->icmp,"netlink_icmp_tmpl"); buffer_init(&st->icmp,calculate_max_start_pad()); h=buf_append(&st->icmp,sizeof(*h)); h->iph.version=4; h->iph.ihl=5; h->iph.tos=0; h->iph.tot_len=htons(len+(h->iph.ihl*4)+8); h->iph.id=0; h->iph.frag=0; h->iph.ttl=255; /* XXX should be configurable */ h->iph.protocol=1; h->iph.saddr=htonl(source); h->iph.daddr=htonl(dest); h->iph.check=0; h->iph.check=ip_fast_csum((uint8_t *)&h->iph,h->iph.ihl); h->check=0; h->d.unused=0; return h; } /* Fill in the ICMP checksum field correctly */ static void netlink_icmp_csum(struct icmphdr *h) { int32_t len; len=ntohs(h->iph.tot_len)-(4*h->iph.ihl); h->check=0; h->check=ip_csum(&h->type,len); } /* RFC1122: * An ICMP error message MUST NOT be sent as the result of * receiving: * * * an ICMP error message, or * * * a datagram destined to an IP broadcast or IP multicast * address, or * * * a datagram sent as a link-layer broadcast, or * * * a non-initial fragment, or * * * a datagram whose source address does not define a single * host -- e.g., a zero address, a loopback address, a * broadcast address, a multicast address, or a Class E * address. */ static bool_t netlink_icmp_may_reply(struct buffer_if *buf) { struct iphdr *iph; struct icmphdr *icmph; uint32_t source; if (buf->size < (int)sizeof(struct icmphdr)) return False; iph=(struct iphdr *)buf->start; icmph=(struct icmphdr *)buf->start; if (iph->protocol==1) { switch(icmph->type) { /* Based on http://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-types * as retrieved Thu, 20 Mar 2014 00:16:44 +0000. * Deprecated, reserved, unassigned and experimental * options are treated as not safe to reply to. */ case 0: /* Echo Reply */ case 8: /* Echo */ case 13: /* Timestamp */ case 14: /* Timestamp Reply */ return True; default: return False; } } /* How do we spot broadcast destination addresses? */ if (ntohs(iph->frag)&IPHDR_FRAG_OFF) return False; source=ntohl(iph->saddr); if (source==0) return False; if ((source&0xff000000)==0x7f000000) return False; /* How do we spot broadcast source addresses? */ if ((source&0xf0000000)==0xe0000000) return False; /* Multicast */ if ((source&0xf0000000)==0xf0000000) return False; /* Class E */ return True; } /* How much of the original IP packet do we include in its ICMP response? The header plus up to 64 bits. */ /* XXX TODO RFC1812: 4.3.2.3 Original Message Header Historically, every ICMP error message has included the Internet header and at least the first 8 data bytes of the datagram that triggered the error. This is no longer adequate, due to the use of IP-in-IP tunneling and other technologies. Therefore, the ICMP datagram SHOULD contain as much of the original datagram as possible without the length of the ICMP datagram exceeding 576 bytes. The returned IP header (and user data) MUST be identical to that which was received, except that the router is not required to undo any modifications to the IP header that are normally performed in forwarding that were performed before the error was detected (e.g., decrementing the TTL, or updating options). Note that the requirements of Section [4.3.3.5] supersede this requirement in some cases (i.e., for a Parameter Problem message, if the problem is in a modified field, the router must undo the modification). See Section [4.3.3.5]). */ static uint16_t netlink_icmp_reply_len(struct buffer_if *buf) { if (buf->size < (int)sizeof(struct iphdr)) return 0; struct iphdr *iph=(struct iphdr *)buf->start; uint16_t hlen,plen; hlen=iph->ihl*4; /* We include the first 8 bytes of the packet data, provided they exist */ hlen+=8; plen=ntohs(iph->tot_len); return MIN(hlen,plen); } /* client indicates where the packet we're constructing a response to comes from. NULL indicates the host. */ static void netlink_icmp_simple(struct netlink *st, struct netlink_client *origsender, struct buffer_if *buf, uint8_t type, uint8_t code, union icmpinfofield info) { struct icmphdr *h; uint16_t len; if (netlink_icmp_may_reply(buf)) { struct iphdr *iph=(struct iphdr *)buf->start; uint32_t icmpdest = ntohl(iph->saddr); uint32_t icmpsource; const char *icmpsourcedebugprefix; if (!st->ptp) { icmpsource=st->secnet_address; icmpsourcedebugprefix=""; } else if (origsender) { /* was from peer, send reply as if from host */ icmpsource=st->local_address; icmpsourcedebugprefix="L!"; } else { /* was from host, send reply as if from peer */ icmpsource=st->secnet_address; /* actually, peer address */ icmpsourcedebugprefix="P!"; } MDEBUG("%s: generating ICMP re %s[%s]->[%s]:" " from %s%s type=%u code=%u\n", st->name, sender_name(origsender), ipaddr_to_string(ntohl(iph->saddr)), ipaddr_to_string(ntohl(iph->daddr)), icmpsourcedebugprefix, ipaddr_to_string(icmpsource), type, code); len=netlink_icmp_reply_len(buf); h=netlink_icmp_tmpl(st,icmpsource,icmpdest,len); h->type=type; h->code=code; h->d=info; BUF_ADD_BYTES(append,&st->icmp,buf->start,len); netlink_icmp_csum(h); if (!st->ptp) { netlink_packet_deliver(st,NULL,&st->icmp); } else if (origsender) { netlink_client_deliver(st,origsender,icmpsource,icmpdest,&st->icmp); } else { netlink_host_deliver(st,NULL,icmpsource,icmpdest,&st->icmp); } BUF_ASSERT_FREE(&st->icmp); } } /* * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the * checksum. * RFC1812: 4.2.2.5 MUST discard messages containing invalid checksums. * * Is the datagram acceptable? * * 1. Length at least the size of an ip header * 2. Version of 4 * 3. Checksums correctly. * 4. Doesn't have a bogus length */ static bool_t netlink_check(struct netlink *st, struct buffer_if *buf, char *errmsgbuf, int errmsgbuflen) { #define BAD(...) do{ \ snprintf(errmsgbuf,errmsgbuflen,__VA_ARGS__); \ return False; \ }while(0) if (buf->size < (int)sizeof(struct iphdr)) BAD("len %"PRIu32"",buf->size); struct iphdr *iph=(struct iphdr *)buf->start; int32_t len; if (iph->version != 4) BAD("version %u",iph->version); if (iph->ihl < 5) BAD("ihl %u",iph->ihl); if (buf->size < iph->ihl*4) BAD("size %"PRId32"<%u*4",buf->size,iph->ihl); if (ip_fast_csum((uint8_t *)iph, iph->ihl)!=0) BAD("csum"); len=ntohs(iph->tot_len); /* There should be no padding */ if (buf->size!=len) BAD("len %"PRId32"!=%"PRId32,buf->size,len); if (len<(iph->ihl<<2)) BAD("len %"PRId32"<(%u<<2)",len,iph->ihl); /* XXX check that there's no source route specified */ return True; #undef BAD } static const char *fragment_filter_header(uint8_t *base, long *hlp) { const int fixedhl = sizeof(struct iphdr); long hl = *hlp; const uint8_t *ipend = base + hl; uint8_t *op = base + fixedhl; const uint8_t *ip = op; while (ip < ipend) { uint8_t opt = ip[0]; int remain = ipend - ip; if (opt == 0x00) /* End of Options List */ break; if (opt == 0x01) /* No Operation */ continue; if (remain < 2) return "IPv4 options truncated at length"; int optlen = ip[1]; if (remain < optlen) return "IPv4 options truncated in option"; if (opt & 0x80) /* copy */ { memmove(op, ip, optlen); op += optlen; } ip += optlen; } while ((hl = (op - base)) & 0x3) *op++ = 0x00 /* End of Option List */; ((struct iphdr*)base)->ihl = hl >> 2; *hlp = hl; return 0; } /* Fragment or send ICMP Fragmentation Needed */ static void netlink_maybe_fragment(struct netlink *st, struct netlink_client *sender, netlink_deliver_fn *deliver, void *deliver_dst, const char *delivery_name, int32_t mtu, uint32_t source, uint32_t dest, struct buffer_if *buf) { struct iphdr *iph=(struct iphdr*)buf->start; long hl = iph->ihl*4; const char *ssource = ipaddr_to_string(source); if (buf->size <= mtu) { deliver(deliver_dst, buf); return; } MDEBUG("%s: fragmenting %s->%s org.size=%"PRId32"\n", st->name, ssource, delivery_name, buf->size); #define BADFRAG(m, ...) \ Message(M_WARNING, \ "%s: fragmenting packet from source %s" \ " for transmission via %s: " m "\n", \ st->name, ssource, delivery_name, \ ## __VA_ARGS__); unsigned orig_frag = ntohs(iph->frag); if (orig_frag&IPHDR_FRAG_DONT) { union icmpinfofield info = { .fragneeded = { .unused = 0, .mtu = htons(mtu) } }; netlink_icmp_simple(st,sender,buf, ICMP_TYPE_UNREACHABLE, ICMP_CODE_FRAGMENTATION_REQUIRED, info); BUF_FREE(buf); return; } if (mtu < hl + 8) { BADFRAG("mtu %"PRId32" too small", mtu); BUF_FREE(buf); return; } /* we (ab)use the icmp buffer to stash the original packet */ struct buffer_if *orig = &st->icmp; BUF_ALLOC(orig,"netlink_client_deliver fragment orig"); buffer_copy(orig,buf); BUF_FREE(buf); const uint8_t *startindata = orig->start + hl; const uint8_t *indata = startindata; const uint8_t *endindata = orig->start + orig->size; _Bool filtered = 0; for (;;) { /* compute our fragment offset */ long dataoffset = indata - startindata + (orig_frag & IPHDR_FRAG_OFF)*8; assert(!(dataoffset & 7)); if (dataoffset > IPHDR_FRAG_OFF*8) { BADFRAG("ultimate fragment offset out of range"); break; } BUF_ALLOC(buf,"netlink_client_deliver fragment frag"); buffer_init(buf,calculate_max_start_pad()); /* copy header (possibly filtered); will adjust in a bit */ struct iphdr *fragh = buf_append(buf, hl); memcpy(fragh, orig->start, hl); /* decide how much payload to copy and copy it */ long avail = mtu - hl; long remain = endindata - indata; long use = avail < remain ? (avail & ~(long)7) : remain; BUF_ADD_BYTES(append, buf, indata, use); indata += use; _Bool last_frag = indata >= endindata; /* adjust the header */ fragh->tot_len = htons(buf->size); fragh->frag = htons((orig_frag & ~IPHDR_FRAG_OFF) | (last_frag ? 0 : IPHDR_FRAG_MORE) | (dataoffset >> 3)); fragh->check = 0; fragh->check = ip_fast_csum((const void*)fragh, fragh->ihl); /* actually send it */ deliver(deliver_dst, buf); if (last_frag) break; /* after copying the header for the first frag, * we filter the header for the remaining frags */ if (!filtered++) { const char *bad = fragment_filter_header(orig->start, &hl); if (bad) { BADFRAG("%s", bad); break; } } } BUF_FREE(orig); #undef BADFRAG } /* Deliver a packet _to_ client; used after we have decided * what to do with it (and just to check that the client has * actually registered a delivery function with us). */ static void netlink_client_deliver(struct netlink *st, struct netlink_client *client, uint32_t source, uint32_t dest, struct buffer_if *buf) { if (!client->deliver) { string_t s,d; s=ipaddr_to_string(source); d=ipaddr_to_string(dest); Message(M_ERR,"%s: dropping %s->%s, client not registered\n", st->name,s,d); BUF_FREE(buf); return; } netlink_maybe_fragment(st,NULL, client->deliver,client->dst,client->name, client->mtu, source,dest,buf); client->outcount++; } /* Deliver a packet to the host; used after we have decided that that * is what to do with it. */ static void netlink_host_deliver(struct netlink *st, struct netlink_client *sender, uint32_t source, uint32_t dest, struct buffer_if *buf) { netlink_maybe_fragment(st,sender, st->deliver_to_host,st->dst,"(host)", st->mtu, source,dest,buf); st->outcount++; } /* Deliver a packet. "sender"==NULL for packets from the host and packets generated internally in secnet. */ static void netlink_packet_deliver(struct netlink *st, struct netlink_client *sender, struct buffer_if *buf) { if (buf->size < (int)sizeof(struct iphdr)) { Message(M_ERR,"%s: trying to deliver a too-short packet" " from %s!\n",st->name, sender_name(sender)); BUF_FREE(buf); return; } struct iphdr *iph=(struct iphdr *)buf->start; uint32_t dest=ntohl(iph->daddr); uint32_t source=ntohl(iph->saddr); uint32_t best_quality; bool_t allow_route=False; bool_t found_allowed=False; int best_match; int i; BUF_ASSERT_USED(buf); if (dest==st->secnet_address) { Message(M_ERR,"%s: trying to deliver a packet to myself!\n",st->name); BUF_FREE(buf); return; } /* Packets from the host (sender==NULL) may always be routed. Packets from clients with the allow_route option will also be routed. */ if (!sender || (sender && (sender->options & OPT_ALLOWROUTE))) allow_route=True; /* If !allow_route, we check the routing table anyway, and if there's a suitable route with OPT_ALLOWROUTE set we use it. If there's a suitable route, but none with OPT_ALLOWROUTE set then we generate ICMP 'communication with destination network administratively prohibited'. */ best_quality=0; best_match=-1; for (i=0; in_clients; i++) { if (st->routes[i]->up && ipset_contains_addr(st->routes[i]->networks,dest)) { /* It's an available route to the correct destination. But is it better than the one we already have? */ /* If we have already found an allowed route then we don't bother looking at routes we're not allowed to use. If we don't yet have an allowed route we'll consider any. */ if (!allow_route && found_allowed) { if (!(st->routes[i]->options&OPT_ALLOWROUTE)) continue; } if (st->routes[i]->link_quality>best_quality || best_quality==0) { best_quality=st->routes[i]->link_quality; best_match=i; if (st->routes[i]->options&OPT_ALLOWROUTE) found_allowed=True; /* If quality isn't perfect we may wish to consider kicking the tunnel with a 0-length packet to prompt it to perform a key setup. Then it'll eventually decide it's up or down. */ /* If quality is perfect and we're allowed to use the route we don't need to search any more. */ if (best_quality>=MAXIMUM_LINK_QUALITY && (allow_route || found_allowed)) break; } } } if (best_match==-1) { /* The packet's not going down a tunnel. It might (ought to) be for the host. */ if (ipset_contains_addr(st->networks,dest)) { netlink_host_deliver(st,sender,source,dest,buf); BUF_ASSERT_FREE(buf); } else { string_t s,d; s=ipaddr_to_string(source); d=ipaddr_to_string(dest); Message(M_DEBUG,"%s: don't know where to deliver packet " "(s=%s, d=%s)\n", st->name, s, d); netlink_icmp_simple(st,sender,buf,ICMP_TYPE_UNREACHABLE, ICMP_CODE_NET_UNREACHABLE, icmp_noinfo); BUF_FREE(buf); } } else { if (!allow_route && !(st->routes[best_match]->options&OPT_ALLOWROUTE)) { string_t s,d; s=ipaddr_to_string(source); d=ipaddr_to_string(dest); /* We have a usable route but aren't allowed to use it. Generate ICMP destination unreachable: communication with destination network administratively prohibited */ Message(M_NOTICE,"%s: denied forwarding for packet (s=%s, d=%s)\n", st->name,s,d); netlink_icmp_simple(st,sender,buf,ICMP_TYPE_UNREACHABLE, ICMP_CODE_NET_PROHIBITED, icmp_noinfo); BUF_FREE(buf); } else { if (best_quality>0) { netlink_client_deliver(st,st->routes[best_match], source,dest,buf); BUF_ASSERT_FREE(buf); } else { /* Generate ICMP destination unreachable */ netlink_icmp_simple(st,sender,buf, ICMP_TYPE_UNREACHABLE, ICMP_CODE_NET_UNREACHABLE, icmp_noinfo); BUF_FREE(buf); } } } BUF_ASSERT_FREE(buf); } static void netlink_packet_forward(struct netlink *st, struct netlink_client *sender, struct buffer_if *buf) { if (buf->size < (int)sizeof(struct iphdr)) return; struct iphdr *iph=(struct iphdr *)buf->start; BUF_ASSERT_USED(buf); /* Packet has already been checked */ if (iph->ttl<=1) { /* Generate ICMP time exceeded */ netlink_icmp_simple(st,sender,buf,ICMP_TYPE_TIME_EXCEEDED, ICMP_CODE_TTL_EXCEEDED,icmp_noinfo); BUF_FREE(buf); return; } iph->ttl--; iph->check=0; iph->check=ip_fast_csum((uint8_t *)iph,iph->ihl); netlink_packet_deliver(st,sender,buf); BUF_ASSERT_FREE(buf); } /* Deal with packets addressed explicitly to us */ static void netlink_packet_local(struct netlink *st, struct netlink_client *sender, struct buffer_if *buf) { struct icmphdr *h; st->localcount++; if (buf->size < (int)sizeof(struct icmphdr)) { Message(M_WARNING,"%s: short packet addressed to secnet; " "ignoring it\n",st->name); BUF_FREE(buf); return; } h=(struct icmphdr *)buf->start; unsigned fraginfo = ntohs(h->iph.frag); if ((fraginfo&(IPHDR_FRAG_OFF|IPHDR_FRAG_MORE))!=0) { if (!(fraginfo & IPHDR_FRAG_OFF)) /* report only for first fragment */ Message(M_WARNING,"%s: fragmented packet addressed to secnet; " "ignoring it\n",st->name); BUF_FREE(buf); return; } if (h->iph.protocol==1) { /* It's ICMP */ if (h->type==ICMP_TYPE_ECHO_REQUEST && h->code==0) { /* ICMP echo-request. Special case: we re-use the buffer to construct the reply. */ h->type=ICMP_TYPE_ECHO_REPLY; h->iph.daddr=h->iph.saddr; h->iph.saddr=htonl(st->secnet_address); h->iph.ttl=255; h->iph.check=0; h->iph.check=ip_fast_csum((uint8_t *)h,h->iph.ihl); netlink_icmp_csum(h); netlink_packet_deliver(st,NULL,buf); return; } Message(M_WARNING,"%s: unknown incoming ICMP\n",st->name); } else { /* Send ICMP protocol unreachable */ netlink_icmp_simple(st,sender,buf,ICMP_TYPE_UNREACHABLE, ICMP_CODE_PROTOCOL_UNREACHABLE,icmp_noinfo); BUF_FREE(buf); return; } BUF_FREE(buf); } /* If cid==NULL packet is from host, otherwise cid specifies which tunnel it came from. */ static void netlink_incoming(struct netlink *st, struct netlink_client *sender, struct buffer_if *buf) { uint32_t source,dest; struct iphdr *iph; char errmsgbuf[50]; const char *sourcedesc=sender?sender->name:"host"; BUF_ASSERT_USED(buf); if (!netlink_check(st,buf,errmsgbuf,sizeof(errmsgbuf))) { Message(M_WARNING,"%s: bad IP packet from %s: %s\n", st->name,sourcedesc, errmsgbuf); BUF_FREE(buf); return; } assert(buf->size >= (int)sizeof(struct iphdr)); iph=(struct iphdr *)buf->start; source=ntohl(iph->saddr); dest=ntohl(iph->daddr); /* Check source. If we don't like the source, there's no point generating ICMP because we won't know how to get it to the source of the packet. */ if (sender) { /* Check that the packet source is appropriate for the tunnel it came down */ if (!ipset_contains_addr(sender->networks,source)) { string_t s,d; s=ipaddr_to_string(source); d=ipaddr_to_string(dest); Message(M_WARNING,"%s: packet from tunnel %s with bad " "source address (s=%s,d=%s)\n",st->name,sender->name,s,d); BUF_FREE(buf); return; } } else { /* Check that the packet originates in our configured local network, and hasn't been forwarded from elsewhere or generated with the wrong source address */ if (!ipset_contains_addr(st->networks,source)) { string_t s,d; s=ipaddr_to_string(source); d=ipaddr_to_string(dest); Message(M_WARNING,"%s: outgoing packet with bad source address " "(s=%s,d=%s)\n",st->name,s,d); BUF_FREE(buf); return; } } /* If this is a point-to-point device we don't examine the destination address at all; we blindly send it down our one-and-only registered tunnel, or to the host, depending on where it came from. It's up to external software to check address validity and generate ICMP, etc. */ if (st->ptp) { if (sender) { netlink_host_deliver(st,sender,source,dest,buf); } else { netlink_client_deliver(st,st->clients,source,dest,buf); } BUF_ASSERT_FREE(buf); return; } /* st->secnet_address needs checking before matching destination addresses */ if (dest==st->secnet_address) { netlink_packet_local(st,sender,buf); BUF_ASSERT_FREE(buf); return; } netlink_packet_forward(st,sender,buf); BUF_ASSERT_FREE(buf); } static void netlink_inst_incoming(void *sst, struct buffer_if *buf) { struct netlink_client *c=sst; struct netlink *st=c->nst; netlink_incoming(st,c,buf); } static void netlink_dev_incoming(void *sst, struct buffer_if *buf) { struct netlink *st=sst; netlink_incoming(st,NULL,buf); } static void netlink_set_quality(void *sst, uint32_t quality) { struct netlink_client *c=sst; struct netlink *st=c->nst; c->link_quality=quality; c->up=(c->link_quality==LINK_QUALITY_DOWN)?False:True; if (c->options&OPT_SOFTROUTE) { st->set_routes(st->dst,c); } } static void netlink_output_subnets(struct netlink *st, uint32_t loglevel, struct subnet_list *snets) { int32_t i; string_t net; for (i=0; ientries; i++) { net=subnet_to_string(snets->list[i]); Message(loglevel,"%s ",net); } } static void netlink_dump_routes(struct netlink *st, bool_t requested) { int i; string_t net; uint32_t c=M_INFO; if (requested) c=M_WARNING; if (st->ptp) { net=ipaddr_to_string(st->secnet_address); Message(c,"%s: point-to-point (remote end is %s); routes: ", st->name, net); netlink_output_subnets(st,c,st->clients->subnets); Message(c,"\n"); } else { Message(c,"%s: routing table:\n",st->name); for (i=0; in_clients; i++) { netlink_output_subnets(st,c,st->routes[i]->subnets); Message(c,"-> tunnel %s (%s,mtu %d,%s routes,%s," "quality %d,use %d,pri %lu)\n", st->routes[i]->name, st->routes[i]->up?"up":"down", st->routes[i]->mtu, st->routes[i]->options&OPT_SOFTROUTE?"soft":"hard", st->routes[i]->options&OPT_ALLOWROUTE?"free":"restricted", st->routes[i]->link_quality, st->routes[i]->outcount, (unsigned long)st->routes[i]->priority); } net=ipaddr_to_string(st->secnet_address); Message(c,"%s/32 -> netlink \"%s\" (use %d)\n", net,st->name,st->localcount); for (i=0; isubnets->entries; i++) { net=subnet_to_string(st->subnets->list[i]); Message(c,"%s ",net); } if (i>0) Message(c,"-> host (use %d)\n",st->outcount); } } /* ap is a pointer to a member of the routes array */ static int netlink_compare_client_priority(const void *ap, const void *bp) { const struct netlink_client *const*a=ap; const struct netlink_client *const*b=bp; if ((*a)->priority==(*b)->priority) return 0; if ((*a)->priority<(*b)->priority) return 1; return -1; } static void netlink_phase_hook(void *sst, uint32_t new_phase) { struct netlink *st=sst; struct netlink_client *c; int32_t i; /* All the networks serviced by the various tunnels should now * have been registered. We build a routing table by sorting the * clients by priority. */ NEW_ARY(st->routes,st->n_clients); /* Fill the table */ i=0; for (c=st->clients; c; c=c->next) { assert(iroutes[i++]=c; } /* Sort the table in descending order of priority */ qsort(st->routes,st->n_clients,sizeof(*st->routes), netlink_compare_client_priority); netlink_dump_routes(st,False); } static void netlink_signal_handler(void *sst, int signum) { struct netlink *st=sst; Message(M_INFO,"%s: route dump requested by SIGUSR1\n",st->name); netlink_dump_routes(st,True); } static void netlink_inst_set_mtu(void *sst, int32_t new_mtu) { struct netlink_client *c=sst; c->mtu=new_mtu; } static void netlink_inst_reg(void *sst, netlink_deliver_fn *deliver, void *dst, uint32_t *localmtu_r) { struct netlink_client *c=sst; struct netlink *st=c->nst; c->deliver=deliver; c->dst=dst; if (localmtu_r) *localmtu_r=st->mtu; } static struct flagstr netlink_option_table[]={ { "soft", OPT_SOFTROUTE }, { "allow-route", OPT_ALLOWROUTE }, { NULL, 0} }; /* This is the routine that gets called when the closure that's returned by an invocation of a netlink device closure (eg. tun, userv-ipif) is invoked. It's used to create routes and pass in information about them; the closure it returns is used by site code. */ static closure_t *netlink_inst_create(struct netlink *st, struct cloc loc, dict_t *dict) { struct netlink_client *c; string_t name; struct ipset *networks; uint32_t options,priority; int32_t mtu; list_t *l; name=dict_read_string(dict, "name", True, st->name, loc); l=dict_lookup(dict,"routes"); if (!l) cfgfatal(loc,st->name,"required parameter \"routes\" not found\n"); networks=string_list_to_ipset(l,loc,st->name,"routes"); options=string_list_to_word(dict_lookup(dict,"options"), netlink_option_table,st->name); priority=dict_read_number(dict,"priority",False,st->name,loc,0); mtu=dict_read_number(dict,"mtu",False,st->name,loc,0); if ((options&OPT_SOFTROUTE) && !st->set_routes) { cfgfatal(loc,st->name,"this netlink device does not support " "soft routes.\n"); return NULL; } if (options&OPT_SOFTROUTE) { /* XXX for now we assume that soft routes require root privilege; this may not always be true. The device driver can tell us. */ require_root_privileges=True; require_root_privileges_explanation="netlink: soft routes"; if (st->ptp) { cfgfatal(loc,st->name,"point-to-point netlinks do not support " "soft routes.\n"); return NULL; } } /* Check that nets are a subset of st->remote_networks; refuse to register if they are not. */ if (!ipset_is_subset(st->remote_networks,networks)) { cfgfatal(loc,st->name,"routes are not allowed\n"); return NULL; } NEW(c); c->cl.description=name; c->cl.type=CL_NETLINK; c->cl.apply=NULL; c->cl.interface=&c->ops; c->ops.st=c; c->ops.reg=netlink_inst_reg; c->ops.deliver=netlink_inst_incoming; c->ops.set_quality=netlink_set_quality; c->ops.set_mtu=netlink_inst_set_mtu; c->nst=st; c->networks=networks; c->subnets=ipset_to_subnet_list(networks); c->priority=priority; c->deliver=NULL; c->dst=NULL; c->name=name; c->link_quality=LINK_QUALITY_UNUSED; c->mtu=mtu?mtu:st->mtu; c->options=options; c->outcount=0; c->up=False; c->kup=False; c->next=st->clients; st->clients=c; assert(st->n_clients < INT_MAX); st->n_clients++; return &c->cl; } static list_t *netlink_inst_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct netlink *st=self->interface; dict_t *dict; item_t *item; closure_t *cl; item=list_elem(args,0); if (!item || item->type!=t_dict) { cfgfatal(loc,st->name,"must have a dictionary argument\n"); } dict=item->data.dict; cl=netlink_inst_create(st,loc,dict); return new_closure(cl); } netlink_deliver_fn *netlink_init(struct netlink *st, void *dst, struct cloc loc, dict_t *dict, cstring_t description, netlink_route_fn *set_routes, netlink_deliver_fn *to_host) { item_t *sa, *ptpa; list_t *l; st->dst=dst; st->cl.description=description; st->cl.type=CL_PURE; st->cl.apply=netlink_inst_apply; st->cl.interface=st; st->clients=NULL; st->routes=NULL; st->n_clients=0; st->set_routes=set_routes; st->deliver_to_host=to_host; st->name=dict_read_string(dict,"name",False,description,loc); if (!st->name) st->name=description; l=dict_lookup(dict,"networks"); if (l) st->networks=string_list_to_ipset(l,loc,st->name,"networks"); else { struct ipset *empty; empty=ipset_new(); st->networks=ipset_complement(empty); ipset_free(empty); } l=dict_lookup(dict,"remote-networks"); if (l) { st->remote_networks=string_list_to_ipset(l,loc,st->name, "remote-networks"); } else { struct ipset *empty; empty=ipset_new(); st->remote_networks=ipset_complement(empty); ipset_free(empty); } st->local_address=string_item_to_ipaddr( dict_find_item(dict,"local-address", True, "netlink", loc),"netlink"); sa=dict_find_item(dict,"secnet-address",False,"netlink",loc); ptpa=dict_find_item(dict,"ptp-address",False,"netlink",loc); if (sa && ptpa) { cfgfatal(loc,st->name,"you may not specify secnet-address and " "ptp-address in the same netlink device\n"); } if (!(sa || ptpa)) { cfgfatal(loc,st->name,"you must specify secnet-address or " "ptp-address for this netlink device\n"); } if (sa) { st->secnet_address=string_item_to_ipaddr(sa,"netlink"); st->ptp=False; } else { st->secnet_address=string_item_to_ipaddr(ptpa,"netlink"); st->ptp=True; } /* To be strictly correct we could subtract secnet_address from networks here. It shouldn't make any practical difference, though, and will make the route dump look complicated... */ st->subnets=ipset_to_subnet_list(st->networks); st->mtu=dict_read_number(dict, "mtu", False, "netlink", loc, DEFAULT_MTU); buffer_new(&st->icmp,MAX(ICMP_BUFSIZE,st->mtu)); st->outcount=0; st->localcount=0; add_hook(PHASE_SETUP,netlink_phase_hook,st); request_signal_notification(SIGUSR1, netlink_signal_handler, st); /* If we're point-to-point then we return a CL_NETLINK directly, rather than a CL_NETLINK_OLD or pure closure (depending on compatibility). This CL_NETLINK is for our one and only client. Our cl.apply function is NULL. */ if (st->ptp) { closure_t *cl; cl=netlink_inst_create(st,loc,dict); st->cl=*cl; } return netlink_dev_incoming; } /* No connection to the kernel at all... */ struct null { struct netlink nl; }; static bool_t null_set_route(void *sst, struct netlink_client *routes) { struct null *st=sst; if (routes->up!=routes->kup) { Message(M_INFO,"%s: setting routes for tunnel %s to state %s\n", st->nl.name,routes->name, routes->up?"up":"down"); routes->kup=routes->up; return True; } return False; } static void null_deliver(void *sst, struct buffer_if *buf) { return; } static list_t *null_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct null *st; item_t *item; dict_t *dict; NEW(st); item=list_elem(args,0); if (!item || item->type!=t_dict) cfgfatal(loc,"null-netlink","parameter must be a dictionary\n"); dict=item->data.dict; netlink_init(&st->nl,st,loc,dict,"null-netlink",null_set_route, null_deliver); return new_closure(&st->nl.cl); } void netlink_module(dict_t *dict) { add_closure(dict,"null-netlink",null_apply); } work/netlink.h0000664000000000000000000000613715063477206010566 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #ifndef netlink_h #define netlink_h #include "ipaddr.h" #define DEFAULT_BUFSIZE 2048 #define DEFAULT_MTU 1000 #define ICMP_BUFSIZE 1024 struct netlink; struct netlink_client { closure_t cl; struct netlink_if ops; struct netlink *nst; struct ipset *networks; struct subnet_list *subnets; /* Same information as 'networks' */ uint32_t priority; /* Higher priority clients have their networks checked first during routing. This allows things like laptops to supersede whole networks. */ netlink_deliver_fn *deliver; void *dst; string_t name; uint32_t link_quality; int32_t mtu; uint32_t options; uint32_t outcount; bool_t up; /* Should these routes exist in the kernel? */ bool_t kup; /* Do these routes exist in the kernel? */ struct netlink_client *next; }; /* options field in 'struct netlink_client' */ #define OPT_SOFTROUTE 1 #define OPT_ALLOWROUTE 2 typedef bool_t netlink_route_fn(void *cst, struct netlink_client *routes); /* Netlink provides one function to the device driver, to call to deliver a packet from the device. The device driver provides one function to netlink, for it to call to deliver a packet to the device. */ struct netlink { closure_t cl; void *dst; /* Pointer to host interface state */ cstring_t name; struct ipset *networks; /* Local networks */ struct subnet_list *subnets; /* Same as networks, for display */ struct ipset *remote_networks; /* Allowable remote networks */ uint32_t local_address; /* host interface address */ uint32_t secnet_address; /* our own address, or the address of the other end of a point-to-point link */ bool_t ptp; int32_t mtu; struct netlink_client *clients; /* Linked list of clients */ struct netlink_client **routes; /* Array of clients, sorted by priority */ int32_t n_clients; netlink_deliver_fn *deliver_to_host; /* Provided by driver */ netlink_route_fn *set_routes; /* Provided by driver */ struct buffer_if icmp; /* Buffer for assembly of outgoing ICMP */ uint32_t outcount; /* Packets sent to host */ uint32_t localcount; /* Packets sent to secnet */ }; extern netlink_deliver_fn *netlink_init(struct netlink *st, void *dst, struct cloc loc, dict_t *dict, cstring_t description, netlink_route_fn *set_routes, netlink_deliver_fn *to_host); #endif /* netlink_h */ work/osdep.c0000664000000000000000000000323515063477206010223 0ustar /* * osdep.c * - portability routines */ /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include "config.h" #include "osdep.h" #include "secnet.h" #include "util.h" #ifndef HAVE_FMEMOPEN # ifdef HAVE_FUNOPEN struct fmemopen_state { const char *bufp; size_t remain; }; static int fmemopen_readfn(void *sst, char *out, int sz) { struct fmemopen_state *st=sst; assert(sz>=0); int now=MIN((size_t)sz,st->remain); memcpy(out,st->bufp,now); st->remain-=now; return now; } static int fmemopen_close(void *sst) { free(sst); return 0; } FILE *fmemopen(void *buf, size_t size, const char *mode) { /* this is just a fake plastic imitation */ assert(!strcmp(mode,"r")); struct fmemopen_state *st; NEW(st); st->bufp=buf; st->remain=size; FILE *f=funopen(st,fmemopen_readfn,0,0,fmemopen_close); if (!f) free(st); return f; } # else /* HAVE_FUNOPEN */ # error no fmemopen, no funopen, cannot proceed # endif #endif /* HAVE_FMEMOPEN */ work/osdep.h0000664000000000000000000000172015063477206010225 0ustar /* * osdep.c * - portability routines */ /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #ifndef osdep_h #define osdep_h #include "config.h" #include #ifndef HAVE_FMEMOPEN extern FILE *fmemopen(void *buf, size_t size, const char *mode); #endif #endif /* osdep_h */ work/parallel-test.make0000664000000000000000000000054515063477206012356 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. # usage # ../parallel-bisect.sh DIRS := $(wildcard d.*) TARGETS := $(addsuffix /done, $(DIRS)) all: $(TARGETS) %/done: set -e; SECNET_TEST_BUILDDIR=$(PWD)/$* ./stest/t-nonnego-oo work/parallel-test.sh0000775000000000000000000000131115063477206012046 0ustar #!/bin/sh # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. # usage # ../parallel-bisect.sh # # There should be subdirectories d.N for N=1..20 # which are build trees of the current secnet. set -ex cd d.1 make -j4 clean make -j4 stest/d-nonnego-oo/ok cd .. for f in d.*; do ln d.1/secnet $f/secnet.new rm $f/secnet mv $f/secnet.new $f/secnet done here=$(git rev-parse HEAD) us=${0%/*} log=$us/at-$here.log >$log for x in `seq 1 ${1-500}`; do echo $x echo >>$log $x make -j -f $us/parallel-test.make >$us/dump/at-$here.log 2>&1 echo >>$log "$x ok" done echo ok work/polypath-interface-monitor-linux0000775000000000000000000000652115063477206015314 0ustar #!/usr/bin/perl -w # This file is part of secnet. # See README for full list of copyright holders. # # secnet 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. # # secnet 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 # version 3 along with secnet; if not, see # https://www.gnu.org/licenses/gpl.html. use strict; use IO::Handle; my $us = $0; $us =~ s{.*/}{}; open DEBUG, ">/dev/null" or die $!; if (@ARGV && $ARGV[0] eq '-D') { shift @ARGV; open DEBUG, ">&STDERR" or die $!; } die "$us: no arguments permitted\n" if @ARGV; our ($monh,$monchild); our %reported; # no entry: not reported, does not exist # /ry+/: reported, entry exists # during processing only: # /r/: reported, may not still exist # /y+/: not reported, entry exists sub killmonitor () { return unless $monchild; kill 9, $monchild or warn "$us: cannot kill monitor child [$monchild]: $!\n"; $monchild=undef; close $monh; } END { killmonitor(); } my $restart; for (;;) { my $o; eval { if (!$monh) { killmonitor(); $monh = new IO::File; $monchild = open $monh, "-|", qw(ip -o monitor addr) or die "spawn monitor: $!\n"; sleep(1) if $restart++; } else { my $discard; my $got = sysread $monh, $discard, 4096; die "read monitor: $!\n" unless defined $got; die "monitor failed\n" unless $got; } $_='r' foreach values %reported; print DEBUG "#########################################\n"; foreach my $ip (qw(4 6)) { print DEBUG "###### $ip:\n"; my $addrh = new IO::File; open $addrh, "-|", qw(ip -o), "-$ip", qw(addr show) or die "spawn addr $ip show: $!\n"; my $afstr = $ip==4 ? 'inet' : $ip==6 ? 'inet6' : die; while (<$addrh>) { print DEBUG "#$_"; if (m{^\d+\:\s*(\S+)\s+$afstr\s+([0-9a-z.:]+)(?:/\d+)?\s}) { my $rhs=$'; #'; my $outline = "$ip $1 $2"; # "ip -o addr show" has a ridiculous output format. In # particular, it mixes output keywords which introduce # values with ones which don't, and there seems to be # no way to tell without knowing all the possible # keywords. We hope that before the \ there is nothing # which contains arbitrary text (specifically, which # might be `tentative' other than to specify IPv6 # tentativeness). We have to do this for IPv6 only # because in the IPv4 output, the interface name # appears here! next if $ip==6 && $rhs=~m{[^\\]* tentative\s}; $reported{$outline} .= "y"; } else { chomp; warn "unexpected output from addr $ip show: $_\n"; } } my $r = close $addrh; die "addr $ip show failed $!\n" unless $r; $o = ''; } foreach my $k (keys %reported) { local $_ = $reported{$k}; if (m/^r$/) { $o .= "-$k\n"; delete $reported{$k}; } elsif (m/^y/) { $o .= "+$k\n"; } } }; if ($@) { print STDERR "$us: $@"; sleep 5; next; } print $o or die $!; STDOUT->flush or die $!; } work/polypath.c0000664000000000000000000006471115063477206010757 0ustar /* polypath * send/receive module for secnet * for multi-route setups */ /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include "secnet.h" #include "util.h" #include "unaligned.h" #include "comm-common.h" #include #include #include #ifdef CONFIG_IPV6 static comm_sendmsg_fn polypath_sendmsg; struct interf { char *name; /* from malloc */ struct udpsocks socks; bool_t experienced_xmit_noaf[MAX_AF+1]; LIST_ENTRY(interf) entry; }; struct polypath { struct udpcommon uc; int max_interfs; const char *const *ifname_pats; const char *const *monitor_command; bool_t permit_loopback; LIST_HEAD(interf_list, interf) interfs_general; struct interf_list interfs_dedicated; struct buffer_if lbuf; int monitor_fd; pid_t monitor_pid; int privsep_incoming_fd; int privsep_ipcsock_fd; }; struct comm_clientinfo { union iaddr dedicated; /* might be AF_UNSPEC */ }; static void polypath_phase_shutdown(void *sst, uint32_t newphase); #define LG 0, st->uc.cc.cl.description, &st->uc.cc.loc static const char *const default_loopback_ifname_pats[] = { "!lo", 0 }; static const char *const default_ifname_pats[] = { "!tun*","!tap*","!sl*","!userv*", "@hippo*", "*", 0 }; static const char *const default_monitor_command[] = { #if __linux__ DATAROOTDIR "/secnet/" "polypath-interface-monitor-linux", 0 #else 0 #endif }; static const char *polypath_addr_to_string(void *commst, const struct comm_addr *ca) { static char sbuf[100]; snprintf(sbuf, sizeof(sbuf), "polypath:%s", iaddr_to_string(&ca->ia)); return sbuf; } static bool_t ifname_search_pats(struct polypath *st, struct cloc loc, const char *ifname, char *want_io, const char *const *pats) { /* Returns True iff we found a list entry, in which case *want_io * is set to the sense of that entry. Otherwise *want_io is set * to the sense of the last entry, or unchanged if there were no pats. */ if (!pats) return False; const char *const *pati; for (pati=pats; *pati; pati++) { const char *pat=*pati; if (*pat=='!' || *pat=='+' || *pat=='@') { *want_io=*pat; pat++; } else if (*pat=='*' || isalnum((unsigned char)*pat)) { *want_io='+'; } else cfgfatal(loc,"polypath","invalid interface name pattern `%s'",pat); int match=fnmatch(pat,ifname,0); if (match==0) return True; if (match!=FNM_NOMATCH) cfgfatal(loc,"polypath","fnmatch failed! (pattern `%s')",pat); } return False; } static char ifname_wanted(struct polypath *st, struct cloc loc, const char *ifname) { char want='!'; /* pretend an empty cfg ends with ! */ if (ifname_search_pats(st,loc,ifname,&want, st->ifname_pats)) return want; if (want!='!') /* last pattern was positive, do not search default */ return '!'; if (!st->permit_loopback && ifname_search_pats(st,loc,ifname,&want, default_loopback_ifname_pats)) return want; if (ifname_search_pats(st,loc,ifname,&want, default_ifname_pats)) return want; abort(); } static struct comm_clientinfo *polypath_clientinfo(void *state, dict_t *dict, struct cloc cloc) { struct comm_clientinfo *clientinfo; NEW(clientinfo); FILLZERO(*clientinfo); clientinfo->dedicated.sa.sa_family=AF_UNSPEC; item_t *item = dict_find_item(dict,"dedicated-interface-addr", False,"polypath",cloc); if (item) string_item_to_iaddr(item,0,&clientinfo->dedicated, "polypath"); return clientinfo; } static int polypath_beforepoll(void *state, struct pollfd *fds, int *nfds_io, int *timeout_io) { struct polypath *st=state; BEFOREPOLL_WANT_FDS(1); fds[0].fd=st->monitor_fd; fds[0].events=POLLIN; return 0; } static inline bool_t matches32(uint32_t word, uint32_t prefix, int prefixlen) { assert(prefixlen>0); assert(prefixlen<=32); uint32_t mask = ~(((uint32_t)1 << (32-prefixlen)) - 1); assert(!(prefix & ~mask)); return (word & mask) == prefix; } /* These macros expect * bad_fn_type *const bad; * void *badctx; * and * out: */ #define BAD(m) do{ bad(st,badctx,M_WARNING,m,0); goto out; }while(0) #define BADE(m,ev) do{ bad(st,badctx,M_WARNING,m,ev); goto out; }while(0) typedef void bad_fn_type(struct polypath *st, void *badctx, int mclass, const char* m, int ev); typedef void polypath_ppml_callback_type(struct polypath *st, bad_fn_type *bad, void *badctx, bool_t add, char want, const char *ifname, const char *ifaddr, const union iaddr *ia, int fd /* -1 if none yet */); struct ppml_bad_ctx { const char *orgl; char *undospace; }; static void ppml_bad(struct polypath *st, void *badctx, int mclass, const char *m, int ev) { struct ppml_bad_ctx *bc=badctx; if (bc->undospace) *(bc->undospace)=' '; lg_perror(LG,mclass,ev, "error processing polypath state change: %s" " (while processing `%s')", m,bc->orgl); } static void polypath_process_monitor_line(struct polypath *st, char *orgl, polypath_ppml_callback_type *callback) /* always calls callback with fd==-1 */ { struct udpcommon *uc=&st->uc; char *l=orgl; bad_fn_type (*const bad)=ppml_bad; struct ppml_bad_ctx badctx[1]={{ .orgl=orgl, .undospace=0 }}; bool_t add; int c=*l++; if (c=='+') add=True; else if (c=='-') add=False; else BAD("bad +/-"); int proto; c=*l++; if (c=='4') proto=AF_INET; else if (c=='6') proto=AF_INET6; else BAD("bad proto"); char *space=strchr(l,' '); if (!space) BAD("no first space"); const char *ifname=space+1; space=strchr(ifname,' '); if (!space) BAD("no second space"); const char *ifaddr=space+1; *space=0; badctx->undospace=space; union iaddr ia; FILLZERO(ia); socklen_t salen=sizeof(ia); int r=adns_text2addr(ifaddr,uc->port, adns_qf_addrlit_ipv4_quadonly, &ia.sa, &salen); assert(r!=ENOSPC); if (r) BADE("adns_text2addr",r); if (ia.sa.sa_family!=proto) BAD("address family mismatch"); #define DONT(m) do{ \ if (add) \ lg_perror(LG,M_INFO,0,"ignoring %s [%s]: %s",ifname,ifaddr,m); \ goto out; \ }while(0) char want=ifname_wanted(st,st->uc.cc.loc,ifname); if (want=='!') DONT("unwanted interface name"); switch (ia.sa.sa_family) { case AF_INET6: { const struct in6_addr *i6=&ia.sin6.sin6_addr; #define DONTKIND(X,m) \ if (IN6_IS_ADDR_##X(i6)) DONT("IPv6 address is " m) DONTKIND(UNSPECIFIED, "unspecified"); DONTKIND(MULTICAST , "multicast" ); DONTKIND(LINKLOCAL , "link local" ); DONTKIND(SITELOCAL , "site local" ); DONTKIND(V4MAPPED , "v4-mapped" ); if (!st->permit_loopback) DONTKIND(LOOPBACK , "loopback" ); #undef DONTKIND #define DONTMASK(w7x,w6x,prefixlen,m) \ if (matches32(get_uint32(i6->s6_addr), \ ((uint32_t)0x##w7x << 16) | (uint32_t)0x##w6x, \ prefixlen)) \ DONT("IPv6 address is " m) DONTMASK( 100, 0, 8, "Discard-Only (RFC6666)"); DONTMASK(2001, 0, 23, "in IETF protocol block (RFC2928)"); DONTMASK(fc00, 0, 7, "Uniqe Local unicast (RFC4193)"); #undef DONTMASK break; } case AF_INET: { const uint32_t i4=htonl(ia.sin.sin_addr.s_addr); if (i4==INADDR_ANY) DONT("IPv4 address is any/unspecified"); if (i4==INADDR_BROADCAST) DONT("IPv4 address is all hosts broadcast"); #define DONTMASK(b3,b2,b1,b0,prefixlen,m) do{ \ const uint8_t prefixbytes[4] = { (b3),(b2),(b1),(b0) }; \ if (matches32(i4,get_uint32(prefixbytes),prefixlen)) \ DONT("IPv4 address is " m); \ }while(0) DONTMASK(169,254,0,0, 16, "link local"); DONTMASK(224, 0,0,0, 4, "multicast"); DONTMASK(192, 0,0,0, 24, "in IETF protocol block (RFC6890)"); DONTMASK(240, 0,0,0, 4, "in reserved addressing block (RFC1112)"); if (!st->permit_loopback) DONTMASK(127, 0,0,0, 8, "loopback"); #undef DONTMASK break; } default: abort(); } #undef DONT /* OK, process it */ callback(st, bad,badctx, add,want, ifname,ifaddr,&ia,-1); out:; } static void dump_pria(struct polypath *st, struct interf_list *interfs, const char *ifname, char want) { #ifdef POLYPATH_DEBUG struct interf *interf; if (ifname) lg_perror(LG,M_DEBUG,0, "polypath record ifaddr `%s' (%c)", ifname, want); LIST_FOREACH(interf, interfs, entry) { lg_perror(LG,M_DEBUG,0, " polypath interface `%s', nsocks=%d", interf->name, interf->socks.n_socks); int i; for (i=0; isocks.n_socks; i++) { struct udpsock *us=&interf->socks.socks[i]; lg_perror(LG,M_DEBUG,0, " polypath sock fd=%d addr=%s", us->fd, iaddr_to_string(&us->addr)); } } #endif } static bool_t polypath_make_socket(struct polypath *st, bad_fn_type *bad, void *badctx, struct udpsock *us, const char *ifname) /* on error exit has called bad; might leave us->fd as -1 */ { assert(us->fd==-1); bool_t ok=udp_make_socket(&st->uc,us,M_WARNING); if (!ok) BAD("unable to set up socket"); int r=setsockopt(us->fd,SOL_SOCKET,SO_BINDTODEVICE, ifname,strlen(ifname)+1); if (r) BADE("setsockopt(,,SO_BINDTODEVICE,)",errno); return True; out: return False; } static void polypath_record_ifaddr(struct polypath *st, bad_fn_type *bad, void *badctx, bool_t add, char want, const char *ifname, const char *ifaddr, const union iaddr *ia, int fd) { struct udpcommon *uc=&st->uc; struct interf *interf=0; int max_interfs; struct udpsock *us=0; struct interf_list *interfs; switch (want) { case '+': interfs=&st->interfs_general; max_interfs=st->max_interfs; break; case '@': interfs=&st->interfs_dedicated; max_interfs=INT_MAX; break; default: fatal("polypath: got bad want (%#x, %s)", want, ifname); } dump_pria(st,interfs,ifname,want); int n_ifs=0; LIST_FOREACH(interf,interfs,entry) { if (!strcmp(interf->name,ifname)) goto found_interf; n_ifs++; } /* not found */ if (n_ifs==max_interfs) BAD("too many interfaces"); interf=malloc(sizeof(*interf)); if (!interf) BADE("malloc for new interface",errno); interf->name=0; interf->socks.n_socks=0; FILLZERO(interf->experienced_xmit_noaf); LIST_INSERT_HEAD(interfs,interf,entry); interf->name=strdup(ifname); udp_socks_register(&st->uc,&interf->socks,interf->name); if (!interf->name) BADE("strdup interface name",errno); found_interf: if (add) { if (interf->socks.n_socks == UDP_MAX_SOCKETS) BAD("too many addresses on this interface"); struct udpsock *us=&interf->socks.socks[interf->socks.n_socks]; us->fd=-1; COPY_OBJ(us->addr,*ia); if (fd<0) { bool_t ok=polypath_make_socket(st,bad,badctx, us,ifname); if (!ok) goto out; } else { bool_t ok=udp_import_socket(uc,us,M_WARNING,fd); if (!ok) goto out; fd=-1; } interf->socks.n_socks++; lg_perror(LG,M_INFO,0,"using %s %s",ifname, iaddr_to_string(&us->addr)); us=0; /* do not destroy this socket during `out' */ } else { int i; for (i=0; isocks.n_socks; i++) if (iaddr_equal(&interf->socks.socks[i].addr,ia,True)) goto address_remove_found; bad(st,badctx,M_DEBUG,"address to remove not found",0); goto out; address_remove_found: lg_perror(LG,M_INFO,0,"removed %s %s",ifname, iaddr_to_string(&interf->socks.socks[i].addr)); udp_destroy_socket(&st->uc,&interf->socks.socks[i]); interf->socks.socks[i]= interf->socks.socks[--interf->socks.n_socks]; } out: if (us) udp_destroy_socket(uc,us); if (fd>=0) close(fd); if (interf && !interf->socks.n_socks) { udp_socks_deregister(&st->uc,&interf->socks); LIST_REMOVE(interf,entry); free(interf->name); free(interf); } dump_pria(st,interfs,0,0); } static void subproc_problem(struct polypath *st, enum async_linebuf_result alr, const char *emsg) { int status; assert(st->monitor_pid); pid_t gotpid=waitpid(st->monitor_pid,&status,WNOHANG); if (gotpid==st->monitor_pid) { st->monitor_pid=0; lg_exitstatus(LG,M_FATAL,status,"interface monitor"); } else if (gotpid<0) lg_perror(LG,M_ERR,errno,"unable to reap interface monitor"); else assert(gotpid==0); if (alr==async_linebuf_eof) lg_perror(LG,M_FATAL,0,"unexpected EOF from interface monitor"); else lg_perror(LG,M_FATAL,0,"bad output from interface monitor: %s",emsg); assert(!"not reached"); } /* Used in non-privsep case, and in privsep child */ static void afterpoll_monitor(struct polypath *st, struct pollfd *fd, polypath_ppml_callback_type *callback) { enum async_linebuf_result alr; const char *emsg; while ((alr=async_linebuf_read(fd,&st->lbuf,&emsg)) == async_linebuf_ok) polypath_process_monitor_line(st,st->lbuf.base,callback); if (alr==async_linebuf_nothing) return; subproc_problem(st,alr,emsg); } /* Used in non-privsep case only - glue for secnet main loop */ static void polypath_afterpoll_monitor(void *state, struct pollfd *fds, int nfds) { struct polypath *st=state; if (nfds<1) return; afterpoll_monitor(st,fds,polypath_record_ifaddr); } /* Actual udp packet sending work */ static void polypath_sendmsg_interf(struct polypath *st, struct interf *interf, struct buffer_if *buf, const struct comm_addr *dest, const union iaddr *dedicated, bool_t *allreasonable) { int i; int af=dest->ia.sa.sa_family; bool_t wanted=False, attempted=False, reasonable=False; for (i=0; isocks.n_socks; i++) { struct udpsock *us=&interf->socks.socks[i]; if (dedicated && !iaddr_equal(dedicated, &us->addr, True)) continue; wanted=True; if (af != us->addr.sa.sa_family) continue; attempted=True; int r=sendto(us->fd,buf->start,buf->size, 0,&dest->ia.sa,iaddr_socklen(&dest->ia)); udp_sock_experienced(0,&st->uc,&interf->socks,us, &dest->ia,af, r,errno); if (r>=0) { reasonable=True; break; } if (!(errno==EAFNOSUPPORT || errno==ENETUNREACH)) reasonable=True; } if (!wanted) return; if (!attempted) if (!interf->experienced_xmit_noaf[af]++) lg_perror(LG,M_WARNING,0, "%s has no suitable address to transmit %s", interf->name, af_name(af)); *allreasonable &= reasonable; } static bool_t polypath_sendmsg(void *commst, struct buffer_if *buf, const struct comm_addr *dest, struct comm_clientinfo *clientinfo) { struct polypath *st=commst; struct interf *interf; bool_t allreasonable=True; LIST_FOREACH(interf,&st->interfs_general,entry) { polypath_sendmsg_interf(st,interf,buf,dest, 0, &allreasonable); } if (clientinfo && clientinfo->dedicated.sa.sa_family != AF_UNSPEC) { LIST_FOREACH(interf,&st->interfs_dedicated,entry) { polypath_sendmsg_interf(st,interf,buf,dest, &clientinfo->dedicated, &allreasonable); } } return allreasonable; } /* Non-privsep: called in (sole) child. Privsep: in grandchild. */ static void child_monitor(struct polypath *st, int childfd) { dup2(childfd,1); execvp(st->monitor_command[0],(char**)st->monitor_command); fprintf(stderr,"secnet: cannot execute %s: %s\n", st->monitor_command[0], strerror(errno)); exit(-1); } /* General utility function. */ static void start_subproc(struct polypath *st, void (*make_fdpair)(int[2]), void (*child)(struct polypath *st, int childfd), const char *desc) { int pfds[2]; assert(!st->monitor_pid); assert(st->monitor_fd<0); make_fdpair(pfds); pid_t pid=fork(); if (!pid) { afterfork(); close(pfds[0]); child(st,pfds[1]); abort(); } if (pid<0) fatal_perror("%s: failed to fork for interface monitoring", st->uc.cc.cl.description); close(pfds[1]); st->monitor_pid=pid; st->monitor_fd=pfds[0]; setnonblock(st->monitor_fd); lg_perror(LG,M_NOTICE,0, "%s: spawning %s [pid %ld]", st->uc.cc.cl.description, desc, (long)st->monitor_pid); } /* Non-privsep only: glue for forking the monitor, from the main loop */ static void polypath_phase_startmonitor(void *sst, uint32_t newphase) { struct polypath *st=sst; start_subproc(st,pipe_cloexec,child_monitor, "interface monitor (no privsep)"); register_for_poll(st,polypath_beforepoll, polypath_afterpoll_monitor,"polypath"); } /*----- Privsep-only: -----*/ /* * We use two subprocesses, a child and a grandchild. These are * forked before secnet drops privilege. * * The grandchild is the same interface monitor helper script as used * in the non-privsep case. But its lines are read by the child * instead of by the main secnet. The child is responsible for * creating the actual socket (binding it, etc.). Each socket is * passed to secnet proper via fd passing, along with a data message * describing the interface name and address. The child does not * retain a list of current interfaces and addresses - it trusts the * interface monitor to get that right. secnet proper still maintains * that data structure. * * The result is that much of the non-privsep code can be reused, but * just plumbed together differently. * * The child does not retain the socket after passing it on. * Interface removals are handled similarly but without any fd. * * The result is that the configuration's limits on which interfaces * and ports secnet may use are enforced by the privileged child. */ struct privsep_mdata { bool_t add; char ifname[100]; union iaddr ia; char want; /* `+' or `@' */ }; static void papp_bad(struct polypath *st, void *badctx, int mclass, const char *m, int ev) { const struct privsep_mdata *mdata=(const void*)st->lbuf.start; const char *addr_str=badctx; lg_perror(LG,mclass,ev, "error processing polypath address change %s %s [%s]: %s", mdata->add ? "+" : "-", mdata->ifname, addr_str, m); } static void polypath_afterpoll_privsep(void *state, struct pollfd *fds, int nfds) /* In secnet proper; receives messages from child. */ { struct polypath *st=state; if (nfds<1) return; int revents=fds[0].revents; const char *badbit=pollbadbit(revents); if (badbit) subproc_problem(st,async_linebuf_broken,badbit); if (!(revents & POLLIN)) return; for (;;) { if (st->lbuf.size==sizeof(struct privsep_mdata)) { const struct privsep_mdata *mdata=(const void*)st->lbuf.start; if (mdata->add && st->privsep_incoming_fd<0) fatal("polypath (privsep): got add message data but no fd"); if (!mdata->add && st->privsep_incoming_fd>=0) fatal("polypath (privsep): got remove message data with fd"); if (!memchr(mdata->ifname,0,sizeof(mdata->ifname))) fatal("polypath (privsep): got ifname with no terminating nul"); int af=mdata->ia.sa.sa_family; if (!(af==AF_INET6 || af==AF_INET)) fatal("polypath (privsep): got message data but bad AF %d",af); const char *addr_str=iaddr_to_string(&mdata->ia); polypath_record_ifaddr(st,papp_bad,(void*)addr_str, mdata->add,mdata->want, mdata->ifname,addr_str, &mdata->ia, st->privsep_incoming_fd); st->privsep_incoming_fd=-1; st->lbuf.size=0; } struct msghdr msg; int fd; size_t cmsgdatalen=sizeof(fd); char cmsg_control_buf[CMSG_SPACE(cmsgdatalen)]; struct iovec iov; FILLZERO(msg); msg.msg_iov=&iov; msg.msg_iovlen=1; iov.iov_base=st->lbuf.start+st->lbuf.size; iov.iov_len=sizeof(struct privsep_mdata)-st->lbuf.size; if (st->privsep_incoming_fd<0) { msg.msg_control=cmsg_control_buf; msg.msg_controllen=sizeof(cmsg_control_buf); } ssize_t got=recvmsg(st->monitor_fd,&msg,0); if (got<0) { if (errno==EINTR) continue; if (iswouldblock(errno)) break; fatal_perror("polypath (privsep): recvmsg failed"); } if (got==0) subproc_problem(st,async_linebuf_eof,0); st->lbuf.size+=got; if (msg.msg_controllen) { size_t cmsgdatalen=sizeof(st->privsep_incoming_fd); struct cmsghdr *h=CMSG_FIRSTHDR(&msg); if (!(st->privsep_incoming_fd==-1 && h && h->cmsg_level==SOL_SOCKET && h->cmsg_type==SCM_RIGHTS && h->cmsg_len==CMSG_LEN(cmsgdatalen) && !CMSG_NXTHDR(&msg,h))) subproc_problem(st,async_linebuf_broken,"bad cmsg"); memcpy(&st->privsep_incoming_fd,CMSG_DATA(h),cmsgdatalen); assert(st->privsep_incoming_fd>=0); } } } static void privsep_handle_ifaddr(struct polypath *st, bad_fn_type *bad, void *badctx, bool_t add, char want, const char *ifname, const char *ifaddr, const union iaddr *ia, int fd_dummy) /* In child: handles discovered wanted interfaces, making sockets and sending them to secnet proper. */ { struct msghdr msg; struct iovec iov; struct udpsock us={ .fd=-1 }; size_t cmsgdatalen=sizeof(us.fd); char cmsg_control_buf[CMSG_SPACE(cmsgdatalen)]; assert(fd_dummy==-1); struct privsep_mdata mdata; FILLZERO(mdata); mdata.add=add; size_t l=strlen(ifname); if (l>=sizeof(mdata.ifname)) BAD("interface name too long"); strcpy(mdata.ifname,ifname); mdata.want=want; COPY_OBJ(mdata.ia,*ia); iov.iov_base=&mdata; iov.iov_len =sizeof(mdata); FILLZERO(msg); msg.msg_iov=&iov; msg.msg_iovlen=1; if (add) { COPY_OBJ(us.addr,*ia); bool_t ok=polypath_make_socket(st,bad,badctx,&us,ifname); if (!ok) goto out; msg.msg_control=cmsg_control_buf; msg.msg_controllen=sizeof(cmsg_control_buf); struct cmsghdr *h=CMSG_FIRSTHDR(&msg); h->cmsg_level=SOL_SOCKET; h->cmsg_type =SCM_RIGHTS; h->cmsg_len =CMSG_LEN(cmsgdatalen); memcpy(CMSG_DATA(h),&us.fd,cmsgdatalen); } while (iov.iov_len) { ssize_t got=sendmsg(st->privsep_ipcsock_fd,&msg,0); if (got<0) { if (errno!=EINTR) fatal_perror("polypath privsep sendmsg"); got=0; } else { assert(got>0); assert((size_t)got<=iov.iov_len); } iov.iov_base=(char*)iov.iov_base+got; iov.iov_len-=got; msg.msg_control=0; msg.msg_controllen=0; } out: if (us.fd>=0) close(us.fd); } static void child_privsep(struct polypath *st, int ipcsockfd) /* Privsep child main loop. */ { struct pollfd fds[2]; enter_phase(PHASE_CHILDPERSIST); st->privsep_ipcsock_fd=ipcsockfd; start_subproc(st,pipe_cloexec,child_monitor, "interface monitor (grandchild)"); for (;;) { int nfds=1; int r=polypath_beforepoll(st,fds,&nfds,0); assert(nfds==1); assert(!r); fds[1].fd=st->privsep_ipcsock_fd; fds[1].events=POLLIN; r=poll(fds,ARRAY_SIZE(fds),-1); if (r<0) { if (errno==EINTR) continue; fatal_perror("polypath privsep poll"); } if (fds[1].revents) { if (fds[1].revents & (POLLHUP|POLLIN)) { polypath_phase_shutdown(st,PHASE_SHUTDOWN); exit(0); } fatal("polypath privsep poll parent socket revents=%#x", fds[1].revents); } if (fds[0].revents & POLLNVAL) fatal("polypath privsep poll child socket POLLNVAL"); afterpoll_monitor(st,fds,privsep_handle_ifaddr); } } static void privsep_socketpair(int *fd) { int r=socketpair(AF_UNIX,SOCK_STREAM,0,fd); if (r) fatal_perror("socketpair(AF_UNIX,SOCK_STREAM,,)"); setcloexec(fd[0]); setcloexec(fd[1]); } static void polypath_phase_startprivsep(void *sst, uint32_t newphase) { struct polypath *st=sst; if (!will_droppriv()) { add_hook(PHASE_RUN, polypath_phase_startmonitor,st); return; } start_subproc(st,privsep_socketpair,child_privsep, "socket generator (privsep interface handler)"); BUF_FREE(&st->lbuf); buffer_destroy(&st->lbuf); buffer_new(&st->lbuf,sizeof(struct privsep_mdata)); BUF_ALLOC(&st->lbuf,"polypath mdata buf"); st->privsep_incoming_fd=-1; register_for_poll(st,polypath_beforepoll, polypath_afterpoll_privsep,"polypath"); } static void polypath_phase_shutdown(void *sst, uint32_t newphase) { struct polypath *st=sst; if (st->monitor_pid) { assert(st->monitor_pid>0); kill(st->monitor_pid,SIGTERM); } } static void polypath_phase_childpersist(void *sst, uint32_t newphase) { struct polypath *st=sst; struct interf *interf; LIST_FOREACH(interf,&st->interfs_general,entry) udp_socks_childpersist(&st->uc,&interf->socks); LIST_FOREACH(interf,&st->interfs_dedicated,entry) udp_socks_childpersist(&st->uc,&interf->socks); } #undef BAD #undef BADE /*----- generic closure and module setup -----*/ static list_t *polypath_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct polypath *st; COMM_APPLY(st,&st->uc.cc,polypath_,"polypath",loc); COMM_APPLY_STANDARD(st,&st->uc.cc,"polypath",args); UDP_APPLY_STANDARD(st,&st->uc,"polypath"); st->uc.cc.ops.clientinfo = polypath_clientinfo; struct udpcommon *uc=&st->uc; struct commcommon *cc=&uc->cc; st->max_interfs=dict_read_number(d,"max-interfaces",False,"polypath",loc,3); st->ifname_pats=dict_read_string_array(d,"interfaces",False,"polypath", cc->loc,0); st->permit_loopback=0; /* ifname_wanted reads this */ ifname_wanted(st,st->uc.cc.loc," "); /* try to check each pattern */ st->monitor_command=dict_read_string_array(d,"monitor-command",False, "polypath",cc->loc, default_monitor_command); if (!st->monitor_command[0]) cfgfatal(loc,"polypath","no polypath interface monitor-command" " (polypath unsupported on this platform?)\n"); st->permit_loopback=dict_read_bool(d,"permit-loopback",False, "polypath",cc->loc,False); LIST_INIT(&st->interfs_general); LIST_INIT(&st->interfs_dedicated); buffer_new(&st->lbuf,ADNS_ADDR2TEXT_BUFLEN+100); BUF_ALLOC(&st->lbuf,"polypath lbuf"); st->monitor_fd=-1; st->monitor_pid=0; add_hook(PHASE_GETRESOURCES, polypath_phase_startprivsep,st); add_hook(PHASE_SHUTDOWN, polypath_phase_shutdown, st); add_hook(PHASE_CHILDPERSIST,polypath_phase_childpersist,st); return new_closure(&cc->cl); } #endif /* CONFIG_IPV6 */ void polypath_module(dict_t *dict) { #ifdef CONFIG_IPV6 add_closure(dict,"polypath",polypath_apply); #endif /* CONFIG_IPV6 */ } work/pretest-to-tested0000775000000000000000000000126315063477206012266 0ustar #!/bin/bash # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. # best to run this in a git-worktree # example runes in main tree: # git-branch -f pretest && git-branch -f tested `git-merge-base HEAD tested` && git-checkout wip stl='' for subtree in base91-c subdirmk base91-python; do st=$(git-subtree split -P $subtree pretest) stl+=" ^$st" done set -e while true; do next=$(git-rev-list --reverse $stl tested..pretest | head -n1) if [ "x$next" = x ]; then break; fi git checkout "$next" ./comprehensive-test git push . HEAD:tested done work/privcache.c0000664000000000000000000001404215063477206011053 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include "secnet.h" #include "util.h" #define DEFAULT_SIZE 5 #define DEFAULT_MAXPRIV_BYTES 4095 struct ent { struct sigkeyid id; struct sigprivkey_if *sigpriv; /* 0 means none such */ }; struct privcache { closure_t cl; struct privcache_if ops; int used, alloc; struct pathprefix_template path; struct ent *ents; struct buffer_if databuf; }; static bool_t uncached_load_file( const struct sigscheme_info *scheme, const char *path, struct buffer_if *databuf, struct sigprivkey_if **sigpriv_r, closure_t **closure_r, struct log_if *log); static struct sigprivkey_if *uncached_get(struct privcache *st, const struct sigkeyid *id, struct log_if *log) { sprintf(st->path.write_here, SIGKEYID_PR_FMT, SIGKEYID_PR_VAL(id)); const char *path=st->path.buffer; const struct sigscheme_info *scheme; for (scheme=sigschemes; scheme->name; scheme++) if (scheme->algid == id->b[GRPIDSZ]) goto found; slilog(log,M_ERR,"private key file %s not loaded (unknown algid)", path); return 0; found:; struct sigprivkey_if *sigpriv; closure_t *cl; bool_t ok=uncached_load_file(scheme, path, &st->databuf, &sigpriv, &cl, log); return ok ? sigpriv : 0; } static bool_t uncached_load_file( const struct sigscheme_info *scheme, const char *path, struct buffer_if *databuf, struct sigprivkey_if **sigpriv_r, closure_t **closure_r, struct log_if *log) { bool_t ok=False; FILE *f=0; struct sigprivkey_if *sigpriv=0; f=fopen(path,"rb"); if (!f) { if (errno == ENOENT) { slilog(log,M_DEBUG,"private key %s not found", path); } else { slilog(log,M_ERR,"failed to open private key file %s", path); } goto error_out; } setbuf(f,0); buffer_init(databuf,0); ssize_t got=fread(databuf->base,1,databuf->alloclen,f); if (ferror(f)) { slilog(log,M_ERR,"failed to read private-key file %s", path); goto error_out; } if (!feof(f)) { slilog(log,M_ERR,"private key file %s longer than max %d", path, (int)databuf->alloclen); goto error_out; } fclose(f); f=0; databuf->start=databuf->base; databuf->size=got; struct cloc loc = { .file=path, .line=0 }; ok=scheme->loadpriv(scheme, databuf, &sigpriv, closure_r, log, loc); if (!ok) goto error_out; /* loadpriv will have logged */ *sigpriv_r=sigpriv; out: if (f) fclose(f); return ok; error_out: if (sigpriv) sigpriv->dispose(sigpriv->st); ok=False; goto out; } static struct sigprivkey_if *privcache_lookup(void *sst, const struct sigkeyid *id, struct log_if *log) { struct privcache *st=sst; int was; struct ent result; for (was=0; wasused; was++) { if (sigkeyid_equal(id, &st->ents[was].id)) { result = st->ents[was]; goto found; } } if (st->used < st->alloc) { was=st->used; st->used++; } else { was=st->used-1; if (st->ents[was].sigpriv) { st->ents[was].sigpriv->dispose(st->ents[was].sigpriv->st); } } COPY_OBJ(result.id, *id); result.sigpriv=uncached_get(st,id,log); found: memmove(&st->ents[1], &st->ents[0], sizeof(st->ents[0]) * was); st->ents[0]=result; return result.sigpriv; } static list_t *privcache_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct privcache *st; item_t *item; dict_t *dict; NEW(st); st->cl.description="privcache"; st->cl.type=CL_PRIVCACHE; st->cl.apply=NULL; st->cl.interface=&st->ops; st->ops.st=st; st->ops.lookup=privcache_lookup; st->ents=0; st->path.buffer=0; st->used=st->alloc=0; item=list_elem(args,0); if (!item || item->type!=t_dict) cfgfatal(loc,"privcache","parameter must be a dictionary\n"); dict=item->data.dict; st->alloc=dict_read_number(dict,"privcache-size",False,"privcache",loc, DEFAULT_SIZE); NEW_ARY(st->ents,st->alloc); st->used=0; int32_t buflen=dict_read_number(dict,"privkey-max",False,"privcache",loc, DEFAULT_MAXPRIV_BYTES); buffer_new(&st->databuf,buflen+1); const char *path=dict_read_string(dict,"privkeys",True,"privcache",loc); pathprefix_template_init(&st->path,path,KEYIDSZ*2); return new_closure(&st->cl); } static list_t *loadprivate_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { CL_GET_STR_ARG(0,algname,"algorithm name"); CL_GET_STR_ARG(1,path,"private key path"); const struct sigscheme_info *sch=sigscheme_lookup(algname); if (!sch) cfgfatal(algname_i->loc,"load-private", "unknown algorithm `%s'",algname); struct buffer_if databuf; buffer_new(&databuf,DEFAULT_MAXPRIV_BYTES); BUF_ALLOC(&databuf,"load-private data buf"); struct cfgfile_log log; cfgfile_log_init(&log,loc,"load-private"); struct sigprivkey_if *sigpriv; closure_t *cl; bool_t ok= uncached_load_file(sch,path,&databuf,&sigpriv,&cl,&log.log); if (!ok) cfgfatal(loc,"load-private","private key loading failed"); BUF_FREE(&databuf); buffer_destroy(&databuf); return new_closure(cl); } void privcache_module(dict_t *dict) { add_closure(dict,"priv-cache",privcache_apply); add_closure(dict,"load-private",loadprivate_apply); } work/process.c0000664000000000000000000002111415063477206010563 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #define _GNU_SOURCE #include "secnet.h" #include #include #include #include #include #include "process.h" /* Process handling - subprocesses, signals, etc. */ static bool_t signal_handling=False; static sigset_t emptyset, fullset; static sigset_t registered,pending; struct child { pid_t pid; cstring_t desc; process_callback_fn *cb; void *cst; bool_t finished; struct child *next; }; static struct child *children=NULL; struct signotify { int signum; signal_notify_fn *notify; void *cst; struct signotify *next; }; static struct signotify *sigs=NULL; static int spw,spr; /* file descriptors for signal notification pipe */ /* Long-lived subprocesses can only be started once we've started signal processing so that we can catch SIGCHLD for them and report their exit status using the callback function. We block SIGCHLD until signal processing has begun. */ pid_t makesubproc(process_entry_fn *entry, process_callback_fn *cb, void *est, void *cst, cstring_t desc) { struct child *c; pid_t p; NEW(c); c->desc=desc; c->cb=cb; c->cst=cst; if (!signal_handling) { fatal("makesubproc called before signal handling started"); } p=fork(); if (p==0) { /* Child process */ afterfork(); entry(est); abort(); } else if (p==-1) { fatal_perror("makesubproc (%s): fork",desc); } c->pid=p; c->finished=False; c->next=children; children=c; return p; } static signal_notify_fn sigchld_handler; static void sigchld_handler(void *st, int signum) { struct child *i,*n,**p; struct work { pid_t pid; process_callback_fn *cb; void *cst; int status; struct work *next; }; struct work *w=NULL, *nw; pid_t rv; int status; for (i=children; i; i=i->next) { rv=waitpid(i->pid,&status,WNOHANG); if (rv==-1) { fatal_perror("sigchld_handler: waitpid"); } if (rv==i->pid) { i->finished=True; NEW(nw); nw->pid=i->pid; nw->cb=i->cb; nw->cst=i->cst; nw->status=status; nw->next=w; w=nw; } } /* Remove all the finished tasks from the list of children */ for (i=children, p=&children; i; i=n) { n=i->next; if (i->finished) { free(i); *p=n; } else { p=&i->next; } } /* Notify as appropriate, then free the list */ while (w) { w->cb(w->cst,w->pid,w->status); nw=w; w=w->next; free(nw); } } int sys_cmd(const char *path, const char *arg, ...) { va_list ap; int rv, rc; pid_t c; c=fork(); if (c) { /* Parent -> wait for child */ do { rc = waitpid(c,&rv,0); } while(rc < 0 && errno == EINTR); if (rc < 0) fatal_perror("sys_cmd: waitpid for %s", path); if (rc != c) /* OS has gone mad */ fatal("sys_cmd: waitpid for %s returned wrong process ID!", path); if (rv) { /* If the command failed report its exit status */ lg_exitstatus(0,"sys_cmd",0,M_ERR,rv,path); } } else if (c==0) { char *args[100]; int i; /* Child -> exec command */ /* Really we ought to strcpy() the arguments into the args array, since the arguments are const char *. Since we'll exit anyway if the execvp() fails this seems somewhat pointless, and increases the chance of the child process failing before it gets to exec(). */ afterfork(); va_start(ap,arg); args[0]=(char *)arg; /* program name */ i=1; while ((args[i++]=va_arg(ap,char *))); execvp(path,args); fprintf(stderr, "sys_cmd(%s,%s,...): %s\n", path, arg, strerror(errno)); _exit(1); } else { /* Error */ fatal_perror("sys_cmd(%s,%s,...)", path, arg); } return rv; } static beforepoll_fn signal_beforepoll; static int signal_beforepoll(void *st, struct pollfd *fds, int *nfds_io, int *timeout_io) { BEFOREPOLL_WANT_FDS(1); fds[0].fd=spr; fds[0].events=POLLIN; return 0; } /* Bodge to work around Ubuntu's strict header files */ static void discard(int anything) {} static afterpoll_fn signal_afterpoll; static void signal_afterpoll(void *st, struct pollfd *fds, int nfds) { uint8_t buf[16]; struct signotify *n; sigset_t todo,old; if (nfds && (fds->revents & POLLIN)) { discard(read(spr,buf,16)); /* We don't actually care what we read; as long as there was at least one byte (which there was) we'll pick up the signals in the pending set */ /* We reset 'pending' before processing any of the signals that were pending so that we don't miss any signals that are delivered partway-through processing (all we assume about signal notification routines is that they handle all the work available at their _start_ and only optionally any work that arrives part-way through their execution). */ sigprocmask(SIG_SETMASK,&fullset,&old); todo=pending; sigemptyset(&pending); sigprocmask(SIG_SETMASK,&old,NULL); for (n=sigs; n; n=n->next) if (sigismember(&todo,n->signum)) n->notify(n->cst,n->signum); } } void afterfork(void) { struct signotify *n; sigset_t done; struct sigaction sa; clear_phase_hooks(PHASE_SHUTDOWN); /* Prevents calls to fatal() etc. in the child from running off and doing a lot of unhelpful things */ sigemptyset(&done); for (n=sigs; n; n=n->next) if (!sigismember(&done,n->signum)) { sigaddset(&done,n->signum); sa.sa_handler=SIG_DFL; sa.sa_mask=emptyset; sa.sa_flags=0; sigaction(n->signum,&sa,NULL); } sigemptyset(&emptyset); sigprocmask(SIG_SETMASK,&emptyset,NULL); } void childpersist_closefd_hook(void *fd_vp, uint32_t newphase) { int *fd_p=fd_vp; int fd=*fd_p; if (fd<0) return; *fd_p=-1; setnonblock(fd); /* in case close() might block */ close(fd); /* discard errors - we don't care, in the child */ } static void signal_handler(int signum) { int saved_errno; uint8_t thing=0; sigaddset(&pending,signum); /* XXX the write() may set errno, which can make the main program fail. However, signal handlers aren't allowed to modify anything which is not of type sig_atomic_t. The world is broken. */ /* I have decided to save and restore errno anyway; on most architectures on which secnet can run modifications to errno will be atomic, and it seems to be the lesser of the two evils. */ saved_errno=errno; discard(write(spw,&thing,1)); /* We don't care if this fails (i.e. the pipe is full) because the service routine will spot the pending signal anyway */ errno=saved_errno; } static void register_signal_handler(struct signotify *s) { struct sigaction sa; int rv; if (!signal_handling) return; if (sigismember(®istered,s->signum)) return; sigaddset(®istered,s->signum); sa.sa_handler=signal_handler; sa.sa_mask=fullset; sa.sa_flags=0; rv=sigaction(s->signum,&sa,NULL); if (rv!=0) { fatal_perror("register_signal_handler: sigaction(%d)",s->signum); } } void request_signal_notification(int signum, signal_notify_fn *notify, void *cst) { struct signotify *s; sigset_t old; NEW(s); s->signum=signum; s->notify=notify; s->cst=cst; s->next=sigs; sigprocmask(SIG_SETMASK,&fullset,&old); sigs=s; register_signal_handler(s); sigprocmask(SIG_SETMASK,&old,NULL); } void start_signal_handling(void) { int p[2]; struct signotify *i; sigemptyset(&emptyset); sigfillset(&fullset); sigemptyset(®istered); sigemptyset(&pending); pipe_cloexec(p); spw=p[1]; spr=p[0]; setnonblock(spw); setnonblock(spr); register_for_poll(NULL,signal_beforepoll,signal_afterpoll,"signal"); signal_handling=True; /* Register signal handlers for all the signals we're interested in */ for (i=sigs; i; i=i->next) { register_signal_handler(i); } request_signal_notification(SIGCHLD,sigchld_handler,NULL); } work/process.h0000664000000000000000000000231115063477206010566 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #ifndef process_h #define process_h #include #include typedef void process_callback_fn(void *cst, pid_t pid, int status); typedef void process_entry_fn(void *cst); typedef void signal_notify_fn(void *cst, int signum); extern pid_t makesubproc(process_entry_fn *entry, process_callback_fn *cb, void *est, void *cbst, cstring_t desc); extern void request_signal_notification(int signum, signal_notify_fn *notify, void *cst); #endif /* process_h */ work/pubkeys.c0000664000000000000000000000520515063477206010572 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include "util.h" #include "base91s/base91.h" #include "pubkeys.h" #include "pubkeys.yy.h" void keyset_dispose(struct peer_keyset **ks_io) { struct peer_keyset *ks=*ks_io; if (!ks) return; *ks_io=0; ks->refcount--; assert(ks->refcount>=0); if (ks->refcount) return; for (int ki=0; kinkeys; ki++) { struct sigpubkey_if *pk=ks->keys[ki].pubkey; pk->dispose(pk->st); } free(ks); } const struct sigscheme_info *sigscheme_lookup(const char *name) { const struct sigscheme_info *scheme; for (scheme=sigschemes; scheme->name; scheme++) if (!strcmp(name,scheme->name)) return scheme; return 0; } static list_t *makepublic_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { CL_GET_STR_ARG(0,algname,"algorithm name"); CL_GET_STR_ARG(1,b91d,"base91s-encoded public key"); const struct sigscheme_info *sch=sigscheme_lookup(algname); if (!sch) cfgfatal(algname_i->loc,"make-public", "unknown algorithm `%s'",algname); size_t b91l=strlen(b91d); if (b91l > INT_MAX/4) cfgfatal(algname_i->loc,"make-public", "base91s data unreasonably long"); struct buffer_if buf; buffer_new(&buf,base91s_decode_maxlen(b91l)); BUF_ALLOC(&buf,"make-public data buf"); assert(buf.start == buf.base); struct base91s b91; base91s_init(&b91); buf.size= base91s_decode(&b91,b91d,b91l,buf.start); buf.size += base91s_decode_end(&b91,buf.start+buf.size); assert(buf.size <= buf.alloclen); struct cfgfile_log log; cfgfile_log_init(&log,loc,"make-public"); struct sigpubkey_if *pubkey; closure_t *cl; bool_t ok=sch->loadpub(sch,&buf,&pubkey,&cl,&log.log,loc); if (!ok) cfgfatal(loc,"make-public","public key loading failed"); BUF_FREE(&buf); buffer_destroy(&buf); return new_closure(cl); } void pubkeys_init(dict_t *dict) { add_closure(dict,"make-public",makepublic_apply); } work/pubkeys.fl.pl0000775000000000000000000001653715063477206011400 0ustar #!/usr/bin/perl -w # -*- C -*- # # secnet - pubkeys.fl.pl # # This file is part of secnet. # See README for full list of copyright holders. # # secnet 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. # # secnet 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 # version 3 along with secnet; if not, see # https://www.gnu.org/licenses/gpl.html. # We process __DATA__ of this file first through the perl code, # and then through flex. We do it like this because directives # with positional arguments are otherwise rather tedious to specify # in flex. Of course we could have used bison too but this seems # better overall. use strict; our $do = ''; our $co = ''; our $kw; our $kwid; our @next_kw; our $in_s; our $data_off; our %subst = (GRPIDSZ => 4, SERIALSZ => 4); our $last_lno = -1; sub lineno (;$$) { my ($always, $delta) = @_; my $o = ''; $delta //= 0; if ($always || $. != $last_lno+1) { $o .= sprintf "#line %d \"%s\"\n", $delta+$data_off+$., $0; } $last_lno = $.; $o; } while () { last if m/^\%\%\s*$/; if (m/^!SUBSTCHECKS\s*$/) { foreach (keys %subst) { $do .= <"; } while () { s#\{!2(\w+)\}# '{'.(2 * ($subst{$1}//die "$1 ?")).'}' #ge; if (m/^!(KEYWORD|KWALIAS) ([-0-9a-z]+)(\s*\{.*\})?$/) { my $kwt=$2; if ($1 eq 'KEYWORD') { die if $kw; $kw = $kwt; } else { die if @next_kw; die unless $kw; } my $xact = $3 // ''; $kwid = $kw; $kwid =~ y/-/_/; $in_s = "HK_${kwid}"; $co .= "{L}$kwt { BEGIN($in_s); $xact }\n"; next; } if (m/^!ARG (\w+) (\S.*\S) \{\s*$/) { die unless $kw; die if @next_kw; $co .= inst("$in_s")."{S} { BEGIN(D_${kwid}_$1); }\n"; $co .= inst("D_${kwid}_$1")."$2 {\n"; $in_s = "HA_${kwid}_$1"; $co .= "\tBEGIN($in_s);\n"; @next_kw = ($kw); $co .= lineno(1,1); next; } if (m/^!\}\s*$/) { die unless @next_kw; $co .= lineno(1,0); $co .= "}\n"; $kw = shift @next_kw; next; } if (m/^!FINAL \{\s*$/) { die unless $kw; die if @next_kw; $co .= inst("FIN_$kwid")."\\n { BEGIN(0); c->loc.line++; }\n"; $co .= inst("$in_s")."{L}/\\n {\n"; $co .= "\tBEGIN(FIN_$kwid);\n"; $co .= lineno(1,1); @next_kw = (undef); next; } if (m/^!/) { die; } $co .= $_; if (m/^\%\%\s*$/) { $co .= lineno(1,1); } } print $do, "%%\n", $co or die $!; BEGIN { $data_off = __LINE__ + 1; } __DATA__ L [ \t]* S [ \t]+ BASE91S []-~!#-&(-[]+ %x SKIPNL %x SYNTAXERR %option yylineno %option noyywrap %option batch %option 8bit %option nodefault %option never-interactive %option prefix="pkyy" %option warn %{ #include "secnet.h" #include "pubkeys.h" #include "util.h" #include "unaligned.h" #include "base91s/base91.h" !SUBSTCHECKS struct pubkeyset_context { /* filled in during setup: */ struct cloc loc; /* line is runtime */ struct log_if *log; struct buffer_if *data_buf; struct peer_keyset *building; /* runtime: */ bool_t had_serial; bool_t fallback_skip; const struct sigscheme_info *scheme; uint8_t grpid[GRPIDSZ]; serialt serial; }; static struct pubkeyset_context c[1]; #define LI (c->log) #define HEX2BIN(v,l) ({ \ int32_t outlen; \ bool_t ok=hex_decode((v), ((l)), &outlen, yytext, False); \ assert(ok); \ assert(outlen==((l))); \ }) #define HEX2BIN_ARRAY(v) HEX2BIN((v),sizeof((v))) #define DOSKIPQ ({ \ BEGIN(SKIPNL); \ break; \ }) #define DOSKIP(m) ({ \ slilog(LI,M_INFO,"%s:%d: " m, c->loc.file, c->loc.line); \ DOSKIPQ; \ }) #define FAIL(m) do{ \ slilog(LI,M_ERR,"%s:%d: " m, c->loc.file, c->loc.line); \ return -1; \ }while(0) %} %% !KEYWORD pkg { c->fallback_skip=0; } !KWALIAS pkgf { c->fallback_skip=!!c->building->nkeys; } !ARG id [0-9a-f]{!2GRPIDSZ} { HEX2BIN_ARRAY(c->grpid); !} !FINAL { !} !KEYWORD pub !ARG algo [-0-9a-z]+ { if (c->fallback_skip) DOSKIP("fallback not needed"); c->scheme = sigscheme_lookup(yytext); if (!c->scheme) DOSKIP("unknown pk algorithm"); !} !ARG data {BASE91S} { /* baseE91 and thus base91s can sometimes store 14 bits per * character pair, so the max decode ratio is 14/16. */ size_t maxl = base91s_decode_maxlen(yyleng); buffer_init(c->data_buf,0); if (buf_remaining_space(c->data_buf) < maxl) DOSKIP("pk data too long"); struct base91s b91; base91s_init(&b91); size_t l = base91s_decode(&b91, yytext, yyleng, c->data_buf->start); l += base91s_decode_end(&b91, c->data_buf->start + l); assert(l <= maxl); buf_append(c->data_buf,l); !} !FINAL { if (c->building->nkeys >= MAX_SIG_KEYS) DOSKIP("too many public keys"); struct sigpubkey_if *pubkey; closure_t *cl; bool_t ok=c->scheme->loadpub(c->scheme,c->data_buf, &pubkey,&cl,c->log,c->loc); if (!ok) break; struct peer_pubkey *fill=&c->building->keys[c->building->nkeys]; memcpy(fill->id.b,c->grpid,GRPIDSZ); assert(ALGIDSZ==1); /* otherwise need htons or htonl or something */ fill->id.b[GRPIDSZ]=c->scheme->algid; fill->pubkey=pubkey; c->building->nkeys++; !} !KEYWORD serial !ARG id [0-9a-f]{!2SERIALSZ} { if (c->had_serial) FAIL("`serial' repeated"); c->had_serial = 1; uint8_t sb[SERIALSZ]; HEX2BIN_ARRAY(sb); c->serial=get_uint32(sb); !} !FINAL { !} {L}[-0-9a-z]+ { DOSKIP("unknown directive"); } {L}\# { BEGIN(SKIPNL); } {L}\n { c->loc.line++; } .*\n { c->loc.line++; BEGIN(0); } <> { return 0; } <*>. { yymore(); BEGIN(SYNTAXERR); } .* { slilog(LI,M_DEBUG,"pubkeys syntax error at `%s'", yytext); FAIL("syntax error"); } <*>\n { FAIL("syntax error - unexpected newline"); } <> { FAIL("syntax error - unexpected eof"); } %% extern struct peer_keyset * keyset_load(const char *path, struct buffer_if *data_buf, struct log_if *log, int logcl_enoent) { assert(!c->building); c->log=log; c->loc.file=path; pkyyin = fopen(path, "r"); if (!pkyyin) { slilog(LI, errno==ENOENT ? logcl_enoent : M_ERR, "%scould not open keyset file %s: %s", logcl_enoent==M_DEBUG && errno==ENOENT ? "expectedly " : "", path,strerror(errno)); goto err; } pkyyrestart(pkyyin); BEGIN(0); c->data_buf=data_buf; NEW(c->building); c->building->nkeys=0; c->building->refcount=1; c->fallback_skip=0; c->had_serial=0; c->loc.line=1; FILLZERO(c->grpid); FILLZERO(c->serial); int r=pkyylex(); if (r) goto err_bad; if (!c->building->nkeys) { slilog(LI,M_ERR,"no useable keys in %s",path); goto err_bad; } fclose(pkyyin); struct peer_keyset *built=c->building; c->building=0; return built; err_bad: errno=EBADMSG; err: if (c->building) { free(c->building); c->building=0; } if (pkyyin) { fclose(pkyyin); pkyyin=0; } return 0; } work/pubkeys.h0000664000000000000000000000274515063477206010605 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #ifndef pubkeys_h #define pubkeys_h #include "secnet.h" /*----- shared with site.c -----*/ struct peer_pubkey { struct sigkeyid id; struct sigpubkey_if *pubkey; }; struct peer_keyset { int refcount; serialt serial; int nkeys; struct peer_pubkey keys[MAX_SIG_KEYS]; }; extern struct peer_keyset * keyset_load(const char *path, struct buffer_if *data_buf, struct log_if *log, int logcl_enoent); extern void keyset_dispose(struct peer_keyset **ks); static inline struct peer_keyset *keyset_dup(struct peer_keyset *in) { in->refcount++; return in; } extern bool_t pubkey_want(struct peer_keyset *building /* refcount and serial undef */, struct sigkeyid *id, const struct sigscheme_info *scheme); #endif /* pubkeys_h */ work/random.c0000664000000000000000000000512215063477206010366 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include "secnet.h" #include #include #include #include #include #include struct rgen_data { closure_t cl; struct random_if ops; struct cloc loc; int fd; }; static random_fn random_generate; static void random_generate(void *data, int32_t bytes, uint8_t *buff) { struct rgen_data *st=data; int r; r= read(st->fd,buff,bytes); assert(r == bytes); /* This is totally crap error checking, but callers of * this function do not check the return value and dealing * with failure of this everywhere would be very inconvenient. */ } static list_t *random_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct rgen_data *st; item_t *arg1, *arg2; string_t filename=NULL; NEW(st); st->cl.description="randomsource"; st->cl.type=CL_RANDOMSRC; st->cl.apply=NULL; st->cl.interface=&st->ops; st->ops.st=st; st->ops.blocking=False; st->ops.generate=random_generate; st->loc=loc; arg1=list_elem(args,0); arg2=list_elem(args,1); if (!arg1) { cfgfatal(loc,"randomsource","requires a filename\n"); } if (arg1->type != t_string) { cfgfatal(arg1->loc,"randomsource", "filename (arg1) must be a string\n"); } filename=arg1->data.string; if (arg2) { if (arg2->type != t_bool) { cfgfatal(arg2->loc,"randomsource", "blocking parameter (arg2) must be bool\n"); } st->ops.blocking=arg2->data.bool_; } if (!filename) { cfgfatal(loc,"randomsource","requires a filename\n"); } st->fd=open(filename,O_RDONLY); if (st->fd<0) { fatal_perror("randomsource (%s:%d): cannot open %s",arg1->loc.file, arg1->loc.line,filename); } return new_closure(&st->cl); } void random_module(dict_t *d) { add_closure(d,"randomfile",random_apply); } work/resolver.c0000664000000000000000000001301015063477206010742 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ /* Name resolution using adns */ #include #include "secnet.h" #include "util.h" #ifndef HAVE_LIBADNS #error secnet requires ADNS version 1.0 or above #endif #include #include #include struct adns { closure_t cl; struct resolver_if ops; struct cloc loc; adns_state ast; }; struct query { void *cst; const char *name; int port; struct comm_if *comm; resolve_answer_fn *answer; adns_query query; }; static resolve_request_fn resolve_request; static bool_t resolve_request(void *sst, cstring_t name, int port, struct comm_if *comm, resolve_answer_fn *cb, void *cst) { struct adns *st=sst; struct query *q; int rv; const int maxlitlen= #ifdef CONFIG_IPV6 ADNS_ADDR2TEXT_BUFLEN*2 #else 50 #endif ; ssize_t l=strlen(name); if (name[0]=='[' && l2 && name[l-1]==']') { char trimmed[maxlitlen+1]; memcpy(trimmed,name+1,l-2); trimmed[l-2]=0; struct comm_addr ca; ca.comm=comm; ca.ix=-1; #ifdef CONFIG_IPV6 socklen_t salen=sizeof(ca.ia); rv=adns_text2addr(trimmed, port, adns_qf_addrlit_ipv4_quadonly, &ca.ia.sa, &salen); assert(rv!=ENOSPC); if (rv) { char msg[250]; snprintf(msg,sizeof(msg),"invalid address literal: %s", strerror(rv)); msg[sizeof(msg)-1]=0; cb(cst,0,0,0,name,msg); } else { cb(cst,&ca,1,1,name,0); } #else ca.ia.sin.sin_family=AF_INET; ca.ia.sin.sin_port=htons(port); if (inet_aton(trimmed,&ca.ia.sin.sin_addr)) cb(cst,&ca,1,1,name,0); else cb(cst,0,0,0,name,"invalid IP address"); #endif return True; } NEW(q); q->cst=cst; q->comm=comm; q->port=port; q->name=name; q->answer=cb; rv=adns_submit(st->ast, name, adns_r_addr, 0, q, &q->query); if (rv) { Message(M_WARNING, "resolver: failed to submit lookup for %s: %s",name, adns_strerror(rv)); free(q); return False; } return True; } static int resolver_beforepoll(void *sst, struct pollfd *fds, int *nfds_io, int *timeout_io) { struct adns *st=sst; return adns_beforepoll(st->ast, fds, nfds_io, timeout_io, tv_now); } static void resolver_afterpoll(void *sst, struct pollfd *fds, int nfds) { struct adns *st=sst; adns_query aq; adns_answer *ans; void *qp; struct query *q; int rv; adns_afterpoll(st->ast, fds, nfds, tv_now); while (True) { aq=NULL; rv=adns_check(st->ast, &aq, &ans, &qp); if (rv==0) { q=qp; if (ans->status!=adns_s_ok) { q->answer(q->cst,NULL,0,0,q->name,adns_strerror(ans->status)); free(q); free(ans); } else { int rslot, wslot, total; int ca_len=MIN(ans->nrrs,MAX_PEER_ADDRS); struct comm_addr ca_buf[ca_len]; for (rslot=0, wslot=0, total=0; rslotnrrs; rslot++) { total++; if (!(wslotrrs.addr[rslot]; struct comm_addr *ca=&ca_buf[wslot]; ca->comm=q->comm; ca->ix=-1; assert(ra->len <= (int)sizeof(ca->ia)); memcpy(&ca->ia,&ra->addr,ra->len); switch (ra->addr.sa.sa_family) { case AF_INET: assert(ra->len == sizeof(ca->ia.sin)); ca->ia.sin.sin_port=htons(q->port); break; #ifdef CONFIG_IPV6 case AF_INET6: assert(ra->len == sizeof(ca->ia.sin6)); ca->ia.sin6.sin6_port=htons(q->port); break; #endif /*CONFIG_IPV6*/ default: /* silently skip unexpected AFs from adns */ continue; } wslot++; } q->answer(q->cst,ca_buf,wslot,total,q->name,0); free(q); free(ans); } } else if (rv==EAGAIN || rv==ESRCH) { break; } else { fatal("resolver_afterpoll: adns_check() returned %d",rv); } } return; } /* Initialise adns, using parameters supplied */ static list_t *adnsresolver_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct adns *st; dict_t *d; item_t *i; string_t conf; NEW(st); st->cl.description="adns"; st->cl.type=CL_RESOLVER; st->cl.apply=NULL; st->cl.interface=&st->ops; st->loc=loc; st->ops.st=st; st->ops.request=resolve_request; i=list_elem(args,0); if (!i || i->type!=t_dict) { cfgfatal(st->loc,"adns","first argument must be a dictionary\n"); } d=i->data.dict; conf=dict_read_string(d,"config",False,"adns",loc); adns_initflags iflags = 0; #if USE_MONOTONIC iflags |= adns_if_monotonic; #endif if (conf) { if (adns_init_strcfg(&st->ast, iflags, 0, conf)) { fatal_perror("Failed to initialise ADNS"); } } else { if (adns_init(&st->ast, iflags, 0)) { fatal_perror("Failed to initialise ADNS"); } } register_for_poll(st, resolver_beforepoll, resolver_afterpoll, "resolver"); return new_closure(&st->cl); } void resolver_module(dict_t *dict) { add_closure(dict,"adns",adnsresolver_apply); } work/rsa.c0000664000000000000000000005127515063477206007705 0ustar /* * rsa.c: implementation of RSA with PKCS#1 padding */ /* * This file is Free Software. It was originally written for secnet. * * Copyright 1995-2003 Stephen Early * Copyright 2002-2014 Ian Jackson * Copyright 2001 Simon Tatham * Copyright 2013 Mark Wooding * * You may redistribute secnet as a whole and/or modify it under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 3, or (at your option) any * later version. * * You may redistribute this file and/or modify it under the terms of * the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later * version. * * This software 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 software; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include #include #include #include "secnet.h" #include "util.h" #include "unaligned.h" #define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n" #define mpp(s,n) do { char *p = mpz_get_str(NULL,16,n); printf("%s 0x%sL\n", s, p); free(p); } while (0) struct rsacommon { uint8_t *hashbuf; }; #define FREE(b) ({ free((b)); (b)=0; }) struct load_ctx { void (*verror)(struct load_ctx *l, struct cloc loc, FILE *maybe_f, const char *message, va_list args); bool_t (*postreadcheck)(struct load_ctx *l, FILE *f); const char *what; dict_t *deprdict; /* used only to look up hash */ struct cloc loc; union { struct { struct log_if *log; } tryload; } u; }; static void load_err(struct load_ctx *l, const struct cloc *maybe_loc, FILE *maybe_f, const char *fmt, ...) { va_list al; va_start(al,fmt); l->verror(l, maybe_loc ? *maybe_loc : l->loc, maybe_f,fmt,al); va_end(al); } FORMAT(printf,4,0) static void verror_tryload(struct load_ctx *l, struct cloc loc, FILE *maybe_f, const char *message, va_list args) { int class=M_ERR; slilog_part(l->u.tryload.log,class,"%s: ",l->what); vslilog(l->u.tryload.log,class,message,args); } static void verror_cfgfatal(struct load_ctx *l, struct cloc loc, FILE *maybe_f, const char *message, va_list args) { vcfgfatal_maybefile(maybe_f,l->loc,l->what,message,args,""); } struct rsapriv { closure_t cl; struct sigprivkey_if ops; struct cloc loc; struct rsacommon common; MP_INT n; MP_INT p, dp; MP_INT q, dq; MP_INT w; }; #define RSAPUB_BNS(each) \ each(0,e,"public exponent") \ each(1,n,"modulus") #define RSAPUB_LOADCORE_PASSBN(ix,en,what) \ en##s, en##_loc, #define RSAPUB_INIT_ST_BN( ix,en,what) mpz_init (&st->en); #define RSAPUB_CLEAR_ST_BN(ix,en,what) mpz_clear(&st->en); struct rsapub { closure_t cl; struct sigpubkey_if ops; struct cloc loc; struct rsacommon common; MP_INT e; MP_INT n; }; /* Sign data. NB data must be smaller than modulus */ #define RSA_MAX_MODBYTES 2048 /* The largest modulus I've seen is 15360 bits, which works out at 1920 * bytes. Using keys this big is quite implausible, but it doesn't cost us * much to support them. */ static const char *hexchars="0123456789abcdef"; static void rsa_sethash(struct load_ctx *l, struct rsacommon *c, const struct hash_if **in_ops) { struct hash_if *hash=0; if (l->deprdict) hash=find_cl_if(l->deprdict,"hash",CL_HASH,False,"site",l->loc); if (!hash) hash=sha1_hash_if; c->hashbuf=safe_malloc(hash->hlen, "generate_msg"); *in_ops=hash; } static void rsacommon_dispose(struct rsacommon *c) { free(c->hashbuf); } static void emsa_pkcs1(MP_INT *n, MP_INT *m, const uint8_t *data, int32_t datalen) { char buff[2*RSA_MAX_MODBYTES + 1]; int msize, i; /* RSA PKCS#1 v1.5 signature padding: * * <------------ msize hex digits ----------> * * 00 01 ff ff .... ff ff 00 vv vv vv .... vv * * <--- datalen --> * bytes * = datalen*2 hex digits * * NB that according to PKCS#1 v1.5 we're supposed to include a * hash function OID in the data. We don't do that (because we * don't have the hash function OID to hand here), thus violating * the spec in a way that affects interop but not security. * * -iwj 17.9.2002 */ msize=mpz_sizeinbase(n, 16); if (datalen*2+6>=msize) { fatal("rsa: message too big"); } strcpy(buff,"0001"); for (i=0; i>4]; buff[msize+(-datalen+i)*2+1]=hexchars[data[i]&0xf]; } buff[msize-datalen*2-2]= '0'; buff[msize-datalen*2-1]= '0'; for (i=4; iops.hash,data,datalen,st->common.hashbuf); /* Construct the message representative. */ emsa_pkcs1(&st->n, &a, st->common.hashbuf, st->ops.hash->hlen); /* * Produce an RSA signature (a^d mod n) using the Chinese * Remainder Theorem. We compute: * * u = a^dp mod p (== a^d mod p, since dp == d mod (p-1)) * v = a^dq mod q (== a^d mod q, similarly) * * We also know w == iqmp * q, which has the property that w == * 0 mod q and w == 1 mod p. So (1-w) has the reverse property * (congruent to 0 mod p and to 1 mod q). Hence we now compute * * b = w * u + (1-w) * v * = w * (u-v) + v * * so that b is congruent to a^d both mod p and mod q. Hence b, * reduced mod n, is the required signature. */ mpz_init(&tmp); mpz_init(&tmp2); mpz_init(&u); mpz_init(&v); mpz_powm_sec(&u, &a, &st->dp, &st->p); mpz_powm_sec(&v, &a, &st->dq, &st->q); mpz_sub(&tmp, &u, &v); mpz_mul(&tmp2, &tmp, &st->w); mpz_add(&tmp, &tmp2, &v); mpz_mod(&b, &tmp, &st->n); mpz_clear(&tmp); mpz_clear(&tmp2); mpz_clear(&u); mpz_clear(&v); signature=write_mpstring(&b); uint8_t *op = buf_append(msg,2); if (!op) { ok=False; goto out; } size_t l = strlen(signature); assert(l < 65536); put_uint16(op, l); op = buf_append(msg,l); if (!op) { ok=False; goto out; } memcpy(op, signature, l); ok = True; out: free(signature); mpz_clear(&b); mpz_clear(&a); return ok; } static bool_t rsa_sig_unpick(void *sst, struct buffer_if *msg, struct alg_msg_data *sig) { uint8_t *lp = buf_unprepend(msg, 2); if (!lp) return False; sig->len = get_uint16(lp); sig->start = buf_unprepend(msg, sig->len); if (!sig->start) return False; /* In `rsa_sig_check' below, we assume that we can write a nul * terminator following the signature. Make sure there's enough space. */ if (msg->start >= msg->base + msg->alloclen) return False; return True; } static sig_checksig_fn rsa_sig_check; static bool_t rsa_sig_check(void *sst, uint8_t *data, int32_t datalen, const struct alg_msg_data *sig) { struct rsapub *st=sst; MP_INT a, b, c; bool_t ok; mpz_init(&a); mpz_init(&b); mpz_init(&c); hash_hash(st->ops.hash,data,datalen,st->common.hashbuf); emsa_pkcs1(&st->n, &a, st->common.hashbuf, st->ops.hash->hlen); /* Terminate signature with a '0' - already checked that this will fit */ int save = sig->start[sig->len]; sig->start[sig->len] = 0; mpz_set_str(&b, sig->start, 16); sig->start[sig->len] = save; mpz_powm(&c, &b, &st->e, &st->n); ok=(mpz_cmp(&a, &c)==0); mpz_clear(&c); mpz_clear(&b); mpz_clear(&a); return ok; } static void rsapub_dispose(void *sst) { struct rsapub *st=sst; if (!st) return; RSAPUB_BNS(RSAPUB_CLEAR_ST_BN) rsacommon_dispose(&st->common); free(st); } #define RSAPUB_LOADCORE_DEFBN(ix,en,what) \ const char *en##s, struct cloc en##_loc, #define LDPUBFATAL(lc,...) ({ \ load_err(l,(lc),0,__VA_ARGS__); \ goto error_out; \ }) static struct rsapub *rsa_loadpub_core(RSAPUB_BNS(RSAPUB_LOADCORE_DEFBN) struct load_ctx *l) { struct rsapub *st; NEW(st); st->cl.description="rsapub"; st->cl.type=CL_SIGPUBKEY; st->cl.apply=NULL; st->cl.interface=&st->ops; st->ops.st=st; st->common.hashbuf=NULL; st->ops.unpick=rsa_sig_unpick; st->ops.check=rsa_sig_check; st->ops.hash=0; st->ops.dispose=rsapub_dispose; st->loc=l->loc; RSAPUB_BNS(RSAPUB_INIT_ST_BN) #define RSAPUB_LOADCORE_GETBN(ix,en,what) \ if (mpz_init_set_str(&st->en,en##s,10)!=0) { \ LDPUBFATAL(&en##_loc, what " \"%s\" is not a " \ "decimal number string",en##s); \ } \ if (mpz_sizeinbase(&st->en, 256) > RSA_MAX_MODBYTES) { \ LDPUBFATAL(&en##_loc, "implausibly large " what); \ } RSAPUB_BNS(RSAPUB_LOADCORE_GETBN) rsa_sethash(l,&st->common,&st->ops.hash); return st; error_out: rsapub_dispose(st); return 0; } static list_t *rsapub_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct load_ctx l[1]; l->verror=verror_cfgfatal; l->postreadcheck=0; l->what="rsa-public"; l->deprdict=context; l->loc=loc; #define RSAPUB_APPLY_GETBN(ix,en,what) \ item_t *en##i; \ const char *en##s; \ en##i=list_elem(args,ix); \ if (!en##i) \ cfgfatal(loc,"rsa-public", \ "you must provide an encryption key\n"); \ struct cloc en##_loc=en##i->loc; \ if (en##i->type!=t_string) \ cfgfatal(en##_loc,"rsa-public", \ "first argument must be a string\n"); \ en##s=en##i->data.string; RSAPUB_BNS(RSAPUB_APPLY_GETBN) struct rsapub *st=rsa_loadpub_core(RSAPUB_BNS(RSAPUB_LOADCORE_PASSBN) l); return new_closure(&st->cl); } bool_t rsa1_loadpub(const struct sigscheme_info *algo, struct buffer_if *pubkeydata, struct sigpubkey_if **sigpub_r, closure_t **closure_r, struct log_if *log, struct cloc loc) { struct rsapub *st=0; struct load_ctx l[1]; l->verror=verror_tryload; l->postreadcheck=0; l->what="rsa1_loadpub"; l->deprdict=0; l->loc=loc; l->u.tryload.log=log; char *nul=buf_append(pubkeydata,1); if (!nul) LDPUBFATAL(0,"rsa1 public key data too long for extra nul"); *nul=0; const char *delim=" \t\n"; char *saveptr; /*unused*/ strtok_r(pubkeydata->start,delim,&saveptr); #define RSAPUB_TRYLOAD_GETBN(ix,en,what) \ struct cloc en##_loc=loc; \ const char *en##s=strtok_r(0,delim,&saveptr); \ if (!en##s) LDPUBFATAL(0,"end of pubkey data looking for " what); RSAPUB_BNS(RSAPUB_TRYLOAD_GETBN); st=rsa_loadpub_core(RSAPUB_BNS(RSAPUB_LOADCORE_PASSBN) l); if (!st) goto error_out; *sigpub_r=&st->ops; *closure_r=&st->cl; return True; error_out: rsapub_dispose(st); return False; } #define LDFATAL(...) ({ load_err(l,0,0,__VA_ARGS__); goto error_out; }) #define LDFATAL_FILE(...) ({ load_err(l,0,f,__VA_ARGS__); goto error_out; }) #define KEYFILE_GET(is) ({ \ uint##is##_t keyfile_get_tmp=keyfile_get_##is(l,f); \ if (!l->postreadcheck(l,f)) goto error_out; \ keyfile_get_tmp; \ }) static uint32_t keyfile_get_32(struct load_ctx *l, FILE *f) { uint32_t r; r=fgetc(f)<<24; r|=fgetc(f)<<16; r|=fgetc(f)<<8; r|=fgetc(f); return r; } static uint16_t keyfile_get_16(struct load_ctx *l, FILE *f) { uint16_t r; r=fgetc(f)<<8; r|=fgetc(f); return r; } static void rsapriv_dispose(void *sst) { struct rsapriv *st=sst; mpz_clear(&st->n); mpz_clear(&st->p); mpz_clear(&st->dp); mpz_clear(&st->q); mpz_clear(&st->dq); mpz_clear(&st->w); rsacommon_dispose(&st->common); free(st); } static struct rsapriv *rsa_loadpriv_core(struct load_ctx *l, FILE *f, struct cloc loc, bool_t do_validity_check) { struct rsapriv *st=0; long length; uint8_t *b=0, *c=0; int cipher_type; MP_INT e,d,iqmp,tmp,tmp2,tmp3; bool_t valid; mpz_init(&e); mpz_init(&d); mpz_init(&iqmp); mpz_init(&tmp); mpz_init(&tmp2); mpz_init(&tmp3); NEW(st); st->cl.description="rsapriv"; st->cl.type=CL_SIGPRIVKEY; st->cl.apply=NULL; st->cl.interface=&st->ops; st->ops.st=st; st->common.hashbuf=NULL; st->ops.sign=rsa_sign; st->ops.hash=0; st->ops.dispose=rsapriv_dispose; st->loc=loc; mpz_init(&st->n); mpz_init(&st->q); mpz_init(&st->p); mpz_init(&st->dp); mpz_init(&st->dq); mpz_init(&st->w); if (!f) { assert(just_check_config); goto assume_valid; } /* Check that the ID string is correct */ length=strlen(AUTHFILE_ID_STRING)+1; b=safe_malloc(length,"rsapriv_apply"); if (fread(b,length,1,f)!=1 || memcmp(b,AUTHFILE_ID_STRING,length)!=0) { LDFATAL_FILE("failed to read magic ID" " string from SSH1 private keyfile\n"); } FREE(b); cipher_type=fgetc(f); KEYFILE_GET(32); /* "Reserved data" */ if (cipher_type != 0) { LDFATAL("we don't support encrypted keyfiles\n"); } /* Read the public key */ KEYFILE_GET(32); /* Not sure what this is */ length=(KEYFILE_GET(16)+7)/8; if (length>RSA_MAX_MODBYTES) { LDFATAL("implausible length %ld for modulus\n", length); } b=safe_malloc(length,"rsapriv_apply"); if (fread(b,length,1,f) != 1) { LDFATAL_FILE("error reading modulus\n"); } read_mpbin(&st->n,b,length); FREE(b); length=(KEYFILE_GET(16)+7)/8; if (length>RSA_MAX_MODBYTES) { LDFATAL("implausible length %ld for e\n",length); } b=safe_malloc(length,"rsapriv_apply"); if (fread(b,length,1,f)!=1) { LDFATAL_FILE("error reading e\n"); } read_mpbin(&e,b,length); FREE(b); length=KEYFILE_GET(32); if (length>1024) { LDFATAL("implausibly long (%ld) key comment\n", length); } c=safe_malloc(length+1,"rsapriv_apply"); if (fread(c,length,1,f)!=1) { LDFATAL_FILE("error reading key comment\n"); } c[length]=0; /* Check that the next two pairs of characters are identical - the keyfile is not encrypted, so they should be */ if (KEYFILE_GET(16) != KEYFILE_GET(16)) { LDFATAL("corrupt keyfile\n"); } /* Read d */ length=(KEYFILE_GET(16)+7)/8; if (length>RSA_MAX_MODBYTES) { LDFATAL("implausibly long (%ld) decryption key\n", length); } b=safe_malloc(length,"rsapriv_apply"); if (fread(b,length,1,f)!=1) { LDFATAL_FILE("error reading decryption key\n"); } read_mpbin(&d,b,length); FREE(b); /* Read iqmp (inverse of q mod p) */ length=(KEYFILE_GET(16)+7)/8; if (length>RSA_MAX_MODBYTES) { LDFATAL("implausibly long (%ld)" " iqmp auxiliary value\n", length); } b=safe_malloc(length,"rsapriv_apply"); if (fread(b,length,1,f)!=1) { LDFATAL_FILE("error reading decryption key\n"); } read_mpbin(&iqmp,b,length); FREE(b); /* Read q (the smaller of the two primes) */ length=(KEYFILE_GET(16)+7)/8; if (length>RSA_MAX_MODBYTES) { LDFATAL("implausibly long (%ld) q value\n", length); } b=safe_malloc(length,"rsapriv_apply"); if (fread(b,length,1,f)!=1) { LDFATAL_FILE("error reading q value\n"); } read_mpbin(&st->q,b,length); FREE(b); /* Read p (the larger of the two primes) */ length=(KEYFILE_GET(16)+7)/8; if (length>RSA_MAX_MODBYTES) { LDFATAL("implausibly long (%ld) p value\n", length); } b=safe_malloc(length,"rsapriv_apply"); if (fread(b,length,1,f)!=1) { LDFATAL_FILE("error reading p value\n"); } read_mpbin(&st->p,b,length); FREE(b); if (ferror(f)) { fatal_perror("rsa-private (%s:%d): ferror",loc.file,loc.line); } rsa_sethash(l,&st->common,&st->ops.hash); /* * Now verify the validity of the key, and set up the auxiliary * values for fast CRT signing. */ valid=False; if (do_validity_check) { /* Verify that p*q is equal to n. */ mpz_mul(&tmp, &st->p, &st->q); if (mpz_cmp(&tmp, &st->n) != 0) goto done_checks; /* * Verify that d*e is congruent to 1 mod (p-1), and mod * (q-1). This is equivalent to it being congruent to 1 mod * lambda(n) = lcm(p-1,q-1). The usual `textbook' condition, * that d e == 1 (mod (p-1)(q-1)) is sufficient, but not * actually necessary. */ mpz_mul(&tmp, &d, &e); mpz_sub_ui(&tmp2, &st->p, 1); mpz_mod(&tmp3, &tmp, &tmp2); if (mpz_cmp_si(&tmp3, 1) != 0) goto done_checks; mpz_sub_ui(&tmp2, &st->q, 1); mpz_mod(&tmp3, &tmp, &tmp2); if (mpz_cmp_si(&tmp3, 1) != 0) goto done_checks; /* Verify that q*iqmp is congruent to 1 mod p. */ mpz_mul(&tmp, &st->q, &iqmp); mpz_mod(&tmp2, &tmp, &st->p); if (mpz_cmp_si(&tmp2, 1) != 0) goto done_checks; } /* Now we know the key is valid (or we don't care). */ valid = True; /* * Now we compute auxiliary values dp, dq and w to allow us * to use the CRT optimisation when signing. * * dp == d mod (p-1) so that a^dp == a^d mod p, for all a * dq == d mod (q-1) similarly mod q * w == iqmp * q so that w == 0 mod q, and w == 1 mod p */ mpz_sub_ui(&tmp, &st->p, 1); mpz_mod(&st->dp, &d, &tmp); mpz_sub_ui(&tmp, &st->q, 1); mpz_mod(&st->dq, &d, &tmp); mpz_mul(&st->w, &iqmp, &st->q); done_checks: if (!valid) { LDFATAL("file does not contain a " "valid RSA key!\n"); } assume_valid: out: mpz_clear(&tmp); mpz_clear(&tmp2); mpz_clear(&tmp3); FREE(b); FREE(c); mpz_clear(&e); mpz_clear(&d); mpz_clear(&iqmp); return st; error_out: if (st) rsapriv_dispose(st); st=0; goto out; } static bool_t postreadcheck_tryload(struct load_ctx *l, FILE *f) { assert(!ferror(f)); if (feof(f)) { load_err(l,0,0,"eof mid-integer"); return False; } return True; } bool_t rsa1_loadpriv(const struct sigscheme_info *algo, struct buffer_if *privkeydata, struct sigprivkey_if **sigpriv_r, closure_t **closure_r, struct log_if *log, struct cloc loc) { FILE *f=0; struct rsapriv *st=0; f=fmemopen(privkeydata->start,privkeydata->size,"r"); if (!f) { slilog(log,M_ERR,"failed to fmemopen private key file\n"); goto error_out; } struct load_ctx l[1]; l->what="rsa1priv load"; l->verror=verror_tryload; l->postreadcheck=postreadcheck_tryload; l->deprdict=0; l->loc=loc; l->u.tryload.log=log; st=rsa_loadpriv_core(l,f,loc,False); if (!st) goto error_out; goto out; error_out: FREE(st); out: if (f) fclose(f); if (!st) return False; *sigpriv_r=&st->ops; *closure_r=&st->cl; return True; } static bool_t postreadcheck_apply(struct load_ctx *l, FILE *f) { cfgfile_postreadcheck(l->loc,f); return True; } static list_t *rsapriv_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct rsapriv *st; item_t *i; cstring_t filename; FILE *f; struct load_ctx l[1]; l->what="rsa-private"; l->verror=verror_cfgfatal; l->postreadcheck=postreadcheck_apply; l->deprdict=context; l->loc=loc; /* Argument is filename pointing to SSH1 private key file */ i=list_elem(args,0); if (i) { if (i->type!=t_string) { cfgfatal(i->loc,"rsa-private","first argument must be a string\n"); } filename=i->data.string; } else { filename=NULL; /* Make compiler happy */ cfgfatal(i->loc,"rsa-private","you must provide a filename\n"); } f=fopen(filename,"rb"); if (!f) { if (just_check_config) { Message(M_WARNING,"rsa-private (%s:%d): cannot open keyfile " "\"%s\" (%s); assuming it's valid while we check the " "rest of the configuration\n", loc.file,loc.line,filename,strerror(errno)); return list_new(); } else { fatal_perror("rsa-private (%s:%d): cannot open file \"%s\"", loc.file,loc.line,filename); } } bool_t do_validity_check=True; i=list_elem(args,1); if (i && i->type==t_bool && i->data.bool_==False) { Message(M_INFO,"rsa-private (%s:%d): skipping RSA key validity " "check\n",loc.file,loc.line); do_validity_check=False; } st=rsa_loadpriv_core(l,f,loc,do_validity_check); fclose(f); return new_closure(&st->cl); } void rsa_module(dict_t *dict) { add_closure(dict,"rsa-private",rsapriv_apply); add_closure(dict,"rsa-public",rsapub_apply); } work/secnet-wireshark.lua0000664000000000000000000006501115063477206012726 0ustar --- -*-lua-*- --- --- This file is part of secnet. --- See README for full list of copyright holders. --- --- secnet 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. --- --- secnet 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 --- version 3 along with secnet; if not, see --- https://www.gnu.org/licenses/gpl.html. local secnet = Proto("secnet", "Secnet VPN") ----------------------------------------------------------------------------- --- Session tracking. --- --- This is the hardest part of the dissector. -- Timelines. A timeline associates pieces of information with times T. local function tl_new() -- Return a fresh shiny timeline. return { } end local function tl__find(tl, t) -- Find and return the earliest association in TL not earlier than T. If -- there is no such entry, return nil. local lo = 1 local hi = #tl + 1 -- Plain old binary search. The active interval is half-open, [lo, hi). while true do local w = hi - lo if w == 0 then return nil end local mid = lo + math.floor(w/2) local tv = tl[mid] if tv.t > t then hi = mid elseif tv.t == t or w == 1 then return tv else lo = mid end end end local function tl_find(tl, t) -- Find and return the state of the timeline at time T, i.e., the earliest -- value in TL not earlier than T. If there is no such entry, return nil. local tv = tl__find(tl, t) if tv == nil then return nil else return tv.v end end local function tl_add(tl, t, v) -- Associate the value V with time T in TL. local tv = tl__find(tl, t) if tv ~= nil and tv.t == t then tv.v = v else -- Append the new item. If necessary, sort the vector; we expect that -- we'll see everything in the right order, so this won't be a problem. local n = #tl tl[n + 1] = { t = t, v = v } if n > 0 and tl[n].t > t then table.sort(tl, function (tv0, tv1) return tv0.t < tv1.t end) end end end local function dump_timeline(tl, cvt) -- Dump a timeline TL, using the function CVT to convert each value to a -- string. for _, tv in ipairs(tl) do print("\t" .. tv.t .. ": " .. cvt(tv.v)) end end local function get_timeline_create(map, index) -- If MAP[INDEX] exists, return it; otherwise set MAP[INDEX] to a fresh -- timeline and return that. local tl = map[index] if tl == nil then tl = tl_new(); map[index] = tl end return tl end local function lookup_timeline(map, index, t) -- If it exists, MAP[INDEX] should be a timeline; find its state at time T. -- Return nil if there's nothing there, or T is too early. local tl = map[index] if tl == nil then return nil else return tl_find(tl, t) end end -- The `SITEMAP' maps site names to little structures. -- -- * `algs' is a map from peer site names to a timeline of structures -- described below. -- -- * `index' is a map from site indices to a timeline of names, reflecting -- that, at some time T, this site thought that some index I referred to -- a peer site P. -- -- The `algs' map contains the following slots, populated during . -- -- * `xform' is a timeline of transform names. local SITEMAP = { } -- The `ADDRMAP' maps (IPv4 or IPv6) socket addresses in the form -- `[ADDR]:PORT' to a timeline of site names, populated based on claims made -- by senders about themselves. The `GUESSMAP' is similar, but populated -- based on assertions about recipients. local ADDRMAP = { } local GUESSMAP = { } local function snd_sockname(st) -- Return the sender's socket name as a thing which can be used as a table -- index. local pinfo = st.pinfo return string.format("[%s]:%d", pinfo.net_src, pinfo.src_port) end local function rcv_sockname(st) -- Return the recipient's socket name as a thing which can be used as a -- table index. local pinfo = st.pinfo return string.format("[%s]:%d", pinfo.net_dst, pinfo.dst_port) end local function get_site_create(name) -- If NAME refers to a known site, then return its information structure; -- otherwise create a new one and return that. local site = SITEMAP[name] if site == nil then site = { algs = { }, index = { } } SITEMAP[name] = site end return site end local function notice_site_name(map, st, sock, name) -- Record in MAP that the packet described in the state ST tells us that, -- at that time, the site NAME appeared to be at address SOCK. tl_add(get_timeline_create(map, sock), st.pinfo.rel_ts, name) end local function dump_algs(algs) -- Dump the algorithms selection ALGS from a site structure. return "xform=" .. algs.transform end local function dump_str(str) return str end local function dump_addrmap(what, map) -- Dump MAP, which is an address map like `ADDRMAP' or `GUESSMAP'; WHAT is -- a string describing which map it is. print(what .. "...") for addr, tl in pairs(map) do print(" " .. addr) dump_timeline(tl, dump_str) end end local function dump_tracking_state() -- Dump the entire tracking state to standard output. dump_addrmap("Address map", ADDRMAP) dump_addrmap("Guess map", GUESSMAP) print("Site map...") for name, site in pairs(SITEMAP) do print(" " .. name) print(" algs...") for peer, tl in pairs(site.algs) do print(" " .. peer) dump_timeline(tl, dump_algs) end print(" index...") for ix, tl in pairs(site.index) do print(" " .. ix) dump_timeline(tl, dump_str) end end end local function notice_sndname(st, name) -- Record that sender of the packet described by state ST is called NAME. st.sndname = name notice_site_name(ADDRMAP, st, snd_sockname(st), name) end local function notice_rcvname(st, name) -- Record that the sender of the packet described by ST thought that its -- recipient was called NAME. st.rcvname = name notice_site_name(GUESSMAP, st, rcv_sockname(st), name) if st.sndname ~= nil then local site = get_site_create(st.sndname) tl_add(get_timeline_create(site.index, st.sndix), st.pinfo.rel_ts, name) end end -- Tables describing the kinds of algorithms which can be selected. local CAPTAB = { [8] = { name = "serpent256cbc", kind = "transform", desc = "Deprecated Serpent256-CBC transform" }, [9] = { name = "eaxserpent", kind = "transform", desc = "Serpent256-EAX transform" }, [31] = { name = "mobile-priority", kind = "early", desc = "Mobile site takes priority in case of MSG1 crossing" } } local function get_algname(kind, cap, dflt) -- Fetch an algorithm of the given KIND, given its capability number CAP; -- if CAP is nil, then return DFLT instead. local name if cap == nil then name = dflt else local info = CAPTAB[cap] if info ~= nil and info.kind == kind then name = info.name else name = string.format("Unknown %s #%d", kind, cap) end end return name end local function notice_alg_selection(st) -- Record the algorithm selections declared in the packet described by ST. local transform = get_algname("transform", st.transform, "serpent256cbc") local site = get_site_create(st.sndname) local peer = get_site_create(st.rcvname) local now = st.pinfo.rel_ts local algs = { transform = transform } tl_add(get_timeline_create(site.algs, st.rcvname), now, algs) tl_add(get_timeline_create(peer.algs, st.sndname), now, algs) end ----------------------------------------------------------------------------- --- Protocol dissection primitives. local PF = { } -- The table of protocol fields, filled in later. local F = { } -- A table of field values, also filled in later. local function msgcode(major, minor) -- Construct a Secnet message number according to the complicated rules. local majlo = bit.band(major, 0x000f) local majhi = bit.band(major, 0xfff0) local minlo = bit.band(minor, 0x000f) local minhi = bit.band(minor, 0xfff0) return bit.bxor(bit.lshift(majlo, 0), bit.lshift(majlo, 8), bit.lshift(majlo, 16), bit.lshift(majlo, 24), bit.lshift(majhi, 4), bit.lshift(minlo, 4), bit.lshift(minlo, 28), bit.lshift(minhi, 16)) end local function msgmajor(label) -- Return the major message number from a LABEL. local lo = bit.band(label, 0x000f) local hi = bit.band(bit.rshift(label, 4), 0xfff0) return bit.bxor(lo, bit.lshift(lo, 4), bit.lshift(lo, 12), hi) end local function msgminor(label) -- Return the minor message number from a LABEL. return bit.bxor(bit.lshift(bit.band(label, 0x00ff), 8), bit.band(bit.rshift(label, 4), 0x000f), bit.band(bit.rshift(label, 16), 0xfff0)) end -- Main message-number table. local M = { NAK = msgcode( 0, 0), MSG0 = msgcode(0x2020, 0), -- ! MSG1 = msgcode( 1, 0), MSG2 = msgcode( 2, 0), MSG3 = msgcode( 3, 0), MSG3BIS = msgcode( 3, 1), MSG4 = msgcode( 4, 0), MSG5 = msgcode( 5, 0), MSG6 = msgcode( 6, 0), MSG7 = msgcode( 7, 0), MSG8 = msgcode( 8, 0), MSG9 = msgcode( 9, 0), PROD = msgcode( 10, 0)} -- The `dissect_*' functions follow a common protocol. They parse a thing -- from a packet buffer BUF, of size SZ, starting from POS, and store -- interesting things in a given TREE; when they're done, they return the -- updated index where the next interesting thing might be, and maybe store -- interesting things in the state ST. As a result, it's usually a simple -- matter to parse a packet by invoking the appropriate primitive dissectors -- in the right order. local function dissect_sequence(dissect, st, buf, tree, pos, sz) -- Dissect pieces of the packed in BUF with each of the dissectors in the -- list DISSECT in turn. for _, d in ipairs(dissect) do pos = d(st, buf, tree, pos, sz) end return pos end local function dissect_wtf(st, buf, tree, pos, sz) -- If POS is not at the end of the buffer, note that there's unexpected -- stuff in the packet. if pos < sz then tree:add(PF["secnet.wtf"], buf(pos, sz - pos)) end return sz end local dissect_caps do -- This will be a list of the capability protocol field names, in the right -- order. We just have to figure out what that will be. local caplist = { } do local caps = { } -- Firstly, build, in `caps', a list of the capability names and their -- numbers. local i = 1 for j, cap in pairs(CAPTAB) do caps[i] = { i = j, cap = cap.name } i = i + 1 end -- Sort the list. Now they're in the right order. table.sort(caps, function (v0, v1) return v0.i < v1.i end) -- Finally, write the entries to `caplist', with the `user' entry at the -- start and the `unassigned' entry at the end. i = 1 caplist[i] = "secnet.cap.user"; i = i + 1 for _, v in ipairs(caps) do caplist[i] = "secnet.cap." .. v.cap i = i + 1 end caplist[i] = "secnet.cap.unassigned"; i = i + 1 end function dissect_caps(st, buf, tree, pos, sz) -- Dissect a capabilities word. if pos < sz then local cap = tree:add(PF["secnet.cap"], buf(pos, 4)) for _, pf in ipairs(caplist) do cap:add(PF[pf], buf(pos, 4)) end pos = pos + 4 end return pos end end local function dissect_mtu(st, buf, tree, pos, sz) -- Dissect an MTU request. if pos < sz then tree:add(PF["secnet.mtu"], buf(pos, 2)); pos = pos + 2 end return pos end local function make_dissect_name_xinfo(label, dissect_xinfo, hook) -- Return a dissector function for reading a name and extra information. -- The function will dissect a subtree rooted at the protocol field LABEL; -- it will dissect the extra information using the list DISSECT_XINFO -- (processed using `dissect_sequence'); and finally, if the packet hasn't -- been visited yet, it will call HOOK(ST, NAME), where NAME is the name -- string extracted from the packet. return function (st, buf, tree, pos, sz) -- Find the length of the whole thing. local len = buf(pos, 2):uint() -- Make the subtree root. local sub = tree:add(PF[label], buf(pos, len + 2)) -- Find the length of the name. This is rather irritating: I'd like to -- get Wireshark to do this, but it seems that `stringz' doesn't pay -- attention to the buffer limits it's given. So read the whole lot and -- find the null by hand. local name = buf(pos + 2, len):string() local z, _ = string.find(name, "\0", 1, true) if z == nil then z = len else z = z - 1 name = string.sub(name, 1, z) end -- Fill in the subtree. sub:add(PF["secnet.namex.len"], buf(pos, 2)); pos = pos + 2 sub:add(PF["secnet.namex.name"], buf(pos, z)) if z < len then dissect_sequence(dissect_xinfo, st, buf, sub, pos + z + 1, pos + len) end -- Maybe call the hook. if hook ~= nil and not st.pinfo.visited then hook(st, name) end -- We're done. return pos + len end end local function dissect_sndnonce(st, buf, tree, pos, sz) -- Dissect the sender's nonce. tree:add(PF["secnet.kx.sndnonce"], buf(pos, 8)); pos = pos + 8 return pos end local function dissect_rcvnonce(st, buf, tree, pos, sz) -- Dissect the recipient's nonce. tree:add(PF["secnet.kx.rcvnonce"], buf(pos, 8)); pos = pos + 8 return pos end local function dissect_transform(st, buf, tree, pos, sz) -- Dissect the selected transform. Note this in the packet state for -- later. st.transform = buf(pos, 1):uint() tree:add(PF["secnet.kx.transform"], buf(pos, 1)); pos = pos + 1 return pos end local function dissect_lenstr(st, buf, tree, label, pos, sz) -- Dissect a simple string given its length. local len = buf(pos, 2):uint() local sub = tree:add(PF[label], buf(pos, len + 2)) sub:add(PF[label .. ".len"], buf(pos, 2)); pos = pos + 2 sub:add(PF[label .. ".text"], buf(pos, len)); pos = pos + len return pos end local function dissect_dhval(st, buf, tree, pos, sz) -- Dissect a Diffie--Hellman public value. return dissect_lenstr(st, buf, tree, "secnet.kx.dhval", pos, sz) end local function dissect_sig(st, buf, tree, pos, sz) -- Dissect a signature. return dissect_lenstr(st, buf, tree, "secnet.kx.sig", pos, sz) end local function find_algs_lookup(map, sock, now, ix) -- Utility for `find_algs': look SOCK up in the address map ADDR, to find a -- site; find its peer with index IX; and return the algorithm selection -- current between the pair at time NOW. If the lookup fails, return nil. local name = lookup_timeline(map, sock, now) if name == nil then return nil end local site = SITEMAP[name] if site == nil then return nil end local peername = lookup_timeline(site.index, ix, now) if peername == nil then return nil end return lookup_timeline(site.algs, peername, now) end local function find_algs(st) -- Return the algorithm selection which applies to the packet described in -- ST. local now = st.pinfo.rel_ts local sock = snd_sockname(st) local algs = find_algs_lookup(ADDRMAP, sock, now, st.sndix) if algs ~= nil then return algs else return find_algs_lookup(GUESSMAP, sock, now, st.rcvix) end end -- Transform-specific dissectors... local dissect_ct = { } function dissect_ct.unknown(st, why, buf, tree, pos, sz) tree:add(PF["secnet.ciphertext.unknown"], buf(pos, sz - pos), "Ciphertext with unknown structure: " .. why) return sz end function dissect_ct.serpent256cbc(st, buf, tree, pos, sz) tree:add(PF["secnet.ciphertext.iv"], buf(pos, 4)); pos = pos + 4 tree:add(PF["secnet.ciphertext.payload"], buf(pos, sz - pos)) return sz end function dissect_ct.eaxserpent(st, buf, tree, pos, sz) local len = sz - pos - 20 tree:add(PF["secnet.ciphertext.payload"], buf(pos, len)); pos = pos + len tree:add(PF["secnet.ciphertext.tag"], buf(pos, 16)); pos = pos + 16 tree:add(PF["secnet.ciphertext.sequence"], buf(pos, 4)); pos = pos + 4 return pos end local function dissect_ciphertext(st, buf, tree, pos, sz) -- Dissect a ciphertext. local sub = tree:add(PF["secnet.ciphertext"], buf(pos, sz - pos)) local algs = find_algs(st) local xform if algs == nil then xform = nil else xform = algs.transform end if xform == nil then pos = dissect_ct.unknown(st, "unable to find negotiated transform", buf, sub, pos, sz) else local func = dissect_ct[xform] if func == nil then pos = dissect_ct.unknown(st, "unsupported transform " .. xform, buf, sub, pos, sz) else pos = func(st, buf, sub, pos, sz) end end return pos end ----------------------------------------------------------------------------- --- The protocol information table. local PKTINFO = { -- This is the main table which describes the protocol. The top level maps -- message labels to structures: -- -- * `label' is the category code's symbolic name; -- -- * `info' is a prefix for the information column display; and -- -- * `dissect' is a sequence of primitive dissectors to run in order to -- parse the rest of the packet. [M.NAK] = { label = "NAK", info = "Stimulate fresh key exchange", dissect = { dissect_wtf } }, [M.MSG0] = { label = "MSG0", info = "MSG0", dissect = { dissect_ciphertext } }, [M.MSG1] = { label = "MSG1", info = "MSG1", dissect = { make_dissect_name_xinfo("secnet.kx.sndname", { dissect_caps, dissect_wtf }, notice_sndname), make_dissect_name_xinfo("secnet.kx.rcvname", { dissect_wtf }, notice_rcvname), dissect_sndnonce, dissect_wtf } }, [M.MSG2] = { label = "MSG2", info = "MSG2", dissect = { make_dissect_name_xinfo("secnet.kx.sndname", { dissect_caps, dissect_wtf }, notice_sndname), make_dissect_name_xinfo("secnet.kx.rcvname", { dissect_wtf }, notice_rcvname), dissect_sndnonce, dissect_rcvnonce, dissect_wtf } }, [M.MSG3] = { label = "MSG3", info = "MSG3", dissect = { make_dissect_name_xinfo("secnet.kx.sndname", { dissect_caps, dissect_mtu, dissect_wtf }, notice_sndname), make_dissect_name_xinfo("secnet.kx.rcvname", { dissect_wtf }, notice_rcvname), dissect_sndnonce, dissect_rcvnonce, dissect_wtf }, hook = notice_alg_selection }, [M.MSG3BIS] = { label = "MSG3BIS", info = "MSG3BIS", dissect = { make_dissect_name_xinfo("secnet.kx.sndname", { dissect_caps, dissect_mtu, dissect_wtf }, notice_sndname), make_dissect_name_xinfo("secnet.kx.rcvname", { dissect_wtf }, notice_rcvname), dissect_sndnonce, dissect_rcvnonce, dissect_transform, dissect_dhval, dissect_sig, dissect_wtf }, hook = notice_alg_selection }, [M.MSG4] = { label = "MSG4", info = "MSG4", dissect = { make_dissect_name_xinfo("secnet.kx.sndname", { dissect_caps, dissect_mtu, dissect_wtf }, notice_sndname), make_dissect_name_xinfo("secnet.kx.rcvname", { dissect_wtf }, notice_rcvname), dissect_sndnonce, dissect_rcvnonce, dissect_dhval, dissect_sig, dissect_wtf } }, [M.MSG5] = { label = "MSG5", info = "MSG5", dissect = { dissect_ciphertext } }, [M.MSG6] = { label = "MSG6", info = "MSG6", dissect = { dissect_ciphertext } }, [M.PROD] = { label = "PROD", info = "PROD", dissect = { make_dissect_name_xinfo("secnet.kx.sndname", { dissect_caps, dissect_wtf }, notice_sndname), make_dissect_name_xinfo("secnet.kx.rcvname", { dissect_wtf }, notice_rcvname), dissect_wtf } }, } do -- Work through the master table and build the `msgtab'' table, mapping -- message codes to their symbolic names for presentation. local msgtab = { } for i, v in pairs(PKTINFO) do msgtab[i] = v.label end local capmap = { transform = { }, early = { } } for i, v in pairs(CAPTAB) do capmap[v.kind][i] = v.desc end local ftab = { -- The protocol fields. This table maps the field names to structures -- used to build the fields, which are then stored in `PF' (declared way -- above): -- -- * `name' is the field name to show in the dissector tree view; -- -- * `type' is the field type; -- -- * `base' is a tweak describing how the field should be formatted; -- -- * `mask' is used to single out a piece of a larger bitfield; -- -- * `tab' names a mapping table used to convert numerical values to -- symbolic names; and -- -- * `hook' is a hook function to run the first time we see a packet, -- to keep track of things. ["secnet.hdr"] = { name = "Common message header", type = ftypes.NONE }, ["secnet.hdr.rcvix"] = { name = "Recipient's site index for sender", type = ftypes.UINT32, base = base.DEC }, ["secnet.hdr.sndix"] = { name = "Sender's site index for recipient", type = ftypes.UINT32, base = base.DEC }, ["secnet.hdr.label"] = { name = "Message label", type = ftypes.UINT32, base = base.HEX, tab = msgtab }, ["secnet.kx.sndname"] = { name = "Sender's site name and extended information", type = ftypes.NONE }, ["secnet.kx.rcvname"] = { name = "Recipient's site name and extended information", type = ftypes.NONE }, ["secnet.namex.len"] = { name = "Name/extended info length", type = ftypes.UINT16, base = base.DEC }, ["secnet.namex.name"] = { name = "Site name", type = ftypes.STRING, field = true, base = base.ASCII, }, ["secnet.cap"] = { name = "Advertised capability bits", type = ftypes.UINT32, base = base.HEX }, ["secnet.cap.user"] = { name = "User-assigned capability bits", type = ftypes.UINT32, mask = 0x000000ff, base = base.HEX }, ["secnet.mtu"] = { name = "Sender's requested MTU", type = ftypes.UINT16, base = base.DEC }, ["secnet.kx.sndnonce"] = { name = "Sender's nonce", type = ftypes.BYTES, base = base.SPACE }, ["secnet.kx.rcvnonce"] = { name = "Recipient's nonce", type = ftypes.BYTES, base = base.SPACE }, ["secnet.kx.transform"] = { name = "Selected bulk-crypto transform", type = ftypes.UINT8, base = base.DEC, tab = capmap.transform }, ["secnet.kx.dhval"] = { name = "Sender's public Diffie--Hellman value", type = ftypes.NONE }, ["secnet.kx.dhval.len"] = { name = "Sender's public Diffie--Hellman length", type = ftypes.UINT16, base = base.DEC }, ["secnet.kx.dhval.text"] = { name = "Sender's public Diffie--Hellman text", type = ftypes.STRING, base = base.ASCII }, ["secnet.kx.sig"] = { name = "Sender's signature", type = ftypes.NONE }, ["secnet.kx.sig.len"] = { name = "Sender's signature length", type = ftypes.UINT16, base = base.DEC }, ["secnet.kx.sig.text"] = { name = "Sender's signature text", type = ftypes.STRING, base = base.ASCII }, ["secnet.ciphertext"] = { name = "Encrypted data", type = ftypes.NONE }, ["secnet.ciphertext.unknown"] = { name = "Ciphertext with unknown structure", type = ftypes.BYTES, base = base.SPACE }, ["secnet.ciphertext.iv"] = { name = "Initialization vector", type = ftypes.BYTES, base = base.SPACE }, ["secnet.ciphertext.sequence"] = { name = "Sequence number", type = ftypes.UINT32, base = base.DEC }, ["secnet.ciphertext.payload"] = { name = "Encrypted payload", type = ftypes.BYTES, base = base.SPACE }, ["secnet.ciphertext.tag"] = { name = "Authentication tag", type = ftypes.BYTES, base = base.SPACE }, ["secnet.wtf"] = { name = "Unexpected trailing data", type = ftypes.BYTES, base = base.SPACE } } -- Add the remaining capability fields. Calculate the unassigned mask -- based on the assigned bits. local unasgn = 0x7fff7f00 for i, v in pairs(CAPTAB) do local flag = bit.lshift(1, i) ftab["secnet.cap." .. v.name] = { name = v.desc, type = ftypes.BOOLEAN, mask = flag, base = 32 } unasgn = bit.band(unasgn, bit.bnot(flag)) end ftab["secnet.cap.unassigned"] = { name = "Unassigned capability bits", type = ftypes.UINT32, mask = unasgn, base = base.HEX } -- Convert this table into the protocol fields, and populate `PF'. local ff = { } local i = 1 -- Figure out whether we can use `none' fields (see below). local use_none_p = rawget(ProtoField, 'none') ~= nil for abbr, args in pairs(ftab) do -- An annoying hack. Older versions of Wireshark don't allow setting -- fields with type `none', which is a shame because they're ideal as -- internal tree nodes. ty = args.type b = args.base if ty == ftypes.NONE then if use_none_p then b = base.NONE else ty = ftypes.BYTES b = base.SPACE end end -- Go make the field. local f = ProtoField.new(args.name, abbr, ty, args.tab, b, args.mask, args.descr) PF[abbr] = f ff[i] = f; i = i + 1 end secnet.fields = PF -- Make readable fields corresponding to especially interesting protocol -- fields. for abbr, args in pairs(ftab) do if args.field then F[abbr] = Field.new(abbr) end end end ----------------------------------------------------------------------------- --- The main dissector. function secnet.dissector(buf, pinfo, tree) -- Fill in the obvious stuff. pinfo.cols.protocol = "Secnet" local sz = buf:reported_length_remaining() local sub = tree:add(secnet, buf(0, sz), "Secnet packet") local p = 12 -- Decode the message header. hdr = sub:add(PF["secnet.hdr"], buf(0, 12)) local rcvix = buf(0, 4):uint(); hdr:add(PF["secnet.hdr.rcvix"], buf(0, 4)) local sndix = buf(4, 4):uint(); hdr:add(PF["secnet.hdr.sndix"], buf(4, 4)) local label = buf(8, 4):uint() hdr:add(PF["secnet.hdr.label"], buf(8, 4), label, string.format("Message label (major = 0x%04x, minor = 0x%04x)", msgmajor(label), msgminor(label))) local st = { pinfo = pinfo, label = label, rcvix = rcvix, sndix = sndix } local info = PKTINFO[label] -- Dispatch using the master protocol table. if info == nil then pinfo.cols.info = string.format("Unknown message label 0x%08x", label) else pinfo.cols.info = info.info p = dissect_sequence(info.dissect, st, buf, sub, p, sz) end -- Invoke the hook if necessary. if not pinfo.visited and info.hook ~= nil then info.hook(st) end -- Return the final position we reached. return p end -- We're done. Register the dissector. DissectorTable.get("udp.port"):add(410, secnet) -------- That's all, folks -------------------------------------------------- work/secnet.80000664000000000000000000005676015063477206010332 0ustar .\" Man page for secnet. .\" .\" See the secnet.git README, or the Debian copyright file, for full .\" list of copyright holders. .\" .\" secnet 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. .\" .\" secnet 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 .\" version 3 along with secnet; if not, see .\" https://www.gnu.org/licenses/gpl.html. .TH secnet 8 .SH NAME secnet \- VPN router daemon .SH SYNOPSIS \fBsecnet\fR [\fIOPTIONS\fR] .SH DESCRIPTION \fBsecnet\fR allows virtual private networks to be constructed spanning multiple separate sites. .SH OPTIONS .TP .B --verbose\fR, \fB-v Enable extra diagnostics. .TP .B --nowarnings\fR, \fB-w Suppress warnings. .TP .B --help Display usage message. .TP .B --version Display version string. .TP .B --nodetach\fR, \fB-n Don't go into background. The default behaviour is to become a daemon during startup. .TP .B --silent\fR, \fB--quiet\fR, \fB-f Suppress error messages. .TP .B --debug\fR, \fB-d Enable debug messages. .TP .B --config\fR, \fB-c \fIPATH Specify configuration file. The default is \fI/etc/secnet/secnet.conf\fR. .TP .B --just-check-config\fR, \fB-j Check configuration and exit. .TP .B --sites-key\fR, \fB-s \fIKEY Configuration file key defining active sites. The default is \fBsites\fR. .SH "CAPABILITY NEGOTIATION" Sites negotiate with each other during key exchange in order to determine which cryptographic algorithms and other features \(en termed .I capabilities \(en they each support. Capabilities are assigned small integer numbers. In many cases, capability numbers can be assigned in the configuration file, as described below; but secnet's default assignments will often be satisfactory. .PP Capability numbers between 0 and 7 inclusive are reserved for local use: secnet will never make use of them without explicit configuration. This may be useful to migrate from one set of parameters for a particular cryptographic algorithm to different, incompatible, parameters for the same algorithm. Other capability numbers are assigned by default by various kinds of closures. See the descriptions below for details. .PP It is essential that a capability number mean the same thing to each of a pair of peers. It's possible to configure a site so that it uses different capability numbers for the same feature when it communicates with different peer sites, but this is likely to be more confusing than useful. .SH "CONFIGURATION FILE" .SS Overview The default configuration file is \fI/etc/secnet/secnet.conf\fR. This can be overridden with the \fB--config\fR option. .PP The configuration file defines a dictionary (a mapping from keys to values) of configuration information for secnet. It is recursive in nature, i.e. values may themselves include dictionaries. Any node in the nested structure thus defined can be identified by a \fIpath\fR, which is the sequence of keys necessary to reach it from the root, separated by "/" characters. See \fBPaths\fR below for how this is used. .PP Furthermore, when a key is looked up in a dictionary, if it cannot be found, it is sought in the parent dictionary, and so on back to the root. For instance, each \fIsite\fR must contain the \fBresolver\fR key, but in a typical configuration there is no value in having different resolvers for each site. Therefore \fBresolver\fR is defined at the root and thus automatically incorporated into all sites. .SS Whitespace Whitespace, including newlines, is ignored except to the extent that it bounds other symbols. .PP Comment begin with "#" and continues to the end of the line. Comments are ignored. .SS Inclusion A file may be recursively included into the configuration file using a line of the form: .IP \fBinclude \fIPATH .PP This is handled at a higher level than the main parser and so precludes the possibility of using the string \fBinclude\fR for any other purpose. .\" check if this is true. it's probably a bug! .SS Assignments The configuration file contains one or more assigments. Each assignment is written: .IP \fIkey\fR [\fB=\fR] \fIlist\fR\fB;\fR .PP i.e. the equals sign is optional. The semicolon is mandatory in all contexts. .PP Keys start with a letter or "_" and continue with any numbers of letters, digits, "_" and "-". .PP Each \fIkey\fR is a list of one or more \fIvalues\fR, separated by commas. Possible values types are \fIboolean\fR, \fIstring\fR, \fInumber\fR, \fIdictionary\fR, \fIpath\fR and \fIclosure evaluation\fR. .\" This man page draws a distinction between a closure (the thing .\" evaluated) and a closure evaluation (the closure plus is .\" arguments). .SS "Strings" Strings are contained within "double quotes". There is (currently) no escape syntax and no way to include quotes inside strings. .PP Example: .nf filename "/var/log/secnet"; .fi .SS "Numbers" Numbers are encoded in decimal and do not include a sign. Numbers must lie in the range 0 to 4294967295. .PP Example: .nf mtu 1400; .fi .SS "Dictionaries" .\" In conffile.y dictionaries can be preceded by a search path, but .\" this is not implemented elsewhere, so not documented here. Dictionaries consist of one or more assignments, in the same syntax as given above, enclosed in "{" and "}". .PP Example: .nf system { userid "secnet"; pidfile "/var/run/secnet.pid"; }; .fi .SS "Paths" Paths allow a key already defined in the configuration to be aliased. .PP Paths consist of a sequence of keys separated by "/". If the path starts with a "/" then it is an \fIabsolute path\fR and the search starts at the root of the configuration. Otherwise it is a \fIrelative path\fR and starts in the containing dictionary or in any of its parents, down to and including the root. If there is more than one match, the one furthest from the root "wins". .PP The value of a path is the list assigned to the key it refers to. Lists are flattened; for example if a key is defined as a list of two paths, and each of those refers to a list of two integers, the original key is therefore defined to be a list of four integers, not a list consisting of two lists. .PP It is not possible to refer to a \fIlater\fR key using a path. .PP Example: .nf vpn { test { kakajou vpn-data/test/kakajou/kakajou; araminta vpn-data/test/araminta/araminta; deodand vpn-data/test/deodand/deodand; all-sites kakajou,araminta,deodand; }; }; all-sites vpn/test/all-sites; .fi .PP Here, each of \fBvpn/test/kakajou\fR, \fBvpn/test/araminta\fR and \fBvpn/test/deodand\fR are defined as aliases to values defined elsewhere. \fBvpn/tests/all-sites\fR is defined as the list of all three of those values, and \fBall-sites\fR is then defined to be an alias for that. .SS "Booleans" The (single-element) paths \fBfalse\fR, \fBno\fR and \fBnowise\fR are predefined and refer to a boolean false value. Similarly \fBtrue\fR, \fByes\fR and \fBverily\fR point at a boolean true value. .PP In all six cases, variants with just the first letter capitalized, and with all letters capitalized, are also provided. .PP Example: .nf random randomfile("/dev/urandom",no); .fi .SS "Closure Evaluation" Closure evaluation uses the following syntax: .IP \fICLOSURE \fB( \fIARGUMENTS \fB) .PP \fICLOSURE\fR may be a path referring to a closure, or may itself be a closure evaluation. .PP \fIARGUMENTS\fR is a list of zero or more values, separated by commas. As a shortcut, if the arguments consist of a single dictionary, the parentheses may be ommitted: .IP \fICLOSURE \fB{ \fR... \fB} .PP Example: .nf sites map(site, vpn/test/all-sites); .fi .PP When a closure is evaluated it returns a value (a list, much as above) and may also have side effects (which may be immediate or may be deferred to some later phase of execution). A list of built-in closures is given below. .SS "Mandatory Keys" Two keys are mandatory. \fBsystem\fR must be a dictionary in which the following keys can be looked up: .TP .B log A \fIlog closure\fR; see the \fBlogfile\fR documentation below. The destination for log messages. Mandatory. .TP .B userid A string. The userid to run as after dropping privilege. Optional. .TP .B pidfile A string. The path to write a pidfile. Optional. .PP \fBsites\fR should be a list of \fIsite closures\fR; see the \fBsite\fR documentation below. This defines the collection of tunnel endpoints that \fBsecnet\fR will communicate with. .PP Recall the recursive lookup logic described in \fBOverview\fR above: if (for instance) \fBlog\fR is defined in the top level dictionary but not in \fBsystem\fR, it will nevertheless be found when looked up in the latter. .SH CLOSURES \fBsecnet\fR contains a collection of built-in closures with names (i.e. single-element paths) given below. .PP Most of them return anonymous closures of various types, which are described contextually. .SS adns \fBadns(\fIDICT\fB)\fR => \fIresolver closure\fR .TP .I DICT This either be empty or contain the single key \fBconfig\fR, with a string value giving configuration to supply to ADNS. This might be read from a file using \fBreadfile\fR. .PP A \fIresolver closure\fR is a means of converting hostnames into network addresses. .SS diffie-hellman .PP \fBdiffie-hellman(\fIMODULUS\fB, \fIGENERATOR\fR[\fB, \fICHECK\fR]\fB)\fR => \fIdh closure\fR .TP .I MODULUS String. The prime modulus \fIp\fR in hex. .TP .I GENERATOR String. The generator \fIg\fR in hex. .TP .I CHECK Boolean. If \fBtrue\fR (the default) then check if \fIp\fR is prime. .PP A \fIdh closure\fR defines a group to be used for key exchange. .SS logfile \fBlogfile(\fIDICT\fB)\fR => \fIlog closure\fR .PP Valid keys in the \fIDICT\fR argument are: .TP .B filename The path to log to. .TP .B class A list of strings defining which classes of message to log. The possible message classes are \fBdebug-config\fR, \fBdebug-phase\fR, \fBdebug\fR, \fBinfo\fR, \fBnotice\fR, \fBwarning\fR, \fBerror\fR, \fBsecurity\fR and \fBfatal\fR. .IP \fBall-debug\fR is the union of all the \fBdebug\fR... classes. \fBdefault\fR is equivalent to \fBwarning, error, security, fatal\fR. \fBverbose\fR is equivalent to \fBinfo, notice, warning, error, security, fatal\fR. \fBquiet\fR is equivalent to \fBfatal\fR. .PP A \fIlog closure\fR is a means of saving log messages. See also \fBsyslog\fR below. .SS makelist \fBmakelist(\fIDICT\fB)\fR => \fILIST\fR .PP Returns the (flattened) list of values from the dictionary, discarding the keys. .SS map \fBmap(\fICLOSURE\fB, \fIINPUT\fR...\fB)\fR => \fILIST\fR .PP Applies \fICLOSURE\fR to all its additional input arguments and returns the resulting list. .SS md5 \fBmd5\fR is a \fIhash closure\fR implementing the MD5 algorithm. .SS null-netlink \fBnull-netlink(\fIDICT\fB)\fR => \fInetlink closure\fR .br \fBnull-netlink(\fIDICT\fB)\fR => \fIpure closure\fR .\" TODO pure closure is what it's called internally but this is a .\" very opaque name to use in docs .PP Valid keys in the \fIDICT\fR argument are: .TP .B name String. The name for the netlink device. The default is \fBnull-netlink\fR. .TP .B networks List of strings. The networks on the host side of the netlink device. .TP .B remote-networks List of strings. Networks that may be claimed by remote sites using this netlink device. .TP .B secnet-address String. IP address of this netlink. Incompatible with \fBptp-address\fR. .TP .B ptp-address String. IP address of the other end of a point-to-point link. Incompatible with \fBsecnet-address\fR. .TP .B mtu Number. The MTU of the netlink device. The default is 1000. .PP If \fBptp-address\fR is used then the result is a \fInetlink closure\fR. This can be used directly with the \fBlink\fR key in the \fBsites\fR closure (see below). .PP If \fBsecnet-address\fR is used then the result is a \fIpure closure\fR. This must be evaluated to yield a \fInetlink closure\fR, using a dictionary argument with the following keys: .TP .B routes String list. networks reachable via this tunnel, in \fIaddress\fB/\fIbits\fR format. .TP .B options String list. A list of options: .RS .TP .B allow-route Allow packets received via this tunnel to be routed down other tunnels (without this option only packets from the host will be routed). .TP .B soft Remove these routes from the host routing table when the link quality is 0. .RE .TP .B mtu Number. Default MTU over this link. The default is inherited from the \fIpure closure\fR. .TP .B priority Number. The priority of this link. Higher values beat lower values. The default is 0. .\" TODO ptp-address turns up in sites.conf, but why? I think this .\" is a bug in make-secnet-sites; it is not used by \" netlink_inst_create. .PP A \fInetlink closure\fR is a virtual IP link, and is supplied to the \fBlink\fR key of a \fIsite\fR closure. .PP The netlink created by \fBnull-netlink\fR has no connection to the host. See \fBtun\fR and \fBuserv-ipif\fR below for more useful alternatives. .SS randomfile \fBrandomfile(\fIFILENAME\fR[\fB, \fIBLOCKING\fR]\fB)\fR => \fIrandomsource closure\fR .TP .I FILENAME String. Path to random device, e.g. \fI/dev/urandom\fR. .TP .I BLOCKING Boolean. \fBTrue\fR if this is a blocking device and \fBfalse\fR otherwise (the default). Blocking device support is not implemented so this must always be \fBFalse\fR or absent. .PP A \fIrandomsource closure\fR is a source of random numbers. .SS readfile \fBreadfile(\fIPATH\fB)\fR => \fISTRING\fR .PP Read the contents of the file \fIPATH\fR (a string) and return it as a string. .SS eax-serpent \fBeax-serpent(\fIDICT\fB)\fR => \fItransform closure\fR .PP Valid keys in the \fIDICT\fR argument are: .TP .B max-sequence-skew The maximum acceptable difference between the sequence number in a received, decrypted message and the previous one. The default is 10. It may be necessary to increase this is if connectivity is poor. .TP .B tag-length-bytes The length of the message authentication tag. The default is 16, for a 128-bit tag length. It must be no longer than the Serpent blocksize, 16. Must be have the same value at both ends. .TP .B padding-rounding Messages are padded to a multiple of this many bytes. This serves to obscure the exact length of messages. The default is 16, .TP .B capab-num The capability number to use when advertising this transform. The default for serpent-eax is 9. .PP A \fItransform closure\fR is a reversible means of transforming messages for transmission over a (presumably) insecure network. It is responsible for both confidentiality and integrity. .SS serpent256-cbc \fBserpent256-cbc(\fIDICT\fB)\fR => \fItransform closure\fR .PP This transform is deprecated as its security properties are poor; it should be specified only alongside a better transform such as eax-serpent. .PP Valid keys in the \fIDICT\fR argument are: .TP .B capab-num As above. The default for serpent256-cbc is 8. .TP .B max-sequence-skew As above. .PP Note that this uses a big-endian variant of the Serpent block cipher (which is not compatible with most other Serpent implementations). .SS rsa-private \fBrsa-private(\fIPATH\fB\fR[, \fICHECK\fR]\fB)\fR => \fIsigprivkey closure\fR .TP .I PATH String. The path to a file containing an RSA private key in SSH format (version 1). There must be no passphrase. .TP .I CHECK Boolean. If \fBtrue\fR (the default) then check that the key is valid. .SS rsa-public \fBrsa-public(\fIKEY\fB, \fIMODULUS\fB)\fR => \fIsigpubkey closure\fR .TP .I KEY String. The public key exponent (\fIe\fR), in decimal. .TP .I MODULUS String. The modulus (\fIn\fR), in decimal. .SS sha1 \fBsha1\fR is a \fIhash closure\fR implementing the SHA-1 algorithm. .SS site \fBsite(\fIDICT\fB)\fR => \fIsite closure\fR .PP Valid keys in the \fIDICT\fR argument are: .TP .B local-name String. The site's name for itself. .TP .B name String. The name of the site's peer. .TP .B link A \fInetlink closure\fR. .TP .B comm A \fIcomm closure\fR. .TP .B resolver A \fIresolver closure\fR. .TP .B random A \fIrandomsource closure\fR. .TP .B local-key An \fIsigprivkey closure\fR. The key used to prove our identity to the peer. .TP .B address String. The DNS name of the peer. Optional, but if it is missing then it will not be possible to initiate new connections to the peer. .TP .B port Number. The port to contact the peer. .TP .B key An \fIsigpubkey closure\fR. The key used to verify the peer's identity. .TP .B transform One or more \fItransform closures\fR. Used to protect packets exchanged with the peer. These should all have distinct \fBcapab-num\fR values, and the same \fBcapab-num\fR value should have the same (or a compatible) meaning at both ends. The list should be in order of preference, most preferred first. (The end which sends MSG1,MSG3 ends up choosing; the ordering at the other end is irrelevant.) .TP .B dh A \fIdh closure\fR. The group to use in key exchange. .TP .B hash The hash function used during setup. .\" TODO clarify what we actually use it for! .TP .B key-lifetime Number. The maximum lifetime of a session key in milliseconds. The default is one hour. .TP .B setup-retries Number. The maximum number of times a key negotiation packet will be transmitted before giving up. The default is 5. .TP .B setup-timeout Number. The time between retransmissions of key negotiation packets, in milliseconds. The default is one second. .TP .B wait-time Number. The time to wait after a failed key setup before making another attempt, in milliseconds. The default is 20s. .TP .B renegotiate-time Number. The time after which a new session key will be negotiated, \fIif\fR there is traffic on the link, in milliseconds. It must not be greater than the \fBkey-lifetime\fR. The default 5 minutes less than the key lifetime, unless the lifetime is less than 10 minutes in which case the default is half the lifetime. .TP .B keepalive Boolean. If \fBtrue\fR then attempt to always maintain a live session key. Not implemented. .TP .B log-events String list. Types of event to log for this site. .RS .TP .B unexpected Unexpected key setup packets (including late retransmissions). .TP .B setup-init Start of attempt to setup a session key. .TP .B setup-timeout Failure of attempt to setup a session key, through timeout. .TP .B activate-key Activation of a new session key. .TP .B timeout-key Deletion of current session key through age. .TP .B security Anything potentially suspicious. .TP .B state-change Steps in the key setup protocol. .TP .B packet-drop Whenever we throw away an outgoing packet. .TP .B dump-packets Every key setup packet we see. .TP .B errors Failure of name resolution, internal errors. .TP .B all Everything (too much!) .RE .PP A \fIsite closure\fR defines one site to communicate with. \fBsecnet\fR expects the (root) key \fBsite\fR to be a list of site closures. .SS sysbuffer \fBsysbuffer(\fR[\fISIZE\fR[\fB, \fIOPTIONS\fR]]\fB)\fR => \fIbuffer closure\fR .TP .I SIZE Number. The size of the buffer in bytes. This must be between 64 and 131072. The default is 4096. .TP .I OPTIONS Dictionary. Optional and presently unused. .\" lockdown is accepted but ignored. .PP A \fIbuffer closure\fR is a means of buffering packets to send or that have been received. .SS syslog \fBsyslog(\fIDICT\fB)\fR => \fIlog closure\fR .PP Valid keys in the \fIDICT\fR argument are: .TP .B ident String. The ident string to pass to \fBopenlog\fR(3); this value will appear in each message. .TP .B facility String. The facility to log as. The possible values are \fBauthpriv\fR, \fBcron\fR, \fBdaemon\fR, \fBkern\fR, \fBlocal0\fR-\fB7\fR, \fBlpr\fR, \fBmail\fR, \fBnews\fR, \fBsyslog\fR, \fBuser\fR and \fBuucp\fR. .PP See also \fBlogfile\fR above. .SS tun \fBtun(\fIDICT\fB)\fR => \fInetlink closure\fR .br \fBtun(\fIDICT\fB)\fR => \fIpure closure\fR .PP Valid keys in the \fIDICT\fR argument are those documented for \fBnull-netlink\fR above, plus: .TP .B flavour String. The type of TUN interface to use. Possible values are \fBlinux\fR, \fBbsd\fR, \fBstreams\fR and \fBguess\fR. The default is \fBguess\fR. .TP .B device String. The path to the TUN/TAP device file. The default is \fI/dev/net/tun\fR for the \fBlinux\fR flavour and \fI/dev/tun\fR for the others. .TP .B interface String. The interface to use. The default is to pick one automatically. This cannot be used with the \fBstreams\fR flavour. .TP .B local-address String. IP address of the host's tunnel interface. .\" README says this belongs to netlink-null but actually it's \" duplicated between slip & tun .TP .B ifconfig-path String. The name of the \fBifconfig\fR command. The default is simply "ifconfig". .TP .B route-path String. The name of the \fBroute\fR command. The default is simply "route". .TP .B ifconfig-type String. The syntax expected by the \fBifconfig\fR command. Possible values are \fBlinux\fR, \fBbsd\fR, \fBioctl\fR, \fBsolaris-2.5\fR and \fBguess\fR. The default is \fBguess\fR. .TP .B route-type String. The syntax expected by the \fBifconfig\fR command. Possible values are \fBlinux\fR, \fBbsd\fR, \fBioctl\fR, \fBsolaris-2.5\fR and \fBguess\fR. The default is \fBguess\fR. .TP .B buffer A \fIbuffer closure\fR to use for packets transferred from the host to secnet. The buffer size must be at least 60 greater than the MTU. .\" TODO rumour has is that buffers are sometimes shareable between .\" netlink devices - document that if the conditions are reasonable .\" ones. .PP The \fBifconfig-type\fR and \fBroute-type\fR values determine how those commands are executed. If they are set to \fBioctl\fR then low-level system calls are used directly instead of invoking the commands. .PP The netlink created by \fBtun\fR uses the \fBtun\fR device to communicate with the host kernel. .SS udp \fBudp(\fIDICT\fB)\fR => \fIcomm closure\fR .PP Valid keys in the \fIDICT\fR argument are: .TP .B address String. The IP address to bind on. The default is 0.0.0.0, i.e. "any". .TP .B port Number. The port number to bind to. The default is 0, i.e. the OS will choose one. It is suggested that any given VPN agree a common port number. .TP .B buffer A \fIbuffer closure\fR. See the \fBsysbuffer\fR closure above. .TP .B authbind String. The path to a helper program to bind the socket. Optional. .IP The program will be invoked with the address and port number as its arguments, and with the socket to bind as file descriptor 0. It should either bind the socket as requested, or exit with nonzero status. .PP A \fIcomm closure\fR is a means of sending and receiving messages via a network. It does not provide confidentiality, reliablity or availability. .SS userv-ipif \fBuserv-ipif(\fIDICT\fB)\fR => \fInetlink closure\fR .br \fBuserv-ipif(\fIDICT\fB)\fR => \fIpure closure\fR .PP Valid keys in the \fIDICT\fR argument are those documented for \fBnull-netlink\fR above, plus: .TP .B local-address String. IP address of the host's SLIP interface. .\" README says this belongs to netlink-null but actually it's \" duplicated between SLIP & tun .TP .B userv-path String. Where to find \fBuserv\fR(1). The default is \fB"userv"\fR. .TP .B service-user String. The name of the user that owns the service. The default is \fB"root"\fR. .TP .B service-name String. The name of the service to request. The default is \fB"ipif"\fR. .TP .B buffer A \fIbuffer closure\fR to use for packets transferred from the host to secnet. .PP The netlink created by \fBuserv-ipif\fR invokes the specified \fBuserv\fR service with pipes connected to its standard input and output. It uses SLIP to communicate with the host kernel via these pipes. .SH FILES .TP .I /etc/secnet/secnet.conf Configuration file. .SH "SEE ALSO" \fBuserv\fR(1) work/secnet.c0000664000000000000000000003366715063477206010406 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include "secnet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "util.h" #include "conffile.h" #include "process.h" #if __APPLE__ /* apple's poll() does not work on char devs */ # define USE_SELECT 1 #endif /* XXX should be from autoconf */ static const char *configfile="/etc/secnet/secnet.conf"; static const char *sites_key="sites"; bool_t just_check_config=False; static char *userid=NULL; static uid_t uid=0; static gid_t gid; bool_t background=True; static char *pidfile=NULL; bool_t require_root_privileges=False; cstring_t require_root_privileges_explanation=NULL; const char *const closure_type_names[] = { [ CL_PURE ] = "PURE" , [ CL_RESOLVER ] = "RESOLVER" , [ CL_RANDOMSRC ] = "RANDOMSRC" , [ CL_SIGPUBKEY ] = "SIGPUBKEY" , [ CL_SIGPRIVKEY ] = "SIGPRIVKEY" , [ CL_COMM ] = "COMM" , [ CL_IPIF ] = "IPIF" , [ CL_LOG ] = "LOG" , [ CL_SITE ] = "SITE" , [ CL_TRANSFORM ] = "TRANSFORM" , [ CL_DH ] = "DH" , [ CL_HASH ] = "HASH" , [ CL_BUFFER ] = "BUFFER" , [ CL_NETLINK ] = "NETLINK" , [ CL_PRIVCACHE ] = "PRIVCACHE" , }; const char *closure_type_name(uint32_t ty, char buf[]) { if (ty < ARRAY_SIZE(closure_type_names)) return closure_type_names[ty]; sprintf(buf, "CL#%.6u", (unsigned)ty); buf[9] = 0; return buf; } static pid_t secnet_pid; /* Structures dealing with poll() call */ struct poll_interest { beforepoll_fn *before; /* 0 if deregistered and waiting to be deleted */ afterpoll_fn *after; void *state; int32_t nfds; cstring_t desc; LIST_ENTRY(poll_interest) entry; }; static LIST_HEAD(, poll_interest) reg = LIST_HEAD_INITIALIZER(®); static bool_t interest_isregistered(const struct poll_interest *i) { return !!i->before; } static bool_t finished=False; /* Parse the command line options */ static void parse_options(int argc, char **argv) { int c; while (True) { int option_index = 0; static struct option long_options[] = { {"verbose", 0, 0, 'v'}, {"nowarnings", 0, 0, 'w'}, {"help", 0, 0, 2}, {"version", 0, 0, 1}, {"nodetach", 0, 0, 'n'}, {"managed", 0, 0, 'm'}, {"silent", 0, 0, 'f'}, {"quiet", 0, 0, 'f'}, {"debug", 0, 0, 'd'}, {"config", 1, 0, 'c'}, {"just-check-config", 0, 0, 'j'}, {"sites-key", 1, 0, 's'}, {0,0,0,0} }; c=getopt_long(argc, argv, "vwdnjc:ft:s:m", long_options, &option_index); if (c==-1) break; switch(c) { case 2: /* Help */ printf("Usage: secnet [OPTION]...\n\n" " -f, --silent, --quiet suppress error messages\n" " -w, --nowarnings suppress warnings\n" " -v, --verbose output extra diagnostics\n" " -c, --config=filename specify a configuration file\n" " -j, --just-check-config stop after reading " "configuration file\n" " -s, --sites-key=name configuration key that " "specifies active sites\n" " -n, --nodetach do not run in background\n" " -m, --managed running under a supervisor\n" " -d, --debug output debug messages\n" " --help display this help and exit\n" " --version output version information " "and exit\n" ); exit(0); break; case 1: /* Version */ printf("%s\n",version); exit(0); break; case 'd': message_level|=M_DEBUG_CONFIG|M_DEBUG_PHASE|M_DEBUG; /* fall through */ case 'v': message_level|=M_INFO|M_NOTICE|M_WARNING|M_ERR|M_SECURITY| M_FATAL; break; case 'w': message_level&=(~M_WARNING); break; case 'f': message_level=M_FATAL; break; case 'n': background=False; break; case 'm': secnet_is_daemon=True; break; case 'c': if (optarg) configfile=safe_strdup(optarg,"config_filename"); else fatal("secnet: no config filename specified"); break; case 'j': just_check_config=True; break; case 's': if (optarg) sites_key=safe_strdup(optarg,"sites-key"); else fatal("secnet: no sites key specified"); break; case '?': exit(1); break; default: Message(M_ERR,"secnet: Unknown getopt code %c\n",c); } } if (argc-optind != 0) { Message(M_ERR,"secnet: You gave extra command line parameters, " "which were ignored.\n"); } } static void setup(dict_t *config) { list_t *l; dict_t *system; struct passwd *pw; struct cloc loc; l=dict_lookup(config,"system"); if (!l || list_elem(l,0)->type!=t_dict) { fatal("configuration does not include a \"system\" dictionary"); } system=list_elem(l,0)->data.dict; loc=list_elem(l,0)->loc; /* Arrange systemwide log facility */ l=dict_lookup(system,"log"); if (!l) { fatal("configuration does not include a system/log facility"); } system_log=init_log(l); /* Who are we supposed to run as? */ userid=dict_read_string(system,"userid",False,"system",loc); if (userid) { if (!(pw=getpwnam(userid))) fatal("userid \"%s\" not found",userid); uid=pw->pw_uid; gid=pw->pw_gid; } /* Pidfile name */ pidfile=dict_read_string(system,"pidfile",False,"system",loc); /* Check whether we need root privileges */ if (require_root_privileges && uid!=0) { fatal("the configured feature \"%s\" requires " "that secnet retain root privileges while running.", require_root_privileges_explanation); } } static void start_sites(dict_t *config) { int i; list_t *l; item_t *site; /* Go along site list, starting sites */ l=dict_lookup(config,sites_key); if (!l) { Message(M_WARNING,"secnet: configuration key \"%s\" is missing; no " "remote sites are defined\n",sites_key); } else { i=0; while ((site=list_elem(l, i++))) { struct site_if *s; if (site->type!=t_closure) { cfgfatal(site->loc,"system","non-closure in site list"); } if (site->data.closure->type!=CL_SITE) { cfgfatal(site->loc,"system","non-site closure in site list"); } s=site->data.closure->interface; s->startup(s->st); } } } struct poll_interest *register_for_poll(void *st, beforepoll_fn *before, afterpoll_fn *after, cstring_t desc) { struct poll_interest *i; NEW(i); i->before=before; i->after=after; i->state=st; i->nfds=0; i->desc=desc; LIST_INSERT_HEAD(®, i, entry); return i; } void deregister_for_poll(struct poll_interest *i) { /* We cannot simply throw this away because we're reentrantly * inside the main loop, which needs to remember which range of * fds corresponds to this now-obsolete interest */ i->before=0; } static void system_phase_hook(void *sst, uint32_t newphase) { if (newphase==PHASE_SHUTDOWN && pidfile) { /* Try to unlink the pidfile; don't care if it fails */ unlink(pidfile); } } #if USE_SELECT static int fakepoll(struct pollfd *fds, int nfds, int timeout) { fd_set infds[1], outfds[1]; int maxfd = -1, i, rc; struct timeval tvtimeout; FD_ZERO(infds); FD_ZERO(outfds); for(i = 0; i < nfds; ++i) { if(fds[i].events & POLLIN) FD_SET(fds[i].fd, infds); if(fds[i].events & POLLOUT) FD_SET(fds[i].fd, outfds); if(fds[i].fd > maxfd) maxfd = fds[i].fd; } if(timeout != -1) { tvtimeout.tv_sec = timeout / 1000; tvtimeout.tv_usec = 1000 * (timeout % 1000); } rc = select(maxfd + 1, infds, outfds, NULL, timeout == -1 ? NULL : &tvtimeout); if(rc >= 0) { for(i = 0; i < nfds; ++i) { int revents = 0; if(FD_ISSET(fds[i].fd, infds)) revents |= POLLIN; if(FD_ISSET(fds[i].fd, outfds)) revents |= POLLOUT; fds[i].revents = revents; } } return rc; } #endif struct timeval tv_now_global; uint64_t now_global; static void run(void) { struct poll_interest *i, *itmp; int rv, nfds, idx; int timeout; struct pollfd *fds=0; int allocdfds=0, shortfall=0; do { #if USE_MONOTONIC struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts)!=0) { fatal_perror("main loop: clock_gettime(CLOCK_MONOTONIC,)"); } tv_now_global.tv_sec = ts.tv_sec; tv_now_global.tv_usec = ts.tv_nsec / 1000; #else /* !USE_MONOTONIC */ if (gettimeofday(&tv_now_global, NULL)!=0) { fatal_perror("main loop: gettimeofday"); } #endif /* !USE_MONOTONIC */ now_global=((uint64_t)tv_now_global.tv_sec*(uint64_t)1000)+ ((uint64_t)tv_now_global.tv_usec/(uint64_t)1000); idx=0; LIST_FOREACH(i, ®, entry) { int check; if (interest_isregistered(i)) { for (check=0; checknfds; check++) { if(fds[idx+check].revents & POLLNVAL) { fatal("run: poll (%s#%d) set POLLNVAL", i->desc, check); } } i->after(i->state, fds+idx, i->nfds); } idx+=i->nfds; } if (shortfall) { allocdfds *= 2; allocdfds += shortfall; REALLOC_ARY(fds,allocdfds); } shortfall=0; idx=0; timeout=-1; LIST_FOREACH_SAFE(i, ®, entry, itmp) { int remain=allocdfds-idx; nfds=remain; if (interest_isregistered(i)) { rv=i->before(i->state, fds+idx, &nfds, &timeout); if (rv!=0) { if (rv!=ERANGE) fatal("run: beforepoll_fn (%s) returns %d",i->desc,rv); assert(nfds < INT_MAX/4 - shortfall); shortfall += nfds-remain; nfds=0; timeout=0; } } else { nfds=0; } if (timeout<-1) { fatal("run: beforepoll_fn (%s) set timeout to %d", i->desc,timeout); } if (!interest_isregistered(i)) { /* check this here, rather than earlier, so that we handle the case where i->before() calls deregister */ LIST_REMOVE(i, entry); free(i); continue; } idx+=nfds; i->nfds=nfds; } do { if (finished) break; #if USE_SELECT rv=fakepoll(fds, idx, timeout); #else rv=poll(fds, idx, timeout); #endif if (rv<0) { if (errno!=EINTR) { fatal_perror("run: poll"); } } } while (rv<0); } while (!finished); free(fds); } bool_t will_droppriv(void) { assert(current_phase >= PHASE_SETUP); return !!uid; } /* Surrender privileges, if necessary */ static void droppriv(void) { if (userid) { if (setgid(gid)!=0) fatal_perror("can't set gid to %ld",(long)gid); if (initgroups(userid, gid) < 0) fatal_perror("initgroups"); if (setuid(uid)!=0) { fatal_perror("can't set uid to \"%s\"",userid); } assert(getuid() == uid); assert(geteuid() == uid); assert(getgid() == gid); assert(getegid() == gid); } } /* Become a daemon, if necessary */ static void become_daemon(void) { FILE *pf=NULL; pid_t p; int errfds[2]; add_hook(PHASE_SHUTDOWN,system_phase_hook,NULL); /* We only want to become a daemon if we are not one already */ if (background && !secnet_is_daemon) { p=fork(); if (p>0) { /* Parent process - just exit */ _exit(0); } else if (p==0) { /* Child process - all done, just carry on */ secnet_is_daemon=True; if (setsid() < 0) fatal_perror("setsid"); } else { /* Error */ fatal_perror("cannot fork"); exit(1); } } if (secnet_is_daemon) { /* stderr etc are redirected to the system/log facility */ pipe_cloexec(errfds); if (dup2(errfds[1],0) < 0 || dup2(errfds[1],1) < 0 || dup2(errfds[1],2) < 0) fatal_perror("can't dup2 pipe"); if (close(errfds[1]) < 0) fatal_perror("can't close redundant pipe endpoint"); log_from_fd(errfds[0],"stderr",system_log); } secnet_pid=getpid(); /* Now we can write the pidfile */ if (pidfile) { pf=fopen(pidfile,"w"); if (!pf) { fatal_perror("cannot open pidfile \"%s\"",pidfile); } if (fprintf(pf,"%ld\n",(long)secnet_pid) < 0 || fclose(pf) < 0) fatal_perror("cannot write to pidfile \"%s\"",pidfile); } } static signal_notify_fn finish,ignore_hup; static void finish(void *st, int signum) { finished=True; Message(M_NOTICE,"%s [%d]: received %s\n",version,secnet_pid,(string_t)st); } static void ignore_hup(void *st, int signum) { Message(M_INFO,"%s [%d]: received SIGHUP\n",version,secnet_pid); return; } int main(int argc, char **argv) { dict_t *config; log_early_init(); phase_hooks_init(); enter_phase(PHASE_GETOPTS); parse_options(argc,argv); log_early_setlevel(); enter_phase(PHASE_READCONFIG); config=read_conffile(configfile); enter_phase(PHASE_SETUP); setup(config); start_sites(config); if (just_check_config) { Message(M_INFO,"configuration file check complete\n"); exit(0); } enter_phase(PHASE_DAEMONIZE); become_daemon(); Message(M_NOTICE,"%s [%d]: starting\n",version,secnet_pid); enter_phase(PHASE_GETRESOURCES); /* Appropriate phase hooks will have been run */ enter_phase(PHASE_DROPPRIV); droppriv(); start_signal_handling(); request_signal_notification(SIGTERM,finish,safe_strdup("SIGTERM","run")); if (!background) request_signal_notification(SIGINT,finish, safe_strdup("SIGINT","run")); request_signal_notification(SIGHUP,ignore_hup,NULL); enter_phase(PHASE_RUN); run(); enter_phase(PHASE_SHUTDOWN); Message(M_NOTICE,"%s [%d]: finished\n",version,secnet_pid); return 0; } work/secnet.h0000664000000000000000000007365515063477206010414 0ustar /* Core interface of secnet, to be used by all modules */ /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #ifndef secnet_h #define secnet_h #define ADNS_FEATURE_MANYAF #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "osdep.h" #define MAX_PEER_ADDRS 5 /* send at most this many copies; honour at most that many addresses */ #define MAX_NAK_MSG 80 #define MAX_SIG_KEYS 4 struct hash_if; struct comm_if; struct comm_addr; struct priomsg; struct log_if; struct buffer_if; struct sigpubkey_if; struct sigprivkey_if; typedef char *string_t; typedef const char *cstring_t; #define False (_Bool)0 #define True (_Bool)1 typedef _Bool bool_t; union iaddr { struct sockaddr sa; struct sockaddr_in sin; #ifdef CONFIG_IPV6 struct sockaddr_in6 sin6; #endif }; #define GRPIDSZ 4 #define ALGIDSZ 1 #define KEYIDSZ (GRPIDSZ+ALGIDSZ) /* Changing these is complex: this is the group id plus algo id */ /* They are costructed by pubkeys.fl.pl. Also hardcoded in _PR_ */ struct sigkeyid { uint8_t b[KEYIDSZ]; }; #define SIGKEYID_PR_FMT "%02x%02x%02x%02x%02x" #define SIGKEYID_PR_VAL(id) /* SIGKEYID_PR_VAL(const sigkeyid *id) */ \ ((id) == (const struct sigkeyid*)0, (id)->b[0]), \ (id)->b[1],(id)->b[2],(id)->b[3],(id)->b[4] static inline bool_t sigkeyid_equal(const struct sigkeyid *a, const struct sigkeyid *b) { return !memcmp(a->b, b->b, KEYIDSZ); } #define SERIALSZ 4 typedef uint32_t serialt; static inline int serial_cmp(serialt a, serialt b) { if (a==b) return 0; if (!a) return -1; if (!b) return +1; return b-a <= (serialt)0x7fffffffUL ? +1 : -1; } #define ASSERT(x) do { if (!(x)) { fatal("assertion failed line %d file " \ __FILE__,__LINE__); } } while(0) /* from version.c */ extern char version[]; /* from logmsg.c */ extern uint32_t message_level; extern bool_t secnet_is_daemon; extern struct log_if *system_log; /* from process.c */ extern void start_signal_handling(void); void afterfork(void); /* Must be called before exec in every child made after start_signal_handling. Safe to call in earlier children too. */ void childpersist_closefd_hook(void *fd_p, uint32_t newphase); /* Convenience hook function for use with add_hook PHASE_CHILDPERSIST. With `int fd' in your state struct, pass fd_p=&fd. The hook checks whether fd>=0, so you can use it for an fd which is only sometimes open. This function will set fd to -1, so it is idempotent. */ /***** CONFIGURATION support *****/ extern bool_t just_check_config; /* If True then we're going to exit after reading the configuration file */ extern bool_t background; /* If True then we'll eventually run as a daemon */ typedef struct dict dict_t; /* Configuration dictionary */ typedef struct closure closure_t; typedef struct item item_t; typedef struct list list_t; /* A list of items */ /* Configuration file location, for error-reporting */ struct cloc { cstring_t file; int line; }; /* Modules export closures, which can be invoked from the configuration file. "Invoking" a closure usually returns another closure (of a different type), but can actually return any configuration object. */ typedef list_t *(apply_fn)(closure_t *self, struct cloc loc, dict_t *context, list_t *data); struct closure { cstring_t description; /* For debugging */ uint32_t type; /* Central registry... */ apply_fn *apply; void *interface; /* Interface for use inside secnet; depends on type */ }; enum types { t_null, t_bool, t_string, t_number, t_dict, t_closure }; struct item { enum types type; union { bool_t bool_; string_t string; uint32_t number; dict_t *dict; closure_t *closure; } data; struct cloc loc; }; /* Note that it is unwise to use this structure directly; use the list manipulation functions instead. */ struct list { item_t *item; struct list *next; }; /* In the following two lookup functions, NULL means 'not found' */ /* Lookup a value in the specified dictionary, or its parents */ extern list_t *dict_lookup(dict_t *dict, cstring_t key); /* Lookup a value in just the specified dictionary */ extern list_t *dict_lookup_primitive(dict_t *dict, cstring_t key); /* Add a value to the specified dictionary */ extern void dict_add(dict_t *dict, cstring_t key, list_t *val); /* Obtain an array of keys in the dictionary. malloced; caller frees */ extern cstring_t *dict_keys(dict_t *dict); /* List-manipulation functions */ extern list_t *list_new(void); extern int32_t list_length(const list_t *a); extern list_t *list_append(list_t *a, item_t *i); extern list_t *list_append_list(list_t *a, list_t *b); /* Returns an item from the list (index starts at 0), or NULL */ extern item_t *list_elem(list_t *l, int32_t index); /* Convenience functions */ extern list_t *new_closure(closure_t *cl); extern void add_closure(dict_t *dict, cstring_t name, apply_fn apply); extern void *find_cl_if(dict_t *dict, cstring_t name, uint32_t type, bool_t required, cstring_t desc, struct cloc loc); extern item_t *dict_find_item(dict_t *dict, cstring_t key, bool_t required, cstring_t desc, struct cloc loc); extern string_t dict_read_string(dict_t *dict, cstring_t key, bool_t required, cstring_t desc, struct cloc loc); extern uint32_t dict_read_number(dict_t *dict, cstring_t key, bool_t required, cstring_t desc, struct cloc loc, uint32_t def); /* return value can safely be assigned to int32_t */ extern bool_t dict_read_bool(dict_t *dict, cstring_t key, bool_t required, cstring_t desc, struct cloc loc, bool_t def); extern dict_t *dict_read_dict(dict_t *dict, cstring_t key, bool_t required, cstring_t desc, struct cloc loc); const char **dict_read_string_array(dict_t *dict, cstring_t key, bool_t required, cstring_t desc, struct cloc loc, const char *const *def); /* Return value is a NULL-terminated array obtained from malloc; * Individual string values are still owned by config file machinery * and must not be modified or freed. Returns NULL if key not * found. */ struct flagstr { cstring_t name; uint32_t value; }; extern uint32_t string_to_word(cstring_t s, struct cloc loc, struct flagstr *f, cstring_t desc); extern uint32_t string_list_to_word(list_t *l, struct flagstr *f, cstring_t desc); /***** END of configuration support *****/ /***** UTILITY functions *****/ extern char *safe_strdup(const char *string, const char *message); extern void *safe_malloc(size_t size, const char *message); extern void *safe_malloc_ary(size_t size, size_t count, const char *message); extern void *safe_realloc_ary(void *p, size_t size, size_t count, const char *message); #define NEW(p) \ ((p)=safe_malloc(sizeof(*(p)), \ __FILE__ ":" #p)) #define NEW_ARY(p,count) \ ((p)=safe_malloc_ary(sizeof(*(p)),(count), \ __FILE__ ":" #p "[" #count "]")) #define REALLOC_ARY(p,count) \ ((p)=safe_realloc_ary((p),sizeof(*(p)),(count), \ __FILE__ ":" #p "[" #count "]")) void setcloexec(int fd); /* cannot fail */ void setnonblock(int fd); /* cannot fail */ void pipe_cloexec(int fd[2]); /* pipe(), setcloexec() twice; cannot fail */ extern int sys_cmd(const char *file, const char *argc, ...); extern uint64_t now_global; extern struct timeval tv_now_global; static const uint64_t *const now = &now_global; static const struct timeval *const tv_now = &tv_now_global; /* "now" is current program time, in milliseconds. It is derived from tv_now. Both are provided by the event loop. */ /***** END of utility functions *****/ /***** START of max_start_pad handling *****/ extern int32_t site_max_start_pad, transform_max_start_pad, comm_max_start_pad; void update_max_start_pad(int32_t *our_module_global, int32_t our_instance); int32_t calculate_max_start_pad(void); /***** END of max_start_pad handling *****/ /***** SCHEDULING support */ /* If nfds_io is insufficient for your needs, set it to the required number and return ERANGE. timeout is in milliseconds; if it is too high then lower it. It starts at -1 (==infinite). */ /* Note that beforepoll_fn may NOT do anything which might change the fds or timeouts wanted by other registered poll loop loopers. Callers should make sure of this by not making any calls into other modules from the beforepoll_fn; the easiest way to ensure this is for beforepoll_fn to only retreive information and not take any action. */ typedef int beforepoll_fn(void *st, struct pollfd *fds, int *nfds_io, int *timeout_io); typedef void afterpoll_fn(void *st, struct pollfd *fds, int nfds); /* If beforepoll_fn returned ERANGE, afterpoll_fn gets nfds==0. afterpoll_fn never gets !!(fds[].revents & POLLNVAL) - such a report is detected as a fatal error by the event loop. */ /* void BEFOREPOLL_WANT_FDS(int want); * Expects: int *nfds_io; * Can perform non-local exit. * Checks whether there is space for want fds. If so, sets *nfds_io. * If not, sets *nfds_io and returns. */ #define BEFOREPOLL_WANT_FDS(want) do{ \ if (*nfds_io<(want)) { *nfds_io=(want); return ERANGE; } \ *nfds_io=(want); \ }while(0) /* Register interest in the main loop of the program. Before a call to poll() your supplied beforepoll function will be called. After the call to poll() the supplied afterpoll function will be called. */ struct poll_interest *register_for_poll(void *st, beforepoll_fn *before, afterpoll_fn *after, cstring_t desc); void deregister_for_poll(struct poll_interest *i); /***** END of scheduling support */ /***** PROGRAM LIFETIME support */ /* The secnet program goes through a number of phases in its lifetime. Module code may arrange to be called just as various phases are entered. Remember to update the table in util.c if changing the set of phases. */ enum phase { PHASE_INIT, PHASE_GETOPTS, /* Process command-line arguments */ PHASE_READCONFIG, /* Parse and process configuration file */ PHASE_SETUP, /* Process information in configuration */ PHASE_DAEMONIZE, /* Become a daemon (if necessary) */ PHASE_GETRESOURCES, /* Obtain all external resources */ PHASE_DROPPRIV, /* Last chance for privileged operations */ PHASE_RUN, PHASE_SHUTDOWN, /* About to die; delete key material, etc. */ PHASE_CHILDPERSIST, /* Forked long-term child: close fds, etc. */ /* Keep this last: */ NR_PHASES, }; /* Each module should, in its CHILDPERSIST hooks, close all fds which constitute ownership of important operating system resources, or which are used for IPC with other processes who want to get the usual disconnection effects if the main secnet process dies. CHILDPERSIST hooks are not run if the child is going to exec; so fds such as described above should be CLOEXEC too. */ typedef void hook_fn(void *self, uint32_t newphase); bool_t add_hook(uint32_t phase, hook_fn *f, void *state); bool_t remove_hook(uint32_t phase, hook_fn *f, void *state); extern uint32_t current_phase; extern void enter_phase(uint32_t new_phase); void phase_hooks_init(void); /* for main() only */ void clear_phase_hooks(uint32_t phase); /* for afterfork() */ /* Some features (like netlink 'soft' routes) require that secnet retain root privileges. They should indicate that here when appropriate. */ extern bool_t require_root_privileges; extern cstring_t require_root_privileges_explanation; /* Some modules may want to know whether secnet is going to drop privilege, so that they know whether to do privsep. Call only in phases SETUP and later. */ bool_t will_droppriv(void); /***** END of program lifetime support *****/ /***** MODULE support *****/ /* Module initialisation function type - modules export one function of this type which is called to initialise them. For dynamically loaded modules it's called "secnet_module". */ typedef void init_module(dict_t *dict); extern void init_builtin_modules(dict_t *dict); extern init_module pubkeys_init; extern init_module resolver_module; extern init_module random_module; extern init_module udp_module; extern init_module polypath_module; extern init_module util_module; extern init_module site_module; extern init_module transform_eax_module; extern init_module transform_cbcmac_module; extern init_module netlink_module; extern init_module rsa_module; extern init_module dh_module; extern init_module md5_module; extern init_module slip_module; extern init_module tun_module; extern init_module sha1_module; extern init_module log_module; extern init_module privcache_module; /***** END of module support *****/ /***** SIGNATURE SCHEMES *****/ struct sigscheme_info; typedef bool_t sigscheme_loadpub(const struct sigscheme_info *algo, struct buffer_if *pubkeydata, struct sigpubkey_if **sigpub_r, closure_t **closure_r, struct log_if *log, struct cloc loc); /* pubkeydata is (supposedly) for this algorithm. * loadpub should log an error if it fails. * pubkeydata may be modified (but not freed). * both *sigpub_r and *closure_r must always be written and must * refer to the same object, so on successful return * (*closure_r)->type==CL_SIGPUBKEY * and (*closure_r)->interface==*sigpub_r */ typedef bool_t sigscheme_loadpriv(const struct sigscheme_info *algo, struct buffer_if *privkeydata, struct sigprivkey_if **sigpriv_r, closure_t **closure_r, struct log_if *log, struct cloc loc); /* Ideally, check whether privkeydata contains data for any algorithm. * That avoids security problems if a key file is misidentified (which * might happen if the file is simply renamed). * If there is an error (including that the key data is not for this * algorithm, return False and log an error at M_ERROR. * On entry privkeydata->base==start. loadpriv may modify * privkeydata, including the contents. */ struct sigscheme_info { const char *name; const uint8_t algid; sigscheme_loadpub *loadpub; sigscheme_loadpriv *loadpriv; }; extern const struct sigscheme_info rsa1_sigscheme; extern const struct sigscheme_info sigschemes[]; /* sentinel has name==0 */ const struct sigscheme_info *sigscheme_lookup(const char *name); extern sigscheme_loadpriv rsa1_loadpriv; extern sigscheme_loadpub rsa1_loadpub; /***** END of signature schemes *****/ /***** CLOSURE TYPES and interface definitions *****/ #define CL_PURE 0 #define CL_RESOLVER 1 #define CL_RANDOMSRC 2 #define CL_SIGPUBKEY 3 #define CL_SIGPRIVKEY 4 #define CL_COMM 5 #define CL_IPIF 6 #define CL_LOG 7 #define CL_SITE 8 #define CL_TRANSFORM 9 #define CL_DH 11 #define CL_HASH 12 #define CL_BUFFER 13 #define CL_NETLINK 14 #define CL_PRIVCACHE 15 /* When editing this list, edit closure_type_names[] too */ extern const char *const closure_type_names[]; const char *closure_type_name(uint32_t ty, char buf[]); struct buffer_if; struct alg_msg_data { uint8_t *start; int32_t len; }; /* PURE closure requires no interface */ /* RESOLVER interface */ /* Answers to queries are delivered to a function of this type. 'address' will be NULL if there was a problem with the query. It will be freed once resolve_answer_fn returns. naddrs is the actual size of the array at addrs; was_naddrs is the number of addresses actually found in the DNS, which may be bigger if addrs is equal to MAX_PEER_ADDRS (ie there were too many). */ typedef void resolve_answer_fn(void *st, const struct comm_addr *addrs, int naddrs, int was_naddrs, const char *name, const char *failwhy); /* name is the same ptr as passed to request, so its lifetime must * be suitable*/ typedef bool_t resolve_request_fn(void *st, cstring_t name, int remoteport, struct comm_if *comm, resolve_answer_fn *cb, void *cst); struct resolver_if { void *st; resolve_request_fn *request; }; /* RANDOMSRC interface */ /* Return some random data. Cannot fail. */ typedef void random_fn(void *st, int32_t bytes, uint8_t *buff); struct random_if { void *st; bool_t blocking; random_fn *generate; }; /* SIGPUBKEY interface */ typedef void sig_dispose_fn(void *st); typedef bool_t sig_unpick_fn(void *sst, struct buffer_if *msg, struct alg_msg_data *sig); typedef bool_t sig_checksig_fn(void *st, uint8_t *data, int32_t datalen, const struct alg_msg_data *sig); struct sigpubkey_if { void *st; sig_unpick_fn *unpick; sig_checksig_fn *check; const struct hash_if *hash; sig_dispose_fn *dispose; }; /* SIGPRIVKEY interface */ /* Appends the signature to msg. * Can fail and returnn False, eg if the buffer is too small. */ typedef bool_t sig_makesig_fn(void *st, uint8_t *data, int32_t datalen, struct buffer_if *msg); struct sigprivkey_if { void *st; sig_makesig_fn *sign; const struct hash_if *hash; sig_dispose_fn *dispose; }; /* PRIVCACHE interface */ typedef struct sigprivkey_if *privcache_lookup_fn(void *st, const struct sigkeyid *id, struct log_if*); /* Return is valid only until you return from the current event! */ struct privcache_if { void *st; privcache_lookup_fn *lookup; }; /* COMM interface */ struct comm_addr { /* This struct is pure data; in particular comm's clients may freely copy it. */ struct comm_if *comm; union iaddr ia; int ix; /* see comment `Re comm_addr.ix' in udp.c */ }; struct comm_clientinfo; /* private for comm */ typedef struct comm_clientinfo *comm_clientinfo_fn(void *state, dict_t*, struct cloc cloc); /* A comm client may call this during configuration, and then pass * the resulting comm_clientinfo* to some or all sendmsg calls. * The semantics depend on the dict and defined by the comm, and * should be documented in README. */ enum { comm_notify_whynot_general, comm_notify_whynot_unpick, comm_notify_whynot_name_local, comm_notify_whynot_name_remote, }; /* Return True if the packet was processed, and shouldn't be passed to any other potential receivers. (buf is freed iff True returned.) */ typedef bool_t comm_notify_fn(void *state, struct buffer_if *buf, const struct comm_addr *source, struct priomsg *whynot); typedef void comm_request_notify_fn(void *commst, void *nst, comm_notify_fn *fn); typedef void comm_release_notify_fn(void *commst, void *nst, comm_notify_fn *fn); typedef bool_t comm_sendmsg_fn(void *commst, struct buffer_if *buf, const struct comm_addr *dest, struct comm_clientinfo* /* 0 OK */); /* Only returns false if (we know that) the local network * environment is such that this address cannot work; transient * or unknown/unexpected failures return true. */ typedef const char *comm_addr_to_string_fn(void *commst, const struct comm_addr *ca); /* Returned string is in a static buffer. */ struct comm_if { void *st; comm_clientinfo_fn *clientinfo; comm_request_notify_fn *request_notify; comm_release_notify_fn *release_notify; comm_sendmsg_fn *sendmsg; comm_addr_to_string_fn *addr_to_string; }; bool_t iaddr_equal(const union iaddr *ia, const union iaddr *ib, bool_t ignoreport); static inline const char *comm_addr_to_string(const struct comm_addr *ca) { return ca->comm->addr_to_string(ca->comm->st, ca); } static inline bool_t comm_addr_equal(const struct comm_addr *a, const struct comm_addr *b) { return a->comm==b->comm && iaddr_equal(&a->ia,&b->ia,False); } /* LOG interface */ #define LOG_MESSAGE_BUFLEN 1023 typedef void log_msg_fn(void *st, int class, const char *message, ...); typedef void log_vmsg_fn(void *st, int class, const char *message, va_list args); struct log_if { void *st; log_vmsg_fn *vlogfn; /* printf format checking. Use [v]slilog instead */ char buff[LOG_MESSAGE_BUFLEN+1]; }; /* (convenience functions, defined in util.c) */ extern void slilog(struct log_if *lf, int class, const char *message, ...) FORMAT(printf,3,4); extern void vslilog(struct log_if *lf, int class, const char *message, va_list) FORMAT(printf,3,0); /* Versions which take (parts of) (multiple) messages, using \n to * distinguish one message from another. */ extern void slilog_part(struct log_if *lf, int class, const char *message, ...) FORMAT(printf,3,4); extern void vslilog_part(struct log_if *lf, int class, const char *message, va_list) FORMAT(printf,3,0); void cfgfile_log__vmsg(void *sst, int class, const char *message, va_list); struct cfgfile_log { struct log_if log; /* private fields */ struct cloc loc; const char *facility; }; static inline void cfgfile_log_init(struct cfgfile_log *cfl, struct cloc loc, const char *facility) { cfl->log.st=cfl; cfl->log.vlogfn=cfgfile_log__vmsg; cfl->loc=loc; cfl->facility=facility; } void log_early_init(void); void log_early_setlevel(void); /* SITE interface */ /* Pretty much a placeholder; allows starting and stopping of processing, key expiry, etc. */ typedef void site_startup_fn(void *st); typedef uint32_t site_status_fn(void *st); struct site_if { void *st; site_startup_fn *startup; site_status_fn *status; }; /* TRANSFORM interface */ /* A reversable transformation. Transforms buffer in-place; may add data to start or end. (Reverse transformations decrease length, of course.) Transformations may be key-dependent, in which case key material is passed in at initialisation time. They may also depend on internal factors (eg. time) and keep internal state. A struct transform_if only represents a particular type of transformation; instances of the transformation (eg. with particular key material) have a different C type. The same secret key will be used in opposite directions between a pair of secnets; one of these pairs will get direction==False, the other True. */ typedef struct transform_inst_if *transform_createinstance_fn(void *st); typedef bool_t transform_setkey_fn(void *st, uint8_t *key, int32_t keylen, bool_t direction); typedef bool_t transform_valid_fn(void *st); /* 0: no key; 1: ok */ typedef void transform_delkey_fn(void *st); typedef void transform_destroyinstance_fn(void *st); typedef enum { transform_apply_ok = 0, /* all is well (everyone may assume==0) */ transform_apply_err = 1, /* any other problem */ transform_apply_seqrange = 2, /* message decrypted but sequence number was out of recent range */ transform_apply_seqdupe = 3, /* message decrypted but was dupe of recent packet */ } transform_apply_return; static inline bool_t transform_apply_return_badseq(transform_apply_return problem) { return problem == transform_apply_seqrange || problem == transform_apply_seqdupe; } typedef transform_apply_return transform_apply_fn(void *st, struct buffer_if *buf, const char **errmsg); struct transform_inst_if { void *st; transform_setkey_fn *setkey; transform_valid_fn *valid; transform_delkey_fn *delkey; transform_apply_fn *forwards; transform_apply_fn *reverse; transform_destroyinstance_fn *destroy; }; struct transform_if { void *st; int capab_bit; int32_t keylen; /* <<< INT_MAX */ transform_createinstance_fn *create; }; /* NETLINK interface */ /* Used by netlink to deliver to site, and by site to deliver to netlink. cid is the client identifier returned by netlink_regnets_fn. If buf has size 0 then the function is just being called for its site-effects (eg. making the site code attempt to bring up a network link) */ typedef void netlink_deliver_fn(void *st, struct buffer_if *buf); /* site code can tell netlink when outgoing packets will be dropped, so netlink can generate appropriate ICMP and make routing decisions */ #define LINK_QUALITY_UNUSED 0 /* This link is unused, do not make this netlink */ #define LINK_QUALITY_DOWN 1 /* No chance of a packet being delivered right away*/ #define LINK_QUALITY_DOWN_STALE_ADDRESS 2 /* Link down, old address information */ #define LINK_QUALITY_DOWN_CURRENT_ADDRESS 3 /* Link down, current address information */ #define LINK_QUALITY_UP 4 /* Link active */ #define MAXIMUM_LINK_QUALITY 3 typedef void netlink_link_quality_fn(void *st, uint32_t quality); typedef void netlink_register_fn(void *st, netlink_deliver_fn *deliver, void *dst, uint32_t *localmtu_r /* NULL ok */); typedef void netlink_output_config_fn(void *st, struct buffer_if *buf); typedef bool_t netlink_check_config_fn(void *st, struct buffer_if *buf); typedef void netlink_set_mtu_fn(void *st, int32_t new_mtu); struct netlink_if { void *st; netlink_register_fn *reg; netlink_deliver_fn *deliver; netlink_link_quality_fn *set_quality; netlink_set_mtu_fn *set_mtu; }; /* DH interface */ /* Returns public key as a malloced hex string */ typedef string_t dh_makepublic_fn(void *st, uint8_t *secret, int32_t secretlen); /* Fills buffer (up to buflen) with shared secret */ typedef void dh_makeshared_fn(void *st, uint8_t *secret, int32_t secretlen, cstring_t rempublic, uint8_t *sharedsecret, int32_t buflen); struct dh_if { void *st; int32_t len; /* Approximate size of modulus in bytes */ int32_t ceil_len; /* Number of bytes just sufficient to contain modulus */ dh_makepublic_fn *makepublic; dh_makeshared_fn *makeshared; }; /* HASH interface */ typedef void hash_init_fn(void *st /* slen bytes alloc'd by caller */); typedef void hash_update_fn(void *st, const void *buf, int32_t len); typedef void hash_final_fn(void *st, uint8_t *digest /* hlen bytes */); struct hash_if { int32_t slen; /* State length in bytes */ int32_t hlen; /* Hash output length in bytes */ hash_init_fn *init; hash_update_fn *update; hash_final_fn *final; }; extern struct hash_if *const sha1_hash_if; /* for where this is hardcoded */ /* BUFFER interface */ struct buffer_if { bool_t free; cstring_t owner; /* Set to constant string */ struct cloc loc; /* Where we were defined */ uint8_t *base; uint8_t *start; int32_t size; /* Size of buffer contents */ int32_t alloclen; /* Total length allocated at base */ }; /***** LOG functions *****/ #define M_DEBUG_CONFIG 0x001 #define M_DEBUG_PHASE 0x002 #define M_DEBUG 0x004 #define M_INFO 0x008 #define M_NOTICE 0x010 #define M_WARNING 0x020 #define M_ERR 0x040 #define M_SECURITY 0x080 #define M_FATAL 0x100 /* The fatal() family of functions require messages that do not end in '\n' */ extern NORETURN(fatal(const char *message, ...)) FORMAT(printf,1,2); extern NORETURN(fatal_perror(const char *message, ...)) FORMAT(printf,1,2); extern NORETURN(fatal_status(int status, const char *message, ...)) FORMAT(printf,2,3); extern NORETURN(fatal_perror_status(int status, const char *message, ...)) FORMAT(printf,2,3); /* Convenient nonfatal logging. Requires message that does not end in '\n'. * If class contains M_FATAL, exits (after entering PHASE_SHUTDOWN). * lg, errnoval and loc may sensibly be 0. desc must NOT be 0. * lg_[v]perror save and restore errno. */ void lg_vperror(struct log_if *lg, const char *desc, struct cloc *loc, int class, int errnoval, const char *fmt, va_list al) FORMAT(printf,6,0); void lg_perror(struct log_if *lg, const char *desc, struct cloc *loc, int class, int errnoval, const char *fmt, ...) FORMAT(printf,6,7); void lg_exitstatus(struct log_if *lg, const char *desc, struct cloc *loc, int class, int status, const char *progname); /* The cfgfatal() family of functions require messages that end in '\n' */ extern NORETURN(cfgfatal(struct cloc loc, cstring_t facility, const char *message, ...)) FORMAT(printf,3,4); extern void cfgfile_postreadcheck(struct cloc loc, FILE *f); extern NORETURN(vcfgfatal_maybefile(FILE *maybe_f, struct cloc loc, cstring_t facility, const char *message, va_list, const char *suffix)) FORMAT(printf,4,0); extern NORETURN(cfgfatal_maybefile(FILE *maybe_f, struct cloc loc, cstring_t facility, const char *message, ...)) FORMAT(printf,4,5); extern NORETURN(cfgfatal_cl_type(struct cloc loc, const char *facility, closure_t *cl, uint32_t exp_type, const char *name)); extern void Message(uint32_t class, const char *message, ...) FORMAT(printf,2,3); extern void log_from_fd(int fd, cstring_t prefix, struct log_if *log); /***** END of log functions *****/ #define STRING2(x) #x #define STRING(x) STRING2(x) #define FILLZERO(obj) (memset(&(obj),0,sizeof((obj)))) #define ARRAY_SIZE(ary) (sizeof((ary))/sizeof((ary)[0])) /* * void COPY_OBJ( OBJECT& dst, const OBJECT& src); * void COPY_ARRAY(OBJECT *dst, const OBJECT *src, INTEGER count); * // Typesafe: we check that the type OBJECT is the same in both cases. * // It is OK to use COPY_OBJ on an array object, provided dst is * // _actually_ the whole array object and not decayed into a * // pointer (e.g. a formal parameter). */ #define COPY_OBJ(dst,src) \ (&(dst)==&(src), memcpy(&(dst),&(src),sizeof((dst)))) #define COPY_ARRAY(dst,src,count) \ (&(dst)[0]==&(src)[0], memcpy((dst),(src),sizeof((dst)[0])*(count))) #endif /* secnet_h */ work/serpent.c0000664000000000000000000003752515063477206010602 0ustar /* * serpent.c: Implementation of the Serpent block cipher */ /* * This file is Free Software. It has been modified to as part of its * incorporation into secnet. * * Copyright 1998 Ross Anderson, Eli Biham, Lars Knudsen * Copyright 1995-2001 Stephen Early * Copyright 2011-2013 Ian Jackson * * For more information about Serpent see * http://www.cl.cam.ac.uk/users/rja14/serpent.html * * You may redistribute secnet as a whole and/or modify it under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 3, or (at your option) any * later version. * * You may redistribute this file and/or modify it under the terms of * the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later * version. * * This software 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 software; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include #include "hexdebug.h" #include "serpent.h" #include "serpentsboxes.h" #ifdef SERPENT_BIGENDIAN #define GETPUT_CP(bytenum) \ (((basep) + (lenbytes) - (offset) - 4)[(bytenum)]) #define SERPENT_DECORATE(func) serpentbe_##func #else /* !defined(SERPENT_BIGENDIAN) */ #define GETPUT_CP(bytenum) \ (((basep) + (offset))[3-(bytenum)]) #define SERPENT_DECORATE(func) serpent_##func #endif /* !defined(SERPENT_BIGENDIAN) */ #if 0 #include static void SERP_DEBUG(const char *str1, const void *ary, int sz, const char *str2) { fprintf(stderr,"%s",str1); hexdebug(stderr,ary,sz); fprintf(stderr,"%s",str2); } #else #define SERP_DEBUG(str1,aryv,sz,str2) /*empty*/ #endif static uint32_t serpent_get_32bit(const uint8_t *basep, int lenbytes, int offset) { return (((uint32_t)GETPUT_CP(0) << 24) | ((uint32_t)GETPUT_CP(1) << 16) | ((uint32_t)GETPUT_CP(2) << +8) | ((uint32_t)GETPUT_CP(3))); } static void serpent_put_32bit(uint8_t *basep, int lenbytes, int offset, uint32_t value) { GETPUT_CP(0) = (char)((value) >> 24); GETPUT_CP(1) = (char)((value) >> 16); GETPUT_CP(2) = (char)((value) >> 8); GETPUT_CP(3) = (char)(value); } void SERPENT_DECORATE(makekey)(struct keyInstance *key, int keyLen, const uint8_t *keyMaterial) { int i; uint32_t j; uint32_t w[132],k[132]; SERP_DEBUG("SERPENT makekey ",keyMaterial,keyLen/8,"\n"); for(i=0; isubkeys[i][j] = k[4*i+j]; } void SERPENT_DECORATE(encrypt)(struct keyInstance *key, const uint8_t plaintext[16], uint8_t ciphertext[16]) { register uint32_t x0, x1, x2, x3; register uint32_t y0, y1, y2, y3; SERP_DEBUG("SERPENT encrypt ",plaintext,16," ->"); x0=serpent_get_32bit(plaintext,16,+0); x1=serpent_get_32bit(plaintext,16,+4); x2=serpent_get_32bit(plaintext,16,+8); x3=serpent_get_32bit(plaintext,16,12); /* Start to encrypt the plaintext x */ keying(x0, x1, x2, x3, key->subkeys[ 0]); RND00(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[ 1]); RND01(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[ 2]); RND02(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[ 3]); RND03(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[ 4]); RND04(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[ 5]); RND05(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[ 6]); RND06(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[ 7]); RND07(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[ 8]); RND08(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[ 9]); RND09(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[10]); RND10(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[11]); RND11(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[12]); RND12(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[13]); RND13(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[14]); RND14(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[15]); RND15(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[16]); RND16(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[17]); RND17(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[18]); RND18(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[19]); RND19(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[20]); RND20(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[21]); RND21(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[22]); RND22(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[23]); RND23(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[24]); RND24(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[25]); RND25(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[26]); RND26(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[27]); RND27(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[28]); RND28(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[29]); RND29(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[30]); RND30(x0, x1, x2, x3, y0, y1, y2, y3); transform(y0, y1, y2, y3, x0, x1, x2, x3); keying(x0, x1, x2, x3, key->subkeys[31]); RND31(x0, x1, x2, x3, y0, y1, y2, y3); x0 = y0; x1 = y1; x2 = y2; x3 = y3; keying(x0, x1, x2, x3, key->subkeys[32]); /* The ciphertext is now in x */ serpent_put_32bit(ciphertext,16,+0, x0); serpent_put_32bit(ciphertext,16,+4, x1); serpent_put_32bit(ciphertext,16,+8, x2); serpent_put_32bit(ciphertext,16,12, x3); SERP_DEBUG(" ",ciphertext,16,"\n"); } void SERPENT_DECORATE(decrypt)(struct keyInstance *key, const uint8_t ciphertext[16], uint8_t plaintext[16]) { register uint32_t x0, x1, x2, x3; register uint32_t y0, y1, y2, y3; SERP_DEBUG("SERPENT decrypt ",ciphertext,16," ->"); x0=serpent_get_32bit(ciphertext,16,+0); x1=serpent_get_32bit(ciphertext,16,+4); x2=serpent_get_32bit(ciphertext,16,+8); x3=serpent_get_32bit(ciphertext,16,12); /* Start to decrypt the ciphertext x */ keying(x0, x1, x2, x3, key->subkeys[32]); InvRND31(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[31]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND30(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[30]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND29(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[29]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND28(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[28]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND27(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[27]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND26(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[26]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND25(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[25]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND24(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[24]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND23(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[23]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND22(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[22]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND21(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[21]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND20(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[20]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND19(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[19]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND18(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[18]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND17(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[17]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND16(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[16]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND15(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[15]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND14(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[14]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND13(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[13]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND12(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[12]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND11(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[11]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND10(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[10]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND09(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[ 9]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND08(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[ 8]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND07(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[ 7]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND06(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[ 6]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND05(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[ 5]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND04(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[ 4]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND03(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[ 3]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND02(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[ 2]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND01(x0, x1, x2, x3, y0, y1, y2, y3); keying(y0, y1, y2, y3, key->subkeys[ 1]); inv_transform(y0, y1, y2, y3, x0, x1, x2, x3); InvRND00(x0, x1, x2, x3, y0, y1, y2, y3); x0 = y0; x1 = y1; x2 = y2; x3 = y3; keying(x0, x1, x2, x3, key->subkeys[ 0]); /* The plaintext is now in x */ serpent_put_32bit(plaintext,16,+0, x0); serpent_put_32bit(plaintext,16,+4, x1); serpent_put_32bit(plaintext,16,+8, x2); serpent_put_32bit(plaintext,16,12, x3); SERP_DEBUG(" ",plaintext,16,"\n"); } work/serpent.h0000664000000000000000000000201015063477206010564 0ustar /* * This file is part of secnet. * See LICENCE and this file CREDITS for full list of copyright holders. * SPDX-License-Identifier: GPL-3.0-or-later * There is NO WARRANTY. */ #ifndef serpent_h #define serpent_h struct keyInstance { uint32_t key[8]; /* The key in binary */ uint32_t subkeys[33][4]; /* Serpent subkeys */ }; /* Function protoypes */ void serpent_makekey(struct keyInstance *key, int keyLen, const uint8_t *keyMaterial); void serpentbe_makekey(struct keyInstance *key, int keyLen, const uint8_t *keyMaterial); void serpent_encrypt(struct keyInstance *key, const uint8_t plaintext[16], uint8_t ciphertext[16]); void serpentbe_encrypt(struct keyInstance *key, const uint8_t plaintext[16], uint8_t ciphertext[16]); void serpent_decrypt(struct keyInstance *key, const uint8_t ciphertext[16], uint8_t plaintext[16]); void serpentbe_decrypt(struct keyInstance *key, const uint8_t ciphertext[16], uint8_t plaintext[16]); #endif /* serpent_h */ work/serpentbe.c0000664000000000000000000000034615063477206011100 0ustar /* * This file is part of secnet. * See LICENCE and this file CREDITS for full list of copyright holders. * SPDX-License-Identifier: GPL-3.0-or-later * There is NO WARRANTY. */ #define SERPENT_BIGENDIAN #include "serpent.c" work/serpentsboxes.h0000664000000000000000000003443515063477206012030 0ustar /* * serpentsboxes.h: S-boxes; internal to Serpent implementation. */ /* * This file is Free Software. It is now being distributed with * secnet. * * Copyright 1998 Ross Anderson, Eli Biham, Lars Knudsen * Copyright 1995-2001 Stephen Early * Copyright 2011-2013 Ian Jackson * * For more information about Serpent see * http://www.cl.cam.ac.uk/users/rja14/serpent.html * * You may redistribute secnet as a whole and/or modify it under the * terms of the GNU General Public License as published by the Free * Software Foundation; either version 3, or (at your option) any * later version. * * You may redistribute this file and/or modify it under the terms of * the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later * version. * * This software 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 software; if not, see * https://www.gnu.org/licenses/gpl.html. */ /* S0: 3 8 15 1 10 6 5 11 14 13 4 2 7 0 9 12 */ /* depth = 5,7,4,2, Total gates=18 */ #define RND00(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t05, t06, t07, t08, t09, t11, t12, t13, t14, t15, t17, t01;\ t01 = b ^ c ; \ t02 = a | d ; \ t03 = a ^ b ; \ z = t02 ^ t01; \ t05 = c | z ; \ t06 = a ^ d ; \ t07 = b | c ; \ t08 = d & t05; \ t09 = t03 & t07; \ y = t09 ^ t08; \ t11 = t09 & y ; \ t12 = c ^ d ; \ t13 = t07 ^ t11; \ t14 = b & t06; \ t15 = t06 ^ t13; \ w = ~ t15; \ t17 = w ^ t14; \ x = t12 ^ t17; } /* InvS0: 13 3 11 0 10 6 5 12 1 14 4 7 15 9 8 2 */ /* depth = 8,4,3,6, Total gates=19 */ #define InvRND00(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t04, t05, t06, t08, t09, t10, t12, t13, t14, t15, t17, t18, t01;\ t01 = c ^ d ; \ t02 = a | b ; \ t03 = b | c ; \ t04 = c & t01; \ t05 = t02 ^ t01; \ t06 = a | t04; \ y = ~ t05; \ t08 = b ^ d ; \ t09 = t03 & t08; \ t10 = d | y ; \ x = t09 ^ t06; \ t12 = a | t05; \ t13 = x ^ t12; \ t14 = t03 ^ t10; \ t15 = a ^ c ; \ z = t14 ^ t13; \ t17 = t05 & t13; \ t18 = t14 | t17; \ w = t15 ^ t18; } /* S1: 15 12 2 7 9 0 5 10 1 11 14 8 6 13 3 4 */ /* depth = 10,7,3,5, Total gates=18 */ #define RND01(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t04, t05, t06, t07, t08, t10, t11, t12, t13, t16, t17, t01;\ t01 = a | d ; \ t02 = c ^ d ; \ t03 = ~ b ; \ t04 = a ^ c ; \ t05 = a | t03; \ t06 = d & t04; \ t07 = t01 & t02; \ t08 = b | t06; \ y = t02 ^ t05; \ t10 = t07 ^ t08; \ t11 = t01 ^ t10; \ t12 = y ^ t11; \ t13 = b & d ; \ z = ~ t10; \ x = t13 ^ t12; \ t16 = t10 | x ; \ t17 = t05 & t16; \ w = c ^ t17; } /* InvS1: 5 8 2 14 15 6 12 3 11 4 7 9 1 13 10 0 */ /* depth = 7,4,5,3, Total gates=18 */ #define InvRND01(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t14, t15, t17, t01;\ t01 = a ^ b ; \ t02 = b | d ; \ t03 = a & c ; \ t04 = c ^ t02; \ t05 = a | t04; \ t06 = t01 & t05; \ t07 = d | t03; \ t08 = b ^ t06; \ t09 = t07 ^ t06; \ t10 = t04 | t03; \ t11 = d & t08; \ y = ~ t09; \ x = t10 ^ t11; \ t14 = a | y ; \ t15 = t06 ^ x ; \ z = t01 ^ t04; \ t17 = c ^ t15; \ w = t14 ^ t17; } /* S2: 8 6 7 9 3 12 10 15 13 1 14 4 0 11 5 2 */ /* depth = 3,8,11,7, Total gates=16 */ #define RND02(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t05, t06, t07, t08, t09, t10, t12, t13, t14, t01;\ t01 = a | c ; \ t02 = a ^ b ; \ t03 = d ^ t01; \ w = t02 ^ t03; \ t05 = c ^ w ; \ t06 = b ^ t05; \ t07 = b | t05; \ t08 = t01 & t06; \ t09 = t03 ^ t07; \ t10 = t02 | t09; \ x = t10 ^ t08; \ t12 = a | d ; \ t13 = t09 ^ x ; \ t14 = b ^ t13; \ z = ~ t09; \ y = t12 ^ t14; } /* InvS2: 12 9 15 4 11 14 1 2 0 3 6 13 5 8 10 7 */ /* depth = 3,6,8,3, Total gates=18 */ #define InvRND02(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t04, t06, t07, t08, t09, t10, t11, t12, t15, t16, t17, t01;\ t01 = a ^ d ; \ t02 = c ^ d ; \ t03 = a & c ; \ t04 = b | t02; \ w = t01 ^ t04; \ t06 = a | c ; \ t07 = d | w ; \ t08 = ~ d ; \ t09 = b & t06; \ t10 = t08 | t03; \ t11 = b & t07; \ t12 = t06 & t02; \ z = t09 ^ t10; \ x = t12 ^ t11; \ t15 = c & z ; \ t16 = w ^ x ; \ t17 = t10 ^ t15; \ y = t16 ^ t17; } /* S3: 0 15 11 8 12 9 6 3 13 1 2 4 10 7 5 14 */ /* depth = 8,3,5,5, Total gates=18 */ #define RND03(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t13, t14, t15, t01;\ t01 = a ^ c ; \ t02 = a | d ; \ t03 = a & d ; \ t04 = t01 & t02; \ t05 = b | t03; \ t06 = a & b ; \ t07 = d ^ t04; \ t08 = c | t06; \ t09 = b ^ t07; \ t10 = d & t05; \ t11 = t02 ^ t10; \ z = t08 ^ t09; \ t13 = d | z ; \ t14 = a | t07; \ t15 = b & t13; \ y = t08 ^ t11; \ w = t14 ^ t15; \ x = t05 ^ t04; } /* InvS3: 0 9 10 7 11 14 6 13 3 5 12 2 4 8 15 1 */ /* depth = 3,6,4,4, Total gates=17 */ #define InvRND03(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t04, t05, t06, t07, t09, t11, t12, t13, t14, t16, t01;\ t01 = c | d ; \ t02 = a | d ; \ t03 = c ^ t02; \ t04 = b ^ t02; \ t05 = a ^ d ; \ t06 = t04 & t03; \ t07 = b & t01; \ y = t05 ^ t06; \ t09 = a ^ t03; \ w = t07 ^ t03; \ t11 = w | t05; \ t12 = t09 & t11; \ t13 = a & y ; \ t14 = t01 ^ t05; \ x = b ^ t12; \ t16 = b | t13; \ z = t14 ^ t16; } /* S4: 1 15 8 3 12 0 11 6 2 5 4 10 9 14 7 13 */ /* depth = 6,7,5,3, Total gates=19 */ #define RND04(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t04, t05, t06, t08, t09, t10, t11, t12, t13, t14, t15, t16, t01;\ t01 = a | b ; \ t02 = b | c ; \ t03 = a ^ t02; \ t04 = b ^ d ; \ t05 = d | t03; \ t06 = d & t01; \ z = t03 ^ t06; \ t08 = z & t04; \ t09 = t04 & t05; \ t10 = c ^ t06; \ t11 = b & c ; \ t12 = t04 ^ t08; \ t13 = t11 | t03; \ t14 = t10 ^ t09; \ t15 = a & t05; \ t16 = t11 | t12; \ y = t13 ^ t08; \ x = t15 ^ t16; \ w = ~ t14; } /* InvS4: 5 0 8 3 10 9 7 14 2 12 11 6 4 15 13 1 */ /* depth = 6,4,7,3, Total gates=17 */ #define InvRND04(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t04, t05, t06, t07, t09, t10, t11, t12, t13, t15, t01;\ t01 = b | d ; \ t02 = c | d ; \ t03 = a & t01; \ t04 = b ^ t02; \ t05 = c ^ d ; \ t06 = ~ t03; \ t07 = a & t04; \ x = t05 ^ t07; \ t09 = x | t06; \ t10 = a ^ t07; \ t11 = t01 ^ t09; \ t12 = d ^ t04; \ t13 = c | t10; \ z = t03 ^ t12; \ t15 = a ^ t04; \ y = t11 ^ t13; \ w = t15 ^ t09; } /* S5: 15 5 2 11 4 10 9 12 0 3 14 8 13 6 7 1 */ /* depth = 4,6,8,6, Total gates=17 */ #define RND05(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t04, t05, t07, t08, t09, t10, t11, t12, t13, t14, t01;\ t01 = b ^ d ; \ t02 = b | d ; \ t03 = a & t01; \ t04 = c ^ t02; \ t05 = t03 ^ t04; \ w = ~ t05; \ t07 = a ^ t01; \ t08 = d | w ; \ t09 = b | t05; \ t10 = d ^ t08; \ t11 = b | t07; \ t12 = t03 | w ; \ t13 = t07 | t10; \ t14 = t01 ^ t11; \ y = t09 ^ t13; \ x = t07 ^ t08; \ z = t12 ^ t14; } /* InvS5: 8 15 2 9 4 1 13 14 11 6 5 3 7 12 10 0 */ /* depth = 4,6,9,7, Total gates=17 */ #define InvRND05(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t04, t05, t07, t08, t09, t10, t12, t13, t15, t16, t01;\ t01 = a & d ; \ t02 = c ^ t01; \ t03 = a ^ d ; \ t04 = b & t02; \ t05 = a & c ; \ w = t03 ^ t04; \ t07 = a & w ; \ t08 = t01 ^ w ; \ t09 = b | t05; \ t10 = ~ b ; \ x = t08 ^ t09; \ t12 = t10 | t07; \ t13 = w | x ; \ z = t02 ^ t12; \ t15 = t02 ^ t13; \ t16 = b ^ d ; \ y = t16 ^ t15; } /* S6: 7 2 12 5 8 4 6 11 14 9 1 15 13 3 10 0 */ /* depth = 8,3,6,3, Total gates=19 */ #define RND06(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t04, t05, t07, t08, t09, t10, t11, t12, t13, t15, t17, t18, t01;\ t01 = a & d ; \ t02 = b ^ c ; \ t03 = a ^ d ; \ t04 = t01 ^ t02; \ t05 = b | c ; \ x = ~ t04; \ t07 = t03 & t05; \ t08 = b & x ; \ t09 = a | c ; \ t10 = t07 ^ t08; \ t11 = b | d ; \ t12 = c ^ t11; \ t13 = t09 ^ t10; \ y = ~ t13; \ t15 = x & t03; \ z = t12 ^ t07; \ t17 = a ^ b ; \ t18 = y ^ t15; \ w = t17 ^ t18; } /* InvS6: 15 10 1 13 5 3 6 0 4 9 14 7 2 12 8 11 */ /* depth = 5,3,8,6, Total gates=19 */ #define InvRND06(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t04, t05, t06, t07, t08, t09, t12, t13, t14, t15, t16, t17, t01;\ t01 = a ^ c ; \ t02 = ~ c ; \ t03 = b & t01; \ t04 = b | t02; \ t05 = d | t03; \ t06 = b ^ d ; \ t07 = a & t04; \ t08 = a | t02; \ t09 = t07 ^ t05; \ x = t06 ^ t08; \ w = ~ t09; \ t12 = b & w ; \ t13 = t01 & t05; \ t14 = t01 ^ t12; \ t15 = t07 ^ t13; \ t16 = d | t02; \ t17 = a ^ x ; \ z = t17 ^ t15; \ y = t16 ^ t14; } /* S7: 1 13 15 0 14 8 2 11 7 4 12 10 9 3 5 6 */ /* depth = 10,7,10,4, Total gates=19 */ #define RND07(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t04, t05, t06, t08, t09, t10, t11, t13, t14, t15, t16, t17, t01;\ t01 = a & c ; \ t02 = ~ d ; \ t03 = a & t02; \ t04 = b | t01; \ t05 = a & b ; \ t06 = c ^ t04; \ z = t03 ^ t06; \ t08 = c | z ; \ t09 = d | t05; \ t10 = a ^ t08; \ t11 = t04 & z ; \ x = t09 ^ t10; \ t13 = b ^ x ; \ t14 = t01 ^ x ; \ t15 = c ^ t05; \ t16 = t11 | t13; \ t17 = t02 | t14; \ w = t15 ^ t17; \ y = a ^ t16; } /* InvS7: 3 0 6 13 9 14 15 8 5 12 11 7 10 1 4 2 */ /* depth = 9,7,3,3, Total gates=18 */ #define InvRND07(a,b,c,d,w,x,y,z) \ { register unsigned long t02, t03, t04, t06, t07, t08, t09, t10, t11, t13, t14, t15, t16, t01;\ t01 = a & b ; \ t02 = a | b ; \ t03 = c | t01; \ t04 = d & t02; \ z = t03 ^ t04; \ t06 = b ^ t04; \ t07 = d ^ z ; \ t08 = ~ t07; \ t09 = t06 | t08; \ t10 = b ^ d ; \ t11 = a | d ; \ x = a ^ t09; \ t13 = c ^ t06; \ t14 = c & t11; \ t15 = d | x ; \ t16 = t01 | t10; \ w = t13 ^ t15; \ y = t14 ^ t16; } #define RND08(a,b,c,d,e,f,g,h) RND00(a,b,c,d,e,f,g,h) #define RND09(a,b,c,d,e,f,g,h) RND01(a,b,c,d,e,f,g,h) #define RND10(a,b,c,d,e,f,g,h) RND02(a,b,c,d,e,f,g,h) #define RND11(a,b,c,d,e,f,g,h) RND03(a,b,c,d,e,f,g,h) #define RND12(a,b,c,d,e,f,g,h) RND04(a,b,c,d,e,f,g,h) #define RND13(a,b,c,d,e,f,g,h) RND05(a,b,c,d,e,f,g,h) #define RND14(a,b,c,d,e,f,g,h) RND06(a,b,c,d,e,f,g,h) #define RND15(a,b,c,d,e,f,g,h) RND07(a,b,c,d,e,f,g,h) #define RND16(a,b,c,d,e,f,g,h) RND00(a,b,c,d,e,f,g,h) #define RND17(a,b,c,d,e,f,g,h) RND01(a,b,c,d,e,f,g,h) #define RND18(a,b,c,d,e,f,g,h) RND02(a,b,c,d,e,f,g,h) #define RND19(a,b,c,d,e,f,g,h) RND03(a,b,c,d,e,f,g,h) #define RND20(a,b,c,d,e,f,g,h) RND04(a,b,c,d,e,f,g,h) #define RND21(a,b,c,d,e,f,g,h) RND05(a,b,c,d,e,f,g,h) #define RND22(a,b,c,d,e,f,g,h) RND06(a,b,c,d,e,f,g,h) #define RND23(a,b,c,d,e,f,g,h) RND07(a,b,c,d,e,f,g,h) #define RND24(a,b,c,d,e,f,g,h) RND00(a,b,c,d,e,f,g,h) #define RND25(a,b,c,d,e,f,g,h) RND01(a,b,c,d,e,f,g,h) #define RND26(a,b,c,d,e,f,g,h) RND02(a,b,c,d,e,f,g,h) #define RND27(a,b,c,d,e,f,g,h) RND03(a,b,c,d,e,f,g,h) #define RND28(a,b,c,d,e,f,g,h) RND04(a,b,c,d,e,f,g,h) #define RND29(a,b,c,d,e,f,g,h) RND05(a,b,c,d,e,f,g,h) #define RND30(a,b,c,d,e,f,g,h) RND06(a,b,c,d,e,f,g,h) #define RND31(a,b,c,d,e,f,g,h) RND07(a,b,c,d,e,f,g,h) #define InvRND08(a,b,c,d,e,f,g,h) InvRND00(a,b,c,d,e,f,g,h) #define InvRND09(a,b,c,d,e,f,g,h) InvRND01(a,b,c,d,e,f,g,h) #define InvRND10(a,b,c,d,e,f,g,h) InvRND02(a,b,c,d,e,f,g,h) #define InvRND11(a,b,c,d,e,f,g,h) InvRND03(a,b,c,d,e,f,g,h) #define InvRND12(a,b,c,d,e,f,g,h) InvRND04(a,b,c,d,e,f,g,h) #define InvRND13(a,b,c,d,e,f,g,h) InvRND05(a,b,c,d,e,f,g,h) #define InvRND14(a,b,c,d,e,f,g,h) InvRND06(a,b,c,d,e,f,g,h) #define InvRND15(a,b,c,d,e,f,g,h) InvRND07(a,b,c,d,e,f,g,h) #define InvRND16(a,b,c,d,e,f,g,h) InvRND00(a,b,c,d,e,f,g,h) #define InvRND17(a,b,c,d,e,f,g,h) InvRND01(a,b,c,d,e,f,g,h) #define InvRND18(a,b,c,d,e,f,g,h) InvRND02(a,b,c,d,e,f,g,h) #define InvRND19(a,b,c,d,e,f,g,h) InvRND03(a,b,c,d,e,f,g,h) #define InvRND20(a,b,c,d,e,f,g,h) InvRND04(a,b,c,d,e,f,g,h) #define InvRND21(a,b,c,d,e,f,g,h) InvRND05(a,b,c,d,e,f,g,h) #define InvRND22(a,b,c,d,e,f,g,h) InvRND06(a,b,c,d,e,f,g,h) #define InvRND23(a,b,c,d,e,f,g,h) InvRND07(a,b,c,d,e,f,g,h) #define InvRND24(a,b,c,d,e,f,g,h) InvRND00(a,b,c,d,e,f,g,h) #define InvRND25(a,b,c,d,e,f,g,h) InvRND01(a,b,c,d,e,f,g,h) #define InvRND26(a,b,c,d,e,f,g,h) InvRND02(a,b,c,d,e,f,g,h) #define InvRND27(a,b,c,d,e,f,g,h) InvRND03(a,b,c,d,e,f,g,h) #define InvRND28(a,b,c,d,e,f,g,h) InvRND04(a,b,c,d,e,f,g,h) #define InvRND29(a,b,c,d,e,f,g,h) InvRND05(a,b,c,d,e,f,g,h) #define InvRND30(a,b,c,d,e,f,g,h) InvRND06(a,b,c,d,e,f,g,h) #define InvRND31(a,b,c,d,e,f,g,h) InvRND07(a,b,c,d,e,f,g,h) /* Linear transformations and key mixing: */ #define ROL(x,n) ((((unsigned long)(x))<<(n))| \ (((unsigned long)(x))>>(32-(n)))) #define ROR(x,n) ((((unsigned long)(x))<<(32-(n)))| \ (((unsigned long)(x))>>(n))) #define transform(x0, x1, x2, x3, y0, y1, y2, y3) \ y0 = ROL(x0, 13); \ y2 = ROL(x2, 3); \ y1 = x1 ^ y0 ^ y2; \ y3 = x3 ^ y2 ^ ((unsigned long)y0)<<3; \ y1 = ROL(y1, 1); \ y3 = ROL(y3, 7); \ y0 = y0 ^ y1 ^ y3; \ y2 = y2 ^ y3 ^ ((unsigned long)y1<<7); \ y0 = ROL(y0, 5); \ y2 = ROL(y2, 22) #define inv_transform(x0, x1, x2, x3, y0, y1, y2, y3) \ y2 = ROR(x2, 22);\ y0 = ROR(x0, 5); \ y2 = y2 ^ x3 ^ ((unsigned long)x1<<7); \ y0 = y0 ^ x1 ^ x3; \ y3 = ROR(x3, 7); \ y1 = ROR(x1, 1); \ y3 = y3 ^ y2 ^ ((unsigned long)y0)<<3; \ y1 = y1 ^ y0 ^ y2; \ y2 = ROR(y2, 3); \ y0 = ROR(y0, 13) #define keying(x0, x1, x2, x3, subkey) \ x0^=subkey[0];x1^=subkey[1]; \ x2^=subkey[2];x3^=subkey[3] /* PHI: Constant used in the key schedule */ #define PHI 0x9e3779b9L work/setup.mac0000775000000000000000000000452315063477206010573 0ustar #!/bin/bash # # Richard Kettlewell 2011-06-18 # # This file is part of secnet. # See README for full list of copyright holders. # # secnet 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. # # secnet 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 # version 3 along with secnet; if not, see # https://www.gnu.org/licenses/gpl.html. # set -e group=${group:-secnet} user=${user:-secnet} # pick ID1 ID2 ... IDn # Echoes an ID matching none of ID1..IDn pick() { local n n=250 # better not choose 0! while :; do ok=true for k in "$@"; do if [ $n = $k ]; then ok=false break fi done if $ok; then echo $n return fi n=$((1+$n)) done } if dscl . -read /Groups/$group >/dev/null 2>&1; then : else gids=$(dscl . -list /Groups PrimaryGroupID|awk '{print $2}') gid=$(pick $gids) dscl . -create /Groups/$group dscl . -create /Groups/$group PrimaryGroupID $gid dscl . -create /Groups/$group Password \* fi if dscl . -read /Users/$user >/dev/null 2>&1; then : else uids=$(dscl . -list /Users UniqueID|awk '{print $2}') uid=$(pick $uids) gid=$(dscl . -read /Groups/$group PrimaryGroupID | awk '{print $2}') dscl . -create /Users/$user dscl . -create /Users/$user UniqueID $uid dscl . -create /Users/$user UserShell /usr/bin/false dscl . -create /Users/$user RealName 'secnet' dscl . -create /Users/$user NFSHomeDirectory /var/empty dscl . -create /Users/$user PrimaryGroupID $gid dscl . -create /Users/$user Password \* fi cp uk.org.greenend.secnet.plist /Library/LaunchDaemons/. launchctl load /Library/LaunchDaemons echo "To start secnet:" echo " sudo launchctl start uk.org.greenend.secnet" echo echo "To stop secnet:" echo " sudo launchctl stop uk.org.greenend.secnet" echo echo "To uninstall:" echo " sudo launchctl unload /Library/LaunchDaemons/uk.org.greenend.secnet.plist" echo " sudo rm -f /Library/LaunchDaemons/uk.org.greenend.secnet.plist" work/sha1.c0000664000000000000000000002502515063477206007746 0ustar /* SHA-1 in C By Steve Reid 100% Public Domain [I interpet this as a blanket permision -iwj.] Note: parts of this file have been removed or modified to work in secnet. Instead of using this file in new projects, I suggest you use the unmodified version. SDE. ----------------- Modified 7/98 By James H. Brown Still 100% Public Domain Corrected a problem which generated improper hash values on 16 bit machines Routine SHA1Update changed from void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len) to void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned long len) The 'len' parameter was declared an int which works fine on 32 bit machines. However, on 16 bit machines an int is too small for the shifts being done against it. This caused the hash function to generate incorrect values if len was greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). Since the file IO in main() reads 16K at a time, any file 8K or larger would be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million "a"s). I also changed the declaration of variables i & j in SHA1Update to unsigned long from unsigned int for the same reason. These changes should make no difference to any 32 bit implementations since an int and a long are the same size in those environments. -- I also corrected a few compiler warnings generated by Borland C. 1. Added #include for exit() prototype 2. Removed unused variable 'j' in SHA1Final 3. Changed exit(0) to return(0) at end of main. ALL changes I made can be located by searching for comments containing 'JHB' ----------------- Modified 8/98 By Steve Reid Still 100% public domain 1- Removed #include and used return() instead of exit() 2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) 3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net ----------------- Modified 4/01 By Saul Kravitz Still 100% PD Modified to run on Compaq Alpha hardware. ----------------- (Further modifications as part of secnet. See git history for full details. - Ian Jackson et al) */ /* Test Vectors (from FIPS PUB 180-1) "abc" A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 A million repetitions of "a" 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ /* #define SHA1HANDSOFF */ #include "secnet.h" #include "util.h" #include #include #define SHA1HANDSOFF #if 0 #ifndef i386 /* For ALPHA (SAK) */ #define LITTLE_ENDIAN typedef long int int64; typedef unsigned long int uint64; typedef int int32; typedef unsigned int uint32; #else /*i386*/ #define LITTLE_ENDIAN typedef long long int int64; typedef unsigned long long int uint64; typedef long int int32; typedef unsigned long int uint32; #endif /*i386*/ #endif /* 0 */ /* Get types and defines from the secnet configuration */ /* typedef int64_t int64; */ typedef uint64_t uint64; /* typedef int32_t int32; */ typedef uint32_t uint32; /* #include */ /* prototype for exit() - JHB */ /* Using return() instead of exit() - SWR */ typedef struct { uint32 state[5]; uint32 count[2]; unsigned char buffer[64]; } SHA1_CTX; void SHA1Transform(uint32 state[5], unsigned char const buffer[64]); void SHA1Init(SHA1_CTX* context); void SHA1Update(SHA1_CTX* context, unsigned char const * data, uint32 len); /* JHB */ void SHA1Final(unsigned char digest[20], SHA1_CTX* context); #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ #ifndef WORDS_BIGENDIAN #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ |(rol(block->l[i],8)&0x00FF00FF)) #else #define blk0(i) block->l[i] #endif #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); #ifdef VERBOSE /* SAK */ void SHAPrintContext(SHA1_CTX *context, char *msg){ printf("%s (%d,%d) %x %x %x %x %x\n", msg, context->count[0], context->count[1], context->state[0], context->state[1], context->state[2], context->state[3], context->state[4]); } #endif /* Hash a single 512-bit block. This is the core of the algorithm. */ void SHA1Transform(uint32 state[5], unsigned char const buffer[64]) { uint32 a, b, c, d, e; typedef union { unsigned char c[64]; uint32 l[16]; } CHAR64LONG16; CHAR64LONG16* block; #ifdef SHA1HANDSOFF static unsigned char workspace[64]; block = (CHAR64LONG16*)workspace; memcpy(block, buffer, 64); #else block = (CHAR64LONG16*)buffer; #endif /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; } /* SHA1Init - Initialize new context */ void SHA1Init(SHA1_CTX* context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* Run your data through this. */ void SHA1Update(SHA1_CTX* context, unsigned char const* data, uint32 len) /* JHB */ { uint32 i, j; /* JHB */ #ifdef VERBOSE SHAPrintContext(context, "before"); #endif j = (context->count[0] >> 3) & 63; if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; context->count[1] += (len >> 29); if ((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64-j)); SHA1Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) { SHA1Transform(context->state, &data[i]); } j = 0; } else i = 0; memcpy(&context->buffer[j], &data[i], len - i); #ifdef VERBOSE SHAPrintContext(context, "after "); #endif } /* Add padding and return the message digest. */ void SHA1Final(unsigned char digest[20], SHA1_CTX* context) { uint32 i; /* JHB */ unsigned char finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } SHA1Update(context, (unsigned char *)"\200", 1); while ((context->count[0] & 504) != 448) { SHA1Update(context, (unsigned char *)"\0", 1); } SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ for (i = 0; i < 20; i++) { digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } /* Wipe variables */ i = 0; /* JHB */ memset(context->buffer, 0, 64); memset(context->state, 0, 20); memset(context->count, 0, 8); memset(finalcount, 0, 8); /* SWR */ #ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */ SHA1Transform(context->state, context->buffer); #endif } /*************************************************************/ /* Everything below here is the interface to secnet */ static void sha1_init(void *sst) { SHA1_CTX *ctx=sst; SHA1Init(ctx); } static void sha1_update(void *sst, const void *buf, int32_t len) { SHA1_CTX *ctx=sst; SHA1Update(ctx,buf,len); } static void sha1_final(void *sst, uint8_t *digest) { SHA1_CTX *ctx=sst; SHA1Final(digest,ctx); } struct sha1 { closure_t cl; struct hash_if ops; }; static struct sha1 st[1]; struct hash_if *const sha1_hash_if = &st->ops; void sha1_module(dict_t *dict) { cstring_t testinput="abcdbcdecdefdefgefghfghigh" "ijhijkijkljklmklmnlmnomnopnopq"; uint8_t expected[20]= { 0x84,0x98,0x3e,0x44, 0x1c,0x3b,0xd2,0x6e, 0xba,0xae,0x4a,0xa1, 0xf9,0x51,0x29,0xe5, 0xe5,0x46,0x70,0xf1}; uint8_t digest[20]; int i; st->cl.description="sha1"; st->cl.type=CL_HASH; st->cl.apply=NULL; st->cl.interface=&st->ops; st->ops.hlen=20; st->ops.slen=sizeof(SHA1_CTX); st->ops.init=sha1_init; st->ops.update=sha1_update; st->ops.final=sha1_final; dict_add(dict,"sha1",new_closure(&st->cl)); hash_hash(&st->ops,testinput,strlen(testinput),digest); for (i=0; i<20; i++) { if (digest[i]!=expected[i]) { fatal("sha1 module failed self-test"); } } } work/sha512.c0000664000000000000000000005350415063477206010120 0ustar /* sha512.c - Functions to compute SHA512 and SHA384 message digest of files or memory blocks according to the NIST specification FIPS-180-2. Copyright (C) 2005, 2006, 2008, 2009, 2010 Free Software Foundation, Inc. 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 . */ /* Written by David Madore, considerably copypasting from Scott G. Miller's sha1.c */ #include #include "sha512.h" #include #include #include #if USE_UNLOCKED_IO # include "unlocked-io.h" #endif #ifdef WORDS_BIGENDIAN # define SWAP(n) (n) #else # define SWAP(n) \ u64or (u64or (u64or (u64shl (n, 56), \ u64shl (u64and (n, u64lo (0x0000ff00)), 40)), \ u64or (u64shl (u64and (n, u64lo (0x00ff0000)), 24), \ u64shl (u64and (n, u64lo (0xff000000)), 8))), \ u64or (u64or (u64and (u64shr (n, 8), u64lo (0xff000000)), \ u64and (u64shr (n, 24), u64lo (0x00ff0000))), \ u64or (u64and (u64shr (n, 40), u64lo (0x0000ff00)), \ u64shr (n, 56)))) #endif #define BLOCKSIZE 32768 #if BLOCKSIZE % 128 != 0 # error "invalid BLOCKSIZE" #endif /* This array contains the bytes used to pad the buffer to the next 128-byte boundary. */ static const unsigned char fillbuf[128] = { 0x80, 0 /* , 0, 0, ... */ }; /* Takes a pointer to a 512 bit block of data (eight 64 bit ints) and intializes it to the start constants of the SHA512 algorithm. This must be called before using hash in the call to sha512_hash */ void sha512_init_ctx (struct sha512_ctx *ctx) { ctx->state[0] = u64hilo (0x6a09e667, 0xf3bcc908); ctx->state[1] = u64hilo (0xbb67ae85, 0x84caa73b); ctx->state[2] = u64hilo (0x3c6ef372, 0xfe94f82b); ctx->state[3] = u64hilo (0xa54ff53a, 0x5f1d36f1); ctx->state[4] = u64hilo (0x510e527f, 0xade682d1); ctx->state[5] = u64hilo (0x9b05688c, 0x2b3e6c1f); ctx->state[6] = u64hilo (0x1f83d9ab, 0xfb41bd6b); ctx->state[7] = u64hilo (0x5be0cd19, 0x137e2179); ctx->total[0] = ctx->total[1] = u64lo (0); ctx->buflen = 0; } void sha384_init_ctx (struct sha512_ctx *ctx) { ctx->state[0] = u64hilo (0xcbbb9d5d, 0xc1059ed8); ctx->state[1] = u64hilo (0x629a292a, 0x367cd507); ctx->state[2] = u64hilo (0x9159015a, 0x3070dd17); ctx->state[3] = u64hilo (0x152fecd8, 0xf70e5939); ctx->state[4] = u64hilo (0x67332667, 0xffc00b31); ctx->state[5] = u64hilo (0x8eb44a87, 0x68581511); ctx->state[6] = u64hilo (0xdb0c2e0d, 0x64f98fa7); ctx->state[7] = u64hilo (0x47b5481d, 0xbefa4fa4); ctx->total[0] = ctx->total[1] = u64lo (0); ctx->buflen = 0; } /* Copy the value from V into the memory location pointed to by *CP, If your architecture allows unaligned access, this is equivalent to * (__typeof__ (v) *) cp = v */ static inline void set_uint64 (char *cp, u64 v) { memcpy (cp, &v, sizeof v); } /* Put result from CTX in first 64 bytes following RESBUF. The result must be in little endian byte order. */ void * sha512_read_ctx (const struct sha512_ctx *ctx, void *resbuf) { int i; char *r = resbuf; for (i = 0; i < 8; i++) set_uint64 (r + i * sizeof ctx->state[0], SWAP (ctx->state[i])); return resbuf; } void * sha384_read_ctx (const struct sha512_ctx *ctx, void *resbuf) { int i; char *r = resbuf; for (i = 0; i < 6; i++) set_uint64 (r + i * sizeof ctx->state[0], SWAP (ctx->state[i])); return resbuf; } /* Process the remaining bytes in the internal buffer and the usual prolog according to the standard and write the result to RESBUF. */ static void sha512_conclude_ctx (struct sha512_ctx *ctx) { /* Take yet unprocessed bytes into account. */ size_t bytes = ctx->buflen; size_t size = (bytes < 112) ? 128 / 8 : 128 * 2 / 8; /* Now count remaining bytes. */ ctx->total[0] = u64plus (ctx->total[0], u64lo (bytes)); if (u64lt (ctx->total[0], u64lo (bytes))) ctx->total[1] = u64plus (ctx->total[1], u64lo (1)); /* Put the 128-bit file length in *bits* at the end of the buffer. Use set_uint64 rather than a simple assignment, to avoid risk of unaligned access. */ set_uint64 ((char *) &ctx->buffer[size - 2], SWAP (u64or (u64shl (ctx->total[1], 3), u64shr (ctx->total[0], 61)))); set_uint64 ((char *) &ctx->buffer[size - 1], SWAP (u64shl (ctx->total[0], 3))); memcpy (&((char *) ctx->buffer)[bytes], fillbuf, (size - 2) * 8 - bytes); /* Process last bytes. */ sha512_process_block (ctx->buffer, size * 8, ctx); } void * sha512_finish_ctx (struct sha512_ctx *ctx, void *resbuf) { sha512_conclude_ctx (ctx); return sha512_read_ctx (ctx, resbuf); } void * sha384_finish_ctx (struct sha512_ctx *ctx, void *resbuf) { sha512_conclude_ctx (ctx); return sha384_read_ctx (ctx, resbuf); } /* Compute SHA512 message digest for bytes read from STREAM. The resulting message digest number will be written into the 64 bytes beginning at RESBLOCK. */ int sha512_stream (FILE *stream, void *resblock) { struct sha512_ctx ctx; size_t sum; char *buffer = malloc (BLOCKSIZE + 72); if (!buffer) return 1; /* Initialize the computation context. */ sha512_init_ctx (&ctx); /* Iterate over full file contents. */ while (1) { /* We read the file in blocks of BLOCKSIZE bytes. One call of the computation function processes the whole buffer so that with the next round of the loop another block can be read. */ size_t n; sum = 0; /* Read block. Take care for partial reads. */ while (1) { n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); sum += n; if (sum == BLOCKSIZE) break; if (n == 0) { /* Check for the error flag IFF N == 0, so that we don't exit the loop after a partial read due to e.g., EAGAIN or EWOULDBLOCK. */ if (ferror (stream)) { free (buffer); return 1; } goto process_partial_block; } /* We've read at least one byte, so ignore errors. But always check for EOF, since feof may be true even though N > 0. Otherwise, we could end up calling fread after EOF. */ if (feof (stream)) goto process_partial_block; } /* Process buffer with BLOCKSIZE bytes. Note that BLOCKSIZE % 128 == 0 */ sha512_process_block (buffer, BLOCKSIZE, &ctx); } process_partial_block:; /* Process any remaining bytes. */ if (sum > 0) sha512_process_bytes (buffer, sum, &ctx); /* Construct result in desired memory. */ sha512_finish_ctx (&ctx, resblock); free (buffer); return 0; } /* FIXME: Avoid code duplication */ int sha384_stream (FILE *stream, void *resblock) { struct sha512_ctx ctx; size_t sum; char *buffer = malloc (BLOCKSIZE + 72); if (!buffer) return 1; /* Initialize the computation context. */ sha384_init_ctx (&ctx); /* Iterate over full file contents. */ while (1) { /* We read the file in blocks of BLOCKSIZE bytes. One call of the computation function processes the whole buffer so that with the next round of the loop another block can be read. */ size_t n; sum = 0; /* Read block. Take care for partial reads. */ while (1) { n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); sum += n; if (sum == BLOCKSIZE) break; if (n == 0) { /* Check for the error flag IFF N == 0, so that we don't exit the loop after a partial read due to e.g., EAGAIN or EWOULDBLOCK. */ if (ferror (stream)) { free (buffer); return 1; } goto process_partial_block; } /* We've read at least one byte, so ignore errors. But always check for EOF, since feof may be true even though N > 0. Otherwise, we could end up calling fread after EOF. */ if (feof (stream)) goto process_partial_block; } /* Process buffer with BLOCKSIZE bytes. Note that BLOCKSIZE % 128 == 0 */ sha512_process_block (buffer, BLOCKSIZE, &ctx); } process_partial_block:; /* Process any remaining bytes. */ if (sum > 0) sha512_process_bytes (buffer, sum, &ctx); /* Construct result in desired memory. */ sha384_finish_ctx (&ctx, resblock); free (buffer); return 0; } /* Compute SHA512 message digest for LEN bytes beginning at BUFFER. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ void * sha512_buffer (const char *buffer, size_t len, void *resblock) { struct sha512_ctx ctx; /* Initialize the computation context. */ sha512_init_ctx (&ctx); /* Process whole buffer but last len % 128 bytes. */ sha512_process_bytes (buffer, len, &ctx); /* Put result in desired memory area. */ return sha512_finish_ctx (&ctx, resblock); } void * sha384_buffer (const char *buffer, size_t len, void *resblock) { struct sha512_ctx ctx; /* Initialize the computation context. */ sha384_init_ctx (&ctx); /* Process whole buffer but last len % 128 bytes. */ sha512_process_bytes (buffer, len, &ctx); /* Put result in desired memory area. */ return sha384_finish_ctx (&ctx, resblock); } void sha512_process_bytes (const void *buffer, size_t len, struct sha512_ctx *ctx) { /* When we already have some bits in our internal buffer concatenate both inputs first. */ if (ctx->buflen != 0) { size_t left_over = ctx->buflen; size_t add = 256 - left_over > len ? len : 256 - left_over; memcpy (&((char *) ctx->buffer)[left_over], buffer, add); ctx->buflen += add; if (ctx->buflen > 128) { sha512_process_block (ctx->buffer, ctx->buflen & ~127, ctx); ctx->buflen &= 127; /* The regions in the following copy operation cannot overlap. */ memcpy (ctx->buffer, &((char *) ctx->buffer)[(left_over + add) & ~127], ctx->buflen); } buffer = (const char *) buffer + add; len -= add; } /* Process available complete blocks. */ if (len >= 128) { #if !_STRING_ARCH_unaligned # define alignof(type) offsetof (struct { char c; type x; }, x) # define UNALIGNED_P(p) (((size_t) p) % alignof (u64) != 0) if (UNALIGNED_P (buffer)) while (len > 128) { sha512_process_block (memcpy (ctx->buffer, buffer, 128), 128, ctx); buffer = (const char *) buffer + 128; len -= 128; } else #endif { sha512_process_block (buffer, len & ~127, ctx); buffer = (const char *) buffer + (len & ~127); len &= 127; } } /* Move remaining bytes in internal buffer. */ if (len > 0) { size_t left_over = ctx->buflen; memcpy (&((char *) ctx->buffer)[left_over], buffer, len); left_over += len; if (left_over >= 128) { sha512_process_block (ctx->buffer, 128, ctx); left_over -= 128; memcpy (ctx->buffer, &ctx->buffer[16], left_over); } ctx->buflen = left_over; } } /* --- Code below is the primary difference between sha1.c and sha512.c --- */ /* SHA512 round constants */ #define K(I) sha512_round_constants[I] static u64 const sha512_round_constants[80] = { u64init (0x428a2f98, 0xd728ae22), u64init (0x71374491, 0x23ef65cd), u64init (0xb5c0fbcf, 0xec4d3b2f), u64init (0xe9b5dba5, 0x8189dbbc), u64init (0x3956c25b, 0xf348b538), u64init (0x59f111f1, 0xb605d019), u64init (0x923f82a4, 0xaf194f9b), u64init (0xab1c5ed5, 0xda6d8118), u64init (0xd807aa98, 0xa3030242), u64init (0x12835b01, 0x45706fbe), u64init (0x243185be, 0x4ee4b28c), u64init (0x550c7dc3, 0xd5ffb4e2), u64init (0x72be5d74, 0xf27b896f), u64init (0x80deb1fe, 0x3b1696b1), u64init (0x9bdc06a7, 0x25c71235), u64init (0xc19bf174, 0xcf692694), u64init (0xe49b69c1, 0x9ef14ad2), u64init (0xefbe4786, 0x384f25e3), u64init (0x0fc19dc6, 0x8b8cd5b5), u64init (0x240ca1cc, 0x77ac9c65), u64init (0x2de92c6f, 0x592b0275), u64init (0x4a7484aa, 0x6ea6e483), u64init (0x5cb0a9dc, 0xbd41fbd4), u64init (0x76f988da, 0x831153b5), u64init (0x983e5152, 0xee66dfab), u64init (0xa831c66d, 0x2db43210), u64init (0xb00327c8, 0x98fb213f), u64init (0xbf597fc7, 0xbeef0ee4), u64init (0xc6e00bf3, 0x3da88fc2), u64init (0xd5a79147, 0x930aa725), u64init (0x06ca6351, 0xe003826f), u64init (0x14292967, 0x0a0e6e70), u64init (0x27b70a85, 0x46d22ffc), u64init (0x2e1b2138, 0x5c26c926), u64init (0x4d2c6dfc, 0x5ac42aed), u64init (0x53380d13, 0x9d95b3df), u64init (0x650a7354, 0x8baf63de), u64init (0x766a0abb, 0x3c77b2a8), u64init (0x81c2c92e, 0x47edaee6), u64init (0x92722c85, 0x1482353b), u64init (0xa2bfe8a1, 0x4cf10364), u64init (0xa81a664b, 0xbc423001), u64init (0xc24b8b70, 0xd0f89791), u64init (0xc76c51a3, 0x0654be30), u64init (0xd192e819, 0xd6ef5218), u64init (0xd6990624, 0x5565a910), u64init (0xf40e3585, 0x5771202a), u64init (0x106aa070, 0x32bbd1b8), u64init (0x19a4c116, 0xb8d2d0c8), u64init (0x1e376c08, 0x5141ab53), u64init (0x2748774c, 0xdf8eeb99), u64init (0x34b0bcb5, 0xe19b48a8), u64init (0x391c0cb3, 0xc5c95a63), u64init (0x4ed8aa4a, 0xe3418acb), u64init (0x5b9cca4f, 0x7763e373), u64init (0x682e6ff3, 0xd6b2b8a3), u64init (0x748f82ee, 0x5defb2fc), u64init (0x78a5636f, 0x43172f60), u64init (0x84c87814, 0xa1f0ab72), u64init (0x8cc70208, 0x1a6439ec), u64init (0x90befffa, 0x23631e28), u64init (0xa4506ceb, 0xde82bde9), u64init (0xbef9a3f7, 0xb2c67915), u64init (0xc67178f2, 0xe372532b), u64init (0xca273ece, 0xea26619c), u64init (0xd186b8c7, 0x21c0c207), u64init (0xeada7dd6, 0xcde0eb1e), u64init (0xf57d4f7f, 0xee6ed178), u64init (0x06f067aa, 0x72176fba), u64init (0x0a637dc5, 0xa2c898a6), u64init (0x113f9804, 0xbef90dae), u64init (0x1b710b35, 0x131c471b), u64init (0x28db77f5, 0x23047d84), u64init (0x32caab7b, 0x40c72493), u64init (0x3c9ebe0a, 0x15c9bebc), u64init (0x431d67c4, 0x9c100d4c), u64init (0x4cc5d4be, 0xcb3e42b6), u64init (0x597f299c, 0xfc657e2a), u64init (0x5fcb6fab, 0x3ad6faec), u64init (0x6c44198c, 0x4a475817), }; /* Round functions. */ #define F2(A, B, C) u64or (u64and (A, B), u64and (C, u64or (A, B))) #define F1(E, F, G) u64xor (G, u64and (E, u64xor (F, G))) /* Process LEN bytes of BUFFER, accumulating context into CTX. It is assumed that LEN % 128 == 0. Most of this code comes from GnuPG's cipher/sha1.c. */ void sha512_process_block (const void *buffer, size_t len, struct sha512_ctx *ctx) { u64 const *words = buffer; u64 const *endp = words + len / sizeof (u64); u64 x[16]; u64 a = ctx->state[0]; u64 b = ctx->state[1]; u64 c = ctx->state[2]; u64 d = ctx->state[3]; u64 e = ctx->state[4]; u64 f = ctx->state[5]; u64 g = ctx->state[6]; u64 h = ctx->state[7]; /* First increment the byte count. FIPS PUB 180-2 specifies the possible length of the file up to 2^128 bits. Here we only compute the number of bytes. Do a double word increment. */ ctx->total[0] = u64plus (ctx->total[0], u64lo (len)); if (u64lt (ctx->total[0], u64lo (len))) ctx->total[1] = u64plus (ctx->total[1], u64lo (1)); #define S0(x) u64xor (u64rol(x, 63), u64xor (u64rol (x, 56), u64shr (x, 7))) #define S1(x) u64xor (u64rol (x, 45), u64xor (u64rol (x, 3), u64shr (x, 6))) #define SS0(x) u64xor (u64rol (x, 36), u64xor (u64rol (x, 30), u64rol (x, 25))) #define SS1(x) u64xor (u64rol(x, 50), u64xor (u64rol (x, 46), u64rol (x, 23))) #define M(I) (x[(I) & 15] \ = u64plus (x[(I) & 15], \ u64plus (S1 (x[((I) - 2) & 15]), \ u64plus (x[((I) - 7) & 15], \ S0 (x[((I) - 15) & 15]))))) #define R(A, B, C, D, E, F, G, H, K, M) \ do \ { \ u64 t0 = u64plus (SS0 (A), F2 (A, B, C)); \ u64 t1 = \ u64plus (H, u64plus (SS1 (E), \ u64plus (F1 (E, F, G), u64plus (K, M)))); \ D = u64plus (D, t1); \ H = u64plus (t0, t1); \ } \ while (0) while (words < endp) { int t; /* FIXME: see sha1.c for a better implementation. */ for (t = 0; t < 16; t++) { x[t] = SWAP (*words); words++; } R( a, b, c, d, e, f, g, h, K( 0), x[ 0] ); R( h, a, b, c, d, e, f, g, K( 1), x[ 1] ); R( g, h, a, b, c, d, e, f, K( 2), x[ 2] ); R( f, g, h, a, b, c, d, e, K( 3), x[ 3] ); R( e, f, g, h, a, b, c, d, K( 4), x[ 4] ); R( d, e, f, g, h, a, b, c, K( 5), x[ 5] ); R( c, d, e, f, g, h, a, b, K( 6), x[ 6] ); R( b, c, d, e, f, g, h, a, K( 7), x[ 7] ); R( a, b, c, d, e, f, g, h, K( 8), x[ 8] ); R( h, a, b, c, d, e, f, g, K( 9), x[ 9] ); R( g, h, a, b, c, d, e, f, K(10), x[10] ); R( f, g, h, a, b, c, d, e, K(11), x[11] ); R( e, f, g, h, a, b, c, d, K(12), x[12] ); R( d, e, f, g, h, a, b, c, K(13), x[13] ); R( c, d, e, f, g, h, a, b, K(14), x[14] ); R( b, c, d, e, f, g, h, a, K(15), x[15] ); R( a, b, c, d, e, f, g, h, K(16), M(16) ); R( h, a, b, c, d, e, f, g, K(17), M(17) ); R( g, h, a, b, c, d, e, f, K(18), M(18) ); R( f, g, h, a, b, c, d, e, K(19), M(19) ); R( e, f, g, h, a, b, c, d, K(20), M(20) ); R( d, e, f, g, h, a, b, c, K(21), M(21) ); R( c, d, e, f, g, h, a, b, K(22), M(22) ); R( b, c, d, e, f, g, h, a, K(23), M(23) ); R( a, b, c, d, e, f, g, h, K(24), M(24) ); R( h, a, b, c, d, e, f, g, K(25), M(25) ); R( g, h, a, b, c, d, e, f, K(26), M(26) ); R( f, g, h, a, b, c, d, e, K(27), M(27) ); R( e, f, g, h, a, b, c, d, K(28), M(28) ); R( d, e, f, g, h, a, b, c, K(29), M(29) ); R( c, d, e, f, g, h, a, b, K(30), M(30) ); R( b, c, d, e, f, g, h, a, K(31), M(31) ); R( a, b, c, d, e, f, g, h, K(32), M(32) ); R( h, a, b, c, d, e, f, g, K(33), M(33) ); R( g, h, a, b, c, d, e, f, K(34), M(34) ); R( f, g, h, a, b, c, d, e, K(35), M(35) ); R( e, f, g, h, a, b, c, d, K(36), M(36) ); R( d, e, f, g, h, a, b, c, K(37), M(37) ); R( c, d, e, f, g, h, a, b, K(38), M(38) ); R( b, c, d, e, f, g, h, a, K(39), M(39) ); R( a, b, c, d, e, f, g, h, K(40), M(40) ); R( h, a, b, c, d, e, f, g, K(41), M(41) ); R( g, h, a, b, c, d, e, f, K(42), M(42) ); R( f, g, h, a, b, c, d, e, K(43), M(43) ); R( e, f, g, h, a, b, c, d, K(44), M(44) ); R( d, e, f, g, h, a, b, c, K(45), M(45) ); R( c, d, e, f, g, h, a, b, K(46), M(46) ); R( b, c, d, e, f, g, h, a, K(47), M(47) ); R( a, b, c, d, e, f, g, h, K(48), M(48) ); R( h, a, b, c, d, e, f, g, K(49), M(49) ); R( g, h, a, b, c, d, e, f, K(50), M(50) ); R( f, g, h, a, b, c, d, e, K(51), M(51) ); R( e, f, g, h, a, b, c, d, K(52), M(52) ); R( d, e, f, g, h, a, b, c, K(53), M(53) ); R( c, d, e, f, g, h, a, b, K(54), M(54) ); R( b, c, d, e, f, g, h, a, K(55), M(55) ); R( a, b, c, d, e, f, g, h, K(56), M(56) ); R( h, a, b, c, d, e, f, g, K(57), M(57) ); R( g, h, a, b, c, d, e, f, K(58), M(58) ); R( f, g, h, a, b, c, d, e, K(59), M(59) ); R( e, f, g, h, a, b, c, d, K(60), M(60) ); R( d, e, f, g, h, a, b, c, K(61), M(61) ); R( c, d, e, f, g, h, a, b, K(62), M(62) ); R( b, c, d, e, f, g, h, a, K(63), M(63) ); R( a, b, c, d, e, f, g, h, K(64), M(64) ); R( h, a, b, c, d, e, f, g, K(65), M(65) ); R( g, h, a, b, c, d, e, f, K(66), M(66) ); R( f, g, h, a, b, c, d, e, K(67), M(67) ); R( e, f, g, h, a, b, c, d, K(68), M(68) ); R( d, e, f, g, h, a, b, c, K(69), M(69) ); R( c, d, e, f, g, h, a, b, K(70), M(70) ); R( b, c, d, e, f, g, h, a, K(71), M(71) ); R( a, b, c, d, e, f, g, h, K(72), M(72) ); R( h, a, b, c, d, e, f, g, K(73), M(73) ); R( g, h, a, b, c, d, e, f, K(74), M(74) ); R( f, g, h, a, b, c, d, e, K(75), M(75) ); R( e, f, g, h, a, b, c, d, K(76), M(76) ); R( d, e, f, g, h, a, b, c, K(77), M(77) ); R( c, d, e, f, g, h, a, b, K(78), M(78) ); R( b, c, d, e, f, g, h, a, K(79), M(79) ); a = ctx->state[0] = u64plus (ctx->state[0], a); b = ctx->state[1] = u64plus (ctx->state[1], b); c = ctx->state[2] = u64plus (ctx->state[2], c); d = ctx->state[3] = u64plus (ctx->state[3], d); e = ctx->state[4] = u64plus (ctx->state[4], e); f = ctx->state[5] = u64plus (ctx->state[5], f); g = ctx->state[6] = u64plus (ctx->state[6], g); h = ctx->state[7] = u64plus (ctx->state[7], h); } } work/sha512.h0000664000000000000000000000707215063477206010124 0ustar /* Declarations of functions and data types used for SHA512 and SHA384 sum library functions. Copyright (C) 2005, 2006, 2008, 2009, 2010 Free Software Foundation, Inc. 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 . */ #ifndef SHA512_H # define SHA512_H 1 # include # include "u64.h" # ifdef __cplusplus extern "C" { # endif /* Structure to save state of computation between the single steps. */ struct sha512_ctx { u64 state[8]; u64 total[2]; size_t buflen; u64 buffer[32]; }; enum { SHA384_DIGEST_SIZE = 384 / 8 }; enum { SHA512_DIGEST_SIZE = 512 / 8 }; /* Initialize structure containing state of computation. */ extern void sha512_init_ctx (struct sha512_ctx *ctx); extern void sha384_init_ctx (struct sha512_ctx *ctx); /* Starting with the result of former calls of this function (or the initialization function update the context for the next LEN bytes starting at BUFFER. It is necessary that LEN is a multiple of 128!!! */ extern void sha512_process_block (const void *buffer, size_t len, struct sha512_ctx *ctx); /* Starting with the result of former calls of this function (or the initialization function update the context for the next LEN bytes starting at BUFFER. It is NOT required that LEN is a multiple of 128. */ extern void sha512_process_bytes (const void *buffer, size_t len, struct sha512_ctx *ctx); /* Process the remaining bytes in the buffer and put result from CTX in first 64 (48) bytes following RESBUF. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ extern void *sha512_finish_ctx (struct sha512_ctx *ctx, void *resbuf); extern void *sha384_finish_ctx (struct sha512_ctx *ctx, void *resbuf); /* Put result from CTX in first 64 (48) bytes following RESBUF. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. IMPORTANT: On some systems it is required that RESBUF is correctly aligned for a 32 bits value. */ extern void *sha512_read_ctx (const struct sha512_ctx *ctx, void *resbuf); extern void *sha384_read_ctx (const struct sha512_ctx *ctx, void *resbuf); /* Compute SHA512 (SHA384) message digest for bytes read from STREAM. The resulting message digest number will be written into the 64 (48) bytes beginning at RESBLOCK. */ extern int sha512_stream (FILE *stream, void *resblock); extern int sha384_stream (FILE *stream, void *resblock); /* Compute SHA512 (SHA384) message digest for LEN bytes beginning at BUFFER. The result is always in little endian byte order, so that a byte-wise output yields to the wanted ASCII representation of the message digest. */ extern void *sha512_buffer (const char *buffer, size_t len, void *resblock); extern void *sha384_buffer (const char *buffer, size_t len, void *resblock); # ifdef __cplusplus } # endif #endif work/site.c0000664000000000000000000026342315063477206010064 0ustar /* site.c - manage communication with a remote network site */ /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ /* The 'site' code doesn't know anything about the structure of the packets it's transmitting. In fact, under the new netlink configuration scheme it doesn't need to know anything at all about IP addresses, except how to contact its peer. This means it could potentially be used to tunnel other protocols too (IPv6, IPX, plain old Ethernet frames) if appropriate netlink code can be written (and that ought not to be too hard, eg. using the TUN/TAP device to pretend to be an Ethernet interface). */ /* At some point in the future the netlink code will be asked for configuration information to go in the PING/PONG packets at the end of the key exchange. */ #include "secnet.h" #include #include #include #include #include #include #include "util.h" #include "unaligned.h" #include "magic.h" #include "pubkeys.h" #define SETUP_BUFFER_LEN 2048 #define DEFAULT_KEY_LIFETIME (3600*1000) /* [ms] */ #define DEFAULT_KEY_RENEGOTIATE_GAP (5*60*1000) /* [ms] */ #define DEFAULT_SETUP_RETRIES 5 #define DEFAULT_SETUP_RETRY_INTERVAL (2*1000) /* [ms] */ #define DEFAULT_WAIT_TIME (20*1000) /* [ms] */ #define DEFAULT_MOBILE_KEY_LIFETIME (2*24*3600*1000) /* [ms] */ #define DEFAULT_MOBILE_KEY_RENEGOTIATE_GAP (12*3600*1000) /* [ms] */ #define DEFAULT_MOBILE_SETUP_RETRIES 30 #define DEFAULT_MOBILE_SETUP_RETRY_INTERVAL (1*1000) /* [ms] */ #define DEFAULT_MOBILE_WAIT_TIME (10*1000) /* [ms] */ #define DEFAULT_MOBILE_PEER_EXPIRY (2*60) /* [s] */ #define PEERKEYS_SUFFIX_MAXLEN (sizeof("~incoming")-1) /* Each site can be in one of several possible states. */ /* States: SITE_STOP - nothing is allowed to happen; tunnel is down; all session keys have been erased -> SITE_RUN upon external instruction SITE_RUN - site up, maybe with valid key -> SITE_RESOLVE upon outgoing packet and no valid key we start name resolution for the other end of the tunnel -> SITE_SENTMSG2 upon valid incoming message 1 and suitable time we send an appropriate message 2 SITE_RESOLVE - waiting for name resolution -> SITE_SENTMSG1 upon successful resolution we send an appropriate message 1 -> SITE_SENTMSG2 upon valid incoming message 1 (then abort resolution) we abort resolution and -> SITE_WAIT on timeout or resolution failure SITE_SENTMSG1 -> SITE_SENTMSG2 upon valid incoming message 1 from higher priority end -> SITE_SENTMSG3 upon valid incoming message 2 -> SITE_WAIT on timeout SITE_SENTMSG2 -> SITE_SENTMSG4 upon valid incoming message 3 -> SITE_WAIT on timeout SITE_SENTMSG3 -> SITE_SENTMSG5 upon valid incoming message 4 -> SITE_WAIT on timeout SITE_SENTMSG4 -> SITE_RUN upon valid incoming message 5 -> SITE_WAIT on timeout SITE_SENTMSG5 -> SITE_RUN upon valid incoming message 6 -> SITE_WAIT on timeout SITE_WAIT - failed to establish key; do nothing for a while -> SITE_RUN on timeout */ #define SITE_STOP 0 #define SITE_RUN 1 #define SITE_RESOLVE 2 #define SITE_SENTMSG1 3 #define SITE_SENTMSG2 4 #define SITE_SENTMSG3 5 #define SITE_SENTMSG4 6 #define SITE_SENTMSG5 7 #define SITE_WAIT 8 #define CASES_MSG3_KNOWN LABEL_MSG3: case LABEL_MSG3BIS struct msg; int32_t site_max_start_pad = 4*4; static cstring_t state_name(uint32_t state) { switch (state) { case 0: return "STOP"; case 1: return "RUN"; case 2: return "RESOLVE"; case 3: return "SENTMSG1"; case 4: return "SENTMSG2"; case 5: return "SENTMSG3"; case 6: return "SENTMSG4"; case 7: return "SENTMSG5"; case 8: return "WAIT"; default: return "*bad state*"; } } #define NONCELEN 8 #define LOG_UNEXPECTED 0x00000001 #define LOG_SETUP_INIT 0x00000002 #define LOG_SETUP_TIMEOUT 0x00000004 #define LOG_ACTIVATE_KEY 0x00000008 #define LOG_TIMEOUT_KEY 0x00000010 #define LOG_SEC 0x00000020 #define LOG_STATE 0x00000040 #define LOG_DROP 0x00000080 #define LOG_DUMP 0x00000100 #define LOG_ERROR 0x00000400 #define LOG_PEER_ADDRS 0x00000800 #define LOG_SIGKEYS 0x00001000 static struct flagstr log_event_table[]={ { "unexpected", LOG_UNEXPECTED }, { "setup-init", LOG_SETUP_INIT }, { "setup-timeout", LOG_SETUP_TIMEOUT }, { "activate-key", LOG_ACTIVATE_KEY }, { "timeout-key", LOG_TIMEOUT_KEY }, { "security", LOG_SEC }, { "state-change", LOG_STATE }, { "packet-drop", LOG_DROP }, { "dump-packets", LOG_DUMP }, { "errors", LOG_ERROR }, { "peer-addrs", LOG_PEER_ADDRS }, { "sigkeys", LOG_SIGKEYS }, { "default", LOG_SETUP_INIT|LOG_SETUP_TIMEOUT| LOG_ACTIVATE_KEY|LOG_TIMEOUT_KEY|LOG_SEC|LOG_ERROR|LOG_SIGKEYS }, { "all", 0xffffffff }, { NULL, 0 } }; /***** TRANSPORT PEERS declarations *****/ /* Details of "mobile peer" semantics: - We use the same data structure for the different configurations, but manage it with different algorithms. - We record up to mobile_peers_max peer address/port numbers ("peers") for key setup, and separately up to mobile_peers_max for data transfer. - In general, we make a new set of addrs (see below) when we start a new key exchange; the key setup addrs become the data transport addrs when key setup complets. If our peer is mobile: - We send to all recent addresses of incoming packets, plus initially all configured addresses (which we also expire). - So, we record addrs of good incoming packets, as follows: 1. expire any peers last seen >120s ("mobile-peer-expiry") ago 2. add the peer of the just received packet to the applicable list (possibly evicting the oldest entries to make room) NB that we do not expire peers until an incoming packet arrives. - If the peer has a configured address or name, we record them the same way, but only as a result of our own initiation of key setup. (We might evict some incoming packet addrs to make room.) - The default number of addrs to keep is 3, or 4 if we have a configured name or address. That's space for two configured addresses (one IPv6 and one IPv4), plus two received addresses. - Outgoing packets are sent to every recorded address in the applicable list. Any unsupported[1] addresses are deleted from the list right away. (This should only happen to configured addresses, of course, but there is no need to check that.) - When we successfully complete a key setup, we merge the key setup peers into the data transfer peers. [1] An unsupported address is one for whose AF we don't have a socket (perhaps because we got EAFNOSUPPORT or some such) or for which sendto gives ENETUNREACH. If neither end is mobile: - When peer initiated the key exchange, we use the incoming packet address. - When we initiate the key exchange, we try configured addresses until we get one which isn't unsupported then fixate on that. - When we complete a key setup, we replace the data transport peers with those from the key setup. If we are mobile: - We can't tell when local network setup changes so we can't cache the unsupported addrs and completely remove the spurious calls to sendto, but we can optimise things a bit by deprioritising addrs which seem to be unsupported. - Use only configured addresses. (Except, that if our peer initiated a key exchange we use the incoming packet address until our name resolution completes.) - When we send a packet, try each address in turn; if addr supported, put that address to the end of the list for future packets, and go onto the next address. - When we complete a key setup, we replace the data transport peers with those from the key setup. */ typedef struct { struct timeval last; struct comm_addr addr; } transport_peer; typedef struct { /* configuration information */ /* runtime information */ int npeers; transport_peer peers[MAX_PEER_ADDRS]; } transport_peers; /* Basic operations on transport peer address sets */ static void transport_peers_init(struct site *st, transport_peers *peers); static void transport_peers_clear(struct site *st, transport_peers *peers); static int transport_peers_valid(transport_peers *peers); static void transport_peers_copy(struct site *st, transport_peers *dst, const transport_peers *src); /* Record address of incoming setup packet; resp. data packet. */ static void transport_setup_msgok(struct site *st, const struct comm_addr *a); static void transport_data_msgok(struct site *st, const struct comm_addr *a); /* Initialise the setup addresses. Called before we send the first * packet in a key exchange. If we are the initiator, as a result of * resolve completing (or being determined not to be relevant) or an * incoming PROD; if we are the responder, as a result of the MSG1. */ static bool_t transport_compute_setupinit_peers(struct site *st, const struct comm_addr *configured_addrs /* 0 if none or not found */, int n_configured_addrs /* 0 if none or not found */, const struct comm_addr *incoming_packet_addr /* 0 if none */); /* Called if we are the responder in a key setup, when the resolve * completes. transport_compute_setupinit_peers will hvae been called * earlier. If _complete is called, we are still doing the key setup * (and we should use the new values for both the rest of the key * setup and the ongoing data exchange); if _tardy is called, the key * setup is done (either completed or not) and only the data peers are * relevant */ static void transport_resolve_complete(struct site *st, const struct comm_addr *addrs, int naddrs); static void transport_resolve_complete_tardy(struct site *st, const struct comm_addr *addrs, int naddrs); static void transport_xmit(struct site *st, transport_peers *peers, struct buffer_if *buf, bool_t candebug); /***** END of transport peers declarations *****/ struct data_key { struct transform_inst_if *transform; uint64_t key_timeout; /* End of life of current key */ uint32_t remote_session_id; }; struct site { closure_t cl; struct site_if ops; /* configuration information */ string_t localname; string_t remotename; bool_t keepalive; bool_t local_mobile, peer_mobile; /* Mobile client support */ int32_t transport_peers_max; string_t tunname; /* localname<->remotename by default, used in logs */ cstring_t *addresses; /* DNS name or address(es) for bootstrapping, optional */ int remoteport; /* Port for bootstrapping, optional */ uint32_t mtu_target; struct netlink_if *netlink; struct comm_if **comms; struct comm_clientinfo **commclientinfos; int ncomms; struct resolver_if *resolver; struct log_if *log; struct random_if *random; struct privcache_if *privkeys; struct sigprivkey_if *privkey_fixed; struct transform_if **transforms; int ntransforms; struct dh_if *dh; uint32_t index; /* Index of this site */ uint32_t early_capabilities; uint32_t local_capabilities; int32_t setup_retries; /* How many times to send setup packets */ int32_t setup_retry_interval; /* Initial timeout for setup packets */ int32_t wait_timeout_mean; /* How long to wait if setup unsuccessful */ int32_t mobile_peer_expiry; /* How long to remember 2ary addresses */ int32_t key_lifetime; /* How long a key lasts once set up */ int32_t key_renegotiate_time; /* If we see traffic (or a keepalive) after this time, initiate a new key exchange */ bool_t our_name_later; /* our name > peer name */ uint32_t log_events; /* runtime information */ uint32_t state; uint64_t now; /* Most recently seen time */ bool_t allow_send_prod; bool_t msg1_crossed_logged; int resolving_count; int resolving_n_results_all; int resolving_n_results_stored; struct comm_addr resolving_results[MAX_PEER_ADDRS]; const char *peerkeys_path; struct pathprefix_template peerkeys_tmpl; struct peer_keyset *peerkeys_current, *peerkeys_kex; /* The currently established session */ struct data_key current; struct data_key auxiliary_key; bool_t auxiliary_is_new; uint64_t renegotiate_key_time; /* When we can negotiate a new key */ uint64_t auxiliary_renegotiate_key_time; transport_peers peers; /* Current address(es) of peer for data traffic */ /* The current key setup protocol exchange. We can only be involved in one of these at a time. There's a potential for denial of service here (the attacker keeps sending a setup packet; we keep trying to continue the exchange, and have to timeout before we can listen for another setup packet); perhaps we should keep a list of 'bad' sources for setup packets. */ uint32_t remote_capabilities; uint16_t remote_adv_mtu; struct transform_if *chosen_transform; uint32_t setup_session_id; transport_peers setup_peers; uint8_t localN[NONCELEN]; /* Nonces for key exchange */ uint8_t remoteN[NONCELEN]; struct buffer_if buffer; /* Current outgoing key exchange packet */ struct buffer_if scratch; int32_t retries; /* Number of retries remaining */ uint64_t timeout; /* Timeout for current state */ uint8_t *dhsecret; uint8_t *sharedsecret; uint32_t sharedsecretlen, sharedsecretallocd; struct transform_inst_if *new_transform; /* For key setup/verify */ }; static uint32_t event_log_priority(struct site *st, uint32_t event) { if (!(event&st->log_events)) return 0; switch(event) { case LOG_UNEXPECTED: return M_INFO; case LOG_SETUP_INIT: return M_INFO; case LOG_SETUP_TIMEOUT: return M_NOTICE; case LOG_ACTIVATE_KEY: return M_INFO; case LOG_TIMEOUT_KEY: return M_INFO; case LOG_SEC: return M_SECURITY; case LOG_STATE: return M_DEBUG; case LOG_DROP: return M_DEBUG; case LOG_DUMP: return M_DEBUG; case LOG_ERROR: return M_ERR; case LOG_PEER_ADDRS: return M_DEBUG; case LOG_SIGKEYS: return M_INFO; default: return M_ERR; } } static uint32_t slog_start(struct site *st, uint32_t event) { uint32_t class=event_log_priority(st, event); if (class) { slilog_part(st->log,class,"%s: ",st->tunname); } return class; } static void vslog(struct site *st, uint32_t event, cstring_t msg, va_list ap) FORMAT(printf,3,0); static void vslog(struct site *st, uint32_t event, cstring_t msg, va_list ap) { uint32_t class; class=slog_start(st,event); if (class) { vslilog_part(st->log,class,msg,ap); slilog_part(st->log,class,"\n"); } } static void slog(struct site *st, uint32_t event, cstring_t msg, ...) FORMAT(printf,3,4); static void slog(struct site *st, uint32_t event, cstring_t msg, ...) { va_list ap; va_start(ap,msg); vslog(st,event,msg,ap); va_end(ap); } static void logtimeout(struct site *st, const char *fmt, ...) FORMAT(printf,2,3); static void logtimeout(struct site *st, const char *fmt, ...) { uint32_t class=event_log_priority(st,LOG_SETUP_TIMEOUT); if (!class) return; va_list ap; va_start(ap,fmt); slilog_part(st->log,class,"%s: ",st->tunname); vslilog_part(st->log,class,fmt,ap); const char *delim; int i; for (i=0, delim=" (tried "; isetup_peers.npeers; i++, delim=", ") { transport_peer *peer=&st->setup_peers.peers[i]; const char *s=comm_addr_to_string(&peer->addr); slilog_part(st->log,class,"%s%s",delim,s); } slilog_part(st->log,class,")\n"); va_end(ap); } static void set_link_quality(struct site *st); static void delete_keys(struct site *st, cstring_t reason, uint32_t loglevel); static void delete_one_key(struct site *st, struct data_key *key, const char *reason /* may be 0 meaning don't log*/, const char *which /* ignored if !reasonn */, uint32_t loglevel /* ignored if !reasonn */); static bool_t initiate_key_setup(struct site *st, cstring_t reason, const struct comm_addr *prod_hint); static void enter_state_run(struct site *st); static bool_t enter_state_resolve(struct site *st); static void decrement_resolving_count(struct site *st, int by); static bool_t enter_new_state(struct site *st,uint32_t next, const struct msg *prompt /* may be 0 for SENTMSG1 */); static void enter_state_wait(struct site *st); static void activate_new_key(struct site *st); static bool_t is_transform_valid(struct transform_inst_if *transform) { return transform && transform->valid(transform->st); } static bool_t current_valid(struct site *st) { return is_transform_valid(st->current.transform); } #define DEFINE_CALL_TRANSFORM(fwdrev) \ static transform_apply_return \ call_transform_##fwdrev(struct site *st, \ struct transform_inst_if *transform, \ struct buffer_if *buf, \ const char **errmsg) \ { \ if (!is_transform_valid(transform)) { \ *errmsg="transform not set up"; \ return transform_apply_err; \ } \ return transform->fwdrev(transform->st,buf,errmsg); \ } DEFINE_CALL_TRANSFORM(forwards) DEFINE_CALL_TRANSFORM(reverse) static void dispose_transform(struct transform_inst_if **transform_var) { struct transform_inst_if *transform=*transform_var; if (transform) { transform->delkey(transform->st); transform->destroy(transform->st); } *transform_var = 0; } #define CHECK_AVAIL(b,l) do { if ((b)->size<(l)) return False; } while(0) #define CHECK_EMPTY(b) do { if ((b)->size!=0) return False; } while(0) #define CHECK_TYPE(b,t) do { uint32_t type; \ CHECK_AVAIL((b),4); \ type=buf_unprepend_uint32((b)); \ if (type!=(t)) return False; } while(0) static _Bool type_is_msg23(uint32_t type) { switch (type) { case LABEL_MSG2: case CASES_MSG3_KNOWN: return True; default: return False; } } static _Bool type_is_msg34(uint32_t type) { switch (type) { case CASES_MSG3_KNOWN: case LABEL_MSG4: return True; default: return False; } } struct parsedname { int32_t len; uint8_t *name; struct buffer_if extrainfo; }; struct msg { uint8_t *hashstart; uint32_t dest; uint32_t source; struct parsedname remote; struct parsedname local; uint32_t remote_capabilities; uint16_t remote_mtu; int capab_transformnum; uint8_t *nR; uint8_t *nL; int32_t pklen; char *pk; int32_t hashlen; struct alg_msg_data sig; int n_pubkeys_accepted_nom; /* may be > MAX_SIG_KEYS ! */ const struct sigkeyid *pubkeys_accepted[MAX_SIG_KEYS]; int signing_key_index; }; static const struct sigkeyid keyid_zero; static int32_t wait_timeout(struct site *st) { int32_t t = st->wait_timeout_mean; int8_t factor; if (t < INT_MAX/2) { st->random->generate(st->random->st,sizeof(factor),&factor); t += (t / 256) * factor; } return t; } static _Bool set_new_transform(struct site *st, char *pk) { _Bool ok; /* Make room for the shared key */ st->sharedsecretlen=st->chosen_transform->keylen?:st->dh->ceil_len; assert(st->sharedsecretlen); if (st->sharedsecretlen > st->sharedsecretallocd) { st->sharedsecretallocd=st->sharedsecretlen; st->sharedsecret=safe_realloc_ary(st->sharedsecret,1, st->sharedsecretallocd, "site:sharedsecret"); } /* Generate the shared key */ st->dh->makeshared(st->dh->st,st->dhsecret,st->dh->len,pk, st->sharedsecret,st->sharedsecretlen); /* Set up the transform */ struct transform_if *generator=st->chosen_transform; struct transform_inst_if *generated=generator->create(generator->st); ok = generated->setkey(generated->st,st->sharedsecret, st->sharedsecretlen,st->our_name_later); dispose_transform(&st->new_transform); if (!ok) return False; st->new_transform=generated; slog(st,LOG_SETUP_INIT,"key exchange negotiated transform" " %d (capabilities ours=%#"PRIx32" theirs=%#"PRIx32")", st->chosen_transform->capab_bit, st->local_capabilities, st->remote_capabilities); return True; } struct xinfoadd { int32_t lenpos, afternul; }; static void append_string_xinfo_start(struct buffer_if *buf, struct xinfoadd *xia, const char *str) /* Helps construct one of the names with additional info as found * in MSG1..4. Call this function first, then append all the * desired extra info (not including the nul byte) to the buffer, * then call append_string_xinfo_done. */ { xia->lenpos = buf->size; buf_append_string(buf,str); buf_append_uint8(buf,0); xia->afternul = buf->size; } static void append_string_xinfo_done(struct buffer_if *buf, struct xinfoadd *xia) { /* we just need to adjust the string length */ if (buf->size == xia->afternul) { /* no extra info, strip the nul too */ buf_unappend_uint8(buf); } else { put_uint16(buf->start+xia->lenpos, buf->size-(xia->lenpos+2)); } } /* Build any of msg1 to msg4. msg5 and msg6 are built from the inside out using a transform of config data supplied by netlink */ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what, const struct msg *prompt /* may be 0 for MSG1 */) { string_t dhpub; unsigned minor; int ki; st->retries=st->setup_retries; BUF_ALLOC(&st->buffer,what); buffer_init(&st->buffer,0); buf_append_uint32(&st->buffer, (type==LABEL_MSG1?0:st->setup_session_id)); buf_append_uint32(&st->buffer,st->index); buf_append_uint32(&st->buffer,type); struct xinfoadd xia; append_string_xinfo_start(&st->buffer,&xia,st->localname); buf_append_uint32(&st->buffer,st->local_capabilities); if (type_is_msg34(type)) { buf_append_uint16(&st->buffer,st->mtu_target); } if (type_is_msg23(type)) { buf_append_uint8(&st->buffer,st->peerkeys_kex->nkeys); for (ki=0; kipeerkeys_kex->nkeys; ki++) { struct peer_pubkey *pk = &st->peerkeys_kex->keys[ki]; BUF_ADD_OBJ(append,&st->buffer,pk->id); } } struct sigprivkey_if *privkey=0; if (type_is_msg34(type)) { assert(prompt->n_pubkeys_accepted_nom>0); for (ki=0; kin_pubkeys_accepted_nom && kipubkeys_accepted[ki]; if (st->privkeys) { privkey=st->privkeys->lookup(st->privkeys->st,kid,st->log); if (privkey) goto privkey_found; } else { if (sigkeyid_equal(&keyid_zero,kid)) { privkey=st->privkey_fixed; goto privkey_found; } } } uint32_t class = slog_start(st,LOG_ERROR); if (class) { slilog_part(st->log,class,"no suitable private key, peer wanted"); for (ki=0; kin_pubkeys_accepted_nom && kilog,class, " " SIGKEYID_PR_FMT, SIGKEYID_PR_VAL(prompt->pubkeys_accepted[ki])); } if (prompt->n_pubkeys_accepted_nom > MAX_SIG_KEYS) slilog_part(st->log,class," +%d", prompt->n_pubkeys_accepted_nom - MAX_SIG_KEYS); slilog_part(st->log,class,"\n"); } return False; privkey_found: slog(st,LOG_SIGKEYS,"using private key #%d " SIGKEYID_PR_FMT, ki, SIGKEYID_PR_VAL(prompt->pubkeys_accepted[ki])); buf_append_uint8(&st->buffer,ki); } append_string_xinfo_done(&st->buffer,&xia); buf_append_string(&st->buffer,st->remotename); BUF_ADD_OBJ(append,&st->buffer,st->localN); if (type==LABEL_MSG1) return True; BUF_ADD_OBJ(append,&st->buffer,st->remoteN); if (type==LABEL_MSG2) return True; if (hacky_par_mid_failnow()) return False; if (MSGMAJOR(type) == 3) do { minor = MSGMINOR(type); if (minor < 1) break; buf_append_uint8(&st->buffer,st->chosen_transform->capab_bit); } while (0); dhpub=st->dh->makepublic(st->dh->st,st->dhsecret,st->dh->len); buf_append_string(&st->buffer,dhpub); free(dhpub); bool_t ok=privkey->sign(privkey->st, st->buffer.start, st->buffer.size, &st->buffer); if (!ok) goto fail; return True; fail: return False; } static bool_t unpick_name(struct buffer_if *msg, struct parsedname *nm) { CHECK_AVAIL(msg,2); nm->len=buf_unprepend_uint16(msg); CHECK_AVAIL(msg,nm->len); nm->name=buf_unprepend(msg,nm->len); uint8_t *nul=memchr(nm->name,0,nm->len); if (!nul) { buffer_readonly_view(&nm->extrainfo,0,0); } else { buffer_readonly_view(&nm->extrainfo, nul+1, msg->start-(nul+1)); nm->len=nul-nm->name; } return True; } static bool_t unpick_msg(struct site *st, uint32_t type, struct buffer_if *msg, struct msg *m) { unsigned minor; m->n_pubkeys_accepted_nom=-1; m->capab_transformnum=-1; m->signing_key_index=-1; m->hashstart=msg->start; CHECK_AVAIL(msg,4); m->dest=buf_unprepend_uint32(msg); CHECK_AVAIL(msg,4); m->source=buf_unprepend_uint32(msg); CHECK_TYPE(msg,type); if (!unpick_name(msg,&m->remote)) return False; m->remote_capabilities=0; m->remote_mtu=0; if (m->remote.extrainfo.size) { CHECK_AVAIL(&m->remote.extrainfo,4); m->remote_capabilities=buf_unprepend_uint32(&m->remote.extrainfo); } if (type_is_msg34(type) && m->remote.extrainfo.size) { CHECK_AVAIL(&m->remote.extrainfo,2); m->remote_mtu=buf_unprepend_uint16(&m->remote.extrainfo); } if (type_is_msg23(type) && m->remote.extrainfo.size) { m->n_pubkeys_accepted_nom = buf_unprepend_uint8(&m->remote.extrainfo); if (!m->n_pubkeys_accepted_nom) return False; for (int ki_nom=0; ki_nomn_pubkeys_accepted_nom; ki_nom++) { CHECK_AVAIL(&m->remote.extrainfo,KEYIDSZ); struct sigkeyid *kid = buf_unprepend(&m->remote.extrainfo,KEYIDSZ); if (ki_nompubkeys_accepted[ki_nom] = kid; } } else { m->n_pubkeys_accepted_nom = 1; m->pubkeys_accepted[0] = &keyid_zero; } if (type_is_msg34(type) && m->remote.extrainfo.size) { m->signing_key_index=buf_unprepend_uint8(&m->remote.extrainfo); } else { m->signing_key_index=0; } if (!unpick_name(msg,&m->local)) return False; if (type==LABEL_PROD) { CHECK_EMPTY(msg); return True; } CHECK_AVAIL(msg,NONCELEN); m->nR=buf_unprepend(msg,NONCELEN); if (type==LABEL_MSG1) { CHECK_EMPTY(msg); return True; } CHECK_AVAIL(msg,NONCELEN); m->nL=buf_unprepend(msg,NONCELEN); if (type==LABEL_MSG2) { CHECK_EMPTY(msg); return True; } if (MSGMAJOR(type) == 3) do { minor = MSGMINOR(type); #define MAYBE_READ_CAP(minminor, kind, dflt) do { \ if (minor < (minminor)) \ m->capab_##kind##num = (dflt); \ else { \ CHECK_AVAIL(msg, 1); \ m->capab_##kind##num = buf_unprepend_uint8(msg); \ } \ } while (0) MAYBE_READ_CAP(1, transform, CAPAB_BIT_ANCIENTTRANSFORM); #undef MAYBE_READ_CAP } while (0); CHECK_AVAIL(msg,2); m->pklen=buf_unprepend_uint16(msg); CHECK_AVAIL(msg,m->pklen); m->pk=buf_unprepend(msg,m->pklen); m->hashlen=msg->start-m->hashstart; if (m->signing_key_index < 0 || m->signing_key_index >= st->peerkeys_kex->nkeys) { return False; } struct sigpubkey_if *pubkey= st->peerkeys_kex->keys[m->signing_key_index].pubkey; if (!pubkey->unpick(pubkey->st,msg,&m->sig)) { return False; } CHECK_EMPTY(msg); return True; } static bool_t name_matches(const struct parsedname *nm, const char *expected) { int expected_len=strlen(expected); return nm->len == expected_len && !memcmp(nm->name, expected, expected_len); } static bool_t check_msg(struct site *st, uint32_t type, struct msg *m, cstring_t *error) { if (type==LABEL_MSG1) return True; /* Check that the site names and our nonce have been sent back correctly, and then store our peer's nonce. */ if (!name_matches(&m->remote,st->remotename)) { *error="wrong remote site name"; return False; } if (!name_matches(&m->local,st->localname)) { *error="wrong local site name"; return False; } if (memcmp(m->nL,st->localN,NONCELEN)!=0) { *error="wrong locally-generated nonce"; return False; } if (type==LABEL_MSG2) return True; if (!consttime_memeq(m->nR,st->remoteN,NONCELEN)) { *error="wrong remotely-generated nonce"; return False; } /* MSG3 has complicated rules about capabilities, which are * handled in process_msg3. */ if (MSGMAJOR(type) == 3) return True; if (m->remote_capabilities!=st->remote_capabilities) { *error="remote capabilities changed"; return False; } if (type==LABEL_MSG4) return True; *error="unknown message type"; return False; } static void peerkeys_maybe_incorporate(struct site *st, const char *file, const char *whatmore, int logcl_enoent) { struct peer_keyset *atsuffix= keyset_load(file,&st->scratch,st->log,logcl_enoent); if (!atsuffix) return; if (st->peerkeys_current && serial_cmp(atsuffix->serial,st->peerkeys_current->serial) <= 0) { slog(st,LOG_SIGKEYS,"keys from %s%s are older, discarding", file,whatmore); keyset_dispose(&atsuffix); int r=unlink(file); if (r) slog(st,LOG_ERROR,"failed to remove old key update %s: %s\n", st->peerkeys_tmpl.buffer,strerror(errno)); return; } else { slog(st,LOG_SIGKEYS,"keys from %s%s are newer, installing", file,whatmore); keyset_dispose(&st->peerkeys_current); st->peerkeys_current=atsuffix; int r=rename(file,st->peerkeys_path); if (r) slog(st,LOG_ERROR,"failed to install key update %s as %s: %s\n", st->peerkeys_tmpl.buffer,st->peerkeys_path, strerror(errno)); } } static void peerkeys_check_for_update(struct site *st) { if (!st->peerkeys_path) return; pathprefix_template_setsuffix(&st->peerkeys_tmpl,"~proc"); peerkeys_maybe_incorporate(st,st->peerkeys_tmpl.buffer, " (found old update)", M_DEBUG); pathprefix_template_setsuffix(&st->peerkeys_tmpl,"~update"); const char *inputp=st->peerkeys_tmpl.buffer; if (access(inputp,R_OK)) { if (errno!=ENOENT) slog(st,LOG_ERROR,"cannot access peer key update file %s\n", inputp); return; } buffer_init(&st->scratch,0); BUF_ADD_BYTES(append,&st->scratch, st->peerkeys_tmpl.buffer, strlen(st->peerkeys_tmpl.buffer)+1); inputp=st->scratch.start; pathprefix_template_setsuffix(&st->peerkeys_tmpl,"~proc"); const char *oursp=st->peerkeys_tmpl.buffer; int r=rename(inputp,oursp); if (r) { slog(st,LOG_ERROR,"failed to claim key update file %s as %s: %s", inputp,oursp,strerror(errno)); return; } peerkeys_maybe_incorporate(st,oursp," (update)",M_ERR); } static bool_t kex_init(struct site *st) { keyset_dispose(&st->peerkeys_kex); peerkeys_check_for_update(st); if (!st->peerkeys_current) { slog(st,LOG_SETUP_INIT,"no peer public keys, abandoning key setup"); return False; } st->peerkeys_kex = keyset_dup(st->peerkeys_current); st->random->generate(st->random->st,NONCELEN,st->localN); return True; } static bool_t generate_msg1(struct site *st, const struct msg *prompt_maybe_0) { return generate_msg(st,LABEL_MSG1,"site:MSG1",prompt_maybe_0); } static bool_t process_msg1(struct site *st, struct buffer_if *msg1, const struct comm_addr *src, const struct msg *m) { /* We've already determined we're in an appropriate state to process an incoming MSG1, and that the MSG1 has correct values of A and B. */ st->setup_session_id=m->source; st->remote_capabilities=m->remote_capabilities; memcpy(st->remoteN,m->nR,NONCELEN); return True; } static bool_t generate_msg2(struct site *st, const struct msg *prompt_may_be_null) { return generate_msg(st,LABEL_MSG2,"site:MSG2",prompt_may_be_null); } static bool_t process_msg2(struct site *st, struct buffer_if *msg2, const struct comm_addr *src, struct msg *m /* returned */) { cstring_t err; if (!unpick_msg(st,LABEL_MSG2,msg2,m)) return False; if (!check_msg(st,LABEL_MSG2,m,&err)) { slog(st,LOG_SEC,"msg2: %s",err); return False; } st->setup_session_id=m->source; st->remote_capabilities=m->remote_capabilities; /* Select the transform to use */ uint32_t remote_crypto_caps = st->remote_capabilities & CAPAB_TRANSFORM_MASK; if (!remote_crypto_caps) /* old secnets only had this one transform */ remote_crypto_caps = 1UL << CAPAB_BIT_ANCIENTTRANSFORM; #define CHOOSE_CRYPTO(kind, whats) do { \ struct kind##_if *iface; \ uint32_t bit, ours = 0; \ int i; \ for (i= 0; i < st->n##kind##s; i++) { \ iface=st->kind##s[i]; \ bit = 1UL << iface->capab_bit; \ if (bit & remote_crypto_caps) goto kind##_found; \ ours |= bit; \ } \ slog(st,LOG_ERROR,"no " whats " in common" \ " (us %#"PRIx32"; them: %#"PRIx32")", \ st->local_capabilities & ours, remote_crypto_caps); \ return False; \ kind##_found: \ st->chosen_##kind = iface; \ } while (0) CHOOSE_CRYPTO(transform, "transforms"); #undef CHOOSE_CRYPTO memcpy(st->remoteN,m->nR,NONCELEN); return True; } static bool_t generate_msg3(struct site *st, const struct msg *prompt) { /* Now we have our nonce and their nonce. Think of a secret key, and create message number 3. */ st->random->generate(st->random->st,st->dh->len,st->dhsecret); return generate_msg(st, (st->remote_capabilities & CAPAB_TRANSFORM_MASK) ? LABEL_MSG3BIS : LABEL_MSG3, "site:MSG3",prompt); } static bool_t process_msg3_msg4(struct site *st, struct msg *m) { /* Check signature and store g^x mod m */ int ki; if (m->signing_key_index >= 0) { if (m->signing_key_index >= st->peerkeys_kex->nkeys) return False; ki=m->signing_key_index; } else { for (ki=0; kipeerkeys_kex->nkeys; ki++) if (sigkeyid_equal(&keyid_zero,&st->peerkeys_kex->keys[ki].id)) goto found; /* not found */ slog(st,LOG_ERROR, "peer signed with keyid zero, which we do not accept"); return False; found:; } struct sigpubkey_if *pubkey=st->peerkeys_kex->keys[ki].pubkey; if (!pubkey->check(pubkey->st, m->hashstart,m->hashlen, &m->sig)) { slog(st,LOG_SEC,"msg3/msg4 signature failed check!" " (key #%d " SIGKEYID_PR_FMT ")", ki, SIGKEYID_PR_VAL(&st->peerkeys_kex->keys[ki].id)); return False; } slog(st,LOG_SIGKEYS,"verified peer signature with key #%d " SIGKEYID_PR_FMT, ki, SIGKEYID_PR_VAL(&st->peerkeys_kex->keys[ki].id)); st->remote_adv_mtu=m->remote_mtu; return True; } static bool_t process_msg3(struct site *st, struct buffer_if *msg3, const struct comm_addr *src, uint32_t msgtype, struct msg *m /* returned */) { cstring_t err; switch (msgtype) { case CASES_MSG3_KNOWN: break; default: assert(0); } if (!unpick_msg(st,msgtype,msg3,m)) return False; if (!check_msg(st,msgtype,m,&err)) { slog(st,LOG_SEC,"msg3: %s",err); return False; } uint32_t capab_adv_late = m->remote_capabilities & ~st->remote_capabilities & st->early_capabilities; if (capab_adv_late) { slog(st,LOG_SEC,"msg3 impermissibly adds early capability flag(s)" " %#"PRIx32" (was %#"PRIx32", now %#"PRIx32")", capab_adv_late, st->remote_capabilities, m->remote_capabilities); return False; } #define CHOSE_CRYPTO(kind, what) do { \ struct kind##_if *iface; \ int i; \ for (i=0; in##kind##s; i++) { \ iface=st->kind##s[i]; \ if (iface->capab_bit == m->capab_##kind##num) \ goto kind##_found; \ } \ slog(st,LOG_SEC,"peer chose unknown-to-us " what " %d!", \ m->capab_##kind##num); \ return False; \ kind##_found: \ st->chosen_##kind=iface; \ } while (0) CHOSE_CRYPTO(transform, "transform"); #undef CHOSE_CRYPTO if (!process_msg3_msg4(st,m)) return False; /* Update our idea of the remote site's capabilities, now that we've * verified that its message was authentic. * * Our previous idea of the remote site's capabilities came from the * unauthenticated MSG1. We've already checked that this new message * doesn't change any of the bits we relied upon in the past, but it may * also have set additional capability bits. We simply throw those away * now, and use the authentic capabilities from this MSG3. */ st->remote_capabilities=m->remote_capabilities; /* Terminate their DH public key with a '0' */ m->pk[m->pklen]=0; /* Invent our DH secret key */ st->random->generate(st->random->st,st->dh->len,st->dhsecret); /* Generate the shared key and set up the transform */ if (!set_new_transform(st,m->pk)) return False; return True; } static bool_t generate_msg4(struct site *st, const struct msg *prompt) { /* We have both nonces, their public key and our private key. Generate our public key, sign it and send it to them. */ return generate_msg(st,LABEL_MSG4,"site:MSG4",prompt); } static bool_t process_msg4(struct site *st, struct buffer_if *msg4, const struct comm_addr *src, struct msg *m /* returned */) { cstring_t err; if (!unpick_msg(st,LABEL_MSG4,msg4,m)) return False; if (!check_msg(st,LABEL_MSG4,m,&err)) { slog(st,LOG_SEC,"msg4: %s",err); return False; } if (!process_msg3_msg4(st,m)) return False; /* Terminate their DH public key with a '0' */ m->pk[m->pklen]=0; /* Generate the shared key and set up the transform */ if (!set_new_transform(st,m->pk)) return False; return True; } struct msg0 { uint32_t dest; uint32_t source; uint32_t type; }; static bool_t unpick_msg0(struct site *st, struct buffer_if *msg0, struct msg0 *m) { CHECK_AVAIL(msg0,4); m->dest=buf_unprepend_uint32(msg0); CHECK_AVAIL(msg0,4); m->source=buf_unprepend_uint32(msg0); CHECK_AVAIL(msg0,4); m->type=buf_unprepend_uint32(msg0); return True; /* Leaves transformed part of buffer untouched */ } static bool_t generate_msg5(struct site *st, const struct msg *prompt) { cstring_t transform_err; BUF_ALLOC(&st->buffer,"site:MSG5"); /* We are going to add four words to the message */ buffer_init(&st->buffer,calculate_max_start_pad()); /* Give the netlink code an opportunity to put its own stuff in the message (configuration information, etc.) */ buf_prepend_uint32(&st->buffer,LABEL_MSG5); if (call_transform_forwards(st,st->new_transform, &st->buffer,&transform_err)) return False; buf_prepend_uint32(&st->buffer,LABEL_MSG5); buf_prepend_uint32(&st->buffer,st->index); buf_prepend_uint32(&st->buffer,st->setup_session_id); st->retries=st->setup_retries; return True; } static bool_t process_msg5(struct site *st, struct buffer_if *msg5, const struct comm_addr *src, struct transform_inst_if *transform) { struct msg0 m; cstring_t transform_err; if (!unpick_msg0(st,msg5,&m)) return False; if (call_transform_reverse(st,transform,msg5,&transform_err)) { /* There's a problem */ slog(st,LOG_SEC,"process_msg5: transform: %s",transform_err); return False; } /* Buffer should now contain untransformed PING packet data */ CHECK_AVAIL(msg5,4); if (buf_unprepend_uint32(msg5)!=LABEL_MSG5) { slog(st,LOG_SEC,"MSG5/PING packet contained wrong label"); return False; } /* Older versions of secnet used to write some config data here * which we ignore. So we don't CHECK_EMPTY */ return True; } static void create_msg6(struct site *st, struct transform_inst_if *transform, uint32_t session_id) { cstring_t transform_err; BUF_ALLOC(&st->buffer,"site:MSG6"); /* We are going to add four words to the message */ buffer_init(&st->buffer,calculate_max_start_pad()); /* Give the netlink code an opportunity to put its own stuff in the message (configuration information, etc.) */ buf_prepend_uint32(&st->buffer,LABEL_MSG6); transform_apply_return problem = call_transform_forwards(st,transform, &st->buffer,&transform_err); assert(!problem); buf_prepend_uint32(&st->buffer,LABEL_MSG6); buf_prepend_uint32(&st->buffer,st->index); buf_prepend_uint32(&st->buffer,session_id); } static bool_t generate_msg6(struct site *st, const struct msg *prompt) { if (!is_transform_valid(st->new_transform)) return False; create_msg6(st,st->new_transform,st->setup_session_id); st->retries=1; /* Peer will retransmit MSG5 if this packet gets lost */ return True; } static bool_t process_msg6(struct site *st, struct buffer_if *msg6, const struct comm_addr *src) { struct msg0 m; cstring_t transform_err; if (!unpick_msg0(st,msg6,&m)) return False; if (call_transform_reverse(st,st->new_transform,msg6,&transform_err)) { /* There's a problem */ slog(st,LOG_SEC,"process_msg6: transform: %s",transform_err); return False; } /* Buffer should now contain untransformed PING packet data */ CHECK_AVAIL(msg6,4); if (buf_unprepend_uint32(msg6)!=LABEL_MSG6) { slog(st,LOG_SEC,"MSG6/PONG packet contained invalid data"); return False; } /* Older versions of secnet used to write some config data here * which we ignore. So we don't CHECK_EMPTY */ return True; } static transform_apply_return decrypt_msg0(struct site *st, struct buffer_if *msg0, const struct comm_addr *src) { cstring_t transform_err, auxkey_err, newkey_err="n/a"; struct msg0 m; transform_apply_return problem; if (!unpick_msg0(st,msg0,&m)) return False; /* Keep a copy so we can try decrypting it with multiple keys */ buffer_copy(&st->scratch, msg0); problem = call_transform_reverse(st,st->current.transform, msg0,&transform_err); if (!problem) { if (!st->auxiliary_is_new) delete_one_key(st,&st->auxiliary_key, "peer has used new key","auxiliary key",LOG_SEC); return 0; } if (transform_apply_return_badseq(problem)) goto badseq; buffer_copy(msg0, &st->scratch); problem = call_transform_reverse(st,st->auxiliary_key.transform, msg0,&auxkey_err); if (!problem) { slog(st,LOG_DROP,"processing packet which uses auxiliary key"); if (st->auxiliary_is_new) { /* We previously timed out in state SENTMSG5 but it turns * out that our peer did in fact get our MSG5 and is * using the new key. So we should switch to it too. */ /* This is a bit like activate_new_key. */ struct data_key t; t=st->current; st->current=st->auxiliary_key; st->auxiliary_key=t; delete_one_key(st,&st->auxiliary_key,"peer has used new key", "previous key",LOG_SEC); st->auxiliary_is_new=0; st->renegotiate_key_time=st->auxiliary_renegotiate_key_time; } return 0; } if (transform_apply_return_badseq(problem)) goto badseq; if (st->state==SITE_SENTMSG5) { buffer_copy(msg0, &st->scratch); problem = call_transform_reverse(st,st->new_transform, msg0,&newkey_err); if (!problem) { /* It looks like we didn't get the peer's MSG6 */ /* This is like a cut-down enter_new_state(SITE_RUN) */ slog(st,LOG_STATE,"will enter state RUN (MSG0 with new key)"); BUF_FREE(&st->buffer); st->timeout=0; activate_new_key(st); return 0; /* do process the data in this packet */ } if (transform_apply_return_badseq(problem)) goto badseq; } slog(st,LOG_SEC,"transform: %s (aux: %s, new: %s)", transform_err,auxkey_err,newkey_err); initiate_key_setup(st,"incoming message would not decrypt",0); send_nak(src,m.dest,m.source,m.type,msg0,"message would not decrypt"); assert(problem); return problem; badseq: slog(st,LOG_DROP,"transform: %s (bad seq.)",transform_err); assert(problem); return problem; } static bool_t process_msg0(struct site *st, struct buffer_if *msg0, const struct comm_addr *src) { uint32_t type; transform_apply_return problem; problem = decrypt_msg0(st,msg0,src); if (problem==transform_apply_seqdupe) { /* We recently received another copy of this packet, maybe due * to polypath. That's not a problem; indeed, for the * purposes of transport address management it is a success. * But we don't want to process the packet. */ transport_data_msgok(st,src); return False; } if (problem) return False; CHECK_AVAIL(msg0,4); type=buf_unprepend_uint32(msg0); switch(type) { case LABEL_MSG7: /* We must forget about the current session. */ delete_keys(st,"request from peer",LOG_SEC); /* probably, the peer is shutting down, and this is going to fail, * but we need to be trying to bring the link up again */ if (st->keepalive) initiate_key_setup(st,"peer requested key teardown",0); return True; case LABEL_MSG9: /* Deliver to netlink layer */ st->netlink->deliver(st->netlink->st,msg0); transport_data_msgok(st,src); /* See whether we should start negotiating a new key */ if (st->now > st->renegotiate_key_time) initiate_key_setup(st,"incoming packet in renegotiation window",0); return True; default: slog(st,LOG_SEC,"incoming encrypted message of type %08x " "(unknown)",type); break; } return False; } static void dump_packet(struct site *st, struct buffer_if *buf, const struct comm_addr *addr, bool_t incoming, bool_t ok) { uint32_t dest=get_uint32(buf->start); uint32_t source=get_uint32(buf->start+4); uint32_t msgtype=get_uint32(buf->start+8); if (st->log_events & LOG_DUMP) slilog(st->log,M_DEBUG,"%s: %s: %08x<-%08x: %08x: %s%s", st->tunname,incoming?"incoming":"outgoing", dest,source,msgtype,comm_addr_to_string(addr), ok?"":" - fail"); } static bool_t comm_addr_sendmsg(struct site *st, const struct comm_addr *dest, struct buffer_if *buf) { int i; struct comm_clientinfo *commclientinfo = 0; for (i=0; i < st->ncomms; i++) { if (st->comms[i] == dest->comm) { commclientinfo = st->commclientinfos[i]; break; } } return dest->comm->sendmsg(dest->comm->st, buf, dest, commclientinfo); } static uint32_t site_status(void *st) { return 0; } static bool_t send_msg(struct site *st) { if (st->retries>0) { transport_xmit(st, &st->setup_peers, &st->buffer, True); st->timeout=st->now+st->setup_retry_interval; st->retries--; return True; } else if (st->state==SITE_SENTMSG5) { logtimeout(st,"timed out sending MSG5, stashing new key"); /* We stash the key we have produced, in case it turns out that * our peer did see our MSG5 after all and starts using it. */ /* This is a bit like some of activate_new_key */ struct transform_inst_if *t; t=st->auxiliary_key.transform; st->auxiliary_key.transform=st->new_transform; st->new_transform=t; dispose_transform(&st->new_transform); st->auxiliary_is_new=1; st->auxiliary_key.key_timeout=st->now+st->key_lifetime; st->auxiliary_renegotiate_key_time=st->now+st->key_renegotiate_time; st->auxiliary_key.remote_session_id=st->setup_session_id; enter_state_wait(st); return False; } else { logtimeout(st,"timed out sending key setup packet " "(in state %s)",state_name(st->state)); enter_state_wait(st); return False; } } static void site_resolve_callback(void *sst, const struct comm_addr *addrs, int stored_naddrs, int all_naddrs, const char *address, const char *failwhy) { struct site *st=sst; if (!stored_naddrs) { slog(st,LOG_ERROR,"resolution of %s failed: %s",address,failwhy); } else { slog(st,LOG_PEER_ADDRS,"resolution of %s completed, %d addrs, eg: %s", address, all_naddrs, comm_addr_to_string(&addrs[0]));; int space=st->transport_peers_max-st->resolving_n_results_stored; int n_tocopy=MIN(stored_naddrs,space); COPY_ARRAY(st->resolving_results + st->resolving_n_results_stored, addrs, n_tocopy); st->resolving_n_results_stored += n_tocopy; st->resolving_n_results_all += all_naddrs; } decrement_resolving_count(st,1); } static void decrement_resolving_count(struct site *st, int by) { assert(st->resolving_count>0); st->resolving_count-=by; if (st->resolving_count) return; /* OK, we are done with them all. Handle combined results. */ const struct comm_addr *addrs=st->resolving_results; int naddrs=st->resolving_n_results_stored; assert(naddrs<=st->transport_peers_max); if (naddrs) { if (naddrs != st->resolving_n_results_all) { slog(st,LOG_SETUP_INIT,"resolution of supplied addresses/names" " yielded too many results (%d > %d), some ignored", st->resolving_n_results_all, naddrs); } slog(st,LOG_STATE,"resolution completed, %d addrs, eg: %s", naddrs, iaddr_to_string(&addrs[0].ia));; } switch (st->state) { case SITE_RESOLVE: if (transport_compute_setupinit_peers(st,addrs,naddrs,0)) { enter_new_state(st,SITE_SENTMSG1,0); } else { /* Can't figure out who to try to to talk to */ slog(st,LOG_SETUP_INIT, "key exchange failed: cannot find peer address"); enter_state_run(st); } break; case SITE_SENTMSG1: case SITE_SENTMSG2: case SITE_SENTMSG3: case SITE_SENTMSG4: case SITE_SENTMSG5: if (naddrs) { /* We start using the address immediately for data too. * It's best to store it in st->peers now because we might * go via SENTMSG5, WAIT, and a MSG0, straight into using * the new key (without updating the data peer addrs). */ transport_resolve_complete(st,addrs,naddrs); } else if (st->local_mobile) { /* We can't let this rest because we may have a peer * address which will break in the future. */ slog(st,LOG_SETUP_INIT,"resolution failed: " "abandoning key exchange"); enter_state_wait(st); } else { slog(st,LOG_SETUP_INIT,"resolution failed: " " continuing to use source address of peer's packets" " for key exchange and ultimately data"); } break; case SITE_RUN: if (naddrs) { slog(st,LOG_SETUP_INIT,"resolution completed tardily," " updating peer address(es)"); transport_resolve_complete_tardy(st,addrs,naddrs); } else if (st->local_mobile) { /* Not very good. We should queue (another) renegotiation * so that we can update the peer address. */ st->key_renegotiate_time=st->now+wait_timeout(st); } else { slog(st,LOG_SETUP_INIT,"resolution failed: " " continuing to use source address of peer's packets"); } break; case SITE_WAIT: case SITE_STOP: /* oh well */ break; } } static bool_t initiate_key_setup(struct site *st, cstring_t reason, const struct comm_addr *prod_hint) { /* Reentrancy hazard: can call enter_new_state/enter_state_* */ if (st->state!=SITE_RUN) return False; slog(st,LOG_SETUP_INIT,"initiating key exchange (%s)",reason); if (st->addresses) { slog(st,LOG_SETUP_INIT,"resolving peer address(es)"); return enter_state_resolve(st); } else if (transport_compute_setupinit_peers(st,0,0,prod_hint)) { return enter_new_state(st,SITE_SENTMSG1,0); } slog(st,LOG_SETUP_INIT,"key exchange failed: no address for peer"); return False; } static void activate_new_key(struct site *st) { struct transform_inst_if *t; /* We have three transform instances, which we swap between old, active and setup */ t=st->auxiliary_key.transform; st->auxiliary_key.transform=st->current.transform; st->current.transform=st->new_transform; st->new_transform=t; dispose_transform(&st->new_transform); st->timeout=0; st->auxiliary_is_new=0; st->auxiliary_key.key_timeout=st->current.key_timeout; st->current.key_timeout=st->now+st->key_lifetime; st->renegotiate_key_time=st->now+st->key_renegotiate_time; transport_peers_copy(st,&st->peers,&st->setup_peers); st->current.remote_session_id=st->setup_session_id; /* Compute the inter-site MTU. This is min( our_mtu, their_mtu ). * But their mtu be unspecified, in which case we just use ours. */ uint32_t intersite_mtu= MIN(st->mtu_target, st->remote_adv_mtu ?: ~(uint32_t)0); st->netlink->set_mtu(st->netlink->st,intersite_mtu); slog(st,LOG_ACTIVATE_KEY,"new key activated" " (mtu ours=%"PRId32" theirs=%"PRId32" intersite=%"PRId32")", st->mtu_target, st->remote_adv_mtu, intersite_mtu); enter_state_run(st); } static void delete_one_key(struct site *st, struct data_key *key, cstring_t reason, cstring_t which, uint32_t loglevel) { if (!is_transform_valid(key->transform)) return; if (reason) slog(st,loglevel,"%s deleted (%s)",which,reason); dispose_transform(&key->transform); key->key_timeout=0; } static void delete_keys(struct site *st, cstring_t reason, uint32_t loglevel) { if (current_valid(st)) { slog(st,loglevel,"session closed (%s)",reason); delete_one_key(st,&st->current,0,0,0); set_link_quality(st); } delete_one_key(st,&st->auxiliary_key,0,0,0); } static void state_assert(struct site *st, bool_t ok) { if (!ok) fatal("site:state_assert"); } static void enter_state_stop(struct site *st) { st->state=SITE_STOP; st->timeout=0; delete_keys(st,"entering state STOP",LOG_TIMEOUT_KEY); dispose_transform(&st->new_transform); } static void set_link_quality(struct site *st) { uint32_t quality; if (current_valid(st)) quality=LINK_QUALITY_UP; else if (st->state==SITE_WAIT || st->state==SITE_STOP) quality=LINK_QUALITY_DOWN; else if (st->addresses) quality=LINK_QUALITY_DOWN_CURRENT_ADDRESS; else if (transport_peers_valid(&st->peers)) quality=LINK_QUALITY_DOWN_STALE_ADDRESS; else quality=LINK_QUALITY_DOWN; st->netlink->set_quality(st->netlink->st,quality); } static void enter_state_run(struct site *st) { if (st->state!=SITE_STOP) slog(st,LOG_STATE,"entering state RUN%s", current_valid(st) ? " (keyed)" : " (unkeyed)"); st->state=SITE_RUN; st->timeout=0; st->setup_session_id=0; transport_peers_clear(st,&st->setup_peers); keyset_dispose(&st->peerkeys_kex); FILLZERO(st->localN); FILLZERO(st->remoteN); dispose_transform(&st->new_transform); memset(st->dhsecret,0,st->dh->len); if (st->sharedsecret) memset(st->sharedsecret,0,st->sharedsecretlen); set_link_quality(st); if (st->keepalive && !current_valid(st)) initiate_key_setup(st, "keepalive", 0); } static bool_t ensure_resolving(struct site *st) { /* Reentrancy hazard: may call site_resolve_callback and hence * enter_new_state, enter_state_* and generate_msg*. */ if (st->resolving_count) return True; assert(st->addresses); /* resolver->request might reentrantly call site_resolve_callback * which will decrement st->resolving, so we need to increment it * twice beforehand to prevent decrement from thinking we're * finished, and decrement it ourselves. Alternatively if * everything fails then there are no callbacks due and we simply * set it to 0 and return false.. */ st->resolving_n_results_stored=0; st->resolving_n_results_all=0; st->resolving_count+=2; const char **addrp=st->addresses; const char *address; bool_t anyok=False; for (; (address=*addrp++); ) { bool_t ok = st->resolver->request(st->resolver->st,address, st->remoteport,st->comms[0], site_resolve_callback,st); if (ok) st->resolving_count++; anyok|=ok; } if (!anyok) { st->resolving_count=0; return False; } decrement_resolving_count(st,2); return True; } static bool_t enter_state_resolve(struct site *st) { /* Reentrancy hazard! See ensure_resolving. */ state_assert(st,st->state==SITE_RUN); slog(st,LOG_STATE,"entering state RESOLVE"); st->state=SITE_RESOLVE; return ensure_resolving(st); } static bool_t enter_new_state(struct site *st, uint32_t next, const struct msg *prompt /* may be 0 for SENTMSG1 */) { bool_t (*gen)(struct site *st, const struct msg *prompt); int r; slog(st,LOG_STATE,"entering state %s",state_name(next)); switch(next) { case SITE_SENTMSG1: state_assert(st,st->state==SITE_RUN || st->state==SITE_RESOLVE); if (!kex_init(st)) return False; gen=generate_msg1; st->msg1_crossed_logged = False; break; case SITE_SENTMSG2: state_assert(st,st->state==SITE_RUN || st->state==SITE_RESOLVE || st->state==SITE_SENTMSG1 || st->state==SITE_WAIT); if (!kex_init(st)) return False; gen=generate_msg2; break; case SITE_SENTMSG3: state_assert(st,st->state==SITE_SENTMSG1); BUF_FREE(&st->buffer); gen=generate_msg3; break; case SITE_SENTMSG4: state_assert(st,st->state==SITE_SENTMSG2); BUF_FREE(&st->buffer); gen=generate_msg4; break; case SITE_SENTMSG5: state_assert(st,st->state==SITE_SENTMSG3); BUF_FREE(&st->buffer); gen=generate_msg5; break; case SITE_RUN: state_assert(st,st->state==SITE_SENTMSG4); BUF_FREE(&st->buffer); gen=generate_msg6; break; default: gen=NULL; fatal("enter_new_state(%s): invalid new state",state_name(next)); break; } if (hacky_par_start_failnow()) return False; r= gen(st,prompt) && send_msg(st); hacky_par_end(&r, st->setup_retries, st->setup_retry_interval, send_msg, st); if (r) { st->state=next; if (next==SITE_RUN) { BUF_FREE(&st->buffer); /* Never reused */ st->timeout=0; /* Never retransmit */ activate_new_key(st); } return True; } slog(st,LOG_ERROR,"error entering state %s",state_name(next)); st->buffer.free=False; /* Unconditionally use the buffer; it may be in either state, and enter_state_wait() will do a BUF_FREE() */ enter_state_wait(st); return False; } /* msg7 tells our peer that we're about to forget our key */ static bool_t send_msg7(struct site *st, cstring_t reason) { cstring_t transform_err; if (current_valid(st) && st->buffer.free && transport_peers_valid(&st->peers)) { BUF_ALLOC(&st->buffer,"site:MSG7"); buffer_init(&st->buffer,calculate_max_start_pad()); buf_append_uint32(&st->buffer,LABEL_MSG7); buf_append_string(&st->buffer,reason); if (call_transform_forwards(st, st->current.transform, &st->buffer, &transform_err)) goto free_out; buf_prepend_uint32(&st->buffer,LABEL_MSG0); buf_prepend_uint32(&st->buffer,st->index); buf_prepend_uint32(&st->buffer,st->current.remote_session_id); transport_xmit(st,&st->peers,&st->buffer,True); BUF_FREE(&st->buffer); free_out: return True; } return False; } /* We go into this state if our peer becomes uncommunicative. Similar to the "stop" state, we forget all session keys for a while, before re-entering the "run" state. */ static void enter_state_wait(struct site *st) { slog(st,LOG_STATE,"entering state WAIT"); st->timeout=st->now+wait_timeout(st); st->state=SITE_WAIT; set_link_quality(st); BUF_FREE(&st->buffer); /* will have had an outgoing packet in it */ /* XXX Erase keys etc. */ } static void generate_prod(struct site *st, struct buffer_if *buf) { buffer_init(buf,0); buf_append_uint32(buf,0); buf_append_uint32(buf,0); buf_append_uint32(buf,LABEL_PROD); buf_append_string(buf,st->localname); buf_append_string(buf,st->remotename); } static void generate_send_prod(struct site *st, const struct comm_addr *source) { if (!st->allow_send_prod) return; /* too soon */ if (!(st->state==SITE_RUN || st->state==SITE_RESOLVE || st->state==SITE_WAIT)) return; /* we'd ignore peer's MSG1 */ slog(st,LOG_SETUP_INIT,"prodding peer for key exchange"); st->allow_send_prod=0; generate_prod(st,&st->scratch); bool_t ok = comm_addr_sendmsg(st, source, &st->scratch); dump_packet(st,&st->scratch,source,False,ok); } static inline void site_settimeout(uint64_t timeout, int *timeout_io) { if (timeout) { int64_t offset=timeout-*now; if (offset<0) offset=0; if (offset>INT_MAX) offset=INT_MAX; if (*timeout_io<0 || offset<*timeout_io) *timeout_io=offset; } } static int site_beforepoll(void *sst, struct pollfd *fds, int *nfds_io, int *timeout_io) { struct site *st=sst; BEFOREPOLL_WANT_FDS(0); /* We don't use any file descriptors */ st->now=*now; /* Work out when our next timeout is. The earlier of 'timeout' or 'current.key_timeout'. A stored value of '0' indicates no timeout active. */ site_settimeout(st->timeout, timeout_io); site_settimeout(st->current.key_timeout, timeout_io); site_settimeout(st->auxiliary_key.key_timeout, timeout_io); return 0; /* success */ } static void check_expiry(struct site *st, struct data_key *key, const char *which) { if (key->key_timeout && *now>key->key_timeout) { delete_one_key(st,key,"maximum life exceeded",which,LOG_TIMEOUT_KEY); } } /* NB site_afterpoll will be called before site_beforepoll is ever called */ static void site_afterpoll(void *sst, struct pollfd *fds, int nfds) { struct site *st=sst; st->now=*now; if (st->timeout && *now>st->timeout) { st->timeout=0; if (st->state>=SITE_SENTMSG1 && st->state<=SITE_SENTMSG5) { if (!hacky_par_start_failnow()) send_msg(st); } else if (st->state==SITE_WAIT) { enter_state_run(st); } else { slog(st,LOG_ERROR,"site_afterpoll: unexpected timeout, state=%d", st->state); } } check_expiry(st,&st->current,"current key"); check_expiry(st,&st->auxiliary_key,"auxiliary key"); } /* This function is called by the netlink device to deliver packets intended for the remote network. The packet is in "raw" wire format, but is guaranteed to be word-aligned. */ static void site_outgoing(void *sst, struct buffer_if *buf) { struct site *st=sst; cstring_t transform_err; if (st->state==SITE_STOP) { BUF_FREE(buf); return; } st->allow_send_prod=1; /* In all other states we consider delivering the packet if we have a valid key and a valid address to send it to. */ if (current_valid(st) && transport_peers_valid(&st->peers)) { /* Transform it and send it */ if (buf->size>0) { buf_prepend_uint32(buf,LABEL_MSG9); if (call_transform_forwards(st, st->current.transform, buf, &transform_err)) goto free_out; buf_prepend_uint32(buf,LABEL_MSG0); buf_prepend_uint32(buf,st->index); buf_prepend_uint32(buf,st->current.remote_session_id); transport_xmit(st,&st->peers,buf,False); } free_out: BUF_FREE(buf); return; } slog(st,LOG_DROP,"discarding outgoing packet of size %d",buf->size); BUF_FREE(buf); initiate_key_setup(st,"outgoing packet",0); } static bool_t named_for_us(struct site *st, const struct buffer_if *buf_in, uint32_t type, struct msg *m, struct priomsg *whynot) /* For packets which are identified by the local and remote names. * If it has our name and our peer's name in it it's for us. */ { struct buffer_if buf[1]; buffer_readonly_clone(buf,buf_in); if (!unpick_msg(st,type,buf,m)) { priomsg_update_fixed(whynot, comm_notify_whynot_unpick, "malformed"); return False; } #define NAME_MATCHES(lr) \ if (!name_matches(&m->lr, st->lr##name)) { \ if (priomsg_update_fixed(whynot, comm_notify_whynot_name_##lr, \ "unknown " #lr " name: ")) { \ truncmsg_add_packet_string(&whynot->m, m->lr.len, m->lr.name); \ } \ return False; \ } NAME_MATCHES(remote); NAME_MATCHES(local ); #undef NAME_MATCHES return True; } static bool_t we_have_priority(struct site *st, const struct msg *m) { if (st->local_capabilities & m->remote_capabilities & CAPAB_PRIORITY_MOBILE) { if (st->local_mobile) return True; if (st-> peer_mobile) return False; } return st->our_name_later; } static bool_t setup_late_msg_ok(struct site *st, const struct buffer_if *buf_in, uint32_t msgtype, const struct comm_addr *source, struct msg *m /* returned */) { /* For setup packets which seem from their type like they are * late. Maybe they came via a different path. All we do is make * a note of the sending address, iff they look like they are part * of the current key setup attempt. */ if (!named_for_us(st,buf_in,msgtype,m,0)) /* named_for_us calls unpick_msg which gets the nonces */ return False; if (!consttime_memeq(m->nR,st->remoteN,NONCELEN) || !consttime_memeq(m->nL,st->localN, NONCELEN)) /* spoof ? from stale run ? who knows */ return False; transport_setup_msgok(st,source); return True; } /* This function is called by the communication device to deliver packets from our peers. It should return True if the packet is recognised as being for this current site instance (and should therefore not be processed by other sites), even if the packet was otherwise ignored. */ static bool_t site_incoming(void *sst, struct buffer_if *buf, const struct comm_addr *source, struct priomsg *whynot) { struct site *st=sst; if (buf->size < 12) return False; uint32_t dest=get_uint32(buf->start); uint32_t msgtype=get_uint32(buf->start+8); struct msg msg; /* initialised by named_for_us, or process_msgN for N!=1 */ if (msgtype==LABEL_MSG1) { if (!named_for_us(st,buf,msgtype,&msg,whynot)) return False; /* It's a MSG1 addressed to us. Decide what to do about it. */ dump_packet(st,buf,source,True,True); if (st->state==SITE_RUN || st->state==SITE_RESOLVE || st->state==SITE_WAIT) { /* We should definitely process it */ transport_compute_setupinit_peers(st,0,0,source); if (process_msg1(st,buf,source,&msg)) { slog(st,LOG_SETUP_INIT,"key setup initiated by peer"); bool_t entered=enter_new_state(st,SITE_SENTMSG2,&msg); if (entered && st->addresses && st->local_mobile) /* We must do this as the very last thing, because the resolver callback might reenter us. */ ensure_resolving(st); } else { slog(st,LOG_ERROR,"failed to process incoming msg1"); } BUF_FREE(buf); return True; } else if (st->state==SITE_SENTMSG1) { /* We've just sent a message 1! They may have crossed on the wire. If we have priority then we ignore the incoming one, otherwise we process it as usual. */ if (we_have_priority(st,&msg)) { BUF_FREE(buf); if (!st->msg1_crossed_logged++) slog(st,LOG_SETUP_INIT,"crossed msg1s; we are higher " "priority => ignore incoming msg1"); return True; } else { slog(st,LOG_SETUP_INIT,"crossed msg1s; we are lower " "priority => use incoming msg1"); if (process_msg1(st,buf,source,&msg)) { BUF_FREE(&st->buffer); /* Free our old message 1 */ transport_setup_msgok(st,source); enter_new_state(st,SITE_SENTMSG2,&msg); } else { slog(st,LOG_ERROR,"failed to process an incoming " "crossed msg1 (we have low priority)"); } BUF_FREE(buf); return True; } } else if (st->state==SITE_SENTMSG2 || st->state==SITE_SENTMSG4) { if (consttime_memeq(msg.nR,st->remoteN,NONCELEN)) { /* We are ahead in the protocol, but that msg1 had the * peer's nonce so presumably it is from this key * exchange run, via a slower route */ transport_setup_msgok(st,source); } else { slog(st,LOG_UNEXPECTED,"competing incoming message 1"); } BUF_FREE(buf); return True; } /* The message 1 was received at an unexpected stage of the key setup. Well, they lost the race. */ slog(st,LOG_UNEXPECTED,"unexpected incoming message 1"); BUF_FREE(buf); return True; } if (msgtype==LABEL_PROD) { if (!named_for_us(st,buf,msgtype,&msg,whynot)) return False; dump_packet(st,buf,source,True,True); if (st->state!=SITE_RUN) { slog(st,LOG_DROP,"ignoring PROD when not in state RUN"); } else if (current_valid(st)) { slog(st,LOG_DROP,"ignoring PROD when we think we have a key"); } else { initiate_key_setup(st,"peer sent PROD packet",source); } BUF_FREE(buf); return True; } if (dest==st->index) { /* Explicitly addressed to us */ if (msgtype!=LABEL_MSG0) dump_packet(st,buf,source,True,True); switch (msgtype) { case LABEL_NAK: /* If the source is our current peer then initiate a key setup, because our peer's forgotten the key */ if (get_uint32(buf->start+4)==st->current.remote_session_id) { bool_t initiated; initiated = initiate_key_setup(st,"received a NAK",source); if (!initiated) generate_send_prod(st,source); } else { slog(st,LOG_SEC,"bad incoming NAK"); } break; case LABEL_MSG0: process_msg0(st,buf,source); break; case LABEL_MSG1: /* Setup packet: should not have been explicitly addressed to us */ slog(st,LOG_SEC,"incoming explicitly addressed msg1"); break; case LABEL_MSG2: /* Setup packet: expected only in state SENTMSG1 */ if (st->state!=SITE_SENTMSG1) { if ((st->state==SITE_SENTMSG3 || st->state==SITE_SENTMSG5) && setup_late_msg_ok(st,buf,msgtype,source,&msg)) break; slog(st,LOG_UNEXPECTED,"unexpected MSG2"); } else if (process_msg2(st,buf,source,&msg)) { transport_setup_msgok(st,source); enter_new_state(st,SITE_SENTMSG3,&msg); } else { slog(st,LOG_SEC,"invalid MSG2"); } break; case CASES_MSG3_KNOWN: /* Setup packet: expected only in state SENTMSG2 */ if (st->state!=SITE_SENTMSG2) { if ((st->state==SITE_SENTMSG4) && setup_late_msg_ok(st,buf,msgtype,source,&msg)) break; slog(st,LOG_UNEXPECTED,"unexpected MSG3"); } else if (process_msg3(st,buf,source,msgtype,&msg)) { transport_setup_msgok(st,source); enter_new_state(st,SITE_SENTMSG4,&msg); } else { slog(st,LOG_SEC,"invalid MSG3"); } break; case LABEL_MSG4: /* Setup packet: expected only in state SENTMSG3 */ if (st->state!=SITE_SENTMSG3) { if ((st->state==SITE_SENTMSG5) && setup_late_msg_ok(st,buf,msgtype,source,&msg)) break; slog(st,LOG_UNEXPECTED,"unexpected MSG4"); } else if (process_msg4(st,buf,source,&msg)) { transport_setup_msgok(st,source); enter_new_state(st,SITE_SENTMSG5,&msg); } else { slog(st,LOG_SEC,"invalid MSG4"); } break; case LABEL_MSG5: /* Setup packet: expected only in state SENTMSG4 */ /* (may turn up in state RUN if our return MSG6 was lost and the new key has already been activated. In that case we discard it. The peer will realise that we are using the new key when they see our data packets. Until then the peer's data packets to us get discarded. */ if (st->state==SITE_SENTMSG4) { if (process_msg5(st,buf,source,st->new_transform)) { transport_setup_msgok(st,source); enter_new_state(st,SITE_RUN,&msg); } else { slog(st,LOG_SEC,"invalid MSG5"); } } else if (st->state==SITE_RUN) { if (process_msg5(st,buf,source,st->current.transform)) { slog(st,LOG_DROP,"got MSG5, retransmitting MSG6"); transport_setup_msgok(st,source); create_msg6(st,st->current.transform, st->current.remote_session_id); transport_xmit(st,&st->peers,&st->buffer,True); BUF_FREE(&st->buffer); } else { slog(st,LOG_SEC,"invalid MSG5 (in state RUN)"); } } else { slog(st,LOG_UNEXPECTED,"unexpected MSG5"); } break; case LABEL_MSG6: /* Setup packet: expected only in state SENTMSG5 */ if (st->state!=SITE_SENTMSG5) { slog(st,LOG_UNEXPECTED,"unexpected MSG6"); } else if (process_msg6(st,buf,source)) { BUF_FREE(&st->buffer); /* Free message 5 */ transport_setup_msgok(st,source); activate_new_key(st); } else { slog(st,LOG_SEC,"invalid MSG6"); } break; default: slog(st,LOG_SEC,"received message of unknown type 0x%08x", msgtype); break; } BUF_FREE(buf); return True; } priomsg_update_fixed(whynot, comm_notify_whynot_general, "not MSG1 or PROD; unknown dest index"); return False; } static void site_startup(void *vst) { struct site *st=vst; enter_state_run(st); } static void site_phase_shutdown_hook(void *sst, uint32_t newphase) { struct site *st=sst; /* The program is shutting down; tell our peer */ send_msg7(st,"shutting down"); } static void site_phase_run_hook(void *sst, uint32_t newphase) { struct site *st=sst; slog(st,LOG_STATE,"entering phase RUN in state %s", state_name(st->state)); } static void site_childpersist_clearkeys(void *sst, uint32_t newphase) { struct site *st=sst; dispose_transform(&st->current.transform); dispose_transform(&st->auxiliary_key.transform); dispose_transform(&st->new_transform); /* Not much point overwiting the signing key, since we loaded it from disk, and it is only valid prospectively if at all, anyway. */ /* XXX it would be best to overwrite the DH state, because that _is_ relevant to forward secrecy. However we have no convenient interface for doing that and in practice gmp has probably dribbled droppings all over the malloc arena. A good way to fix this would be to have a privsep child for asymmetric crypto operations, but that's a task for another day. */ } static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { static uint32_t index_sequence; struct site *st; item_t *item; dict_t *dict; int i; NEW(st); st->cl.description="site"; st->cl.type=CL_SITE; st->cl.apply=NULL; st->cl.interface=&st->ops; st->ops.st=st; st->ops.startup=site_startup; st->ops.status=site_status; st->peerkeys_path=0; st->peerkeys_tmpl.buffer=0; st->peerkeys_current=st->peerkeys_kex=0; buffer_new(&st->scratch,SETUP_BUFFER_LEN); BUF_ALLOC(&st->scratch,"site:scratch"); /* First parameter must be a dict */ item=list_elem(args,0); if (!item || item->type!=t_dict) cfgfatal(loc,"site","parameter must be a dictionary\n"); dict=item->data.dict; st->log=find_cl_if(dict,"log",CL_LOG,True,"site",loc); st->log_events=string_list_to_word(dict_lookup(dict,"log-events"), log_event_table,"site"); st->localname=dict_read_string(dict, "local-name", True, "site", loc); st->remotename=dict_read_string(dict, "name", True, "site", loc); st->tunname=safe_malloc(strlen(st->localname)+strlen(st->remotename)+5, "site_apply"); sprintf(st->tunname,"%s<->%s",st->localname,st->remotename); /* Now slog is working */ st->keepalive=dict_read_bool(dict,"keepalive",False,"site",loc,False); st->peer_mobile=dict_read_bool(dict,"mobile",False,"site",loc,False); st->local_mobile= dict_read_bool(dict,"local-mobile",False,"site",loc,False); /* Sanity check (which also allows the 'sites' file to include site() closures for all sites including our own): refuse to talk to ourselves */ if (strcmp(st->localname,st->remotename)==0) { Message(M_DEBUG,"site %s: local-name==name -> ignoring this site\n", st->localname); if (st->peer_mobile != st->local_mobile) cfgfatal(loc,"site","site %s's peer-mobile=%d" " but our local-mobile=%d\n", st->localname, st->peer_mobile, st->local_mobile); free(st); return NULL; } if (st->peer_mobile && st->local_mobile) { Message(M_WARNING,"site %s: site is mobile but so are we" " -> ignoring this site\n", st->remotename); free(st); return NULL; } assert(index_sequence < 0xffffffffUL); st->index = ++index_sequence; st->local_capabilities = 0; st->early_capabilities = CAPAB_PRIORITY_MOBILE; st->netlink=find_cl_if(dict,"link",CL_NETLINK,True,"site",loc); #define GET_CLOSURE_LIST(dictkey,things,nthings,CL_TYPE) do{ \ list_t *things##_cfg=dict_lookup(dict,dictkey); \ if (!things##_cfg) \ cfgfatal(loc,"site","closure list \"%s\" not found\n",dictkey); \ st->nthings=list_length(things##_cfg); \ NEW_ARY(st->things,st->nthings); \ assert(st->nthings); \ for (i=0; inthings; i++) { \ item_t *item=list_elem(things##_cfg,i); \ if (item->type!=t_closure) \ cfgfatal(loc,"site","%s is not a closure\n",dictkey); \ closure_t *cl=item->data.closure; \ if (cl->type!=CL_TYPE) \ cfgfatal_cl_type(loc,"site",cl,CL_TYPE,dictkey); \ st->things[i]=cl->interface; \ } \ }while(0) GET_CLOSURE_LIST("comm",comms,ncomms,CL_COMM); NEW_ARY(st->commclientinfos, st->ncomms); dict_t *comminfo = dict_read_dict(dict,"comm-info",False,"site",loc); for (i=0; incomms; i++) { st->commclientinfos[i] = !comminfo ? 0 : st->comms[i]->clientinfo(st->comms[i],comminfo,loc); } st->resolver=find_cl_if(dict,"resolver",CL_RESOLVER,True,"site",loc); st->random=find_cl_if(dict,"random",CL_RANDOMSRC,True,"site",loc); st->privkeys=find_cl_if(dict,"key-cache",CL_PRIVCACHE,False,"site",loc); if (!st->privkeys) { st->privkey_fixed= find_cl_if(dict,"local-key",CL_SIGPRIVKEY,True,"site",loc); } struct sigpubkey_if *fixed_pubkey =find_cl_if(dict,"key",CL_SIGPUBKEY,False,"site",loc); st->peerkeys_path=dict_read_string(dict,"peer-keys",fixed_pubkey==0, "site",loc); if (st->peerkeys_path) { pathprefix_template_init(&st->peerkeys_tmpl,st->peerkeys_path, PEERKEYS_SUFFIX_MAXLEN + 1 /* nul */); st->peerkeys_current=keyset_load(st->peerkeys_path, &st->scratch,st->log,M_ERR); if (fixed_pubkey) { fixed_pubkey->dispose(fixed_pubkey->st); } } else { assert(fixed_pubkey); NEW(st->peerkeys_current); st->peerkeys_current->refcount=1; st->peerkeys_current->nkeys=1; st->peerkeys_current->keys[0].id=keyid_zero; st->peerkeys_current->keys[0].pubkey=fixed_pubkey; slog(st,LOG_SIGKEYS, "using old-style fixed peer public key (no `peer-keys')"); } st->addresses=dict_read_string_array(dict,"address",False,"site",loc,0); if (st->addresses) st->remoteport=dict_read_number(dict,"port",True,"site",loc,0); else st->remoteport=0; GET_CLOSURE_LIST("transform",transforms,ntransforms,CL_TRANSFORM); st->dh=find_cl_if(dict,"dh",CL_DH,True,"site",loc); #define DEFAULT(D) (st->peer_mobile || st->local_mobile \ ? DEFAULT_MOBILE_##D : DEFAULT_##D) #define CFG_NUMBER(k,D) dict_read_number(dict,(k),False,"site",loc,DEFAULT(D)); st->key_lifetime= CFG_NUMBER("key-lifetime", KEY_LIFETIME); st->setup_retries= CFG_NUMBER("setup-retries", SETUP_RETRIES); st->setup_retry_interval= CFG_NUMBER("setup-timeout", SETUP_RETRY_INTERVAL); st->wait_timeout_mean= CFG_NUMBER("wait-time", WAIT_TIME); st->mtu_target= dict_read_number(dict,"mtu-target",False,"site",loc,0); st->mobile_peer_expiry= dict_read_number( dict,"mobile-peer-expiry",False,"site",loc,DEFAULT_MOBILE_PEER_EXPIRY); const char *peerskey= st->peer_mobile ? "mobile-peers-max" : "static-peers-max"; st->transport_peers_max= dict_read_number( dict,peerskey,False,"site",loc, st->addresses ? 4 : 3); if (st->transport_peers_max<1 || st->transport_peers_max>MAX_PEER_ADDRS) { cfgfatal(loc,"site", "%s must be in range 1.." STRING(MAX_PEER_ADDRS) "\n", peerskey); } if (st->key_lifetime < DEFAULT(KEY_RENEGOTIATE_GAP)*2) st->key_renegotiate_time=st->key_lifetime/2; else st->key_renegotiate_time=st->key_lifetime-DEFAULT(KEY_RENEGOTIATE_GAP); st->key_renegotiate_time=dict_read_number( dict,"renegotiate-time",False,"site",loc,st->key_renegotiate_time); if (st->key_renegotiate_time > st->key_lifetime) { cfgfatal(loc,"site", "renegotiate-time must be less than key-lifetime\n"); } st->resolving_count=0; st->allow_send_prod=0; /* The information we expect to see in incoming messages of type 1 */ /* fixme: lots of unchecked overflows here, but the results are only corrupted packets rather than undefined behaviour */ st->our_name_later=(strcmp(st->localname,st->remotename)>0); buffer_new(&st->buffer,SETUP_BUFFER_LEN); /* We are interested in poll(), but only for timeouts. We don't have any fds of our own. */ register_for_poll(st, site_beforepoll, site_afterpoll, "site"); st->timeout=0; st->remote_capabilities=0; st->chosen_transform=0; st->current.key_timeout=0; st->auxiliary_key.key_timeout=0; transport_peers_init(st,&st->peers); transport_peers_init(st,&st->setup_peers); /* XXX mlock these */ st->dhsecret=safe_malloc(st->dh->len,"site:dhsecret"); st->sharedsecretlen=st->sharedsecretallocd=0; st->sharedsecret=0; #define SET_CAPBIT(bit) do { \ uint32_t capflag = 1UL << (bit); \ if (st->local_capabilities & capflag) \ slog(st,LOG_ERROR,"capability bit" \ " %d (%#"PRIx32") reused", (bit), capflag); \ st->local_capabilities |= capflag; \ } while (0) for (i=0; intransforms; i++) SET_CAPBIT(st->transforms[i]->capab_bit); #undef SET_CAPBIT if (st->local_mobile || st->peer_mobile) st->local_capabilities |= CAPAB_PRIORITY_MOBILE; /* We need to register the remote networks with the netlink device */ uint32_t netlink_mtu; /* local virtual interface mtu */ st->netlink->reg(st->netlink->st, site_outgoing, st, &netlink_mtu); if (!st->mtu_target) st->mtu_target=netlink_mtu; for (i=0; incomms; i++) st->comms[i]->request_notify(st->comms[i]->st, st, site_incoming); st->current.transform=0; st->auxiliary_key.transform=0; st->new_transform=0; st->auxiliary_is_new=0; enter_state_stop(st); add_hook(PHASE_SHUTDOWN,site_phase_shutdown_hook,st); add_hook(PHASE_RUN, site_phase_run_hook, st); add_hook(PHASE_CHILDPERSIST,site_childpersist_clearkeys,st); return new_closure(&st->cl); } void site_module(dict_t *dict) { add_closure(dict,"site",site_apply); } /***** TRANSPORT PEERS definitions *****/ static void transport_peers_debug(struct site *st, transport_peers *dst, const char *didwhat, int nargs, const struct comm_addr *args, size_t stride) { int i; char *argp; if (!(st->log_events & LOG_PEER_ADDRS)) return; /* an optimisation */ slog(st, LOG_PEER_ADDRS, "peers (%s) %s nargs=%d => npeers=%d", (dst==&st->peers ? "data" : dst==&st->setup_peers ? "setup" : "UNKNOWN"), didwhat, nargs, dst->npeers); for (i=0, argp=(void*)args; inpeers; i++) { struct timeval diff; timersub(tv_now,&dst->peers[i].last,&diff); const struct comm_addr *ca=&dst->peers[i].addr; slog(st, LOG_PEER_ADDRS, " peers: addrs[%d]=%s T-%ld.%06ld", i, comm_addr_to_string(ca), (unsigned long)diff.tv_sec, (unsigned long)diff.tv_usec); } } static void transport_peers_expire(struct site *st, transport_peers *peers) { /* peers must be sorted first */ if (st->local_mobile) return; int previous_peers=peers->npeers; struct timeval oldest; oldest.tv_sec = tv_now->tv_sec - st->mobile_peer_expiry; oldest.tv_usec = tv_now->tv_usec; while (peers->npeers>1 && timercmp(&peers->peers[peers->npeers-1].last, &oldest, <)) peers->npeers--; if (peers->npeers != previous_peers) transport_peers_debug(st,peers,"expire", 0,0,0); } static bool_t transport_peer_record_one(struct site *st, transport_peers *peers, const struct comm_addr *ca, const struct timeval *tv) { /* returns false if output is full */ int search; if (peers->npeers >= st->transport_peers_max) return 0; for (search=0; searchnpeers; search++) if (comm_addr_equal(&peers->peers[search].addr, ca)) return 1; peers->peers[peers->npeers].addr = *ca; peers->peers[peers->npeers].last = *tv; peers->npeers++; return 1; } static void transport_record_peers(struct site *st, transport_peers *peers, const struct comm_addr *addrs, int naddrs, const char *m) { /* We add addrs into peers. The new entries end up at the front * and displace entries towards the end (perhaps even off the * end). Any existing matching entries are moved up to the front. * * Caller must first call transport_peers_expire. */ if (naddrs==1) { /* avoids debug for uninteresting updates */ int i; for (i=0; inpeers; i++) { if (comm_addr_equal(&addrs[0], &peers->peers[i].addr)) { memmove(peers->peers+1, peers->peers, sizeof(peers->peers[0]) * i); peers->peers[0].addr = addrs[0]; peers->peers[0].last = *tv_now; return; } } } int old_npeers=peers->npeers; transport_peer old_peers[old_npeers]; COPY_ARRAY(old_peers,peers->peers,old_npeers); peers->npeers=0; int i; for (i=0; iaddr, &old->last)) break; } transport_peers_debug(st,peers,m, naddrs,addrs,0); } static void transport_expire_record_peers(struct site *st, transport_peers *peers, const struct comm_addr *addrs, int naddrs, const char *m) { /* Convenience function */ transport_peers_expire(st,peers); transport_record_peers(st,peers,addrs,naddrs,m); } static bool_t transport_compute_setupinit_peers(struct site *st, const struct comm_addr *configured_addrs /* 0 if none or not found */, int n_configured_addrs /* 0 if none or not found */, const struct comm_addr *incoming_packet_addr /* 0 if none */) { if (!n_configured_addrs && !incoming_packet_addr && !transport_peers_valid(&st->peers)) return False; slog(st,LOG_SETUP_INIT, "using: %d configured addr(s);%s %d old peer addrs(es)", n_configured_addrs, incoming_packet_addr ? " incoming packet address;" : "", st->peers.npeers); /* Non-mobile peers try addresses until one is plausible. The * effect is that this code always tries first the configured * address if supplied, or otherwise the address of the incoming * PROD, or finally the existing data peer if one exists; this is * as desired. */ transport_peers_copy(st,&st->setup_peers,&st->peers); transport_peers_expire(st,&st->setup_peers); if (incoming_packet_addr) transport_record_peers(st,&st->setup_peers, incoming_packet_addr,1, "incoming"); if (n_configured_addrs) transport_record_peers(st,&st->setup_peers, configured_addrs,n_configured_addrs, "setupinit"); assert(transport_peers_valid(&st->setup_peers)); return True; } static void transport_setup_msgok(struct site *st, const struct comm_addr *a) { if (st->peer_mobile) transport_expire_record_peers(st,&st->setup_peers,a,1,"setupmsg"); } static void transport_data_msgok(struct site *st, const struct comm_addr *a) { if (st->peer_mobile) transport_expire_record_peers(st,&st->peers,a,1,"datamsg"); } static int transport_peers_valid(transport_peers *peers) { return peers->npeers; } static void transport_peers_init(struct site *st, transport_peers *peers) { peers->npeers= 0; } static void transport_peers_clear(struct site *st, transport_peers *peers) { bool_t need_debug=!!peers->npeers; peers->npeers= 0; if (need_debug) transport_peers_debug(st,peers,"clear",0,0,0); } static void transport_peers_copy(struct site *st, transport_peers *dst, const transport_peers *src) { dst->npeers=src->npeers; COPY_ARRAY(dst->peers, src->peers, dst->npeers); transport_peers_debug(st,dst,"copy", src->npeers, &src->peers->addr, sizeof(*src->peers)); } static void transport_resolve_complete(struct site *st, const struct comm_addr *addrs, int naddrs) { transport_expire_record_peers(st,&st->peers,addrs,naddrs, "resolved data"); transport_expire_record_peers(st,&st->setup_peers,addrs,naddrs, "resolved setup"); } static void transport_resolve_complete_tardy(struct site *st, const struct comm_addr *addrs, int naddrs) { transport_expire_record_peers(st,&st->peers,addrs,naddrs, "resolved tardily"); } static void transport_peers__copy_by_mask(transport_peer *out, int *nout_io, unsigned mask, const transport_peers *inp) { /* out and in->peers may be the same region, or nonoverlapping */ const transport_peer *in=inp->peers; int slot; for (slot=0; slotnpeers; slot++) { if (!(mask & (1U << slot))) continue; if (!(out==in && slot==*nout_io)) COPY_OBJ(out[*nout_io], in[slot]); (*nout_io)++; } } void transport_xmit(struct site *st, transport_peers *peers, struct buffer_if *buf, bool_t candebug) { int slot; transport_peers_expire(st, peers); unsigned failed=0; /* bitmask */ assert(MAX_PEER_ADDRS < sizeof(unsigned)*CHAR_BIT); int nfailed=0; for (slot=0; slotnpeers; slot++) { transport_peer *peer=&peers->peers[slot]; bool_t ok = comm_addr_sendmsg(st, &peer->addr, buf); if (candebug) dump_packet(st, buf, &peer->addr, False, ok); if (!ok) { failed |= 1U << slot; nfailed++; } if (ok && !st->peer_mobile) break; } /* Now we need to demote/delete failing addrs: if we are mobile we * merely demote them; otherwise we delete them. */ if (st->local_mobile) { unsigned expected = ((1U << nfailed)-1) << (peers->npeers-nfailed); /* `expected' has all the failures at the end already */ if (failed != expected) { int fslot=0; transport_peer failedpeers[nfailed]; transport_peers__copy_by_mask(failedpeers, &fslot, failed,peers); assert(fslot == nfailed); int wslot=0; transport_peers__copy_by_mask(peers->peers,&wslot,~failed,peers); assert(wslot+nfailed == peers->npeers); COPY_ARRAY(peers->peers+wslot, failedpeers, nfailed); transport_peers_debug(st,peers,"mobile failure reorder",0,0,0); } } else { if (failed && peers->npeers > 1) { int wslot=0; transport_peers__copy_by_mask(peers->peers,&wslot,~failed,peers); peers->npeers=wslot; transport_peers_debug(st,peers,"non-mobile failure cleanup",0,0,0); } } } /***** END of transport peers declarations *****/ work/slip.c0000664000000000000000000002766615063477206010076 0ustar /* When dealing with SLIP (to a pty, or ipif) we have separate rx, tx and client buffers. When receiving we may read() any amount, not just whole packets. When transmitting we need to bytestuff anyway, and may be part-way through receiving. */ /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include "secnet.h" #include "util.h" #include "netlink.h" #include "process.h" #include "unaligned.h" #include #include #include #include #include #define SLIP_END 192 #define SLIP_ESC 219 #define SLIP_ESCEND 220 #define SLIP_ESCESC 221 struct slip { struct netlink nl; struct buffer_if *buff; /* We unstuff received packets into here and send them to the netlink code. */ bool_t pending_esc; bool_t ignoring_packet; /* If this packet was corrupt or overlong, we ignore everything up to the next END */ netlink_deliver_fn *netlink_to_tunnel; }; /* Generic SLIP mangling code */ static void slip_write(int fd, const uint8_t *p, size_t l) { while (l) { ssize_t written=write(fd,p,l); if (written<0) { if (errno==EINTR) { continue; } else if (iswouldblock(errno)) { lg_perror(0,"slip",0,M_ERR,errno,"write() (packet(s) lost)"); return; } else { fatal_perror("slip_stuff: write()"); } } assert(written>0); assert((size_t)written<=l); p+=written; l-=written; } } static void slip_stuff(struct slip *st, struct buffer_if *buf, int fd) { uint8_t txbuf[DEFAULT_BUFSIZE]; uint8_t *i; int32_t j=0; BUF_ASSERT_USED(buf); /* There's probably a much more efficient way of implementing this */ txbuf[j++]=SLIP_END; for (i=buf->start; i<(buf->start+buf->size); i++) { switch (*i) { case SLIP_END: txbuf[j++]=SLIP_ESC; txbuf[j++]=SLIP_ESCEND; break; case SLIP_ESC: txbuf[j++]=SLIP_ESC; txbuf[j++]=SLIP_ESCESC; break; default: txbuf[j++]=*i; break; } if ((j+2)>DEFAULT_BUFSIZE) { slip_write(fd,txbuf,j); j=0; } } txbuf[j++]=SLIP_END; slip_write(fd,txbuf,j); BUF_FREE(buf); } static void slip_unstuff(struct slip *st, uint8_t *buf, uint32_t l) { uint32_t i; BUF_ASSERT_USED(st->buff); for (i=0; ibuff->size) buffer_init(st->buff,calculate_max_start_pad()); if (st->pending_esc) { st->pending_esc=False; switch(buf[i]) { case SLIP_ESCEND: outputchr=SLIP_END; break; case SLIP_ESCESC: outputchr=SLIP_ESC; break; default: if (!st->ignoring_packet) { Message(M_WARNING, "userv_afterpoll: bad SLIP escape" " character, dropping packet\n"); } st->ignoring_packet=True; outputchr=OUTPUT_NOTHING; break; } } else { switch (buf[i]) { case SLIP_END: outputchr=OUTPUT_END; break; case SLIP_ESC: st->pending_esc=True; outputchr=OUTPUT_NOTHING; break; default: outputchr=buf[i]; break; } } if (st->ignoring_packet) { if (outputchr == OUTPUT_END) { st->ignoring_packet=False; st->buff->size=0; } } else { if (outputchr == OUTPUT_END) { if (st->buff->size>0) { st->netlink_to_tunnel(&st->nl,st->buff); BUF_ALLOC(st->buff,"userv_afterpoll"); } st->buff->size=0; } else if (outputchr != OUTPUT_NOTHING) { if (buf_remaining_space(st->buff)) { buf_append_uint8(st->buff,outputchr); } else { Message(M_WARNING, "userv_afterpoll: dropping overlong" " SLIP packet\n"); st->ignoring_packet=True; } } } } } static void slip_init(struct slip *st, struct cloc loc, dict_t *dict, cstring_t name, netlink_deliver_fn *to_host) { st->netlink_to_tunnel= netlink_init(&st->nl,st,loc,dict, "netlink-userv-ipif",NULL,to_host); st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"name",loc); BUF_ALLOC(st->buff,"slip_init"); st->pending_esc=False; st->ignoring_packet=False; } /* Connection to the kernel through userv-ipif */ struct userv { struct slip slip; int txfd; /* We transmit to userv */ int rxfd; /* We receive from userv */ cstring_t userv_path; cstring_t service_user; cstring_t service_name; pid_t pid; bool_t expecting_userv_exit; }; static int userv_beforepoll(void *sst, struct pollfd *fds, int *nfds_io, int *timeout_io) { struct userv *st=sst; if (st->rxfd!=-1) { BEFOREPOLL_WANT_FDS(2); fds[0].fd=st->txfd; fds[0].events=0; /* Might want to pick up POLLOUT sometime */ fds[1].fd=st->rxfd; fds[1].events=POLLIN; } else { BEFOREPOLL_WANT_FDS(0); } return 0; } static void userv_afterpoll(void *sst, struct pollfd *fds, int nfds) { struct userv *st=sst; uint8_t rxbuf[DEFAULT_BUFSIZE]; int l; if (nfds==0) return; if (fds[1].revents&POLLERR) { Message(M_ERR,"%s: userv_afterpoll: POLLERR!\n",st->slip.nl.name); } if (fds[1].revents&POLLIN) { l=read(st->rxfd,rxbuf,DEFAULT_BUFSIZE); if (l<0) { if (errno!=EINTR && !iswouldblock(errno)) fatal_perror("%s: userv_afterpoll: read(rxfd)", st->slip.nl.name); } else if (l==0) { fatal("%s: userv_afterpoll: read(rxfd)=0; userv gone away?", st->slip.nl.name); } else slip_unstuff(&st->slip,rxbuf,l); } } /* Send buf to the kernel. Free buf before returning. */ static void userv_deliver_to_kernel(void *sst, struct buffer_if *buf) { struct userv *st=sst; if (buf->size > st->slip.nl.mtu) { Message(M_ERR,"%s: packet of size %"PRIu32" exceeds mtu %"PRIu32":" " cannot be injected into kernel, dropped\n", st->slip.nl.name, buf->size, st->slip.nl.mtu); BUF_FREE(buf); return; } slip_stuff(&st->slip,buf,st->txfd); } static void userv_userv_callback(void *sst, pid_t pid, int status) { struct userv *st=sst; if (pid!=st->pid) { Message(M_WARNING,"userv_callback called unexpectedly with pid %d " "(expected %d)\n",pid,st->pid); return; } if (!(st->expecting_userv_exit && (!status || (WIFSIGNALED(status) && WTERMSIG(status)==SIGTERM)))) { lg_exitstatus(0,st->slip.nl.name,0, st->expecting_userv_exit ? M_WARNING : M_FATAL, status,"userv"); } st->pid=0; } struct userv_entry_rec { cstring_t path; const char **argv; int in; int out; /* XXX perhaps we should collect and log stderr? */ }; static void userv_entry(void *sst) { struct userv_entry_rec *st=sst; dup2(st->in,0); dup2(st->out,1); setsid(); execvp(st->path,(char *const*)st->argv); perror("userv-entry: execvp()"); exit(1); } static void userv_invoke_userv(struct userv *st) { struct userv_entry_rec er[1]; int c_stdin[2]; int c_stdout[2]; string_t nets; string_t s; struct netlink_client *r; struct ipset *allnets; struct subnet_list *snets; int i, nread; uint8_t confirm; if (st->pid) { fatal("userv_invoke_userv: already running"); } /* This is where we actually invoke userv - all the networks we'll be using should already have been registered. */ char addrs[512]; snprintf(addrs,sizeof(addrs),"%s,%s,%d,slip", ipaddr_to_string(st->slip.nl.local_address), ipaddr_to_string(st->slip.nl.secnet_address),st->slip.nl.mtu); allnets=ipset_new(); for (r=st->slip.nl.clients; r; r=r->next) { if (r->link_quality > LINK_QUALITY_UNUSED) { struct ipset *nan; r->kup=True; nan=ipset_union(allnets,r->networks); ipset_free(allnets); allnets=nan; } } snets=ipset_to_subnet_list(allnets); ipset_free(allnets); nets=safe_malloc(20*snets->entries,"userv_invoke_userv:nets"); *nets=0; for (i=0; ientries; i++) { s=subnet_to_string(snets->list[i]); strcat(nets,s); strcat(nets,","); } nets[strlen(nets)-1]=0; subnet_list_free(snets); Message(M_INFO,"%s: about to invoke: %s %s %s %s %s\n",st->slip.nl.name, st->userv_path,st->service_user,st->service_name,addrs,nets); st->slip.pending_esc=False; /* Invoke userv */ pipe_cloexec(c_stdin); pipe_cloexec(c_stdout); st->txfd=c_stdin[1]; st->rxfd=c_stdout[0]; er->in=c_stdin[0]; er->out=c_stdout[1]; /* The arguments are: userv service-user service-name local-addr,secnet-addr,mtu,protocol route1,route2,... */ const char *er_argv[6]; er->argv=er_argv; er->argv[0]=st->userv_path; er->argv[1]=st->service_user; er->argv[2]=st->service_name; er->argv[3]=addrs; er->argv[4]=nets; er->argv[5]=NULL; er->path=st->userv_path; st->pid=makesubproc(userv_entry, userv_userv_callback, er, st, st->slip.nl.name); close(er->in); close(er->out); free(nets); Message(M_INFO,"%s: userv-ipif pid is %d\n",st->slip.nl.name,st->pid); /* Read a single character from the pipe to confirm userv-ipif is running. If we get a SIGCHLD at this point then we'll get EINTR. */ if ((nread=read(st->rxfd,&confirm,1))!=1) { if (errno==EINTR) { Message(M_WARNING,"%s: read of confirmation byte was " "interrupted\n",st->slip.nl.name); } else { if (nread<0) { fatal_perror("%s: error reading confirmation byte", st->slip.nl.name); } else { fatal("%s: unexpected EOF instead of confirmation byte" " - userv ipif failed?", st->slip.nl.name); } } } else { if (confirm!=SLIP_END) { fatal("%s: bad confirmation byte %d from userv-ipif", st->slip.nl.name,confirm); } } setnonblock(st->txfd); setnonblock(st->rxfd); add_hook(PHASE_CHILDPERSIST,childpersist_closefd_hook,&st->txfd); add_hook(PHASE_CHILDPERSIST,childpersist_closefd_hook,&st->rxfd); } static void userv_kill_userv(struct userv *st) { if (st->pid) { kill(-st->pid,SIGTERM); st->expecting_userv_exit=True; } } static void userv_phase_hook(void *sst, uint32_t newphase) { struct userv *st=sst; /* We must wait until signal processing has started before forking userv */ if (newphase==PHASE_RUN) { userv_invoke_userv(st); /* Register for poll() */ register_for_poll(st, userv_beforepoll, userv_afterpoll, st->slip.nl.name); } if (newphase==PHASE_SHUTDOWN) { userv_kill_userv(st); } } static list_t *userv_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct userv *st; item_t *item; dict_t *dict; NEW(st); /* First parameter must be a dict */ item=list_elem(args,0); if (!item || item->type!=t_dict) cfgfatal(loc,"userv-ipif","parameter must be a dictionary\n"); dict=item->data.dict; slip_init(&st->slip,loc,dict,"netlink-userv-ipif", userv_deliver_to_kernel); st->userv_path=dict_read_string(dict,"userv-path",False,"userv-netlink", loc); st->service_user=dict_read_string(dict,"service-user",False, "userv-netlink",loc); st->service_name=dict_read_string(dict,"service-name",False, "userv-netlink",loc); if (!st->userv_path) st->userv_path="userv"; if (!st->service_user) st->service_user="root"; if (!st->service_name) st->service_name="ipif"; st->rxfd=-1; st->txfd=-1; st->pid=0; st->expecting_userv_exit=False; add_hook(PHASE_RUN,userv_phase_hook,st); add_hook(PHASE_SHUTDOWN,userv_phase_hook,st); return new_closure(&st->slip.nl.cl); } void slip_module(dict_t *dict) { add_closure(dict,"userv-ipif",userv_apply); } work/stamp-h.in0000664000000000000000000000001215063477206010634 0ustar timestamp work/stest/0000775000000000000000000000000015063477206010104 5ustar work/stest/Dir.sd.mk0000664000000000000000000000253415063477206011564 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. &TARGETS += & udp-preload.so &DEPS += & udp-preload.so &DEPS += &^ common.tcl &DEPS += secnet &DEPS += base91s/base91s &DEPS += test-example/sites.conf &DEPS += test-example/sites-nonego.conf &DEPS += $(test-example_PRIVKEYS) &:include test-common.sd.mk &OBJECTS += & udp-preload.o $(&OBJECTS) : ALL_CFLAGS += -D_REENTRANT -fPIC -Wno-unused-result &udp-preload.so: $(&OBJECTS) $(CC) -shared -Wl,-soname,$@.1 $^ -o $@ -ldl # These test scripts use little cpu but contain sleeps etc. So when # there are several, we are going to want to run *loads* in parallel. # # Ideally we would do something like "every one of these counts for a # tenth of a job" but make can't do that. So bodge it: we treat all the # tests as a single job, and disconnect the parent's jobserver. # # make.info says $(MAKE) causes special handling of the rule but only # if it's written literally like that in the rule, hence this # indirection. We need no squash MAKEFLAGS and MFLAGS too. # MAKELEVEL seems like it will be fine to pass on. MAKE_NOTSPECIAL:=$(MAKE) &check:: $(&DEPS) env -u MAKEFLAGS -u MFLAGS \ $(MAKE_NOTSPECIAL) -f main.mk -j$(shell nproc || echo 1)0 &check-real &:include subdirmk/cdeps.sd.mk work/stest/common.tcl0000664000000000000000000002150215063477206012100 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. source test-common.tcl package require Tclx load chiark_tcl_hbytes-1.so load chiark_tcl_dgram-1.so set netlink(inside) { local-address "172.18.232.9"; secnet-address "172.18.232.10"; remote-networks "172.18.232.0/28"; } set netlink(outside) { local-address "172.18.232.1"; secnet-address "172.18.232.2"; remote-networks "172.18.232.0/28"; } set ports(inside) {16913 16910} set ports(outside) 16900 set defnet_v4 198.51.100 set defnet_v6 2001:db8:ff00 set defaddr_v4 ${defnet_v4}.1 set defaddr_v6 ${defnet_v6}::1 set extra(inside) { local-mobile True; mtu-target 1260; } set extra(outside) {} set privkey(inside) test-example/inside.privkeys/ set privkey(outside) test-example/outside.privkeys/ set initiator inside proc sitesconf_hook {l} { return $l } proc oldsecnet {site} { upvar #0 oldsecnet($site) oldsecnet expr {[info exists oldsecnet] && [set oldsecnet]} } proc mkconf {location site} { global tmp global builddir global netlink global ports global extra global netlinkfh global defaddr_v4 defaddr_v6 upvar #0 privkey($site) privkey set pipefp $tmp/$site.netlink foreach tr {t r} { file delete $pipefp.$tr exec mkfifo -m600 $pipefp.$tr set netlinkfh($site.$tr) [set fh [open $pipefp.$tr r+]] fconfigure $fh -blocking 0 -buffering none -translation binary } fileevent $netlinkfh($site.r) readable \ [list netlink-readable $location $site] set fakeuf $tmp/$site.fake-userv set fakeuh [open $fakeuf w 0755] puts $fakeuh "#!/bin/sh set -e exec 3<&0 cat <&3 3<&- >$pipefp.r & exec 3<>$pipefp.t exec <$pipefp.t exec 3<&- exec cat " close $fakeuh set cfg " hash sha1; netlink userv-ipif { name \"netlink\"; userv-path \"$fakeuf\"; $netlink($site) mtu 1400; buffer sysbuffer(2048); interface \"secnet-test-[string range $site 0 0]\"; }; comm " set delim {} foreach port $ports($site) { append cfg "$delim udp { port $port; address \"$defaddr_v6\", \"$defaddr_v4\"; buffer sysbuffer(4096); } " set delim , } append cfg "; local-name \"test-example/$location/$site\"; " switch -glob $privkey { */ { set sitesconf sites.conf append cfg " key-cache priv-cache({ privkeys \"$builddir/${privkey}priv.\"; }); " } {load-private *} { set sitesconf sites-nonego.conf append cfg " local-key load-private(\"[lindex $privkey 1]\",\"$builddir/[lindex $privkey 2]\"); " } * { set sitesconf sites-nonego.conf append cfg " local-key rsa-private(\"$builddir/$privkey\"); " } } set sitesconf $builddir/test-example/$sitesconf append cfg $extra($site) append cfg " log logfile { prefix \"$site\"; class \"debug\",\"info\",\"notice\",\"warning\",\"error\",\"security\",\"fatal\"; " if {[oldsecnet $site]} { append cfg " filename \"/dev/stderr\"; " } append cfg " }; " append cfg { system { }; resolver adns { }; log-events "all"; random randomfile("/dev/urandom",no); transform eax-serpent { }, serpent256-cbc { }; } set pubkeys $tmp/$site.pubkeys file delete -force $pubkeys exec cp -rl $builddir/test-example/pubkeys $pubkeys set f [open $sitesconf r] while {[gets $f l] >= 0} { regsub {\"[^\"]*test-example/pubkeys/} $l "\"$pubkeys/" l regsub -all {\"\[127\.0\.0\.1\]\"} $l "\"\[$defaddr_v4\]\"" l regsub -all {\"\[::1]\"} $l "\"\[$defaddr_v6\]\"" l set l [sitesconf_hook $l] append cfg $l "\n" } set sites [read $f] close $f append cfg $sites append cfg { sites map(site,all-sites); } return $cfg } proc spawn-secnet {location site} { global tmp global builddir global netlinkfh global env global pidmap global readbuf upvar #0 pids($site) pid set readbuf($site) {} set cf $tmp/$site.conf set ch [open $cf w] puts $ch [mkconf $location $site] close $ch set secnet $builddir/secnet if {[oldsecnet $site]} { set secnet $env(OLD_SECNET_DIR)/secnet } set argl [list $secnet -dvnc $cf] set divertk SECNET_STEST_DIVERT_$site set spawn_info "spawn:" foreach k [array names env] { switch -glob $k { SECNET_STEST_DIVERT_* - SECNET_TEST_BUILDDIR - OLD_SECNET_DIR { } *SECNET* - *PRELOAD* { append spawn_info " $k=$env($k)" } } } if {[info exists env($divertk)]} { set divert $env($divertk) } else { set divert {} } switch -glob $divert { i - {i *} { regsub {^i} $divert {} divert_prefix puts "$spawn_info $divert_prefix $argl" puts -nonewline "run ^ command, hit return " flush stdout gets stdin set argl {} } 0 - "" { puts "$spawn_info $argl" } /* - ./* { puts "$spawn_info $argl" set argl [split $divert] puts "... $argl" } * { error "$divertk not understood" } } if {[llength $argl]} { set pid [fork] set pidmap($pid) "secnet $location/$site" if {!$pid} { execl [lindex $argl 0] [lrange $argl 1 end] } } puts -nonewline $netlinkfh($site.t) [hbytes h2raw c0] } proc netlink-readable {location site} { global ok upvar #0 readbuf($site) buf upvar #0 netlinkfh($site.r) fh while 1 { set x [read $fh] set h [hbytes raw2h $x] if {![hbytes length $h]} return append buf $h #puts "READABLE $site buf=$buf" while {[regexp {^((?:..)*?)c0(.*)$} $buf dummy now buf]} { #puts "READABLE $site now=$now (buf=$buf)" regsub -all {^((?:..)*?)dbdc} $now {\1c0} now regsub -all {^((?:..)*?)dbdd} $now {\1db} now puts "netlink-got-packet $location $site $now" netlink-got-packet $location $site $now } } } proc netlink-got-packet {location site data} { global initiator if {![hbytes length $data]} return switch -exact $site!$initiator { inside!inside - outside!outside { switch -glob $data { 45000054ed9d4000fe0166d9ac12e802ac12e80900* { puts "OK $data" finish 0 } * { error "unexpected $site $data" } } } default { error "$site rx'd! (initiator $initiator)" } } } proc bgerror {message} { global errorInfo errorCode catch { puts stderr " ---------------------------------------- $errorInfo $errorCode $message ---------------------------------------- " } finish 1 } proc sendpkt {} { global netlinkfh global initiator set p { 4500 0054 ed9d 4000 4001 24da ac12 e809 ac12 e802 0800 1de4 2d96 0001 f1d4 a05d 0000 0000 507f 0b00 0000 0000 1011 1213 1415 1617 1819 1a1b 1c1d 1e1f 2021 2223 2425 2627 2829 2a2b 2c2d 2e2f 3031 3233 3435 3637 } puts -nonewline $netlinkfh($initiator.t) \ [hbytes h2raw c0[join $p ""]c0] } set socktmp $tmp/s exec mkdir -p -m700 $socktmp regsub {^(?!/|\./)} $socktmp {./} socktmp ;# dgram-socket wants ./ or / proc prefix_preload {lib} { prefix_some_path LD_PRELOAD $lib } set env(UDP_PRELOAD_DIR) $socktmp prefix_preload $builddir/stest/udp-preload.so proc finish {estatus} { puts stderr "FINISHING $estatus" signal default SIGCHLD global pidmap foreach pid [array names pidmap] { kill KILL $pid } exit $estatus } proc reap {} { global pidmap #puts stderr REAPING foreach pid [array names pidmap] { set got [wait -nohang $pid] if {![llength $got]} continue set info $pidmap($pid) unset pidmap($pid) puts stderr "reaped $info: $got" finish 1 } } signal -restart trap SIGCHLD { after idle reap } proc udp-proxy {} { global socktmp udpsock set u $socktmp/udp file delete $u regsub {^(?!/)} $u {./} u set udpsock [dgram-socket create $u] dgram-socket on-receive $udpsock udp-relay } proc udp-relay {data src sock args} { global udpsock socktmp set headerlen [expr {52+1}] set orgsrc $src set dst [hbytes range $data 0 $headerlen] regsub {(?:00)*$} $dst {} dst set dst [hbytes h2raw $dst] hbytes overwrite data 0 [hbytes zeroes $headerlen] regsub {.*/} $src {} src set srch [hbytes raw2h $src] hbytes append srch 00 if {[catch { if {[regexp {[^.,:0-9a-f]} $dst c]} { error "bad dst" } if {[hbytes length $srch] > $headerlen} { error "src addr too long" } hbytes overwrite data 0 $srch dgram-socket transmit $udpsock $data $socktmp/$dst } emsg]} { puts stderr "$orgsrc -> $dst: $emsg" } } proc adj-after {timeout args} { upvar #0 env(SECNET_STEST_TIMEOUT_MUL) mul if {[info exists mul]} { set timeout [expr {$timeout * $mul}] } eval after $timeout $args } proc test-kex {} { udp-proxy spawn-secnet in inside spawn-secnet out outside adj-after 500 sendpkt adj-after 1000 sendpkt adj-after 5000 timed-out vwait ok } work/stest/t-Cnonnego-on0000775000000000000000000000036015063477206012452 0ustar #! /usr/bin/tclsh # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. set oldsecnet(inside) 1 source stest/t-nonnego-on work/stest/t-Cnonnego-onr0000775000000000000000000000040615063477206012635 0ustar #! /usr/bin/tclsh # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. set oldsecnet(inside) 1 set initiator outside source stest/t-nonnego-on work/stest/t-basic-kex0000775000000000000000000000034015063477206012136 0ustar #! /usr/bin/tclsh # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. source stest/common.tcl test-kex work/stest/t-nonnego-on0000775000000000000000000000062715063477206012355 0ustar #! /usr/bin/tclsh # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. source stest/common.tcl # `non-negotiating' ends: set privkey(inside) test-example/inside.key # So old, new; ie -on # There is no -no because the sites file tells a new inside to expect # a different key. test-kex work/stest/t-nonnego-oo0000775000000000000000000000151715063477206012355 0ustar #! /usr/bin/tclsh # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. source stest/common.tcl # `non-negotiating' ends: set privkey(inside) {load-private rsa1 test-example/inside.key} set privkey(outside) {load-private rsa1 test-example/outside.key} # So old, old; ie -oo # There is no -no because the sites file tells a new inside to expect # a different key. proc sitesconf_hook {l} { global builddir # Use `make-public' verb, so we have a test case for it if {[regexp {^(.* key )rsa-public\("(\d+)","(\d+)"\)(;.*)$} \ $l dummy lhs rsa_e rsa_n rhs]} { set b91 [exec $builddir/base91s/base91s -w0 << "42 $rsa_e $rsa_n"] set l "${lhs}make-public(\"rsa1\",\"$b91\")${rhs}" } return $l } test-kex work/stest/udp-preload.c0000664000000000000000000002234415063477206012471 0ustar /* * udp-preload.c - testing mock library for secnet udp * This file is part of secnet. * * Copyright (C) 1998,2003-2004,2012,2017,2019 Ian Jackson * * 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, 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #define STDERRSTR_CONST(m) write(2,m,sizeof(m)-1) #define STDERRSTR_STRING(m) write(2,m,strlen(m)) typedef void anyfn_type(void); static anyfn_type *find_any(const char *name) { static const char *dlerr; anyfn_type *kv; kv= dlsym(RTLD_NEXT,name); if (kv) return kv; dlerr= dlerror(); if (!dlerr) dlerr= "dlsym() failed for no reason"; STDERRSTR_CONST("udp-preload: error finding original version of "); STDERRSTR_STRING(name); STDERRSTR_CONST(": "); STDERRSTR_STRING(dlerr); STDERRSTR_STRING("\n"); errno= ENOSYS; return 0; } #define socket_args int domain, int type, int protocol #define close_args int fd #define bind_args int fd, const struct sockaddr *addr, socklen_t addrlen #define sendto_args int fd, const void *buf, size_t len, int flags, \ const struct sockaddr *addr, socklen_t addrlen #define recvfrom_args int fd, void *buf, size_t len, int flags, \ struct sockaddr *addr, socklen_t *addrlen #define setsockopt_args int fd, int level, int optname, \ const void *optval, socklen_t optlen #define getsockname_args int fd, struct sockaddr *addr, socklen_t *addrlen #define WRAPS(X) \ X(socket, int, (domain,type,protocol)) \ X(close, int, (fd)) \ X(bind, int, (fd,addr,addrlen)) \ X(sendto, ssize_t, (fd,buf,len,flags,addr,addrlen)) \ X(recvfrom, ssize_t, (fd,buf,len,flags,addr,addrlen)) \ X(setsockopt, int, (fd,level,optname,optval,optlen)) \ X(getsockname,int, (fd,addr,addrlen)) #define DEF_OLD(fn,rt,args) \ typedef rt fn##_fn_type(fn##_args); \ static fn##_fn_type find_##fn, *old_##fn=find_##fn; \ static rt find_##fn(fn##_args) { \ anyfn_type *anyfn; \ anyfn= find_any(#fn); if (!anyfn) return -1; \ old_##fn= (fn##_fn_type*)anyfn; \ return old_##fn args; \ } WRAPS(DEF_OLD) #define WRAP(fn) int fn(fn##_args) #define TWRAP(fn) fn(fn##_args) typedef struct{ int af; } fdinfo; static fdinfo **table; static int tablesz; static fdinfo *lookup(int fd) { if (fd<0 || fd>=tablesz) return 0; return table[fd]; } #define ADDRPORTSTRLEN (INET6_ADDRSTRLEN+1+5) /* not including nul */ static int addrport2str(char buf[ADDRPORTSTRLEN+1], const struct sockaddr *addr, socklen_t addrlen) { const void *addrv=addr; const void *iav; const struct sockaddr_in *sin; const struct sockaddr_in6 *sin6; uint16_t port; socklen_t el; switch (addr->sa_family) { case AF_INET: sin =addrv; el=sizeof(*sin ); iav=&sin ->sin_addr ; port=sin ->sin_port ; break; case AF_INET6: sin6=addrv; el=sizeof(*sin6); iav=&sin6->sin6_addr; port=sin6->sin6_port; break; default: errno=ESRCH; return -1; } //fprintf(stderr,"af=%lu el=%lu addrlen=%lu\n", // (unsigned long)addr->sa_family, // (unsigned long)el, // (unsigned long)addrlen); if (addrlen!=el) { errno=EINVAL; return -1; } char *p=buf; if (!inet_ntop(addr->sa_family,iav,p,INET6_ADDRSTRLEN)) return -1; p+=strlen(p); sprintf(p,",%u",(unsigned)ntohs(port)); return 0; } static int str2addrport(char *str, struct sockaddr *addr, socklen_t *addrlen) { union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } si; memset(&si,0,sizeof(si)); int af; void *iav; uint16_t *portp; socklen_t al; switch (str[strcspn(str,".:")]) { case '.': af=AF_INET ; iav=&si.sin .sin_addr ; al=sizeof(si.sin ); portp=&si.sin .sin_port ; break; case ':': af=AF_INET6; iav=&si.sin6.sin6_addr; al=sizeof(si.sin6); portp=&si.sin6.sin6_port; break; default: errno=ESRCH; return -1; } si.sin.sin_family=af; char *comma=strchr(str,','); if (!comma) { errno=ESRCH; return -1; } *comma++=0; int r=inet_pton(af,str,iav); //fprintf(stderr,"inet_pton(%d,\"%s\",)=%d\n",af,str,r); if (r<0) return -1; if (r==0) { errno=ENOTTY; return -1; } char *ep; errno=0; unsigned long port=strtoul(comma,&ep,10); if (ep==comma || *ep || errno || port>65536) { errno=ESRCH; return -1; } *portp= htons(port); if (addr) memcpy(addr,&si, *addrlensun_family=AF_UNIX; size_t dl = strlen(dir); if (dl + 1 + strlen(leaf) + 1 > sizeof(sun->sun_path)) { errno=ENAMETOOLONG; return -1; } strcpy(sun->sun_path,dir); sun->sun_path[dl]='/'; strcpy(sun->sun_path+dl+1,leaf); return 0; } WRAP(socket) { if (!((domain==AF_INET || domain==AF_INET6) && type==SOCK_DGRAM)) return old_socket(domain,type,protocol); int fd=socket(AF_UNIX,SOCK_DGRAM,0); if (fd<0) return fd; if (fd>=tablesz) { int newsz=(fd+1)*2; table=realloc(table,newsz*sizeof(*table)); if (!table) goto fail; while (tableszaf=domain; return fd; fail: close(fd); return -1; } WRAP(close) { if (fd>=0 && fdaf==AF_INET6 && level==IPPROTO_IPV6 && optname==IPV6_V6ONLY && optlen==sizeof(int) && *(int*)optval==1) { return 0; } errno=ENOTTY; return -1; } WRAP(getsockname) { fdinfo *ent=lookup(fd); if (!ent) return old_getsockname(fd,addr,addrlen); struct sockaddr_un sun; socklen_t sunlen=sizeof(sun); if (old_getsockname(fd,(void*)&sun,&sunlen)) return -1; if (sun.sun_family!=AF_UNIX || sunlen>sizeof(sun)) { //fprintf(stderr,"old_getsockname af=%lu sunlen=%lu\n", // (unsigned long)sun.sun_family, // (unsigned long)sunlen); errno=EDOM; return -1; } char *slash=strrchr(sun.sun_path,'/'); if (str2addrport(slash ? slash+1 : sun.sun_path, addr,addrlen)) return -1; return 0; } ssize_t TWRAP(sendto) { fdinfo *ent=lookup(fd); if (!ent) return old_sendto(fd,buf,len,flags,addr,addrlen); if (flags) { errno=ENOEXEC; return -1; } const char *leaf=getenv("UDP_PRELOAD_SERVER"); if (!leaf) leaf="udp"; if (strlen(leaf) > ADDRPORTSTRLEN) { errno=ENAMETOOLONG; return -1; } struct sockaddr_un sun; if (sun_prep(&sun,leaf)) return -1; char tbuf[ADDRPORTSTRLEN+1]; memset(tbuf,0,sizeof(tbuf)); if (addrport2str(tbuf,addr,addrlen)) return -1; struct iovec iov[2]; iov[0].iov_base=tbuf; iov[0].iov_len=sizeof(tbuf); iov[1].iov_base=(void*)buf; iov[1].iov_len=len; struct msghdr m; memset(&m,0,sizeof(m)); m.msg_name=&sun; m.msg_namelen=sizeof(sun); m.msg_iov=iov; m.msg_iovlen=2; return sendmsg(fd,&m,0); } ssize_t TWRAP(recvfrom) { fdinfo *ent=lookup(fd); if (!ent) return old_recvfrom(fd,buf,len,flags,addr,addrlen); //fprintf(stderr,"recvfrom %d len=%lu flags=%d al=%lu\n", // fd, (unsigned long)len, flags, (unsigned long)*addrlen); if (flags) { errno=ENOEXEC; return -1; } char tbuf[ADDRPORTSTRLEN+1]; struct iovec iov[2]; iov[0].iov_base=tbuf; iov[0].iov_len=sizeof(tbuf); iov[1].iov_base=buf; iov[1].iov_len=len; struct msghdr m; memset(&m,0,sizeof(m)); m.msg_iov=iov; m.msg_iovlen=2; ssize_t rr=recvmsg(fd,&m,0); if (rr==-1) return rr; if ((size_t)rr Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! work/subdirmk/README0000664000000000000000000005361415063477206011453 0ustar subdirmk - assistance for non-recursive use of make =================================================== Introduction ------------ Peter Miller's 1997 essay _Recursive Make Considered Harmful_ persuasively argues that it is better to arrange to have a single make invocation with the project's complete dependency tree, rather than the currently conventional `$(MAKE) -C subdirectory' approach. However, actually writing a project's build system in a non-recursive style is not very ergonomic. The main difficulties are: - constantly having to write out long file and directory names - the lack of a per-directory make variable namespace means long make variables (or namespace clashes) - it is difficult to arrange that one can cd to a subdirectory and say `make all' and have something reasonable happen (to wit, build an appropriate subset) `subdirmk' is an attempt to solve these problems (and it also slightly alleviates some of the boilerplate needed to support out-of-tree builds well, and helps a bit with metaprogramming and rule writing). Basic approach -------------- The developer is expected to write a makefile fragment, in each relevant subdirectory, called `Dir.sd.mk'. These fragments may contain ordinary make language. Unqualified filenames are relative to the build toplevel, and all commands all run there. However, the sigil & is treated specially. By and large, it refers to `the build directory corresponding to this .sd.mk file', etc. There are a variety of convenient constructions. The result is that to a large extent, the Dir.sd.mk has an easy way to namespace its "local" make variables, and an easy way to refer to its "local" filenames (and filenames in general). The Dir.sd.mk's are filtered, fed through autoconf in the usual way (for @..@-substitutions) and included by one autogenerated toplevel makefile. So all of the input is combined and passed to one make invocation. (A corollary is that there is no enforcement of the namespacing: discipline is required to prefix relevant variable names with &, etc.) Each subdirectory is also provided with an autogenerated `Makefile' which exists purely to capture ordinary make invocations and arrange for something suitable to happen. Where there are dependencies between subdirectories, each Dir.sd.mk can simply refer to files in other subdirectories directly. Substitution syntax ------------------- In general & expands to the subdirectory name when used for a filename, and to the subdirectory name with / replaced with _ for variable names. (If your variables start with capital letters and your filenames with lowercase. Otherwise, use &/ or &_.) Note that & is processed *even in makefile comments*. The substitutor does not understand make syntax, or shell syntax, at all. However, the substitution rules are chosen to work well with constructs which are common in makefiles. In the notation below, we suppose that the substitution is being in done in a subdirectory sub/dir of the source tree. In the RH column we describe the expansion at the top level, which is often a special case (in general in variable names we call that TOP rather than the empty string). &CAPS => sub_dir_CAPS or TOP_CAPS &lc => sub/dir/lc or lc Here CAPS is any ASCII letter A-Z and lc is a-z. The assumption is that filenames are usually lowercase and variables usually uppercase. Otherwise, use another syntax: &/ => sub/dir/ or nothing &_ => sub_dir_ or TOP_ &. => sub/dir or . (This implies that `&./' works roughly like `&/', although it can produce a needless `./') &= => sub_dir or TOP &^lc => ${top_srcdir}/sub/dir/lc &^/ => ${top_srcdir}/sub/dir/ &^. => ${top_srcdir}/sub/dir &~lc => ${top_srcdir}/lc &~/ => ${top_srcdir}/ &~. => ${top_srcdir} In general: ^ pathname of this subdirectory in source tree ~ pathname of top level of source tree / terminates the path escape } needed if next is _ terminates the var escape } not letter or space) . terminates path escape giving dir name (excluding /) = terminates var escape giving only prefix part (rarely needed) lwsp starts multi-word processing (see below) So pathname syntax is a subset of: '&' [ '^' | '~' ] [ lc | '/' | '.' ] && => && for convenience in shell runes &\& => & general escaping mechanism &\$ => $ provided for $-doubling regimes &\NEWLINE eats the newline and vanishes &$VARIABLE => ${sub_dir_VARIABLE} or ${TOP_VARIABLE} VARIABLE is ASCII starting with a letter and matching \w+ & thing thing... & &^ thing thing... & &~ thing thing... & Convenience syntax for prefixing multiple filenames. Introduced by & followed by lwsp where lc could go. Each lwsp-separated non-ws word is prefixed by &/ etc. etc. respectively. No other & escapes are recognised. This processing continues until & preceded by lwsp, or until EOL (the end of the line), or \ then EOL. &: .... recognised at start of line only (possibly after lwsp) &: => &: for make multiple targets syntax recognised anywhere *except* start of line &:include filename filename should usually be [&]foo.sd.mk &:-include filename tolerate nonexistent file RHS is &-expanded but filenames are relative to the top srcdir. This implies that unqualified names are like &~/ whereas &/ is like &^/. &^ and &~ do not work here because they expand to constructions involving literally `$(top_srcdir)', but the RHS is not make-expanded. &! disables & until EOL (and then disappears) &# delete everything to end of line (useful if the RHS contains unrecognised & constructions) &TARGETS_things Handled specially. If mentioned at the start of a line (possibly following whitespace), declares that this subdir ought to have a target `things'. The rule will be &/things:: $(&TARGETS_things) You may extend it by adding more :: rules for the target, but the preferred style is to do things like this: &TARGETS_check += & test-passed.stamp It is important to mention &TARGETS_things at least once in the context of each applicable directory, because doing so arranges that the *parent* will also have a `things' target which recursively implies this directory's `things'. Must be spelled exactly &TARGETS_things. &_TARGETS_things, for example, is not magic. To make the target exist without providing any prerequisites for it, write a line containing just `&TARGETS_things +='. `all' is extra special: every directory has an `all' target, which corresponds to &TARGETS. Directives - - - - - &:warn [!]WARNTAG ... Suppress (with !) or re-enable (without !) warnings tagged WARNTAG (see section `Warnings', below). The suppression list is reset at the start of processing in each subdirectory. Warnings that appear at the end of processing are controlled by the final warning state after processing all the toplevel input files (including Final.sd.mk). &:local+global [!][&]VARIABLE ... Suppresses any warnings relating to forthcoming mentions to VARIABLE or &VARIABLE, as applicable. Scope ends at the end of the current directory's Suffix.sd.mk. Prefixing with ! removes [&]VARIABLE from the suppresion list. &:changequote NEWQUOTE changes the escape sequence from & to literally NEWQUOTE NEWQUOTE may be any series of of non-whitespace characters, and is terminated by EOL or lwsp. The whole line is discarded. After this, write NEWQUOTE instead of &, everywhere. The effect is unscoped and lasts until the next setting, or until the end of the current directory's Suffix.sd.mk. It takes effect on &:include'd files too, so maybe set it back before using &:include. Notably NEWQUOTENEWQUOTE => NEWQUOTENEWQUOTE NEWQUOTE\NEWQUOTE => NEWQUOTE NEWQUOTE\$ => $ NEWQUOTE:changequote & set escape back to & Dollar doubling and macro assistance ------------------------------------ &$+ Starts dollar-doubling &$- Stops dollar-doubling Both are idempotent and local to the file or context. This is useful both for make macrology involving $(eval ...), and possibly for helping write complicated recipes involving shell variables, inline Perl code, etc. Sometimes we will show $'s being doubled inside another construct. This means the content of the construct is $-doubled: $-doubling is locally enabled, and restored afterwards. &:macro NAME => define NAME STUFF $ THINGS .. STUFF $$ THINGS &:endm .. endef NAME is processed for & &{..$..} => ${eval ${call ..$$..}} (matches { } pairs to find the end) content is $-doubled (unless it contains &$- to turn that off) contrast &(...), see "Convenience syntax for call", below. Together &:macro and &{...} provide a more reasonable macro facility than raw make. They solve the problem that make expansions cannot directly generate multiple rules, variables, etc.; instead, `$(eval )' must be used, but that re-expands the argument, meaning that all the literal text must be $-doubled. This applies to the macro text and to the arguments. Also `$(eval $(call ...))' is an unfortunate syntax. Hence &:macro and &{...}. While dollar-doubling: - - - - - - - - - - - $ => $$ including $'s produced by other &-expansions not mentioned here &\$ => $ &$( => $( &$NN => ${NN} where N are digits A few contexts do not support $-doubling, such as directive arguments or places where this might imply $-quadrupling. (There is no way to get $-quadrupling.) Convenience syntax for call - - - - - - - - - - - - - - &(thing => $(call thing &( thing => $(call thing and specially: &(&lc => $(call sub_dir_lc or $(call TOP_lc &( &lc => $(call sub_dir_lc or $(call TOP_lc even though lc would normally be thought a filename Unlike &{...}, this does not involve any dollar-doubling. Use this when the expansion is going to be a piece of text to be used as part of a rule, filename, etc. When the expansion is top-level make text (eg, rules), use &:macro and &{...}. Invocation, "recursive" per-directory targets --------------------------------------------- Arrangements are made so that when you run `make foo' in a subdirectory, it is like running the whole toplevel makefile, from the toplevel, as `make subdir/foo'. If `subdir/foo' is a file that might be built, that builds it. But `foo' can also be a conventional target like `all'. Each subdirectory has its own `all' target. For example a subdirectory `src' has a target `src/all'. The rules for these are automatically generated from the settings of the per-directory &TARGETS variables. &TARGETS is magic in this way. (In src/Dir.sd.mk, &TARGETS of course refers to a make variable called src_TARGETS.) The `all' target in a parent directory is taken to imply the `all' targets in all of its subdirectories, recursively. And in the autogenerated stub Makefiles, `all' is the default target. So if you just type `make' in the toplevel, you are asking for `&all' (/all) for every directory in the project. In a parallel build, the rules for all these various subdirectory targets may be in run in parallel: there is only one `make' invocation at a time. There is no sequencing between subdirectories, only been individual targets (as specified according to their dependencies). You can define other per-directory recursive targets too: set the variable &TARGETS_zonk, or whatever (being sure to write &TARGETS_zonk at the start of a line). This will create a src/zonk target (for appropriate value of src/). Unlike `all', these other targets only exist in areas of the project where at least something mentions them. So for example, if &TARGETS_zonk is set in src but not lib, `make zonk' in lib will fail. If you want to make a target exist everywhere, += it with nothing in Prefix.sd.mk or Suffix.sd.mk (see below). Prefix.sd.mk, Suffix.sd.mk, Final.sd.mk, inclusion -------------------------------------------------- The files Prefix.sd.mk and Suffix.sd.mk in the toplevel of the source are automatically processed before and after each individual directory's Dir.sd.mk, and the &-substituted contents therefore appear once for each subdirectory. This lets you do per-directory boilerplate. Some useful boilerplate is already provided in subdirmk, for you to reference like this: &:include subdirmk/cdeps.sd.mk &:include subdirmk/clean.sd.mk For example you could put that in Suffix.sd.mk. The top-level Dir.sd.mk is the first makefile included after the autogenerated `main.mk' which merely has some basic settings and includes. So if you want to get in early and set global variables, put them near the top of Dir.sd.mk. The file Final.sd.mk in the toplevel directory is processed and the result included after all the other files. Its subdirmk filtering context inherits warning suppressions from the toplevel's Dir.sd.mk etc., but not anything else. subdirmk's filter script itself sets (only) these variables: top_srcdir abs_top_srcdir SUBDIRMK_MAKEFILES MAKEFILE_TEMPLATES You are likely to want to define $(PWD), and shorter names for top_srdir and abs_top_srcdir (we suggest $(src) and $(abs_src)). Warnings -------- subdirmk's `generate' program, which does the acual &-substitution, can produce some warnings about your .sd.mk files. These can be suppressed with the &:warn directive. The warning tags are: local+global The same VARNAME was used both with and without an & prefix. This can be confusing. Also, if you avoid this then you will get a warning iff you accidentally leave off a needed &. The generation of this warning depends on scanning your makefile for things that look like variable references, which subdirmk does not do completely perfectly. Exciting make syntax may evade this warning, or require suppressions. (You can suppress this warning for a particular VARNAME with the &:local+global directive.) single-char-var A variable expansion like $FBAR. make's expansion rules interpret this as $(F)BAR. It's normally better to write it this way, at least if the variable expansion is followed by more letters. Note that &$FOO works differently to raw make: it expands to ${sub_dir_FOO}. broken-var-ref An attempt at variable expansion looking like $&... You probably expected this to mean $(TOP_F)BAR but it expands to $TOP_FBAR which make thinks means $(T)OP_FBAR. unknown-warning &:warn was used to try to enable a warning that this version of subdirmk does not understand. (Note that an attempt to *dis*able an unknown warning is only reported if some other warning was issued which might have been disabled.) Guides, hints, and further explanations ======================================= Incorporating this into your project ------------------------------------ Use `git-subtree' to merge the subdirmk/ directory. You may find it useful to symlink the DEVELOPER-CERTIFICATE file (git can store symlinks as symlinks - just `git add' the link). And you probably want to mention the situation in your top-level COPYING and HACKING. Symlink autogen.sh into your project toplevel. In your configure.ac, say m4_include([subdirmk/subdirmk.ac]) SUBDIRMK_SUBDIRS([...list of subdirectories in relative syntax...]) Write a Dir.sd.mk in each directory. See the substitution syntax reference, above, and the example/ directory here. The toplevel Dir.sd.mk should probably contain: include subdirmk/usual.mk include subdirmk/regen.mk Write a Suffix.sd.mk in the toplevel, if you want. It should probably have: &:include subdirmk/cdeps.sd.mk &:include subdirmk/clean.sd.mk Hints ----- You can convert your project incrementally. Start with the top-level Makefile.in and rename it to Dir.sd.mk, and add the appropriate stuff to configure.ac, and fix everything up. Leave the existing $(MAKE) -C for your existing subdirectories alone. Then you can convert individual subdirectories, or classes of subdirectories, at your leisure. (You must be /sure/ that each recursive (non-subdirmk) subdirectory will be entered only once at a time, but your existing recursive make descent system should already do that or you already have concurrency bugs.) Aside from this, be very wary of any invocation of $(MAKE) anywhere. This is a frequent source of concurrency bugs in recursive make build systems. When combined with nonrecursive make it's all in the same directory and there is nothing stopping the different invocations ending up trying to make the same targets at the same time. That causes hideous racy lossage. There are ways to get this to work reliably but it is advanced stuff. If you make syntax errors, or certain kinds of other errors, in your makefiles, you may find that just `make' is broken now and cannot get far enough to regenerate a working set of makefiles. If this happens just rerun ./config.status by hand. If you go back and forth between different versions of your code you can sometimes find that `make' complains that one of your Dir.sd.mk files is missing: typically, if iot was used and therefore a dependency in some other version of your code. If you run `make clean' (or `make realclean') these dependencies are suppressed, which will clear up the problem. Global definitions ------------------ If want to set global variables, such as CC, that should only be done once. You can put them in your top-level Dir.sd.mk, or a separate file you `include' and declare using SUBDIRMK_MAKEFILES. If you need different settings of variables like CC for different subdirectories, you should probably do that with target-specific variable settings. See the info node `(make) Target-specific'. Directory templates `.sd.mk' vs plain autoconf templates `.mk.in' -------------------------------------------------------------------- There are two kinds of template files. Filename .sd.mk .mk.in Processed by &-substitution, autoconf only then autoconf Instantiated Usu. once per subdir Once only Need to be mentioned No, but Dir.sd.mk All not in subdirmk/ in configure.ac? via SUBDIRMK_SUBDIRS via SUBDIRMK_MAKEFILES How to include `&:include foo.sd.mk' `include foo.mk' in all relevant .sd.mk in only one (but not needed for Dir.sd.mk Prefix, Suffix, Final) If you `include subdirmk/regen.mk', dependency management and automatic regeneration for all of this template substitution, and for config.status etc. is done for you. Tables of file reference syntaxes --------------------------------- In a nonrecursive makefile supporting out of tree builds there are three separate important distinctions between different file locations: (i) In the build tree, or in the source tree ? (ii) In (or relative to) the subdirectory to which this Dir.sd.mk relates, or relative to the project's top level ? (iii) Absolute or relative pathname ? Usually relative pathnames suffice. Where an absolute pathname is needed, it can be built out of &/ and an appropriate make variable such as $(PWD). Path construction &-expansions are built from the following: Relative paths in... build source This directory & &^ Top level . &~ In more detail, with all the various options laid out: Recommended Relative paths in... Absolute paths in... for build source build source This lc &file &^file $(PWD)/&file $(abs_src)/&file directory any &/file &^/file $(PWD)/&/file $(abs_src)/&/file several & f g h &^ f g h $(addprefix...) Top lc file &~file level any file &~/file $(PWD)/file $(abs_src)/file .mk.in file $(src)/file $(PWD)/file $(abs_src)/file several f g h &~ f g h $(addprefix...) (This assumes you have appropriate make variables src, PWD and abs_src.) Subdirectory and variable naming -------------------------------- The simple variable decoration scheme does not enforce a strict namespace distinction between parts of variable names which come from subdirectory names, and parts that mean something else. So it is a good idea to be a bit careful with your directory naming. `TOP', names that contain `_', and names that are similar to parts of make variables (whether conventional ones, or ones used in your project) are best avoided. If you name your variables in ALL CAPS and your subdirectories in lower case with `-' rather than `_', there will be no confusion. Legal information ================= subdirmk is Copyright 2019-2020 Ian Jackson Copyright 2019 Mark Wooding subdirmk and its example is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library as the file LGPL-2. If not, see https://www.gnu.org/. Individual files generally contain the following tag in the copyright notice, instead of the full licence grant text: SPDX-License-Identifier: LGPL-2.0-or-later As is conventional, this should be read as a licence grant. Contributions are accepted based on the git commit Signed-off-by convention, by which the contributors' certify their contributions according to the Developer Certificate of Origin version 1.1 - see the file DEVELOPER-CERTIFICATE. Where subdirmk is used by and incorporated into another project (eg via git subtree), the directory subdirmk/ is under GNU LGPL-2.0+, and the rest of the project are under that other project's licence(s). (The project's overall licence must be compatible with LGPL-2.0+.) work/subdirmk/autogen.sh0000775000000000000000000000031115063477206012556 0ustar #!/bin/sh # subdirmk, autogen.sh (conventional autoconf invocation script) # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. set -e cd ${0%/*} autoconf work/subdirmk/cdeps.sd.mk0000664000000000000000000000152315063477206012617 0ustar # subdirmk - useful rules for making and using cpp .*.d files # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. &# Usage: &# &:include subdirmk/cdeps.sd.mk &# (probably in Suffix.sd.mk) &# &# Arranges for automatic #include dependency tracking for &# C compilation. The compiler is asked to write the dependencies to &# .*.d and these are automatically included. &# &# There is a bug: if a #included file is deleted and all references &# in .c files to it removed, `make' will complain that it is needed &# and can't be built. `make clean' will fix this. CDEPS_CFLAGS ?= -MD -MF $(*D)/.$(*F).d &CDEPS_OBJECTS += $(&OBJECTS) &DEPFILES += $(foreach b,$(patsubst %.o,%,$(&CDEPS_OBJECTS)), \ $(dir $b).$(notdir $b).d) -include $(&DEPFILES) &CLEAN += $(&DEPFILES) work/subdirmk/clean.sd.mk0000664000000000000000000000122415063477206012601 0ustar # subdirmk - useful rules for clean target # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. &# Usage: &# &:include subdirmk/clean.sd.mk &# (probably in Suffix.sd.mk) &# &# Provides a per-directory `clean' target, which deletes all the files &# in &CLEAN. &OBJECTS, &DEPFILES and &TARGETS are automatically deleted. &# &# If you want to delete a directory, extend the target with &# &/clean:: &# $(RM) -r somethingn &# ($(RM) conventionally contains `-f'.) &CLEAN += & *~ .*~ *.tmp &CLEAN += $(&OBJECTS) &CLEAN += $(&TARGETS) &TARGETS_clean += &/clean:: $(RM) $(&CLEAN) work/subdirmk/example/0000775000000000000000000000000015063477206012215 5ustar work/subdirmk/example/.gitignore0000664000000000000000000000073515063477206014212 0ustar # subdirmk example - .gitignore # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. #----- Build artifacts ----- *.a .*.d *.o *.stamp /lib/t/toytest /src/toy #----- Autoconf cruft ----- /autom4te.cache/ /config.log /config.status /configure .makefiles.stamp /main.mk Makefile Dir.mk Final.mk *.tmp #----- For our tests ----- /build /for-test-final.sd.mk /src/for-test.sd.mk /lib/for-test.mk.in work/subdirmk/example/DEVELOPER-CERTIFICATE0000777000000000000000000000000015063477206022010 2subdirmk/DEVELOPER-CERTIFICATEustar work/subdirmk/example/Dir.sd.mk0000664000000000000000000000036715063477206013677 0ustar # subdirmk example - top-level Dir.sd.mk # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. INCLUDES += -I&^/lib/ include subdirmk/usual.mk include subdirmk/regen.mk work/subdirmk/example/Final.sd.mk0000664000000000000000000000036015063477206014203 0ustar # subdirmk - example Final.sd.mk # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. # This is a hook for subdirmk's test suite. &:-include for-test-final.sd.mk work/subdirmk/example/LGPL-20000777000000000000000000000000015063477206015470 2subdirmk/LGPL-2ustar work/subdirmk/example/Suffix.sd.mk0000664000000000000000000000040015063477206014411 0ustar # subdirmk example - general replicated per-directory template # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. &:include subdirmk/cdeps.sd.mk &:include subdirmk/clean.sd.mk work/subdirmk/example/autogen.sh0000777000000000000000000000000015063477206020024 2subdirmk/autogen.shustar work/subdirmk/example/configure.ac0000664000000000000000000000117515063477206014507 0ustar dnl -*-autoconf-*- dnl subdirmk example - configuration script dnl Copyright 2019 Mark Wooding dnl Copyright 2019 Ian Jackson dnl SPDX-License-Identifier: LGPL-2.0-or-later dnl There is NO WARRANTY. AC_INIT([mktoy], [0.9.0], [mdw@distorted.org.uk]) AC_CONFIG_SRCDIR([src/toy.c]) AC_PROG_CC INCLUDES= AC_SUBST(INCLUDES) m4_include([subdirmk/subdirmk.ac]) SUBDIRMK_SUBDIRS([lib]) SUBDIRMK_SUBDIRS([lib/t src]) # This is a hook for subdirmk's test suite. if test -f $srcdir/lib/for-test.mk.in; then SUBDIRMK_MAKEFILES([lib/for-test.mk]) fi AC_OUTPUT dnl----- That's all, folks -------------------------------------------------- work/subdirmk/example/lib/0000775000000000000000000000000015063477206012763 5ustar work/subdirmk/example/lib/Dir.sd.mk0000664000000000000000000000050515063477206014437 0ustar # subdirmk example - subdirectory rules # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. &TARGETS += & libtoy.a &OBJECTS += & toylib.o &libtoy.a: $(&OBJECTS) $(AR) rc $@ $^ # This is a hook for subdirmk's test suite. -include &for-test.mk work/subdirmk/example/lib/t/0000775000000000000000000000000015063477206013226 5ustar work/subdirmk/example/lib/t/Dir.sd.mk0000664000000000000000000000056615063477206014711 0ustar # subdirmk example - subdirectory rules # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. &TARGETS_check += & toytest.stamp &OBJECTS += & toytest.o &LIBS += lib/libtoy.a &CLEAN += & toytest toytest.stamp &toytest: $(&OBJECTS) $(&LIBS) $(LINK) $^ &toytest.stamp: & toytest $< touch $@ work/subdirmk/example/lib/t/toytest.c0000664000000000000000000000065115063477206015107 0ustar /* * subdirmk - example code * Copyright 2019 Mark Wooding * SPDX-License-Identifier: LGPL-2.0-or-later * There is NO WARRANTY. */ #include #include #include #include "toylib.h" int main(void) { const char *p; p = greeting(); if (STRNCMP(p, !=, "Hello", 5)) { fprintf(stderr, "greeting `%s' has bad salutation\n", p); exit(1); } printf("all ok\n"); return (0); } work/subdirmk/example/lib/toylib.c0000664000000000000000000000033215063477206014427 0ustar /* * subdirmk - example code * Copyright 2019 Mark Wooding * SPDX-License-Identifier: LGPL-2.0-or-later * There is NO WARRANTY. */ #include "toylib.h" const char *greeting(void) { return "Hello, world!\n"; } work/subdirmk/example/lib/toylib.h0000664000000000000000000000062315063477206014437 0ustar /* * subdirmk - example code * Copyright 2019 Mark Wooding * SPDX-License-Identifier: LGPL-2.0-or-later * There is NO WARRANTY. */ #ifndef LIBTOY_H #define LIBTOY_H #include #define STRCMP(x, op, y) (strcmp((x), (y)) op 0) #define STRNCMP(x, op, y, n) (strncmp((x), (y), (n)) op 0) #define MEMCMP(x, op, y, n) (memcmp((x), (y), (n)) op 0) extern const char *greeting(void); #endif work/subdirmk/example/src/0000775000000000000000000000000015063477206013004 5ustar work/subdirmk/example/src/Dir.sd.mk0000664000000000000000000000053415063477206014462 0ustar # subdirmk example - subdirectory rules # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. &TARGETS += & toy &OBJECTS += & toy.o &LIBS += lib/libtoy.a &toy: $(&OBJECTS) $(&LIBS) $(LINK) $^ # This is a hook for subdirmk's test suite. &:-include src/for-test.sd.mk work/subdirmk/example/src/toy.c0000664000000000000000000000035615063477206013767 0ustar /* * subdirmk - example code * Copyright 2019 Mark Wooding * SPDX-License-Identifier: LGPL-2.0-or-later * There is NO WARRANTY. */ #include #include "toylib.h" int main(void) { fputs(greeting(), stdout); return (0); } work/subdirmk/example/subdirmk0000777000000000000000000000000015063477206014111 2..ustar work/subdirmk/generate0000775000000000000000000003411115063477206012302 0ustar #!/usr/bin/perl -w # # subdirmk - &-filter (makefile generation program) # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. # # $(srcdir)/subdirmk/generate [--srcdir=SRCDIR] [--] SUBDIR... # # generates in each subdirectory # Dir.mk.tmp # Makefile # and in toplevel # main.mk.tmp use strict; use POSIX; print "$0 @ARGV\n" or die $!; our $srcdir='.'; # error handling methods: # # Error in input file, while $err_file and $. set, eg in most of # process_input_mk: # err "message"; # # Other input or usage errors: # die "subdirmk: $file:$lno: problem\n"; # die "subdirmk: some problem not locatable in that way\n"; # # Usage error: # die "subdirmk $0: explanation of problem\n"; # # System call error (not ENOENT) accessing input/output files: # die "description of problem eg maybe erbing noun: $!\n"; # # Bug detedcted in `generate': # die "internal error (some information)?"; # or similar while (@ARGV && $ARGV[0] =~ m/^-/) { $_ = shift @ARGV; last if $_ eq '--'; if (s/^--srcdir=//) { $srcdir=$'; } else { die "subdirmk $0: unknown option \`$_'\n"; } } our @subdirs = @ARGV; s{/+$}{} foreach @subdirs; our $root = [ '.', [ ], 1 ]; # each node is [ 'relative subdir name', \@children, $mentioned ] sub build_tree () { foreach my $subdir (@subdirs) { my @path = $subdir eq '.' ? () : split m{/+}, $subdir; my $node = $root; foreach my $d (@path) { my ($c,) = grep { $_->[0] eq $d } @{ $node->[1] }; if (!$c) { $c = [ $d, [ ] ]; push @{ $node->[1] }, $c; } $node = $c; } $node->[2] = 1; } } sub target_varname ($$) { my ($var_prefix, $target) = @_; return $var_prefix.'TARGETS'.($target eq 'all' ? '' : "_$target"); } our $writing_output; our $buffering_output; our %output_files; our %input_files; our @output_makefiles; sub close_any_output_file() { return unless defined $writing_output; O->error and die "error writing $writing_output.tmp: $! (?)\n"; close O or die "error closing $writing_output.tmp: $!\n"; $writing_output = undef; } sub oraw { die 'internal error' unless defined $writing_output; print O @_ or die "error writing $writing_output.tmp: $!\n"; } sub oud { # undoubled if (defined $buffering_output) { $buffering_output .= $_ foreach @_; return; } oraw @_; } our $ddbl; sub od { # maybe $-doubled if (!$ddbl) { oud @_; return; } foreach (@_) { my $e = $_; $e =~ s{\$}{\$\$}g; oud $e; } } sub start_output_file ($) { close_any_output_file(); ($writing_output) = @_; die "internal error ($writing_output?)" if $output_files{$writing_output}++; my $tmp = "$writing_output.tmp"; open O, ">", $tmp or die "create $tmp: $!\n"; oraw "# autogenerated - do not edit\n"; } sub install_output_files () { close_any_output_file(); foreach my $f (sort keys %output_files) { rename "$f.tmp", $f or die "install new $f: $!\n"; } } sub write_makefile ($$) { my ($dir_prefix,$depth) = @_; #print STDERR "write_makefile @_\n"; start_output_file("${dir_prefix}Makefile"); my $cd = $depth ? join('/', ('..',) x $depth) : '.'; my $suppress_templates= '$(if $(filter-out clean real-clean, $(subdirmk_targets)),,'. ' MAKEFILE_TEMPLATES=)'; oraw < 1 } qw( local+global single-char-var unknown-warning broken-var-ref ); our %warn_ena = @warn_ena_dfl; our $warned; our %warn_unk; sub err ($) { my ($m) = @_; die defined $err_file ? "subdirmk: ${err_file}:$.: $m\n" : "subdirmk: $m\n"; } sub wrncore ($$) { my ($wk,$m) = @_; return 0 unless $warn_ena{$wk} // warn "internal error $wk ?"; $warned++; print STDERR "subdirmk: warning ($wk): $m\n"; return 1; } sub wrn ($$) { my ($wk,$m) = @_; our %warn_dedupe; return 0 if $warn_dedupe{$err_file,$.,$wk,$m}++; wrncore($wk, "${err_file}:$.: $m"); } sub ddbl_only ($) { my ($e) = @_; return if $ddbl; err "escape &$e is valid only during \$-doubling"; } sub process_input_mk ($$$$); sub process_input_mk ($$$$) { my ($targets, $f, $esclitr, $enoent_ok) = @_; my $caps_re = qr{[A-Z]}; my $lc_re = qr{[a-z]}; my $esc; my $set_esc = sub { $esc = $$esclitr; $esc =~ s/\W/\\$&/g; }; $set_esc->(); my $input = new IO::File $f, '<'; if (!$input) { err "open $f: $!" unless $!==ENOENT && $enoent_ok; return; } $input_files{$f}++; local $err_file=$f; my %srcdirmap = ( '^' => "\${top_srcdir}${dir_suffix}", '~' => "\${top_srcdir}", ); my %pfxmap = ( '' => $dir_prefix, ); $pfxmap{$_} = $srcdirmap{$_}.'/' foreach keys %srcdirmap; local $ddbl; my @nest = (['']); my $evalcall_brackets; my $push_nest = sub { my ($nk, $nndbl, $what) = @_; unshift @nest, [ $nk, $ddbl, $what, $. ]; $ddbl = $nndbl; }; my $pop_nest = sub { my ($nk) = @_; err "unexpectedly closed $nk in middle of $nest[0][0] ($nest[0][2])" unless $nest[0][0] eq $nk; $ddbl = (shift @nest)[1]; }; # Our detection of variable settings does not have to be completely # accurate, since it is only going to be used for advice to the user. my $note_varref = sub { my ($vn,$amp) = @_; my $exp = !!$varref_exp{$vn}{$amp}; $varref{$vn}{$exp}{$amp}{"$f:$."} = 1; }; while (<$input>) { if (m#^\s*($esc)?(\w+)\s*(?:=|\+=|\?=|:=)# || m#^\s*(?:$esc\:macro|define)\s+($esc)?(\S+)\s#) { $note_varref->($2,!!$1); } if (s#^\s*$esc\:changequote\s+(\S+)\s+$##) { $$esclitr = $1; $set_esc->(); next; } elsif (s#^\s*$esc\:endm\s+$##) { $pop_nest->('macro'); od "endef\n"; next; } elsif (s#^\s*$esc\:warn\s+(\S.*)$##) { foreach my $wk (split /\s+/, $1) { my $yes = $wk !~ s{^!}{}; if (defined $warn_ena{$wk}) { $warn_ena{$wk} = $yes; next; } elsif ($yes) { wrn 'unknown-warning', "unknown warning $wk requested"; } else { $warn_unk{$wk} //= "$f:$."; } } next; } elsif (s#^\s*$esc\:local\+global\s+(\S.*)$##) { foreach my $vn (split /\s+/, $1) { my $pos = !($vn =~ s{^!}{}); my $amp = $vn =~ s{^$esc}{}; $varref_exp{$vn}{!!$amp} = $pos; } next; } elsif (s#^\s*$esc\:(?=(-?)include|macro)##) { $buffering_output=''; } elsif (m#^\s*$esc\:([a-z][-+0-9a-z_]*)#) { err "unknown directive &:$1 or bad argumnt syntax"; } elsif (s{^\s*${esc}TARGETS(?:_([0-9a-zA-Z_]+))?(?=\W)}{}) { my $t = $1 // 'all'; my $vn = target_varname($var_prefix, $t); $note_varref->($vn,1); od $vn; $targets->{$t} //= [ ]; } for (;;) { err 'cannot $-double &-processed RHS of directive' if $ddbl && defined $buffering_output; unless ($nest[0][0] eq 'eval' ? s{^(.*?)($esc|\$|[{}])}{} : s{^(.*?)($esc|\$)}{}) { od $_; last; } od $1; if ($2 eq '{') { od $2; $evalcall_brackets++; next; } elsif ($2 eq '}') { od $2; next if --$evalcall_brackets; $pop_nest->('eval'); od '}'; next; } elsif ($2 eq '$') { od $2; if (s{^\$}{}) { od $&; } elsif (m{^[a-zA-Z]\w}) { wrn 'single-char-var', 'possibly confusing unbracketed single-char $-expansion'; } elsif (m{^$esc}) { wrn 'broken-var-ref', 'broken $&... expansion; you probably meant &$'; } elsif (m{^\(($esc)?([^()\$]+)\)} || m{^\{($esc)?([^{}\$]+)\}}) { $note_varref->($2,!!$1); } next; } if (s{^\\$esc}{}) { od "$$esclitr" } elsif (s{^:}{}) { od "$$esclitr:" } elsif (s{^\\\$}{}) { oud '$' } elsif (s{^\\\s+$}{}) { } elsif (s{^$esc}{}) { od "$$esclitr$$esclitr" } elsif (m{^(?=$caps_re)}) { od $var_prefix } elsif (s{^\$([A-Za-z]\w+)}{}) { $note_varref->($1,1); od "\${${var_prefix}$1}"; } elsif (s{^([~^]?)(?=$lc_re)}{}) { od $pfxmap{$1} } elsif (s{^_}{}) { od $var_prefix } elsif (s{^=}{}) { od $var_prefix_name } elsif (s{^([~^]?)/}{}) { od $pfxmap{$1} } elsif (s{^\.}{}) { od $dir_name } elsif (s{^([~^])\.}{}) { od $srcdirmap{$1} } elsif (s{^\$\-}{}) { $ddbl=undef; } elsif (s{^\$\+}{}) { $ddbl=1; } elsif (s{^\$\(}{}) { ddbl_only($&); oud "\$("; $note_varref->($2,!!$1) if m{^($esc)?([^()\$]+\))}; } elsif (s{^\$(\d+)}{}) { ddbl_only($&); oud "\${$1}"; } elsif (s{^\(\s*$esc(?=$lc_re)}{}) { od "\$(call ${var_prefix}" } elsif (s{^\(\s*(?=\S)}{} ) { od "\$(call " } elsif (s{^\{}{}) { err 'macro invocation cannot be re-$-doubled' if $ddbl; od '${eval ${call '; $evalcall_brackets = 1; $push_nest->('eval',1, '&{...}'); $note_varref->($2,!!$1) if m{^\s*($esc)?([^,{}\$]+)}; } elsif (s{^([~^]?)(?=[ \t])}{}) { my $prefix = $pfxmap{$1} // die "internal error ($1?)"; my $after=''; if (m{([ \t])$esc}) { ($_,$after) = ($`, $1.$'); } s{(?<=[ \t])(?=\S)(?!\\\s*$)}{$prefix}g; od $_; $_ = $after; } elsif (s{^\#}{}) { $_ = ''; } elsif (s{^![ \t]+}{}) { od $_; $_ = ''; } else { m{^.{0,5}}; err "bad &-escape \`$$esclitr$&'"; } } if (defined $buffering_output) { $_=$buffering_output; $buffering_output=undef; if (m#^(-?)include\s+(\S+)\s+$#) { my $subf = "$srcdir/$2"; process_input_mk($targets, $subf, $esclitr, $1); od "\n"; } elsif (m#^macro\s+(\S+)\s+$#) { od "define $1\n"; $push_nest->('macro', 1, '&:macro'); } else { err "bad directive argument syntax"; } } } die "subdirmk: $f:$nest[0][3]: unclosed $nest[0][0] ($nest[0][2])\n" if $nest[0][0]; $input->error and die "read $f: $!\n"; close $input or die "close $f: $!\n"; } sub filter_subdir_mk ($) { my ($targets) = @_; #use Data::Dumper; #print STDERR "filter @_\n"; my $esclit = '&'; my $pi = sub { my ($f, $enoentok) = @_; process_input_mk($targets, "${srcdir}/$f", \$esclit, $enoentok); }; $pi->("Prefix.sd.mk", 1); $pi->("${dir_prefix}Dir.sd.mk", 0); $pi->("Suffix.sd.mk", 1); } sub process_subtree ($$); sub process_subtree ($$) { # => list of targets (in form SUBDIR/) # recursive, children first my ($node, $path) = @_; #use Data::Dumper; #print STDERR Dumper(\@_); local %varref_exp; my $dir_prefix = dir_prefix($path); # ^ this is the only var which we need before we come back from # the recursion. push @output_makefiles, "${dir_prefix}Dir.mk"; write_makefile($dir_prefix, scalar @$path); my %targets = (all => []); foreach my $child (@{ $node->[1] }) { my @childpath = (@$path, $child->[0]); my $child_subdir = join '/', @childpath; mkdir $child_subdir or $!==EEXIST or die "mkdir $child_subdir: $!\n"; local %warn_ena = @warn_ena_dfl; push @{ $targets{$_} }, $child_subdir foreach process_subtree($child, \@childpath); } set_dir_vars($path); start_output_file("${dir_prefix}Dir.mk.tmp"); if ($node->[2]) { filter_subdir_mk(\%targets); } else { my $sdmk = "${dir_prefix}Dir.sd.mk"; if (stat $sdmk) { die "subdirmk: $sdmk unexpectedly exists (${dir_prefix} not mentioned on subdirmk/generate command line, maybe directory is missing from SUBDIRMK_SUBDIRS)"; } elsif ($!==ENOENT) { } else { die "stat $sdmk: $!\n"; } } oraw "\n"; my @targets = sort keys %targets; foreach my $target (@targets) { my $target_varname = target_varname($var_prefix, $target); oraw "${dir_prefix}${target}:: \$($target_varname)"; foreach my $child_subdir (@{ $targets{$target} }) { oraw " $child_subdir/$target"; } oraw "\n"; } if (@targets) { oraw ".PHONY:"; oraw " ${dir_prefix}${_}" foreach @targets; oraw "\n"; } return @targets; } sub process_final ($) { my ($otargets) = @_; set_dir_vars([]); push @output_makefiles, "Final.mk"; start_output_file("Final.mk.tmp"); my %ntargets; my $esclit='&'; process_input_mk(\%ntargets, "${srcdir}/Final.sd.mk", \$esclit, 1); delete $ntargets{$_} foreach @$otargets; my @ntargets = sort keys %ntargets; die "subdirmk: Final.sd.mk may not introduce new top-level targets". " (@ntargets)\n" if @ntargets; } sub process_tree() { my @targets = process_subtree($root, [ ]); process_final(\@targets); start_output_file("main.mk.tmp"); foreach my $v (qw(top_srcdir abs_top_srcdir)) { oraw "$v=\@$v@\n"; } oraw "SUBDIRMK_MAKEFILES :=\n"; oraw "MAKEFILE_TEMPLATES :=\n"; foreach my $mf (@output_makefiles) { oraw "SUBDIRMK_MAKEFILES += $mf\n"; } foreach my $input (sort keys %input_files) { oraw "MAKEFILE_TEMPLATES += $input\n"; } oraw "include \$(SUBDIRMK_MAKEFILES)\n"; } sub flmap ($) { local ($_) = @_; s{:(\d+)$}{ sprintf ":%10d", $1 }e; $_; } sub print_varref_warnings () { foreach my $vn (sort keys %varref) { my $vv = $varref{$vn}; next unless $vv->{''}{''} && $vv->{''}{1}; wrncore 'local+global', "saw both $vn and &$vn" or return; foreach my $exp ('', 1) { foreach my $amp ('', 1) { printf STDERR ($exp ? " expectedly saw %s%s at %s\n" : " saw %s%s at %s\n"), ($amp ? '&' : ''), $vn, $_ foreach sort { flmap($a) cmp flmap($b) } keys %{ $vv->{$exp}{$amp} }; } } } } sub print_warning_warnings () { return unless $warned; foreach my $wk (sort keys %warn_unk) { wrncore 'unknown-warning', "$warn_unk{$wk}: attempt to suppress unknown warning(s) \`$wk'"; } } build_tree(); process_tree(); print_varref_warnings(); print_warning_warnings(); install_output_files(); work/subdirmk/regen.mk.in0000664000000000000000000000572515063477206012631 0ustar # subdirmk - rules for regenerating makefiles etc. # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. # Usage: # include subdirmk/regen.mk # (probably in toplevel Dir.sd.mk) # # Arranges that config.status is automatically rerun to update # makefiles from templates, whenever a template *.sd.mk or *.mk.in is # edited; and that autoconf is rerun if configure's inputs are edited. # # If you add includes to configure.ac, add them to CONFIGURE_ACS. # # Makefiles updated by config.status and passed to SUBDIRMK_MAKEFILES # in configure.ac are automatically handled too. If you have other # files updated by config.status (eg, the output of autoheader) you # need to put them in CONFIG_STATUS_OUTPUTS (before your inclusion # of regen.mk). # # Also provides a `realclean::' target at the toplevel which deletes # the autoconf output. (This is suitable for being part of a recursive # target creaed by setting &TARGETS_realclean in appropriate .sd.mk.) CONFIGURE ?= configure CONFIGURE_AC ?= $(CONFIGURE).ac CONFIG_STATUS ?= config.status CONFIGURE_ACS += $(CONFIGURE_AC) CONFIGURE_ACS += subdirmk/subdirmk.ac # To turn on debugging here, export SUBDIRMK_REGEN_NDEBUG='' SUBDIRMK_REGEN_NDEBUG ?= @ $(top_srcdir)/$(CONFIGURE): $(addprefix $(top_srcdir)/,$(CONFIGURE_ACS)) cd $(top_srcdir) && autoconf $(CONFIG_STATUS): $(top_srcdir)/$(CONFIGURE) ./$(CONFIG_STATUS) --recheck # generate will add all its own inputs and outputs to these variables SUBDIRMK_MAKEFILES += @_SUBDIRMK_MAKEFILES@ MAKEFILE_TEMPLATES += $(addprefix $(top_srcdir)/, $(addsuffix .in, \ @_SUBDIRMK_MAKEFILES@ \ )) main.mk $(SUBDIRMK_MAKEFILES) $(CONFIG_STATUS_OUTPUTS): .makefiles.stamp $(SUBDIRMK_REGEN_NDEBUG): REGEN STAMP CAUSES TARGET=$@ .makefiles.stamp: \ $(top_srcdir)/subdirmk/generate \ $(CONFIG_STATUS) \ $(MAKEFILE_TEMPLATES) # This filtering arranges that we can often run config.status to # generate only particular output files. We look for *inputs* that # have changed. If the only inputs that have changed are ones that we # know affect only one output (Dir.sd.mk, Final.sd.mk and *.mk.in), # we pass config.status the corresponding output file names. # Otherwise we pass nothing and config.status does them all. We need # to mention Dir.sd.mk twice because if $(top_srcdir) is `.', make # elides the directory part from $?. Similarly but not identically # Final.sd.mk. $(SUBDIRMK_REGEN_NDEBUG): REGEN STAMP WANTS DEPS=$? ./$(CONFIG_STATUS) $(if \ $(filter-out Dir.sd.mk %/Dir.sd.mk \ Final.sd.mk $(top_srcdir)/Final.sd.mk \ %.mk.in \ , $?),, \ $(patsubst $(top_srcdir)/%,%, $(sort \ $(patsubst %.sd.mk,%.mk,$(filter %.sd.mk,$?)) \ $(patsubst %.mk.in,%.mk,$(filter %.mk.in,$?))))) touch $@ realclean:: clean $(RM) config.status config.log $(RM) main.mk $(SUBDIRMK_MAKEFILES) @_SUBDIRMK_MAKEFILES@ $(RM) $(addsuffix Makefile,$(dir $(SUBDIRMK_MAKEFILES))) -include $(ALL_DEPFILES) work/subdirmk/subdirmk.ac0000664000000000000000000000165115063477206012712 0ustar dnl -*-autoconf-*- dnl subdirmk - autoconf macros dnl Copyright 2019 Mark Wooding dnl Copyright 2019 Ian Jackson dnl SPDX-License-Identifier: LGPL-2.0-or-later dnl There is NO WARRANTY. _SUBDIRMK_MAKEFILES="" AC_SUBST([_SUBDIRMK_MAKEFILES]) AC_DEFUN([SUBDIRMK_SUBDIRS], [_SUBDIRMK_INIT m4_map_args_w([$1],[_SUBDIRMK_SUBDIR(],[/)])])dnl AC_DEFUN_ONCE([_SUBDIRMK_INIT],[ AC_CONFIG_FILES([ main.mk:main.mk.tmp Dir.mk:Dir.mk.tmp Final.mk:Final.mk.tmp ],[],[ '$srcdir'/subdirmk/generate --srcdir='$srcdir' $subdirmk_subdirs ]) SUBDIRMK_MAKEFILES([subdirmk/regen.mk subdirmk/usual.mk]) ]) AC_DEFUN([_SUBDIRMK_SUBDIR],[ subdirmk_subdirs="$subdirmk_subdirs '$1'" AC_CONFIG_FILES([$1Dir.mk:$1Dir.mk.tmp]) ]) AC_DEFUN([SUBDIRMK_MAKEFILES], [_SUBDIRMK_INIT m4_map_args_w([$1],[_SUBDIRMK_MAKEFILE(],[)])])dnl AC_DEFUN([_SUBDIRMK_MAKEFILE],[ _SUBDIRMK_MAKEFILES="$_SUBDIRMK_MAKEFILES $1" AC_CONFIG_FILES([$1:$1.in]) ]) work/subdirmk/tests/0000775000000000000000000000000015063477206011724 5ustar work/subdirmk/tests/.gitignore0000664000000000000000000000025515063477206013716 0ustar # subdirmk - part of the test suite # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. */log work/subdirmk/tests/advance-tested0000775000000000000000000000156415063477206014547 0ustar #!/bin/bash # subdirmk - test suite runner helper script # Copyright various contributors - see top level README. # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. set -e branch=$(git symbolic-ref -q HEAD || test $? = 1) base=$(git merge-base tested HEAD) git branch -D test-failed 2>&1 ||: case "$branch" in refs/heads/tested|refs/heads/test-failed) echo >&2 "unexpectedly on branch $branch"; exit 1 ;; refs/heads/*) branch=${branch#refs/heads/} ;; *) branch=''; esac restore-branch () { if [ "$branch" ]; then git checkout $branch; fi } git checkout --detach git clean -xdff if ! git rebase --exec 'tests/check && git branch -f tested' $base; then git branch -f test-failed git rebase --abort echo >&2 '^ ignore previous message from git-rebase!' echo >&2 'Test failed, made local branch ref test-failed' restore-branch exit 1 fi restore-branch work/subdirmk/tests/build-common0000664000000000000000000000077415063477206014244 0ustar # subdirmk - part of the test suite # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. SUBDIRMK_REGEN_NDEBUG='' export SUBDIRMK_REGEN_NDEBUG make_copy () { rm -rf tests/$1/example mkdir tests/$1/example git ls-files -z example \ | xargs -0 \ sh -xec 'rsync -R -l "$@" tests/'$1'/' x rm tests/$1/example/subdirmk git ls-files -z :. :!example \ | xargs -0 \ sh -xec 'rsync -R -l "$@" tests/'$1'/example/subdirmk' x } work/subdirmk/tests/check0000775000000000000000000000052715063477206012733 0ustar #!/bin/bash # subdirmk - toplevel invocation script for the test suite # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. set -e j=$(nproc 2>/dev/null || echo 1) j=$(( $j * 5 / 4 + 1 )) x () { echo "$@"; "$@"; } x ${MAKE-make} -f tests/tests.mk -j$j echo 'ok.' work/subdirmk/tests/example/0000775000000000000000000000000015063477206013357 5ustar work/subdirmk/tests/example/.gitignore0000664000000000000000000000025715063477206015353 0ustar # subdirmk - part of the test suite # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. example work/subdirmk/tests/example/check0000775000000000000000000000214615063477206014365 0ustar #!/bin/sh # subdirmk - part of the test suite # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. set -ex . tests/build-common make_copy example cd tests/example/example : ----- out of tree build ----- mkdir build cd build >>../src/for-test.sd.mk >>../lib/for-test.mk.in >>../for-test-final.sd.mk .././autogen.sh && ../configure make -j4 all check : ----- testing rebuild on input change ----- reset_times () { cd .. find ! -path './build/*' -type f -print0 \ | xargs -0 \ touch -hmd 'now -2000 seconds' -- cd build find -type f -print0 \ | xargs -0 \ touch -hmd 'now -1000 seconds' -- } : ----- for-check-1 ----- reset_times echo 'for-check-1:' >>../src/for-test.sd.mk make -j4 for-check-1 grep '^for-check-1:' src/Dir.mk || false : ----- for-check-2 ----- reset_times echo 'for-check-2:' >>../lib/for-test.mk.in make -j4 for-check-2 grep '^for-check-2:' lib/for-test.mk || false : ----- for-check-3 ----- reset_times echo 'for-check-3:' >>../for-test-final.sd.mk make -j4 for-check-3 grep '^for-check-3:' Final.mk echo ok. work/subdirmk/tests/filter/0000775000000000000000000000000015063477206013211 5ustar work/subdirmk/tests/filter/.gitignore0000664000000000000000000000032615063477206015202 0ustar # subdirmk - part of the test suite # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. Makefile *.tmp doctests.sd.mk doctests.mk.part work/subdirmk/tests/filter/Dir.mk.expected0000664000000000000000000000116415063477206016062 0ustar # autogenerated - do not edit # subdirmk - test cases for generate script # Copyright various contributors - see top level README. # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. # Prefix in . WARN += 3 TOP_WARN += 3 # $WARN # $(WARN) # $(TOP_WARN) # ${TOP_WARN} # ${TOP_NOWARN1} $(NOWARN1) # ${TOP_NOWARN2} $(NOWARN2) ${eval ${call some-macro, 42, $$x, { $(foreach something) } }} $TOP_FBAR # doctests: # Suffix in . all:: $(TOP_TARGETS) sub/all sometarget1:: $(TOP_TARGETS_sometarget1) sub/sometarget1 sometarget2:: $(TOP_TARGETS_sometarget2) sub/sometarget2 .PHONY: all sometarget1 sometarget2 work/subdirmk/tests/filter/Dir.sd.mk0000664000000000000000000000063715063477206014673 0ustar &# subdirmk - test cases for generate script &# Copyright various contributors - see top level README. &# SPDX-License-Identifier: LGPL-2.0-or-later &# There is NO WARRANTY. WARN += 3 &WARN += 3 # $WARN # $(WARN) # $(&WARN) # &$WARN &:local+global NOWARN1 &NOWARN2 # &$NOWARN1 $(NOWARN1) # &$NOWARN2 $(NOWARN2) &{ some-macro, 42, $x, { &$- $(foreach something) } } $&FBAR # doctests: &:include &doctests.sd.mk work/subdirmk/tests/filter/Final.mk.expected0000664000000000000000000000032215063477206016370 0ustar # autogenerated - do not edit # subdirmk - test cases for generate script # Copyright various contributors - see top level README. # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. # Final work/subdirmk/tests/filter/Final.sd.mk0000664000000000000000000000026615063477206015204 0ustar # subdirmk - test cases for generate script # Copyright various contributors - see top level README. # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. # Final &/ work/subdirmk/tests/filter/Prefix.sd.mk0000664000000000000000000000034315063477206015404 0ustar # subdirmk - test cases for generate script # Copyright various contributors - see top level README. # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. # Prefix in &. &:warn many-requests-for-unknown-warning work/subdirmk/tests/filter/Suffix.sd.mk0000664000000000000000000000027615063477206015420 0ustar &# subdirmk - test cases for generate script &# Copyright various contributors - see top level README. &# SPDX-License-Identifier: LGPL-2.0-or-later &# There is NO WARRANTY. # Suffix in &. work/subdirmk/tests/filter/check0000775000000000000000000000113615063477206014215 0ustar #!/bin/bash # subdirmk - part of the test suite # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. set -e set -o pipefail cd tests/filter expand <../../README | ./extract-doctests . >/dev/null set +e ../../generate sub/dir 2>stderr.tmp rc=$? set -e if [ $rc != 0 ]; then cat stderr.tmp; exit 1; fi ok=true files=$(find -name \*.expected) for f in $files; do i=$f o=$f.tmp sed <$i >$o ' /^# doctests:/ { r '"${f%/*}/doctests.mk.part"' a } ' diff -u $f.tmp ${f%.expected}.tmp || ok=false done $ok echo ok. work/subdirmk/tests/filter/extract-doctests0000775000000000000000000001131715063477206016442 0ustar #!/usr/bin/perl -w # subdirmk - script for extracting doctests from README # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. # # usage: # expand ) { # adhoc: rely on structure of indented examples in &:changequote part $in_changequote = (m{^\&\:changequote}...m{^\S}) && m{^\s}; if (m{^-----|^- - - - -}) { # parse: rely on underlines for (sub)section headings $csection = $lastl; next; } $lastl = $_; my $e = { L => $. }; # parse: rely on looking for => (and .. on subsequent lines) next unless m{\=\>} or ($cent and m{ \.\. }); my $mapop = '=>'; # adhoc: special case NEWQUOTE here so we recognise things in changequote if (s{^()(\&\:\w+(?: \S+)*)\s{2,}(\=\>)\s{2,}($withspcs)$}{} || s{^(\s*)(\&$withspcs|NEWQUOTE\S+|\$)\s+(\=\>|\.\.)\s+($withspcs)\s+}{} || $cent && s{^()($withspcs)\s{2,}(\.\.)\s{2,}($withspcs)$}{}) { # adhoc: expected indented iff in changequote part confess if length($1) xor $in_changequote; $mapop = $3; confess if !$cent && $mapop ne '=>'; $e->{In} = $2; $e->{Out} = $4; if (# adhoc: `or ...' introduces the `at toplevel' expansion s{^or ($withspcs)$}{}) { $e->{OutTop} = $1 eq 'nothing' ? '' : $1; } elsif (# parse: expect other wordish things to be comments m{^(?!or\b)\(?\w{2,} }) { } elsif (m/^$/) { } else { confess "unk rhs $_ (In=\"$e->{In}\" out=\"$e->{Out}\"?"; } $e->{CQ} = $in_changequote; # adhoc: rely on this specific section title $e->{DD} = $csection =~ m{^while dollar[- ]doubling}i; } else { confess "$_ ?"; } if ($mapop eq '=>') { if ($e->{In} =~ m/\bNN\b/) { # adhoc: special case NN in examples confess if defined $cent->{OutTop}; foreach my $nn (0..11, 999) { my $f = { %$e }; foreach my $k (qw(In Out)) { $f->{$k} = $e->{$k}; ($f->{$k} =~ s/\bNN\b/$nn/g) == 1 or confess; } push @exp, $f; } $cent=undef; } else { push @exp, $e; $cent=$e; } } elsif ($mapop eq '..') { confess if defined $cent->{OutTop}; foreach my $k (qw(In Out)) { $cent->{$k} .= "\n".$e->{$k}; } } } print Dumper(\@exp); sub oi { print I @_ or die $!; } sub oo { print O @_ or die $!; } sub oh { oi @_; oo @_; } sub write_permode ($$$$$;$$) { my ($dir_prefix, $start, $end, $senl, $what, $filter, $omap) = @_; $filter //= sub { 1 }; $omap //= sub { $_[0] }; oi $start; oh "${senl}# ----- $what starts -----\n"; foreach my $e (@exp) { next unless $filter->($e); my $desc = $e->{In}; $desc =~ s/\&/AMP /g; $desc =~ s/\$/DOLLAR /g; $desc =~ s/NEWQUOTE/NEW_QUOTE /g; my ($f,$pdesc) = $desc =~ m/^(.*)\n/ ? ("\n# %s:\n%s\n\n", $1) : ("%-30s: %s .\n", $desc); my $o; $o = $e->{OutTop} if $dir_prefix eq ''; $o //= $e->{Out}; $o =~ s{/sub/dir}{} if $dir_prefix eq '' && !defined $e->{OutTop}; $o = $omap->($o, $e); oi sprintf $f, $pdesc, $e->{In}; oo sprintf $f, $pdesc, $o; } oi $end; oh "${senl}# ----- $what ends -----\n"; } sub writeout ($) { my ($dir_prefix) = @_; open I, '>', "$outdir/${dir_prefix}doctests.sd.mk" or die $!; open O, '>', "$outdir/${dir_prefix}doctests.mk.part" or die $!; oh "# doctests start $dir_prefix\n"; write_permode($dir_prefix, '','','', 'normal', sub { !$_[0]{DD} && !$_[0]{CQ} } ); write_permode($dir_prefix, '&$+', '&$-', "\n", 'dollar doubling', sub { my ($e) = @_; # adhoc: skip &:macro in already-doubling part return 0 if $e->{In} =~ m{^\&\:macro}; # adhoc: skip &${ ie eval in already-doubling part return 0 if $e->{In} =~ m{^\&\{}; return 0 if $e->{CQ}; return $e->{DD} || !grep { # If there are two entries with the same In, # use only the one from the `while dollar # doubling' section. So entries there override # entries in the rest o the file. $_ ne $e && $_->{In} eq $e->{In} } @exp; }, sub { $_=$_[0]; s/\$/\$\$/g unless $_[1]{DD}; $_; } ); write_permode($dir_prefix, "&:changequote NEWQUOTE\n", "NEWQUOTE:changequote &\n", "", 'changequote', sub { $_[0]{CQ} } ); oh "# doctests end\n"; close I or die $!; } writeout(''); writeout('sub/dir/'); work/subdirmk/tests/filter/main.mk.expected0000664000000000000000000000106215063477206016265 0ustar # autogenerated - do not edit top_srcdir=@top_srcdir@ abs_top_srcdir=@abs_top_srcdir@ SUBDIRMK_MAKEFILES := MAKEFILE_TEMPLATES := SUBDIRMK_MAKEFILES += Dir.mk SUBDIRMK_MAKEFILES += sub/Dir.mk SUBDIRMK_MAKEFILES += sub/dir/Dir.mk SUBDIRMK_MAKEFILES += Final.mk MAKEFILE_TEMPLATES += ./Dir.sd.mk MAKEFILE_TEMPLATES += ./Final.sd.mk MAKEFILE_TEMPLATES += ./Prefix.sd.mk MAKEFILE_TEMPLATES += ./Suffix.sd.mk MAKEFILE_TEMPLATES += ./doctests.sd.mk MAKEFILE_TEMPLATES += ./sub/dir/Dir.sd.mk MAKEFILE_TEMPLATES += ./sub/dir/doctests.sd.mk include $(SUBDIRMK_MAKEFILES) work/subdirmk/tests/filter/stderr.expected0000664000000000000000000000176315063477206016246 0ustar subdirmk: warning (unknown-warning): ./Prefix.sd.mk:7: unknown warning many-requests-for-unknown-warning requested subdirmk: warning (single-char-var): ./Dir.sd.mk:8: possibly confusing unbracketed single-char $-expansion subdirmk: warning (broken-var-ref): ./Dir.sd.mk:19: broken $&... expansion; you probably meant &$ subdirmk: warning (local+global): saw both NOWARN1 and &NOWARN1 saw NOWARN1 at ./sub/dir/Dir.sd.mk:24 saw &NOWARN1 at ./Dir.sd.mk:14 expectedly saw NOWARN1 at ./Dir.sd.mk:14 subdirmk: warning (local+global): saw both WARN and &WARN saw WARN at ./Dir.sd.mk:6 saw WARN at ./Dir.sd.mk:9 saw WARN at ./sub/dir/Dir.sd.mk:19 saw WARN at ./sub/dir/Dir.sd.mk:22 saw &WARN at ./Dir.sd.mk:7 saw &WARN at ./Dir.sd.mk:10 saw &WARN at ./Dir.sd.mk:11 saw &WARN at ./sub/dir/Dir.sd.mk:18 saw &WARN at ./sub/dir/Dir.sd.mk:27 expectedly saw &WARN at ./sub/dir/Dir.sd.mk:21 subdirmk: warning (unknown-warning): ./sub/dir/Dir.sd.mk:6: attempt to suppress unknown warning(s) `some-unknown-warning' work/subdirmk/tests/filter/sub/0000775000000000000000000000000015063477206014002 5ustar work/subdirmk/tests/filter/sub/Dir.mk.expected0000664000000000000000000000036615063477206016656 0ustar # autogenerated - do not edit sub/all:: $(sub_TARGETS) sub/dir/all sub/sometarget1:: $(sub_TARGETS_sometarget1) sub/dir/sometarget1 sub/sometarget2:: $(sub_TARGETS_sometarget2) sub/dir/sometarget2 .PHONY: sub/all sub/sometarget1 sub/sometarget2 work/subdirmk/tests/filter/sub/dir/0000775000000000000000000000000015063477206014560 5ustar work/subdirmk/tests/filter/sub/dir/Dir.mk.expected0000664000000000000000000000126715063477206017435 0ustar # autogenerated - do not edit # subdirmk - test cases for generate script # Copyright various contributors - see top level README. # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. # Prefix in sub/dir sub/dir/ # sub_dir_TARGETS_notarget += 42 sub_dir_TARGETS_sometarget1 sub_dir_TARGETS_sometarget2 line joining sub_dir_WARN += 4 WARN += 4 sub_dir_WARN += 5 # this warning suppressed, precisely WARN += 5 $(NOWARN1) sub_dir_WARN += 6 # doctests: # Suffix in sub/dir sub/dir/all:: $(sub_dir_TARGETS) sub/dir/sometarget1:: $(sub_dir_TARGETS_sometarget1) sub/dir/sometarget2:: $(sub_dir_TARGETS_sometarget2) .PHONY: sub/dir/all sub/dir/sometarget1 sub/dir/sometarget2 work/subdirmk/tests/filter/sub/dir/Dir.sd.mk0000664000000000000000000000075115063477206016237 0ustar &# subdirmk - subdirectory test cases &# Copyright various contributors - see top level README. &# SPDX-License-Identifier: LGPL-2.0-or-later &# There is NO WARRANTY. &:warn !some-unknown-warning &:changequote & &/ # &TARGETS_notarget += 42 &TARGETS_sometarget1 &TARGETS_sometarget2 line &\ joining &WARN += 4 WARN += 4 &:local+global &WARN &WARN += 5 # this warning suppressed, precisely WARN += 5 $(NOWARN1) &:local+global !&WARN &WARN += 6 # doctests: &:include &doctests.sd.mk work/subdirmk/tests/filter/update-expected0000775000000000000000000000114115063477206016215 0ustar #!/bin/sh # subdirmk - part of the test suite # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. # # Usual approach to updating the expected outputs is # tests/filter/check # tests/filter/update-expected # selectively git-add the things that are right, after inspecting them set -e files=$(find tests/filter -name \*.expected.tmp) for f in $files; do perl -pe ' (s/\n//, $stripnl=0) if $stripnl; next unless /^# doctests start/../^# doctests end/; $_=""; $stripnl=1; ' \ <${f%.expected.tmp}.tmp >${f%.tmp} done work/subdirmk/tests/intree/0000775000000000000000000000000015063477206013212 5ustar work/subdirmk/tests/intree/.gitignore0000664000000000000000000000025715063477206015206 0ustar # subdirmk - part of the test suite # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. example work/subdirmk/tests/intree/check0000775000000000000000000000052615063477206014220 0ustar #!/bin/sh # subdirmk - part of the test suite # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. set -ex . tests/build-common make_copy intree cd tests/intree/example ./autogen.sh && ./configure make -j4 all check make -j4 clean make -j4 all check echo ok. work/subdirmk/tests/make-release0000775000000000000000000000165715063477206014216 0ustar #!/bin/sh # subdirmk - release script # Copyright various contributors - see top level README. # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. set -e fail () { echo >&2 "error: $*"; $dryrun exit 1; } case "$1" in -n) dryrun=: ; shift ;; -*) fail "unknown option $1" ;; esac x () { echo >&2 " $*"; $dryrun "$@"; } head=$(git rev-parse HEAD~0) for branch in master tested; do bv=$(git rev-parse refs/heads/$branch) test $bv = $head || fail "error: HEAD=$head, $branch=$bv" done status=$(git status --porcelain --ignored) if [ "$status" ]; then printf >&2 '%s\n' "$status" fail 'tree not sufficiently clean' fi v="$1" case "$v" in subdirmk/*) v=${v#subdirmk/} ;; esac case "$v" in [0-9]*.*) ;; *) fail 'bad version' ;; esac tag=subdirmk/$v key=0x559AE46C2D6B6D3265E7CBA1E3E3392348B50D39 export GPG_TTY=`tty` # wtf x git tag -s -u $key -m "subdirmk $v" $tag x git push origin master $tag $dryrun echo 'done.' work/subdirmk/tests/tests.mk0000664000000000000000000000052015063477206013414 0ustar # subdirmk - part of the test suite # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. TESTS=$(wildcard tests/*/check) all: $(addsuffix .done, $(TESTS)) .PHONY: tests/%/check.done all tests/%/check.done: tests/$*/check >tests/$*/log 2>&1 @echo $* ok. work/subdirmk/usual.mk.in0000664000000000000000000000126715063477206012657 0ustar # subdirmk - usual variable settings # Copyright 2019 Mark Wooding # Copyright 2019 Ian Jackson # SPDX-License-Identifier: LGPL-2.0-or-later # There is NO WARRANTY. # Usage: # include subdirmk/usual.mk # (probably in toplevel Dir.sd.mk) # # Provides various conventional `make' variables, and a # rule for compiling C programs. VPATH = $(top_srcdir) prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ CC ?= @CC@ CFLAGS ?= @CFLAGS@ DEFS ?= @DEFS@ INCLUDES ?= @INCLUDES@ LD ?= @CC@ LDFLAGS ?= @LDFLAGS@ LIBS ?= @LIBS@ LINK ?= $(CC) -o$@ $(CFLAGS) $(LDFLAGS) AR ?= ar COMPILE ?= $(CC) -c -o$@ $(CDEPS_CFLAGS) $(DEFS) $(INCLUDES) $(CFLAGS) %.o: %.c $(COMPILE) $< work/test-common.sd.mk0000664000000000000000000000172415063477206012151 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. include common.make &TESTSCRIPTS ?= $(wildcard &^/t-[a-z]*[0-9a-z]) ifneq ($(OLD_SECNET_DIR),) &TESTSCRIPTS += $(wildcard &^/t-C*[0-9a-z]) endif &TESTNAMES := $(patsubst t-%,%,$(notdir $(&TESTSCRIPTS))) &DEPS += $(src)/test-common.tcl &DEPS += common.make &DEPS += $(src)/test-common.sd.mk &DEPS += &/Dir.mk &check-real: $(foreach t,$(&TESTNAMES),&d-$t/ok) RECHECK_RM += &d-* CHECK_SILENT ?= @ &d-%/ok: &^/t-% $(&DEPS) $(CHECK_SILENT) rm -rf &d-$*; mkdir &d-$* $(CHECK_SILENT) export SECNET_TEST_BUILDDIR=$(topbuilddir); \ export PYTHONBYTECODEBASE=/dev/null; \ cd $(src) && \ &/t-$* >$(topbuilddir)/&/d-$*/log 2>&\&1 \ || { cat $(topbuilddir)/&/d-$*/log >&\&2; false; } $(CHECK_SILENT) printf "&/$* " $(CHECK_SILENT) touch $@ &CLEAN += & *.so &clean:: $(RM) -rf & tmp $(RM) -rf & d-* work/test-common.tcl0000664000000000000000000000135615063477206011720 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. proc prefix_some_path {pathvar entry} { global env set l {} catch { set l [split $env($pathvar) :] } set l [concat [list $entry] $l] set env($pathvar) [join $l :] } proc prexec {args} { puts "exec $args" eval exec $args } if {![catch { set builddir $env(SECNET_TEST_BUILDDIR) }]} {} else { set builddir . } if {![catch { set tmp $env(AUTOPKGTEST_ARTIACTS) }]} {} elseif {![catch { set tmp $env(AUTOPKGTEST_TMP) }]} {} elseif {[regsub {^(?:\./)?([sm]test)/t-} $argv0 {\1/d-} tmp]} { set tmp $builddir/$tmp file mkdir $tmp } work/test-example/0000775000000000000000000000000015063477206011352 5ustar work/test-example/Dir.sd.mk0000664000000000000000000000177115063477206013034 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. &TARGETS += & sites.conf sites-nonego.conf include common.make &/%.key: &^/%.key.b64 base64 -d <$< >$@.new && mv -f $@.new $@ &sites-nonego.conf: $(src)/make-secnet-sites &^/sites &/Dir.mk $(src)/make-secnet-sites --output-version=1 &^/sites $@ &sites.conf: $(src)/make-secnet-sites &^/sites &/Dir.mk mkdir -p &pubkeys &~/make-secnet-sites --pubkeys-dir=&pubkeys --pubkeys-install \ &^/sites $@.tmp && mv -f $@.tmp $@ &clean:: rm -rf &pubkeys &:macro &privkey &/&$1.privkeys/priv.&$2: &/&$3 mkdir -p $(dir $@) && cp $< $@.tmp && mv -f $@.tmp $@ &PRIVKEYS += &/&$3 &/&$1.privkeys/priv.&$2 &clean:: rm -rf &/&$1.privkeys &:endm &{&privkey,outside,5dc36a4700,rsa1-sites2.key} &{&privkey,outside,0000000000,outside.key} &{&privkey,inside,0000000000,inside.key} &all-privkeys:: $(&PRIVKEYS) &TARGETS += $(&PRIVKEYS) &CLEAN += *.new work/test-example/README0000664000000000000000000000402215063477206012230 0ustar This directory contains some files useful for ad-hoc tests. With these it is possible to run a test of secnet on a Linux host even if that Linux host does not have another working network connection. The keys here are (obviously) public. They were generated like this: ssh-keygen -C inside@example.com -f test-example/inside.key -t rsa1 -b 1024 ssh-keygen -C outside@example.com -f test-example/outside.key -t rsa1 -b 1024 # edit sites to paste {inside,outside}.key.pub into pubkey lines base64 inside.key.b64 base64 outside.key.b64 To run the test: Run the makefile: make -C test-example/ In one window, as root ./secnet -dvnc test-example/inside.conf And in another ./secnet -dvnc test-example/outside.conf Then in a third ping -I secnet-test-i 172.18.232.2 For running under valgrind memcheck, do something like this: valgrind --num-callers=40 --gen-suppressions=yes --db-attach=yes \ --leak-check=full --suppressions=test-example/memcheck.suppressions \ ./secnet -dvnc test-example/outside.conf NB that --num-callers is needed as secnet's stack can be deep. The config file outside-unshare.conf can be used on Linux in conjunction with test-example/fake-userv and a built checkout of userv-utils.git to run the "outside" copy of secnet in a new "network namespace". Everything in this directory is part of secnet. See README (in the directory above) for full list of copyright holders. secnet 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. secnet 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 version 3 along with secnet; if not, see https://www.gnu.org/licenses/gpl.html. work/test-example/bogus-setup-request.c0000664000000000000000000000614415063477206015466 0ustar /* * This file is part of secnet. * See LICENCE and this file CREDITS for full list of copyright holders. * SPDX-License-Identifier: GPL-3.0-or-later * There is NO WARRANTY. */ /* test-example/bogus-setup-request 127.0.0.1 19098 test-example/inside/inside 127.0.0.1 16096 test-example/outside/outside */ #include #include #include #include #include #include #include #include #include /* | 00000 00 00 00 00 00 00 00 01 01 01 01 01 00 1a 74 65 ........ ......te | ~~~~~~~~~~~ ~~~~~~~~~~~ ~~~~~~~~~~~ ~~~~~|~~~~~ sessionid sender's type sender's zero in index fixed for name msg1 msg1 | 00010 73 74 2d 65 78 61 6d 70 6c 65 2f 69 6e 73 69 64 st-examp le/insid | | 00020 65 2f 69 6e 73 69 64 65 00 1c 74 65 73 74 2d 65 e/inside ..test-e | ~~~~~|~~~~~~~~~~~~~~~~~ recipient's name | 00030 78 61 6d 70 6c 65 2f 6f 75 74 73 69 64 65 2f 6f xample/o utside/o | | 00040 75 74 73 69 64 65 8d f0 3f 35 d6 c8 1f c0 utside.. ?5.... | ~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ sender's nonce */ typedef struct { const char *name; union { struct sockaddr sa; struct sockaddr_in sin; }; } Ep; static void endaddr(Ep *ep, char **argv, int base) { int r; ep->sin.sin_family=AF_INET; r=inet_aton(argv[base],&ep->sin.sin_addr); assert(r); ep->sin.sin_port=htons(atoi(argv[base+1])); ep->name=argv[base+2]; } static void endname(uint8_t **msgp, const Ep *ep) { int l=strlen(ep->name); assert(l<=65535); *(*msgp)++ = l>>8; *(*msgp)++ = l; memcpy(*msgp, ep->name, l); *msgp += l; } static Ep us, them; int main(int argc, char **argv) { int r; assert(argc==7); endaddr(&us,argv,1); endaddr(&them,argv,4); static const uint8_t mprefix[]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, }; static const uint8_t msuffix[]={ /* our nonce, fixed he he */ 0x8d, 0xf0, 0x3f, 0x35, 0xd6, 0xc8, 0x1f, 0xc0 }; int msglen= (sizeof(mprefix) + 2+strlen(us.name) + 2+strlen(them.name) + sizeof(msuffix)); uint8_t msg[msglen]; uint8_t *msgp=msg; #define PREFIXSUFFIX(prefixsuffix) do { \ memcpy(msgp,prefixsuffix,sizeof(prefixsuffix)); \ msgp += sizeof(prefixsuffix); \ }while(0) PREFIXSUFFIX(mprefix); endname(&msgp,&us); endname(&msgp,&them); PREFIXSUFFIX(msuffix); assert(msgp == msg+msglen); struct protoent *proto=getprotobyname("udp"); int fd=socket(AF_INET, SOCK_DGRAM, proto->p_proto); r=bind(fd,&us.sa,sizeof(us.sin)); if (r) { perror("bind us2"); exit(1); } for (;;) { r=sendto(fd,msg,msglen,0,&them.sa,sizeof(them.sin)); if (r < 0) perror("sendto"); r=getchar(); if (r==EOF) { if (ferror(stdin)) { perror("getchar"); exit(1); } break; } if (r!='\n') break; } exit(0); } work/test-example/common.conf0000664000000000000000000000077315063477206013520 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. log logfile { filename "/dev/tty"; class "info","notice","warning","error","security","fatal"; }; system { userid "secnet"; }; resolver adns { }; log-events "all"; random randomfile("/dev/urandom",no); transform eax-serpent { }, serpent256-cbc { }; include test-example/sites.conf sites map(site,vpn/Vtest-example/all-sites); work/test-example/fake-userv0000775000000000000000000000063315063477206013352 0ustar #!/bin/sh # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. set -e echo >&2 "$0: invoked as $0 $*" shift shift exec 3<&0 4>&1 5>&2 >&2 &4 2>&5 & sleep 0.1 env - bash -i ' x "$@" work/test-example/inside-polypath.conf0000664000000000000000000000133115063477206015330 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. comm polypath { buffer sysbuffer(4096); monitor-command "./polypath-interface-monitor-linux"; interfaces "!secnet-test*"; permit-loopback True; }; netlink tun { name "netlink-tun"; # Printed in log messages from this netlink local-address "172.18.232.9"; secnet-address "172.18.232.10"; remote-networks "172.18.232.0/28"; mtu 1400; buffer sysbuffer(2048); interface "secnet-test-i"; }; local-name "test-example/inside/inside"; local-key rsa-private("test-example/inside.key"); local-mobile True; mtu-target 1260; include test-example/common.conf work/test-example/inside.conf0000664000000000000000000000077115063477206013501 0ustar netlink tun { name "netlink-tun"; # Printed in log messages from this netlink local-address "172.18.232.9"; secnet-address "172.18.232.10"; remote-networks "172.18.232.0/28"; mtu 1400; buffer sysbuffer(2048); interface "secnet-test-i"; }; comm udp { port 16913; buffer sysbuffer(4096); }, udp { port 16910; buffer sysbuffer(4096); }; local-name "test-example/inside/inside"; local-key rsa-private("test-example/inside.key"); local-mobile True; mtu-target 1260; include test-example/common.conf work/test-example/inside.key.b640000664000000000000000000000132615063477206013733 0ustar U1NIIFBSSVZBVEUgS0VZIEZJTEUgRk9STUFUIDEuMQoAAAAAAAAAAAQABAC5N9rmU46hhdLO1FVh Efkc9cq+x/UdC/a+nt0yM4HswxfChfJpcHq008Hkd4KOqRZORG7N5Q8fKPpkrnt3T3qSDX4P5HOW 5Q+2Qc82h1hO4mDbHo2xqmp4hv/88fHgPQTW9MffriDFs24HTt7uOqvx5LNtdmrw5ws6cXuyLwan lQARAQABAAAAEmluc2lkZUBleGFtcGxlLmNvbe8z7zMD/1/rgT3PAAq+V1ItvJmsySoqUHlE7LfC PmKxuzQIYLzQvDlNTSE10xZapAtBqSdggeC+p/ORMKeefS4u/lnnmz2tW9TlbtwWfj5Bwm/ftUZR 8BhelZQn5+/vTv1jLZ9dibLhemd20XxpMRIoOg+1w4xfbh1DoJbqs8OCCPPnNVJxAf9h3Hq0x84a P0JOgyFFNatWcRKVJxapseeZPnpIAnkaDZ0KirE1RZFkHbfL4HFL3kI3MI657rE7rSC2yakvJtX9 AgDD98/vGKw19bSdM8dHbocQQdDmn3SG5U9psbkvNQh06seKNL9QOeH2iHqjzBXmwTjPiWphdsVP dFOBy1VE52YPAgDx9QU0xrSytFrjcqlP/FICaBiuJ9g0t4RbYBcm2iZaXLwXLDTX91arNJJrzblX 9yMkHDBDw9j1nKXnig+8dtwbAAAAAA== work/test-example/inside.key.pub0000664000000000000000000000052415063477206014125 0ustar 1024 65537 130064631890186713927887504218626486455931306300999583387009075747001546036643522074275473238061323169592347601185592753550279410171535737146240085267000508853176463710554801101055212967131924064664249613912656320653505750073021702169423354903540699008756137338575553686987244488914481168225136440872431691669 inside@example.com work/test-example/memcheck.suppressions0000664000000000000000000000024615063477206015627 0ustar { secnet_read_conffile Memcheck:Leak ... fun:read_conffile fun:main } { secnet_enter_phase Memcheck:Leak ... fun:enter_phase fun:main } work/test-example/null-fake-userv0000775000000000000000000000152615063477206014324 0ustar #!/bin/sh # # stunt userv-ipif standin which discards sent packets and receives nothing # # This file is part of secnet. # See README for full list of copyright holders. # # secnet 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. # # secnet 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 # version 3 along with secnet; if not, see # https://www.gnu.org/licenses/gpl.html. set -e printf \\300 exec cat 2>&1 >/dev/null work/test-example/outside-random.conf0000664000000000000000000000115615063477206015156 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. netlink userv-ipif { name "netlink-ipif"; # Printed in log messages from this netlink local-address "172.18.232.1"; secnet-address "172.18.232.2"; remote-networks "172.18.232.0/28"; mtu 1400; buffer sysbuffer(2048); userv-path "test-example/random-fake-userv"; }; comm udp { port 16900; buffer sysbuffer(4096); }; local-name "test-example/outside/outside"; local-key rsa-private("test-example/outside.key"); include test-example/common.conf work/test-example/outside-unshare.conf0000664000000000000000000000114715063477206015343 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. netlink userv-ipif { name "netlink-ipif"; # Printed in log messages from this netlink local-address "172.18.232.1"; secnet-address "172.18.232.2"; remote-networks "172.18.232.0/28"; mtu 1400; buffer sysbuffer(2048); userv-path "test-example/fake-userv"; }; comm udp { port 16900; buffer sysbuffer(4096); }; local-name "test-example/outside/outside"; local-key rsa-private("test-example/outside.key"); include test-example/common.conf work/test-example/outside.conf0000664000000000000000000000112415063477206013673 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. netlink tun { name "netlink-tun"; # Printed in log messages from this netlink local-address "172.18.232.1"; secnet-address "172.18.232.2"; remote-networks "172.18.232.0/28"; mtu 1400; buffer sysbuffer(2048); interface "secnet-test-o"; }; comm udp { port 16900; buffer sysbuffer(4096); }; local-name "test-example/outside/outside"; local-key rsa-private("test-example/outside.key"); include test-example/common.conf work/test-example/outside.key.b640000664000000000000000000000132615063477206014134 0ustar U1NIIFBSSVZBVEUgS0VZIEZJTEUgRk9STUFUIDEuMQoAAAAAAAAAAAQABAC4D2q3B/nZUjsGMX72 5FrgEB1y0uYS732QF/NXOEs9FA8/xmM68NF8JRfCctlCm9kQ9t/0xW+wOQTNg0BFIdgbZjXIwXLy K9rreM1G1BsTjROtiz1UyjZMpo3Z89SWjtYCVN/UldRhakw/o0vrEKkZDTxiryhhYCGDUkONNsa5 1QARAQABAAAAE291dHNpZGVAZXhhbXBsZS5jb23IlsiWA/9AO6kbPN5VmBvfGnDbim+oWBde1fjS zN895Q3X915Sb2iu8fX5QMdqkqtLAbeORkMnZ3BaxHgowI1Lhy1rstbuiUcd3WWB6xUDcQll85Cy +2IFfvFDKH7HsrzxgWx9M23WewlTje2NmVF0Y3xR39w2jUCLuEcyaWdPPQiLTucCgQH/axUYwPI5 6QEKPiONve88GpehGCjereP5EjWTJomjQI+brOhnPckiWLwXXtWZoa894jpbVT8BtHNdDUg2gPFV pwIA0FQowUgwxCnCoNZe/v/K5zwP3ar8OPoBV2c8rnEuZ2sR0AdLcBpaCpOQf7LKk9p+GUOHlMJy hkrz7tAitvXEdQIA4i2dOA/PVYD6ZCZrwY5SToBmVtOzt2TVdhLbB/XDJ91ydl0uDdyN0Sn/Dyx+ I55YwyhLA8zNV8mL4ZQS8OLz4QAAAAA= work/test-example/outside.key.pub0000664000000000000000000000052515063477206014327 0ustar 1024 65537 129251483458784900555621175262818292872587807329014927540074484804119474262261383244074013537736576331652560727149001626325243856012659665194546933097292703586821422085819615124517093786704646988649444946154384037948502112302285511195679291084694375811092516151263088200304199780052361048758446082354317801941 outside@example.com work/test-example/random-fake-userv0000775000000000000000000000313315063477206014626 0ustar #!/usr/bin/perl -w # # stunt userv-ipif standin which generates random data # # This file is part of secnet. # See README for full list of copyright holders. # # secnet 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. # # secnet 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 # version 3 along with secnet; if not, see # https://www.gnu.org/licenses/gpl.html. use strict; use POSIX; open R, '/dev/urandom' or die $!; system 'cat >/dev/null &'; sub randbytes ($) { my ($count) = @_; my $s; my $r = read R, $s, $count; die $! unless $r==$count; return $s; } sub randbyteval () { my $b = randbytes 1; my ($r) = unpack 'C', $b; return $r; } sub randvalue ($$) { my ($min,$maxplus1) = @_; my $b = randbyteval; return floor(($b/256.0) * ($maxplus1-$min)) + $min; } for (;;) { my $lenbits = randvalue 0,14; my $len= (randbyteval << 8) | randbyteval; $len &= (1 << $lenbits)-1; my $data = randbytes $len; if (randbyteval >= 0x80) { $data =~ s{[\xc0\xdb]}{ $& eq "\xc0" ? "\xcb\xdc" : $& eq "\xdb" ? "\xcb\xdd" : die }ge; } print "\xc0"; print $data; STDOUT->flush; } work/test-example/rsa1-sites2.key.b640000664000000000000000000000244615063477206014541 0ustar U1NIIFBSSVZBVEUgS0VZIEZJTEUgRk9STUFUIDEuMQoAAAAAAAAAAAgACAC+8TCcl8qe3rzdNgTT UD/CTftxO+MZqUknkxvI26AA0NBjaYhtfCU2R281I+NtT48a99K7ByZ0+R4UHfWjaaeddfory2Q1 bSL/JPmJ524vdnTp/jIr4mYEql4QBvy1q7x8giKXpjs3NTpFWD5rLsCN+dkmCkkMXk4UqS1lwJyL uyddzyDLZTp//tVQNra6ZhHbEuF3wn/b+A66CAGjHTNEhMS/dxz4NlQ4P8oKUgw0ajo/H6W/W0gt XCOPxqIA9mByLWL6L+TXM3H4dmyEORSVPrstCv0v/32E7O8xVCyrp7r2bbTtZD+8gO+IMll76HiO 49YwrAsnmHVWhREZbe3PABEBAAEAAAAKaWFuQHplYWxvdI+rj6sH/jzebny89t7GaraqLcefYXPz oCBs0uyvRp7TCpxxRwR7jLLkRZWBaetvwkfSdQG3xwr1zbvJf7zyXWBUXKYHXGOZ5HJpmDxf2jQz +Ui4+isvO/4MBrpupc7+8JVzZMQVRzT510U4vM/QrA7HHr7UXJXl/A9gYAENXx6+/7XcPQtdxSgZ LB3fDKh7ac9RIELrujM3RaInVTW1RuoKjxmza/ZRb8OGgrZCFXvDMvzaveGD06LSumuVOc35Gy/i pCfhyZU5ZKKCq99NQmbDMzVuZXhgbkFj6I5Hfn0K7sV9BI9wii/7a3wfqmSfQf6/ZyDcjxF1HFWb aZm39RD+1o8XwIED/AudMGNYF3auLToQDhfTbB1n2qni+88Mtjb1K0SetQQLRXEXRLzaACE4zU3l +v78nKRhi+wV+W+UedVBYbCZaT6vY/Akllpc1f7TfLXiQ64vFWPzd4OgOXoZGFVNyag5r4Jp6Isw pagk3s9Js1lu1zGeZy63RTIzMw4JJVRTJhT9BADZlMJg+cn+eO09a/5LNaCVY3nCUji3GUSkhqrQ 8JyZG53o6zwyKnwNFt4x8VvJ4XzNSlNb2SIcd6SFQ6baNns2XO6milV7D75D47TB3w/cTodla9iH pPwDfPnR58QaDKR7SrGFsLzQr0RMANQPv0S6cKQR8mpp6MVloJMdx8cVkQQA4KhHqVVEpRH/eqVM qv288awsDh8OeQ0S1mIB3PpsiPvKsv0Udnvp36Om6ksVAZ9Rvjzq6Dwyu5/rXOaLdrDfO6Rg4ERv b31VsTF1hhKwBY/SW8LWSf3m19ZUgehrbydntRoYSc7fhiI+XwgcY+g3hUlH+p2K6lxDRO2gTHOj nV8AAAAA work/test-example/rsa1-sites2.key.pub0000664000000000000000000000120015063477206014717 0ustar 2048 65537 24104213110797824055140161151913051305999303744244245538616867433010123928547772585164857844773599715378140515394197403325476306321618029548629773764525038084664382384465558827523669858194978897965295333256643190607316924148587954891292549882502404695138437562562382196324392976634799923334183631987978068608255623637553440025606960971733876084111830783382626819339190125655665183104355565691970919273870624728431953156654768349323549801247517262721074491393358334108678866178248743405367462158492230242408187673473228900258799327086061288180749147397116786858491515534054986740592092556131569932455039077414318501327 ian@zealot work/test-example/sites0000664000000000000000000000446415063477206012434 0ustar # This file is part of secnet. # See LICENCE and this file CREDITS for full list of copyright holders. # SPDX-License-Identifier: GPL-3.0-or-later # There is NO WARRANTY. vpn test-example contact devnull@example.com dh 8db5f2c15ac96d9f3382d1ef4688fba14dc7908ae7dfd71a9cfe7f479a75d506dc53f159aeaf488bde073fe544bc91c099f101fcf60074f30c06e36263c03ca9e07931ce3fc235fe1171dc6d9316fb097bd4362891e2c36e234e7c16b038fd97b1f165c710e90537de66ee4f54001f5712b050d4e07de3fba07607b19b64f6c3 2 hash sha1 key-lifetime 72000000 restrict-nets 172.18.232.0/28 setup-timeout 2000 setup-retries 5 location out root site outside networks 172.18.232.0/29 peer 172.18.232.1 address [::1] 16900 serial 5dcfe8e9 pkg 5dc36a47 pub rsa1 zt1E8Wddh26=^XgSyt2x2i)G7I5=Z[*T:4(aml1Xs1UxvnCS81>:tN$Ss!9E;mSuAJq@)YAS=4CF^heMR2=:_@7RVz>x2iooQJq@^jKUB29ECk5X7ID?N[*Tu!Yb:msMQJ9=EkgS37ux;m]i<1::rvKURztEbjkMQJD?OCmTb%tE;miM;Is@KC7RJ)gbWoiM$JD?iCFTQz`E4iVB$Jq@PvlT[+gb2i0o@JG?eChSpwQbllESQJ::xN+Tb%1ElluMs16+26FT-1;aLm[zAJm/26{Q[+),;m+GR2S*&6{Q-1_,N pub unknown-algo TPwJh>A pkgf 00000000 pubkey 1024 65537 129251483458784900555621175262818292872587807329014927540074484804119474262261383244074013537736576331652560727149001626325243856012659665194546933097292703586821422085819615124517093786704646988649444946154384037948502112302285511195679291084694375811092516151263088200304199780052361048758446082354317801941 outside@example.com location in root site inside networks 172.18.232.8/29 peer 172.18.232.9 address [127.0.0.1] 16910 mobile True pubkey 1024 65537 130064631890186713927887504218626486455931306300999583387009075747001546036643522074275473238061323169592347601185592753550279410171535737146240085267000508853176463710554801101055212967131924064664249613912656320653505750073021702169423354903540699008756137338575553686987244488914481168225136440872431691669 inside@example.com work/transform-cbcmac.c0000664000000000000000000002423415063477206012334 0ustar /* Transform module - bulk data transformation */ /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ /* For now it's hard-coded to do sequence number/pkcs5/serpent-cbcmac/serpent with a 256 bit key for each instance of serpent. We also require key material for the IVs for cbcmac and cbc. Hack: we're not using full 128-bit IVs, we're just using 32 bits and encrypting to get the full IV to save space in the packets sent over the wire. */ #include #include #include "secnet.h" #include "util.h" #include "serpent.h" #include "unaligned.h" #include "hexdebug.h" /* Required key length in bytes */ #define REQUIRED_KEYLEN ((512+64+32)/8) #include "transform-common.h" struct transform_params { SEQNUM_PARAMS_FIELDS; }; struct transform { closure_t cl; struct transform_if ops; struct transform_params p; }; struct transform_inst { struct transform_inst_if ops; struct transform_params p; struct keyInstance cryptkey; struct keyInstance mackey; uint32_t cryptiv; uint32_t maciv; SEQNUM_KEYED_FIELDS; }; #define PKCS5_MASK 15 static bool_t transform_setkey(void *sst, uint8_t *key, int32_t keylen, bool_t direction) { struct transform_inst *ti=sst; if (keylencryptkey,256,key); serpentbe_makekey(&ti->mackey,256,key+32); ti->cryptiv=get_uint32(key+64); ti->maciv=get_uint32(key+68); uint32_t firstseq=get_uint32(key+72); SEQNUM_KEYED_INIT(firstseq,firstseq); return True; } TRANSFORM_VALID; static void transform_delkey(void *sst) { struct transform_inst *ti=sst; FILLZERO(ti->cryptkey); FILLZERO(ti->mackey); ti->keyed=False; } static transform_apply_return transform_forward(void *sst, struct buffer_if *buf, const char **errmsg) { struct transform_inst *ti=sst; uint8_t *padp; int padlen; uint8_t iv[16]; uint8_t macplain[16]; uint8_t macacc[16]; uint8_t *p, *n; int i; KEYED_CHECK; /* Sequence number */ buf_prepend_uint32(buf,ti->sendseq); ti->sendseq++; /* PKCS5, stolen from IWJ */ /* eg with blocksize=4 mask=3 mask+2=5 */ /* msgsize 20 21 22 23 24 */ padlen= PKCS5_MASK-buf->size; /* -17 -18 -19 -16 -17 */ padlen &= PKCS5_MASK; /* 3 2 1 0 3 */ padlen++; /* 4 3 2 1 4 */ padp=buf_append(buf,padlen); memset(padp,padlen,padlen); /* Serpent-CBCMAC. We expand the IV from 32-bit to 128-bit using one encryption. Then we do the MAC and append the result. We don't bother sending the IV - it's the same each time. (If we wanted to send it we've have to add 16 bytes to each message, not 4, so that the message stays a multiple of 16 bytes long.) */ FILLZERO(iv); put_uint32(iv, ti->maciv); serpentbe_encrypt(&ti->mackey,iv,macacc); /* CBCMAC: encrypt in CBC mode. The MAC is the last encrypted block encrypted once again. */ for (n=buf->start; nstart+buf->size; n+=16) { for (i = 0; i < 16; i++) macplain[i] = macacc[i] ^ n[i]; serpentbe_encrypt(&ti->mackey,macplain,macacc); } serpentbe_encrypt(&ti->mackey,macacc,macacc); BUF_ADD_BYTES(append,buf,macacc,16); /* Serpent-CBC. We expand the ID as for CBCMAC, do the encryption, and prepend the IV before increasing it. */ FILLZERO(iv); put_uint32(iv, ti->cryptiv); serpentbe_encrypt(&ti->cryptkey,iv,iv); /* CBC: each block is XORed with the previous encrypted block (or the IV) before being encrypted. */ p=iv; for (n=buf->start; nstart+buf->size; n+=16) { for (i = 0; i < 16; i++) n[i] ^= p[i]; serpentbe_encrypt(&ti->cryptkey,n,n); p=n; } buf_prepend_uint32(buf,ti->cryptiv); ti->cryptiv++; return 0; } static transform_apply_return transform_reverse(void *sst, struct buffer_if *buf, const char **errmsg) { struct transform_inst *ti=sst; uint8_t *padp; int padlen; int i; uint32_t seqnum; uint8_t iv[16]; uint8_t pct[16]; uint8_t macplain[16]; uint8_t macacc[16]; uint8_t *n; uint8_t *macexpected; KEYED_CHECK; if (buf->size < 4 + 16 + 16) { *errmsg="msg too short"; return transform_apply_err; } /* CBC */ FILLZERO(iv); { uint32_t ivword = buf_unprepend_uint32(buf); put_uint32(iv, ivword); } /* Assert bufsize is multiple of blocksize */ if (buf->size&0xf) { *errmsg="msg not multiple of cipher blocksize"; return transform_apply_err; } serpentbe_encrypt(&ti->cryptkey,iv,iv); for (n=buf->start; nstart+buf->size; n+=16) { for (i = 0; i < 16; i++) pct[i] = n[i]; serpentbe_decrypt(&ti->cryptkey,n,n); for (i = 0; i < 16; i++) n[i] ^= iv[i]; COPY_OBJ(iv, pct); } /* CBCMAC */ macexpected=buf_unappend(buf,16); FILLZERO(iv); put_uint32(iv, ti->maciv); serpentbe_encrypt(&ti->mackey,iv,macacc); /* CBCMAC: encrypt in CBC mode. The MAC is the last encrypted block encrypted once again. */ for (n=buf->start; nstart+buf->size; n+=16) { for (i = 0; i < 16; i++) macplain[i] = macacc[i] ^ n[i]; serpentbe_encrypt(&ti->mackey,macplain,macacc); } serpentbe_encrypt(&ti->mackey,macacc,macacc); if (!consttime_memeq(macexpected,macacc,16)) { *errmsg="invalid MAC"; return transform_apply_err; } /* PKCS5, stolen from IWJ */ padp=buf_unappend(buf,1); padlen=*padp; if (!padlen || (padlen > PKCS5_MASK+1)) { *errmsg="pkcs5: invalid length"; return transform_apply_err; } buf_unappend(buf,padlen-1); /* Sequence number must be within max_skew of lastrecvseq; lastrecvseq is only allowed to increase. */ seqnum=buf_unprepend_uint32(buf); SEQNUM_CHECK(seqnum, &ti->p); return 0; } TRANSFORM_DESTROY; static struct transform_inst_if *transform_create(void *sst) { struct transform *st=sst; TRANSFORM_CREATE_CORE; ti->p=st->p; return &ti->ops; } static list_t *transform_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct transform *st; item_t *item; dict_t *dict; NEW(st); st->cl.description="serpent-cbc256"; st->cl.type=CL_TRANSFORM; st->cl.apply=NULL; st->cl.interface=&st->ops; st->ops.st=st; update_max_start_pad(&transform_max_start_pad, 28); /* 4byte seqnum, 16byte pad, 4byte MACIV, 4byte IV */ /* We need 256*2 bits for serpent keys, 32 bits for CBC-IV and 32 bits for CBCMAC-IV, and 32 bits for init sequence number */ st->ops.keylen=REQUIRED_KEYLEN; st->ops.create=transform_create; /* First parameter must be a dict */ item=list_elem(args,0); if (!item || item->type!=t_dict) cfgfatal(loc,"serpent256-cbc","parameter must be a dictionary\n"); dict=item->data.dict; SEQNUM_PARAMS_INIT(dict,&st->p,"serpent-cbc256",loc); SET_CAPAB_BIT(CAPAB_BIT_SERPENT256CBC); return new_closure(&st->cl); } void transform_cbcmac_module(dict_t *dict) { struct keyInstance k; uint8_t data[32]; uint8_t plaintext[16]; uint8_t ciphertext[16]; /* * Serpent self-test. * * This test pattern was taken directly from the Serpent test * vectors, which results in a big-endian Serpent which is not * compatible with other implementations. */ /* Serpent self-test */ memcpy(data, "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff" "\xff\xee\xdd\xcc\xbb\xaa\x99\x88\x77\x66\x55\x44\x33\x22\x11\x00", 32); serpentbe_makekey(&k,256,data); memcpy(plaintext, "\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10", 16); serpentbe_encrypt(&k,plaintext,ciphertext); if (memcmp(ciphertext, "\xca\x7f\xa1\x93\xe3\xeb\x9e\x99" "\xbd\x87\xe3\xaf\x3c\x9a\xdf\x93", 16)) { fatal("transform_module: serpent failed self-test (encrypt)"); } serpentbe_decrypt(&k,ciphertext,plaintext); if (memcmp(plaintext, "\x01\x23\x45\x67\x89\xab\xcd\xef" "\xfe\xdc\xba\x98\x76\x54\x32\x10", 16)) { fatal("transform_module: serpent failed self-test (decrypt)"); } add_closure(dict,"serpent256-cbc",transform_apply); #ifdef TEST_WHOLE_TRANSFORM { struct transform *tr; void *ti; struct buffer_if buf; const char text[] = "This is a piece of test text."; char keymaterial[76] = "Seventy-six bytes i" "n four rows of 19; " "this looks almost l" "ike a poem but not."; const char *errmsg; int i; NEW(tr); tr->max_seq_skew = 20; ti = transform_create(tr); transform_setkey(ti, keymaterial, 76); buf.base = malloc(4096); buffer_init(&buf, 2048); BUF_ADD_OBJ(append, buf, text, sizeof(text)); if (transform_forward(ti, &buf, &errmsg)) { fatal("transform_forward test: %s", errmsg); } printf("transformed text is:\n"); for (i = 0; i < buf.size; i++) printf("%02x%c", buf.start[i], (i%16==15 || i==buf.size-1 ? '\n' : ' ')); if (transform_reverse(ti, &buf, &errmsg)) { fatal("transform_reverse test: %s", errmsg); } printf("transform reversal worked OK\n"); } #endif } work/transform-common.h0000664000000000000000000000747415063477206012430 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #ifndef TRANSFORM_COMMON_H #define TRANSFORM_COMMON_H #include "magic.h" #define KEYED_CHECK do{ \ if (!ti->keyed) { \ *errmsg="transform unkeyed"; \ return transform_apply_err; \ } \ }while(0) #define RECVBITMAP_SIZE 32 typedef uint32_t recvbitmap_type; #define SEQNUM_CHECK(seqnum, p) do{ \ uint32_t skew=seqnum-ti->lastrecvseq; \ if (skew<0x8fffffff) { \ /* Ok */ \ ti->lastrecvseq=seqnum; \ if (skew < RECVBITMAP_SIZE) \ ti->recvbitmap <<= skew; \ else \ ti->recvbitmap=0; \ skew=0; \ } else if ((0-skew)<(p)->max_seq_skew) { \ /* Ok */ \ } else { \ /* Too much skew */ \ *errmsg="seqnum: too much skew"; \ return transform_apply_seqrange; \ } \ if ((p)->dedupe) { \ recvbitmap_type recvbit=(uint32_t)1 << skew; \ if (ti->recvbitmap & recvbit) { \ *errmsg="seqnum: duplicate"; \ return transform_apply_seqdupe; \ } \ ti->recvbitmap |= recvbit; \ } \ }while(0) #define SEQNUM_KEYED_FIELDS \ uint32_t sendseq; \ uint32_t lastrecvseq; \ recvbitmap_type recvbitmap; /* 1<<0 is lastrecvseq (i.e., most recent) */ \ bool_t keyed #define SEQNUM_KEYED_INIT(initlastrecvseq,initsendseq) \ (ti->lastrecvseq=(initlastrecvseq), \ ti->sendseq=(initsendseq), \ ti->recvbitmap=0, \ ti->keyed=True) #define TRANSFORM_VALID \ static bool_t transform_valid(void *sst) \ { \ struct transform_inst *ti=sst; \ \ return ti->keyed; \ } #define TRANSFORM_DESTROY \ static void transform_destroy(void *sst) \ { \ struct transform_inst *st=sst; \ \ FILLZERO(*st); /* Destroy key material */ \ free(st); \ } #define SET_CAPAB_BIT(def) do{ \ st->ops.capab_bit=dict_read_number(dict, "capab-num", \ False, "transform", loc, (def)); \ if (st->ops.capab_bit > CAPAB_BIT_MAX) \ cfgfatal(loc,"transform","capab-num out of range 0..%d\n", \ CAPAB_BIT_MAX); \ }while(0) #define TRANSFORM_CREATE_CORE \ struct transform_inst *ti; \ NEW(ti); \ /* mlock XXX */ \ ti->ops.st=ti; \ ti->ops.setkey=transform_setkey; \ ti->ops.valid=transform_valid; \ ti->ops.delkey=transform_delkey; \ ti->ops.forwards=transform_forward; \ ti->ops.reverse=transform_reverse; \ ti->ops.destroy=transform_destroy; \ ti->keyed=False; #define SEQNUM_PARAMS_FIELDS \ uint32_t max_seq_skew; \ bool_t dedupe; #define SEQNUM_PARAMS_INIT(dict,p,desc,loc) \ (p)->max_seq_skew=dict_read_number((dict), "max-sequence-skew", \ False, (desc), (loc), 10); \ bool_t can_dedupe=(p)->max_seq_skew < RECVBITMAP_SIZE; \ (p)->dedupe=dict_read_bool((dict), "dedupe", \ False,(desc),(loc), can_dedupe); \ if ((p)->dedupe && !can_dedupe) \ cfgfatal(loc,"transform", \ "cannot dedupe with max-sequence-skew>=32"); \ else (void)0 #endif /*TRANSFORM_COMMON_H*/ work/transform-eax.c0000664000000000000000000002053515063477206011701 0ustar /* * eax-transform.c: EAX-Serpent bulk data transformation */ /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ /* * We use EAX with the following parameters: * * Plaintext: * Concatenation of: * Data packet as supplied to us * Zero or more zero bytes ignored by receiver } padding * One byte padding length } * This is a bit like PKCS#5. It helps disguise message lengths. * It also provides a further room for future expansion. When * transmitting we pad the message to the next multiple of * a configurable rounding factor, 16 bytes by default. * * Transmitted message: * Concatenation of: * EAX ciphertext * 32-bit sequence number (initially zero) * The sequence number allows us to discard far-too-old * packets. * * Nonce: * Concatenation of: * 32-bit sequence number (big endian) * initial value comes from SHA-512 hash (see below) * 1 byte: 0x01 if sender has setup priority, 0x00 if it doesn't * (ie, the direction of data flow) * * Header: None * * Tag length: * 16 bytes (128 bits) by default * * Key: * The first 32 bytes of the SHA-512 hash of the shared secret * from the DH key exchange (the latter being expressed as * the shortest possible big-endian octet string). * * The bytes [32,40> of the hash of the shared secret are used for * initial sequence numbers: [32,36> for those sent by the end without * setup priority, [36,40> for those for the other end. * */ #include "secnet.h" #include "unaligned.h" #include "util.h" #include "serpent.h" #include "sha512.h" #include "transform-common.h" #include "hexdebug.h" #define BLOCK_SIZE 16 #define SEQLEN 4 struct transform_params { SEQNUM_PARAMS_FIELDS; uint32_t tag_length, padding_mask; }; struct transform { closure_t cl; struct transform_if ops; struct transform_params p; }; struct transform_inst { struct transform_inst_if ops; struct transform_params p; /* remaining valid iff keyed */ unsigned direction:1; SEQNUM_KEYED_FIELDS; struct keyInstance key; uint8_t info_b[BLOCK_SIZE], info_p[BLOCK_SIZE]; }; static void block_encrypt(struct transform_inst *transform_inst, uint8_t dst[BLOCK_SIZE], const uint8_t src[BLOCK_SIZE]) { serpent_encrypt(&transform_inst->key, src, dst); } #define INFO struct transform_inst *transform_inst #define I transform_inst #define EAX_ENTRYPOINT_DECL static #define BLOCK_ENCRYPT(dst,src) block_encrypt(transform_inst,dst,src) #define INFO_B (transform_inst->info_b) #define INFO_P (transform_inst->info_p) #include "eax.c" #if 0 #define TEAX_DEBUG(ary,sz) teax_debug(__func__,__LINE__,#ary,#sz,ary,sz) static void teax_debug(const char *func, int line, const char *aryp, const char *szp, const void *ary, size_t sz) { fprintf(stderr,"TEAX %s:%-3d %10s %15s : ", func,line,aryp,szp); hexdebug(stderr,ary,sz); fprintf(stderr,"\n"); } #else #define TEAX_DEBUG(ary,sz) /* empty */ #endif static bool_t transform_setkey(void *sst, uint8_t *key, int32_t keylen, bool_t direction) { struct transform_inst *ti=sst; struct sha512_ctx hash_ctx; uint8_t hash_out[64]; TEAX_DEBUG(key,keylen); sha512_init_ctx(&hash_ctx); sha512_process_bytes(key, keylen, &hash_ctx); sha512_finish_ctx(&hash_ctx, hash_out); TEAX_DEBUG(hash_out,32); TEAX_DEBUG(hash_out+32,8); ti->direction=direction; serpent_makekey(&ti->key, 32*8, hash_out); eax_setup(ti); SEQNUM_KEYED_INIT(get_uint32(hash_out+32+!direction*4), get_uint32(hash_out+32+direction*4)); return True; } TRANSFORM_VALID; TRANSFORM_DESTROY; static void transform_delkey(void *sst) { struct transform_inst *ti=sst; FILLZERO(ti->key); FILLZERO(ti->info_b); FILLZERO(ti->info_p); ti->keyed=False; } static transform_apply_return transform_forward(void *sst, struct buffer_if *buf, const char **errmsg) { struct transform_inst *ti=sst; KEYED_CHECK; size_t padlen = ti->p.padding_mask - buf->size; padlen &= ti->p.padding_mask; padlen++; uint8_t *pad = buf_append(buf,padlen); memset(pad, 0, padlen-1); pad[padlen-1] = padlen; uint8_t nonce[SEQLEN+1]; put_uint32(nonce,ti->sendseq); nonce[SEQLEN] = ti->direction; TEAX_DEBUG(nonce,sizeof(nonce)); TEAX_DEBUG(buf->start,buf->size); assert(buf_append(buf,ti->p.tag_length)); eax_encrypt(ti, nonce,sizeof(nonce), 0,0, buf->start,buf->size-ti->p.tag_length, ti->p.tag_length, buf->start); TEAX_DEBUG(buf->start,buf->size); BUF_ADD_BYTES(append,buf,nonce,SEQLEN); TEAX_DEBUG(nonce,SEQLEN); ti->sendseq++; return 0; } static transform_apply_return transform_reverse(void *sst, struct buffer_if *buf, const char **errmsg) { struct transform_inst *ti=sst; KEYED_CHECK; TEAX_DEBUG(buf->start,buf->size); uint8_t nonce[SEQLEN+1]; const uint8_t *seqp = buf_unappend(buf,SEQLEN); if (!seqp) goto too_short; TEAX_DEBUG(seqp,SEQLEN); uint32_t seqnum = get_uint32(seqp); memcpy(nonce,seqp,SEQLEN); nonce[4] = !ti->direction; TEAX_DEBUG(nonce,sizeof(nonce)); TEAX_DEBUG(buf->start,buf->size); bool_t ok = eax_decrypt(ti, nonce,sizeof(nonce), 0,0, buf->start,buf->size, ti->p.tag_length, buf->start); if (!ok) { TEAX_DEBUG(0,0); *errmsg="EAX decryption failed"; return transform_apply_err; } assert(buf->size >= (int)ti->p.tag_length); buf->size -= ti->p.tag_length; TEAX_DEBUG(buf->start,buf->size); const uint8_t *padp = buf_unappend(buf,1); if (!padp) goto too_short; TEAX_DEBUG(padp,1); size_t padlen = *padp; if (!buf_unappend(buf,padlen-1)) goto too_short; SEQNUM_CHECK(seqnum, &ti->p); TEAX_DEBUG(buf->start,buf->size); return 0; too_short: *errmsg="ciphertext or plaintext too short"; return transform_apply_err; } static struct transform_inst_if *transform_create(void *sst) { struct transform *st=sst; TRANSFORM_CREATE_CORE; ti->p=st->p; return &ti->ops; } static list_t *transform_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct transform *st; item_t *item; dict_t *dict; NEW(st); st->cl.description="eax-serpent"; st->cl.type=CL_TRANSFORM; st->cl.apply=NULL; st->cl.interface=&st->ops; st->ops.st=st; /* First parameter must be a dict */ item=list_elem(args,0); if (!item || item->type!=t_dict) cfgfatal(loc,"eax-serpent","parameter must be a dictionary\n"); dict=item->data.dict; SET_CAPAB_BIT(CAPAB_BIT_EAXSERPENT); SEQNUM_PARAMS_INIT(dict,&st->p,"eax-serpent",loc); st->p.tag_length=dict_read_number(dict, "tag-length-bytes", False, "eax-serpent", loc, 128/8); if (st->p.tag_length<1 || st->p.tag_length>BLOCK_SIZE) cfgfatal(loc,"eax-serpent","tag-length-bytes out of range 0..%d\n", BLOCK_SIZE); uint32_t padding_round=dict_read_number(dict, "padding-rounding", False, "eax-serpent", loc, 16); if (padding_round & (padding_round-1)) cfgfatal(loc,"eax-serpent","padding-round not a power of two\n"); if (padding_round > 255) cfgfatal(loc,"eax-serpent","padding-round must be 1..128\n"); if (padding_round == 0) padding_round = 1; st->p.padding_mask = padding_round-1; update_max_start_pad(&transform_max_start_pad, 0); st->ops.keylen=0; st->ops.create=transform_create; return new_closure(&st->cl); } void transform_eax_module(dict_t *dict) { add_closure(dict,"eax-serpent",transform_apply); } work/tun.c0000664000000000000000000004553715063477206007732 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include "secnet.h" #include "util.h" #include "netlink.h" #include #include #include #include #include #include #include #include #ifdef HAVE_NET_IF_H #include #ifdef HAVE_LINUX_IF_TUN_H #include #define LINUX_TUN_SUPPORTED #endif #endif #ifdef HAVE_NET_ROUTE_H #include #endif #if defined(HAVE_STROPTS_H) && defined(HAVE_SYS_SOCKIO_H) && \ defined(HAVE_NET_IF_TUN_H) #define HAVE_TUN_STREAMS #endif #ifdef HAVE_TUN_STREAMS #include #include #include #endif #define TUN_FLAVOUR_GUESS 0 #define TUN_FLAVOUR_BSD 1 #define TUN_FLAVOUR_LINUX 2 #define TUN_FLAVOUR_STREAMS 3 static struct flagstr flavours[]={ {"guess", TUN_FLAVOUR_GUESS}, {"bsd", TUN_FLAVOUR_BSD}, {"BSD", TUN_FLAVOUR_BSD}, {"linux", TUN_FLAVOUR_LINUX}, {"streams", TUN_FLAVOUR_STREAMS}, {"STREAMS", TUN_FLAVOUR_STREAMS}, {NULL, 0} }; #define TUN_CONFIG_GUESS 0 #define TUN_CONFIG_IOCTL 1 #define TUN_CONFIG_BSD 2 #define TUN_CONFIG_LINUX 3 #define TUN_CONFIG_SOLARIS25 4 static struct flagstr config_types[]={ {"guess", TUN_CONFIG_GUESS}, {"ioctl", TUN_CONFIG_IOCTL}, {"bsd", TUN_CONFIG_BSD}, {"BSD", TUN_CONFIG_BSD}, {"linux", TUN_CONFIG_LINUX}, {"solaris-2.5", TUN_CONFIG_SOLARIS25}, {NULL, 0} }; /* Connection to the kernel through the universal TUN/TAP driver */ struct tun { struct netlink nl; int fd; cstring_t device_path; cstring_t ip_path; string_t interface_name; cstring_t ifconfig_path; uint32_t ifconfig_type; cstring_t route_path; uint32_t route_type; uint32_t tun_flavour; bool_t search_for_if; /* Applies to tun-BSD only */ struct buffer_if *buff; /* We receive packets into here and send them to the netlink code. */ netlink_deliver_fn *netlink_to_tunnel; }; static cstring_t tun_flavour_str(uint32_t flavour) { switch (flavour) { case TUN_FLAVOUR_GUESS: return "guess"; case TUN_FLAVOUR_BSD: return "BSD"; case TUN_FLAVOUR_LINUX: return "linux"; case TUN_FLAVOUR_STREAMS: return "STREAMS"; default: return "unknown"; } } static int tun_beforepoll(void *sst, struct pollfd *fds, int *nfds_io, int *timeout_io) { struct tun *st=sst; BEFOREPOLL_WANT_FDS(1); fds[0].fd=st->fd; fds[0].events=POLLIN; return 0; } static void tun_afterpoll(void *sst, struct pollfd *fds, int nfds) { struct tun *st=sst; int l; if (nfds==0) return; if (fds[0].revents&POLLERR) { printf("tun_afterpoll: hup!\n"); } if (fds[0].revents&POLLIN) { BUF_ALLOC(st->buff,"tun_afterpoll"); buffer_init(st->buff,calculate_max_start_pad()); l=read(st->fd, st->buff->start, buf_remaining_space(st->buff)); if (l<0) { if (errno==EINTR || iswouldblock(errno)) return; fatal_perror("tun_afterpoll: read()"); } if (l==0) { fatal("tun_afterpoll: read()=0; device gone away?"); } if (l>0) { st->buff->size=l; st->netlink_to_tunnel(&st->nl,st->buff); BUF_ASSERT_FREE(st->buff); } } } static void tun_deliver_to_kernel(void *sst, struct buffer_if *buf) { struct tun *st=sst; ssize_t rc; BUF_ASSERT_USED(buf); /* Log errors, so we can tell what's going on, but only once a minute, so we don't flood the logs. Short writes count as errors. */ rc = write(st->fd,buf->start,buf->size); if(rc != buf->size) { static struct timeval last_report; if(tv_now_global.tv_sec >= last_report.tv_sec + 60) { if(rc < 0) Message(M_WARNING, "failed to deliver packet to tun device: %s\n", strerror(errno)); else Message(M_WARNING, "truncated packet delivered to tun device\n"); last_report = tv_now_global; } } BUF_FREE(buf); } static bool_t tun_set_route(void *sst, struct netlink_client *routes) { struct tun *st=sst; string_t network, mask, secnetaddr; struct subnet_list *nets; int32_t i; int fd=-1; bool_t up; if (routes->options & OPT_SOFTROUTE) up = routes->up; else up = routes->link_quality > LINK_QUALITY_UNUSED; if (up == routes->kup) return False; if (st->route_type==TUN_CONFIG_IOCTL) { if (st->tun_flavour==TUN_FLAVOUR_STREAMS) { fd=open(st->ip_path,O_RDWR); if (fd<0) { fatal_perror("tun_set_route: can't open %s",st->ip_path); } } else { fd=socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); if (fd<0) { fatal_perror("tun_set_route: socket()"); } } } nets=routes->subnets; secnetaddr=ipaddr_to_string(st->nl.secnet_address); for (i=0; ientries; i++) { network=ipaddr_to_string(nets->list[i].prefix); mask=ipaddr_to_string(nets->list[i].mask); Message(M_INFO,"%s: %s route %s/%d %s kernel routing table\n", st->nl.name,up?"adding":"deleting",network, nets->list[i].len,up?"to":"from"); switch (st->route_type) { case TUN_CONFIG_LINUX: sys_cmd(st->route_path,"route",up?"add":"del", "-net",network,"netmask",mask, "gw",secnetaddr,(char *)0); break; case TUN_CONFIG_BSD: sys_cmd(st->route_path,"route",up?"add":"del", "-net",network,secnetaddr,mask,(char *)0); break; case TUN_CONFIG_SOLARIS25: sys_cmd(st->route_path,"route",up?"add":"del", network,secnetaddr,(char *)0); break; case TUN_CONFIG_IOCTL: { /* darwin rtentry has a different format, use /sbin/route instead */ #if HAVE_NET_ROUTE_H && ! __APPLE__ struct rtentry rt; struct sockaddr_in *sa; int action; FILLZERO(rt); sa=(struct sockaddr_in *)&rt.rt_dst; sa->sin_family=AF_INET; sa->sin_addr.s_addr=htonl(nets->list[i].prefix); sa=(struct sockaddr_in *)&rt.rt_genmask; sa->sin_family=AF_INET; sa->sin_addr.s_addr=htonl(nets->list[i].mask); sa=(struct sockaddr_in *)&rt.rt_gateway; sa->sin_family=AF_INET; sa->sin_addr.s_addr=htonl(st->nl.secnet_address); rt.rt_flags=RTF_UP|RTF_GATEWAY; action=up?SIOCADDRT:SIOCDELRT; if (ioctl(fd,action,&rt)<0) { fatal_perror("tun_set_route: ioctl()"); } #else fatal("tun_set_route: ioctl method not supported"); #endif } break; default: fatal("tun_set_route: unsupported route command type"); break; } } if (fd >= 0) { close(fd); } routes->kup=up; return True; } static void tun_phase_hook(void *sst, uint32_t newphase) { struct tun *st=sst; string_t hostaddr,secnetaddr; char mtu[6]; struct netlink_client *r; if (st->tun_flavour==TUN_FLAVOUR_BSD) { if (st->search_for_if) { string_t dname; int i; dname=safe_malloc(strlen(st->device_path)+4,"tun_old_apply"); st->interface_name=safe_malloc(8,"tun_phase_hook"); for (i=0; i<255; i++) { sprintf(dname,"%s%d",st->device_path,i); if ((st->fd=open(dname,O_RDWR))>0) { sprintf(st->interface_name,"tun%d",i); Message(M_INFO,"%s: allocated network interface %s " "through %s\n",st->nl.name,st->interface_name, dname); break; } } if (st->fd==-1) { fatal("%s: unable to open any TUN device (%s...)", st->nl.name,st->device_path); } } else { st->fd=open(st->device_path,O_RDWR); if (st->fd==-1) { fatal_perror("%s: unable to open TUN device file %s", st->nl.name,st->device_path); } } } else if (st->tun_flavour==TUN_FLAVOUR_LINUX) { #ifdef LINUX_TUN_SUPPORTED struct ifreq ifr; /* New TUN interface: open the device, then do ioctl TUNSETIFF to set or find out the network interface name. */ st->fd=open(st->device_path,O_RDWR); if (st->fd==-1) { fatal_perror("%s: can't open device file %s",st->nl.name, st->device_path); } FILLZERO(ifr); ifr.ifr_flags = IFF_TUN | IFF_NO_PI; /* Just send/receive IP packets, no extra headers */ if (st->interface_name) strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ); if (ioctl(st->fd,TUNSETIFF,&ifr)<0) { fatal_perror("%s: ioctl(TUNSETIFF)",st->nl.name); } if (!st->interface_name) { st->interface_name=safe_malloc(strlen(ifr.ifr_name)+1,"tun_apply"); strcpy(st->interface_name,ifr.ifr_name); Message(M_INFO,"%s: allocated network interface %s\n",st->nl.name, st->interface_name); } #else fatal("tun_phase_hook: TUN_FLAVOUR_LINUX unexpected"); #endif /* LINUX_TUN_SUPPORTED */ } else if (st->tun_flavour==TUN_FLAVOUR_STREAMS) { #ifdef HAVE_TUN_STREAMS int tun_fd, if_fd, ppa=-1, ip_fd; if ((ip_fd=open(st->ip_path, O_RDWR)) < 0) { fatal_perror("%s: can't open %s",st->nl.name,st->ip_path); } if ((tun_fd=open(st->device_path,O_RDWR)) < 0) { fatal_perror("%s: can't open %s",st->nl.name,st->device_path); } if ((ppa=ioctl(tun_fd,TUNNEWPPA,ppa)) < 0) { fatal_perror("%s: can't assign new interface"); } if ((if_fd=open(st->device_path,O_RDWR)) < 0) { fatal_perror("%s: can't open %s (2)",st->nl.name,st->device_path); } if (ioctl(if_fd,I_PUSH,"ip") < 0) { fatal_perror("%s: can't push IP module",st->nl.name); } if (ioctl(if_fd,IF_UNITSEL,(char *)&ppa) < 0) { fatal_perror("%s: can't set ppa %d",st->nl.name,ppa); } if (ioctl(ip_fd, I_LINK, if_fd) < 0) { fatal_perror("%s: can't link TUN device to IP",st->nl.name); } st->interface_name=safe_malloc(10,"tun_apply"); sprintf(st->interface_name,"tun%d",ppa); st->fd=tun_fd; setcloexec(if_ifd); setcloexec(ip_ifd); #else fatal("tun_phase_hook: TUN_FLAVOUR_STREAMS unexpected"); #endif /* HAVE_TUN_STREAMS */ } else { fatal("tun_phase_hook: unknown flavour of TUN"); } /* All the networks we'll be using have been registered. Invoke ifconfig to set the TUN device's address, and route to add routes to all our networks. */ setcloexec(st->fd); setnonblock(st->fd); hostaddr=ipaddr_to_string(st->nl.local_address); secnetaddr=ipaddr_to_string(st->nl.secnet_address); snprintf(mtu,sizeof(mtu),"%d",st->nl.mtu); mtu[5]=0; switch (st->ifconfig_type) { case TUN_CONFIG_LINUX: sys_cmd(st->ifconfig_path,"ifconfig",st->interface_name, hostaddr,"netmask","255.255.255.255","-broadcast", "-multicast", "pointopoint",secnetaddr,"mtu",mtu,"up",(char *)0); break; case TUN_CONFIG_BSD: sys_cmd(st->ifconfig_path,"ifconfig",st->interface_name, hostaddr,"netmask","255.255.255.255", secnetaddr,"mtu",mtu,"up",(char *)0); break; case TUN_CONFIG_SOLARIS25: sys_cmd(st->ifconfig_path,"ifconfig",st->interface_name, hostaddr,secnetaddr,"mtu",mtu,"up",(char *)0); break; case TUN_CONFIG_IOCTL: #if HAVE_NET_IF_H && ! __APPLE__ { int fd; struct ifreq ifr; struct sockaddr_in *sa; fd=socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); /* Interface address */ strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ); sa=(struct sockaddr_in *)&ifr.ifr_addr; FILLZERO(*sa); sa->sin_family=AF_INET; sa->sin_addr.s_addr=htonl(st->nl.local_address); if (ioctl(fd,SIOCSIFADDR, &ifr)!=0) { fatal_perror("tun_apply: SIOCSIFADDR"); } #ifdef SIOCSIFNETMASK /* Netmask */ strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ); sa=(struct sockaddr_in *)&ifr.ifr_netmask; FILLZERO(*sa); sa->sin_family=AF_INET; sa->sin_addr.s_addr=htonl(0xffffffff); if (ioctl(fd,SIOCSIFNETMASK, &ifr)!=0) { fatal_perror("tun_apply: SIOCSIFNETMASK"); } #endif /* Destination address (point-to-point) */ strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ); sa=(struct sockaddr_in *)&ifr.ifr_dstaddr; FILLZERO(*sa); sa->sin_family=AF_INET; sa->sin_addr.s_addr=htonl(st->nl.secnet_address); if (ioctl(fd,SIOCSIFDSTADDR, &ifr)!=0) { fatal_perror("tun_apply: SIOCSIFDSTADDR"); } /* MTU */ strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ); ifr.ifr_mtu=st->nl.mtu; if (ioctl(fd,SIOCSIFMTU, &ifr)!=0) { fatal_perror("tun_apply: SIOCSIFMTU"); } /* Flags */ strncpy(ifr.ifr_name,st->interface_name,IFNAMSIZ); ifr.ifr_flags=IFF_UP|IFF_POINTOPOINT|IFF_RUNNING|IFF_NOARP; if (ioctl(fd,SIOCSIFFLAGS, &ifr)!=0) { fatal_perror("tun_apply: SIOCSIFFLAGS"); } close(fd); } #else fatal("tun_apply: ifconfig by ioctl() not supported"); #endif /* HAVE_NET_IF_H */ break; default: fatal("tun_apply: unsupported ifconfig method"); break; } for (r=st->nl.clients; r; r=r->next) { tun_set_route(st,r); } add_hook(PHASE_CHILDPERSIST,childpersist_closefd_hook,&st->fd); /* Register for poll() */ register_for_poll(st, tun_beforepoll, tun_afterpoll, st->nl.name); } static list_t *tun_create(closure_t *self, struct cloc loc, dict_t *context, list_t *args,uint32_t default_flavour) { struct tun *st; item_t *item; dict_t *dict; string_t flavour,type; NEW(st); /* First parameter must be a dict */ item=list_elem(args,0); if (!item || item->type!=t_dict) cfgfatal(loc,"tun","parameter must be a dictionary\n"); dict=item->data.dict; st->netlink_to_tunnel= netlink_init(&st->nl,st,loc,dict, "netlink-tun",tun_set_route,tun_deliver_to_kernel); flavour=dict_read_string(dict,"flavour",False,"tun-netlink",loc); if (flavour) st->tun_flavour=string_to_word(flavour,loc,flavours,"tun-flavour"); else st->tun_flavour=default_flavour; st->device_path=dict_read_string(dict,"device",False,"tun-netlink",loc); st->ip_path=dict_read_string(dict,"ip-path",False,"tun-netlink",loc); st->interface_name=dict_read_string(dict,"interface",False, "tun-netlink",loc); st->search_for_if=dict_read_bool(dict,"interface-search",False, "tun-netlink",loc,st->device_path==NULL); type=dict_read_string(dict,"ifconfig-type",False,"tun-netlink",loc); if (type) st->ifconfig_type=string_to_word(type,loc,config_types, "ifconfig-type"); else st->ifconfig_type=TUN_CONFIG_GUESS; st->ifconfig_path=dict_read_string(dict,"ifconfig-path",False, "tun-netlink",loc); type=dict_read_string(dict,"route-type",False,"tun-netlink",loc); if (type) st->route_type=string_to_word(type,loc,config_types, "route-type"); else st->route_type=TUN_CONFIG_GUESS; st->route_path=dict_read_string(dict,"route-path",False,"tun-netlink",loc); st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"tun-netlink",loc); if (st->tun_flavour==TUN_FLAVOUR_GUESS) { /* If we haven't been told what type of TUN we're using, take a guess based on the system details. */ struct utsname u; if (uname(&u)<0) { fatal_perror("tun_create: uname"); } if (strcmp(u.sysname,"Linux")==0) { st->tun_flavour=TUN_FLAVOUR_LINUX; } else if (strcmp(u.sysname,"SunOS")==0) { st->tun_flavour=TUN_FLAVOUR_STREAMS; } else if (strcmp(u.sysname,"FreeBSD")==0 || strcmp(u.sysname,"Darwin")==0) { st->tun_flavour=TUN_FLAVOUR_BSD; } } if (st->tun_flavour==TUN_FLAVOUR_GUESS) { cfgfatal(loc,"tun","cannot guess which type of TUN is in use; " "specify the flavour explicitly\n"); } if (st->ifconfig_type==TUN_CONFIG_GUESS) { switch (st->tun_flavour) { case TUN_FLAVOUR_LINUX: st->ifconfig_type=TUN_CONFIG_IOCTL; break; case TUN_FLAVOUR_BSD: #if __linux__ /* XXX on Linux we still want TUN_CONFIG_IOCTL. Perhaps we can use this on BSD too. */ st->ifconfig_type=TUN_CONFIG_IOCTL; #else st->ifconfig_type=TUN_CONFIG_BSD; #endif break; case TUN_FLAVOUR_STREAMS: st->ifconfig_type=TUN_CONFIG_BSD; break; } } if (st->route_type==TUN_CONFIG_GUESS) st->route_type=st->ifconfig_type; if (st->ifconfig_type==TUN_CONFIG_GUESS) { cfgfatal(loc,"tun","cannot guess which ifconfig method to use\n"); } if (st->route_type==TUN_CONFIG_GUESS) { cfgfatal(loc,"tun","cannot guess which route method to use\n"); } if (st->ifconfig_type==TUN_CONFIG_IOCTL && st->ifconfig_path) { cfgfatal(loc,"tun","ifconfig-type \"ioctl\" is incompatible with " "ifconfig-path\n"); } if (st->route_type==TUN_CONFIG_IOCTL && st->route_path) { cfgfatal(loc,"tun","route-type \"ioctl\" is incompatible with " "route-path\n"); } Message(M_DEBUG_CONFIG,"%s: tun flavour %s\n",st->nl.name, tun_flavour_str(st->tun_flavour)); switch (st->tun_flavour) { case TUN_FLAVOUR_BSD: if (!st->device_path) st->device_path="/dev/tun"; break; case TUN_FLAVOUR_LINUX: if (!st->device_path) st->device_path="/dev/net/tun"; break; case TUN_FLAVOUR_STREAMS: if (!st->device_path) st->device_path="/dev/tun"; if (st->interface_name) cfgfatal(loc,"tun","interface name cannot " "be specified with STREAMS TUN\n"); break; } if (!st->ip_path) st->ip_path="/dev/ip"; if (!st->ifconfig_path) st->ifconfig_path="ifconfig"; if (!st->route_path) st->route_path="route"; #ifndef HAVE_TUN_STREAMS if (st->tun_flavour==TUN_FLAVOUR_STREAMS) { cfgfatal(loc,"tun","TUN flavour STREAMS unsupported in this build " "of secnet\n"); } #endif #ifndef LINUX_TUN_SUPPORTED if (st->tun_flavour==TUN_FLAVOUR_LINUX) { cfgfatal(loc,"tun","TUN flavour LINUX unsupported in this build " "of secnet\n"); } #endif /* Old TUN interface: the network interface name depends on which /dev/tunX file we open. If 'interface-search' is set to true, treat 'device' as the prefix and try numbers from 0--255. If it's set to false, treat 'device' as the whole name, and require than an appropriate interface name be specified. */ if (st->tun_flavour==TUN_FLAVOUR_BSD) { if (st->search_for_if && st->interface_name) { cfgfatal(loc,"tun","you may not specify an interface name " "in interface-search mode\n"); } if (!st->search_for_if && !st->interface_name) { cfgfatal(loc,"tun","you must specify an interface name " "when you explicitly specify a TUN device file\n"); } } add_hook(PHASE_GETRESOURCES,tun_phase_hook,st); return new_closure(&st->nl.cl); } static list_t *tun_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { return tun_create(self,loc,context,args,TUN_FLAVOUR_GUESS); } static list_t *tun_bsd_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { Message(M_WARNING,"(%s,%d): obsolete use of tun-old; replace with tun " "and specify flavour \"bsd\".\n",loc.file,loc.line); return tun_create(self,loc,context,args,TUN_FLAVOUR_BSD); } void tun_module(dict_t *dict) { add_closure(dict,"tun",tun_apply); add_closure(dict,"tun-old",tun_bsd_apply); } work/u64.h0000664000000000000000000000642715063477206007542 0ustar /* uint64_t-like operations that work even on hosts lacking uint64_t Copyright (C) 2006, 2009, 2010 Free Software Foundation, Inc. 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 . */ /* Written by Paul Eggert. */ #include #include /* Return X rotated left by N bits, where 0 < N < 64. */ #define u64rol(x, n) u64or (u64shl (x, n), u64shr (x, 64 - n)) #ifdef UINT64_MAX /* Native implementations are trivial. See below for comments on what these operations do. */ typedef uint64_t u64; # define u64hilo(hi, lo) ((u64) (((u64) (hi) << 32) + (lo))) # define u64init(hi, lo) u64hilo (hi, lo) # define u64lo(x) ((u64) (x)) # define u64lt(x, y) ((x) < (y)) # define u64and(x, y) ((x) & (y)) # define u64or(x, y) ((x) | (y)) # define u64xor(x, y) ((x) ^ (y)) # define u64plus(x, y) ((x) + (y)) # define u64shl(x, n) ((x) << (n)) # define u64shr(x, n) ((x) >> (n)) #else /* u64 is a 64-bit unsigned integer value. u64init (HI, LO), is like u64hilo (HI, LO), but for use in initializer contexts. */ # ifdef WORDS_BIGENDIAN typedef struct { uint32_t hi, lo; } u64; # define u64init(hi, lo) { hi, lo } # else typedef struct { uint32_t lo, hi; } u64; # define u64init(hi, lo) { lo, hi } # endif /* Given the high and low-order 32-bit quantities HI and LO, return a u64 value representing (HI << 32) + LO. */ static inline u64 u64hilo (uint32_t hi, uint32_t lo) { u64 r; r.hi = hi; r.lo = lo; return r; } /* Return a u64 value representing LO. */ static inline u64 u64lo (uint32_t lo) { u64 r; r.hi = 0; r.lo = lo; return r; } /* Return X < Y. */ static inline int u64lt (u64 x, u64 y) { return x.hi < y.hi || (x.hi == y.hi && x.lo < y.lo); } /* Return X & Y. */ static inline u64 u64and (u64 x, u64 y) { u64 r; r.hi = x.hi & y.hi; r.lo = x.lo & y.lo; return r; } /* Return X | Y. */ static inline u64 u64or (u64 x, u64 y) { u64 r; r.hi = x.hi | y.hi; r.lo = x.lo | y.lo; return r; } /* Return X ^ Y. */ static inline u64 u64xor (u64 x, u64 y) { u64 r; r.hi = x.hi ^ y.hi; r.lo = x.lo ^ y.lo; return r; } /* Return X + Y. */ static inline u64 u64plus (u64 x, u64 y) { u64 r; r.lo = x.lo + y.lo; r.hi = x.hi + y.hi + (r.lo < x.lo); return r; } /* Return X << N. */ static inline u64 u64shl (u64 x, int n) { u64 r; if (n < 32) { r.hi = (x.hi << n) | (x.lo >> (32 - n)); r.lo = x.lo << n; } else { r.hi = x.lo << (n - 32); r.lo = 0; } return r; } /* Return X >> N. */ static inline u64 u64shr (u64 x, int n) { u64 r; if (n < 32) { r.hi = x.hi >> n; r.lo = (x.hi << (32 - n)) | (x.lo >> n); } else { r.hi = 0; r.lo = x.hi >> (n - 32); } return r; } #endif work/udp.c0000664000000000000000000003354315063477206007706 0ustar /* UDP send/receive module for secnet */ /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ /* This module enables sites to communicate by sending UDP * packets. When an instance of the module is created we can * optionally bind to a particular local IP address (not implemented * yet). * * Packets are offered to registered receivers in turn. Once one * accepts it, it isn't offered to any more. */ #include "secnet.h" #include #include #include #include #include #include #include #include #include #include "util.h" #include "magic.h" #include "unaligned.h" #include "ipaddr.h" #include "magic.h" #include "comm-common.h" static comm_sendmsg_fn udp_sendmsg; struct udp { struct udpcommon uc; struct udpsocks socks; bool_t addr_configured; unsigned counter; }; /* * Re comm_addr.ix: This field allows us to note in the comm_addr * which socket an incoming packet was received on. This is required * for conveniently logging the actual source of a packet. But the ix * does not formally form part of the address: it is not used when * sending, nor when comparing two comm_addrs. * * The special value -1 means that the comm_addr was constructed by * another module in secnet (eg the resolver), rather than being a * description of the source of an incoming packet. */ static const char *udp_addr_to_string(void *commst, const struct comm_addr *ca) { struct udp *st=commst; struct udpsocks *socks=&st->socks; static char sbuf[100]; int ix=ca->ix>=0 ? ca->ix : 0; assert(ix>=0 && ixn_socks); snprintf(sbuf, sizeof(sbuf), "udp#%u@l%d:%s%s-%s", st->counter, st->uc.cc.loc.line, iaddr_to_string(&socks->socks[ix].addr), ca->ix<0 && socks->n_socks>1 ? "&" : "", iaddr_to_string(&ca->ia)); return sbuf; } static int udp_socks_beforepoll(void *state, struct pollfd *fds, int *nfds_io, int *timeout_io) { struct udpsocks *socks=state; int i; BEFOREPOLL_WANT_FDS(socks->n_socks); for (i=0; in_socks; i++) { fds[i].fd=socks->socks[i].fd; fds[i].events=POLLIN; } return 0; } const char *af_name(int af) { switch (af) { case AF_INET6: return "IPv6"; case AF_INET: return "IPv4"; case 0: return "(any)"; default: abort(); } } void udp_sock_experienced(struct log_if *lg, struct udpcommon *uc, struct udpsocks *socks, struct udpsock *us, const union iaddr *dest, int af, int r, int errnoval) { bool_t success=r>=0; if (us->experienced[!!dest][af][success]++) return; lg_perror(lg, uc->cc.cl.description, &uc->cc.loc, success ? M_INFO : M_WARNING, success ? 0 : errnoval, "%s %s experiencing some %s %s%s%s%s%s%s", socks->desc,iaddr_to_string(&us->addr), success?"success":"trouble", dest?"transmitting":"receiving", af?" ":"", af?af_name(af):"", dest?" (to ":"", dest?iaddr_to_string(dest):"", dest?")":""); } static void udp_socks_afterpoll(void *state, struct pollfd *fds, int nfds) { struct udpsocks *socks=state; struct udpcommon *uc=socks->uc; union iaddr from; socklen_t fromlen; bool_t done; int rv; int i; struct commcommon *cc=&uc->cc; for (i=0; in_socks; i++) { struct udpsock *us=&socks->socks[i]; if (i>=nfds) continue; if (!(fds[i].revents & POLLIN)) continue; assert(fds[i].fd == us->fd); int fd=us->fd; do { fromlen=sizeof(from); BUF_ASSERT_FREE(cc->rbuf); BUF_ALLOC(cc->rbuf,"udp_afterpoll"); buffer_init(cc->rbuf,calculate_max_start_pad()); rv=recvfrom(fd, cc->rbuf->start, buf_remaining_space(cc->rbuf), 0, &from.sa, &fromlen); if (rv>0) { cc->rbuf->size=rv; if (uc->use_proxy) { /* Check that the packet came from our poxy server; we shouldn't be contacted directly by anybody else (since they can trivially forge source addresses) */ if (!iaddr_equal(&from,&uc->proxy,False)) { Message(M_INFO,"udp: received packet that's not " "from the proxy\n"); BUF_FREE(cc->rbuf); continue; } /* proxy protocol supports ipv4 transport only */ from.sa.sa_family=AF_INET; BUF_GET_BYTES(unprepend,cc->rbuf,&from.sin.sin_addr,4); buf_unprepend(cc->rbuf,2); BUF_GET_BYTES(unprepend,cc->rbuf,&from.sin.sin_port,2); } struct comm_addr ca; ca.comm=&cc->ops; ca.ia=from; ca.ix=i; done=comm_notify(cc, cc->rbuf, &ca); if (done) { udp_sock_experienced(0,uc,socks,us,0, from.sa.sa_family,0,0); } else { uint32_t msgtype; if (cc->rbuf->size>12 /* prevents traffic amplification */ && ((msgtype=get_uint32(cc->rbuf->start+8)) != LABEL_NAK)) { uint32_t source,dest; /* Manufacture and send NAK packet */ source=get_uint32(cc->rbuf->start); /* Us */ dest=get_uint32(cc->rbuf->start+4); /* Them */ send_nak(&ca,source,dest,msgtype,cc->rbuf, priomsg_getmessage(&cc->why_unwanted, "unwanted")); } BUF_FREE(cc->rbuf); } BUF_ASSERT_FREE(cc->rbuf); } else { /* rv<=0 */ if (errno!=EINTR && !iswouldblock(errno)) udp_sock_experienced(0,uc,socks,us, 0,0, rv,errno); BUF_FREE(cc->rbuf); } } while (rv>=0); } } static bool_t udp_sendmsg(void *commst, struct buffer_if *buf, const struct comm_addr *dest, struct comm_clientinfo *clientinfo) { struct udp *st=commst; struct udpcommon *uc=&st->uc; struct udpsocks *socks=&st->socks; uint8_t *sa; if (uc->use_proxy) { struct udpsock *us=&socks->socks[0]; sa=buf_prepend(buf,8); if (dest->ia.sa.sa_family != AF_INET) { Message(M_INFO, "udp: proxy means dropping outgoing non-IPv4 packet to %s\n", iaddr_to_string(&dest->ia)); return False; } memcpy(sa,&dest->ia.sin.sin_addr,4); memset(sa+4,0,4); memcpy(sa+6,&dest->ia.sin.sin_port,2); int r=sendto(us->fd,sa,buf->size+8,0,&uc->proxy.sa, iaddr_socklen(&uc->proxy)); udp_sock_experienced(0,uc,socks,us, &dest->ia,0, r,errno); buf_unprepend(buf,8); } else { int i,r; bool_t allunsupported=True; int af=dest->ia.sa.sa_family; for (i=0; in_socks; i++) { struct udpsock *us=&socks->socks[i]; if (us->addr.sa.sa_family != af) /* no point even trying */ continue; r=sendto(us->fd, buf->start, buf->size, 0, &dest->ia.sa, iaddr_socklen(&dest->ia)); udp_sock_experienced(0,uc,socks,us, &dest->ia,af, r,errno); if (r>=0) return True; if (!(errno==EAFNOSUPPORT || errno==ENETUNREACH)) /* who knows what that error means? */ allunsupported=False; } return !allunsupported; /* see doc for comm_sendmsg_fn in secnet.h */ } return True; } void udp_destroy_socket(struct udpcommon *uc, struct udpsock *us) { if (us->fd>=0) { close(us->fd); us->fd=-1; } } #define FAIL_LG 0, cc->cl.description, &cc->loc, failmsgclass #define FAIL(...) do{ \ lg_perror(FAIL_LG,errno,__VA_ARGS__); \ goto failed; \ }while(0) static bool_t record_socket_gotaddr(struct udpcommon *uc, struct udpsock *us, int failmsgclass) { struct commcommon *cc=&uc->cc; socklen_t salen=sizeof(us->addr); int r=getsockname(us->fd,&us->addr.sa,&salen); if (r) FAIL("getsockname()"); if ((size_t)salen>sizeof(us->addr)) /* cast squashes clang warning */ { errno=0; FAIL("getsockname() length"); } return True; failed: return False; } bool_t udp_import_socket(struct udpcommon *uc, struct udpsock *us, int failmsgclass, int fd) { FILLZERO(us->experienced); us->fd=fd; return record_socket_gotaddr(uc,us,failmsgclass); } bool_t udp_make_socket(struct udpcommon *uc, struct udpsock *us, int failmsgclass) { const union iaddr *addr=&us->addr; struct commcommon *cc=&uc->cc; us->fd=-1; FILLZERO(us->experienced); us->fd=socket(addr->sa.sa_family, SOCK_DGRAM, IPPROTO_UDP); if (us->fd<0) FAIL("socket"); setnonblock(us->fd); setcloexec(us->fd); #ifdef CONFIG_IPV6 if (addr->sa.sa_family==AF_INET6) { int r; int optval=1; socklen_t optlen=sizeof(optval); r=setsockopt(us->fd,IPPROTO_IPV6,IPV6_V6ONLY,&optval,optlen); if (r) FAIL("setsockopt(,IPV6_V6ONLY,&1,)"); } #endif if (uc->authbind) { pid_t c; int status; char desc[200]; snprintf(desc,sizeof(desc),"authbind for %s: %s", iaddr_to_string(addr), uc->authbind); /* XXX this fork() and waitpid() business needs to be hidden in some system-specific library functions. */ c=fork(); if (c==-1) FAIL("fork() for authbind"); if (c==0) { char *argv[5], addrstr[33], portstr[5]; const char *addrfam; int port; afterfork(); switch (addr->sa.sa_family) { case AF_INET: sprintf(addrstr,"%08lX",(long)addr->sin.sin_addr.s_addr); port=addr->sin.sin_port; addrfam=NULL; break; #ifdef CONFIG_IPV6 case AF_INET6: { int i; for (i=0; i<16; i++) sprintf(addrstr+i*2,"%02X",addr->sin6.sin6_addr.s6_addr[i]); port=addr->sin6.sin6_port; addrfam="6"; break; } #endif /*CONFIG_IPV6*/ default: fatal("udp (%s:%d): unsupported address family for authbind", cc->loc.file,cc->loc.line); } sprintf(portstr,"%04X",port); argv[0]=uc->authbind; argv[1]=addrstr; argv[2]=portstr; argv[3]=(char*)addrfam; argv[4]=NULL; dup2(us->fd,0); execvp(uc->authbind,argv); _exit(255); } while (waitpid(c,&status,0)==-1) { if (errno==EINTR) continue; FAIL("waitpid for authbind"); } if (status) { if (WIFEXITED(status) && WEXITSTATUS(status)<127) { int es=WEXITSTATUS(status); lg_perror(FAIL_LG,es, "%s exited with error exit status %d;" " indicates error",desc,es); } else { lg_exitstatus(FAIL_LG,status,desc); } goto failed; } } else { if (bind(us->fd, &addr->sa, iaddr_socklen(addr))!=0) FAIL("bind (%s)",iaddr_to_string(addr)); } bool_t ok=record_socket_gotaddr(uc,us,failmsgclass); if (!ok) goto failed; return True; failed: udp_destroy_socket(uc,us); return False; } #undef FAIL void udp_socks_register(struct udpcommon *uc, struct udpsocks *socks, const char *desc) { socks->uc=uc; socks->desc=desc; socks->interest= register_for_poll(socks,udp_socks_beforepoll,udp_socks_afterpoll,"udp"); } void udp_socks_deregister(struct udpcommon *uc, struct udpsocks *socks) { socks->uc=uc; deregister_for_poll(socks->interest); } void udp_socks_childpersist(struct udpcommon *uc, struct udpsocks *socks) { int i; for (i=0; in_socks; i++) udp_destroy_socket(uc,&socks->socks[i]); } static void udp_childpersist_hook(void *sst, uint32_t new_phase) { struct udp *st=sst; udp_socks_childpersist(&st->uc,&st->socks); } static void udp_phase_hook(void *sst, uint32_t new_phase) { struct udp *st=sst; struct udpsocks *socks=&st->socks; struct udpcommon *uc=&st->uc; int i; bool_t anydone=0; for (i=0; in_socks; i++) { bool_t required=st->addr_configured || (!anydone && i==socks->n_socks-1); anydone += udp_make_socket(uc,&socks->socks[i], required ? M_FATAL : M_WARNING); } udp_socks_register(uc,socks, uc->use_proxy ? "proxy" : "socket"); add_hook(PHASE_CHILDPERSIST,udp_childpersist_hook,st); } static list_t *udp_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { static unsigned counter; struct udp *st; list_t *caddrl; list_t *l; uint32_t a; int i; COMM_APPLY(st,&st->uc.cc,udp_,"udp",loc); COMM_APPLY_STANDARD(st,&st->uc.cc,"udp",args); UDP_APPLY_STANDARD(st,&st->uc,"udp"); struct udpcommon *uc=&st->uc; struct udpsocks *socks=&st->socks; struct commcommon *cc=&uc->cc; st->counter=counter++; union iaddr defaultaddrs[] = { #ifdef CONFIG_IPV6 { .sin6 = { .sin6_family=AF_INET6, .sin6_port=htons(uc->port), .sin6_addr=IN6ADDR_ANY_INIT } }, #endif { .sin = { .sin_family=AF_INET, .sin_port=htons(uc->port), .sin_addr= { .s_addr=INADDR_ANY } } } }; caddrl=dict_lookup(d,"address"); st->addr_configured=!!caddrl; socks->n_socks=st->addr_configured ? list_length(caddrl) : (int)ARRAY_SIZE(defaultaddrs); if (socks->n_socks<=0 || socks->n_socks>UDP_MAX_SOCKETS) cfgfatal(cc->loc,"udp","`address' must be 1..%d addresses", UDP_MAX_SOCKETS); for (i=0; in_socks; i++) { struct udpsock *us=&socks->socks[i]; if (!st->addr_configured) { us->addr=defaultaddrs[i]; } else { string_item_to_iaddr(list_elem(caddrl,i),uc->port,&us->addr,"udp"); } us->fd=-1; } l=dict_lookup(d,"proxy"); if (l) { uc->use_proxy=True; uc->proxy.sa.sa_family=AF_INET; item=list_elem(l,0); if (!item || item->type!=t_string) { cfgfatal(cc->loc,"udp","proxy must supply ""addr"",port\n"); } a=string_item_to_ipaddr(item,"proxy"); uc->proxy.sin.sin_addr.s_addr=htonl(a); item=list_elem(l,1); if (!item || item->type!=t_number) { cfgfatal(cc->loc,"udp","proxy must supply ""addr"",port\n"); } uc->proxy.sin.sin_port=htons(item->data.number); } update_max_start_pad(&comm_max_start_pad, uc->use_proxy ? 8 : 0); add_hook(PHASE_GETRESOURCES,udp_phase_hook,st); return new_closure(&cc->cl); } void udp_module(dict_t *dict) { add_closure(dict,"udp",udp_apply); } work/uk.org.greenend.secnet.plist0000664000000000000000000000244115063477206014273 0ustar EnvironmentVariables LANG en_GB.UTF-8 LC_ALL en_GB.UTF-8 Label uk.org.greenend.secnet ProgramArguments /usr/local/sbin/secnet -m WorkingDirectory / RunAtLoad work/unaligned.h0000664000000000000000000000423415063477206011064 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #ifndef unaligned_h #define unaligned_h #include #include "util.h" /* Parts of the secnet key-exchange protocol require access to unaligned big-endian quantities in buffers. These macros provide convenient access, even on architectures that don't support unaligned accesses. */ #define put_uint32(a,v) do { (a)[0]=(v)>>24; (a)[1]=((v)&0xff0000)>>16; \ (a)[2]=((v)&0xff00)>>8; (a)[3]=(v)&0xff; } while(0) #define put_uint16(a,v) do {(a)[0]=((v)&0xff00)>>8; (a)[1]=(v)&0xff;} while(0) #define put_uint8(a,v) do {(a)[0]=((v)&0xff);} while(0) #define get_uint32(a) \ (((uint32_t)(a)[0]<<24) | ((uint32_t)(a)[1]<<16) | \ ((uint32_t)(a)[2]<<8) | (uint32_t)(a)[3]) #define get_uint16(a) (((uint16_t)(a)[0]<<8)|(uint16_t)(a)[1]) #define get_uint8(a) (((uint8_t)(a)[0])) #define UNALIGNED_DEF_FORTYPE(type,appre) \ static inline void buf_##appre##_##type(struct buffer_if *buf, type##_t v) \ { \ uint8_t *c=buf_##appre(buf,sizeof(type##_t)); \ put_##type(c,v); \ } \ static inline type##_t buf_un##appre##_##type(struct buffer_if *buf) \ { \ const uint8_t *c=buf_un##appre(buf,sizeof(type##_t)); \ return get_##type(c); \ } UNALIGNED_DEF_FORTYPE(uint32,append) UNALIGNED_DEF_FORTYPE(uint16,append) UNALIGNED_DEF_FORTYPE(uint8,append) UNALIGNED_DEF_FORTYPE(uint32,prepend) UNALIGNED_DEF_FORTYPE(uint16,prepend) UNALIGNED_DEF_FORTYPE(uint8,prepend) #endif /* unaligned_h */ work/util.c0000664000000000000000000004666415063477206010103 0ustar /* * util.c * - output and logging support * - program lifetime support * - IP address and subnet munging routines * - MPI convenience functions */ /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #include "secnet.h" #include #include #include #include #include #include #include #include #include "util.h" #include "unaligned.h" #include "magic.h" #include "ipaddr.h" #define MIN_BUFFER_SIZE 64 #define DEFAULT_BUFFER_SIZE 4096 #define MAX_BUFFER_SIZE 131072 static const char *hexdigits="0123456789abcdef"; uint32_t current_phase=0; struct phase_hook { hook_fn *fn; void *state; LIST_ENTRY(phase_hook) entry; }; static LIST_HEAD(, phase_hook) hooks[NR_PHASES]; char *safe_strdup(const char *s, const char *message) { char *d; d=strdup(s); if (!d) { fatal_perror("%s",message); } return d; } void *safe_malloc(size_t size, const char *message) { void *r; if (!size) return 0; r=malloc(size); if (!r) { fatal_perror("%s",message); } return r; } void *safe_realloc_ary(void *p, size_t size, size_t count, const char *message) { if (count >= INT_MAX/size) { fatal("array allocation overflow: %s", message); } assert(size && count); p = realloc(p, size*count); if (!p) fatal_perror("%s", message); return p; } void *safe_malloc_ary(size_t size, size_t count, const char *message) { if (!size || !count) return 0; return safe_realloc_ary(0,size,count,message); } void hex_encode(const uint8_t *bin, int binsize, char *buff) { int i; for (i=0; i> 4]; buff[i*2+1]=hexdigits[(bin[i] & 0xf)]; } buff[binsize*2]=0; } string_t hex_encode_alloc(const uint8_t *bin, int binsize) { char *buff; buff=safe_malloc(hex_encode_size(binsize),"hex_encode"); hex_encode(bin,binsize,buff); return buff; } static uint8_t hexval(uint8_t c) { switch (c) { case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; case 'a': return 10; case 'A': return 10; case 'b': return 11; case 'B': return 11; case 'c': return 12; case 'C': return 12; case 'd': return 13; case 'D': return 13; case 'e': return 14; case 'E': return 14; case 'f': return 15; case 'F': return 15; } return -1; } bool_t hex_decode(uint8_t *buffer, int32_t buflen, int32_t *outlen, cstring_t hb, bool_t allow_odd_nibble) { int i = 0, j = 0, l = strlen(hb), hi, lo; bool_t ok = False; if (!l || !buflen) { ok = !l; goto done; } if (l&1) { /* The number starts with a half-byte */ if (!allow_odd_nibble) goto done; lo = hexval(hb[j++]); if (lo < 0) goto done; buffer[i++] = lo; } for (; hb[j] && i < buflen; i++) { hi = hexval(hb[j++]); lo = hexval(hb[j++]); if (hi < 0 || lo < 0) goto done; buffer[i] = (hi << 4) | lo; } ok = !hb[j]; done: *outlen = i; return ok; } void read_mpbin(MP_INT *a, uint8_t *bin, int binsize) { char *buff = hex_encode_alloc(bin, binsize); mpz_set_str(a, buff, 16); free(buff); } char *write_mpstring(MP_INT *a) { char *buff; buff=safe_malloc(mpz_sizeinbase(a,16)+2,"write_mpstring"); mpz_get_str(buff, 16, a); return buff; } #define DEFINE_SETFDFLAG(fn,FL,FLAG) \ void fn(int fd) { \ int r=fcntl(fd, F_GET##FL); \ if (r<0) fatal_perror("fcntl(,F_GET" #FL ") failed"); \ r=fcntl(fd, F_SET##FL, r|FLAG); \ if (r<0) fatal_perror("fcntl(,F_SET" #FL ",|" #FLAG ") failed"); \ } DEFINE_SETFDFLAG(setcloexec,FD,FD_CLOEXEC); DEFINE_SETFDFLAG(setnonblock,FL,O_NONBLOCK); void pipe_cloexec(int fd[2]) { int r=pipe(fd); if (r) fatal_perror("pipe"); setcloexec(fd[0]); setcloexec(fd[1]); } static const char *phases[NR_PHASES]={ "PHASE_INIT", "PHASE_GETOPTS", "PHASE_READCONFIG", "PHASE_SETUP", "PHASE_DAEMONIZE", "PHASE_GETRESOURCES", "PHASE_DROPPRIV", "PHASE_RUN", "PHASE_SHUTDOWN", "PHASE_CHILDPERSIST" }; void enter_phase(uint32_t new_phase) { struct phase_hook *i; if (!LIST_EMPTY(&hooks[new_phase])) Message(M_DEBUG_PHASE,"Running hooks for %s...\n", phases[new_phase]); current_phase=new_phase; LIST_FOREACH(i, &hooks[new_phase], entry) i->fn(i->state, new_phase); Message(M_DEBUG_PHASE,"Now in %s\n",phases[new_phase]); } void phase_hooks_init(void) { int i; for (i=0; ifn=fn; h->state=state; LIST_INSERT_HEAD(&hooks[phase],h,entry); return True; } bool_t remove_hook(uint32_t phase, hook_fn *fn, void *state) { fatal("remove_hook: not implemented"); return False; } void vslilog(struct log_if *lf, int priority, const char *message, va_list ap) { lf->vlogfn(lf->st,priority,message,ap); } void slilog(struct log_if *lf, int priority, const char *message, ...) { va_list ap; va_start(ap,message); vslilog(lf,priority,message,ap); va_end(ap); } struct buffer { closure_t cl; struct buffer_if ops; }; void buffer_assert_free(struct buffer_if *buffer, cstring_t file, int line) { if (!buffer->free) { fprintf(stderr,"secnet: BUF_ASSERT_FREE, %s line %d, owned by %s", file,line,buffer->owner); assert(!"buffer_assert_free failure"); } } void buffer_assert_used(struct buffer_if *buffer, cstring_t file, int line) { if (buffer->free) { fprintf(stderr,"secnet: BUF_ASSERT_USED, %s line %d, last owned by %s", file,line,buffer->owner); assert(!"buffer_assert_used failure"); } } void buffer_init(struct buffer_if *buffer, int32_t max_start_pad) { assert(max_start_pad<=buffer->alloclen); buffer->start=buffer->base+max_start_pad; buffer->size=0; } void buffer_destroy(struct buffer_if *buf) { BUF_ASSERT_FREE(buf); free(buf->base); buf->start=buf->base=0; buf->size=buf->alloclen=0; } void *buf_append(struct buffer_if *buf, int32_t amount) { void *p; assert(amount <= buf_remaining_space(buf)); p=buf->start + buf->size; buf->size+=amount; return p; } void *buf_prepend(struct buffer_if *buf, int32_t amount) { assert(amount <= buf->start - buf->base); buf->size+=amount; return buf->start-=amount; } void *buf_unappend(struct buffer_if *buf, int32_t amount) { if (buf->size < amount) return 0; return buf->start+(buf->size-=amount); } void *buf_unprepend(struct buffer_if *buf, int32_t amount) { void *p; if (buf->size < amount) return 0; p=buf->start; buf->start+=amount; buf->size-=amount; return p; } void buf_append_string(struct buffer_if *buf, cstring_t s) { size_t len; len=strlen(s); /* fixme: if string is longer than 65535, result is a corrupted packet */ buf_append_uint16(buf,len); BUF_ADD_BYTES(append,buf,s,len); } void truncmsg_add_string(struct buffer_if *buf, cstring_t s) { int32_t l = MIN((int32_t)strlen(s), buf_remaining_space(buf)); BUF_ADD_BYTES(append, buf, s, l); } void truncmsg_add_packet_string(struct buffer_if *buf, int32_t l, const uint8_t *s) { char c; while (l-- > 0) { c = *s++; if (c >= ' ' && c <= 126 && c != '\\' && c != '"' && c != '\'') { if (!buf_remaining_space(buf)) break; buf->start[buf->size++] = c; continue; } char quoted[5]; quoted[0] = '\\'; quoted[2] = 0; switch (c) { case '\n': quoted[1] = 'n'; break; case '\r': quoted[1] = 'r'; break; case '\t': quoted[1] = 't'; break; case '\\': case '"': case '\'': quoted[1] = c; break; default: sprintf(quoted, "\\x%02x", (unsigned)c); } truncmsg_add_string(buf, quoted); } } const char *truncmsg_terminate(const struct buffer_if *buf) { if (buf_remaining_space(buf)) { buf->start[buf->size] = 0; } else { assert(buf->size >= 4); strcpy(buf->start + buf->size - 4, "..."); } return buf->start; } void priomsg_new(struct priomsg *pm, int32_t maxlen) { buffer_new(&pm->m, maxlen); pm->prio = INT_MIN; } void priomsg_reset(struct priomsg *pm) { buffer_init(&pm->m, 0); pm->prio = INT_MIN; } bool_t priomsg_update_p(struct priomsg *pm, int prio) { if (!pm) return False; if (prio <= pm->prio) return False; buffer_init(&pm->m, 0); pm->prio = prio; return True; } const char *priomsg_getmessage(const struct priomsg *pm, const char *defmsg) { if (pm->prio >= INT_MIN) return truncmsg_terminate(&pm->m); else return defmsg; } bool_t priomsg_update_fixed(struct priomsg *pm, int prio, const char *m) { if (!priomsg_update_p(pm, prio)) return False; truncmsg_add_string(&pm->m, m); return True; } void buffer_new(struct buffer_if *buf, int32_t len) { buf->free=True; buf->owner=NULL; buf->loc.file=NULL; buf->loc.line=0; buf->size=0; buf->alloclen=len; buf->start=NULL; buf->base=safe_malloc(len,"buffer_new"); } void buffer_readonly_view(struct buffer_if *buf, const void *data, int32_t len) { buf->free=False; buf->owner="READONLY"; buf->loc.file=NULL; buf->loc.line=0; buf->size=buf->alloclen=len; buf->base=buf->start=(uint8_t*)data; } void buffer_readonly_clone(struct buffer_if *out, const struct buffer_if *in) { buffer_readonly_view(out,in->start,in->size); } void buffer_copy(struct buffer_if *dst, const struct buffer_if *src) { if (dst->alloclen < src->alloclen) { dst->base=realloc(dst->base,src->alloclen); if (!dst->base) fatal_perror("buffer_copy"); dst->alloclen = src->alloclen; } dst->start = dst->base + (src->start - src->base); dst->size = src->size; memcpy(dst->start, src->start, dst->size); } static list_t *buffer_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { struct buffer *st; item_t *item; dict_t *dict; bool_t lockdown=False; uint32_t len=DEFAULT_BUFFER_SIZE; NEW(st); st->cl.description="buffer"; st->cl.type=CL_BUFFER; st->cl.apply=NULL; st->cl.interface=&st->ops; /* First argument, if present, is buffer length */ item=list_elem(args,0); if (item) { if (item->type!=t_number) { cfgfatal(st->ops.loc,"buffer","first parameter must be a " "number (buffer size)\n"); } len=item->data.number; if (lenops.loc,"buffer","ludicrously small buffer size\n"); } if (len>MAX_BUFFER_SIZE) { cfgfatal(st->ops.loc,"buffer","ludicrously large buffer size\n"); } } /* Second argument, if present, is a dictionary */ item=list_elem(args,1); if (item) { if (item->type!=t_dict) { cfgfatal(st->ops.loc,"buffer","second parameter must be a " "dictionary\n"); } dict=item->data.dict; lockdown=dict_read_bool(dict,"lockdown",False,"buffer",st->ops.loc, False); } buffer_new(&st->ops,len); if (lockdown) { /* XXX mlock the buffer if possible */ } return new_closure(&st->cl); } void send_nak(const struct comm_addr *dest, uint32_t our_index, uint32_t their_index, uint32_t msgtype, struct buffer_if *buf, const char *logwhy) { buffer_init(buf,calculate_max_start_pad()); buf_append_uint32(buf,their_index); buf_append_uint32(buf,our_index); buf_append_uint32(buf,LABEL_NAK); if (logwhy) Message(M_INFO,"%s: sending NAK for" " %08"PRIx32" %08"PRIx32"<-%08"PRIx32":" " %s\n", comm_addr_to_string(dest), msgtype, our_index, their_index, logwhy); dest->comm->sendmsg(dest->comm->st, buf, dest, 0); } int consttime_memeq(const void *s1in, const void *s2in, size_t n) { const uint8_t *s1=s1in, *s2=s2in; register volatile uint8_t accumulator=0; while (n-- > 0) { accumulator |= (*s1++ ^ *s2++); } accumulator |= accumulator >> 4; /* constant-time */ accumulator |= accumulator >> 2; /* boolean canonicalisation */ accumulator |= accumulator >> 1; accumulator &= 1; accumulator ^= 1; return accumulator; } void hash_hash(const struct hash_if *hashi, const void *msg, int32_t len, uint8_t *digest) { uint8_t hst[hashi->slen]; hashi->init(hst); hashi->update(hst,msg,len); hashi->final(hst,digest); } void util_module(dict_t *dict) { add_closure(dict,"sysbuffer",buffer_apply); } void update_max_start_pad(int32_t *our_module_global, int32_t our_instance) { if (*our_module_global < our_instance) *our_module_global=our_instance; } int32_t transform_max_start_pad, comm_max_start_pad; int32_t calculate_max_start_pad(void) { return site_max_start_pad + transform_max_start_pad + comm_max_start_pad; } void vslilog_part(struct log_if *lf, int priority, const char *message, va_list ap) { char *buff=lf->buff; size_t bp; char *nlp; bp=strlen(buff); assert(bp < LOG_MESSAGE_BUFLEN); vsnprintf(buff+bp,LOG_MESSAGE_BUFLEN-bp,message,ap); buff[LOG_MESSAGE_BUFLEN-1] = '\n'; buff[LOG_MESSAGE_BUFLEN] = '\0'; /* Each line is sent separately */ while ((nlp=strchr(buff,'\n'))) { *nlp=0; slilog(lf,priority,"%s",buff); memmove(buff,nlp+1,strlen(nlp+1)+1); } } extern void slilog_part(struct log_if *lf, int priority, const char *message, ...) { va_list ap; va_start(ap,message); vslilog_part(lf,priority,message,ap); va_end(ap); } void string_item_to_iaddr(const item_t *item, uint16_t port, union iaddr *ia, const char *desc) { #ifndef CONFIG_IPV6 ia->sin.sin_family=AF_INET; ia->sin.sin_addr.s_addr=htonl(string_item_to_ipaddr(item,desc)); ia->sin.sin_port=htons(port); #else /* CONFIG_IPV6 => we have adns_text2addr */ if (item->type!=t_string) cfgfatal(item->loc,desc,"expecting a string IP (v4 or v6) address\n"); socklen_t salen=sizeof(*ia); int r=adns_text2addr(item->data.string, port, adns_qf_addrlit_ipv4_quadonly, &ia->sa, &salen); assert(r!=ENOSPC); if (r) cfgfatal(item->loc,desc,"invalid IP (v4 or v6) address: %s\n", strerror(r)); #endif /* CONFIG_IPV6 */ } #define IADDR_NBUFS 8 const char *iaddr_to_string(const union iaddr *ia) { #ifndef CONFIG_IPV6 SBUF_DEFINE(IADDR_NBUFS, 100); assert(ia->sa.sa_family == AF_INET); snprintf(SBUF, sizeof(SBUF), "[%s]:%d", inet_ntoa(ia->sin.sin_addr), ntohs(ia->sin.sin_port)); #else /* CONFIG_IPV6 => we have adns_addr2text */ SBUF_DEFINE(IADDR_NBUFS, 1+ADNS_ADDR2TEXT_BUFLEN+20); int port; char *addrbuf = SBUF; *addrbuf++ = '['; int addrbuflen = ADNS_ADDR2TEXT_BUFLEN; int r = adns_addr2text(&ia->sa, 0, addrbuf, &addrbuflen, &port); if (r) { const char fmt[]= "bad addr, error: %.*s"; sprintf(addrbuf, fmt, (int)(ADNS_ADDR2TEXT_BUFLEN - sizeof(fmt)) /* underestimate */, strerror(r)); } char *portbuf = addrbuf; int addrl = strlen(addrbuf); portbuf += addrl; snprintf(portbuf, sizeof(SBUF)-addrl, "]:%d", port); #endif /* CONFIG_IPV6 */ return SBUF; } bool_t iaddr_equal(const union iaddr *ia, const union iaddr *ib, bool_t ignoreport) { if (ia->sa.sa_family != ib->sa.sa_family) return 0; switch (ia->sa.sa_family) { case AF_INET: return ia->sin.sin_addr.s_addr == ib->sin.sin_addr.s_addr && (ignoreport || ia->sin.sin_port == ib->sin.sin_port); #ifdef CONFIG_IPV6 case AF_INET6: return !memcmp(&ia->sin6.sin6_addr, &ib->sin6.sin6_addr, 16) && ia->sin6.sin6_scope_id == ib->sin6.sin6_scope_id && (ignoreport || ia->sin6.sin6_port == ib->sin6.sin6_port) /* we ignore the flowinfo field */; #endif /* CONFIG_IPV6 */ default: abort(); } } int iaddr_socklen(const union iaddr *ia) { switch (ia->sa.sa_family) { case AF_INET: return sizeof(ia->sin); #ifdef CONFIG_IPV6 case AF_INET6: return sizeof(ia->sin6); #endif /* CONFIG_IPV6 */ default: abort(); } } const char *pollbadbit(int revents) { #define BADBIT(b) \ if ((revents & b)) return #b BADBIT(POLLERR); BADBIT(POLLHUP); /* POLLNVAL is handled by the event loop - see afterpoll_fn comment */ #undef BADBIT return 0; } void pathprefix_template_init(struct pathprefix_template *out, const char *prefix, int maxsuffix) { size_t l=strlen(prefix); NEW_ARY(out->buffer,l+maxsuffix+1); strcpy(out->buffer,prefix); out->write_here=out->buffer+l; } enum async_linebuf_result async_linebuf_read(struct pollfd *pfd, struct buffer_if *buf, const char **emsg_out) { int revents=pfd->revents; #define BAD(m) do{ *emsg_out=(m); return async_linebuf_broken; }while(0) const char *badbit=pollbadbit(revents); if (badbit) BAD(badbit); if (!(revents & POLLIN)) return async_linebuf_nothing; /* * Data structure: A line which has been returned to the user is * stored in buf at base before start. But we retain the usual * buffer meaning of size. So: * * | returned : | input read, | unused | * | to user : \0 | awaiting | buffer | * | : | processing | space | * | : | | | * ^base ^start ^start+size ^base+alloclen */ BUF_ASSERT_USED(buf); /* firstly, eat any previous */ if (buf->start != buf->base) { memmove(buf->base,buf->start,buf->size); buf->start=buf->base; } uint8_t *searched=buf->base; /* * During the workings here we do not use start. We set start * when we return some actual data. So we have this: * * | searched | read, might | unused | * | for \n | contain \n | buffer | * | none found | but not \0 | space | * | | | | * ^base ^searched ^base+size ^base+alloclen * [^start] ^dataend * */ for (;;) { uint8_t *dataend=buf->base+buf->size; char *newline=memchr(searched,'\n',dataend-searched); if (newline) { *newline=0; buf->start=newline+1; buf->size=dataend-buf->start; return async_linebuf_ok; } searched=dataend; ssize_t space=(buf->base+buf->alloclen)-dataend; if (!space) BAD("input line too long"); ssize_t r=read(pfd->fd,searched,space); if (r==0) { *searched=0; *emsg_out=buf->size?"no newline at eof":0; buf->start=searched+1; buf->size=0; return async_linebuf_eof; } if (r<0) { if (errno==EINTR) continue; if (iswouldblock(errno)) return async_linebuf_nothing; BAD(strerror(errno)); } assert(r<=space); if (memchr(searched,0,r)) BAD("nul in input data"); buf->size+=r; } #undef BAD } work/util.h0000664000000000000000000002653415063477206010102 0ustar /* * This file is part of secnet. * See README for full list of copyright holders. * * secnet 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. * * secnet 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 * version 3 along with secnet; if not, see * https://www.gnu.org/licenses/gpl.html. */ #ifndef util_h #define util_h #include "secnet.h" #include #include "hackypar.h" #define BUF_ASSERT_FREE(buf) do { buffer_assert_free((buf), \ __FILE__,__LINE__); } \ while(0) #define BUF_ASSERT_USED(buf) do { buffer_assert_used((buf), \ __FILE__,__LINE__); } \ while(0) #define BUF_ALLOC(buf,own) do { buffer_assert_free((buf),__FILE__,__LINE__); \ (buf)->free=False; (buf)->owner=(own); (buf)->start=(buf)->base; \ (buf)->size=0; } while(0) #define BUF_FREE(buf) do { (buf)->free=True; } while(0) extern void buffer_assert_free(struct buffer_if *buffer, cstring_t file, int line); extern void buffer_assert_used(struct buffer_if *buffer, cstring_t file, int line); extern void buffer_new(struct buffer_if *buffer, int32_t len); extern void buffer_init(struct buffer_if *buffer, int32_t max_start_pad); extern void buffer_destroy(struct buffer_if *buffer); extern void buffer_copy(struct buffer_if *dst, const struct buffer_if *src); extern void *buf_append(struct buffer_if *buf, int32_t amount); extern void *buf_prepend(struct buffer_if *buf, int32_t amount); extern void *buf_unappend(struct buffer_if *buf, int32_t amount); extern void *buf_unprepend(struct buffer_if *buf, int32_t amount); /* These construct a message in a buffer, truncating if necessary. * _string is only safe for trusted input and *not* UTF-8 (sorry). * _packet_string is safe for any input, including untrusted. * _terminate arranges for the buffer to be null-terminated (and * maybe for a trailing `...' to indicate truncation), and returns * a pointer to the null-terminated string. */ void truncmsg_add_string(struct buffer_if *buf, cstring_t s); void truncmsg_add_packet_string(struct buffer_if*, int32_t, const uint8_t*); const char *truncmsg_terminate(const struct buffer_if *buf); struct priomsg { /* U: uninitialised * F: initialised but free (no memory allocated), no leak if discarded * Z: contains no message yet * M: contains some message; you may call truncmsg_add_* */ int prio; struct buffer_if m; }; void priomsg_new(struct priomsg *pm, int32_t maxlen); /* UF -> Z */ void priomsg_destroy(struct priomsg *pm, int32_t maxlen); /* FZM -> F */ void priomsg_reset(struct priomsg *pm); /* FZM -> Z */ bool_t priomsg_update_p(struct priomsg *pm, int prio); /* ZM -> M */ /* returns true iff message of priority prio ought to be added, * caller should then call truncmsg_add_*. * pm may be NULL, in which case it just returns false */ const char *priomsg_getmessage(const struct priomsg *pm, const char *defmsg); /* return value is null-terminated, valid until next call * or until defmsg is no longer valid ZM */ bool_t priomsg_update_fixed(struct priomsg *pm, int prio, const char *m); /* convenience combination of _update_p and truncmsg_add_string */ /* * void BUF_ADD_BYTES(append, struct buffer_if*, const void*, int32_t size); * void BUF_ADD_BYTES(prepend, struct buffer_if*, const void*, int32_t size); * void BUF_GET_BYTES(unappend, struct buffer_if*, void*, int32_t size); * void BUF_GET_BYTES(unprepend, struct buffer_if*, void*, int32_t size); * // all of these evaluate size twice * * void BUF_ADD_OBJ(append, struct_buffer_if*, const OBJECT& something); * void BUF_ADD_OBJ(prepend, struct_buffer_if*, const OBJECT& something); * void BUF_GET_OBJ(unappend, struct_buffer_if*, OBJECT& something); * void BUF_GET_OBJ(unprepend, struct_buffer_if*, OBJECT& something); */ #define BUF_ADD_BYTES(appendprepend, bufp, datap, size) \ (buf_un##appendprepend /* ensures we have correct direction */, \ memcpy(buf_##appendprepend((bufp),(size)),(datap),(size))) #define BUF_ADD_OBJ(appendprepend, bufp, obj) \ BUF_ADD_BYTES(appendprepend,(bufp),&(obj),sizeof((obj))) #define BUF_GET_BYTES(unappendunprepend, bufp, datap, size) \ (BUF_GET__DOESNOTEXIST__buf_un##unappendunprepend, \ memcpy((datap),buf_##unappendunprepend((bufp),(size)),(size))) #define BUF_GET_OBJ(unappendunprepend, bufp, obj) \ BUF_ADD_BYTES(unappendunprepend,&(obj),(bufp),sizeof((obj))) #define BUF_GET__DOESNOTEXIST__buf_ununappend 0 #define BUF_GET__DOESNOTEXIST__buf_ununprepend 0 static inline int32_t buf_remaining_space(const struct buffer_if *buf) { return (buf->base + buf->alloclen) - (buf->start + buf->size); } extern void buffer_readonly_view(struct buffer_if *n, const void*, int32_t len); extern void buffer_readonly_clone(struct buffer_if *n, const struct buffer_if*); /* Caller must only use unappend, unprepend et al. on n. * New buffer state (in n) before this can be undefined. After use, * it must NOT be freed. */ extern void buf_append_string(struct buffer_if *buf, cstring_t s); /* Append a two-byte length and the string to the buffer. Length is in * network byte order. */ static inline int hex_encode_size(int binsize) { return binsize*2 + 1; } extern void hex_encode(const uint8_t *bin, int binsize, char *buf); /* Convert a byte array to hex into a supplied buffer. */ extern string_t hex_encode_alloc(const uint8_t *bin, int binsize); /* Returns the result in a freshly allocated string. */ extern bool_t hex_decode(uint8_t *buffer, int32_t buflen, int32_t *outlen, cstring_t hb, bool_t allow_odd_nibble); /* Convert a hex string to binary, storing the result in buffer. If * allow_odd_nibble then it is acceptable if the input is an odd number of * digits, and an additional leading zero digit is assumed; otherwise, this * is not acceptable and the conversion fails. * * The input is processed left to right until it is consumed, the buffer is * full, or an error is encountered in the input. The length of output * produced is stored in *outlen. Returns true if the entire input was * processed without error; otherwise false. */ extern void read_mpbin(MP_INT *a, uint8_t *bin, int binsize); /* Convert a buffer into its MP_INT representation */ extern char *write_mpstring(MP_INT *a); /* Convert a MP_INT into a hex string */ extern struct log_if *init_log(list_t *loglist); extern void send_nak(const struct comm_addr *dest, uint32_t our_index, uint32_t their_index, uint32_t msgtype, struct buffer_if *buf, const char *logwhy); extern int consttime_memeq(const void *s1, const void *s2, size_t n); void hash_hash(const struct hash_if *hashi, const void *msg, int32_t len, uint8_t *digest /* hi->hlen bytes */); const char *iaddr_to_string(const union iaddr *ia); int iaddr_socklen(const union iaddr *ia); void string_item_to_iaddr(const item_t *item, uint16_t port, union iaddr *ia, const char *desc); /*----- pathprefix_template -----*/ struct pathprefix_template { char *buffer; char *write_here; }; void pathprefix_template_init(struct pathprefix_template *out, const char *prefix, int maxsuffix); static inline void pathprefix_template_setsuffix (struct pathprefix_template *upd, const char *suffix) { strcpy(upd->write_here,suffix); } /* * SBUF_DEFINE(int nbufs, size_t size); * // Generates a number of definitions and statements organising * // nbufs rotating char[size] buffers such that subsequent code * // may refer to: * char *const SBUF; */ #define SBUF_DEFINE(nbufs, size) \ static int static_bufs__bufnum; \ static char static_bufs__bufs[(nbufs)][(size)]; \ static_bufs__bufnum++; \ static_bufs__bufnum %= (nbufs); \ static_bufs__bufs[static_bufs__bufnum] #define SBUF (static_bufs__bufs[static_bufs__bufnum]) /*----- line-buffered asynch input -----*/ enum async_linebuf_result { async_linebuf_nothing, async_linebuf_ok, async_linebuf_eof, async_linebuf_broken, }; const char *pollbadbit(int revents); /* returns 0, or bad bit description */ enum async_linebuf_result async_linebuf_read(struct pollfd *pfd, struct buffer_if *buf, const char **emsg_out); /* Implements reading whole lines, asynchronously. Use like * this: * - set up the fd, which should be readable, O_NONBLOCK * - set up and initialise buffer, which should be big enough * for one line plus its trailing newline, and be empty * with start==base * - in your beforepoll_fn, be interested in POLLIN * - in your afterpoll_fn, repeatedly call this function * until it doesn't return `nothing' * - after you're done, simply close fd and free or reset buf * State on return from async_linebuf_read depends on return value: * * async_linebuf_nothing: * * No complete lines available right now. You should return * from afterpoll. buf should be left untouched until the * next call to async_linebuf_read. * * async_linebuf_ok: * * buf->base contains a input line as a nul-terminated string * (\n replaced by \0); *emsg_out==0. You must call * async_linebuf_read again before returning from afterpoll. * * async_linebuf_eof: * * EOF on stream. buf->base contains any partial * (non-newline-terminated) line; *emsg_out!=0 iff there was * such a partial line. You can call async_linebuf_read again * if you like but it will probably just return eof again. * * broken: * * Fatal problem (might be overly long lines, nuls in input * data, bad bits in pfd->revents, errors from read, etc.) * * *emsg_out is the error message describing the problem; * this message might be stored in buf, might be from * strerror, or might be a constant. * * You must not call async_linebuf_read again. buf contents * is undefined: it is only safe to reset or free. * * While using this function, do not look at buf->start or ->size * or anything after the first '\0' in buf. * * If you decide to stop reading with async_linebuf_read that's * fine and you can reset or free buf, but you risk missing some * read-but-not-reported data. */ /*----- some handy macros -----*/ #define CL_GET_STR_ARG(ix,vn,what) \ item_t *vn##_i=list_elem(args,ix); \ if (!vn##_i) cfgfatal(loc,"make-public","need " what); \ if (vn##_i->type!=t_string) cfgfatal(vn##_i->loc,"make-public", \ what "must be string"); \ const char *vn=vn##_i->data.string #define MINMAX(ae,be,op) ({ \ typeof((ae)) a=(ae); \ typeof((be)) b=(be); \ a op b ? a : b; \ }) #define MAX(a,b) MINMAX((a),(b),>) #define MIN(a,b) MINMAX((a),(b),<) #define MAX_RAW(a,b) ((a)>(b)?(a):(b)) #define MIN_RAW(a,b) ((a)<(b)?(a):(b)) static inline bool_t iswouldblock(int e) { return e==EWOULDBLOCK || e==EAGAIN; } #endif /* util_h */