From: Ian Jackson Date: Sun, 29 Dec 2019 14:23:00 +0000 (+0000) Subject: Merge subdirmk X-Git-Tag: v0.6.0~251 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=commitdiff_plain;h=3a2c5e1f10dc6b3cc86f74c287d8c14be14d7e80;hp=411c2cad33613d6790cb819e8bd91a7f58a3998f;p=secnet.git Merge subdirmk --- diff --git a/.dir-locals.el b/.dir-locals.el new file mode 100644 index 0000000..7ca463d --- /dev/null +++ b/.dir-locals.el @@ -0,0 +1,8 @@ +;;; 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 . 8))) diff --git a/.gitignore b/.gitignore index a5b02c2..b166c42 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,49 @@ -# subdirmk - subdirmk/.gitignore -# Copyright 2019 Mark Wooding -# Copyright 2019 Ian Jackson -# SPDX-License-Identifier: LGPL-2.0-or-later - -#----- subdirmk-generated ----- -/regen.mk -/usual.mk +*.o +.*.d +*.pyc +conffile.tab.[ch] +conffile.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 +Subdir.mk +/main.mk + +debian/files +debian/secnet.debhelper.log +debian/*.debhelper +debian/secnet +debian/secnet.substvars +*.xcodeproj +/build + +test-example/*.key +test-example/sites.conf +test-example/bogus-setup-request +build-stamp + +[sm]test/d-* +stest/udp-preload.so diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..02a2de3 --- /dev/null +++ b/BUGS @@ -0,0 +1,11 @@ +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. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/CREDITS b/CREDITS new file mode 100644 index 0000000..6cb5490 --- /dev/null +++ b/CREDITS @@ -0,0 +1,17 @@ +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 + +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 diff --git a/DEVELOPER-CERTIFICATE b/DEVELOPER-CERTIFICATE deleted file mode 100644 index 912d22e..0000000 --- a/DEVELOPER-CERTIFICATE +++ /dev/null @@ -1,38 +0,0 @@ -Developer Certificate of Origin -Version 1.1 - -Copyright (C) 2004, 2006 The Linux Foundation and its contributors. -1 Letterman Drive -Suite D4700 -San Francisco, CA, 94129 - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. - - -Developer's Certificate of Origin 1.1 - -By making a contribution to this project, I certify that: - -(a) The contribution was created in whole or in part by me and I - have the right to submit it under the open source license - indicated in the file; or - -(b) The contribution is based upon previous work that, to the best - of my knowledge, is covered under an appropriate open source - license and I have the right under that license to submit that - work with modifications, whether created in whole or in part - by me, under the same open source license (unless I am - permitted to submit under a different license), as indicated - in the file; or - -(c) The contribution was provided directly to me by some other - person who certified (a), (b) or (c) and I have not modified - it. - -(d) I understand and agree that this project and the contribution - are public and that a record of the contribution (including all - personal information I submit with it, including my sign-off) is - maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. - diff --git a/DEVELOPER-CERTIFICATE b/DEVELOPER-CERTIFICATE new file mode 120000 index 0000000..d56384e --- /dev/null +++ b/DEVELOPER-CERTIFICATE @@ -0,0 +1 @@ +subdirmk/DEVELOPER-CERTIFICATE \ No newline at end of file diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..c9db174 --- /dev/null +++ b/INSTALL @@ -0,0 +1,188 @@ +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 diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..8094c3e --- /dev/null +++ b/NEWS @@ -0,0 +1,331 @@ +* 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. diff --git a/NOTES b/NOTES new file mode 100644 index 0000000..884ae30 --- /dev/null +++ b/NOTES @@ -0,0 +1,380 @@ +* 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. + +** 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. + * 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. Sadly, + advertising an early capability flag will produce MSG1s which are + not understood by versions of secnet which predate the capability + mechanism. + +2. Late capability flags are advertised in MSG2 or MSG3, as + applicable. They may also appear in MSG1, but this is not + guaranteed. 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. diff --git a/Perdir.sd.mk b/Perdir.sd.mk new file mode 100644 index 0000000..c5ca200 --- /dev/null +++ b/Perdir.sd.mk @@ -0,0 +1,5 @@ +# &TARGETS_check +# &TARGETS_fullcheck + +&:include subdirmk/cdeps.sd.mk +&:include subdirmk/clean.sd.mk diff --git a/README b/README index 27dd855..ce3a4d7 100644 --- a/README +++ b/README @@ -1,416 +1,598 @@ -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). - -Basic approach --------------- - -The developer is expected to write a makefile fragment, in each -relevant subdirectory, called `Subdir.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 Subdir.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 Subdir.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 Subdir.sd.mk -can simply refer to files in other subdirectories directly. - -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'. +secnet - flexible VPN software -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/Subdir.sd.mk, &TARGETS of course refers to a make variable called -src_TARGETS.) +* Copying -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: simply -mention (usually, by setting) the variable &TARGETS_zonk, or whatever. -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 mentioned in src but not lib, `make zonk' in -lib will fail. If you want to make a target exist everywhere, -mention its name in Perdir.sd.mk (see below). - -Perdir.sd.mk, inclusion ------------------------ - -The file Perdir.sd.mk in the toplevel of the source is automatically -processed after each individual directory's Subdir.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 Perdir.sd.mk. - -The top-level Subdir.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 Subdir.sd.mk. - -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)). - -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 Subdir.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'. - -Subdirectory 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 Subdir.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 Subdir.sd.mk - Subdir and Perdir) - -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 Subdir.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.) - -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. - -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: +secnet is + Copyright 1995-2003 Stephen Early + Copyright 2002-2014 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 2000 Vincent Rijmen, Antoon Bosselaers, Paulo Barreto + 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 -&_ => sub_dir_ or TOP_ -&=_ => sub_dir or TOP +secnet is distributed under the terms of the GNU General Public +License, version 3 or later. Some individual files have more +permissive licences; where this is the case, it is documented in the +header comment for the files in question. + +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 contains a copy of the GNU GPL v3. + + +* 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). -&/ => sub/dir/ or nothing -&=/ => sub/dir or . +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. -&^lc => $(top_srcdir)/sub/dir/lc -&^/ => $(top_srcdir)/sub/dir/ +Example: -&~lc => $(top_srcdir)/lc -&~/ => $(top_srcdir)/ +a=1; +b=2; +c={ d=3; e=a; }; +f={ a=4; g=c; }; -In general: - = return subdir without delimiter (not allowed with `^' `~') - ^ pathname of this subdirectory in source tree - ~ pathname of top level of source tree - / terminates the escape (needed if next is not lwsp or space) - lwsp starts multi-word processing (see below) - -So pathname syntax is a subset of: - '&' [ '^' | '~' ] [ lc | '/' ] - -&& => && for convenience in shell runes -\& => & general escaping mechanism - -& 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) - args are processed for & - -&:include filename filename should usually be foo.sd.mk -&:-include filename tolerate nonexistent file - filenames are relative to $(top_srcdir) - -&! disables & until EOL (and then disappears) - -&# delete everything to end of line - (useful if the RHS contains unrecognised & constructions) - -&!STUFF - changes the escape sequence from & to literally STUFF - STUFF may be any series of of non-whitespace characters, - and is terminated by EOL or lwsp. &!STUFF and the lwsp - are discarded. - - After this, write STUFF instead of &, everywhere. - The effect is global and lasts until the next setting. - It takes effect on &:include'd files too, so maybe set - it back before using &:include. - - Notably - STUFFSTUFF => STUFFSTUFF - \STUFF => STUFF - STUFF!& set escape back to & - -&TARGETS_things - Handled specially. If mentioned, 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. But mentioning &TARGETS_things in - a #-comment *does* work because the & filter does not care - about comments. - - `all' is extra special: every directory has an `all' - target, which corresponds to &TARGETS. - -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. - -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. - -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 Subdir.sd.mk in each directory. The toplevel one should -probably contain: - - include subdirmk/usual.mk - include subdirmk/regen.mk - -Write a Perdir.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 Subdir.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 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 Subdir.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. - - -Legal information ------------------ - -subdirmk is - Copyright 2019 Mark Wooding - Copyright 2019 Ian Jackson - - 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+.) +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 + +* 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 + 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) + local-key (rsaprivkey closure) + 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 + key (rsapubkey closure): our peer's public key + transform (transform closure): how to mangle packets sent between sites + dh (dh closure) + hash (hash 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. + +** rsa + +Defines: + rsa-private (closure => rsaprivkey closure) + rsa-public (closure => rsapubkey closure) + +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) + +** 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. diff --git a/README.mac b/README.mac new file mode 100644 index 0000000..c84a84f --- /dev/null +++ b/README.mac @@ -0,0 +1,83 @@ +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 diff --git a/README.make-secnet-sites b/README.make-secnet-sites new file mode 100644 index 0000000..ea767e9 --- /dev/null +++ b/README.make-secnet-sites @@ -0,0 +1,235 @@ +USAGE + + make-secnet-sites [-P PREFIX] [IN [OUT]] + make-secnet-sites -u HEADER GRPDIR SITESFILE GROUP + + The `-P' option sets the PREFIX string, mentioned below in + `OUTPUT STRUCTURE'; the default is empty. + + In the former mode, `make-secnet-sites' reads a single input + file from IN (defaulting to standard input), and writes a Secnet + configuration fragment to OUT (defaulting to standard output). + + In the latter, `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. + + +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; required at site level. + + mobile BOOL + Assigns BOOL to the `mobile' key. Acceptable only at + site level, but optional. + + +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. diff --git a/Subdir.sd.mk b/Subdir.sd.mk new file mode 100644 index 0000000..e0096df --- /dev/null +++ b/Subdir.sd.mk @@ -0,0 +1,255 @@ +# 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.5.1 + +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) + +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 \ + 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 @LIBOBJS@ \ + hackypar.o +# version.o is handled specially below and in the link rule for secnet. + +PYMODULES := ipaddrset.py argparseactionnoyes.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 \ + ))) + +%.c: %.y + +%.yy.c: %.fl + flex --header=$*.yy.h -o$@ $< + +%.tab.c %.tab.h: %.y + bison -d -o $@ $< + +%.o: %.c conffile.yy.h + $(CC) $(CPPFLAGS) $(ALL_CFLAGS) $(CDEPS_CFLAGS) -c $< -o $@ + +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.fl conffile.tab.c +conffile.yy.h: conffile.yy.c +conffile.tab.c: conffile.y +# End of manual dependencies section + +conffile.yy.o: ALL_CFLAGS += -Wno-sign-compare + +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 -f $(&TARGETS_check) + rm -rf $(addsuffix /d-*, $(TESTDIRS)) + $(MAKE) -f main.mk 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 $@ + +.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 + +# Release checklist: +# +# 0. Use this checklist from Subdir.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/ +# +# 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 ~ diff --git a/TODO b/TODO new file mode 100644 index 0000000..2d5e447 --- /dev/null +++ b/TODO @@ -0,0 +1,34 @@ +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 diff --git a/ac_prog_cc_no_writeable_strings.m4 b/ac_prog_cc_no_writeable_strings.m4 new file mode 100644 index 0000000..ee7aa5d --- /dev/null +++ b/ac_prog_cc_no_writeable_strings.m4 @@ -0,0 +1,143 @@ +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 appears to be a previous version of +dnl ax_cflags_no_writable_strings.m4 which is nowadays to be found in +dnl the Autoconf Archive. It was imported there on 2007-02-14 +dnl in commit 16aee45643e593e2833e4dff19df7b5f14267a79 where the file +dnl has a GPLv2 permission notice. Therefore I feel justified in +dnl adding the copyright permission notice below: -iwj] +dnl +dnl This file is Free Software. It has been copied into secnet. +dnl +dnl Copyright 2002 Guido Draheim +dnl +dnl You may redistribute secnet as a whole and/or modify it under the +dnl terms of the GNU General Public License as published by the Free +dnl Software Foundation; either version 3, or (at your option) any +dnl later version. +dnl +dnl You may redistribute this file and/or modify it under the terms of +dnl the GNU General Public License as published by the Free Software +dnl Foundation; either version 2, or (at your option) any later +dnl version. +dnl +dnl This software is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this software; if not, see +dnl https://www.gnu.org/licenses/gpl.html. + +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 +]) + + diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..970fb7d --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,35 @@ +# 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.]) + ]) +]) diff --git a/aes.c b/aes.c new file mode 100644 index 0000000..d82c81d --- /dev/null +++ b/aes.c @@ -0,0 +1,1350 @@ +/* + * 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); + } + } +} diff --git a/aes.h b/aes.h new file mode 100644 index 0000000..23259f6 --- /dev/null +++ b/aes.h @@ -0,0 +1,103 @@ +/* + * 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 */ diff --git a/argparseactionnoyes.py b/argparseactionnoyes.py new file mode 100644 index 0000000..a7eef77 --- /dev/null +++ b/argparseactionnoyes.py @@ -0,0 +1,37 @@ +# 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) diff --git a/autogen.sh b/autogen.sh index e28e80e..dddafac 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,7 +1,22 @@ #!/bin/sh -# subdirmk, autogen.sh (conventional autoconf invocation script) -# Copyright 2019 Ian Jackson -# SPDX-License-Identifier: LGPL-2.0-or-later +# +# 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 -cd ${0%/*} autoconf +autoheader diff --git a/comm-common.c b/comm-common.c new file mode 100644 index 0000000..48041cd --- /dev/null +++ b/comm-common.c @@ -0,0 +1,82 @@ +/* + * 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); +} diff --git a/comm-common.h b/comm-common.h new file mode 100644 index 0000000..45340e4 --- /dev/null +++ b/comm-common.h @@ -0,0 +1,146 @@ +/* + * 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) notify; + +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*/ diff --git a/common.make.in b/common.make.in new file mode 100644 index 0000000..7c073b1 --- /dev/null +++ b/common.make.in @@ -0,0 +1,35 @@ +# 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 diff --git a/comprehensive-test b/comprehensive-test new file mode 100755 index 0000000..d8cf07d --- /dev/null +++ b/comprehensive-test @@ -0,0 +1,63 @@ +#!/bin/bash +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 + +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 -----" diff --git a/conffile.c b/conffile.c new file mode 100644 index 0000000..c03c1d8 --- /dev/null +++ b/conffile.c @@ -0,0 +1,843 @@ +/* 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 fail_if_invalid, cstring_t desc, struct cloc loc) +{ + item_t *i; + closure_t *cl; + + i = dict_find_item(dict,name,fail_if_invalid,desc,loc); + if (i->type!=t_closure) { + if (!fail_if_invalid) return NULL; + cfgfatal(loc,desc,"\"%s\" must be a closure\n",name); + } + cl=i->data.closure; + if (cl->type!=type) { + if (!fail_if_invalid) return NULL; + cfgfatal(loc,desc,"\"%s\" is the wrong type of closure\n",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); +} diff --git a/conffile.fl b/conffile.fl new file mode 100644 index 0000000..6b0259c --- /dev/null +++ b/conffile.fl @@ -0,0 +1,172 @@ +/* + * 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; diff --git a/conffile.h b/conffile.h new file mode 100644 index 0000000..c4834b1 --- /dev/null +++ b/conffile.h @@ -0,0 +1,27 @@ +/* + * 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 */ diff --git a/conffile.y b/conffile.y new file mode 100644 index 0000000..9f32cfd --- /dev/null +++ b/conffile.y @@ -0,0 +1,113 @@ +%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; +} diff --git a/conffile_internal.h b/conffile_internal.h new file mode 100644 index 0000000..c2127ab --- /dev/null +++ b/conffile_internal.h @@ -0,0 +1,69 @@ +/* + * 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 */ diff --git a/config.h.in b/config.h.in new file mode 100644 index 0000000..a59a02c --- /dev/null +++ b/config.h.in @@ -0,0 +1,183 @@ +/* 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 header file. */ +#undef HAVE_INTTYPES_H + +/* 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_MEMORY_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_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_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 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_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 + +/* The size of `unsigned char', as computed by sizeof. */ +#undef SIZEOF_UNSIGNED_CHAR + +/* The size of `unsigned int', as computed by sizeof. */ +#undef SIZEOF_UNSIGNED_INT + +/* The size of `unsigned long', as computed by sizeof. */ +#undef SIZEOF_UNSIGNED_LONG + +/* The size of `unsigned long long', as computed by sizeof. */ +#undef SIZEOF_UNSIGNED_LONG_LONG + +/* The size of `unsigned short', as computed by sizeof. */ +#undef SIZEOF_UNSIGNED_SHORT + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* 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 HAVE_INTTYPES_H +#include +#else +#ifdef HAVE_STDINT_H +#include +#else +#if SIZEOF_UNSIGNED_LONG_LONG==8 +typedef unsigned long long uint64_t; +typedef long long int64_t; +#elif SIZEOF_UNSIGNED_LONG==8 +typedef unsigned long uint64_t; +typedef long int64_t; +#else +#error I do not know what to use for a uint64_t. +#endif + +/* Give us an unsigned 32-bit data type. */ +#if SIZEOF_UNSIGNED_LONG==4 +typedef unsigned long uint32_t; +typedef long int32_t; +#elif SIZEOF_UNSIGNED_INT==4 +typedef unsigned int uint32_t; +typedef int int32_t; +#else +#error I do not know what to use for a uint32_t. +#endif + +/* An unsigned 16-bit data type. */ +#if SIZEOF_UNSIGNED_INT==2 +typedef unsigned int uint16_t; +typedef int int16_t; +#elif SIZEOF_UNSIGNED_SHORT==2 +typedef unsigned short uint16_t; +typedef short int16_t; +#else +#error I do not know what to use for a uint16_t. +#endif + +/* An unsigned 8-bit data type */ +#if SIZEOF_UNSIGNED_CHAR==1 +typedef unsigned char uint8_t; +#else +#error I do not know what to use for a uint8_t. +#endif +#endif +#endif + +#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 */ + diff --git a/configure b/configure new file mode 100755 index 0000000..51252f1 --- /dev/null +++ b/configure @@ -0,0 +1,6045 @@ +#! /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 +test \$(( 1 + 1 )) = 2 || 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@chiark.greenend.org.uk about your system, +$0: 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@chiark.greenend.org.uk' +PACKAGE_URL='' + +ac_unique_file="secnet.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='LTLIBOBJS +LIBOBJS +WRITESTRINGS +EGREP +GREP +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_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_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@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_compute_int LINENO EXPR VAR INCLUDES +# -------------------------------------------- +# Tries to find the compile-time value of EXPR in a program that includes +# INCLUDES, setting VAR accordingly. Returns whether the value could be +# computed +ac_fn_c_compute_int () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if test "$cross_compiling" = yes; then + # Depending upon the size, compute the lo and hi bounds. +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=0 ac_mid=0 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid; break +else + as_fn_arith $ac_mid + 1 && ac_lo=$as_val + if test $ac_lo -le $ac_mid; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) < 0)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=-1 ac_mid=-1 + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) >= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_lo=$ac_mid; break +else + as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val + if test $ac_mid -le $ac_hi; then + ac_lo= ac_hi= + break + fi + as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +else + ac_lo= ac_hi= +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# Binary search between lo and hi bounds. +while test "x$ac_lo" != "x$ac_hi"; do + as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main () +{ +static int test_array [1 - 2 * !(($2) <= $ac_mid)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_hi=$ac_mid +else + as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +done +case $ac_lo in #(( +?*) eval "$3=\$ac_lo"; ac_retval=0 ;; +'') ac_retval=1 ;; +esac + else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +static long int longval () { return $2; } +static unsigned long int ulongval () { return $2; } +#include +#include +int +main () +{ + + FILE *f = fopen ("conftest.val", "w"); + if (! f) + return 1; + if (($2) < 0) + { + long int i = longval (); + if (i != ($2)) + return 1; + fprintf (f, "%ld", i); + } + else + { + unsigned long int i = ulongval (); + if (i != ($2)) + return 1; + fprintf (f, "%lu", i); + } + /* Do not output a trailing newline, as this causes \r\n confusion + on some platforms. */ + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + echo >>conftest.val; read $3 &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 Subdir.mk:Subdir.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/Subdir.mk:test-example/Subdir.mk.tmp" + + + subdirmk_subdirs="$subdirmk_subdirs 'mtest/'" + ac_config_files="$ac_config_files mtest/Subdir.mk:mtest/Subdir.mk.tmp" + + + subdirmk_subdirs="$subdirmk_subdirs 'stest/'" + ac_config_files="$ac_config_files stest/Subdir.mk:stest/Subdir.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 + +{ $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 + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +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_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +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 stdint.h inttypes.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" "$ac_includes_default" +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 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" "$ac_includes_default" +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" "$ac_includes_default" +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" "#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" "$ac_includes_default" +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. */ +$ac_includes_default +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 + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long long" >&5 +$as_echo_n "checking size of unsigned long long... " >&6; } +if ${ac_cv_sizeof_unsigned_long_long+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long long))" "ac_cv_sizeof_unsigned_long_long" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_unsigned_long_long" = yes; then + { { $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 "cannot compute sizeof (unsigned long long) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_unsigned_long_long=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long_long" >&5 +$as_echo "$ac_cv_sizeof_unsigned_long_long" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_UNSIGNED_LONG_LONG $ac_cv_sizeof_unsigned_long_long +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned long" >&5 +$as_echo_n "checking size of unsigned long... " >&6; } +if ${ac_cv_sizeof_unsigned_long+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long))" "ac_cv_sizeof_unsigned_long" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_unsigned_long" = yes; then + { { $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 "cannot compute sizeof (unsigned long) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_unsigned_long=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long" >&5 +$as_echo "$ac_cv_sizeof_unsigned_long" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned int" >&5 +$as_echo_n "checking size of unsigned int... " >&6; } +if ${ac_cv_sizeof_unsigned_int+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned int))" "ac_cv_sizeof_unsigned_int" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_unsigned_int" = yes; then + { { $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 "cannot compute sizeof (unsigned int) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_unsigned_int=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_int" >&5 +$as_echo "$ac_cv_sizeof_unsigned_int" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_UNSIGNED_INT $ac_cv_sizeof_unsigned_int +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned short" >&5 +$as_echo_n "checking size of unsigned short... " >&6; } +if ${ac_cv_sizeof_unsigned_short+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned short))" "ac_cv_sizeof_unsigned_short" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_unsigned_short" = yes; then + { { $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 "cannot compute sizeof (unsigned short) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_unsigned_short=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_short" >&5 +$as_echo "$ac_cv_sizeof_unsigned_short" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_UNSIGNED_SHORT $ac_cv_sizeof_unsigned_short +_ACEOF + + +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of unsigned char" >&5 +$as_echo_n "checking size of unsigned char... " >&6; } +if ${ac_cv_sizeof_unsigned_char+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned char))" "ac_cv_sizeof_unsigned_char" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_unsigned_char" = yes; then + { { $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 "cannot compute sizeof (unsigned char) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_unsigned_char=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_char" >&5 +$as_echo "$ac_cv_sizeof_unsigned_char" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_UNSIGNED_CHAR $ac_cv_sizeof_unsigned_char +_ACEOF + + + + 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" "$ac_includes_default" +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" "$ac_includes_default" +if test "x$ac_cv_header_adns_h" = xyes; then : + +else + as_fn_error $? "adns.h not found" "$LINENO" 5 +fi + + + +{ $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 +" +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 + + + + _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" ;; + "Subdir.mk") CONFIG_FILES="$CONFIG_FILES Subdir.mk:Subdir.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/Subdir.mk") CONFIG_FILES="$CONFIG_FILES test-example/Subdir.mk:test-example/Subdir.mk.tmp" ;; + "mtest/Subdir.mk") CONFIG_FILES="$CONFIG_FILES mtest/Subdir.mk:mtest/Subdir.mk.tmp" ;; + "stest/Subdir.mk") CONFIG_FILES="$CONFIG_FILES stest/Subdir.mk:stest/Subdir.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 + + + + + diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..8f8fab3 --- /dev/null +++ b/configure.ac @@ -0,0 +1,173 @@ +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@chiark.greenend.org.uk) +AC_CONFIG_SRCDIR(secnet.c) +AC_CONFIG_HEADER(config.h) + +SUBDIRMK_SUBDIRS([test-example mtest stest]) + +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 + +AC_PROG_MAKE_SET +AC_PROG_CC +AC_PROG_INSTALL +AC_STDC_HEADERS +AC_CHECK_HEADERS([stdint.h inttypes.h]) +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_CHECK_SIZEOF(unsigned long long) +AC_CHECK_SIZEOF(unsigned long) +AC_CHECK_SIZEOF(unsigned int) +AC_CHECK_SIZEOF(unsigned short) +AC_CHECK_SIZEOF(unsigned char) +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_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 + +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 HAVE_INTTYPES_H +#include +#else +#ifdef HAVE_STDINT_H +#include +#else +#if SIZEOF_UNSIGNED_LONG_LONG==8 +typedef unsigned long long uint64_t; +typedef long long int64_t; +#elif SIZEOF_UNSIGNED_LONG==8 +typedef unsigned long uint64_t; +typedef long int64_t; +#else +#error I do not know what to use for a uint64_t. +#endif + +/* Give us an unsigned 32-bit data type. */ +#if SIZEOF_UNSIGNED_LONG==4 +typedef unsigned long uint32_t; +typedef long int32_t; +#elif SIZEOF_UNSIGNED_INT==4 +typedef unsigned int uint32_t; +typedef int int32_t; +#else +#error I do not know what to use for a uint32_t. +#endif + +/* An unsigned 16-bit data type. */ +#if SIZEOF_UNSIGNED_INT==2 +typedef unsigned int uint16_t; +typedef int int16_t; +#elif SIZEOF_UNSIGNED_SHORT==2 +typedef unsigned short uint16_t; +typedef short int16_t; +#else +#error I do not know what to use for a uint16_t. +#endif + +/* An unsigned 8-bit data type */ +#if SIZEOF_UNSIGNED_CHAR==1 +typedef unsigned char uint8_t; +#else +#error I do not know what to use for a uint8_t. +#endif +#endif +#endif + +#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 */ +]) diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..7d9c4eb --- /dev/null +++ b/debian/changelog @@ -0,0 +1,426 @@ +secnet (0.5.2~) unstable; urgency=medium + + * + + -- + +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: + * Creit 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 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..45dfe28 --- /dev/null +++ b/debian/control @@ -0,0 +1,17 @@ +Source: secnet +Section: net +Priority: extra +Maintainer: Ian Jackson +Uploaders: Stephen Early , + Richard Kettlewell +Build-Depends: debhelper (>= 5), libgmp3-dev, libadns1-dev, bison, flex, + libbsd-dev, python3, tclx, tcl, libtcl-chiark-1 +Standards-Version: 3.0.1 + +Package: secnet +Architecture: any +Depends: ${shlibs:Depends}, ${misc: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. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..243dbd0 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,33 @@ +This is secnet packages for Debian GNU/Linux systems and derivatives. + +secnet is + Copyright 1995-2003 Stephen Early + Copyright 2002-2014 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 2000 Vincent Rijmen, Antoon Bosselaers, Paulo Barreto + 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 Mark Wooding + Copyright 1995-2013 Simon Tatham + Copyright 2012,2013 "Omnifarious" and "btel" on Stackoverflow + +The Debian package, additionally, is: + Copyright 2001 Joey Hess + +secnet is distributed under the terms of the GNU General Public +License, version 3 or later. A copy of this licence can be found on +your Debian system in /usr/share/common-licenses/GPL-3. + +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. diff --git a/debian/default b/debian/default new file mode 100644 index 0000000..63f2c5b --- /dev/null +++ b/debian/default @@ -0,0 +1 @@ +RUN_SECNET=no diff --git a/debian/init b/debian/init new file mode 100644 index 0000000..8df268b --- /dev/null +++ b/debian/init @@ -0,0 +1,84 @@ +#! /bin/sh +# /etc/init.d/secnet +# +# skeleton example file to build /etc/init.d/ scripts. +# This file should be used to construct scripts for /etc/init.d. +# +# Written by Miquel van Smoorenburg . +# Modified for Debian GNU/Linux +# by Ian Murdock . +# +# Version: @(#)skeleton 1.8 03-Mar-1998 miquels@cistron.nl +# + +### 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 +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 \ + --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 diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..f94e752 --- /dev/null +++ b/debian/rules @@ -0,0 +1,99 @@ +#!/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) + +build build-arch: build-stamp +build-stamp: + dh_testdir + + # Add here commands to compile the package. + ./configure --prefix=/usr --sysconfdir=/etc && $(MAKE) + + touch build-stamp + +clean: + dh_testdir + dh_testroot + rm -f build-stamp + + # Add here commands to clean up after the build process. + -$(MAKE) realclean + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/ + $(MAKE) DESTDIR=`pwd`/debian/`dh_listpackages` install + +# Build architecture-independent files here. +build-indep binary-indep: +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot +# dh_installdebconf + dh_installdocs INSTALL README NOTES TODO NEWS BUGS CREDITS + dh_installexamples example.conf +# dh_installmenu +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime + dh_installinit +# dh_installcron + dh_installman +# dh_installinfo + dh_installchangelogs + dh_link + dh_strip + dh_compress + dh_fixperms +# dh_makeshlibs + dh_installdeb +# dh_perl + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/dh.c b/dh.c new file mode 100644 index 0000000..0616a43 --- /dev/null +++ b/dh.c @@ -0,0 +1,167 @@ +/* + * 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 dh_makeshared_fn dh_makeshared; +static void dh_makeshared(void *sst, uint8_t *secret, int32_t secretlen, + cstring_t rempublic, uint8_t *sharedsecret, + int32_t buflen) +{ + struct dh *st=sst; + MP_INT a, b, c; + + mpz_init(&a); + mpz_init(&b); + mpz_init(&c); + + read_mpbin(&a, secret, secretlen); + mpz_set_str(&b, rempublic, 16); + + mpz_powm_sec(&c, &b, &a, &st->p); + + write_mpbin(&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); +} diff --git a/eax-aes-test.c b/eax-aes-test.c new file mode 100644 index 0000000..1838553 --- /dev/null +++ b/eax-aes-test.c @@ -0,0 +1,53 @@ +/* + * 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" diff --git a/eax-aes-test.vectors b/eax-aes-test.vectors new file mode 100644 index 0000000..f6bf3f4 --- /dev/null +++ b/eax-aes-test.vectors @@ -0,0 +1,59 @@ +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 diff --git a/eax-serpent-test.c b/eax-serpent-test.c new file mode 100644 index 0000000..9c7133b --- /dev/null +++ b/eax-serpent-test.c @@ -0,0 +1,56 @@ +/* + * 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" diff --git a/eax-serpent-test.vectors b/eax-serpent-test.vectors new file mode 100644 index 0000000..e016783 --- /dev/null +++ b/eax-serpent-test.vectors @@ -0,0 +1,59 @@ +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 diff --git a/eax-serpentbe-test.c b/eax-serpentbe-test.c new file mode 100644 index 0000000..ce5b3dc --- /dev/null +++ b/eax-serpentbe-test.c @@ -0,0 +1,9 @@ +#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" diff --git a/eax-serpentbe-test.vectors b/eax-serpentbe-test.vectors new file mode 100644 index 0000000..d7e4e6e --- /dev/null +++ b/eax-serpentbe-test.vectors @@ -0,0 +1,59 @@ +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 diff --git a/eax-test.c b/eax-test.c new file mode 100644 index 0000000..ea63fdf --- /dev/null +++ b/eax-test.c @@ -0,0 +1,162 @@ +/* + * 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; +} diff --git a/eax-test.h b/eax-test.h new file mode 100644 index 0000000..f6eb201 --- /dev/null +++ b/eax-test.h @@ -0,0 +1,62 @@ +/* + * 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 */ diff --git a/eax.c b/eax.c new file mode 100644 index 0000000..a32baac --- /dev/null +++ b/eax.c @@ -0,0 +1,346 @@ +/* + * 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 diff --git a/example.conf b/example.conf new file mode 100644 index 0000000..f1d8758 --- /dev/null +++ b/example.conf @@ -0,0 +1,178 @@ +# secnet example configuration file + +# 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 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"); + +# 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,vpn/example/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 diff --git a/hackypar.c b/hackypar.c new file mode 100644 index 0000000..25e41ea --- /dev/null +++ b/hackypar.c @@ -0,0 +1,145 @@ +/* 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 diff --git a/log.c b/log.c new file mode 100644 index 0000000..d5669ca --- /dev/null +++ b/log.c @@ -0,0 +1,654 @@ +/* + * 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) +{ + + if (system_log) { + /* Messages go to the system log interface */ + vslilog_part(system_log, class, message, args); + } else { + vMessageFallback(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) +{ + 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); + 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); + va_end(args); +} + +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_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; + 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 (secnet_is_daemon && st->f) { + if (class&st->level) { + t=time(NULL); + tm=localtime(&t); + fprintf(st->f,"%s %2d %02d:%02d:%02d %s", + months[tm->tm_mon],tm->tm_mday,tm->tm_hour,tm->tm_min, + tm->tm_sec, + pidbuf); + vfprintf(st->f,message,args); + fprintf(st->f,"\n"); + fflush(st->f); + } + } else { + if (pidbuf[0]) MessageFallback(class,"%s",pidbuf); + vMessageFallback(class,message,args); + MessageFallback(class,"\n"); + } +} + +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; + 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) { + 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 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->cl.description="logfile"; + 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->loc=loc; + st->f=NULL; + st->forked=0; + + 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",True,"logfile",loc); + st->level=string_list_to_word(dict_lookup(dict,"class"), + message_class_table,"logfile"); + + add_hook(PHASE_GETRESOURCES,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_GETRESOURCES,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); +} + +void log_module(dict_t *dict) +{ + add_closure(dict,"logfile",logfile_apply); + add_closure(dict,"syslog",syslog_apply); +} diff --git a/magic.h b/magic.h new file mode 100644 index 0000000..15d8498 --- /dev/null +++ b/magic.h @@ -0,0 +1,115 @@ +/* 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< maxlen: + self._bad(what,'too long (max %d)' % maxlen) + return self + + 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 self + if bad.search(self._s): return self._bad(what,'bad syntax') + return self + + 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): + ok=self._re_ok(Tainted.bad_name,'name') + 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) + + 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) + +def parse_args(): + global service + global inputfile + global header + global groupfiledir + global sitesfile + global group + global user + global of + global prefix + global key_prefix + + ap = argparse.ArgumentParser(description='process secnet sites files') + ap.add_argument('--userv', '-u', action='store_true', + help='userv service fragment update mode') + ap.add_argument('--conf-key-prefix', action=ActionNoYes, + default=True, + help='prefix conf file key names derived from sites data') + ap.add_argument('--prefix', '-P', nargs=1, + help='set prefix') + ap.add_argument('arg',nargs=argparse.REMAINDER) + av = ap.parse_args() + #print(repr(av), file=sys.stderr) + service = 1 if av.userv else 0 + prefix = '' if av.prefix is None else av.prefix[0] + key_prefix = av.conf_key_prefix + if service: + if len(av.arg)!=4: + print("Wrong number of arguments") + sys.exit(1) + (header, groupfiledir, sitesfile, group) = av.arg + group = Tainted(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) + 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 group==i: ok=1 + if not ok: + print("caller not in group %s"%group) + sys.exit(1) + else: + if len(av.arg)>3: + print("Too many arguments") + sys.exit(1) + (inputfile, outputfile) = (av.arg + [None]*2)[0:2] + if outputfile is None: of=sys.stdout + else: of=open(outputfile,'w') + +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())) + +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 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 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 rsakey (basetype): + "An 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() + def __str__(self): + return 'rsa-public("%s","%s")'%(self.e,self.n) + +# 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"), + 'pubkey':(rsakey,"RSA public site key"), + '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, + 'pubkey':(lambda n,v:"key %s;\n"%v), + '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", + 'pubkey':"RSA public key of the site", + } + 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.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" + global complaints + print(("%s line %d: "%(file,line))+msg) + complaints=complaints+1 +def moan(msg): + "Complain about something in general" + global complaints + print(msg); + 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] + if prop.raw() in obj.properties: + obj.properties[prop.raw_mark_ok()].add(obj,w) + else: + obj.properties[prop.raw()]=keywords[prop.raw_mark_ok()][0](w) + + +def pline(il,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=lambda: [' '*len(obstack) + + ' '.join([ww.output() for ww in w]) + + '\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 service and group and current.depth==2: + if group!=current.group: + complain("Incorrect group!") + w[2].groupname() + 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 + +if service: + # Put the user's input into their group file, and rebuild the main + # sites file + f=open(groupfiledir+"/T"+group.groupname(),'w') + f.write("# Section submitted by user %s, %s\n"% + (user,time.asctime(time.localtime(time.time())))) + f.write("# Checked by make-secnet-sites version %s\n\n"%VERSION) + for i in userinput: f.write(i) + f.write("\n") + f.close() + os.rename(groupfiledir+"/T"+group.groupname(), + groupfiledir+"/R"+group.groupname()) + f=open(sitesfile+"-tmp",'w') + f.write("# sites file autogenerated by make-secnet-sites\n") + f.write("# generated %s, invoked by %s\n"% + (time.asctime(time.localtime(time.time())),user)) + f.write("# use make-secnet-sites to turn this file into a\n") + f.write("# valid /etc/secnet/sites.conf file\n\n") + for i in headerinput: f.write(i) + files=os.listdir(groupfiledir) + for i in files: + if i[0]=='R': + j=open(groupfiledir+"/"+i) + f.write(j.read()) + j.close() + f.write("# end of sites file\n") + f.close() + os.rename(sitesfile+"-tmp",sitesfile) +else: + outputsites(of) diff --git a/md5.c b/md5.c new file mode 100644 index 0000000..ca9ddb1 --- /dev/null +++ b/md5.c @@ -0,0 +1,297 @@ +/* + * 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"); + } + } +} diff --git a/md5.h b/md5.h new file mode 100644 index 0000000..b5441d4 --- /dev/null +++ b/md5.h @@ -0,0 +1,41 @@ +/* + * 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 */ diff --git a/modules.c b/modules.c new file mode 100644 index 0000000..24c1459 --- /dev/null +++ b/modules.c @@ -0,0 +1,40 @@ +/* + * 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) +{ + 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); +} diff --git a/msgcode-test.c b/msgcode-test.c new file mode 100644 index 0000000..401bf6f --- /dev/null +++ b/msgcode-test.c @@ -0,0 +1,93 @@ +/* + * 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); +} diff --git a/mtest/Ginside.sites b/mtest/Ginside.sites new file mode 100644 index 0000000..3348266 --- /dev/null +++ b/mtest/Ginside.sites @@ -0,0 +1,8 @@ +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 diff --git a/mtest/Goutside.sites b/mtest/Goutside.sites new file mode 100644 index 0000000..c85f5f8 --- /dev/null +++ b/mtest/Goutside.sites @@ -0,0 +1,7 @@ +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 diff --git a/mtest/Subdir.sd.mk b/mtest/Subdir.sd.mk new file mode 100644 index 0000000..bdb872f --- /dev/null +++ b/mtest/Subdir.sd.mk @@ -0,0 +1,8 @@ + +&DEPS += &~/make-secnet-sites +&DEPS += &~/ipaddrset.py +&DEPS += &^/common.tcl + +&:include test-common.sd.mk + +&check:: &check-real diff --git a/mtest/common.tcl b/mtest/common.tcl new file mode 100644 index 0000000..b6fde3e --- /dev/null +++ b/mtest/common.tcl @@ -0,0 +1,42 @@ +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 exec env USERV_USER=$user USERV_GROUP=$group] \ + [mss-program] \ + $args +} + +proc run-mss {args} { eval [list exec] [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 +} + +file mkdir $tmp/groupfiles + +set env(PYTHONHASHSEED) 0 +set env(PYTHONBYTECODEBASE) 0 + +set seddery { sed -n 's/^[ \t]*//; /^[^#]/p' } + +prefix_some_path PYTHONPATH . diff --git a/mtest/delegations.sites b/mtest/delegations.sites new file mode 100644 index 0000000..f6632e2 --- /dev/null +++ b/mtest/delegations.sites @@ -0,0 +1,5 @@ +location outside Goutside +restrict-nets 172.18.232.0/29 + +location inside Ginside +restrict-nets 172.18.232.8/29 diff --git a/mtest/e-basic.conf b/mtest/e-basic.conf new file mode 100644 index 0000000..9a4fde8 --- /dev/null +++ b/mtest/e-basic.conf @@ -0,0 +1,48 @@ +# secnet sites file autogenerated by make-secnet-sites version 0.1.18 +# Tue Nov 19 01:02:50 2019 +# Command line: ./make-secnet-sites --no-conf-key-prefix test-example/sites ./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"; + address "[127.0.0.1]"; port 16910; + mobile True; + key rsa-public("65537","130064631890186713927887504218626486455931306300999583387009075747001546036643522074275473238061323169592347601185592753550279410171535737146240085267000508853176463710554801101055212967131924064664249613912656320653505750073021702169423354903540699008756137338575553686987244488914481168225136440872431691669"); + link netlink { + routes "172.18.232.8/29"; + ptp-address "172.18.232.9"; + }; + }; + }; + out { + outside { + name "test-example/out/outside"; + address "[::1]"; port 16900; + key rsa-public("65537","129251483458784900555621175262818292872587807329014927540074484804119474262261383244074013537736576331652560727149001626325243856012659665194546933097292703586821422085819615124517093786704646988649444946154384037948502112302285511195679291084694375811092516151263088200304199780052361048758446082354317801941"); + 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; diff --git a/mtest/e-userv.sites b/mtest/e-userv.sites new file mode 100644 index 0000000..e9108a6 --- /dev/null +++ b/mtest/e-userv.sites @@ -0,0 +1,35 @@ +# 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 diff --git a/mtest/header.sites b/mtest/header.sites new file mode 100644 index 0000000..f4b3481 --- /dev/null +++ b/mtest/header.sites @@ -0,0 +1,13 @@ +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 + diff --git a/mtest/t-basic b/mtest/t-basic new file mode 100755 index 0000000..ad754c1 --- /dev/null +++ b/mtest/t-basic @@ -0,0 +1,8 @@ +#! /usr/bin/tclsh + +source mtest/common.tcl + +run-mss --no-conf-key-prefix test-example/sites $tmp/out.conf + +set seddery { sed -n 's/^[ \t]*//; /^[^#]/p' } +diff mtest/e-basic.conf $tmp/out.conf $seddery diff --git a/mtest/t-prefix b/mtest/t-prefix new file mode 100755 index 0000000..2db65e3 --- /dev/null +++ b/mtest/t-prefix @@ -0,0 +1,10 @@ +#! /usr/bin/tclsh + +source mtest/common.tcl + +run-mss -Ppprefix --no-conf-key-prefix test-example/sites $tmp/out.conf + +set seddery { sed -n 's/^[ \t]*//; /^[^#]/p' } +diff mtest/e-basic.conf $tmp/out.conf \ + "sed -e 's/vpn/pprefixvpn/g; s/^all-sites/pprefix&/' | $seddery" \ + $seddery diff --git a/mtest/t-userv b/mtest/t-userv new file mode 100755 index 0000000..5d159bdb --- /dev/null +++ b/mtest/t-userv @@ -0,0 +1,37 @@ +#! /usr/bin/tclsh + +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" + } + } +} + diff --git a/netlink.c b/netlink.c new file mode 100644 index 0000000..7add6d7 --- /dev/null +++ b/netlink.c @@ -0,0 +1,1345 @@ +/* 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); +} diff --git a/netlink.h b/netlink.h new file mode 100644 index 0000000..93b556d --- /dev/null +++ b/netlink.h @@ -0,0 +1,91 @@ +/* + * 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 */ diff --git a/polypath-interface-monitor-linux b/polypath-interface-monitor-linux new file mode 100755 index 0000000..3ecae59 --- /dev/null +++ b/polypath-interface-monitor-linux @@ -0,0 +1,122 @@ +#!/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 $!; +} diff --git a/polypath.c b/polypath.c new file mode 100644 index 0000000..64284a3 --- /dev/null +++ b/polypath.c @@ -0,0 +1,938 @@ +/* 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; + lg_perror(LG,M_DEBUG,errno,"%s [%s] xmit %"PRIu32" bytes to %s", + interf->name,iaddr_to_string(&us->addr), + buf->size,iaddr_to_string(&dest->ia)); + } + 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 */ +} diff --git a/pretest-to-tested b/pretest-to-tested new file mode 100755 index 0000000..7166486 --- /dev/null +++ b/pretest-to-tested @@ -0,0 +1,14 @@ +#!/bin/bash + +# 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 + +set -e +while true; do + next=$(git-rev-list --reverse tested..pretest | head -n1) + if [ "x$next" = x ]; then break; fi + git checkout "$next" + ./comprehensive-test + git push . HEAD:tested +done diff --git a/process.c b/process.c new file mode 100644 index 0000000..3444308 --- /dev/null +++ b/process.c @@ -0,0 +1,349 @@ +/* + * 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); +} diff --git a/process.h b/process.h new file mode 100644 index 0000000..99681b0 --- /dev/null +++ b/process.h @@ -0,0 +1,36 @@ +/* + * 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 */ diff --git a/random.c b/random.c new file mode 100644 index 0000000..323fffd --- /dev/null +++ b/random.c @@ -0,0 +1,102 @@ +/* + * 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); +} diff --git a/resolver.c b/resolver.c new file mode 100644 index 0000000..3a6ad5a --- /dev/null +++ b/resolver.c @@ -0,0 +1,232 @@ +/* + * 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); + + if (conf) { + if (adns_init_strcfg(&st->ast, 0, 0, conf)) { + fatal_perror("Failed to initialise ADNS"); + } + } else { + if (adns_init(&st->ast, 0, 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); +} diff --git a/rsa.c b/rsa.c new file mode 100644 index 0000000..28a4c1b --- /dev/null +++ b/rsa.c @@ -0,0 +1,593 @@ +/* + * 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 { + struct hash_if *hashi; + uint8_t *hashbuf; +}; + +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; +}; +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 rsacommon *c, struct hash_if *hash) +{ + free(c->hashbuf); + c->hashbuf=safe_malloc(hash->hlen, "generate_msg"); + c->hashi=hash; +} +static void rsa_pub_sethash(void *sst, struct hash_if *hash) +{ + struct rsapub *st=sst; + rsa_sethash(&st->common, hash); +} +static void rsa_priv_sethash(void *sst, struct hash_if *hash) +{ + struct rsapriv *st=sst; + rsa_sethash(&st->common, hash); +} +static void rsa_hash(struct rsacommon *c, const uint8_t *buf, int32_t len) +{ + hash_hash(c->hashi,buf,len,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_sign: 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; icommon,data,datalen); + /* Construct the message representative. */ + emsa_pkcs1(&st->n, &a, st->common.hashbuf, st->common.hashi->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); + + rsa_hash(&st->common,data,datalen); + emsa_pkcs1(&st->n, &a, st->common.hashbuf, st->common.hashi->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 list_t *rsapub_apply(closure_t *self, struct cloc loc, dict_t *context, + list_t *args) +{ + struct rsapub *st; + item_t *i; + string_t e,n; + + 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->ops.sethash=rsa_pub_sethash; + st->common.hashbuf=NULL; + st->ops.unpick=rsa_sig_unpick; + st->ops.check=rsa_sig_check; + st->loc=loc; + + i=list_elem(args,0); + if (i) { + if (i->type!=t_string) { + cfgfatal(i->loc,"rsa-public","first argument must be a string\n"); + } + e=i->data.string; + if (mpz_init_set_str(&st->e,e,10)!=0) { + cfgfatal(i->loc,"rsa-public","encryption key \"%s\" is not a " + "decimal number string\n",e); + } + } else { + cfgfatal(loc,"rsa-public","you must provide an encryption key\n"); + } + if (mpz_sizeinbase(&st->e, 256) > RSA_MAX_MODBYTES) { + cfgfatal(loc, "rsa-public", "implausibly large public exponent\n"); + } + + i=list_elem(args,1); + if (i) { + if (i->type!=t_string) { + cfgfatal(i->loc,"rsa-public","second argument must be a string\n"); + } + n=i->data.string; + if (mpz_init_set_str(&st->n,n,10)!=0) { + cfgfatal(i->loc,"rsa-public","modulus \"%s\" is not a decimal " + "number string\n",n); + } + } else { + cfgfatal(loc,"rsa-public","you must provide a modulus\n"); + } + if (mpz_sizeinbase(&st->n, 256) > RSA_MAX_MODBYTES) { + cfgfatal(loc, "rsa-public", "implausibly large modulus\n"); + } + return new_closure(&st->cl); +} + +static uint32_t keyfile_get_int(struct cloc loc, FILE *f) +{ + uint32_t r; + r=fgetc(f)<<24; + r|=fgetc(f)<<16; + r|=fgetc(f)<<8; + r|=fgetc(f); + cfgfile_postreadcheck(loc,f); + return r; +} + +static uint16_t keyfile_get_short(struct cloc loc, FILE *f) +{ + uint16_t r; + r=fgetc(f)<<8; + r|=fgetc(f); + cfgfile_postreadcheck(loc,f); + return r; +} + +static list_t *rsapriv_apply(closure_t *self, struct cloc loc, dict_t *context, + list_t *args) +{ + struct rsapriv *st; + FILE *f; + cstring_t filename; + item_t *i; + long length; + uint8_t *b, *c; + int cipher_type; + MP_INT e,d,iqmp,tmp,tmp2,tmp3; + bool_t valid; + + 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->ops.sethash=rsa_priv_sethash; + st->common.hashbuf=NULL; + st->ops.sign=rsa_sign; + st->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(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\"; assuming it's valid while we check the " + "rest of the configuration\n",loc.file,loc.line,filename); + goto assume_valid; + } else { + fatal_perror("rsa-private (%s:%d): cannot open file \"%s\"", + loc.file,loc.line,filename); + } + } + + /* 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) { + cfgfatal_maybefile(f,loc,"rsa-private","failed to read magic ID" + " string from SSH1 private keyfile \"%s\"\n", + filename); + } + free(b); + + cipher_type=fgetc(f); + keyfile_get_int(loc,f); /* "Reserved data" */ + if (cipher_type != 0) { + cfgfatal(loc,"rsa-private","we don't support encrypted keyfiles\n"); + } + + /* Read the public key */ + keyfile_get_int(loc,f); /* Not sure what this is */ + length=(keyfile_get_short(loc,f)+7)/8; + if (length>RSA_MAX_MODBYTES) { + cfgfatal(loc,"rsa-private","implausible length %ld for modulus\n", + length); + } + b=safe_malloc(length,"rsapriv_apply"); + if (fread(b,length,1,f) != 1) { + cfgfatal_maybefile(f,loc,"rsa-private","error reading modulus\n"); + } + mpz_init(&st->n); + read_mpbin(&st->n,b,length); + free(b); + length=(keyfile_get_short(loc,f)+7)/8; + if (length>RSA_MAX_MODBYTES) { + cfgfatal(loc,"rsa-private","implausible length %ld for e\n",length); + } + b=safe_malloc(length,"rsapriv_apply"); + if (fread(b,length,1,f)!=1) { + cfgfatal_maybefile(f,loc,"rsa-private","error reading e\n"); + } + mpz_init(&e); + read_mpbin(&e,b,length); + free(b); + + length=keyfile_get_int(loc,f); + if (length>1024) { + cfgfatal(loc,"rsa-private","implausibly long (%ld) key comment\n", + length); + } + c=safe_malloc(length+1,"rsapriv_apply"); + if (fread(c,length,1,f)!=1) { + cfgfatal_maybefile(f,loc,"rsa-private","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_short(loc,f) != keyfile_get_short(loc,f)) { + cfgfatal(loc,"rsa-private","corrupt keyfile\n"); + } + + /* Read d */ + length=(keyfile_get_short(loc,f)+7)/8; + if (length>RSA_MAX_MODBYTES) { + cfgfatal(loc,"rsa-private","implausibly long (%ld) decryption key\n", + length); + } + b=safe_malloc(length,"rsapriv_apply"); + if (fread(b,length,1,f)!=1) { + cfgfatal_maybefile(f,loc,"rsa-private", + "error reading decryption key\n"); + } + mpz_init(&d); + read_mpbin(&d,b,length); + free(b); + /* Read iqmp (inverse of q mod p) */ + length=(keyfile_get_short(loc,f)+7)/8; + if (length>RSA_MAX_MODBYTES) { + cfgfatal(loc,"rsa-private","implausibly long (%ld)" + " iqmp auxiliary value\n", length); + } + b=safe_malloc(length,"rsapriv_apply"); + if (fread(b,length,1,f)!=1) { + cfgfatal_maybefile(f,loc,"rsa-private", + "error reading decryption key\n"); + } + mpz_init(&iqmp); + read_mpbin(&iqmp,b,length); + free(b); + /* Read q (the smaller of the two primes) */ + length=(keyfile_get_short(loc,f)+7)/8; + if (length>RSA_MAX_MODBYTES) { + cfgfatal(loc,"rsa-private","implausibly long (%ld) q value\n", + length); + } + b=safe_malloc(length,"rsapriv_apply"); + if (fread(b,length,1,f)!=1) { + cfgfatal_maybefile(f,loc,"rsa-private", + "error reading q value\n"); + } + mpz_init(&st->q); + read_mpbin(&st->q,b,length); + free(b); + /* Read p (the larger of the two primes) */ + length=(keyfile_get_short(loc,f)+7)/8; + if (length>RSA_MAX_MODBYTES) { + cfgfatal(loc,"rsa-private","implausibly long (%ld) p value\n", + length); + } + b=safe_malloc(length,"rsapriv_apply"); + if (fread(b,length,1,f)!=1) { + cfgfatal_maybefile(f,loc,"rsa-private", + "error reading p value\n"); + } + mpz_init(&st->p); + read_mpbin(&st->p,b,length); + free(b); + + if (fclose(f)!=0) { + fatal_perror("rsa-private (%s:%d): fclose",loc.file,loc.line); + } + + /* + * Now verify the validity of the key, and set up the auxiliary + * values for fast CRT signing. + */ + valid=False; + i=list_elem(args,1); + mpz_init(&tmp); + mpz_init(&tmp2); + mpz_init(&tmp3); + 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); + } else { + /* 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_init(&st->dp); + mpz_init(&st->dq); + mpz_init(&st->w); + 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) { + cfgfatal(loc,"rsa-private","file \"%s\" does not contain a " + "valid RSA key!\n",filename); + } + mpz_clear(&tmp); + mpz_clear(&tmp2); + mpz_clear(&tmp3); + + free(c); + mpz_clear(&e); + mpz_clear(&d); + mpz_clear(&iqmp); + +assume_valid: + 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); +} diff --git a/secnet-wireshark.lua b/secnet-wireshark.lua new file mode 100644 index 0000000..62739bc --- /dev/null +++ b/secnet-wireshark.lua @@ -0,0 +1,883 @@ +--- -*-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 d 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 -------------------------------------------------- diff --git a/secnet.8 b/secnet.8 new file mode 100644 index 0000000..670bff7 --- /dev/null +++ b/secnet.8 @@ -0,0 +1,858 @@ +.\" 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 => \fIrsaprivkey 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 => \fIrsapubkey 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 \fIrsaprivkey 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 \fIrsapubkey 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) diff --git a/secnet.c b/secnet.c new file mode 100644 index 0000000..c14a835 --- /dev/null +++ b/secnet.c @@ -0,0 +1,533 @@ +/* + * 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 "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; + +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; + item_t *site; + dict_t *system; + struct passwd *pw; + struct cloc loc; + int i; + + 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); + } + + /* 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->control(s->st,True); + } + } +} + +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; + + Message(M_NOTICE,"%s [%d]: starting\n",version,secnet_pid); + + do { + if (gettimeofday(&tv_now_global, NULL)!=0) { + fatal_perror("main loop: gettimeofday"); + } + 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; + + phase_hooks_init(); + + enter_phase(PHASE_GETOPTS); + parse_options(argc,argv); + + enter_phase(PHASE_READCONFIG); + config=read_conffile(configfile); + + enter_phase(PHASE_SETUP); + setup(config); + + if (just_check_config) { + Message(M_INFO,"configuration file check complete\n"); + exit(0); + } + + enter_phase(PHASE_DAEMONIZE); + become_daemon(); + + 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; +} diff --git a/secnet.h b/secnet.h new file mode 100644 index 0000000..7da7c2d --- /dev/null +++ b/secnet.h @@ -0,0 +1,751 @@ +/* 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 + +#define MAX_PEER_ADDRS 5 +/* send at most this many copies; honour at most that many addresses */ + +#define MAX_NAK_MSG 80 + +struct hash_if; +struct comm_if; +struct comm_addr; +struct priomsg; + +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 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 fail_if_invalid, 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 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; + +/***** END of module support *****/ + +/***** 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 + +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_sethash_fn(void *st, struct hash_if *hash); +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_sethash_fn *sethash; /* must be called before check, if non-0 */ + sig_unpick_fn *unpick; + sig_checksig_fn *check; +}; + +/* 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_sethash_fn *sethash; /* must be called before sign, if non-0 */ + sig_makesig_fn *sign; +}; + +/* 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); + +/* SITE interface */ + +/* Pretty much a placeholder; allows starting and stopping of processing, + key expiry, etc. */ +typedef void site_control_fn(void *st, bool_t run); +typedef uint32_t site_status_fn(void *st); +struct site_if { + void *st; + site_control_fn *control; + 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; +}; + +/* 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)) + 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 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 */ diff --git a/serpent.c b/serpent.c new file mode 100644 index 0000000..a8bfe2a --- /dev/null +++ b/serpent.c @@ -0,0 +1,398 @@ +/* + * 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"); +} diff --git a/serpent.h b/serpent.h new file mode 100644 index 0000000..857b1b9 --- /dev/null +++ b/serpent.h @@ -0,0 +1,25 @@ +#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 */ diff --git a/serpentbe.c b/serpentbe.c new file mode 100644 index 0000000..758d5c9 --- /dev/null +++ b/serpentbe.c @@ -0,0 +1,2 @@ +#define SERPENT_BIGENDIAN +#include "serpent.c" diff --git a/serpentsboxes.h b/serpentsboxes.h new file mode 100644 index 0000000..c264d63 --- /dev/null +++ b/serpentsboxes.h @@ -0,0 +1,505 @@ +/* + * 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 diff --git a/setup.mac b/setup.mac new file mode 100755 index 0000000..a7a7359 --- /dev/null +++ b/setup.mac @@ -0,0 +1,83 @@ +#!/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" diff --git a/sha1.c b/sha1.c new file mode 100644 index 0000000..f442e08 --- /dev/null +++ b/sha1.c @@ -0,0 +1,350 @@ +/* +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; +}; + +void sha1_module(dict_t *dict) +{ + struct sha1 *st; + 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; + + NEW(st); + 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"); + } + } +} diff --git a/sha512.c b/sha512.c new file mode 100644 index 0000000..5c0b2ed --- /dev/null +++ b/sha512.c @@ -0,0 +1,619 @@ +/* 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); + } +} diff --git a/sha512.h b/sha512.h new file mode 100644 index 0000000..ea85194 --- /dev/null +++ b/sha512.h @@ -0,0 +1,95 @@ +/* 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 diff --git a/site.c b/site.c new file mode 100644 index 0000000..3796889 --- /dev/null +++ b/site.c @@ -0,0 +1,2605 @@ +/* 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" + +#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] */ + +/* 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 + +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 }, + { "default", LOG_SETUP_INIT|LOG_SETUP_TIMEOUT| + LOG_ACTIVATE_KEY|LOG_TIMEOUT_KEY|LOG_SEC|LOG_ERROR }, + { "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_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 sigprivkey_if *privkey; + struct sigpubkey_if *pubkey; + 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]; + + /* 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; + 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_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; +}; + +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; + + 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); + if ((st->local_capabilities & st->early_capabilities) || + (type != LABEL_MSG1)) { + buf_append_uint32(&st->buffer,st->local_capabilities); + } + if (type_is_msg34(type)) { + buf_append_uint16(&st->buffer,st->mtu_target); + } + 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=st->privkey->sign(st->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->capab_transformnum=-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 (!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 (!st->pubkey->unpick(st->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 bool_t kex_init(struct site *st) +{ + 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 */ + if (!st->pubkey->check(st->pubkey->st, + m->hashstart,m->hashlen, + &m->sig)) { + slog(st,LOG_SEC,"msg3/msg4 signature failed check!"); + return False; + } + + 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) +{ + 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); + 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_control(void *vst, bool_t run) +{ + struct site *st=vst; + if (run) enter_state_run(st); + else enter_state_stop(st); +} + +static void site_phase_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_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.control=site_control; + st->ops.status=site_status; + + /* 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->localname=dict_read_string(dict, "local-name", True, "site", loc); + st->remotename=dict_read_string(dict, "name", True, "site", loc); + + 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(loc,"site","%s closure wrong type\n",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->log=find_cl_if(dict,"log",CL_LOG,True,"site",loc); + st->random=find_cl_if(dict,"random",CL_RANDOMSRC,True,"site",loc); + + st->privkey=find_cl_if(dict,"local-key",CL_SIGPRIVKEY,True,"site",loc); + 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; + st->pubkey=find_cl_if(dict,"key",CL_SIGPUBKEY,True,"site",loc); + + GET_CLOSURE_LIST("transform",transforms,ntransforms,CL_TRANSFORM); + + st->dh=find_cl_if(dict,"dh",CL_DH,True,"site",loc); + + if (st->privkey->sethash || st->pubkey->sethash) { + struct hash_if *hash=find_cl_if(dict,"hash",CL_HASH,True,"site",loc); + if (st->privkey->sethash) st->privkey->sethash(st->privkey->st,hash); + if (st->pubkey->sethash) st->pubkey->sethash(st->pubkey->st,hash); + } + +#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->log_events=string_list_to_word(dict_lookup(dict,"log-events"), + log_event_table,"site"); + + st->resolving_count=0; + st->allow_send_prod=0; + + st->tunname=safe_malloc(strlen(st->localname)+strlen(st->remotename)+5, + "site_apply"); + sprintf(st->tunname,"%s<->%s",st->localname,st->remotename); + + /* 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); + + buffer_new(&st->scratch,SETUP_BUFFER_LEN); + BUF_ALLOC(&st->scratch,"site:scratch"); + + /* 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_clear(st,&st->peers); + transport_peers_clear(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_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 */ + 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_clear(struct site *st, transport_peers *peers) { + peers->npeers= 0; + 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 *****/ diff --git a/slip.c b/slip.c new file mode 100644 index 0000000..6631fae --- /dev/null +++ b/slip.c @@ -0,0 +1,474 @@ +/* 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); +} diff --git a/stamp-h.in b/stamp-h.in new file mode 100644 index 0000000..9788f70 --- /dev/null +++ b/stamp-h.in @@ -0,0 +1 @@ +timestamp diff --git a/stest/Subdir.sd.mk b/stest/Subdir.sd.mk new file mode 100644 index 0000000..d819170 --- /dev/null +++ b/stest/Subdir.sd.mk @@ -0,0 +1,38 @@ + +&TARGETS += & udp-preload.so + +&DEPS += & udp-preload.so +&DEPS += &^ common.tcl +&DEPS += secnet +&DEPS += test-example/sites.conf +&DEPS += test-example/inside.key +&DEPS += test-example/outside.key + +&: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 diff --git a/stest/common.tcl b/stest/common.tcl new file mode 100644 index 0000000..0c1202c --- /dev/null +++ b/stest/common.tcl @@ -0,0 +1,220 @@ +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 extra(inside) { + local-mobile True; + mtu-target 1260; +} +set extra(outside) {} + +proc mkconf {location site} { + global tmp + global builddir + global netlink + global ports + global extra + global netlinkfh + 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 " + 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 \"::1\", \"127.0.0.1\"; + buffer sysbuffer(4096); + } + " + set delim , + } + append cfg "; + local-name \"test-example/$location/$site\"; + local-key rsa-private(\"$builddir/test-example/$site.key\"); +" + append cfg $extra($site) + append cfg { + log logfile { + filename "/dev/tty"; + class "info","notice","warning","error","security","fatal"; + }; + system { + }; + resolver adns { + }; + log-events "all"; + random randomfile("/dev/urandom",no); + transform eax-serpent { }, serpent256-cbc { }; + } + + set f [open $builddir/test-example/sites.conf r] + 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 + upvar #0 pids($site) pid + set cf $tmp/$site.conf + set ch [open $cf w] + puts $ch [mkconf $location $site] + close $ch + set argl [list $builddir/secnet -dvnc $cf] + set pid [fork] + 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 netlinkfh($site.r) fh + read $fh; # empty the buffer + switch -exact $site { + inside { + puts OK + set ok 1; # what a bodge + return + } + outside { + error "inside rx'd!" + } + } +} + +proc bgerror {message} { + global errorInfo errorCode + catch { + puts stderr " +---------------------------------------- +$errorInfo + +$errorCode +$message +---------------------------------------- + " + } + exit 1 +} + +proc sendpkt {} { + global netlinkfh + 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(inside.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 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 test-kex {} { + udp-proxy + spawn-secnet in inside + spawn-secnet out outside + + after 500 sendpkt + after 1000 sendpkt + after 5000 timed-out + + vwait ok +} diff --git a/stest/t-basic-kex b/stest/t-basic-kex new file mode 100755 index 0000000..0952d5f --- /dev/null +++ b/stest/t-basic-kex @@ -0,0 +1,5 @@ +#! /usr/bin/tclsh + +source stest/common.tcl + +test-kex diff --git a/stest/t-dyni-kex b/stest/t-dyni-kex new file mode 100755 index 0000000..4bc0087 --- /dev/null +++ b/stest/t-dyni-kex @@ -0,0 +1,7 @@ +#! /usr/bin/tclsh + +# Dyamic key rollover config on inside only + +source stest/common.tcl + +test-kex diff --git a/stest/udp-preload.c b/stest/udp-preload.c new file mode 100644 index 0000000..b5897bb --- /dev/null +++ b/stest/udp-preload.c @@ -0,0 +1,322 @@ +/* + * 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/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: simply +mention (usually, by setting) the variable &TARGETS_zonk, or whatever. +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 mentioned in src but not lib, `make zonk' in +lib will fail. If you want to make a target exist everywhere, +mention its name in Perdir.sd.mk (see below). + +Perdir.sd.mk, inclusion +----------------------- + +The file Perdir.sd.mk in the toplevel of the source is automatically +processed after each individual directory's Subdir.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 Perdir.sd.mk. + +The top-level Subdir.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 Subdir.sd.mk. + +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)). + +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 Subdir.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'. + +Subdirectory 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 Subdir.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 Subdir.sd.mk + Subdir and Perdir) + +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 Subdir.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.) + +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. + +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 TOP_ +&=_ => sub_dir or TOP + +&/ => sub/dir/ or nothing +&=/ => sub/dir or . + +&^lc => $(top_srcdir)/sub/dir/lc +&^/ => $(top_srcdir)/sub/dir/ + +&~lc => $(top_srcdir)/lc +&~/ => $(top_srcdir)/ + +In general: + = return subdir without delimiter (not allowed with `^' `~') + ^ pathname of this subdirectory in source tree + ~ pathname of top level of source tree + / terminates the escape (needed if next is not lwsp or space) + lwsp starts multi-word processing (see below) + +So pathname syntax is a subset of: + '&' [ '^' | '~' ] [ lc | '/' ] + +&& => && for convenience in shell runes +\& => & general escaping mechanism + +& 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) + args are processed for & + +&:include filename filename should usually be foo.sd.mk +&:-include filename tolerate nonexistent file + filenames are relative to $(top_srcdir) + +&! disables & until EOL (and then disappears) + +&# delete everything to end of line + (useful if the RHS contains unrecognised & constructions) + +&!STUFF + changes the escape sequence from & to literally STUFF + STUFF may be any series of of non-whitespace characters, + and is terminated by EOL or lwsp. &!STUFF and the lwsp + are discarded. + + After this, write STUFF instead of &, everywhere. + The effect is global and lasts until the next setting. + It takes effect on &:include'd files too, so maybe set + it back before using &:include. + + Notably + STUFFSTUFF => STUFFSTUFF + \STUFF => STUFF + STUFF!& set escape back to & + +&TARGETS_things + Handled specially. If mentioned, 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. But mentioning &TARGETS_things in + a #-comment *does* work because the & filter does not care + about comments. + + `all' is extra special: every directory has an `all' + target, which corresponds to &TARGETS. + +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. + +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. + +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 Subdir.sd.mk in each directory. The toplevel one should +probably contain: + + include subdirmk/usual.mk + include subdirmk/regen.mk + +Write a Perdir.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 Subdir.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 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 Subdir.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. + + +Legal information +----------------- + +subdirmk is + Copyright 2019 Mark Wooding + Copyright 2019 Ian Jackson + + 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+.) diff --git a/subdirmk/autogen.sh b/subdirmk/autogen.sh new file mode 100755 index 0000000..e28e80e --- /dev/null +++ b/subdirmk/autogen.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# subdirmk, autogen.sh (conventional autoconf invocation script) +# Copyright 2019 Ian Jackson +# SPDX-License-Identifier: LGPL-2.0-or-later +set -e +cd ${0%/*} +autoconf diff --git a/cdeps.sd.mk b/subdirmk/cdeps.sd.mk similarity index 100% rename from cdeps.sd.mk rename to subdirmk/cdeps.sd.mk diff --git a/clean.sd.mk b/subdirmk/clean.sd.mk similarity index 100% rename from clean.sd.mk rename to subdirmk/clean.sd.mk diff --git a/example/.gitignore b/subdirmk/example/.gitignore similarity index 100% rename from example/.gitignore rename to subdirmk/example/.gitignore diff --git a/example/DEVELOPER-CERTIFICATE b/subdirmk/example/DEVELOPER-CERTIFICATE similarity index 100% rename from example/DEVELOPER-CERTIFICATE rename to subdirmk/example/DEVELOPER-CERTIFICATE diff --git a/example/LGPL-2 b/subdirmk/example/LGPL-2 similarity index 100% rename from example/LGPL-2 rename to subdirmk/example/LGPL-2 diff --git a/example/Perdir.sd.mk b/subdirmk/example/Perdir.sd.mk similarity index 100% rename from example/Perdir.sd.mk rename to subdirmk/example/Perdir.sd.mk diff --git a/example/Subdir.sd.mk b/subdirmk/example/Subdir.sd.mk similarity index 100% rename from example/Subdir.sd.mk rename to subdirmk/example/Subdir.sd.mk diff --git a/example/autogen.sh b/subdirmk/example/autogen.sh similarity index 100% rename from example/autogen.sh rename to subdirmk/example/autogen.sh diff --git a/example/configure.ac b/subdirmk/example/configure.ac similarity index 100% rename from example/configure.ac rename to subdirmk/example/configure.ac diff --git a/example/lib/Subdir.sd.mk b/subdirmk/example/lib/Subdir.sd.mk similarity index 100% rename from example/lib/Subdir.sd.mk rename to subdirmk/example/lib/Subdir.sd.mk diff --git a/example/lib/t/Subdir.sd.mk b/subdirmk/example/lib/t/Subdir.sd.mk similarity index 100% rename from example/lib/t/Subdir.sd.mk rename to subdirmk/example/lib/t/Subdir.sd.mk diff --git a/example/lib/t/toytest.c b/subdirmk/example/lib/t/toytest.c similarity index 100% rename from example/lib/t/toytest.c rename to subdirmk/example/lib/t/toytest.c diff --git a/example/lib/toylib.c b/subdirmk/example/lib/toylib.c similarity index 100% rename from example/lib/toylib.c rename to subdirmk/example/lib/toylib.c diff --git a/example/lib/toylib.h b/subdirmk/example/lib/toylib.h similarity index 100% rename from example/lib/toylib.h rename to subdirmk/example/lib/toylib.h diff --git a/example/src/Subdir.sd.mk b/subdirmk/example/src/Subdir.sd.mk similarity index 100% rename from example/src/Subdir.sd.mk rename to subdirmk/example/src/Subdir.sd.mk diff --git a/example/src/toy.c b/subdirmk/example/src/toy.c similarity index 100% rename from example/src/toy.c rename to subdirmk/example/src/toy.c diff --git a/example/subdirmk b/subdirmk/example/subdirmk similarity index 100% rename from example/subdirmk rename to subdirmk/example/subdirmk diff --git a/generate b/subdirmk/generate similarity index 100% rename from generate rename to subdirmk/generate diff --git a/regen.mk.in b/subdirmk/regen.mk.in similarity index 100% rename from regen.mk.in rename to subdirmk/regen.mk.in diff --git a/subdirmk.ac b/subdirmk/subdirmk.ac similarity index 100% rename from subdirmk.ac rename to subdirmk/subdirmk.ac diff --git a/tests/check b/subdirmk/tests/check similarity index 100% rename from tests/check rename to subdirmk/tests/check diff --git a/usual.mk.in b/subdirmk/usual.mk.in similarity index 100% rename from usual.mk.in rename to subdirmk/usual.mk.in diff --git a/test-common.sd.mk b/test-common.sd.mk new file mode 100644 index 0000000..bf9c6cf --- /dev/null +++ b/test-common.sd.mk @@ -0,0 +1,30 @@ + +include common.make + +&TESTSCRIPTS ?= $(shell echo &^/t-*[0-9a-z]) +&TESTNAMES := $(patsubst t-%,%,$(notdir $(&TESTSCRIPTS))) + +&DEPS += $(src)/test-common.tcl +&DEPS += common.make +&DEPS += $(src)/test-common.sd.mk +&DEPS += &/Subdir.mk + +&check-real: $(foreach t,$(&TESTNAMES),&d-$t/ok) + +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-* diff --git a/test-common.tcl b/test-common.tcl new file mode 100644 index 0000000..162ad3a --- /dev/null +++ b/test-common.tcl @@ -0,0 +1,23 @@ + +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 :] +} + +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 +} diff --git a/test-example/README b/test-example/README new file mode 100644 index 0000000..72f98ab --- /dev/null +++ b/test-example/README @@ -0,0 +1,50 @@ +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. diff --git a/test-example/Subdir.sd.mk b/test-example/Subdir.sd.mk new file mode 100644 index 0000000..345b11a --- /dev/null +++ b/test-example/Subdir.sd.mk @@ -0,0 +1,11 @@ +&TARGETS += & sites.conf inside.key outside.key + +include common.make + +&/%.key: &^/%.key.b64 + base64 -d <$< >$@.new && mv -f $@.new $@ + +&sites.conf: $(src)/make-secnet-sites &^/sites &/Subdir.mk + $(src)/make-secnet-sites &^/sites $@ + +&CLEAN += *.new diff --git a/test-example/bogus-setup-request.c b/test-example/bogus-setup-request.c new file mode 100644 index 0000000..c9cfc3c --- /dev/null +++ b/test-example/bogus-setup-request.c @@ -0,0 +1,115 @@ +/* + 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); +} diff --git a/test-example/common.conf b/test-example/common.conf new file mode 100644 index 0000000..8fefad6 --- /dev/null +++ b/test-example/common.conf @@ -0,0 +1,14 @@ +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/test-example/all-sites); diff --git a/test-example/fake-userv b/test-example/fake-userv new file mode 100755 index 0000000..6f5da40 --- /dev/null +++ b/test-example/fake-userv @@ -0,0 +1,11 @@ +#!/bin/sh +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 "$@" diff --git a/test-example/inside-polypath.conf b/test-example/inside-polypath.conf new file mode 100644 index 0000000..5e66e01 --- /dev/null +++ b/test-example/inside-polypath.conf @@ -0,0 +1,20 @@ +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 diff --git a/test-example/inside.conf b/test-example/inside.conf new file mode 100644 index 0000000..060a0bf --- /dev/null +++ b/test-example/inside.conf @@ -0,0 +1,21 @@ +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 diff --git a/test-example/inside.key.b64 b/test-example/inside.key.b64 new file mode 100644 index 0000000..d384c7c --- /dev/null +++ b/test-example/inside.key.b64 @@ -0,0 +1,10 @@ +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== diff --git a/test-example/inside.key.pub b/test-example/inside.key.pub new file mode 100644 index 0000000..85fea76 --- /dev/null +++ b/test-example/inside.key.pub @@ -0,0 +1 @@ +1024 65537 130064631890186713927887504218626486455931306300999583387009075747001546036643522074275473238061323169592347601185592753550279410171535737146240085267000508853176463710554801101055212967131924064664249613912656320653505750073021702169423354903540699008756137338575553686987244488914481168225136440872431691669 inside@example.com diff --git a/test-example/memcheck.suppressions b/test-example/memcheck.suppressions new file mode 100644 index 0000000..42080da --- /dev/null +++ b/test-example/memcheck.suppressions @@ -0,0 +1,14 @@ +{ + secnet_read_conffile + Memcheck:Leak + ... + fun:read_conffile + fun:main +} +{ + secnet_enter_phase + Memcheck:Leak + ... + fun:enter_phase + fun:main +} diff --git a/test-example/outside-random.conf b/test-example/outside-random.conf new file mode 100644 index 0000000..5c23920 --- /dev/null +++ b/test-example/outside-random.conf @@ -0,0 +1,16 @@ +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 diff --git a/test-example/outside-unshare.conf b/test-example/outside-unshare.conf new file mode 100644 index 0000000..2811962 --- /dev/null +++ b/test-example/outside-unshare.conf @@ -0,0 +1,16 @@ +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 diff --git a/test-example/outside.conf b/test-example/outside.conf new file mode 100644 index 0000000..8944130 --- /dev/null +++ b/test-example/outside.conf @@ -0,0 +1,16 @@ +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 diff --git a/test-example/outside.key.b64 b/test-example/outside.key.b64 new file mode 100644 index 0000000..f8ed4aa --- /dev/null +++ b/test-example/outside.key.b64 @@ -0,0 +1,10 @@ +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= diff --git a/test-example/outside.key.pub b/test-example/outside.key.pub new file mode 100644 index 0000000..5e4cc0f --- /dev/null +++ b/test-example/outside.key.pub @@ -0,0 +1 @@ +1024 65537 129251483458784900555621175262818292872587807329014927540074484804119474262261383244074013537736576331652560727149001626325243856012659665194546933097292703586821422085819615124517093786704646988649444946154384037948502112302285511195679291084694375811092516151263088200304199780052361048758446082354317801941 outside@example.com diff --git a/test-example/random-fake-userv b/test-example/random-fake-userv new file mode 100755 index 0000000..cccd22c --- /dev/null +++ b/test-example/random-fake-userv @@ -0,0 +1,45 @@ +#!/usr/bin/perl -w + +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; +} diff --git a/test-example/sites b/test-example/sites new file mode 100644 index 0000000..93d536a --- /dev/null +++ b/test-example/sites @@ -0,0 +1,23 @@ +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 + 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 diff --git a/transform-cbcmac.c b/transform-cbcmac.c new file mode 100644 index 0000000..b481346 --- /dev/null +++ b/transform-cbcmac.c @@ -0,0 +1,381 @@ +/* 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 +} diff --git a/transform-common.h b/transform-common.h new file mode 100644 index 0000000..4351ce8 --- /dev/null +++ b/transform-common.h @@ -0,0 +1,127 @@ +/* + * 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*/ diff --git a/transform-eax.c b/transform-eax.c new file mode 100644 index 0000000..04cd0e6 --- /dev/null +++ b/transform-eax.c @@ -0,0 +1,324 @@ +/* + * 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); +} diff --git a/tun.c b/tun.c new file mode 100644 index 0000000..3ba62b3 --- /dev/null +++ b/tun.c @@ -0,0 +1,650 @@ +/* + * 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); +} diff --git a/u64.h b/u64.h new file mode 100644 index 0000000..0d35c55 --- /dev/null +++ b/u64.h @@ -0,0 +1,159 @@ +/* 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 diff --git a/udp.c b/udp.c new file mode 100644 index 0000000..d1ada01 --- /dev/null +++ b/udp.c @@ -0,0 +1,516 @@ +/* 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); +} diff --git a/uk.org.greenend.secnet.plist b/uk.org.greenend.secnet.plist new file mode 100644 index 0000000..f462c03 --- /dev/null +++ b/uk.org.greenend.secnet.plist @@ -0,0 +1,42 @@ + + + + + + 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 + + + diff --git a/unaligned.h b/unaligned.h new file mode 100644 index 0000000..2c48d0e --- /dev/null +++ b/unaligned.h @@ -0,0 +1,65 @@ +/* + * 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 */ diff --git a/util.c b/util.c new file mode 100644 index 0000000..977e131 --- /dev/null +++ b/util.c @@ -0,0 +1,807 @@ +/* + * 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; +} + +int32_t write_mpbin(MP_INT *a, uint8_t *buffer, int32_t buflen) +{ + char *hb = write_mpstring(a); + int32_t len; + hex_decode(buffer, buflen, &len, hb, True); + free(hb); + return len; +} + +#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; +} + +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 +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..a2ef828 --- /dev/null +++ b/util.h @@ -0,0 +1,268 @@ +/* + * 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 int32_t write_mpbin(MP_INT *a, uint8_t *buffer, int32_t buflen); + /* Convert a MP_INT into a buffer; return length; truncate if necessary */ + +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); + + +/* + * 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 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 */