=== added file '.bzrignore' --- .bzrignore 1970-01-01 00:00:00 +0000 +++ .bzrignore 2008-09-06 16:33:54 +0000 @@ -0,0 +1,8 @@ +*.5 +*.8 +*.8mandos +plugin-runner +plugins.d/password-prompt +plugins.d/mandos-client +confdir +keydir === added file 'COPYING' --- COPYING 1970-01-01 00:00:00 +0000 +++ COPYING 2008-08-15 20:17:32 +0000 @@ -0,0 +1,676 @@ + + 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 +. + === added file 'INSTALL' --- INSTALL 1970-01-01 00:00:00 +0000 +++ INSTALL 2008-09-08 18:54:47 +0000 @@ -0,0 +1,114 @@ +-*- org -*- + +* Prerequisites + +** Operating System + + Debian 5.0 "lenny" or Ubuntu 8.04 "Hardy Heron". + + This is mostly for the support scripts which make sure that the + client is installed and started in the initial RAM disk environment + and that the initrd.img file is automatically made unreadable. The + server and client programs themselves *could* be run in other + distributions, but they *are* specific to GNU/Linux systems, and + are not intended to be portable to other Unixes. + +** Libraries + + The following libraries and packages are needed. (It is possible + that it might work with older versions of some of these, but these + versions are confirmed to work. Newer versions are almost + certainly OK.) + +*** Documentation + These are required to build the manual pages for both the server + and client: + + + DocBook 4.5 http://www.docbook.org/ + Note: DocBook 5.0 is not compatible. + + DocBook XSL stylesheets 1.71.0 + http://wiki.docbook.org/topic/DocBookXslStylesheets + + Package names: + docbook docbook-xsl + + To build just the documentation, run the command "make doc". Then + the manual page "mandos.8", for example, can be read by running + "man -l mandos.8". + +*** Mandos Server + + GnuTLS 2.4 http://www.gnu.org/software/gnutls/ + + Avahi 0.6.16 http://www.avahi.org/ + + Python 2.4 http://www.python.org/ + + Python-GnuTLS 1.1.5 http://pypi.python.org/pypi/python-gnutls/ + + dbus-python 0.82.4 http://dbus.freedesktop.org/doc/dbus-python/ + + python-ctypes 1.0.0 http://pypi.python.org/pypi/ctypes + + Strongly recommended: + + fping 2.4b2-to-ipv6 http://www.fping.com/ + + Package names: + python-gnutls avahi-daemon python python-avahi python-dbus + python-ctypes + +*** Mandos Client + + initramfs-tools 0.85i + http://packages.qa.debian.org/i/initramfs-tools.html + + GnuTLS 2.4 http://www.gnu.org/software/gnutls/ + + Avahi 0.6.16 http://www.avahi.org/ + + GnuPG 1.4.9 http://www.gnupg.org/ + + GPGME 1.1.6 http://www.gnupg.org/related_software/gpgme/ + + Package names: + initramfs-tools libgnutls-dev libavahi-core-dev gnupg + libgpgme11-dev + +* Installing the Mandos server + + 1. Do "make doc". + + 2. On the computer to run as a Mandos server, run the following + command: + For Debian: su -c 'make install-server' + For Ubuntu: sudo make install-server + + (This creates a configuration without any clients configured; you + need an actually configured client to do that; see below.) + +* Installing the Mandos client. + + 1. Do "make all doc". + + 2. On the computer to run as a Mandos client, run the following + command: + For Debian: su -c 'make install-client' + For Ubuntu: sudo make install-client + + This will also create an OpenPGP key, which will take some time + and entropy, so be patient. + + 3. Run the following command: + For Debian: su -c 'mandos-keygen --password' + For Ubuntu: sudo mandos-keygen --password + + When prompted, enter the password/passphrase for the encrypted + root file system on this client computer. The command will + output a section of text, starting with a [section header]. Copy + and append this to the file "/etc/mandos/clients.conf" *on the + server computer*. + + 4. On the server computer, start the server by running the command + For Debian: su -c 'invoke-rc.d mandos start' + For Ubuntu: sudo invoke-rc.d mandos start + + After this, the client computer should be able to reboot without + needing a password entered on the console, as long as it does not + take more than an hour to reboot. + +* Further customizations + + You may want to tighten or loosen the timeouts in the server + configuration files; see mandos.conf(5) and mandos-clients.conf(5). + Is IPsec is not used, it is suggested that a more cryptographically + secure checker program is used and configured, since without IPsec + ping packets can be faked. === modified file 'Makefile' --- Makefile 2007-10-28 17:59:38 +0000 +++ Makefile 2008-09-07 09:36:35 +0000 @@ -1,7 +1,255 @@ -CXXFLAGS=-Wall -W -g -LDFLAGS=-lgnutls - -all: client server +WARN=-O -Wall -Wformat=2 -Winit-self -Wmissing-include-dirs \ + -Wswitch-default -Wswitch-enum -Wunused-parameter \ + -Wstrict-aliasing=2 -Wextra -Wfloat-equal -Wundef -Wshadow \ + -Wunsafe-loop-optimizations -Wpointer-arith \ + -Wbad-function-cast -Wcast-qual -Wcast-align -Wwrite-strings \ + -Wconversion -Wstrict-prototypes -Wold-style-definition \ + -Wpacked -Wnested-externs -Winline -Wvolatile-register-var +# -Wunreachable-code +#DEBUG=-ggdb3 +# For info about _FORTIFY_SOURCE, see +# +FORTIFY=-D_FORTIFY_SOURCE=2 # -fstack-protector-all +#COVERAGE=--coverage +OPTIMIZE=-Os +LANGUAGE=-std=gnu99 + +## Use these settings for a traditional /usr/local install +# PREFIX=$(DESTDIR)/usr/local +# CONFDIR=$(DESTDIR)/etc/mandos +# KEYDIR=$(DESTDIR)/etc/mandos/keys +# MANDIR=$(PREFIX)/man +# INITRAMFSTOOLS=$(DESTDIR)/etc/initramfs-tools +## + +## These settings are for a package-type install +PREFIX=$(DESTDIR)/usr +CONFDIR=$(DESTDIR)/etc/mandos +KEYDIR=$(DESTDIR)/etc/keys/mandos +MANDIR=$(PREFIX)/share/man +INITRAMFSTOOLS=$(DESTDIR)/usr/share/initramfs-tools +## + +GNUTLS_CFLAGS=$(shell libgnutls-config --cflags) +GNUTLS_LIBS=$(shell libgnutls-config --libs) +AVAHI_CFLAGS=$(shell pkg-config --cflags-only-I avahi-core) +AVAHI_LIBS=$(shell pkg-config --libs avahi-core) +GPGME_CFLAGS=$(shell gpgme-config --cflags) +GPGME_LIBS=$(shell gpgme-config --libs) + +# Do not change these two +CFLAGS=$(WARN) $(DEBUG) $(FORTIFY) $(COVERAGE) $(OPTIMIZE) \ + $(LANGUAGE) $(GNUTLS_CFLAGS) $(AVAHI_CFLAGS) $(GPGME_CFLAGS) +LDFLAGS=$(COVERAGE) + +# Commands to format a DocBook document into a manual page +DOCBOOKTOMAN=cd $(dir $<); xsltproc --nonet --xinclude \ + --param man.charmap.use.subset 0 \ + --param make.year.ranges 1 \ + --param make.single.year.ranges 1 \ + --param man.output.quietly 1 \ + --param man.authors.section.enabled 0 \ + /usr/share/xml/docbook/stylesheet/nwalsh/manpages/docbook.xsl \ + $(notdir $<); \ + $(MANPOST) $(notdir $@) +# DocBook-to-man post-processing to fix a '\n' escape bug +MANPOST=sed --in-place --expression='s,\\\\en,\\en,g;s,\\n,\\en,g' + +PLUGINS=plugins.d/password-prompt plugins.d/mandos-client +PROGS=plugin-runner $(PLUGINS) +DOCS=mandos.8 plugin-runner.8mandos mandos-keygen.8 \ + plugins.d/mandos-client.8mandos \ + plugins.d/password-prompt.8mandos mandos.conf.5 \ + mandos-clients.conf.5 + +objects=$(addsuffix .o,$(PROGS)) + +all: $(PROGS) + +doc: $(DOCS) + +%.5: %.xml legalnotice.xml + $(DOCBOOKTOMAN) + +%.8: %.xml legalnotice.xml + $(DOCBOOKTOMAN) + +%.8mandos: %.xml legalnotice.xml + $(DOCBOOKTOMAN) + +mandos.8: mandos.xml mandos-options.xml overview.xml legalnotice.xml + $(DOCBOOKTOMAN) + +mandos-keygen.8: mandos-keygen.xml overview.xml legalnotice.xml + $(DOCBOOKTOMAN) + +mandos.conf.5: mandos.conf.xml mandos-options.xml legalnotice.xml + $(DOCBOOKTOMAN) + +plugin-runner.8mandos: plugin-runner.xml overview.xml legalnotice.xml + $(DOCBOOKTOMAN) + +plugins.d/mandos-client.8mandos: plugins.d/mandos-client.xml \ + mandos-options.xml \ + overview.xml legalnotice.xml + $(DOCBOOKTOMAN) + +plugins.d/mandos-client: plugins.d/mandos-client.o + $(LINK.o) $(GNUTLS_LIBS) $(AVAHI_LIBS) $(GPGME_LIBS) \ + $(COMMON) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +.PHONY : all doc clean distclean run-client run-server install \ + install-server install-client uninstall uninstall-server \ + uninstall-client purge purge-server purge-client clean: - rm -f server client + -rm --force $(PROGS) $(objects) $(DOCS) core + +distclean: clean +mostlyclean: clean +maintainer-clean: clean + -rm --force --recursive keydir confdir + +check: + ./mandos --check + +# Run the client with a local config and key +run-client: all keydir/seckey.txt keydir/pubkey.txt + ./plugin-runner --plugin-dir=plugins.d \ + --config-file=plugin-runner.conf \ + --options-for=mandos-client:--seckey=keydir/seckey.txt,--pubkey=keydir/pubkey.txt + +# Used by run-client +keydir/seckey.txt keydir/pubkey.txt: mandos-keygen + install --directory keydir + ./mandos-keygen --dir keydir --force + +# Run the server with a local config +run-server: confdir/mandos.conf confdir/clients.conf + ./mandos --debug --configdir=confdir + +# Used by run-server +confdir/mandos.conf: mandos.conf + install --directory confdir + install --mode=u=rw,go=r $^ $@ +confdir/clients.conf: clients.conf keydir/seckey.txt + install --directory confdir + install --mode=u=rw $< $@ +# Add a client password + ./mandos-keygen --dir keydir --password >> $@ + +install: install-server install-client + +install-server: doc + install --directory $(CONFDIR) $(MANDIR)/man5 \ + $(MANDIR)/man8 + install --mode=u=rwx,go=rx mandos $(PREFIX)/sbin/mandos + install --mode=u=rw,go=r --target-directory=$(CONFDIR) \ + mandos.conf + install --mode=u=rw --target-directory=$(CONFDIR) \ + clients.conf + install --mode=u=rwx,go=rx init.d-mandos \ + $(DESTDIR)/etc/init.d/mandos + install --mode=u=rw,go=r default-mandos \ + $(DESTDIR)/etc/default/mandos + update-rc.d mandos defaults + gzip --best --to-stdout mandos.8 \ + > $(MANDIR)/man8/mandos.8.gz + gzip --best --to-stdout mandos.conf.5 \ + > $(MANDIR)/man5/mandos.conf.5.gz + gzip --best --to-stdout mandos-clients.conf.5 \ + > $(MANDIR)/man5/mandos-clients.conf.5.gz + +install-client: all doc $(INITRAMFSTOOLS)/hooks/. + install --directory $(PREFIX)/lib/mandos $(CONFDIR) \ + $(MANDIR)/man8 + install --directory --mode=u=rwx $(KEYDIR) + install --directory --mode=u=rwx \ + $(PREFIX)/lib/mandos/plugins.d + if [ "$(CONFDIR)" != "$(PREFIX)/lib/mandos" ]; then \ + install --mode=u=rwx \ + --directory "$(CONFDIR)/plugins.d"; \ + install --mode=u=rw,go=r etc-plugins.d-README \ + $(CONFDIR)/plugins.d/README ; \ + fi + install --mode=u=rwx,go=rx \ + --target-directory=$(PREFIX)/lib/mandos plugin-runner + install --mode=u=rwx,go=rx --target-directory=$(PREFIX)/sbin \ + mandos-keygen + install --mode=u=rwx,go=rx \ + --target-directory=$(PREFIX)/lib/mandos/plugins.d \ + plugins.d/password-prompt + install --mode=u=rwxs,go=rx \ + --target-directory=$(PREFIX)/lib/mandos/plugins.d \ + plugins.d/mandos-client + install --mode=u=rwx,go=rx \ + --target-directory=$(PREFIX)/lib/mandos/plugins.d \ + plugins.d/usplash + install initramfs-tools-hook \ + $(INITRAMFSTOOLS)/hooks/mandos + install initramfs-tools-hook-conf \ + $(INITRAMFSTOOLS)/conf-hooks.d/mandos + install initramfs-tools-script \ + $(INITRAMFSTOOLS)/scripts/local-top/mandos + install --mode=u=rw,go=r plugin-runner.conf $(CONFDIR) + gzip --best --to-stdout mandos-keygen.8 \ + > $(MANDIR)/man8/mandos-keygen.8.gz + gzip --best --to-stdout plugin-runner.8mandos \ + > $(MANDIR)/man8/plugin-runner.8mandos.gz + gzip --best --to-stdout plugins.d/password-prompt.8mandos \ + > $(MANDIR)/man8/password-prompt.8mandos.gz + gzip --best --to-stdout plugins.d/mandos-client.8mandos \ + > $(MANDIR)/man8/mandos-client.8mandos.gz +# Post-installation stuff + -$(PREFIX)/sbin/mandos-keygen --dir "$(KEYDIR)" + update-initramfs -k all -u + echo "Now run mandos-keygen --password --dir $(KEYDIR)" + +uninstall: uninstall-server uninstall-client + +uninstall-server: + -rm --force $(PREFIX)/sbin/mandos \ + $(MANDIR)/man8/mandos.8.gz \ + $(MANDIR)/man5/mandos.conf.5.gz \ + $(MANDIR)/man5/mandos-clients.conf.5.gz + update-rc.d -f mandos remove + -rmdir $(CONFDIR) + +uninstall-client: +# Refuse to uninstall client if /etc/crypttab is explicitly configured +# to use it. + ! grep --regexp='^ *[^ #].*keyscript=[^,=]*/mandos/' \ + $(DESTDIR)/etc/crypttab + -rm --force $(PREFIX)/sbin/mandos-keygen \ + $(PREFIX)/lib/mandos/plugin-runner \ + $(PREFIX)/lib/mandos/plugins.d/password-prompt \ + $(PREFIX)/lib/mandos/plugins.d/mandos-client \ + $(PREFIX)/lib/mandos/plugins.d/usplash \ + $(INITRAMFSTOOLS)/hooks/mandos \ + $(INITRAMFSTOOLS)/conf-hooks.d/mandos \ + $(INITRAMFSTOOLS)/scripts/local-top/mandos \ + $(MANDIR)/man8/plugin-runner.8mandos.gz \ + $(MANDIR)/man8/mandos-keygen.8.gz \ + $(MANDIR)/man8/password-prompt.8mandos.gz \ + $(MANDIR)/man8/mandos-client.8mandos.gz + if [ "$(CONFDIR)" != "$(PREFIX)/lib/mandos" ]; then \ + rm --force $(CONFDIR)/plugins.d/README; \ + fi + -rmdir $(PREFIX)/lib/mandos/plugins.d $(CONFDIR)/plugins.d \ + $(PREFIX)/lib/mandos $(CONFDIR) $(KEYDIR) + update-initramfs -k all -u + +purge: purge-server purge-client + +purge-server: uninstall-server + -rm --force $(CONFDIR)/mandos.conf $(CONFDIR)/clients.conf \ + $(DESTDIR)/etc/default/mandos \ + $(DESTDIR)/etc/init.d/mandos \ + $(DESTDIR)/var/run/mandos.pid + -rmdir $(CONFDIR) + +purge-client: uninstall-client + -shred --remove $(KEYDIR)/seckey.txt + -rm --force $(CONFDIR)/plugin-runner.conf \ + $(KEYDIR)/pubkey.txt $(KEYDIR)/seckey.txt + -rmdir $(KEYDIR) $(CONFDIR)/plugins.d $(CONFDIR) === added file 'README' --- README 1970-01-01 00:00:00 +0000 +++ README 2008-09-05 07:11:24 +0000 @@ -0,0 +1,131 @@ +-*- org -*- + +* Mandos + - Have your cake and eat it too! + + You know how it is. You've heard of it happening. The Man comes + and takes away your servers, your friends' servers, the servers of + everybody in the same hosting facility. The servers of their + neighbors, and their neighbors' friends. The servers of people who + owe them money. And like *that*, they're gone. And you doubt + you'll ever see them again. + + That is why your servers have encrypted root file systems. However, + there's a downside. There's no going around it: rebooting is a + pain. Dragging out that rarely-used keyboard and screen and + unraveling cables behind your servers to plug them in to type in + that password is messy, especially if you have many servers. There + are some people who do clever things like using serial line consoles + and daisy-chain it to the next server, and keep all the servers + connected in a ring with serial cables, which will work, if your + servers are physically close enough. There are also other + out-of-band management solutions, but with *all* these, you still + have to be on hand and manually type in the password at boot time. + Otherwise the server just sits there, waiting for a password. + + Wouldn't it be great if you could have the security of encrypted + root file systems and still have servers that could boot up + automatically if there was a short power outage while you were + asleep? That you could reboot at will, without having someone run + over to the server to type in the password? + + Well, with Mandos, you (almost) can! The gain in convenience will + only be offset by a small loss in security. The setup is as + follows: + + The server will still have its encrypted root file system. The + password to this file system will be stored on another computer + (henceforth known as the Mandos server) on the same local network. + The password will *not* be stored in plaintext, but encrypted with + OpenPGP. To decrypt this password, a key is needed. This key (the + Mandos client key) will not be stored there, but back on the + original server (henceforth known as the Mandos client) in the + initial RAM disk image. Oh, and all network Mandos client/server + communications will be encrypted, using TLS (SSL). + + So, at boot time, the Mandos client will ask for its encrypted data + over the network, decrypt it to get the password, use it to decrypt + the root file, and continue booting. + + Now, of course the initial RAM disk image is not on the encrypted + root file system, so anyone who had physical access could take the + server offline and read the disk with their own tools to get the + authentication keys used by a client. *But*, by then the Mandos + server should notice that the original server has been offline for + too long, and will no longer give out the encrypted key. The timing + here is the only real weak point, and the method, frequency and + timeout of the server’s checking can be adjusted to any desired + level of paranoia + + (The encrypted keys on the Mandos server is on its normal file + system, so those are safe, provided the root file system of *that* + server is encrypted.) + +* FAQ - couldn’t the security be defeated by... + +** Grabbing the Mandos client key from the initrd *really quickly*? + This, as mentioned above, is the only real weak point. But if you + set the timing values tight enough, this will be really difficult + to do. An attacker would have to physically disassemble the client + computer, extract the key from the initial RAM disk image, and then + connect to a *still online* Mandos server to get the encrypted key, + all *before* the Mandos server timeout kicks in and the Mandos + server refuses to give out the key to anyone. + + Now, as the typical SOP seems to be to barge in and turn off and + grab *all* computers, to maybe look at them months later, this is + not likely. If someone does that, the whole system *will* lock + itself up completely, since Mandos servers are no longer running. + + For sophisticated attackers who *could* do the clever thing, *and* + had physical access to the server for enough time, it would be + simpler to get a key for an encrypted file system by using hardware + memory scanners and reading it right off the memory bus. + +** Replay attacks? + Nope, the network stuff is all done over TLS, which provides + protection against that. + +** Man-in-the-middle? + No. The server only gives out the passwords to clients which have + *in the TLS handshake* proven that they do indeed hold the OpenPGP + private key corresponding to that client. + +** Physically grabbing the Mandos server computer? + You could protect *that* computer the old-fashioned way, with a + must-type-in-the-password-at-boot method. Or you could have two + computers be the Mandos server for each other. + + Multiple Mandos servers can coexist on a network without any + trouble. They do not clash, and clients will try all available + servers. This means that if just one reboots then the other can + bring it back up, but if both reboots at the same time they will + stay down until someone types in the password on one of them. + +** Faking ping replies? + The default for the server is to use "fping", the replies to which + could be faked to eliminate the timeout. But this could easily be + changed to any shell command, with any security measures you like. + It could, for instance, be changed to an SSH command with strict + keychecking, which could not be faked. Or IPsec could be used for + the ping packets, making them secure. + +* Security Summary + So, in summary: The only weakness in the Mandos system is from + people who have: + 1. The power to come in and physically take your servers, *and* + 2. The cunning and patience to do it carefully, one at a time, and + *quickly*, faking Mandos client/server responses for each one + before the timeout. + + While there are some who may be threatened by people who have *both* + these attributes, they do not, probably, constitute the majority. + + If you *do* face such opponents, you must figure that they could + just as well open your servers and read the file system keys right + off the memory by running wires to the memory bus. + + What Mandos is designed to protect against is *not* such determined, + focused, and competent attacks, but against the early morning knock + on your door and the sudden absence of all the servers in your + server room. Which it does nicely. === modified file 'TODO' --- TODO 2007-12-11 23:40:35 +0000 +++ TODO 2008-09-06 16:31:49 +0000 @@ -1,18 +1,85 @@ -[Client] -configuration for cert, key, CA and interface -IPv4 support -OpenPGP keys support - -[Server] -Configuration for key, cert, ca, crl, and stuff - config file -client-list - gnupg fil-hantering -client-monitoring -OpenPGP keys support -run-time communication with server - -[Mandos-tools/utilities] - List clients - Enable client - Disable client +-*- org -*- + +* plugin-runner + +* mandos-client +** [#B] Temporarily lower kernel log level + for less printouts during sucessfull boot. +** IPv4 support +** use strsep instead of strtok? +** Do not depend on GnuPG key rings on disk + This would mean creating new GnuPG key rings with GPGME by + importing the key files from scratch on every program start. +** Keydir move: /etc/mandos -> /etc/keys/mandos + Must create in preinst if not pre-depending on cryptsetup + +* password-prompt + +* mandos (server) +** [#A] /etc/init.d/mandos-server :teddy: +** [#B] Log level :bugs: +** /etc/mandos/clients.d/*.conf + Watch this directory and add/remove/update clients? +** config for TXT record +** [#B] Run-time communication with server :bugs: + Probably using D-Bus + See also [[*Mandos-tools]] +** Implement --foreground :bugs: + [[info:standards:Option%20Table][Table of Long Options]] +** Implement --socket + [[info:standards:Option%20Table][Table of Long Options]] +** Date+time on console log messages :bugs: + Is this the default? +** delete hook when clients fall out by timeout + +* Mandos-tools/utilities + All of this probably using D-Bus +** List clients +** Disable client +** Enable client +** Reboot timer + +* Man pages +** Use xinclude for common sections + Like authors, etc. + + +* Installer +** Client-side +*** Update initrd.img after installation + This seems to use some kind of "trigger" system + [[file:/usr/share/doc/dpkg/triggers.txt.gz]] + dpkg-trigger(1), deb-triggers(5) +*** mandos-keygen +**** "--passfile" option + Using the "secfile" option instead of "secret" +**** [#A] "--test" option + For testing decryption before rebooting. +** Server-side +*** [#A] Create mandos user and group for server +*** [#A] Create /var/run/mandos directory with perm and ownership +*** [#A] install rc.d script and do update-rc.d + between config files and man pages + + +* [#A] Package +** /usr/share/initramfs-tools/hooks/mandos +*** Do not install in initrd.img if configured not to. + Use "/etc/initramfs-tools/conf.d/mandos"? Definitely a debconf + question. +** /etc/bash_completion.d/mandos + From XML sources directly? +** unperish +** bzr-builddeb + +* INSTALL file + +* Web site + +* Mailing list + +* Announce project on news + [[news:comp.os.linux.announce]] + + +#+STARTUP: showall === removed file 'bad-ca.pem' --- bad-ca.pem 2007-10-20 21:38:25 +0000 +++ bad-ca.pem 1970-01-01 00:00:00 +0000 @@ -1,40 +0,0 @@ ------BEGIN CERTIFICATE----- -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 -YECR0qG15JD8TKJzZ6pLeK882aYvlBa0CN2png== ------END CERTIFICATE----- === removed file 'bad-cert.pem' --- bad-cert.pem 2007-10-20 21:38:25 +0000 +++ bad-cert.pem 1970-01-01 00:00:00 +0000 @@ -1,40 +0,0 @@ ------BEGIN CERTIFICATE----- -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 -EHN2JwZCBCN2fo+4r+CfOZm23g== ------END CERTIFICATE----- === removed file 'bad-client-cert.pem' --- bad-client-cert.pem 2007-10-20 21:38:25 +0000 +++ bad-client-cert.pem 1970-01-01 00:00:00 +0000 @@ -1,40 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIHEjCCBPqgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBjzELMAkGA1UEBhMCU0Ux -CzAJBgNVBAgTAkJMMRMwEQYDVQQHEwpLYXJsc2tyb25hMRUwEwYDVQQKEwxFdmls -IG1vbmtleXMxFDASBgNVBAsTC0V2aWwgd2UgYXJlMRMwEQYDVQQDEwppbnZhbGlk -IENBMRwwGgYJKoZIhvcNAQkBFg1ldmlsQGNlcnQuYmFkMB4XDTA3MTAxNjEzNTk0 -MVoXDTA4MTAxNTEzNTk0MVowgY4xCzAJBgNVBAYTAlNFMQswCQYDVQQIEwJCTDET -MBEGA1UEBxMKS2FybHNrcm9uYTEVMBMGA1UEChMMRXZpbCBtb25rZXlzMRQwEgYD -VQQLEwtFdmlsIHdlIGFyZTESMBAGA1UEAxQJRXZpbF9jZXJ0MRwwGgYJKoZIhvcN -AQkBFg1ldmlsQGNlcnQuYmFkMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC -AgEA59+lmXRec7Ro/SWlcw8UVpFS4U/ITlGtyd8mjyfVekY69A7gtC5s7cp/YHTe -OTB6piYcSmzADeaugNzz7Jg95bw9uBY+QcckTD3E06CjkRX27NTOY+0g2r1nlSdg -+ceYLHv6FmE9vE7cSDXWFCs0EuI39l0SiSQVqaeT2g9QY/f6L9FnZQnQz5USZqcc -g4ojYblybyi6dKQs8ToLicszBWwq9LyaFncq64df4rUINZSUzwHG3on5zJ6yYgpG -LWq6NSpAzjP1xk73aZqrj9J4eb9TTpgDiHwsdnoDsPeneTsJvyMlYJjEoP1KA0Sz -HDuO99BHU3GfprhCnXHzZL6LvfWO4A8oRI3To6zeTNOITL+SNfPLXvF5JQTAFHMU -6ZXUgXlOk9NLQ0KXmyaANurFAIcAmcPuf2NhWfd92ZlCbe9ZMVEe49WwvCVP8QrZ -Tk5O3xQ1ycd7Np5oyaDc+FT3VSPXpuMEV6BzVXB5/MEgSb+oVM3kC14lXQRj3G9T -9yvBXr5iNdgq4Mf2jmxsG9m/HhYzLsq05bqyHrHTmanWIxPsHy0Xk2bx9As3K7rd -O55ZRVVkAFPClv2wyLcHlVaUsL5zcM/l910wY8Wp46nidDujMbYwC6t+07FhoCiY -O/p3urT8zKa10R1hjcEjvmHnDzIaICk2U1cb82DKQfGdc+sCAwEAAaOCAXYwggFy -MAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgSwMCsGCWCGSAGG+EIBDQQeFhxU -aW55Q0EgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBRpk+NyHyWMbmj7 -6oQgWKpZLnMcTzCBxAYDVR0jBIG8MIG5gBSvCm4Xh0lucON3WqKrCK1orNs8KqGB -laSBkjCBjzELMAkGA1UEBhMCU0UxCzAJBgNVBAgTAkJMMRMwEQYDVQQHEwpLYXJs -c2tyb25hMRUwEwYDVQQKEwxFdmlsIG1vbmtleXMxFDASBgNVBAsTC0V2aWwgd2Ug -YXJlMRMwEQYDVQQDEwppbnZhbGlkIENBMRwwGgYJKoZIhvcNAQkBFg1ldmlsQGNl -cnQuYmFkggkA4J5pHzjTP20wGAYDVR0SBBEwD4ENZXZpbEBjZXJ0LmJhZDAYBgNV -HREEETAPgQ1ldmlsQGNlcnQuYmFkMAsGA1UdDwQEAwIFoDANBgkqhkiG9w0BAQUF -AAOCAgEAUPoa11yhVE6+/8TMhpNbderJ5ptfzBLbOEqBg4MW+w6lQTRcRuTL0YbC -J0lQahID0BxCiWxdNUoLj9BkiE2DpKDTcK/KKS7zxfdmlUmavbfNIPLxYXFsc3OG -eUbC0sG1YdsSc4JZTPBI5bwvIzED3/fJnvA4LNmmLeF/b+3wGY94IhhIZzXsW0KF -7nx3Rq7er6mom22Xwmc/xYrl+jqrzqEQjIVMtOPO77e44Gr9A1vZ5CM28WS+Fv43 -TDGoeB5y93uyjw8DjTtW2de1xb40CBAr/qmgIvFDxB3SpuXXPjoOarDckOS4rE0R -kinp/h7ddeOZsMEujsVGhNKx41pjUpnPAJzqHWC6fkwOyzDL6EjbwCQH6fJZb4/H -grLyhVrqMRQofRSdRc2dspF3OdIXlkjhZeohfAT3gXuchjyGijL4YXiu/X/Zdhxz -rpE+Og0FA9sZX1Lzv4xH3XjZWR9I+JzHe0ih3Lyt7UQpo1G95F2cmETrEyQrNxiD -56nz3G2x03btchbVM7AwEZ7T5F9gf2YZ2yXlvmvvoSM8qcG2+j6v1PPydCXaCviw -SOwIUYcAS/P/YDapMaUuImmbs+ZMeECi/052slB7lZdNzWit1Nw+cHgevHz2lX5p -pjPOT9FWd+w4+ebiww4BOK04X8h9zbb+4mn4dCitKdiEpVhGR9w= ------END CERTIFICATE----- === removed file 'bad-client-key.pem' --- bad-client-key.pem 2007-10-20 21:38:25 +0000 +++ bad-client-key.pem 1970-01-01 00:00:00 +0000 @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- === removed file 'bad-crl.pem' --- bad-crl.pem 2007-10-20 21:38:25 +0000 +++ bad-crl.pem 1970-01-01 00:00:00 +0000 @@ -1,18 +0,0 @@ ------BEGIN X509 CRL----- -MIIC1jCBvzANBgkqhkiG9w0BAQUFADCBjzELMAkGA1UEBhMCU0UxCzAJBgNVBAgT -AkJMMRMwEQYDVQQHEwpLYXJsc2tyb25hMRUwEwYDVQQKEwxFdmlsIG1vbmtleXMx -FDASBgNVBAsTC0V2aWwgd2UgYXJlMRMwEQYDVQQDEwppbnZhbGlkIENBMRwwGgYJ -KoZIhvcNAQkBFg1ldmlsQGNlcnQuYmFkFw0wNzEwMTYxNDQxMTVaFw0wNzExMTUx -NDQxMTVaMA0GCSqGSIb3DQEBBQUAA4ICAQCfNVz+uVzX2TaHb3YtAzVFrrjysuL/ -ltA2DAv4cpQSAYi6Gt5xDqcXE8ZonEA26taHYpXYnStnVsrrbK44sGFuvJc/y0jM -AdA3Ook/ECTPmnJMU3jUbtK6dqFmF0425dDVPAdVqxlB2+wlA+tk/RHSfKMVi44X -VCLPReQFpHUqMBdpqdfmxmtV4QXDgYAE5ramDiC0Eh9IZ1yEIKrFPx6VDUqph+uh -uM3lusqW3afqaNPLWNf2+hJoeHAYd+b0Gd/W9epONJ2hI8jdo3QrdGHgOxlKRyJa -3U4DDzi5rOy1eivxLo+OIzaxrff9Sw2yj0SvR7U9Tpkd3zIFBFxGujgNpA3g4NrK -xGk1cQtx96siwkt6hvfnsPY5HjtUedOxOlVs2Rji9fxFAu7t5q10HHIHgHpTTSaU -8GUmN8uiibndrrKEask6L0O48wg5fnjm7W1oOJG30z2N0zKV+HMSv146MoBUZFrK -xSDjHP5qjL6H3GCZPIIvNqEFbHeOafWcqQAsusvxiMgM73ZwBs68hZDvQ1VcYKAL -bsOwbzo/T3jh1shtpR2xL0pdTg+olqvgPlhLB07G4rCucVvrZnuw9YrtjBOEbIeH -w7Lgq2CmpEK5tH+Zv8wt+k6rkdafaGe7G+fN48ZXfJ2dHIH37ZmIjC6t0UjybEmy -e0R0/SpWZspsSQ== ------END X509 CRL----- === removed file 'bad-key.pem' --- bad-key.pem 2007-10-20 21:38:25 +0000 +++ bad-key.pem 1970-01-01 00:00:00 +0000 @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- === removed file 'ca.pem' --- ca.pem 2007-10-20 21:38:25 +0000 +++ ca.pem 1970-01-01 00:00:00 +0000 @@ -1,40 +0,0 @@ ------BEGIN CERTIFICATE----- -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 ------END CERTIFICATE----- === removed file 'cert.pem' --- cert.pem 2007-10-20 21:38:25 +0000 +++ cert.pem 1970-01-01 00:00:00 +0000 @@ -1,40 +0,0 @@ ------BEGIN CERTIFICATE----- -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 -QjmtPc7y1g== ------END CERTIFICATE----- === removed file 'client-cert.pem' --- client-cert.pem 2007-10-20 21:38:25 +0000 +++ client-cert.pem 1970-01-01 00:00:00 +0000 @@ -1,40 +0,0 @@ ------BEGIN CERTIFICATE----- -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 -63Z87xA2LHvcoMEpP/nJHnvts3X2o3u7FFG9 ------END CERTIFICATE----- === removed file 'client-key.pem' --- client-key.pem 2007-10-20 21:38:25 +0000 +++ client-key.pem 1970-01-01 00:00:00 +0000 @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- === removed file 'client.cpp' --- client.cpp 2007-10-28 17:59:38 +0000 +++ client.cpp 1970-01-01 00:00:00 +0000 @@ -1,147 +0,0 @@ -extern "C" { -#include // getaddrinfo, gai_strerror, socket, inet_pton - // connect -#include // getaddrinfo, gai_strerror, socket, inet_pton - // connect -#include // close -#include // getaddrinfo, gai_strerror -#include // inet_pton -#include // select -#include -} - -#include // fprintf -#include // perror -#include // memset - -#define SOCKET_ERR(err,s) if(err<0) {perror(s);return(1);} -#define PORT 49001 -#define CERTFILE "client-cert.pem" -#define KEYFILE "client-key.pem" -#define CAFILE "ca.pem" - -gnutls_certificate_credentials_t x509_cred; - -gnutls_session_t -initgnutls(){ - gnutls_session_t session; - - gnutls_global_init (); - - /* X509 stuff */ - gnutls_certificate_allocate_credentials (&x509_cred); - gnutls_certificate_set_x509_trust_file (x509_cred, CAFILE, GNUTLS_X509_FMT_PEM); - gnutls_certificate_set_x509_key_file (x509_cred, CERTFILE, KEYFILE, - GNUTLS_X509_FMT_PEM); - - //Gnutls stuff - gnutls_init (&session, GNUTLS_CLIENT); - gnutls_set_default_priority (session); - gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred); - return session; -} - - -int main (){ - int sd, ret; - char buffer[4096]; - struct sockaddr_in6 to; - struct sockaddr_in6 from; - gnutls_session_t session; - fd_set rfds_orig; - struct timeval timeout; - - session = initgnutls (); - - sd = socket(PF_INET6, SOCK_DGRAM, 0); - SOCKET_ERR(sd,"socket"); - - { - int flag = 1; - ret = setsockopt(sd, SOL_SOCKET, SO_BROADCAST, & flag, sizeof(flag)); - SOCKET_ERR(ret,"setsockopt broadcast"); - } - - setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5); - SOCKET_ERR(ret,"setsockopt bindtodevice"); - - memset (&to, '\0', sizeof (to)); - to.sin6_family = AF_INET6; - ret = inet_pton(AF_INET6, "ff02::1" , &to.sin6_addr); - SOCKET_ERR(ret,"setsockopt bindtodevice"); - to.sin6_port = htons (PORT); // Server Port number - - FD_ZERO(&rfds_orig); - FD_SET(sd, &rfds_orig); - - timeout.tv_sec = 10; - timeout.tv_usec = 0; - - - for(;;){ - sendto(sd, "Marco", 5, 0, reinterpret_cast(&to), sizeof(to)); - - fd_set rfds = rfds_orig; - - ret = select(sd+1, &rfds, 0, 0, & timeout); - SOCKET_ERR(sd,"select"); - - if (ret){ - socklen_t from_len = sizeof(from); - ret = recvfrom(sd,buffer,512,0, reinterpret_cast(& from), - & from_len); - SOCKET_ERR(ret,"recv"); - - if (strncmp(buffer,"Polo", 4) == 0){ - break; - } - } - } - - //shutdown procedure - close(sd); - - sleep(1); - - sd = socket(PF_INET6, SOCK_STREAM, 0); - SOCKET_ERR(sd,"socket"); - - setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5); - SOCKET_ERR(ret,"setsockopt bindtodevice"); - - memset(&to,0,sizeof(to)); - to.sin6_family = from.sin6_family; - to.sin6_port = from.sin6_port; - to.sin6_addr = from.sin6_addr; - to.sin6_scope_id = from.sin6_scope_id; - - ret = connect(sd,reinterpret_cast(&to),sizeof(to)); - SOCKET_ERR(ret,"connect"); - - gnutls_transport_set_ptr (session, reinterpret_cast (sd)); - - ret = gnutls_handshake (session); - - if (ret < 0) - { - fprintf (stderr, "*** Handshake failed\n"); - gnutls_perror (ret); - return 1; - } - - //retrive password - ret = gnutls_record_recv (session, buffer, sizeof(buffer)); - - write(1,buffer,ret); - - //shutdown procedure - gnutls_bye (session, GNUTLS_SHUT_RDWR); - close(sd); - gnutls_deinit (session); - gnutls_certificate_free_credentials (x509_cred); - gnutls_global_deinit (); - - close(sd); - - return 0; -} === removed file 'clients.conf' --- clients.conf 2007-10-28 17:59:38 +0000 +++ clients.conf 1970-01-01 00:00:00 +0000 @@ -1,3 +0,0 @@ -C=SE,ST=BL,L=Ronneby,O=gnustuff,CN=braxen_client,EMAIL=belorn@fukt.bsnet.se -The secret message is "squeamish ossifrage" -asdjiadsjadsads === renamed file 'mandos-clients.conf' => 'clients.conf' --- mandos-clients.conf 2007-12-11 23:40:35 +0000 +++ clients.conf 2008-08-27 01:18:25 +0000 @@ -1,11 +1,65 @@ -# Example -[foo] -dn = C=EX,ST=Example State,L=Example Locality,O=Example Organization,CN=foo.example.org -password = qwerty12 -fqdn = foo.example.org - -[braxen_client] -dn = C=SE,ST=BL,L=Ronneby,O=gnustuff,CN=braxen_client,EMAIL=belorn@fukt.bsnet.se -password = Squeamish ossifrage -fqdn = localhost -interval = 5m +# Default settings for all clients. These values are the default +# values, so uncomment and change them if you want different ones. +[DEFAULT] + +# How long until a client is considered invalid - that is, ineligible +# to get the data this server holds. +;timeout = 1h + +# How often to run the checker to confirm that a client is still up. +# Note: a new checker will not be started if an old one is still +# running. The server will wait for a checker to complete until the +# above "timeout" occurs, at which time the client will be marked +# invalid, and any running checker killed. +;interval = 5m + +# What command to run as "the checker". +;checker = fping -q -- %(host)s + + +;#### +;# Example client +;[foo] +; +;# OpenPGP key fingerprint +;fingerprint = 7788 2722 5BA7 DE53 9C5A 7CFA 59CF F7CD BD9A 5920 +; +;# This is base64-encoded binary data. It will be decoded and sent to +;# the client matching the above fingerprint. This should, of course, +;# be OpenPGP encrypted data, decryptable only by the client. +;secret = +; hQIOA6QdEjBs2L/HEAf/TCyrDe5Xnm9esa+Pb/vWF9CUqfn4srzVgSu234 +; REJMVv7lBSrPE2132Lmd2gqF1HeLKDJRSVxJpt6xoWOChGHg+TMyXDxK+N +; Xl89vGvdU1XfhKkVm9MDLOgT5ECDPysDGHFPDhqHOSu3Kaw2DWMV/iH9vz +; 3Z20erVNbdcvyBnuojcoWO/6yfB5EQO0BXp7kcyy00USA3CjD5FGZdoQGI +; Tb8A/ar0tVA5crSQmaSotm6KmNLhrFnZ5BxX+TiE+eTUTqSloWRY6VAvqW +; QHC7OASxK5E6RXPBuFH5IohUA2Qbk5AHt99pYvsIPX88j2rWauOokoiKZo +; t/9leJ8VxO5l3wf/U64IH8bkPIoWmWZfd/nqh4uwGNbCgKMyT+AnvH7kMJ +; 3i7DivfWl2mKLV0PyPHUNva0VQxX6yYjcOhj1R6fCr/at8/NSLe2OhLchz +; dC+Ls9h+kvJXgF8Sisv+Wk/1RadPLFmraRlqvJwt6Ww21LpiXqXHV2mIgq +; WnR98YgSvUi3TJHrUQiNc9YyBzuRo0AjgG2C9qiE3FM+Y28+iQ/sR3+bFs +; zYuZKVTObqiIslwXu7imO0cvvFRgJF/6u3HNFQ4LUTGhiM3FQmC6NNlF3/ +; vJM2hwRDMcJqDd54Twx90Wh+tYz0z7QMsK4ANXWHHWHR0JchnLWmenzbtW +; 5MHdW9AYsNJZAQSOpirE4Xi31CSlWAi9KV+cUCmWF5zOFy1x23P6PjdaRm +; 4T2zw4dxS5NswXWU0sVEXxjs6PYxuIiCTL7vdpx8QjBkrPWDrAbcMyBr2O +; QlnHIvPzEArRQLo= +; +;# Host name; used only by the checker, not used by the server itself. +;host = foo.example.org +;#### + +;#### +;# Another example client, named "bar". +;[bar] +;# The fingerprint is not space or case sensitive +;fingerprint = 3e393aeaefb84c7e89e2f547b3a107558fca3a27 +; +;# If "secret" is not specified, a file can be read for the data. +;;secfile = /etc/mandos/bar-secret.txt.asc +; +;# An IP address for host is also fine, if the checker accepts it. +;host = 192.0.2.3 +; +;# Parameters from the [DEFAULT] section can be overridden per client. +;interval = 5m +;#### === removed file 'crl.pem' --- crl.pem 2007-10-20 21:38:25 +0000 +++ crl.pem 1970-01-01 00:00:00 +0000 @@ -1,18 +0,0 @@ ------BEGIN X509 CRL----- -MIIC0zCBvDANBgkqhkiG9w0BAQUFADCBjDELMAkGA1UEBhMCU0UxCzAJBgNVBAgT -AkJMMRAwDgYDVQQHEwdSb25uZWJ5MSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRz -IFB0eSBMdGQxFjAUBgNVBAMTDUJyYXhlbiB1bml0ZWQxIzAhBgkqhkiG9w0BCQEW -FGJlbG9ybkBmdWt0LmJzbmV0LnNlFw0wNzEwMTUxNzQ2MTlaFw0wNzExMTQxNzQ2 -MTlaMA0GCSqGSIb3DQEBBQUAA4ICAQCYTCUYRfx8+lTNXsVMhLT/890agPGj7BQw -NhwHTZuEudPTxBtOLPf0za4z7eGTD9ggu7SayQWEOV4bfjv1yiOLzf6vEHdzv1Ee -mlLhYgDMIhACrQmfKAmjoabsaK+VccBJW/R1oNW5Z9sWjFP91+3T7lfp8pPvWAlf -+9mJaaysd1yguY0OITAIWEL2lLlGtd85RYLvJe2nWZ6GrH5mIEYA7IQrnPgcU3ij -eAEn88I7EofUHfn1TMpMDJgMKm/edvEerLKb62AblcGLfo4gOBQWcvCAWLPzqxhE -wKag1xL8ucG6250yfkYBf3KEMLZAU8py5MwaIqMVOQzz3gsQ7dE87xR5GndLvPvr -149RbKDSZDdPDOEmCqlmb/Sxppm7jsNwqAphrZlNsBgLTrxih7Ex1cFph4jA5AZ5 -Hgqpftb94CdauOPz/AVu5aeXIwG0dpxhN0dtemwhHIxslFwDtuFWwcmP3upDZUOM -Q4ZZBp2A1lhDW86w3law8E2TCuDFmwNtqp5zOMtOwAJF2RK2DquaaxKQG0JRhdTi -f3mzjTTRPXomBgf7U1W25RFMO91uslCijAr/ELAa9SkFYWArnbfGabDsoy3OGL8D -fjuaz2eIdwvASyPwUkGlfeFBRDNIRCZ3szsvtThMFWUxKvKTRBTJf4AFLwx3XWOS -R6IxSk3t0A== ------END X509 CRL----- === added file 'default-mandos' --- default-mandos 1970-01-01 00:00:00 +0000 +++ default-mandos 2008-09-05 08:38:30 +0000 @@ -0,0 +1,4 @@ +# Directory where configuration files are located. Default is +# "/etc/mandos". +# +#CONFIGDIR=/etc/mandos === added file 'etc-plugins.d-README' --- etc-plugins.d-README 1970-01-01 00:00:00 +0000 +++ etc-plugins.d-README 2008-09-06 16:11:50 +0000 @@ -0,0 +1,5 @@ +Any plugins found here in /etc/mandos/plugins.d will override and add +to the normal Mandos plugins. When adding or changing plugins, do not +forget to update the initital RAM disk image: + +# update-initramfs -k all -u === added file 'init.d-mandos' --- init.d-mandos 1970-01-01 00:00:00 +0000 +++ init.d-mandos 2008-09-05 16:24:33 +0000 @@ -0,0 +1,159 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: mandos +# Required-Start: $remote_fs +# Required-Stop: $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Mandos server +# Description: Gives encrypted passwords to Mandos clients +### END INIT INFO + +# Author: Teddy Hogeborn +# Author: Björn Påhlsson +# +# Please remove the "Author" lines above and replace them +# with your own name if you copy and modify this script. + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="Mandos root file system password server" +NAME=mandos +DAEMON=/usr/sbin/$NAME +DAEMON_ARGS="" +PIDFILE=/var/run/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +if [ -n "$CONFIGDIR" ]; then + DAEMON_ARGS="$DAEMON_ARGS --configdir $CONFIGDIR" +fi + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.0-6) to ensure that this file is present. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2 + exit 3 + ;; +esac + +: === added file 'initramfs-tools-hook' --- initramfs-tools-hook 1970-01-01 00:00:00 +0000 +++ initramfs-tools-hook 2008-09-07 09:36:35 +0000 @@ -0,0 +1,162 @@ +#!/bin/sh + +# This script will be run by 'mkinitramfs' when it creates the image. +# Its job is to decide which files to install, then install them into +# the staging area, where the initramfs is being created. This +# happens when a new 'linux-image' package is installed, or when the +# administrator runs 'update-initramfs' by hand to update an initramfs +# image. + +# The environment contains at least: +# +# DESTDIR -- The staging directory where the image is being built. + +# No initramfs pre-requirements +PREREQ="cryptroot" + +prereqs() +{ + echo "$PREREQ" +} + +case $1 in +# get pre-requisites +prereqs) + prereqs + exit 0 + ;; +esac + +. /usr/share/initramfs-tools/hook-functions + +for d in /usr /usr/local; do + if [ -d "$d"/lib/mandos ]; then + prefix="$d" + break + fi +done +if [ -z "$prefix" ]; then + # Mandos not found + exit 1 +fi + +for d in /etc/keys/mandos /etc/mandos/keys; do + if [ -d "$d" ]; then + keydir="$d" + break + fi +done +if [ -z "$keydir" ]; then + # Mandos key directory not found + exit 1 +fi + +mandos_user="`{ getent passwd mandos \ + || getent passwd nobody \ + || echo ::65534::::; } \ + | awk --field-separator=: '{ print $3 }'`" +mandos_group="`{ getent group mandos \ + || getent group nogroup \ + || echo ::65534:; } \ + | awk --field-separator=: '{ print $3 }'`" + +# The Mandos network client uses the network +auto_add_modules net +# The Mandos network client uses IPv6 +force_load ipv6 + +# These are directories inside the initrd +CONFDIR="/conf/conf.d/mandos" +MANDOSDIR="/lib/mandos" +PLUGINDIR="${MANDOSDIR}/plugins.d" + +# Make directories +install --directory --mode=u=rwx,go=rx "${DESTDIR}${CONFDIR}" \ + "${DESTDIR}${MANDOSDIR}" +install --owner=${mandos_user} --group=${mandos_group} --directory \ + --mode=u=rwx "${DESTDIR}${PLUGINDIR}" + +# Copy the Mandos plugin runner +copy_exec "$prefix"/lib/mandos/plugin-runner "${MANDOSDIR}" + +# Copy the plugins + +# Copy the packaged plugins +for file in "$prefix"/lib/mandos/plugins.d/*; do + base="`basename \"$file\"`" + # Is this plugin overridden? + if [ -e "/etc/mandos/plugins.d/$base" ]; then + continue + fi + case "$base" in + *~|.*|\#*\#|*.dpkg-old|*.dpkg-new|*.dpkg-divert) : ;; + "*") :;; + *) copy_exec "$file" "${PLUGINDIR}";; + esac +done + +# Copy any user-supplied plugins +for file in /etc/mandos/plugins.d/*; do + base="`basename \"$file\"`" + case "$base" in + *~|.*|\#*\#|*.dpkg-old|*.dpkg-new|*.dpkg-divert) : ;; + "*") :;; + *) copy_exec "$file" "${PLUGINDIR}";; + esac +done + +# GPGME needs /usr/bin/gpg +if [ ! -e "${DESTDIR}/usr/bin/gpg" \ + -a -n "`ls \"${DESTDIR}\"/usr/lib/libgpgme.so* \ + 2>/dev/null`" ]; then + copy_exec /usr/bin/gpg +fi + +# Config files +for file in /etc/mandos/*; do + if [ -d "$file" ]; then + continue + fi + cp --archive --sparse=always "$file" "${DESTDIR}${CONFDIR}" +done + +# Key files +for file in "$keydir"/*; do + if [ -d "$file" ]; then + continue + fi + cp --archive --sparse=always "$file" "${DESTDIR}${CONFDIR}" + chown ${mandos_user}:${mandos_group} \ + "${DESTDIR}${CONFDIR}/`basename \"$file\"`" +done + +# /lib/mandos/plugin-runner will drop priviliges, but needs access to +# its plugin directory and its config file. However, since almost all +# files in initrd have been created with umask 027, this opening of +# permissions is needed. +# +# (The umask is not really intended to affect the files inside the +# initrd; it is intended to affect the initrd.img file itself, since +# it now contains secret key files. There is, however, no other way +# to set the permission of the initrd.img file without a race +# condition. This umask is set by "initramfs-tools-hook-conf", +# installed as "/usr/share/initramfs-tools/conf-hooks.d/mandos".) +# +for full in "${MANDOSDIR}" "${CONFDIR}"; do + while [ "$full" != "/" ]; do + chmod a+rX "${DESTDIR}$full" + full="`dirname \"$full\"`" + done +done + +# Reset some other things to sane permissions which we have +# inadvertently affected with our umask setting. +for dir in / /bin /etc /keyscripts /sbin /scripts /usr /usr/bin; do + if [ -d "${DESTDIR}$dir" ]; then + chmod a+rX "${DESTDIR}$dir" + fi +done +for dir in /lib /usr/lib; do + find "${DESTDIR}$dir" \! -perm -u+rw,g+r -prune -or -print0 \ + | xargs --null --no-run-if-empty chmod a+rX +done === added file 'initramfs-tools-hook-conf' --- initramfs-tools-hook-conf 1970-01-01 00:00:00 +0000 +++ initramfs-tools-hook-conf 2008-08-12 19:22:34 +0000 @@ -0,0 +1,1 @@ +UMASK=027 === added file 'initramfs-tools-script' --- initramfs-tools-script 1970-01-01 00:00:00 +0000 +++ initramfs-tools-script 2008-09-07 15:42:11 +0000 @@ -0,0 +1,76 @@ +#!/bin/sh -e +# +# This script will run in the initrd environment at boot and edit +# /conf/conf.d/cryptroot to set /lib/mandos/plugin-runner as keyscript +# when no other keyscript is set, before cryptsetup. +# + +# This script should be installed as +# "/usr/share/initramfs-tools/scripts/local-top/mandos" which will +# eventually be "/scripts/local-top/mandos" in the initrd.img file. + +# No initramfs pre-requirements; we must instead run BEFORE cryptroot. +# This is not a problem, since cryptroot forces itself to run LAST. +PREREQ="" +prereqs() +{ + echo "$PREREQ" +} + +case $1 in +prereqs) + prereqs + exit 0 + ;; +esac + +chmod a=rwxt /tmp + +test -w /conf/conf.d/cryptroot + +# Do not replace cryptroot file unless we need to. +replace_cryptroot=no + +# Our keyscript +mandos=/lib/mandos/plugin-runner + +# parse /conf/conf.d/cryptroot. Format: +# target=sda2_crypt,source=/dev/sda2,key=none,keyscript=/foo/bar/baz +exec 3>/conf/conf.d/cryptroot.mandos +while read options; do + newopts="" + # Split option line on commas + old_ifs="$IFS" + IFS="$IFS," + for opt in $options; do + # Find the keyscript option, if any + case "$opt" in + keyscript=*) + keyscript="${opt#keyscript=}" + newopts="$newopts,$opt" + ;; + "") : ;; + *) + newopts="$newopts,$opt" + ;; + esac + done + IFS="$old_ifs" + unset old_ifs + # If there was no keyscript option, add one. + if [ -z "$keyscript" ]; then + replace_cryptroot=yes + newopts="$newopts,keyscript=$mandos" + fi + newopts="${newopts#,}" + echo "$newopts" >&3 +done < /conf/conf.d/cryptroot +exec 3>&- + +# If we need to, replace the old cryptroot file with the new file. +if [ "$replace_cryptroot" = yes ]; then + mv /conf/conf.d/cryptroot /conf/conf.d/cryptroot.mandos-old + mv /conf/conf.d/cryptroot.mandos /conf/conf.d/cryptroot +else + rm /conf/conf.d/cryptroot.mandos +fi === removed file 'key.pem' --- key.pem 2007-10-20 21:38:25 +0000 +++ key.pem 1970-01-01 00:00:00 +0000 @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- === added file 'legalnotice.xml' --- legalnotice.xml 1970-01-01 00:00:00 +0000 +++ legalnotice.xml 2008-09-06 17:24:58 +0000 @@ -0,0 +1,27 @@ + + + + + This manual page 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 manual page 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 + http://www.gnu.org/licenses/. + + === renamed file 'server.py' => 'mandos' --- server.py 2007-12-11 23:40:35 +0000 +++ mandos 2008-09-05 18:37:28 +0000 @@ -1,4 +1,36 @@ #!/usr/bin/python +# -*- mode: python; coding: utf-8 -*- +# +# Mandos server - give out binary blobs to connecting clients. +# +# This program is partly derived from an example program for an Avahi +# service publisher, downloaded from +# . This includes the +# methods "add" and "remove" in the "AvahiService" class, the +# "server_state_changed" and "entry_group_state_changed" functions, +# and some lines in "main". +# +# Everything else is +# Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson +# +# 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 +# . +# +# Contact the authors at . +# + +from __future__ import division import SocketServer import socket @@ -9,194 +41,883 @@ import gnutls.crypto import gnutls.connection import gnutls.errors +import gnutls.library.functions +import gnutls.library.constants +import gnutls.library.types import ConfigParser +import sys +import re +import os +import signal +from sets import Set +import subprocess +import atexit +import stat +import logging +import logging.handlers +import pwd + +import dbus +import gobject +import avahi +from dbus.mainloop.glib import DBusGMainLoop +import ctypes + +version = "1.0" + +logger = logging.Logger('mandos') +syslogger = logging.handlers.SysLogHandler\ + (facility = logging.handlers.SysLogHandler.LOG_DAEMON, + address = "/dev/log") +syslogger.setFormatter(logging.Formatter\ + ('Mandos: %(levelname)s: %(message)s')) +logger.addHandler(syslogger) + +console = logging.StreamHandler() +console.setFormatter(logging.Formatter('%(name)s: %(levelname)s:' + ' %(message)s')) +logger.addHandler(console) + +class AvahiError(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) + +class AvahiServiceError(AvahiError): + pass + +class AvahiGroupError(AvahiError): + pass + + +class AvahiService(object): + """An Avahi (Zeroconf) service. + Attributes: + interface: integer; avahi.IF_UNSPEC or an interface index. + Used to optionally bind to the specified interface. + name: string; Example: 'Mandos' + type: string; Example: '_mandos._tcp'. + See + port: integer; what port to announce + TXT: list of strings; TXT record for the service + domain: string; Domain to publish on, default to .local if empty. + host: string; Host to publish records for, default is localhost + max_renames: integer; maximum number of renames + rename_count: integer; counter so we only rename after collisions + a sensible number of times + """ + def __init__(self, interface = avahi.IF_UNSPEC, name = None, + type = None, port = None, TXT = None, domain = "", + host = "", max_renames = 32768): + self.interface = interface + self.name = name + self.type = type + self.port = port + if TXT is None: + self.TXT = [] + else: + self.TXT = TXT + self.domain = domain + self.host = host + self.rename_count = 0 + self.max_renames = max_renames + def rename(self): + """Derived from the Avahi example code""" + if self.rename_count >= self.max_renames: + logger.critical(u"No suitable Zeroconf service name found" + u" after %i retries, exiting.", + rename_count) + raise AvahiServiceError("Too many renames") + self.name = server.GetAlternativeServiceName(self.name) + logger.info(u"Changing Zeroconf service name to %r ...", + str(self.name)) + syslogger.setFormatter(logging.Formatter\ + ('Mandos (%s): %%(levelname)s:' + ' %%(message)s' % self.name)) + self.remove() + self.add() + self.rename_count += 1 + def remove(self): + """Derived from the Avahi example code""" + if group is not None: + group.Reset() + def add(self): + """Derived from the Avahi example code""" + global group + if group is None: + group = dbus.Interface\ + (bus.get_object(avahi.DBUS_NAME, + server.EntryGroupNew()), + avahi.DBUS_INTERFACE_ENTRY_GROUP) + group.connect_to_signal('StateChanged', + entry_group_state_changed) + logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...", + service.name, service.type) + group.AddService( + self.interface, # interface + avahi.PROTO_INET6, # protocol + dbus.UInt32(0), # flags + self.name, self.type, + self.domain, self.host, + dbus.UInt16(self.port), + avahi.string_array_to_txt_array(self.TXT)) + group.Commit() + +# From the Avahi example code: +group = None # our entry group +# End of Avahi example code + class Client(object): - def __init__(self, name=None, dn=None, password=None, - passfile=None, fqdn=None, timeout=None, - interval=-1): + """A representation of a client host served by this server. + Attributes: + name: string; from the config file, used in log messages + fingerprint: string (40 or 32 hexadecimal digits); used to + uniquely identify the client + secret: bytestring; sent verbatim (over TLS) to client + host: string; available for use by the checker command + created: datetime.datetime(); object creation, not client host + last_checked_ok: datetime.datetime() or None if not yet checked OK + timeout: datetime.timedelta(); How long from last_checked_ok + until this client is invalid + interval: datetime.timedelta(); How often to start a new checker + stop_hook: If set, called by stop() as stop_hook(self) + checker: subprocess.Popen(); a running checker process used + to see if the client lives. + 'None' if no process is running. + checker_initiator_tag: a gobject event source tag, or None + stop_initiator_tag: - '' - + checker_callback_tag: - '' - + checker_command: string; External command which is run to check if + client lives. %() expansions are done at + runtime with vars(self) as dict, so that for + instance %(name)s can be used in the command. + Private attibutes: + _timeout: Real variable for 'timeout' + _interval: Real variable for 'interval' + _timeout_milliseconds: Used when calling gobject.timeout_add() + _interval_milliseconds: - '' - + """ + def _set_timeout(self, timeout): + "Setter function for 'timeout' attribute" + self._timeout = timeout + self._timeout_milliseconds = ((self.timeout.days + * 24 * 60 * 60 * 1000) + + (self.timeout.seconds * 1000) + + (self.timeout.microseconds + // 1000)) + timeout = property(lambda self: self._timeout, + _set_timeout) + del _set_timeout + def _set_interval(self, interval): + "Setter function for 'interval' attribute" + self._interval = interval + self._interval_milliseconds = ((self.interval.days + * 24 * 60 * 60 * 1000) + + (self.interval.seconds + * 1000) + + (self.interval.microseconds + // 1000)) + interval = property(lambda self: self._interval, + _set_interval) + del _set_interval + def __init__(self, name = None, stop_hook=None, config={}): + """Note: the 'checker' key in 'config' sets the + 'checker_command' attribute and *not* the 'checker' + attribute.""" self.name = name - self.dn = dn - if password: - self.password = password - elif passfile: - self.password = open(passfile).readall() - else: - print "No Password or Passfile in client config file" - # raise RuntimeError XXX - self.password = "gazonk" - self.fqdn = fqdn - # self.created = ... - self.last_seen = None - if timeout is None: - timeout = self.server.options.timeout - self.timeout = timeout - if interval == -1: - interval = self.server.options.interval - self.interval = interval - -def server_bind(self): - if self.options.interface: - if not hasattr(socket, "SO_BINDTODEVICE"): - # From /usr/include/asm-i486/socket.h - socket.SO_BINDTODEVICE = 25 + logger.debug(u"Creating client %r", self.name) + # Uppercase and remove spaces from fingerprint for later + # comparison purposes with return value from the fingerprint() + # function + self.fingerprint = config["fingerprint"].upper()\ + .replace(u" ", u"") + logger.debug(u" Fingerprint: %s", self.fingerprint) + if "secret" in config: + self.secret = config["secret"].decode(u"base64") + elif "secfile" in config: + sf = open(config["secfile"]) + self.secret = sf.read() + sf.close() + else: + raise TypeError(u"No secret or secfile for client %s" + % self.name) + self.host = config.get("host", "") + self.created = datetime.datetime.now() + self.last_checked_ok = None + self.timeout = string_to_delta(config["timeout"]) + self.interval = string_to_delta(config["interval"]) + self.stop_hook = stop_hook + self.checker = None + self.checker_initiator_tag = None + self.stop_initiator_tag = None + self.checker_callback_tag = None + self.check_command = config["checker"] + def start(self): + """Start this client's checker and timeout hooks""" + # Schedule a new checker to be started an 'interval' from now, + # and every interval from then on. + self.checker_initiator_tag = gobject.timeout_add\ + (self._interval_milliseconds, + self.start_checker) + # Also start a new checker *right now*. + self.start_checker() + # Schedule a stop() when 'timeout' has passed + self.stop_initiator_tag = gobject.timeout_add\ + (self._timeout_milliseconds, + self.stop) + def stop(self): + """Stop this client. + The possibility that a client might be restarted is left open, + but not currently used.""" + # If this client doesn't have a secret, it is already stopped. + if hasattr(self, "secret") and self.secret: + logger.info(u"Stopping client %s", self.name) + self.secret = None + else: + return False + if getattr(self, "stop_initiator_tag", False): + gobject.source_remove(self.stop_initiator_tag) + self.stop_initiator_tag = None + if getattr(self, "checker_initiator_tag", False): + gobject.source_remove(self.checker_initiator_tag) + self.checker_initiator_tag = None + self.stop_checker() + if self.stop_hook: + self.stop_hook(self) + # Do not run this again if called by a gobject.timeout_add + return False + def __del__(self): + self.stop_hook = None + self.stop() + def checker_callback(self, pid, condition): + """The checker has completed, so take appropriate actions.""" + now = datetime.datetime.now() + self.checker_callback_tag = None + self.checker = None + if os.WIFEXITED(condition) \ + and (os.WEXITSTATUS(condition) == 0): + logger.info(u"Checker for %(name)s succeeded", + vars(self)) + self.last_checked_ok = now + gobject.source_remove(self.stop_initiator_tag) + self.stop_initiator_tag = gobject.timeout_add\ + (self._timeout_milliseconds, + self.stop) + elif not os.WIFEXITED(condition): + logger.warning(u"Checker for %(name)s crashed?", + vars(self)) + else: + logger.info(u"Checker for %(name)s failed", + vars(self)) + def start_checker(self): + """Start a new checker subprocess if one is not running. + If a checker already exists, leave it running and do + nothing.""" + # The reason for not killing a running checker is that if we + # did that, then if a checker (for some reason) started + # running slowly and taking more than 'interval' time, the + # client would inevitably timeout, since no checker would get + # a chance to run to completion. If we instead leave running + # checkers alone, the checker would have to take more time + # than 'timeout' for the client to be declared invalid, which + # is as it should be. + if self.checker is None: + try: + # In case check_command has exactly one % operator + command = self.check_command % self.host + except TypeError: + # Escape attributes for the shell + escaped_attrs = dict((key, re.escape(str(val))) + for key, val in + vars(self).iteritems()) + try: + command = self.check_command % escaped_attrs + except TypeError, error: + logger.error(u'Could not format string "%s":' + u' %s', self.check_command, error) + return True # Try again later + try: + logger.info(u"Starting checker %r for %s", + command, self.name) + # We don't need to redirect stdout and stderr, since + # in normal mode, that is already done by daemon(), + # and in debug mode we don't want to. (Stdin is + # always replaced by /dev/null.) + self.checker = subprocess.Popen(command, + close_fds=True, + shell=True, cwd="/") + self.checker_callback_tag = gobject.child_watch_add\ + (self.checker.pid, + self.checker_callback) + except OSError, error: + logger.error(u"Failed to start subprocess: %s", + error) + # Re-run this periodically if run by gobject.timeout_add + return True + def stop_checker(self): + """Force the checker process, if any, to stop.""" + if self.checker_callback_tag: + gobject.source_remove(self.checker_callback_tag) + self.checker_callback_tag = None + if getattr(self, "checker", None) is None: + return + logger.debug(u"Stopping checker for %(name)s", vars(self)) try: - self.socket.setsockopt(socket.SOL_SOCKET, - socket.SO_BINDTODEVICE, - self.options.interface) - except socket.error, error: - if error[0] == errno.EPERM: - print "Warning: Denied permission to bind to interface", \ - self.options.interface - else: - raise error - return super(type(self), self).server_bind() - - -def init_with_options(self, *args, **kwargs): - if "options" in kwargs: - self.options = kwargs["options"] - del kwargs["options"] - if "clients" in kwargs: - self.clients = kwargs["clients"] - del kwargs["clients"] - if "credentials" in kwargs: - self.credentials = kwargs["credentials"] - del kwargs["credentials"] - return super(type(self), self).__init__(*args, **kwargs) - - -class udp_handler(SocketServer.DatagramRequestHandler, object): - def handle(self): - self.wfile.write("Polo") - print "UDP request answered" - - -class IPv6_UDPServer(SocketServer.UDPServer, object): - __init__ = init_with_options - address_family = socket.AF_INET6 - allow_reuse_address = True - server_bind = server_bind - def verify_request(self, request, client_address): - print "UDP request came" - return request[0] == "Marco" + os.kill(self.checker.pid, signal.SIGTERM) + #os.sleep(0.5) + #if self.checker.poll() is None: + # os.kill(self.checker.pid, signal.SIGKILL) + except OSError, error: + if error.errno != errno.ESRCH: # No such process + raise + self.checker = None + def still_valid(self): + """Has the timeout not yet passed for this client?""" + now = datetime.datetime.now() + if self.last_checked_ok is None: + return now < (self.created + self.timeout) + else: + return now < (self.last_checked_ok + self.timeout) + + +def peer_certificate(session): + "Return the peer's OpenPGP certificate as a bytestring" + # If not an OpenPGP certificate... + if gnutls.library.functions.gnutls_certificate_type_get\ + (session._c_object) \ + != gnutls.library.constants.GNUTLS_CRT_OPENPGP: + # ...do the normal thing + return session.peer_certificate + list_size = ctypes.c_uint() + cert_list = gnutls.library.functions.gnutls_certificate_get_peers\ + (session._c_object, ctypes.byref(list_size)) + if list_size.value == 0: + return None + cert = cert_list[0] + return ctypes.string_at(cert.data, cert.size) + + +def fingerprint(openpgp): + "Convert an OpenPGP bytestring to a hexdigit fingerprint string" + # New GnuTLS "datum" with the OpenPGP public key + datum = gnutls.library.types.gnutls_datum_t\ + (ctypes.cast(ctypes.c_char_p(openpgp), + ctypes.POINTER(ctypes.c_ubyte)), + ctypes.c_uint(len(openpgp))) + # New empty GnuTLS certificate + crt = gnutls.library.types.gnutls_openpgp_crt_t() + gnutls.library.functions.gnutls_openpgp_crt_init\ + (ctypes.byref(crt)) + # Import the OpenPGP public key into the certificate + gnutls.library.functions.gnutls_openpgp_crt_import\ + (crt, ctypes.byref(datum), + gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW) + # Verify the self signature in the key + crtverify = ctypes.c_uint(); + gnutls.library.functions.gnutls_openpgp_crt_verify_self\ + (crt, 0, ctypes.byref(crtverify)) + if crtverify.value != 0: + gnutls.library.functions.gnutls_openpgp_crt_deinit(crt) + raise gnutls.errors.CertificateSecurityError("Verify failed") + # New buffer for the fingerprint + buffer = ctypes.create_string_buffer(20) + buffer_length = ctypes.c_size_t() + # Get the fingerprint from the certificate into the buffer + gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\ + (crt, ctypes.byref(buffer), ctypes.byref(buffer_length)) + # Deinit the certificate + gnutls.library.functions.gnutls_openpgp_crt_deinit(crt) + # Convert the buffer to a Python bytestring + fpr = ctypes.string_at(buffer, buffer_length.value) + # Convert the bytestring to hexadecimal notation + hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr) + return hex_fpr class tcp_handler(SocketServer.BaseRequestHandler, object): + """A TCP request handler class. + Instantiated by IPv6_TCPServer for each request to handle it. + Note: This will run in its own forked process.""" + def handle(self): - print "TCP request came" - print "Request:", self.request - print "Client Address:", self.client_address - print "Server:", self.server - session = gnutls.connection.ServerSession(self.request, - self.server.credentials) - session.handshake() - if session.peer_certificate: - print "DN:", session.peer_certificate.subject - try: - session.verify_peer() - except gnutls.errors.CertificateError, error: - print "Verify failed", error - session.bye() - return - try: - session.send(dict((client.dn, client.password) - for client in self.server.clients) - [session.peer_certificate.subject]) - except KeyError: - session.send("gazonk") - # Log maybe? XXX + logger.info(u"TCP connection from: %s", + unicode(self.client_address)) + session = gnutls.connection.ClientSession\ + (self.request, gnutls.connection.X509Credentials()) + + line = self.request.makefile().readline() + logger.debug(u"Protocol version: %r", line) + try: + if int(line.strip().split()[0]) > 1: + raise RuntimeError + except (ValueError, IndexError, RuntimeError), error: + logger.error(u"Unknown protocol version: %s", error) + return + + # Note: gnutls.connection.X509Credentials is really a generic + # GnuTLS certificate credentials object so long as no X.509 + # keys are added to it. Therefore, we can use it here despite + # using OpenPGP certificates. + + #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC", + # "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP", + # "+DHE-DSS")) + priority = "NORMAL" # Fallback default, since this + # MUST be set. + if self.server.settings["priority"]: + priority = self.server.settings["priority"] + gnutls.library.functions.gnutls_priority_set_direct\ + (session._c_object, priority, None); + + try: + session.handshake() + except gnutls.errors.GNUTLSError, error: + logger.warning(u"Handshake failed: %s", error) + # Do not run session.bye() here: the session is not + # established. Just abandon the request. + return + try: + fpr = fingerprint(peer_certificate(session)) + except (TypeError, gnutls.errors.GNUTLSError), error: + logger.warning(u"Bad certificate: %s", error) + session.bye() + return + logger.debug(u"Fingerprint: %s", fpr) + client = None + for c in self.server.clients: + if c.fingerprint == fpr: + client = c + break + if not client: + logger.warning(u"Client not found for fingerprint: %s", + fpr) + session.bye() + return + # Have to check if client.still_valid(), since it is possible + # that the client timed out while establishing the GnuTLS + # session. + if not client.still_valid(): + logger.warning(u"Client %(name)s is invalid", + vars(client)) + session.bye() + return + sent_size = 0 + while sent_size < len(client.secret): + sent = session.send(client.secret[sent_size:]) + logger.debug(u"Sent: %d, remaining: %d", + sent, len(client.secret) + - (sent_size + sent)) + sent_size += sent session.bye() + class IPv6_TCPServer(SocketServer.ForkingTCPServer, object): - __init__ = init_with_options + """IPv6 TCP server. Accepts 'None' as address and/or port. + Attributes: + settings: Server settings + clients: Set() of Client objects + enabled: Boolean; whether this server is activated yet + """ address_family = socket.AF_INET6 - allow_reuse_address = True - request_queue_size = 1024 - server_bind = server_bind - - -in6addr_any = "::" - -cred = None + def __init__(self, *args, **kwargs): + if "settings" in kwargs: + self.settings = kwargs["settings"] + del kwargs["settings"] + if "clients" in kwargs: + self.clients = kwargs["clients"] + del kwargs["clients"] + self.enabled = False + return super(type(self), self).__init__(*args, **kwargs) + def server_bind(self): + """This overrides the normal server_bind() function + to bind to an interface if one was specified, and also NOT to + bind to an address or port if they were not specified.""" + if self.settings["interface"]: + # 25 is from /usr/include/asm-i486/socket.h + SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25) + try: + self.socket.setsockopt(socket.SOL_SOCKET, + SO_BINDTODEVICE, + self.settings["interface"]) + except socket.error, error: + if error[0] == errno.EPERM: + logger.error(u"No permission to" + u" bind to interface %s", + self.settings["interface"]) + else: + raise error + # Only bind(2) the socket if we really need to. + if self.server_address[0] or self.server_address[1]: + if not self.server_address[0]: + in6addr_any = "::" + self.server_address = (in6addr_any, + self.server_address[1]) + elif not self.server_address[1]: + self.server_address = (self.server_address[0], + 0) +# if self.settings["interface"]: +# self.server_address = (self.server_address[0], +# 0, # port +# 0, # flowinfo +# if_nametoindex +# (self.settings +# ["interface"])) + return super(type(self), self).server_bind() + def server_activate(self): + if self.enabled: + return super(type(self), self).server_activate() + def enable(self): + self.enabled = True + + +def string_to_delta(interval): + """Parse a string and return a datetime.timedelta + + >>> string_to_delta('7d') + datetime.timedelta(7) + >>> string_to_delta('60s') + datetime.timedelta(0, 60) + >>> string_to_delta('60m') + datetime.timedelta(0, 3600) + >>> string_to_delta('24h') + datetime.timedelta(1) + >>> string_to_delta(u'1w') + datetime.timedelta(7) + >>> string_to_delta('5m 30s') + datetime.timedelta(0, 330) + """ + timevalue = datetime.timedelta(0) + for s in interval.split(): + try: + suffix=unicode(s[-1]) + value=int(s[:-1]) + if suffix == u"d": + delta = datetime.timedelta(value) + elif suffix == u"s": + delta = datetime.timedelta(0, value) + elif suffix == u"m": + delta = datetime.timedelta(0, 0, 0, 0, value) + elif suffix == u"h": + delta = datetime.timedelta(0, 0, 0, 0, 0, value) + elif suffix == u"w": + delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value) + else: + raise ValueError + except (ValueError, IndexError): + raise ValueError + timevalue += delta + return timevalue + + +def server_state_changed(state): + """Derived from the Avahi example code""" + if state == avahi.SERVER_COLLISION: + logger.error(u"Zeroconf server name collision") + service.remove() + elif state == avahi.SERVER_RUNNING: + service.add() + + +def entry_group_state_changed(state, error): + """Derived from the Avahi example code""" + logger.debug(u"Avahi state change: %i", state) + + if state == avahi.ENTRY_GROUP_ESTABLISHED: + logger.debug(u"Zeroconf service established.") + elif state == avahi.ENTRY_GROUP_COLLISION: + logger.warning(u"Zeroconf service name collision.") + service.rename() + elif state == avahi.ENTRY_GROUP_FAILURE: + logger.critical(u"Avahi: Error in group state changed %s", + unicode(error)) + raise AvahiGroupError("State changed: %s", str(error)) + +def if_nametoindex(interface): + """Call the C function if_nametoindex(), or equivalent""" + global if_nametoindex + try: + if "ctypes.util" not in sys.modules: + import ctypes.util + if_nametoindex = ctypes.cdll.LoadLibrary\ + (ctypes.util.find_library("c")).if_nametoindex + except (OSError, AttributeError): + if "struct" not in sys.modules: + import struct + if "fcntl" not in sys.modules: + import fcntl + def if_nametoindex(interface): + "Get an interface index the hard way, i.e. using fcntl()" + SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h + s = socket.socket() + ifreq = fcntl.ioctl(s, SIOCGIFINDEX, + struct.pack("16s16x", interface)) + s.close() + interface_index = struct.unpack("I", ifreq[16:20])[0] + return interface_index + return if_nametoindex(interface) + + +def daemon(nochdir = False, noclose = False): + """See daemon(3). Standard BSD Unix function. + This should really exist as os.daemon, but it doesn't (yet).""" + if os.fork(): + sys.exit() + os.setsid() + if not nochdir: + os.chdir("/") + if os.fork(): + sys.exit() + if not noclose: + # Close all standard open file descriptors + null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR) + if not stat.S_ISCHR(os.fstat(null).st_mode): + raise OSError(errno.ENODEV, + "/dev/null not a character device") + os.dup2(null, sys.stdin.fileno()) + os.dup2(null, sys.stdout.fileno()) + os.dup2(null, sys.stderr.fileno()) + if null > 2: + os.close(null) + def main(): - parser = OptionParser() + global main_loop_started + main_loop_started = False + + parser = OptionParser(version = "%%prog %s" % version) parser.add_option("-i", "--interface", type="string", - default="eth0", metavar="IF", - help="Interface to bind to") - parser.add_option("--cert", type="string", default="cert.pem", - metavar="FILE", - help="Public key certificate to use") - parser.add_option("--key", type="string", default="key.pem", - metavar="FILE", - help="Private key to use") - parser.add_option("--ca", type="string", default="ca.pem", - metavar="FILE", - help="Certificate Authority certificate to use") - parser.add_option("--crl", type="string", default="crl.pem", - metavar="FILE", - help="Certificate Revokation List to use") - parser.add_option("-p", "--port", type="int", default=49001, + metavar="IF", help="Bind to interface IF") + parser.add_option("-a", "--address", type="string", + help="Address to listen for requests on") + parser.add_option("-p", "--port", type="int", help="Port number to receive requests on") - parser.add_option("--dh", type="int", metavar="BITS", - help="DH group to use") - parser.add_option("-t", "--timeout", type="string", # Parsed later - default="15m", - help="Amount of downtime allowed for clients") + parser.add_option("--check", action="store_true", default=False, + help="Run self-test") + parser.add_option("--debug", action="store_true", + help="Debug mode; run in foreground and log to" + " terminal") + parser.add_option("--priority", type="string", help="GnuTLS" + " priority string (see GnuTLS documentation)") + parser.add_option("--servicename", type="string", metavar="NAME", + help="Zeroconf service name") + parser.add_option("--configdir", type="string", + default="/etc/mandos", metavar="DIR", + help="Directory to search for configuration" + " files") (options, args) = parser.parse_args() - # Parse the time argument - try: - suffix=options.timeout[-1] - value=int(options.timeout[:-1]) - if suffix == "d": - options.timeout = datetime.timedelta(value) - elif suffix == "s": - options.timeout = datetime.timedelta(0, value) - elif suffix == "m": - options.timeout = datetime.timedelta(0, 0, 0, 0, value) - elif suffix == "h": - options.timeout = datetime.timedelta(0, 0, 0, 0, 0, value) - elif suffix == "w": - options.timeout = datetime.timedelta(0, 0, 0, 0, 0, 0, - value) - else: - raise ValueError - except (ValueError, IndexError): - parser.error("option --timeout: Unparseable time") - - cert = gnutls.crypto.X509Certificate(open(options.cert).read()) - key = gnutls.crypto.X509PrivateKey(open(options.key).read()) - ca = gnutls.crypto.X509Certificate(open(options.ca).read()) - crl = gnutls.crypto.X509CRL(open(options.crl).read()) - cred = gnutls.connection.X509Credentials(cert, key, [ca], [crl]) - - # Parse config file - defaults = {} - client_config_object = ConfigParser.SafeConfigParser(defaults) - client_config_object.read("mandos-clients.conf") - clients = [Client(name=section, - **(dict(client_config_object.items(section)))) - for section in client_config_object.sections()] - - udp_server = IPv6_UDPServer((in6addr_any, options.port), - udp_handler, - options=options) - - tcp_server = IPv6_TCPServer((in6addr_any, options.port), + if options.check: + import doctest + doctest.testmod() + sys.exit() + + # Default values for config file for server-global settings + server_defaults = { "interface": "", + "address": "", + "port": "", + "debug": "False", + "priority": + "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP", + "servicename": "Mandos", + } + + # Parse config file for server-global settings + server_config = ConfigParser.SafeConfigParser(server_defaults) + del server_defaults + server_config.read(os.path.join(options.configdir, "mandos.conf")) + # Convert the SafeConfigParser object to a dict + server_settings = server_config.defaults() + # Use getboolean on the boolean config option + server_settings["debug"] = server_config.getboolean\ + ("DEFAULT", "debug") + del server_config + + # Override the settings from the config file with command line + # options, if set. + for option in ("interface", "address", "port", "debug", + "priority", "servicename", "configdir"): + value = getattr(options, option) + if value is not None: + server_settings[option] = value + del options + # Now we have our good server settings in "server_settings" + + debug = server_settings["debug"] + + if not debug: + syslogger.setLevel(logging.WARNING) + console.setLevel(logging.WARNING) + + if server_settings["servicename"] != "Mandos": + syslogger.setFormatter(logging.Formatter\ + ('Mandos (%s): %%(levelname)s:' + ' %%(message)s' + % server_settings["servicename"])) + + # Parse config file with clients + client_defaults = { "timeout": "1h", + "interval": "5m", + "checker": "fping -q -- %(host)s", + "host": "", + } + client_config = ConfigParser.SafeConfigParser(client_defaults) + client_config.read(os.path.join(server_settings["configdir"], + "clients.conf")) + + clients = Set() + tcp_server = IPv6_TCPServer((server_settings["address"], + server_settings["port"]), tcp_handler, - options=options, - clients=clients, - credentials=cred) - - while True: - in_, out, err = select.select((udp_server, - tcp_server), (), ()) - for server in in_: - server.handle_request() - - -if __name__ == "__main__": + settings=server_settings, + clients=clients) + pidfilename = "/var/run/mandos.pid" + try: + pidfile = open(pidfilename, "w") + except IOError, error: + logger.error("Could not open file %r", pidfilename) + + uid = 65534 + gid = 65534 + try: + uid = pwd.getpwnam("mandos").pw_uid + except KeyError: + try: + uid = pwd.getpwnam("nobody").pw_uid + except KeyError: + pass + try: + gid = pwd.getpwnam("mandos").pw_gid + except KeyError: + try: + gid = pwd.getpwnam("nogroup").pw_gid + except KeyError: + pass + try: + os.setuid(uid) + os.setgid(gid) + except OSError, error: + if error[0] != errno.EPERM: + raise error + + global service + service = AvahiService(name = server_settings["servicename"], + type = "_mandos._tcp", ); + if server_settings["interface"]: + service.interface = if_nametoindex\ + (server_settings["interface"]) + + global main_loop + global bus + global server + # From the Avahi example code + DBusGMainLoop(set_as_default=True ) + main_loop = gobject.MainLoop() + bus = dbus.SystemBus() + server = dbus.Interface(bus.get_object(avahi.DBUS_NAME, + avahi.DBUS_PATH_SERVER), + avahi.DBUS_INTERFACE_SERVER) + # End of Avahi example code + + def remove_from_clients(client): + clients.remove(client) + if not clients: + logger.critical(u"No clients left, exiting") + sys.exit() + + clients.update(Set(Client(name = section, + stop_hook = remove_from_clients, + config + = dict(client_config.items(section))) + for section in client_config.sections())) + if not clients: + logger.critical(u"No clients defined") + sys.exit(1) + + if debug: + # Redirect stdin so all checkers get /dev/null + null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR) + os.dup2(null, sys.stdin.fileno()) + if null > 2: + os.close(null) + else: + # No console logging + logger.removeHandler(console) + # Close all input and output, do double fork, etc. + daemon() + + try: + pid = os.getpid() + pidfile.write(str(pid) + "\n") + pidfile.close() + del pidfile + except IOError, err: + logger.error(u"Could not write to file %r with PID %d", + pidfilename, pid) + except NameError: + # "pidfile" was never created + pass + del pidfilename + + def cleanup(): + "Cleanup function; run on exit" + global group + # From the Avahi example code + if not group is None: + group.Free() + group = None + # End of Avahi example code + + while clients: + client = clients.pop() + client.stop_hook = None + client.stop() + + atexit.register(cleanup) + + if not debug: + signal.signal(signal.SIGINT, signal.SIG_IGN) + signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit()) + signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit()) + + for client in clients: + client.start() + + tcp_server.enable() + tcp_server.server_activate() + + # Find out what port we got + service.port = tcp_server.socket.getsockname()[1] + logger.info(u"Now listening on address %r, port %d, flowinfo %d," + u" scope_id %d" % tcp_server.socket.getsockname()) + + #service.interface = tcp_server.socket.getsockname()[3] + + try: + # From the Avahi example code + server.connect_to_signal("StateChanged", server_state_changed) + try: + server_state_changed(server.GetState()) + except dbus.exceptions.DBusException, error: + logger.critical(u"DBusException: %s", error) + sys.exit(1) + # End of Avahi example code + + gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN, + lambda *args, **kwargs: + tcp_server.handle_request\ + (*args[2:], **kwargs) or True) + + logger.debug(u"Starting main loop") + main_loop_started = True + main_loop.run() + except AvahiError, error: + logger.critical(u"AvahiError: %s" + unicode(error)) + sys.exit(1) + except KeyboardInterrupt: + if debug: + print + +if __name__ == '__main__': main() - === added file 'mandos-clients.conf.xml' --- mandos-clients.conf.xml 1970-01-01 00:00:00 +0000 +++ mandos-clients.conf.xml 2008-09-04 13:36:59 +0000 @@ -0,0 +1,397 @@ + + + +/etc/mandos/clients.conf"> + +]> + + + + Mandos Manual + + Mandos + &VERSION; + &TIMESTAMP; + + + Björn + Påhlsson +
+ belorn@fukt.bsnet.se +
+
+ + Teddy + Hogeborn +
+ teddy@fukt.bsnet.se +
+
+
+ + 2008 + Teddy Hogeborn + Björn Påhlsson + + +
+ + + &CONFNAME; + 5 + + + + &CONFNAME; + + Configuration file for the Mandos server + + + + + &CONFPATH; + + + + DESCRIPTION + + The file &CONFPATH; is a configuration file for mandos + 8, read by it at startup. + The file needs to list all clients that should be able to use + the service. All clients listed will be regarded as valid, even + if a client was declared invalid in a previous run of the + server. + + + The format starts with a [section + header] which is either + [DEFAULT] or [client + name]. The client + name can be anything, and is not tied to a host + name. Following the section header is any number of + option=value entries, + with continuations in the style of RFC 822. option: value is also accepted. Note that + leading whitespace is removed from values. Values can contain + format strings which refer to other values in the same section, + or values in the DEFAULT section (see ). Lines beginning with # + or ; are ignored and may be used to provide + comments. + + + + + OPTIONS + + Note: all option values are subject to + start time expansion, see . + + + Unknown options are ignored. The used options are as follows: + + + + + + + + + This option is optional. + + + The timeout is how long the server will wait for a + successful checker run until a client is considered + invalid - that is, ineligible to get the data this server + holds. By default Mandos will use 1 hour. + + + The TIME is specified as a + space-separated number of values, each of which is a + number and a one-character suffix. The suffix must be one + of d, s, m, + h, and w for days, seconds, + minutes, hours, and weeks, respectively. The values are + added together to give the total time value, so all of + 330s, + 110s 110s 110s, and + 5m 30s will give a value + of five minutes and thirty seconds. + + + + + + + + + This option is optional. + + + How often to run the checker to confirm that a client is + still up. Note: a new checker will + not be started if an old one is still running. The server + will wait for a checker to complete until the above + timeout occurs, at which + time the client will be marked invalid, and any running + checker killed. The default interval is 5 minutes. + + + The format of TIME is the same + as for timeout above. + + + + + + + + + This option is optional. + + + This option allows you to override the default shell + command that the server will use to check if the client is + still up. Any output of the command will be ignored, only + the exit code is checked: If the exit code of the command + is zero, the client is considered up. The command will be + run using /bin/sh + , so + PATH will be searched. The default + value for the checker command is fping %(host)s. + + + In addition to normal start time expansion, this option + will also be subject to runtime expansion; see . + + + + + + + + + This option is required. + + + This option sets the OpenPGP fingerprint that identifies + the public key that clients authenticate themselves with + through TLS. The string needs to be in hexidecimal form, + but spaces or upper/lower case are not significant. + + + + + + + + + If this option is not specified, the option is required + to be present. + + + If present, this option must be set to a string of + base64-encoded binary data. It will be decoded and sent + to the client matching the above + . This should, of course, be + OpenPGP encrypted data, decryptable only by the client. + The program mandos-keygen8 can, using its + option, be used to generate + this, if desired. + + + Note: this value of this option will probably be very + long. A useful feature to avoid having unreadably-long + lines is that a line beginning with white space adds to + the value of the previous line, RFC 822-style. + + + + + + + + + This option is only used if is not + specified, in which case this option is + required. + + + Similar to the , except the secret + data is in an external file. The contents of the file + should not be base64-encoded, but + will be sent to clients verbatim. + + + + + + + + + This option is optional, but highly + recommended unless the + option is modified to a + non-standard value without %(host)s in it. + + + Host name for this client. This is not used by the server + directly, but can be, and is by default, used by the + checker. See the option. + + + + + + + + + EXPANSION + + There are two forms of expansion: Start time expansion and + runtime expansion. + + + START TIME EXPANSION + + Any string in an option value of the form + %(foo)s will be replaced by the value of the option + foo either in the same section, or, if it + does not exist there, the [DEFAULT] + section. This is done at start time, when the configuration + file is read. + + + Note that this means that, in order to include an actual + percent character (%) in an option value, two + percent characters in a row (%%) must be + entered. + + + + RUNTIME EXPANSION + + This is currently only done for the checker + option. + + + Any string in an option value of the form + %%(foo)s will be replaced by the value of the attribute + foo of the internal + Client object. See the + source code for details, and let the authors know of any + attributes that are useful so they may be preserved to any new + versions of this software. + + + Note that this means that, in order to include an actual + percent character (%) in a + checker option, four + percent characters in a row (%%%%) must be + entered. Also, a bad format here will lead to an immediate + but silent run-time fatal exit; debug + mode is needed to expose an error of this kind. + + + + + + + FILES + + The file described here is &CONFPATH; + + + + + BUGS + + The format for specifying times for timeout + and interval is not very good. + + + The difference between + %%(foo)s and + %(foo)s is + obscure. + + + + + EXAMPLE + + +[DEFAULT] +timeout = 1h +interval = 5m +checker = fping -q -- %(host)s + +# Client "foo" +[foo] +fingerprint = 7788 2722 5BA7 DE53 9C5A 7CFA 59CF F7CD BD9A 5920 +secret = + hQIOA6QdEjBs2L/HEAf/TCyrDe5Xnm9esa+Pb/vWF9CUqfn4srzVgSu234 + REJMVv7lBSrPE2132Lmd2gqF1HeLKDJRSVxJpt6xoWOChGHg+TMyXDxK+N + Xl89vGvdU1XfhKkVm9MDLOgT5ECDPysDGHFPDhqHOSu3Kaw2DWMV/iH9vz + 3Z20erVNbdcvyBnuojcoWO/6yfB5EQO0BXp7kcyy00USA3CjD5FGZdoQGI + Tb8A/ar0tVA5crSQmaSotm6KmNLhrFnZ5BxX+TiE+eTUTqSloWRY6VAvqW + QHC7OASxK5E6RXPBuFH5IohUA2Qbk5AHt99pYvsIPX88j2rWauOokoiKZo + t/9leJ8VxO5l3wf/U64IH8bkPIoWmWZfd/nqh4uwGNbCgKMyT+AnvH7kMJ + 3i7DivfWl2mKLV0PyPHUNva0VQxX6yYjcOhj1R6fCr/at8/NSLe2OhLchz + dC+Ls9h+kvJXgF8Sisv+Wk/1RadPLFmraRlqvJwt6Ww21LpiXqXHV2mIgq + WnR98YgSvUi3TJHrUQiNc9YyBzuRo0AjgG2C9qiE3FM+Y28+iQ/sR3+bFs + zYuZKVTObqiIslwXu7imO0cvvFRgJF/6u3HNFQ4LUTGhiM3FQmC6NNlF3/ + vJM2hwRDMcJqDd54Twx90Wh+tYz0z7QMsK4ANXWHHWHR0JchnLWmenzbtW + 5MHdW9AYsNJZAQSOpirE4Xi31CSlWAi9KV+cUCmWF5zOFy1x23P6PjdaRm + 4T2zw4dxS5NswXWU0sVEXxjs6PYxuIiCTL7vdpx8QjBkrPWDrAbcMyBr2O + QlnHIvPzEArRQLo= +host = foo.example.org +interval = 1m + +# Client "bar" +[bar] +fingerprint = 3e393aeaefb84c7e89e2f547b3a107558fca3a27 +secfile = /etc/mandos/bar-secret +timeout = 15m + + + + + + + SEE ALSO + + mandos-keygen + 8, + mandos.conf + 5, + mandos + 8 + + +
+ + + + + === added file 'mandos-keygen' --- mandos-keygen 1970-01-01 00:00:00 +0000 +++ mandos-keygen 2008-09-08 12:03:16 +0000 @@ -0,0 +1,304 @@ +#!/bin/sh -e +# +# Mandos key generator - create a new OpenPGP key for a Mandos client +# +# Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson +# +# 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 . +# +# Contact the authors at . +# + +VERSION="1.0" + +KEYDIR="/etc/keys/mandos" +KEYTYPE=DSA +KEYLENGTH=2048 +SUBKEYTYPE=ELG-E +SUBKEYLENGTH=2048 +KEYNAME="`hostname --fqdn`" +KEYEMAIL="" +KEYCOMMENT="Mandos client key" +KEYEXPIRE=0 +FORCE=no +KEYCOMMENT_ORIG="$KEYCOMMENT" +mode=keygen + +if [ ! -d "$KEYDIR" ]; then + KEYDIR="/etc/mandos/keys" +fi + +# Parse options +TEMP=`getopt --options vhd:t:l:n:e:c:x:f \ + --longoptions version,help,password,dir:,type:,length:,subtype:,sublength:,name:,email:,comment:,expire:,force \ + --name "$0" -- "$@"` + +help(){ +basename="`basename $0`" +cat <&2; exit 1;; + esac +done +if [ "$#" -gt 0 ]; then + echo "Unknown arguments: '$@'" >&2 + exit 1 +fi + +SECKEYFILE="$KEYDIR/seckey.txt" +PUBKEYFILE="$KEYDIR/pubkey.txt" + +# Check for some invalid values +if [ ! -d "$KEYDIR" ]; then + echo "$KEYDIR not a directory" >&2 + exit 1 +fi +if [ ! -r "$KEYDIR" ]; then + echo "Directory $KEYDIR not readable" >&2 + exit 1 +fi + +if [ "$mode" = keygen ]; then + if [ ! -w "$KEYDIR" ]; then + echo "Directory $KEYDIR not writeable" >&2 + exit 1 + fi + if [ -z "$KEYTYPE" ]; then + echo "Empty key type" >&2 + exit 1 + fi + + if [ -z "$KEYNAME" ]; then + echo "Empty key name" >&2 + exit 1 + fi + + if [ -z "$KEYLENGTH" ] || [ "$KEYLENGTH" -lt 512 ]; then + echo "Invalid key length" >&2 + exit 1 + fi + + if [ -z "$KEYEXPIRE" ]; then + echo "Empty key expiration" >&2 + exit 1 + fi + + # Make FORCE be 0 or 1 + case "$FORCE" in + [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) FORCE=1;; + [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) FORCE=0;; + esac + + if [ \( -e "$SECKEYFILE" -o -e "$PUBKEYFILE" \) \ + -a "$FORCE" -eq 0 ]; then + echo "Refusing to overwrite old key files; use --force" >&2 + exit 1 + fi + + # Set lines for GnuPG batch file + if [ -n "$KEYCOMMENT" ]; then + KEYCOMMENTLINE="Name-Comment: $KEYCOMMENT" + fi + if [ -n "$KEYEMAIL" ]; then + KEYEMAILLINE="Name-Email: $KEYEMAIL" + fi + + # Create temporary gpg batch file + BATCHFILE="`mktemp -t mandos-keygen-batch.XXXXXXXXXX`" +fi + +if [ "$mode" = password ]; then + # Create temporary encrypted password file + SECFILE="`mktemp -t mandos-keygen-secfile.XXXXXXXXXX`" +fi + +# Create temporary key ring directory +RINGDIR="`mktemp -d -t mandos-keygen-keyrings.XXXXXXXXXX`" + +# Remove temporary files on exit +trap " +set +e; \ +test -n \"$SECFILE\" && shred --remove \"$SECFILE\"; \ +shred --remove \"$RINGDIR\"/sec*; +test -n \"$BATCHFILE\" && rm --force \"$BATCHFILE\"; \ +rm --recursive --force \"$RINGDIR\"; +stty echo; \ +" EXIT + +umask 077 + +if [ "$mode" = keygen ]; then + # Create batch file for GnuPG + cat >"$BATCHFILE" <<-EOF + Key-Type: $KEYTYPE + Key-Length: $KEYLENGTH + #Key-Usage: encrypt,sign,auth + Subkey-Type: $SUBKEYTYPE + Subkey-Length: $SUBKEYLENGTH + #Subkey-Usage: encrypt,sign,auth + Name-Real: $KEYNAME + $KEYCOMMENTLINE + $KEYEMAILLINE + Expire-Date: $KEYEXPIRE + #Preferences: + #Handle: + #%pubring pubring.gpg + #%secring secring.gpg + %commit + EOF + + # Generate a new key in the key rings + gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ + --homedir "$RINGDIR" --trust-model always \ + --gen-key "$BATCHFILE" + rm --force "$BATCHFILE" + + # Backup any old key files + if cp --backup=numbered --force "$SECKEYFILE" "$SECKEYFILE" \ + 2>/dev/null; then + shred --remove "$SECKEYFILE" + fi + if cp --backup=numbered --force "$PUBKEYFILE" "$PUBKEYFILE" \ + 2>/dev/null; then + rm --force "$PUBKEYFILE" + fi + + FILECOMMENT="Mandos client key for $KEYNAME" + if [ "$KEYCOMMENT" != "$KEYCOMMENT_ORIG" ]; then + FILECOMMENT="$FILECOMMENT ($KEYCOMMENT)" + fi + + if [ -n "$KEYEMAIL" ]; then + FILECOMMENT="$FILECOMMENT <$KEYEMAIL>" + fi + + # Export keys from key rings to key files + gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ + --homedir "$RINGDIR" --armor --export-options export-minimal \ + --comment "$FILECOMMENT" --output "$SECKEYFILE" \ + --export-secret-keys + gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ + --homedir "$RINGDIR" --armor --export-options export-minimal \ + --comment "$FILECOMMENT" --output "$PUBKEYFILE" --export +fi + +if [ "$mode" = password ]; then + # Import keys into temporary key rings + gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ + --homedir "$RINGDIR" --trust-model always --armor \ + --import "$SECKEYFILE" + gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ + --homedir "$RINGDIR" --trust-model always --armor \ + --import "$PUBKEYFILE" + + # Get fingerprint of key + FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \ + --enable-dsa2 --homedir \"$RINGDIR\" --trust-model always \ + --fingerprint --with-colons \ + | sed --quiet \ + --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`" + + test -n "$FINGERPRINT" + + FILECOMMENT="Encrypted password for a Mandos client" + + stty -echo + echo -n "Enter passphrase: " >&2 + head --lines=1 | tr --delete '\n' \ + | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ + --homedir "$RINGDIR" --trust-model always --armor --encrypt \ + --recipient "$FINGERPRINT" --comment "$FILECOMMENT" \ + > "$SECFILE" + echo >&2 + stty echo + + cat <<-EOF + [$KEYNAME] + host = $KEYNAME + fingerprint = $FINGERPRINT + secret = +EOF + sed --quiet --expression=' + /^-----BEGIN PGP MESSAGE-----$/,/^-----END PGP MESSAGE-----$/{ + /^$/,${ + # Remove 24-bit Radix-64 checksum + s/=....$// + # Indent four spaces + /^[^-]/s/^/ /p + } + }' < "$SECFILE" +fi + +trap - EXIT + +set +e +# Remove the password file, if any +if [ -n "$SECFILE" ]; then + shred --remove "$SECFILE" +fi +# Remove the key rings +shred --remove "$RINGDIR"/sec* +rm --recursive --force "$RINGDIR" === added file 'mandos-keygen.xml' --- mandos-keygen.xml 1970-01-01 00:00:00 +0000 +++ mandos-keygen.xml 2008-09-06 16:31:49 +0000 @@ -0,0 +1,492 @@ + + + + +]> + + + + Mandos Manual + + Mandos + &VERSION; + &TIMESTAMP; + + + Björn + Påhlsson +
+ belorn@fukt.bsnet.se +
+
+ + Teddy + Hogeborn +
+ teddy@fukt.bsnet.se +
+
+
+ + 2008 + Teddy Hogeborn + Björn Påhlsson + + +
+ + + &COMMANDNAME; + 8 + + + + &COMMANDNAME; + + Generate key and password for Mandos client and server. + + + + + + &COMMANDNAME; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &COMMANDNAME; + + + + + + + + + + + + + + + + + &COMMANDNAME; + + + + + + + &COMMANDNAME; + + + + + + + + + DESCRIPTION + + &COMMANDNAME; is a program to generate the + OpenPGP key used by + mandos-client + 8mandos. The key is + normally written to /etc/mandos for later installation into the + initrd image, but this, and most other things, can be changed + with command line options. + + + This program can also be used with the + option to generate a ready-made + section for clients.conf (see + mandos-clients.conf + 5). + + + + + PURPOSE + + The purpose of this is to enable remote and unattended + rebooting of client host computer with an + encrypted root file system. See for details. + + + + + OPTIONS + + + + + + + + Show a help message and exit + + + + + + + + + + Target directory for key files. Default is + /etc/mandos. + + + + + + + + + + Key type. Default is DSA. + + + + + + + + + + Key length in bits. Default is 2048. + + + + + + + + + + Subkey type. Default is ELG-E (Elgamal + encryption-only). + + + + + + + + + + Subkey length in bits. Default is 2048. + + + + + + + + + + Email address of key. Default is empty. + + + + + + + + + + Comment field for key. The default value is + Mandos client key. + + + + + + + + + + Key expire time. Default is no expiration. See + gpg + 1 for syntax. + + + + + + + + + + Force overwriting old key. + + + + + + + + + Prompt for a password and encrypt it with the key already + present in either /etc/mandos or the + directory specified with the + option. Outputs, on standard output, a section suitable + for inclusion in mandos-clients.conf8. The host name or the name + specified with the option is used + for the section header. All other options are ignored, + and no key is created. + + + + + + + + OVERVIEW + + + This program is a small utility to generate new OpenPGP keys for + new Mandos clients, and to generate sections for inclusion in + clients.conf on the server. + + + + + EXIT STATUS + + The exit status will be 0 if a new key (or password, if the + option was used) was successfully + created, otherwise not. + + + + + ENVIRONMENT + + + TMPDIR + + + If set, temporary files will be created here. See + mktemp + 1. + + + + + + + + FILES + + Use the option to change where + &COMMANDNAME; will write the key files. The + default file names are shown here. + + + + /etc/mandos/seckey.txt + + + OpenPGP secret key file which will be created or + overwritten. + + + + + /etc/mandos/pubkey.txt + + + OpenPGP public key file which will be created or + overwritten. + + + + + /tmp + + + Temporary files will be written here if + TMPDIR is not set. + + + + + + + + + + + + + + EXAMPLE + + + Normal invocation needs no options: + + + &COMMANDNAME; + + + + + Create key in another directory and of another type. Force + overwriting old key files: + + + + +&COMMANDNAME; --dir ~/keydir --type RSA --force + + + + + + Prompt for a password, encrypt it with the key in + /etc/mandos and output a section suitable + for clients.conf. + + + &COMMANDNAME; --password + + + + + Prompt for a password, encrypt it with the key in the + client-key directory and output a section + suitable for clients.conf. + + + + +&COMMANDNAME; --password --dir client-key + + + + + + + SECURITY + + The , , + , and + options can be used to create keys of low security. If in + doubt, leave them to the default values. + + + The key expire time is not guaranteed to be + honored by mandos + 8. + + + + + SEE ALSO + + gpg + 1, + mandos-clients.conf + 5, + mandos + 8, + mandos-client + 8mandos + + + +
+ + + + + === added file 'mandos-options.xml' --- mandos-options.xml 1970-01-01 00:00:00 +0000 +++ mandos-options.xml 2008-09-06 16:31:49 +0000 @@ -0,0 +1,69 @@ + + + + + +
+ + + <para id="interface"> + If this is specified, the server will only announce the service + and listen to requests on the specified network interface. + Default is to use all available interfaces. <emphasis + >Note:</emphasis> a failure to bind to the specified + interface is not considered critical, and the server will not + exit, but instead continue normally. + </para> + + <para id="address"> + If this option is used, the server will only listen to the + specified IPv6 address. If a link-local address is specified, an + interface should be set, since a link-local address is only valid + on a single interface. By default, the server will listen to all + available addresses. If set, this must be an IPv6 address; an + IPv4 address can only be specified using IPv4-mapped IPv6 address + syntax: <quote><systemitem class="ipaddress" + >::FFFF:192.0.2.3</systemitem ></quote>. + </para> + + <para id="port"> + If this option is used, the server will bind to that port. By + default, the server will listen to an arbitrary port given by the + operating system. + </para> + + <para id="debug"> + If the server is run in debug mode, it will run in the foreground + and print a lot of debugging information. The default is to + <emphasis>not</emphasis> run in debug mode. + </para> + + <para id="priority"> + GnuTLS priority string for the <acronym>TLS</acronym> handshake. + The default is <quote><literal + >SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP</literal></quote>. See + <citerefentry><refentrytitle>gnutls_priority_init</refentrytitle> + <manvolnum>3</manvolnum></citerefentry> for the syntax. + <emphasis>Warning</emphasis>: changing this may make the + <acronym>TLS</acronym> handshake fail, making server-client + communication impossible. + </para> + + <para id="servicename"> + Zeroconf service name. The default is + <quote><literal>Mandos</literal></quote>. This only needs to be + changed this if it, for some reason, is necessary to run more than + one server on the same <emphasis>host</emphasis>, which would not + normally be useful. If there are name collisions on the same + <emphasis>network</emphasis>, the newer server will automatically + rename itself to <quote><literal>Mandos #2</literal></quote>, and + so on; therefore, this option is not needed in that case. + </para> + +</section> === added file 'mandos.conf' --- mandos.conf 1970-01-01 00:00:00 +0000 +++ mandos.conf 2008-08-18 23:55:28 +0000 @@ -0,0 +1,38 @@ +# This file must have exactly one section named "DEFAULT". +[DEFAULT] + +# These are the default values for the server, uncomment and change +# them if needed. + + +# If "interface" is set, the server will only listen to a specific +# network interface. +;interface = + + +# If "address" is set, the server will only listen to a specific +# address. This must currently be an IPv6 address; an IPv4 address +# can be specified using the "::FFFF:192.0.2.3" syntax. Also, if this +# is a link-local address, an interface should be set above. +;address = + + +# If "port" is set, the server to bind to that port. By default, the +# server will listen to an arbitrary port. +;port = + + +# If "debug" is true, the server will run in the foreground and print +# a lot of debugging information. +;debug = False + + +# GnuTLS priority for the TLS handshake. See gnutls_priority_init(3). +;priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP + + +# Zeroconf service name. You need to change this if you for some +# reason want to run more than one server on the same *host*. +# If there are name collisions on the same *network*, the server will +# rename itself to "Mandos #2", etc. +;servicename = Mandos === added file 'mandos.conf.xml' --- mandos.conf.xml 1970-01-01 00:00:00 +0000 +++ mandos.conf.xml 2008-09-04 13:36:59 +0000 @@ -0,0 +1,240 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" + "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ +<!ENTITY VERSION "1.0"> +<!ENTITY CONFNAME "mandos.conf"> +<!ENTITY CONFPATH "<filename>/etc/mandos/mandos.conf</filename>"> +<!ENTITY TIMESTAMP "2008-09-04"> +]> + +<refentry xmlns:xi="http://www.w3.org/2001/XInclude"> + <refentryinfo> + <title>Mandos Manual + + Mandos + &VERSION; + &TIMESTAMP; + + + Björn + Påhlsson +
+ belorn@fukt.bsnet.se +
+
+ + Teddy + Hogeborn +
+ teddy@fukt.bsnet.se +
+
+
+ + 2008 + Teddy Hogeborn + Björn Påhlsson + + + + + + &CONFNAME; + 5 + + + + &CONFNAME; + + Configuration file for the Mandos server + + + + + &CONFPATH; + + + + DESCRIPTION + + The file &CONFPATH; is a simple configuration file for + mandos + 8, and is read by it at + startup. The configuration file starts with [DEFAULT] on a line by itself, followed by + any number of option=value entries, + with continuations in the style of RFC 822. option: value is also accepted. Note that + leading whitespace is removed from values. Lines beginning with + # or ; are ignored and may be used + to provide comments. + + + + + OPTIONS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FILES + + The file described here is &CONFPATH; + + + + + BUGS + + The [DEFAULT] is necessary because the Python + built-in module ConfigParser + requires it. + + + + + EXAMPLE + + + No options are actually required: + + +[DEFAULT] + + + + + An example using all the options: + + +[DEFAULT] +# A configuration example +interface = eth0 +address = 2001:db8:f983:bd0b:30de:ae4a:71f2:f672 +port = 1025 +debug = true +priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP +servicename = Daena + + + + + + SEE ALSO + + gnutls_priority_init3, + mandos + 8, + mandos-clients.conf + 5 + + + + + + RFC 4291: IP Version 6 Addressing + Architecture + + + + + Section 2.2: Text Representation of + Addresses + + + + Section 2.5.5.2: IPv4-Mapped IPv6 + Address + + + + Section 2.5.6, Link-Local IPv6 Unicast + Addresses + + + The clients use IPv6 link-local addresses, which are + immediately usable since a link-local addresses is + automatically assigned to a network interfaces when it + is brought up. + + + + + + + + + Zeroconf + + + + Zeroconf is the network protocol standard used by clients + for finding the Mandos server on the local network. + + + + + + + + + + + === added file 'mandos.xml' --- mandos.xml 1970-01-01 00:00:00 +0000 +++ mandos.xml 2008-09-06 16:31:49 +0000 @@ -0,0 +1,659 @@ + + + + +]> + + + + Mandos Manual + + Mandos + &VERSION; + &TIMESTAMP; + + + Björn + Påhlsson +
+ belorn@fukt.bsnet.se +
+
+ + Teddy + Hogeborn +
+ teddy@fukt.bsnet.se +
+
+
+ + 2008 + Teddy Hogeborn + Björn Påhlsson + + +
+ + + &COMMANDNAME; + 8 + + + + &COMMANDNAME; + + Gives encrypted passwords to authenticated Mandos clients + + + + + + &COMMANDNAME; + + + + + + + + + + + + + + + + + + + + + + + + + &COMMANDNAME; + + + + + + + &COMMANDNAME; + + + + &COMMANDNAME; + + + + + + DESCRIPTION + + &COMMANDNAME; is a server daemon which + handles incoming request for passwords for a pre-defined list of + client host computers. The Mandos server uses Zeroconf to + announce itself on the local network, and uses TLS to + communicate securely with and to authenticate the clients. The + Mandos server uses IPv6 to allow Mandos clients to use IPv6 + link-local addresses, since the clients will probably not have + any other addresses configured (see ). + Any authenticated client is then given the stored pre-encrypted + password for that specific client. + + + + + PURPOSE + + The purpose of this is to enable remote and unattended + rebooting of client host computer with an + encrypted root file system. See for details. + + + + + OPTIONS + + + + + + + Show a help message and exit + + + + + + + NAME + + NAME + + + + + + + + + + + + + + + + + + + + + + + + + + Run the server’s self-tests. This includes any unit + tests, etc. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Directory to search for configuration files. Default is + /etc/mandos. See + mandos.conf + 5 and + mandos-clients.conf + 5. + + + + + + + + + Prints the program version and exit. + + + + + + + + OVERVIEW + + + This program is the server part. It is a normal server program + and will run in a normal system environment, not in an initial + RAM disk environment. + + + + + NETWORK PROTOCOL + + The Mandos server announces itself as a Zeroconf service of type + _mandos._tcp. The Mandos + client connects to the announced address and port, and sends a + line of text where the first whitespace-separated field is the + protocol version, which currently is + 1. The client and server then + start a TLS protocol handshake with a slight quirk: the Mandos + server program acts as a TLS client while the + connecting Mandos client acts as a TLS server. + The Mandos client must supply an OpenPGP certificate, and the + fingerprint of this certificate is used by the Mandos server to + look up (in a list read from clients.conf + at start time) which binary blob to give the client. No other + authentication or authorization is done by the server. + + + Mandos Protocol (Version 1) + + Mandos Client + Direction + Mandos Server + + + + Connect + -> + + + 1\r\n + -> + + + TLS handshake as TLS server + + <-> + TLS handshake as TLS client + + + + OpenPGP public key (part of TLS handshake) + -> + + + + <- + Binary blob (client will assume OpenPGP data) + + + + <- + Close + +
+
+ + + CHECKING + + The server will, by default, continually check that the clients + are still up. If a client has not been confirmed as being up + for some time, the client is assumed to be compromised and is no + longer eligible to receive the encrypted password. The timeout, + checker program, and interval between checks can be configured + both globally and per client; see + mandos-clients.conf + 5. + + + + + LOGGING + + The server will send log message with various severity levels to + /dev/log. With the + option, it will log even more messages, + and also show them on the console. + + + + + EXIT STATUS + + The server will exit with a non-zero exit status only when a + critical error is encountered. + + + + + ENVIRONMENT + + + PATH + + + To start the configured checker (see ), the server uses + /bin/sh, which in turn uses + PATH to search for matching commands if + an absolute path is not given. See + sh1 + . + + + + + + + + FILES + + Use the option to change where + &COMMANDNAME; looks for its configurations + files. The default file names are listed here. + + + + /etc/mandos/mandos.conf + + + Server-global settings. See + mandos.conf + 5 for details. + + + + + /etc/mandos/clients.conf + + + List of clients and client-specific settings. See + mandos-clients.conf + 5 for details. + + + + + /var/run/mandos.pid + + + The file containing the process id of + &COMMANDNAME;. + + + + + /dev/log + + + The Unix domain socket to where local syslog messages are + sent. + + + + + /bin/sh + + + This is used to start the configured checker command for + each client. See + mandos-clients.conf + 5 for details. + + + + + + + + BUGS + + This server might, on especially fatal errors, emit a Python + backtrace. This could be considered a feature. + + + Currently, if a client is declared invalid due to + having timed out, the server does not record this fact onto + permanent storage. This has some security implications, see + . + + + There is currently no way of querying the server of the current + status of clients, other than analyzing its syslog output. + + + There is no fine-grained control over logging and debug output. + + + Debug mode is conflated with running in the foreground. + + + The console log messages does not show a time stamp. + + + This server does not check the expire time of clients’ OpenPGP + keys. + + + + + EXAMPLE + + + Normal invocation needs no options: + + + &COMMANDNAME; + + + + + Run the server in debug mode, read configuration files from + the ~/mandos directory, and use the + Zeroconf service name Test to not collide with + any other official Mandos server on this host: + + + + +&COMMANDNAME; --debug --configdir ~/mandos --servicename Test + + + + + + Run the server normally, but only listen to one interface and + only on the link-local address on that interface: + + + + +&COMMANDNAME; --interface eth7 --address fe80::aede:48ff:fe71:f6f2 + + + + + + + SECURITY + + SERVER + + Running this &COMMANDNAME; server program + should not in itself present any security risk to the host + computer running it. The program switches to a non-root user + soon after startup. + + + + CLIENTS + + The server only gives out its stored data to clients which + does have the OpenPGP key of the stored fingerprint. This is + guaranteed by the fact that the client sends its OpenPGP + public key in the TLS handshake; this ensures it to be + genuine. The server computes the fingerprint of the key + itself and looks up the fingerprint in its list of + clients. The clients.conf file (see + mandos-clients.conf + 5) + must be made non-readable by anyone + except the user running the server. + + + As detailed in , the status of all + client computers will continually be checked and be assumed + compromised if they are gone for too long. + + + If a client is compromised, its downtime should be duly noted + by the server which would therefore declare the client + invalid. But if the server was ever restarted, it would + re-read its client list from its configuration file and again + regard all clients therein as valid, and hence eligible to + receive their passwords. Therefore, be careful when + restarting servers if it is suspected that a client has, in + fact, been compromised by parties who may now be running a + fake Mandos client with the keys from the non-encrypted + initial RAM image of the client host. What + should be done in that case (if restarting the server program + really is necessary) is to stop the server program, edit the + configuration file to omit any suspect clients, and restart + the server program. + + + For more details on client-side security, see + mandos-client + 8mandos. + + + + + + SEE ALSO + + + mandos-clients.conf + 5, + mandos.conf + 5, + mandos-client + 8mandos, + sh1 + + + + + + Zeroconf + + + + Zeroconf is the network protocol standard used by clients + for finding this Mandos server on the local network. + + + + + + Avahi + + + + Avahi is the library this server calls to implement + Zeroconf service announcements. + + + + + + GnuTLS + + + + GnuTLS is the library this server uses to implement TLS for + communicating securely with the client, and at the same time + confidently get the client’s public OpenPGP key. + + + + + + RFC 4291: IP Version 6 Addressing + Architecture + + + + + Section 2.2: Text Representation of + Addresses + + + + Section 2.5.5.2: IPv4-Mapped IPv6 + Address + + + + Section 2.5.6, Link-Local IPv6 Unicast + Addresses + + + The clients use IPv6 link-local addresses, which are + immediately usable since a link-local addresses is + automatically assigned to a network interfaces when it + is brought up. + + + + + + + + + RFC 4346: The Transport Layer Security (TLS) + Protocol Version 1.1 + + + + TLS 1.1 is the protocol implemented by GnuTLS. + + + + + + RFC 4880: OpenPGP Message Format + + + + The data sent to clients is binary encrypted OpenPGP data. + + + + + + RFC 5081: Using OpenPGP Keys for Transport Layer + Security + + + + This is implemented by GnuTLS and used by this server so + that OpenPGP keys can be used. + + + + + +
+ + + + + === added file 'overview.xml' --- overview.xml 1970-01-01 00:00:00 +0000 +++ overview.xml 2008-09-01 08:29:23 +0000 @@ -0,0 +1,15 @@ + + + + This is part of the Mandos system for allowing computers to have + encrypted root file systems and at the same time be capable of + remote and/or unattended reboots. The computers run a small client + program in the initial RAM disk environment which + will communicate with a server over a network. The clients are + identified by the server using a OpenPGP key; each client has one + unique to it. The server sends the clients an encrypted password. + The encrypted password is decrypted by the clients using the same + OpenPGP key, and the password is then used to unlock the root file + system, whereupon the computers can continue booting normally. + === added file 'plugin-runner.c' --- plugin-runner.c 1970-01-01 00:00:00 +0000 +++ plugin-runner.c 2008-09-07 15:42:11 +0000 @@ -0,0 +1,1105 @@ +/* -*- coding: utf-8 -*- */ +/* + * Mandos plugin runner - Run Mandos plugins + * + * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson + * + * 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 + * . + * + * Contact the authors at . + */ + +#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), getline(), + asprintf() */ +#include /* size_t, NULL */ +#include /* malloc(), exit(), EXIT_FAILURE, + EXIT_SUCCESS, realloc() */ +#include /* bool, true, false */ +#include /* perror, popen(), fileno(), + fprintf(), stderr, STDOUT_FILENO */ +#include /* DIR, opendir(), stat(), struct + stat, waitpid(), WIFEXITED(), + WEXITSTATUS(), wait(), pid_t, + uid_t, gid_t, getuid(), getgid(), + dirfd() */ +#include /* fd_set, select(), FD_ZERO(), + FD_SET(), FD_ISSET(), FD_CLR */ +#include /* wait(), waitpid(), WIFEXITED(), + WEXITSTATUS() */ +#include /* struct stat, stat(), S_ISREG() */ +#include /* and, or, not */ +#include /* DIR, struct dirent, opendir(), + readdir(), closedir(), dirfd() */ +#include /* struct stat, stat(), S_ISREG(), + fcntl(), setuid(), setgid(), + F_GETFD, F_SETFD, FD_CLOEXEC, + access(), pipe(), fork(), close() + dup2, STDOUT_FILENO, _exit(), + execv(), write(), read(), + close() */ +#include /* fcntl(), F_GETFD, F_SETFD, + FD_CLOEXEC */ +#include /* strsep, strlen(), asprintf() */ +#include /* errno */ +#include /* struct argp_option, struct + argp_state, struct argp, + argp_parse(), ARGP_ERR_UNKNOWN, + ARGP_KEY_END, ARGP_KEY_ARG, + error_t */ +#include /* struct sigaction, sigemptyset(), + sigaddset(), sigaction(), + sigprocmask(), SIG_BLOCK, SIGCHLD, + SIG_UNBLOCK, kill() */ +#include /* errno, EBADF */ + +#define BUFFER_SIZE 256 + +#define PDIR "/lib/mandos/plugins.d" +#define AFILE "/conf/conf.d/mandos/plugin-runner.conf" + +const char *argp_program_version = "plugin-runner 1.0"; +const char *argp_program_bug_address = ""; + +struct plugin; + +typedef struct plugin{ + char *name; /* can be NULL or any plugin name */ + char **argv; + int argc; + char **environ; + int envc; + bool disabled; + + /* Variables used for running processes*/ + pid_t pid; + int fd; + char *buffer; + size_t buffer_size; + size_t buffer_length; + bool eof; + volatile bool completed; + volatile int status; + struct plugin *next; +} plugin; + +static plugin *plugin_list = NULL; + +/* Gets an existing plugin based on name, + or if none is found, creates a new one */ +static plugin *getplugin(char *name){ + /* Check for exiting plugin with that name */ + for (plugin *p = plugin_list; p != NULL; p = p->next){ + if ((p->name == name) + or (p->name and name and (strcmp(p->name, name) == 0))){ + return p; + } + } + /* Create a new plugin */ + plugin *new_plugin = malloc(sizeof(plugin)); + if (new_plugin == NULL){ + return NULL; + } + char *copy_name = NULL; + if(name != NULL){ + copy_name = strdup(name); + if(copy_name == NULL){ + return NULL; + } + } + + *new_plugin = (plugin) { .name = copy_name, + .argc = 1, + .disabled = false, + .next = plugin_list }; + + new_plugin->argv = malloc(sizeof(char *) * 2); + if (new_plugin->argv == NULL){ + free(copy_name); + free(new_plugin); + return NULL; + } + new_plugin->argv[0] = copy_name; + new_plugin->argv[1] = NULL; + + new_plugin->environ = malloc(sizeof(char *)); + if(new_plugin->environ == NULL){ + free(copy_name); + free(new_plugin->argv); + free(new_plugin); + return NULL; + } + new_plugin->environ[0] = NULL; + + /* Append the new plugin to the list */ + plugin_list = new_plugin; + return new_plugin; +} + +/* Helper function for add_argument and add_environment */ +static bool add_to_char_array(const char *new, char ***array, + int *len){ + /* Resize the pointed-to array to hold one more pointer */ + *array = realloc(*array, sizeof(char *) + * (size_t) ((*len) + 2)); + /* Malloc check */ + if(*array == NULL){ + return false; + } + /* Make a copy of the new string */ + char *copy = strdup(new); + if(copy == NULL){ + return false; + } + /* Insert the copy */ + (*array)[*len] = copy; + (*len)++; + /* Add a new terminating NULL pointer to the last element */ + (*array)[*len] = NULL; + return true; +} + +/* Add to a plugin's argument vector */ +static bool add_argument(plugin *p, const char *arg){ + if(p == NULL){ + return false; + } + return add_to_char_array(arg, &(p->argv), &(p->argc)); +} + +/* Add to a plugin's environment */ +static bool add_environment(plugin *p, const char *def, bool replace){ + if(p == NULL){ + return false; + } + /* namelen = length of name of environment variable */ + size_t namelen = (size_t)(strchrnul(def, '=') - def); + /* Search for this environment variable */ + for(char **e = p->environ; *e != NULL; e++){ + if(strncmp(*e, def, namelen + 1) == 0){ + /* It already exists */ + if(replace){ + char *new = realloc(*e, strlen(def) + 1); + if(new == NULL){ + return false; + } + *e = new; + strcpy(*e, def); + } + return true; + } + } + return add_to_char_array(def, &(p->environ), &(p->envc)); +} + +/* + * Based on the example in the GNU LibC manual chapter 13.13 "File + * Descriptor Flags". + * *Note File Descriptor Flags:(libc)Descriptor Flags. + */ +static int set_cloexec_flag(int fd) +{ + int ret = fcntl(fd, F_GETFD, 0); + /* If reading the flags failed, return error indication now. */ + if(ret < 0){ + return ret; + } + /* Store modified flag word in the descriptor. */ + return fcntl(fd, F_SETFD, ret | FD_CLOEXEC); +} + + +/* Mark processes as completed when they exit, and save their exit + status. */ +void handle_sigchld(__attribute__((unused)) int sig){ + while(true){ + plugin *proc = plugin_list; + int status; + pid_t pid = waitpid(-1, &status, WNOHANG); + if(pid == 0){ + /* Only still running child processes */ + break; + } + if(pid == -1){ + if (errno != ECHILD){ + perror("waitpid"); + } + /* No child processes */ + break; + } + + /* A child exited, find it in process_list */ + while(proc != NULL and proc->pid != pid){ + proc = proc->next; + } + if(proc == NULL){ + /* Process not found in process list */ + continue; + } + proc->status = status; + proc->completed = true; + } +} + +/* Prints out a password to stdout */ +bool print_out_password(const char *buffer, size_t length){ + ssize_t ret; + for(size_t written = 0; written < length; written += (size_t)ret){ + ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written, + length - written)); + if(ret < 0){ + return false; + } + } + return true; +} + +/* Removes and free a plugin from the plugin list */ +static void free_plugin(plugin *plugin_node){ + + for(char **arg = plugin_node->argv; *arg != NULL; arg++){ + free(*arg); + } + free(plugin_node->argv); + for(char **env = plugin_node->environ; *env != NULL; env++){ + free(*env); + } + free(plugin_node->environ); + free(plugin_node->buffer); + + /* Removes the plugin from the singly-linked list */ + if(plugin_node == plugin_list){ + /* First one - simple */ + plugin_list = plugin_list->next; + } else { + /* Second one or later */ + for(plugin *p = plugin_list; p != NULL; p = p->next){ + if(p->next == plugin_node){ + p->next = plugin_node->next; + break; + } + } + } + + free(plugin_node); +} + +static void free_plugin_list(void){ + while(plugin_list != NULL){ + free_plugin(plugin_list); + } +} + +int main(int argc, char *argv[]){ + char *plugindir = NULL; + char *argfile = NULL; + FILE *conffp; + size_t d_name_len; + DIR *dir = NULL; + struct dirent *dirst; + struct stat st; + fd_set rfds_all; + int ret, maxfd = 0; + uid_t uid = 65534; + gid_t gid = 65534; + bool debug = false; + int exitstatus = EXIT_SUCCESS; + struct sigaction old_sigchld_action; + struct sigaction sigchld_action = { .sa_handler = handle_sigchld, + .sa_flags = SA_NOCLDSTOP }; + char **custom_argv = NULL; + int custom_argc = 0; + + /* Establish a signal handler */ + sigemptyset(&sigchld_action.sa_mask); + ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD); + if(ret == -1){ + perror("sigaddset"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action); + if(ret == -1){ + perror("sigaction"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + + /* The options we understand. */ + struct argp_option options[] = { + { .name = "global-options", .key = 'g', + .arg = "OPTION[,OPTION[,...]]", + .doc = "Options passed to all plugins" }, + { .name = "global-env", .key = 'G', + .arg = "VAR=value", + .doc = "Environment variable passed to all plugins" }, + { .name = "options-for", .key = 'o', + .arg = "PLUGIN:OPTION[,OPTION[,...]]", + .doc = "Options passed only to specified plugin" }, + { .name = "env-for", .key = 'E', + .arg = "PLUGIN:ENV=value", + .doc = "Environment variable passed to specified plugin" }, + { .name = "disable", .key = 'd', + .arg = "PLUGIN", + .doc = "Disable a specific plugin", .group = 1 }, + { .name = "enable", .key = 'e', + .arg = "PLUGIN", + .doc = "Enable a specific plugin", .group = 1 }, + { .name = "plugin-dir", .key = 128, + .arg = "DIRECTORY", + .doc = "Specify a different plugin directory", .group = 2 }, + { .name = "config-file", .key = 129, + .arg = "FILE", + .doc = "Specify a different configuration file", .group = 2 }, + { .name = "userid", .key = 130, + .arg = "ID", .flags = 0, + .doc = "User ID the plugins will run as", .group = 3 }, + { .name = "groupid", .key = 131, + .arg = "ID", .flags = 0, + .doc = "Group ID the plugins will run as", .group = 3 }, + { .name = "debug", .key = 132, + .doc = "Debug mode", .group = 4 }, + { .name = NULL } + }; + + error_t parse_opt (int key, char *arg, __attribute__((unused)) + struct argp_state *state) { + switch (key) { + case 'g': /* --global-options */ + if (arg != NULL){ + char *p; + while((p = strsep(&arg, ",")) != NULL){ + if(p[0] == '\0'){ + continue; + } + if(not add_argument(getplugin(NULL), p)){ + perror("add_argument"); + return ARGP_ERR_UNKNOWN; + } + } + } + break; + case 'G': /* --global-env */ + if(arg == NULL){ + break; + } + if(not add_environment(getplugin(NULL), arg, true)){ + perror("add_environment"); + } + break; + case 'o': /* --options-for */ + if (arg != NULL){ + char *p_name = strsep(&arg, ":"); + if(p_name[0] == '\0' or arg == NULL){ + break; + } + char *opt = strsep(&arg, ":"); + if(opt[0] == '\0' or opt == NULL){ + break; + } + char *p; + while((p = strsep(&opt, ",")) != NULL){ + if(p[0] == '\0'){ + continue; + } + if(not add_argument(getplugin(p_name), p)){ + perror("add_argument"); + return ARGP_ERR_UNKNOWN; + } + } + } + break; + case 'E': /* --env-for */ + if(arg == NULL){ + break; + } + { + char *envdef = strchr(arg, ':'); + if(envdef == NULL){ + break; + } + *envdef = '\0'; + if(not add_environment(getplugin(arg), envdef+1, true)){ + perror("add_environment"); + } + } + break; + case 'd': /* --disable */ + if (arg != NULL){ + plugin *p = getplugin(arg); + if(p == NULL){ + return ARGP_ERR_UNKNOWN; + } + p->disabled = true; + } + break; + case 'e': /* --enable */ + if (arg != NULL){ + plugin *p = getplugin(arg); + if(p == NULL){ + return ARGP_ERR_UNKNOWN; + } + p->disabled = false; + } + break; + case 128: /* --plugin-dir */ + free(plugindir); + plugindir = strdup(arg); + if(plugindir == NULL){ + perror("strdup"); + } + break; + case 129: /* --config-file */ + /* This is already done by parse_opt_config_file() */ + break; + case 130: /* --userid */ + uid = (uid_t)strtol(arg, NULL, 10); + break; + case 131: /* --groupid */ + gid = (gid_t)strtol(arg, NULL, 10); + break; + case 132: /* --debug */ + debug = true; + break; + case ARGP_KEY_ARG: + /* Cryptsetup always passes an argument, which is an empty + string if "none" was specified in /etc/crypttab. So if + argument was empty, we ignore it silently. */ + if(arg[0] != '\0'){ + fprintf(stderr, "Ignoring unknown argument \"%s\"\n", arg); + } + break; + case ARGP_KEY_END: + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; + } + + /* This option parser is the same as parse_opt() above, except it + ignores everything but the --config-file option. */ + error_t parse_opt_config_file (int key, char *arg, + __attribute__((unused)) + struct argp_state *state) { + switch (key) { + case 'g': /* --global-options */ + case 'G': /* --global-env */ + case 'o': /* --options-for */ + case 'E': /* --env-for */ + case 'd': /* --disable */ + case 'e': /* --enable */ + case 128: /* --plugin-dir */ + break; + case 129: /* --config-file */ + free(argfile); + argfile = strdup(arg); + if(argfile == NULL){ + perror("strdup"); + } + break; + case 130: /* --userid */ + case 131: /* --groupid */ + case 132: /* --debug */ + case ARGP_KEY_ARG: + case ARGP_KEY_END: + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; + } + + struct argp argp = { .options = options, + .parser = parse_opt_config_file, + .args_doc = "", + .doc = "Mandos plugin runner -- Run plugins" }; + + /* Parse using the parse_opt_config_file in order to get the custom + config file location, if any. */ + ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL); + if (ret == ARGP_ERR_UNKNOWN){ + fprintf(stderr, "Unknown error while parsing arguments\n"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + + /* Reset to the normal argument parser */ + argp.parser = parse_opt; + + /* Open the configfile if available */ + if (argfile == NULL){ + conffp = fopen(AFILE, "r"); + } else { + conffp = fopen(argfile, "r"); + } + if(conffp != NULL){ + char *org_line = NULL; + char *p, *arg, *new_arg, *line; + size_t size = 0; + ssize_t sret; + const char whitespace_delims[] = " \r\t\f\v\n"; + const char comment_delim[] = "#"; + + custom_argc = 1; + custom_argv = malloc(sizeof(char*) * 2); + if(custom_argv == NULL){ + perror("malloc"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + custom_argv[0] = argv[0]; + custom_argv[1] = NULL; + + /* for each line in the config file, strip whitespace and ignore + commented text */ + while(true){ + sret = getline(&org_line, &size, conffp); + if(sret == -1){ + break; + } + + line = org_line; + arg = strsep(&line, comment_delim); + while((p = strsep(&arg, whitespace_delims)) != NULL){ + if(p[0] == '\0'){ + continue; + } + new_arg = strdup(p); + if(new_arg == NULL){ + perror("strdup"); + exitstatus = EXIT_FAILURE; + free(org_line); + goto fallback; + } + + custom_argc += 1; + custom_argv = realloc(custom_argv, sizeof(char *) + * ((unsigned int) custom_argc + 1)); + if(custom_argv == NULL){ + perror("realloc"); + exitstatus = EXIT_FAILURE; + free(org_line); + goto fallback; + } + custom_argv[custom_argc-1] = new_arg; + custom_argv[custom_argc] = NULL; + } + } + free(org_line); + } else { + /* Check for harmful errors and go to fallback. Other errors might + not affect opening plugins */ + if (errno == EMFILE or errno == ENFILE or errno == ENOMEM){ + perror("fopen"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + } + /* If there was any arguments from configuration file, + pass them to parser as command arguments */ + if(custom_argv != NULL){ + ret = argp_parse (&argp, custom_argc, custom_argv, ARGP_IN_ORDER, + 0, NULL); + if (ret == ARGP_ERR_UNKNOWN){ + fprintf(stderr, "Unknown error while parsing arguments\n"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + } + + /* Parse actual command line arguments, to let them override the + config file */ + ret = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, NULL); + if (ret == ARGP_ERR_UNKNOWN){ + fprintf(stderr, "Unknown error while parsing arguments\n"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + + if(debug){ + for(plugin *p = plugin_list; p != NULL; p=p->next){ + fprintf(stderr, "Plugin: %s has %d arguments\n", + p->name ? p->name : "Global", p->argc - 1); + for(char **a = p->argv; *a != NULL; a++){ + fprintf(stderr, "\tArg: %s\n", *a); + } + fprintf(stderr, "...and %u environment variables\n", p->envc); + for(char **a = p->environ; *a != NULL; a++){ + fprintf(stderr, "\t%s\n", *a); + } + } + } + + /* Strip permissions down to nobody */ + ret = setuid(uid); + if (ret == -1){ + perror("setuid"); + } + setgid(gid); + if (ret == -1){ + perror("setgid"); + } + + if (plugindir == NULL){ + dir = opendir(PDIR); + } else { + dir = opendir(plugindir); + } + + if(dir == NULL){ + perror("Could not open plugin dir"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + + /* Set the FD_CLOEXEC flag on the directory, if possible */ + { + int dir_fd = dirfd(dir); + if(dir_fd >= 0){ + ret = set_cloexec_flag(dir_fd); + if(ret < 0){ + perror("set_cloexec_flag"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + } + } + + FD_ZERO(&rfds_all); + + /* Read and execute any executable in the plugin directory*/ + while(true){ + dirst = readdir(dir); + + /* All directory entries have been processed */ + if(dirst == NULL){ + if (errno == EBADF){ + perror("readdir"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + break; + } + + d_name_len = strlen(dirst->d_name); + + /* Ignore dotfiles, backup files and other junk */ + { + bool bad_name = false; + + const char const *bad_prefixes[] = { ".", "#", NULL }; + + const char const *bad_suffixes[] = { "~", "#", ".dpkg-new", + ".dpkg-old", + ".dpkg-divert", NULL }; + for(const char **pre = bad_prefixes; *pre != NULL; pre++){ + size_t pre_len = strlen(*pre); + if((d_name_len >= pre_len) + and strncmp((dirst->d_name), *pre, pre_len) == 0){ + if(debug){ + fprintf(stderr, "Ignoring plugin dir entry \"%s\"" + " with bad prefix %s\n", dirst->d_name, *pre); + } + bad_name = true; + break; + } + } + if(bad_name){ + continue; + } + for(const char **suf = bad_suffixes; *suf != NULL; suf++){ + size_t suf_len = strlen(*suf); + if((d_name_len >= suf_len) + and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf) + == 0)){ + if(debug){ + fprintf(stderr, "Ignoring plugin dir entry \"%s\"" + " with bad suffix %s\n", dirst->d_name, *suf); + } + bad_name = true; + break; + } + } + + if(bad_name){ + continue; + } + } + + char *filename; + if(plugindir == NULL){ + ret = asprintf(&filename, PDIR "/%s", dirst->d_name); + } else { + ret = asprintf(&filename, "%s/%s", plugindir, dirst->d_name); + } + if(ret < 0){ + perror("asprintf"); + continue; + } + + ret = stat(filename, &st); + if (ret == -1){ + perror("stat"); + free(filename); + continue; + } + + /* Ignore non-executable files */ + if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){ + if(debug){ + fprintf(stderr, "Ignoring plugin dir entry \"%s\"" + " with bad type or mode\n", filename); + } + free(filename); + continue; + } + + plugin *p = getplugin(dirst->d_name); + if(p == NULL){ + perror("getplugin"); + free(filename); + continue; + } + if(p->disabled){ + if(debug){ + fprintf(stderr, "Ignoring disabled plugin \"%s\"\n", + dirst->d_name); + } + free(filename); + continue; + } + { + /* Add global arguments to argument list for this plugin */ + plugin *g = getplugin(NULL); + if(g != NULL){ + for(char **a = g->argv + 1; *a != NULL; a++){ + if(not add_argument(p, *a)){ + perror("add_argument"); + } + } + /* Add global environment variables */ + for(char **e = g->environ; *e != NULL; e++){ + if(not add_environment(p, *e, false)){ + perror("add_environment"); + } + } + } + } + /* If this plugin has any environment variables, we will call + using execve and need to duplicate the environment from this + process, too. */ + if(p->environ[0] != NULL){ + for(char **e = environ; *e != NULL; e++){ + if(not add_environment(p, *e, false)){ + perror("add_environment"); + } + } + } + + int pipefd[2]; + ret = pipe(pipefd); + if (ret == -1){ + perror("pipe"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + /* Ask OS to automatic close the pipe on exec */ + ret = set_cloexec_flag(pipefd[0]); + if(ret < 0){ + perror("set_cloexec_flag"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + ret = set_cloexec_flag(pipefd[1]); + if(ret < 0){ + perror("set_cloexec_flag"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + /* Block SIGCHLD until process is safely in process list */ + ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL); + if(ret < 0){ + perror("sigprocmask"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + /* Starting a new process to be watched */ + pid_t pid = fork(); + if(pid == -1){ + perror("fork"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + if(pid == 0){ + /* this is the child process */ + ret = sigaction(SIGCHLD, &old_sigchld_action, NULL); + if(ret < 0){ + perror("sigaction"); + _exit(EXIT_FAILURE); + } + ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); + if(ret < 0){ + perror("sigprocmask"); + _exit(EXIT_FAILURE); + } + + ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */ + if(ret == -1){ + perror("dup2"); + _exit(EXIT_FAILURE); + } + + if(dirfd(dir) < 0){ + /* If dir has no file descriptor, we could not set FD_CLOEXEC + above and must now close it manually here. */ + closedir(dir); + } + if(p->environ[0] == NULL){ + if(execv(filename, p->argv) < 0){ + perror("execv"); + _exit(EXIT_FAILURE); + } + } else { + if(execve(filename, p->argv, p->environ) < 0){ + perror("execve"); + _exit(EXIT_FAILURE); + } + } + /* no return */ + } + /* Parent process */ + close(pipefd[1]); /* Close unused write end of pipe */ + free(filename); + plugin *new_plugin = getplugin(dirst->d_name); + if (new_plugin == NULL){ + perror("getplugin"); + ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); + if(ret < 0){ + perror("sigprocmask"); + } + exitstatus = EXIT_FAILURE; + goto fallback; + } + + new_plugin->pid = pid; + new_plugin->fd = pipefd[0]; + + /* Unblock SIGCHLD so signal handler can be run if this process + has already completed */ + ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); + if(ret < 0){ + perror("sigprocmask"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + + FD_SET(new_plugin->fd, &rfds_all); + + if (maxfd < new_plugin->fd){ + maxfd = new_plugin->fd; + } + + } + + closedir(dir); + dir = NULL; + + for(plugin *p = plugin_list; p != NULL; p = p->next){ + if(p->pid != 0){ + break; + } + if(p->next == NULL){ + fprintf(stderr, "No plugin processes started. Incorrect plugin" + " directory?\n"); + free_plugin_list(); + } + } + + /* Main loop while running plugins exist */ + while(plugin_list){ + fd_set rfds = rfds_all; + int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL); + if (select_ret == -1){ + perror("select"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + /* OK, now either a process completed, or something can be read + from one of them */ + for(plugin *proc = plugin_list; proc != NULL;){ + /* Is this process completely done? */ + if(proc->eof and proc->completed){ + /* Only accept the plugin output if it exited cleanly */ + if(not WIFEXITED(proc->status) + or WEXITSTATUS(proc->status) != 0){ + /* Bad exit by plugin */ + + if(debug){ + if(WIFEXITED(proc->status)){ + fprintf(stderr, "Plugin %u exited with status %d\n", + (unsigned int) (proc->pid), + WEXITSTATUS(proc->status)); + } else if(WIFSIGNALED(proc->status)) { + fprintf(stderr, "Plugin %u killed by signal %d\n", + (unsigned int) (proc->pid), + WTERMSIG(proc->status)); + } else if(WCOREDUMP(proc->status)){ + fprintf(stderr, "Plugin %d dumped core\n", + (unsigned int) (proc->pid)); + } + } + + /* Remove the plugin */ + FD_CLR(proc->fd, &rfds_all); + + /* Block signal while modifying process_list */ + ret = sigprocmask(SIG_BLOCK, &sigchld_action.sa_mask, NULL); + if(ret < 0){ + perror("sigprocmask"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + + /* We are done modifying process list, so unblock signal */ + ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, + NULL); + if(ret < 0){ + perror("sigprocmask"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + + if(plugin_list == NULL){ + break; + } + + plugin *next_plugin = proc->next; + free_plugin(proc); + proc = next_plugin; + continue; + } + + /* This process exited nicely, so print its buffer */ + + bool bret = print_out_password(proc->buffer, + proc->buffer_length); + if(not bret){ + perror("print_out_password"); + exitstatus = EXIT_FAILURE; + } + goto fallback; + } + + /* This process has not completed. Does it have any output? */ + if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ + /* This process had nothing to say at this time */ + proc = proc->next; + continue; + } + /* Before reading, make the process' data buffer large enough */ + if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){ + proc->buffer = realloc(proc->buffer, proc->buffer_size + + (size_t) BUFFER_SIZE); + if (proc->buffer == NULL){ + perror("malloc"); + exitstatus = EXIT_FAILURE; + goto fallback; + } + proc->buffer_size += BUFFER_SIZE; + } + /* Read from the process */ + ret = read(proc->fd, proc->buffer + proc->buffer_length, + BUFFER_SIZE); + if(ret < 0){ + /* Read error from this process; ignore the error */ + proc = proc->next; + continue; + } + if(ret == 0){ + /* got EOF */ + proc->eof = true; + } else { + proc->buffer_length += (size_t) ret; + } + } + } + + + fallback: + + if(plugin_list == NULL or exitstatus != EXIT_SUCCESS){ + /* Fallback if all plugins failed, none are found or an error + occured */ + bool bret; + fprintf(stderr, "Going to fallback mode using getpass(3)\n"); + char *passwordbuffer = getpass("Password: "); + size_t len = strlen(passwordbuffer); + /* Strip trailing newline */ + if(len > 0 and passwordbuffer[len-1] == '\n'){ + passwordbuffer[len-1] = '\0'; /* not strictly necessary */ + len--; + } + bret = print_out_password(passwordbuffer, len); + if(not bret){ + perror("print_out_password"); + exitstatus = EXIT_FAILURE; + } + } + + /* Restore old signal handler */ + ret = sigaction(SIGCHLD, &old_sigchld_action, NULL); + if(ret == -1){ + perror("sigaction"); + exitstatus = EXIT_FAILURE; + } + + if(custom_argv != NULL){ + for(char **arg = custom_argv+1; *arg != NULL; arg++){ + free(*arg); + } + free(custom_argv); + } + + if(dir != NULL){ + closedir(dir); + } + + /* Free the process list and kill the processes */ + for(plugin *p = plugin_list; p != NULL; p = p->next){ + if(p->pid != 0){ + close(p->fd); + ret = kill(p->pid, SIGTERM); + if(ret == -1 and errno != ESRCH){ + /* Set-uid proccesses might not get closed */ + perror("kill"); + } + } + } + + /* Wait for any remaining child processes to terminate */ + do{ + ret = wait(NULL); + } while(ret >= 0); + if(errno != ECHILD){ + perror("wait"); + } + + free_plugin_list(); + + free(plugindir); + free(argfile); + + return exitstatus; +} === added file 'plugin-runner.conf' --- plugin-runner.conf 1970-01-01 00:00:00 +0000 +++ plugin-runner.conf 2008-09-06 16:31:49 +0000 @@ -0,0 +1,9 @@ +## This is the configuration file for plugin-runner. It should be +## installed as "/etc/mandos/plugin-runner.conf", which will be copied +## to "/conf/conf.d/mandos/plugin-runner.conf" in the initrd.img file. +## +## The default network interface for mandos-client(8mandos) is +## "eth0". Uncomment this line and change it if necessary. +## + +#--options-for=mandos-client:--interface=eth0 === added file 'plugin-runner.xml' --- plugin-runner.xml 1970-01-01 00:00:00 +0000 +++ plugin-runner.xml 2008-09-06 16:31:49 +0000 @@ -0,0 +1,638 @@ + + + + +]> + + + + Mandos Manual + + Mandos + &VERSION; + &TIMESTAMP; + + + Björn + Påhlsson +
+ belorn@fukt.bsnet.se +
+
+ + Teddy + Hogeborn +
+ teddy@fukt.bsnet.se +
+
+
+ + 2008 + Teddy Hogeborn + Björn Påhlsson + + +
+ + + &COMMANDNAME; + 8mandos + + + + &COMMANDNAME; + + Run Mandos plugins, pass data from first to succeed. + + + + + + &COMMANDNAME; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &COMMANDNAME; + + + + + + + &COMMANDNAME; + + + + &COMMANDNAME; + + + + + + + + + DESCRIPTION + + &COMMANDNAME; is a program which is meant to + be specified as a keyscript for the root disk in + crypttab + 5. The aim of this + program is therefore to output a password, which then + cryptsetup + 8 will use to unlock the + root disk. + + + This program is not meant to be invoked directly, but can be in + order to test it. Note that any password obtained will simply + be output on standard output. + + + + + PURPOSE + + The purpose of this is to enable remote and unattended + rebooting of client host computer with an + encrypted root file system. See for details. + + + + + OPTIONS + + + + + + + This option will add an environment variable setting to + all plugins. This will override any inherited environment + variable. + + + + + + + + + + This option will add an environment variable setting to + the PLUGIN plugin. This will + override any inherited environment variables or + environment variables specified using + . + + + + + + + + + + Pass some options to all plugins. + OPTIONS is a comma separated + list of options. This is not a very useful option, except + for specifying the + option to all plugins. + + + + + + + + + + Pass some options to a specific plugin. PLUGIN is the name (file basename) of a + plugin, and OPTIONS is a comma + separated list of options. + + + Note that since options are not split on whitespace, the + way to pass, to the plugin + foo, the option + with the option argument + baz is either + --options-for=foo:--bar=baz or + --options-for=foo:--bar,baz. Using + --options-for="foo:--bar baz". will + not work. + + + + + + + + + + Disable the plugin named + PLUGIN. The plugin will not be + started. + + + + + + + + + + Re-enable the plugin named + PLUGIN. This is only useful to + undo a previous option, maybe + from the configuration file. + + + + + + + + + Change to group ID ID on + startup. The default is 65534. All plugins will be + started using this group ID. Note: + This must be a number, not a name. + + + + + + + + + Change to user ID ID on + startup. The default is 65534. All plugins will be + started using this user ID. Note: + This must be a number, not a name. + + + + + + + + + Specify a different plugin directory. The default is + /lib/mandos/plugins.d, which will + exist in the initial RAM disk + environment. + + + + + + + + + Specify a different file to read additional options from. + See . Other command line options + will override options specified in the file. + + + + + + + + + Enable debug mode. This will enable a lot of output to + standard error about what the program is doing. The + program will still perform all other functions normally. + The default is to not run in debug + mode. + + + The plugins will not be affected by + this option. Use + + if complete debugging eruption is desired. + + + + + + + + + + Gives a help message about options and their meanings. + + + + + + + + + Gives a short usage message. + + + + + + + + + + Prints the program version. + + + + + + + + OVERVIEW + + + This program will run on the client side in the initial + RAM disk environment, and is responsible for + getting a password. It does this by running plugins, one of + which will normally be the actual client program communicating + with the server. + + + + PLUGINS + + This program will get a password by running a number of + plugins, which are simply executable + programs in a directory in the initial RAM + disk environment. The default directory is + /lib/mandos/plugins.d, but this can be + changed with the option. The + plugins are started in parallel, and the first plugin to output + a password and exit with a successful exit + code will make this plugin-runner output the password from that + plugin, stop any other plugins, and exit. + + + + WRITING PLUGINS + + A plugin is simply a program which prints a password to its + standard output and then exits with a successful (zero) exit + status. If the exit status is not zero, any output on + standard output will be ignored by the plugin runner. Any + output on its standard error channel will simply be passed to + the standard error of the plugin runner, usually the system + console. + + + If the password is a single-line, manually entered passprase, + a final trailing newline character should + not be printed. + + + The plugin will run in the initial RAM disk environment, so + care must be taken not to depend on any files or running + services not available there. + + + The plugin must exit cleanly and free all allocated resources + upon getting the TERM signal, since this is what the plugin + runner uses to stop all other plugins when one plugin has + output a password and exited cleanly. + + + The plugin must not use resources, like for instance reading + from the standard input, without knowing that no other plugin + is also using it. + + + It is useful, but not required, for the plugin to take the + option. + + + + + + FALLBACK + + If no plugins succeed, this program will, as a fallback, ask for + a password on the console using getpass3, + and output it. This is not meant to be the normal mode of + operation, as there is a separate plugin for getting a password + from the console. + + + + + EXIT STATUS + + Exit status of this program is zero if no errors were + encountered, and otherwise not. The fallback (see ) may or may not have succeeded in either + case. + + + + + ENVIRONMENT + + This program does not use any environment variables itself, it + only passes on its environment to all the plugins. The + environment passed to plugins can be modified using the + and + options. + + + + + FILES + + + + /conf/conf.d/mandos/plugin-runner.conf + + + Since this program will be run as a keyscript, there is + little to no opportunity to pass command line arguments + to it. Therefore, it will also + read this file and use its contents as + whitespace-separated command line options. Also, + everything from a # character to the end + of a line is ignored. + + + This program is meant to run in the initial RAM disk + environment, so that is where this file is assumed to + exist. The file does not need to exist in the normal + file system. + + + This file will be processed before + the normal command line options, so the latter can + override the former, if need be. + + + This file name is the default; the file to read for + arguments can be changed using the + option. + + + + + + + + + BUGS + + The option is ignored when + specified from within a configuration file. + + + + + EXAMPLE + + + Normal invocation needs no options: + + + &COMMANDNAME; + + + + + Run the program, but not the plugins, in debug mode: + + + + + &COMMANDNAME; --debug + + + + + + Run all plugins, but run the foo plugin in + debug mode: + + + + + &COMMANDNAME; --options-for=foo:--debug + + + + + + Run all plugins, but not the program, in debug mode: + + + + + &COMMANDNAME; --global-options=--debug + + + + + + Run plugins from a different directory, read a different + configuration file, and add two options to the + mandos-client + 8mandos plugin: + + + + +&COMMANDNAME; --config-file=/etc/mandos/plugin-runner.conf --plugin-dir /usr/lib/mandos/plugins.d --options-for=mandos-client:--pubkey=/etc/keys/mandos/pubkey.txt,--seckey=/etc/keys/mandos/seckey.txt + + + + + + SECURITY + + This program will, when starting, try to switch to another user. + If it is started as root, it will succeed, and will by default + switch to user and group 65534, which are assumed to be + non-privileged. This user and group is then what all plugins + will be started as. Therefore, the only way to run a plugin as + a privileged user is to have the set-user-ID or set-group-ID bit + set on the plugin executable file (see + execve2 + ). + + + If this program is used as a keyscript in crypttab5 + , there is a slight risk that if this program + fails to work, there might be no way to boot the system except + for booting from another media and editing the initial RAM disk + image to not run this program. This is, however, unlikely, + since the password-prompt8mandos + plugin will read a password from the console in + case of failure of the other plugins, and this plugin runner + will also, in case of catastrophic failure, itself fall back to + asking and outputting a password on the console (see ). + + + + + SEE ALSO + + cryptsetup + 8, + crypttab + 5, + execve + 2, + mandos + 8, + password-prompt + 8mandos, + mandos-client + 8mandos + + + +
+ + + + + === added directory 'plugins.d' === added file 'plugins.d/mandos-client.c' --- plugins.d/mandos-client.c 1970-01-01 00:00:00 +0000 +++ plugins.d/mandos-client.c 2008-09-07 01:44:44 +0000 @@ -0,0 +1,1147 @@ +/* -*- coding: utf-8 -*- */ +/* + * Mandos client - get and decrypt data from a Mandos server + * + * This program is partly derived from an example program for an Avahi + * service browser, downloaded from + * . This + * includes the following functions: "resolve_callback", + * "browse_callback", and parts of "main". + * + * Everything else is + * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson + * + * 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 + * . + * + * Contact the authors at . + */ + +/* Needed by GPGME, specifically gpgme_data_seek() */ +#define _LARGEFILE_SOURCE +#define _FILE_OFFSET_BITS 64 + +#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */ + +#include /* fprintf(), stderr, fwrite(), + stdout, ferror() */ +#include /* uint16_t, uint32_t */ +#include /* NULL, size_t, ssize_t */ +#include /* free(), EXIT_SUCCESS, EXIT_FAILURE, + srand() */ +#include /* bool, true */ +#include /* memset(), strcmp(), strlen(), + strerror(), asprintf(), strcpy() */ +#include /* ioctl */ +#include /* socket(), inet_pton(), sockaddr, + sockaddr_in6, PF_INET6, + SOCK_STREAM, INET6_ADDRSTRLEN, + uid_t, gid_t, open(), opendir(), DIR */ +#include /* open() */ +#include /* socket(), struct sockaddr_in6, + struct in6_addr, inet_pton(), + connect() */ +#include /* open() */ +#include /* opendir(), struct dirent, readdir() */ +#include /* PRIu16 */ +#include /* assert() */ +#include /* perror(), errno */ +#include /* time() */ +#include /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, + SIOCSIFFLAGS, if_indextoname(), + if_nametoindex(), IF_NAMESIZE */ +#include +#include /* close(), SEEK_SET, off_t, write(), + getuid(), getgid(), setuid(), + setgid() */ +#include /* inet_pton(), htons */ +#include /* not, and */ +#include /* struct argp_option, error_t, struct + argp_state, struct argp, + argp_parse(), ARGP_KEY_ARG, + ARGP_KEY_END, ARGP_ERR_UNKNOWN */ + +/* Avahi */ +/* All Avahi types, constants and functions + Avahi*, avahi_*, + AVAHI_* */ +#include +#include +#include +#include +#include +#include + +/* GnuTLS */ +#include /* All GnuTLS types, constants and + functions: + gnutls_* + init_gnutls_session(), + GNUTLS_* */ +#include /* gnutls_certificate_set_openpgp_key_file(), + GNUTLS_OPENPGP_FMT_BASE64 */ + +/* GPGME */ +#include /* All GPGME types, constants and + functions: + gpgme_* + GPGME_PROTOCOL_OpenPGP, + GPG_ERR_NO_* */ + +#define BUFFER_SIZE 256 + +/* + #define PATHDIR "/conf/conf.d/mandos" +*/ + +#define PATHDIR "/conf/conf.d/mandos" +#define SECKEY "seckey.txt" +#define PUBKEY "pubkey.txt" + +bool debug = false; +static const char mandos_protocol_version[] = "1"; +const char *argp_program_version = "mandos-client 1.0"; +const char *argp_program_bug_address = ""; + +/* Used for passing in values through the Avahi callback functions */ +typedef struct { + AvahiSimplePoll *simple_poll; + AvahiServer *server; + gnutls_certificate_credentials_t cred; + unsigned int dh_bits; + gnutls_dh_params_t dh_params; + const char *priority; + gpgme_ctx_t ctx; +} mandos_context; + +/* + * Make room in "buffer" for at least BUFFER_SIZE additional bytes. + * "buffer_capacity" is how much is currently allocated, + * "buffer_length" is how much is already used. + */ +size_t adjustbuffer(char **buffer, size_t buffer_length, + size_t buffer_capacity){ + if (buffer_length + BUFFER_SIZE > buffer_capacity){ + *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE); + if (buffer == NULL){ + return 0; + } + buffer_capacity += BUFFER_SIZE; + } + return buffer_capacity; +} + +/* + * Initialize GPGME. + */ +static bool init_gpgme(mandos_context *mc, const char *seckey, + const char *pubkey, const char *tempdir){ + int ret; + gpgme_error_t rc; + gpgme_engine_info_t engine_info; + + + /* + * Helper function to insert pub and seckey to the enigne keyring. + */ + bool import_key(const char *filename){ + int fd; + gpgme_data_t pgp_data; + + fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY)); + if(fd == -1){ + perror("open"); + return false; + } + + rc = gpgme_data_new_from_fd(&pgp_data, fd); + if (rc != GPG_ERR_NO_ERROR){ + fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n", + gpgme_strsource(rc), gpgme_strerror(rc)); + return false; + } + + rc = gpgme_op_import(mc->ctx, pgp_data); + if (rc != GPG_ERR_NO_ERROR){ + fprintf(stderr, "bad gpgme_op_import: %s: %s\n", + gpgme_strsource(rc), gpgme_strerror(rc)); + return false; + } + + ret = TEMP_FAILURE_RETRY(close(fd)); + if(ret == -1){ + perror("close"); + } + gpgme_data_release(pgp_data); + return true; + } + + if (debug){ + fprintf(stderr, "Initialize gpgme\n"); + } + + /* Init GPGME */ + gpgme_check_version(NULL); + rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP); + if (rc != GPG_ERR_NO_ERROR){ + fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n", + gpgme_strsource(rc), gpgme_strerror(rc)); + return false; + } + + /* Set GPGME home directory for the OpenPGP engine only */ + rc = gpgme_get_engine_info (&engine_info); + if (rc != GPG_ERR_NO_ERROR){ + fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n", + gpgme_strsource(rc), gpgme_strerror(rc)); + return false; + } + while(engine_info != NULL){ + if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){ + gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, + engine_info->file_name, tempdir); + break; + } + engine_info = engine_info->next; + } + if(engine_info == NULL){ + fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir); + return false; + } + + /* Create new GPGME "context" */ + rc = gpgme_new(&(mc->ctx)); + if (rc != GPG_ERR_NO_ERROR){ + fprintf(stderr, "bad gpgme_new: %s: %s\n", + gpgme_strsource(rc), gpgme_strerror(rc)); + return false; + } + + if (not import_key(pubkey) or not import_key(seckey)){ + return false; + } + + return true; +} + +/* + * Decrypt OpenPGP data. + * Returns -1 on error + */ +static ssize_t pgp_packet_decrypt (const mandos_context *mc, + const char *cryptotext, + size_t crypto_size, + char **plaintext){ + gpgme_data_t dh_crypto, dh_plain; + gpgme_error_t rc; + ssize_t ret; + size_t plaintext_capacity = 0; + ssize_t plaintext_length = 0; + + if (debug){ + fprintf(stderr, "Trying to decrypt OpenPGP data\n"); + } + + /* Create new GPGME data buffer from memory cryptotext */ + rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size, + 0); + if (rc != GPG_ERR_NO_ERROR){ + fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n", + gpgme_strsource(rc), gpgme_strerror(rc)); + return -1; + } + + /* Create new empty GPGME data buffer for the plaintext */ + rc = gpgme_data_new(&dh_plain); + if (rc != GPG_ERR_NO_ERROR){ + fprintf(stderr, "bad gpgme_data_new: %s: %s\n", + gpgme_strsource(rc), gpgme_strerror(rc)); + gpgme_data_release(dh_crypto); + return -1; + } + + /* Decrypt data from the cryptotext data buffer to the plaintext + data buffer */ + rc = gpgme_op_decrypt(mc->ctx, dh_crypto, dh_plain); + if (rc != GPG_ERR_NO_ERROR){ + fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n", + gpgme_strsource(rc), gpgme_strerror(rc)); + plaintext_length = -1; + if (debug){ + gpgme_decrypt_result_t result; + result = gpgme_op_decrypt_result(mc->ctx); + if (result == NULL){ + fprintf(stderr, "gpgme_op_decrypt_result failed\n"); + } else { + fprintf(stderr, "Unsupported algorithm: %s\n", + result->unsupported_algorithm); + fprintf(stderr, "Wrong key usage: %u\n", + result->wrong_key_usage); + if(result->file_name != NULL){ + fprintf(stderr, "File name: %s\n", result->file_name); + } + gpgme_recipient_t recipient; + recipient = result->recipients; + if(recipient){ + while(recipient != NULL){ + fprintf(stderr, "Public key algorithm: %s\n", + gpgme_pubkey_algo_name(recipient->pubkey_algo)); + fprintf(stderr, "Key ID: %s\n", recipient->keyid); + fprintf(stderr, "Secret key available: %s\n", + recipient->status == GPG_ERR_NO_SECKEY + ? "No" : "Yes"); + recipient = recipient->next; + } + } + } + } + goto decrypt_end; + } + + if(debug){ + fprintf(stderr, "Decryption of OpenPGP data succeeded\n"); + } + + /* Seek back to the beginning of the GPGME plaintext data buffer */ + if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){ + perror("gpgme_data_seek"); + plaintext_length = -1; + goto decrypt_end; + } + + *plaintext = NULL; + while(true){ + plaintext_capacity = adjustbuffer(plaintext, + (size_t)plaintext_length, + plaintext_capacity); + if (plaintext_capacity == 0){ + perror("adjustbuffer"); + plaintext_length = -1; + goto decrypt_end; + } + + ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length, + BUFFER_SIZE); + /* Print the data, if any */ + if (ret == 0){ + /* EOF */ + break; + } + if(ret < 0){ + perror("gpgme_data_read"); + plaintext_length = -1; + goto decrypt_end; + } + plaintext_length += ret; + } + + if(debug){ + fprintf(stderr, "Decrypted password is: "); + for(ssize_t i = 0; i < plaintext_length; i++){ + fprintf(stderr, "%02hhX ", (*plaintext)[i]); + } + fprintf(stderr, "\n"); + } + + decrypt_end: + + /* Delete the GPGME cryptotext data buffer */ + gpgme_data_release(dh_crypto); + + /* Delete the GPGME plaintext data buffer */ + gpgme_data_release(dh_plain); + return plaintext_length; +} + +static const char * safer_gnutls_strerror (int value) { + const char *ret = gnutls_strerror (value); /* Spurious warning */ + if (ret == NULL) + ret = "(unknown)"; + return ret; +} + +/* GnuTLS log function callback */ +static void debuggnutls(__attribute__((unused)) int level, + const char* string){ + fprintf(stderr, "GnuTLS: %s", string); +} + +static int init_gnutls_global(mandos_context *mc, + const char *pubkeyfilename, + const char *seckeyfilename){ + int ret; + + if(debug){ + fprintf(stderr, "Initializing GnuTLS\n"); + } + + ret = gnutls_global_init(); + if (ret != GNUTLS_E_SUCCESS) { + fprintf (stderr, "GnuTLS global_init: %s\n", + safer_gnutls_strerror(ret)); + return -1; + } + + if (debug){ + /* "Use a log level over 10 to enable all debugging options." + * - GnuTLS manual + */ + gnutls_global_set_log_level(11); + gnutls_global_set_log_function(debuggnutls); + } + + /* OpenPGP credentials */ + gnutls_certificate_allocate_credentials(&mc->cred); + if (ret != GNUTLS_E_SUCCESS){ + fprintf (stderr, "GnuTLS memory error: %s\n", /* Spurious + warning */ + safer_gnutls_strerror(ret)); + gnutls_global_deinit (); + return -1; + } + + if(debug){ + fprintf(stderr, "Attempting to use OpenPGP public key %s and" + " secret key %s as GnuTLS credentials\n", pubkeyfilename, + seckeyfilename); + } + + ret = gnutls_certificate_set_openpgp_key_file + (mc->cred, pubkeyfilename, seckeyfilename, + GNUTLS_OPENPGP_FMT_BASE64); + if (ret != GNUTLS_E_SUCCESS) { + fprintf(stderr, + "Error[%d] while reading the OpenPGP key pair ('%s'," + " '%s')\n", ret, pubkeyfilename, seckeyfilename); + fprintf(stderr, "The GnuTLS error is: %s\n", + safer_gnutls_strerror(ret)); + goto globalfail; + } + + /* GnuTLS server initialization */ + ret = gnutls_dh_params_init(&mc->dh_params); + if (ret != GNUTLS_E_SUCCESS) { + fprintf (stderr, "Error in GnuTLS DH parameter initialization:" + " %s\n", safer_gnutls_strerror(ret)); + goto globalfail; + } + ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits); + if (ret != GNUTLS_E_SUCCESS) { + fprintf (stderr, "Error in GnuTLS prime generation: %s\n", + safer_gnutls_strerror(ret)); + goto globalfail; + } + + gnutls_certificate_set_dh_params(mc->cred, mc->dh_params); + + return 0; + + globalfail: + + gnutls_certificate_free_credentials(mc->cred); + gnutls_global_deinit(); + gnutls_dh_params_deinit(mc->dh_params); + return -1; +} + +static int init_gnutls_session(mandos_context *mc, + gnutls_session_t *session){ + int ret; + /* GnuTLS session creation */ + ret = gnutls_init(session, GNUTLS_SERVER); + if (ret != GNUTLS_E_SUCCESS){ + fprintf(stderr, "Error in GnuTLS session initialization: %s\n", + safer_gnutls_strerror(ret)); + } + + { + const char *err; + ret = gnutls_priority_set_direct(*session, mc->priority, &err); + if (ret != GNUTLS_E_SUCCESS) { + fprintf(stderr, "Syntax error at: %s\n", err); + fprintf(stderr, "GnuTLS error: %s\n", + safer_gnutls_strerror(ret)); + gnutls_deinit (*session); + return -1; + } + } + + ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, + mc->cred); + if (ret != GNUTLS_E_SUCCESS) { + fprintf(stderr, "Error setting GnuTLS credentials: %s\n", + safer_gnutls_strerror(ret)); + gnutls_deinit (*session); + return -1; + } + + /* ignore client certificate if any. */ + gnutls_certificate_server_set_request (*session, + GNUTLS_CERT_IGNORE); + + gnutls_dh_set_prime_bits (*session, mc->dh_bits); + + return 0; +} + +/* Avahi log function callback */ +static void empty_log(__attribute__((unused)) AvahiLogLevel level, + __attribute__((unused)) const char *txt){} + +/* Called when a Mandos server is found */ +static int start_mandos_communication(const char *ip, uint16_t port, + AvahiIfIndex if_index, + mandos_context *mc){ + int ret, tcp_sd; + union { struct sockaddr in; struct sockaddr_in6 in6; } to; + char *buffer = NULL; + char *decrypted_buffer; + size_t buffer_length = 0; + size_t buffer_capacity = 0; + ssize_t decrypted_buffer_size; + size_t written; + int retval = 0; + char interface[IF_NAMESIZE]; + gnutls_session_t session; + + ret = init_gnutls_session (mc, &session); + if (ret != 0){ + return -1; + } + + if(debug){ + fprintf(stderr, "Setting up a tcp connection to %s, port %" PRIu16 + "\n", ip, port); + } + + tcp_sd = socket(PF_INET6, SOCK_STREAM, 0); + if(tcp_sd < 0) { + perror("socket"); + return -1; + } + + if(debug){ + if(if_indextoname((unsigned int)if_index, interface) == NULL){ + perror("if_indextoname"); + return -1; + } + fprintf(stderr, "Binding to interface %s\n", interface); + } + + memset(&to, 0, sizeof(to)); + to.in6.sin6_family = AF_INET6; + /* It would be nice to have a way to detect if we were passed an + IPv4 address here. Now we assume an IPv6 address. */ + ret = inet_pton(AF_INET6, ip, &to.in6.sin6_addr); + if (ret < 0 ){ + perror("inet_pton"); + return -1; + } + if(ret == 0){ + fprintf(stderr, "Bad address: %s\n", ip); + return -1; + } + to.in6.sin6_port = htons(port); /* Spurious warning */ + + to.in6.sin6_scope_id = (uint32_t)if_index; + + if(debug){ + fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip, + port); + char addrstr[INET6_ADDRSTRLEN] = ""; + if(inet_ntop(to.in6.sin6_family, &(to.in6.sin6_addr), addrstr, + sizeof(addrstr)) == NULL){ + perror("inet_ntop"); + } else { + if(strcmp(addrstr, ip) != 0){ + fprintf(stderr, "Canonical address form: %s\n", addrstr); + } + } + } + + ret = connect(tcp_sd, &to.in, sizeof(to)); + if (ret < 0){ + perror("connect"); + return -1; + } + + const char *out = mandos_protocol_version; + written = 0; + while (true){ + size_t out_size = strlen(out); + ret = TEMP_FAILURE_RETRY(write(tcp_sd, out + written, + out_size - written)); + if (ret == -1){ + perror("write"); + retval = -1; + goto mandos_end; + } + written += (size_t)ret; + if(written < out_size){ + continue; + } else { + if (out == mandos_protocol_version){ + written = 0; + out = "\r\n"; + } else { + break; + } + } + } + + if(debug){ + fprintf(stderr, "Establishing TLS session with %s\n", ip); + } + + gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd); + + do{ + ret = gnutls_handshake (session); + } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED); + + if (ret != GNUTLS_E_SUCCESS){ + if(debug){ + fprintf(stderr, "*** GnuTLS Handshake failed ***\n"); + gnutls_perror (ret); + } + retval = -1; + goto mandos_end; + } + + /* Read OpenPGP packet that contains the wanted password */ + + if(debug){ + fprintf(stderr, "Retrieving pgp encrypted password from %s\n", + ip); + } + + while(true){ + buffer_capacity = adjustbuffer(&buffer, buffer_length, + buffer_capacity); + if (buffer_capacity == 0){ + perror("adjustbuffer"); + retval = -1; + goto mandos_end; + } + + ret = gnutls_record_recv(session, buffer+buffer_length, + BUFFER_SIZE); + if (ret == 0){ + break; + } + if (ret < 0){ + switch(ret){ + case GNUTLS_E_INTERRUPTED: + case GNUTLS_E_AGAIN: + break; + case GNUTLS_E_REHANDSHAKE: + do{ + ret = gnutls_handshake (session); + } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED); + if (ret < 0){ + fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n"); + gnutls_perror (ret); + retval = -1; + goto mandos_end; + } + break; + default: + fprintf(stderr, "Unknown error while reading data from" + " encrypted session with Mandos server\n"); + retval = -1; + gnutls_bye (session, GNUTLS_SHUT_RDWR); + goto mandos_end; + } + } else { + buffer_length += (size_t) ret; + } + } + + if(debug){ + fprintf(stderr, "Closing TLS session\n"); + } + + gnutls_bye (session, GNUTLS_SHUT_RDWR); + + if (buffer_length > 0){ + decrypted_buffer_size = pgp_packet_decrypt(mc, buffer, + buffer_length, + &decrypted_buffer); + if (decrypted_buffer_size >= 0){ + written = 0; + while(written < (size_t) decrypted_buffer_size){ + ret = (int)fwrite (decrypted_buffer + written, 1, + (size_t)decrypted_buffer_size - written, + stdout); + if(ret == 0 and ferror(stdout)){ + if(debug){ + fprintf(stderr, "Error writing encrypted data: %s\n", + strerror(errno)); + } + retval = -1; + break; + } + written += (size_t)ret; + } + free(decrypted_buffer); + } else { + retval = -1; + } + } else { + retval = -1; + } + + /* Shutdown procedure */ + + mandos_end: + free(buffer); + ret = TEMP_FAILURE_RETRY(close(tcp_sd)); + if(ret == -1){ + perror("close"); + } + gnutls_deinit (session); + return retval; +} + +static void resolve_callback(AvahiSServiceResolver *r, + AvahiIfIndex interface, + AVAHI_GCC_UNUSED AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char *type, + const char *domain, + const char *host_name, + const AvahiAddress *address, + uint16_t port, + AVAHI_GCC_UNUSED AvahiStringList *txt, + AVAHI_GCC_UNUSED AvahiLookupResultFlags + flags, + void* userdata) { + mandos_context *mc = userdata; + assert(r); + + /* Called whenever a service has been resolved successfully or + timed out */ + + switch (event) { + default: + case AVAHI_RESOLVER_FAILURE: + fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'" + " of type '%s' in domain '%s': %s\n", name, type, domain, + avahi_strerror(avahi_server_errno(mc->server))); + break; + + case AVAHI_RESOLVER_FOUND: + { + char ip[AVAHI_ADDRESS_STR_MAX]; + avahi_address_snprint(ip, sizeof(ip), address); + if(debug){ + fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %" + PRIu16 ") on port %d\n", name, host_name, ip, + interface, port); + } + int ret = start_mandos_communication(ip, port, interface, mc); + if (ret == 0){ + avahi_simple_poll_quit(mc->simple_poll); + } + } + } + avahi_s_service_resolver_free(r); +} + +static void browse_callback( AvahiSServiceBrowser *b, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AVAHI_GCC_UNUSED AvahiLookupResultFlags + flags, + void* userdata) { + mandos_context *mc = userdata; + assert(b); + + /* Called whenever a new services becomes available on the LAN or + is removed from the LAN */ + + switch (event) { + default: + case AVAHI_BROWSER_FAILURE: + + fprintf(stderr, "(Avahi browser) %s\n", + avahi_strerror(avahi_server_errno(mc->server))); + avahi_simple_poll_quit(mc->simple_poll); + return; + + case AVAHI_BROWSER_NEW: + /* We ignore the returned Avahi resolver object. In the callback + function we free it. If the Avahi server is terminated before + the callback function is called the Avahi server will free the + resolver for us. */ + + if (!(avahi_s_service_resolver_new(mc->server, interface, + protocol, name, type, domain, + AVAHI_PROTO_INET6, 0, + resolve_callback, mc))) + fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n", + name, avahi_strerror(avahi_server_errno(mc->server))); + break; + + case AVAHI_BROWSER_REMOVE: + break; + + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + if(debug){ + fprintf(stderr, "No Mandos server found, still searching...\n"); + } + break; + } +} + +int main(int argc, char *argv[]){ + AvahiSServiceBrowser *sb = NULL; + int error; + int ret; + int exitcode = EXIT_SUCCESS; + const char *interface = "eth0"; + struct ifreq network; + int sd; + uid_t uid; + gid_t gid; + char *connect_to = NULL; + char tempdir[] = "/tmp/mandosXXXXXX"; + AvahiIfIndex if_index = AVAHI_IF_UNSPEC; + const char *seckey = PATHDIR "/" SECKEY; + const char *pubkey = PATHDIR "/" PUBKEY; + + mandos_context mc = { .simple_poll = NULL, .server = NULL, + .dh_bits = 1024, .priority = "SECURE256" + ":!CTYPE-X.509:+CTYPE-OPENPGP" }; + bool gnutls_initalized = false; + bool gpgme_initalized = false; + + { + struct argp_option options[] = { + { .name = "debug", .key = 128, + .doc = "Debug mode", .group = 3 }, + { .name = "connect", .key = 'c', + .arg = "ADDRESS:PORT", + .doc = "Connect directly to a specific Mandos server", + .group = 1 }, + { .name = "interface", .key = 'i', + .arg = "NAME", + .doc = "Interface that will be used to search for Mandos" + " servers", + .group = 1 }, + { .name = "seckey", .key = 's', + .arg = "FILE", + .doc = "OpenPGP secret key file base name", + .group = 1 }, + { .name = "pubkey", .key = 'p', + .arg = "FILE", + .doc = "OpenPGP public key file base name", + .group = 2 }, + { .name = "dh-bits", .key = 129, + .arg = "BITS", + .doc = "Bit length of the prime number used in the" + " Diffie-Hellman key exchange", + .group = 2 }, + { .name = "priority", .key = 130, + .arg = "STRING", + .doc = "GnuTLS priority string for the TLS handshake", + .group = 1 }, + { .name = NULL } + }; + + error_t parse_opt (int key, char *arg, + struct argp_state *state) { + /* Get the INPUT argument from `argp_parse', which we know is + a pointer to our plugin list pointer. */ + switch (key) { + case 128: /* --debug */ + debug = true; + break; + case 'c': /* --connect */ + connect_to = arg; + break; + case 'i': /* --interface */ + interface = arg; + break; + case 's': /* --seckey */ + seckey = arg; + break; + case 'p': /* --pubkey */ + pubkey = arg; + break; + case 129: /* --dh-bits */ + errno = 0; + mc.dh_bits = (unsigned int) strtol(arg, NULL, 10); + if (errno){ + perror("strtol"); + exit(EXIT_FAILURE); + } + break; + case 130: /* --priority */ + mc.priority = arg; + break; + case ARGP_KEY_ARG: + argp_usage (state); + case ARGP_KEY_END: + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; + } + + struct argp argp = { .options = options, .parser = parse_opt, + .args_doc = "", + .doc = "Mandos client -- Get and decrypt" + " passwords from a Mandos server" }; + ret = argp_parse (&argp, argc, argv, 0, 0, NULL); + if (ret == ARGP_ERR_UNKNOWN){ + fprintf(stderr, "Unknown error while parsing arguments\n"); + exitcode = EXIT_FAILURE; + goto end; + } + } + + /* If the interface is down, bring it up */ + { + sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); + if(sd < 0) { + perror("socket"); + exitcode = EXIT_FAILURE; + goto end; + } + strcpy(network.ifr_name, interface); + ret = ioctl(sd, SIOCGIFFLAGS, &network); + if(ret == -1){ + perror("ioctl SIOCGIFFLAGS"); + exitcode = EXIT_FAILURE; + goto end; + } + if((network.ifr_flags & IFF_UP) == 0){ + network.ifr_flags |= IFF_UP; + ret = ioctl(sd, SIOCSIFFLAGS, &network); + if(ret == -1){ + perror("ioctl SIOCSIFFLAGS"); + exitcode = EXIT_FAILURE; + goto end; + } + } + ret = TEMP_FAILURE_RETRY(close(sd)); + if(ret == -1){ + perror("close"); + } + } + + uid = getuid(); + gid = getgid(); + + ret = setuid(uid); + if (ret == -1){ + perror("setuid"); + } + + setgid(gid); + if (ret == -1){ + perror("setgid"); + } + + ret = init_gnutls_global(&mc, pubkey, seckey); + if (ret == -1){ + fprintf(stderr, "init_gnutls_global failed\n"); + exitcode = EXIT_FAILURE; + goto end; + } else { + gnutls_initalized = true; + } + + if(mkdtemp(tempdir) == NULL){ + perror("mkdtemp"); + tempdir[0] = '\0'; + goto end; + } + + if(not init_gpgme(&mc, pubkey, seckey, tempdir)){ + fprintf(stderr, "gpgme_initalized failed\n"); + exitcode = EXIT_FAILURE; + goto end; + } else { + gpgme_initalized = true; + } + + if_index = (AvahiIfIndex) if_nametoindex(interface); + if(if_index == 0){ + fprintf(stderr, "No such interface: \"%s\"\n", interface); + exit(EXIT_FAILURE); + } + + if(connect_to != NULL){ + /* Connect directly, do not use Zeroconf */ + /* (Mainly meant for debugging) */ + char *address = strrchr(connect_to, ':'); + if(address == NULL){ + fprintf(stderr, "No colon in address\n"); + exitcode = EXIT_FAILURE; + goto end; + } + errno = 0; + uint16_t port = (uint16_t) strtol(address+1, NULL, 10); + if(errno){ + perror("Bad port number"); + exitcode = EXIT_FAILURE; + goto end; + } + *address = '\0'; + address = connect_to; + ret = start_mandos_communication(address, port, if_index, &mc); + if(ret < 0){ + exitcode = EXIT_FAILURE; + } else { + exitcode = EXIT_SUCCESS; + } + goto end; + } + + if (not debug){ + avahi_set_log_function(empty_log); + } + + /* Initialize the pseudo-RNG for Avahi */ + srand((unsigned int) time(NULL)); + + /* Allocate main Avahi loop object */ + mc.simple_poll = avahi_simple_poll_new(); + if (mc.simple_poll == NULL) { + fprintf(stderr, "Avahi: Failed to create simple poll" + " object.\n"); + exitcode = EXIT_FAILURE; + goto end; + } + + { + AvahiServerConfig config; + /* Do not publish any local Zeroconf records */ + avahi_server_config_init(&config); + config.publish_hinfo = 0; + config.publish_addresses = 0; + config.publish_workstation = 0; + config.publish_domain = 0; + + /* Allocate a new server */ + mc.server = avahi_server_new(avahi_simple_poll_get + (mc.simple_poll), &config, NULL, + NULL, &error); + + /* Free the Avahi configuration data */ + avahi_server_config_free(&config); + } + + /* Check if creating the Avahi server object succeeded */ + if (mc.server == NULL) { + fprintf(stderr, "Failed to create Avahi server: %s\n", + avahi_strerror(error)); + exitcode = EXIT_FAILURE; + goto end; + } + + /* Create the Avahi service browser */ + sb = avahi_s_service_browser_new(mc.server, if_index, + AVAHI_PROTO_INET6, + "_mandos._tcp", NULL, 0, + browse_callback, &mc); + if (sb == NULL) { + fprintf(stderr, "Failed to create service browser: %s\n", + avahi_strerror(avahi_server_errno(mc.server))); + exitcode = EXIT_FAILURE; + goto end; + } + + /* Run the main loop */ + + if (debug){ + fprintf(stderr, "Starting Avahi loop search\n"); + } + + avahi_simple_poll_loop(mc.simple_poll); + + end: + + if (debug){ + fprintf(stderr, "%s exiting\n", argv[0]); + } + + /* Cleanup things */ + if (sb != NULL) + avahi_s_service_browser_free(sb); + + if (mc.server != NULL) + avahi_server_free(mc.server); + + if (mc.simple_poll != NULL) + avahi_simple_poll_free(mc.simple_poll); + + if (gnutls_initalized){ + gnutls_certificate_free_credentials(mc.cred); + gnutls_global_deinit (); + gnutls_dh_params_deinit(mc.dh_params); + } + + if(gpgme_initalized){ + gpgme_release(mc.ctx); + } + + /* Removes the temp directory used by GPGME */ + if(tempdir[0] != '\0'){ + DIR *d; + struct dirent *direntry; + d = opendir(tempdir); + if(d == NULL){ + perror("opendir"); + } else { + while(true){ + direntry = readdir(d); + if(direntry == NULL){ + break; + } + if (direntry->d_type == DT_REG){ + char *fullname = NULL; + ret = asprintf(&fullname, "%s/%s", tempdir, + direntry->d_name); + if(ret < 0){ + perror("asprintf"); + continue; + } + ret = unlink(fullname); + if(ret == -1){ + fprintf(stderr, "unlink(\"%s\"): %s", + fullname, strerror(errno)); + } + free(fullname); + } + } + closedir(d); + } + ret = rmdir(tempdir); + if(ret == -1){ + perror("rmdir"); + } + } + + return exitcode; +} === added file 'plugins.d/mandos-client.xml' --- plugins.d/mandos-client.xml 1970-01-01 00:00:00 +0000 +++ plugins.d/mandos-client.xml 2008-09-06 16:33:08 +0000 @@ -0,0 +1,610 @@ + + + + +]> + + + + Mandos Manual + + Mandos + &VERSION; + &TIMESTAMP; + + + Björn + Påhlsson +
+ belorn@fukt.bsnet.se +
+
+ + Teddy + Hogeborn +
+ teddy@fukt.bsnet.se +
+
+
+ + 2008 + Teddy Hogeborn + Björn Påhlsson + + +
+ + + &COMMANDNAME; + 8mandos + + + + &COMMANDNAME; + + Client for Mandos + + + + + + &COMMANDNAME; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + &COMMANDNAME; + + + + + + + &COMMANDNAME; + + + + &COMMANDNAME; + + + + + + + + + DESCRIPTION + + &COMMANDNAME; is a client program that + communicates with mandos8 + to get a password. It uses IPv6 link-local addresses to get + network connectivity, Zeroconf to find servers, and TLS with an + OpenPGP key to ensure authenticity and confidentiality. It + keeps running, trying all servers on the network, until it + receives a satisfactory reply or a TERM signal is received. + + + This program is not meant to be run directly; it is really meant + to run as a plugin of the Mandos + plugin-runner + 8mandos, which runs in the + initial RAM disk environment because it is + specified as a keyscript in the + crypttab5 + file. + + + + + PURPOSE + + The purpose of this is to enable remote and unattended + rebooting of client host computer with an + encrypted root file system. See for details. + + + + + OPTIONS + + This program is commonly not invoked from the command line; it + is normally started by the Mandos + plugin runner, see plugin-runner8mandos + . Any command line options this program accepts + are therefore normally provided by the plugin runner, and not + directly. + + + + + + + + + Do not use Zeroconf to locate servers. Connect directly + to only one specified Mandos + server. Note that an IPv6 address has colon characters in + it, so the last colon character is + assumed to separate the address from the port number. + + + This option is normally only useful for testing and + debugging. + + + + + + + + + + Network interface that will be brought up and scanned for + Mandos servers to connect to. The default it + eth0. + + + If the option is used, this + specifies the interface to use to connect to the address + given. + + + + + + + + + + OpenPGP public key file name. The default name is + /conf/conf.d/mandos/pubkey.txt. + + + + + + + + + + OpenPGP secret key file name. The default name is + /conf/conf.d/mandos/seckey.txt. + + + + + + + + + + + + + + + + Sets the number of bits to use for the prime number in the + TLS Diffie-Hellman key exchange. Default is 1024. + + + + + + + + + Enable debug mode. This will enable a lot of output to + standard error about what the program is doing. The + program will still perform all other functions normally. + + + It will also enable debug mode in the Avahi and GnuTLS + libraries, making them print large amounts of debugging + output. + + + + + + + + + + Gives a help message about options and their meanings. + + + + + + + + + Gives a short usage message. + + + + + + + + + + Prints the program version. + + + + + + + + OVERVIEW + + + This program is the client part. It is a plugin started by + plugin-runner + 8mandos which will run in + an initial RAM disk environment. + + + This program could, theoretically, be used as a keyscript in + /etc/crypttab, but it would then be + impossible to enter a password for the encrypted root disk at + the console, since this program does not read from the console + at all. This is why a separate plugin runner ( + plugin-runner + 8mandos) is used to run + both this program and others in in parallel, + one of which will prompt for passwords on + the system console. + + + + + EXIT STATUS + + This program will exit with a successful (zero) exit status if a + server could be found and the password received from it could be + successfully decrypted and output on standard output. The + program will exit with a non-zero exit status only if a critical + error occurs. Otherwise, it will forever connect to new + Mandos servers as they appear, trying + to get a decryptable password and print it. + + + + + ENVIRONMENT + + This program does not use any environment variables, not even + the ones provided by cryptsetup8 + . + + + + + FILES + + + /conf/conf.d/mandos/pubkey.txt + /conf/conf.d/mandos/seckey.txt + + + OpenPGP public and private key files, in ASCII + Armor format. These are the default file names, + they can be changed with the and + options. + + + + + + + + + + + + + + EXAMPLE + + Note that normally, command line options will not be given + directly, but via options for the Mandos plugin-runner + 8mandos. + + + + Normal invocation needs no options, if the network interface + is eth0: + + + &COMMANDNAME; + + + + + Search for Mandos servers (and connect to them) using another + interface: + + + + &COMMANDNAME; --interface eth1 + + + + + Run in debug mode, and use a custom key: + + + + +&COMMANDNAME; --debug --pubkey keydir/pubkey.txt --seckey keydir/seckey.txt + + + + + + Run in debug mode, with a custom key, and do not use Zeroconf + to locate a server; connect directly to the IPv6 address + 2001:db8:f983:bd0b:30de:ae4a:71f2:f672, + port 4711, using interface eth2: + + + + +&COMMANDNAME; --debug --pubkey keydir/pubkey.txt --seckey keydir/seckey.txt --connect 2001:db8:f983:bd0b:30de:ae4a:71f2:f672:4711 --interface eth2 + + + + + + + SECURITY + + This program is set-uid to root, but will switch back to the + original (and presumably non-privileged) user and group after + bringing up the network interface. + + + To use this program for its intended purpose (see ), the password for the root file system will + have to be given out to be stored in a server computer, after + having been encrypted using an OpenPGP key. This encrypted data + which will be stored in a server can only be decrypted by the + OpenPGP key, and the data will only be given out to those + clients who can prove they actually have that key. This key, + however, is stored unencrypted on the client side in its initial + RAM disk image file system. This is normally + readable by all, but this is normally fixed during installation + of this program; file permissions are set so that no-one is able + to read that file. + + + The only remaining weak point is that someone with physical + access to the client hard drive might turn off the client + computer, read the OpenPGP keys directly from the hard drive, + and communicate with the server. The defense against this is + that the server is supposed to notice the client disappearing + and will stop giving out the encrypted data. Therefore, it is + important to set the timeout and checker interval values tightly + on the server. See mandos8. + + + It will also help if the checker program on the server is + configured to request something from the client which can not be + spoofed by someone else on the network, unlike unencrypted + ICMP echo (ping) replies. + + + Note: This makes it completely insecure to + have Mandos clients which dual-boot + to another operating system which is not + trusted to keep the initial RAM disk image + confidential. + + + + + SEE ALSO + + cryptsetup + 8, + crypttab + 5, + mandos + 8, + password-prompt + 8mandos, + plugin-runner + 8mandos + + + + + Zeroconf + + + + Zeroconf is the network protocol standard used for finding + Mandos servers on the local network. + + + + + + Avahi + + + + Avahi is the library this program calls to find Zeroconf + services. + + + + + + GnuTLS + + + + GnuTLS is the library this client uses to implement TLS for + communicating securely with the server, and at the same time + send the public OpenPGP key to the server. + + + + + + GPGME + + + + GPGME is the library used to decrypt the OpenPGP data sent + by the server. + + + + + + RFC 4291: IP Version 6 Addressing + Architecture + + + + + Section 2.2: Text Representation of + Addresses + + + + Section 2.5.5.2: IPv4-Mapped IPv6 + Address + + + + Section 2.5.6, Link-Local IPv6 Unicast + Addresses + + + This client uses IPv6 link-local addresses, which are + immediately usable since a link-local addresses is + automatically assigned to a network interfaces when it + is brought up. + + + + + + + + + RFC 4346: The Transport Layer Security (TLS) + Protocol Version 1.1 + + + + TLS 1.1 is the protocol implemented by GnuTLS. + + + + + + RFC 4880: OpenPGP Message Format + + + + The data received from the server is binary encrypted + OpenPGP data. + + + + + + RFC 5081: Using OpenPGP Keys for Transport Layer + Security + + + + This is implemented by GnuTLS and used by this program so + that OpenPGP keys can be used. + + + + + + +
+ + + + + === added file 'plugins.d/password-prompt.c' --- plugins.d/password-prompt.c 1970-01-01 00:00:00 +0000 +++ plugins.d/password-prompt.c 2008-09-07 01:44:44 +0000 @@ -0,0 +1,271 @@ +/* -*- coding: utf-8 -*- */ +/* + * Passprompt - Read a password from the terminal and print it + * + * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson + * + * 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 + * . + * + * Contact the authors at and + * . + */ + +#define _GNU_SOURCE /* getline() */ + +#include /* struct termios, tcsetattr(), + TCSAFLUSH, tcgetattr(), ECHO */ +#include /* struct termios, tcsetattr(), + STDIN_FILENO, TCSAFLUSH, + tcgetattr(), ECHO */ +#include /* sig_atomic_t, raise(), struct + sigaction, sigemptyset(), + sigaction(), sigaddset(), SIGINT, + SIGQUIT, SIGHUP, SIGTERM */ +#include /* NULL, size_t, ssize_t */ +#include /* ssize_t */ +#include /* EXIT_SUCCESS, EXIT_FAILURE, + getopt_long, getenv() */ +#include /* fprintf(), stderr, getline(), + stdin, feof(), perror(), fputc(), + stdout, getopt_long */ +#include /* errno, EINVAL */ +#include /* or, not */ +#include /* bool, false, true */ +#include /* strlen, rindex, strncmp, strcmp */ +#include /* struct argp_option, struct + argp_state, struct argp, + argp_parse(), error_t, + ARGP_KEY_ARG, ARGP_KEY_END, + ARGP_ERR_UNKNOWN */ + +volatile bool quit_now = false; +bool debug = false; +const char *argp_program_version = "password-prompt 1.0"; +const char *argp_program_bug_address = ""; + +static void termination_handler(__attribute__((unused))int signum){ + quit_now = true; +} + +int main(int argc, char **argv){ + ssize_t ret; + size_t n; + struct termios t_new, t_old; + char *buffer = NULL; + char *prefix = NULL; + int status = EXIT_SUCCESS; + struct sigaction old_action, + new_action = { .sa_handler = termination_handler, + .sa_flags = 0 }; + { + struct argp_option options[] = { + { .name = "prefix", .key = 'p', + .arg = "PREFIX", .flags = 0, + .doc = "Prefix shown before the prompt", .group = 2 }, + { .name = "debug", .key = 128, + .doc = "Debug mode", .group = 3 }, + { .name = NULL } + }; + + error_t parse_opt (int key, char *arg, struct argp_state *state) { + /* Get the INPUT argument from `argp_parse', which we know is a + pointer to our plugin list pointer. */ + switch (key) { + case 'p': + prefix = arg; + break; + case 128: + debug = true; + break; + case ARGP_KEY_ARG: + argp_usage (state); + break; + case ARGP_KEY_END: + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; + } + + struct argp argp = { .options = options, .parser = parse_opt, + .args_doc = "", + .doc = "Mandos password-prompt -- Read and" + " output a password" }; + ret = argp_parse (&argp, argc, argv, 0, 0, NULL); + if (ret == ARGP_ERR_UNKNOWN){ + fprintf(stderr, "Unknown error while parsing arguments\n"); + return EXIT_FAILURE; + } + } + + if (debug){ + fprintf(stderr, "Starting %s\n", argv[0]); + } + if (debug){ + fprintf(stderr, "Storing current terminal attributes\n"); + } + + if (tcgetattr(STDIN_FILENO, &t_old) != 0){ + perror("tcgetattr"); + return EXIT_FAILURE; + } + + sigemptyset(&new_action.sa_mask); + sigaddset(&new_action.sa_mask, SIGINT); + sigaddset(&new_action.sa_mask, SIGHUP); + sigaddset(&new_action.sa_mask, SIGTERM); + ret = sigaction(SIGINT, NULL, &old_action); + if(ret == -1){ + perror("sigaction"); + return EXIT_FAILURE; + } + if (old_action.sa_handler != SIG_IGN){ + ret = sigaction(SIGINT, &new_action, NULL); + if(ret == -1){ + perror("sigaction"); + return EXIT_FAILURE; + } + } + ret = sigaction(SIGHUP, NULL, &old_action); + if(ret == -1){ + perror("sigaction"); + return EXIT_FAILURE; + } + if (old_action.sa_handler != SIG_IGN){ + ret = sigaction(SIGHUP, &new_action, NULL); + if(ret == -1){ + perror("sigaction"); + return EXIT_FAILURE; + } + } + ret = sigaction(SIGTERM, NULL, &old_action); + if(ret == -1){ + perror("sigaction"); + return EXIT_FAILURE; + } + if (old_action.sa_handler != SIG_IGN){ + ret = sigaction(SIGTERM, &new_action, NULL); + if(ret == -1){ + perror("sigaction"); + return EXIT_FAILURE; + } + } + + + if (debug){ + fprintf(stderr, "Removing echo flag from terminal attributes\n"); + } + + t_new = t_old; + t_new.c_lflag &= ~ECHO; + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){ + perror("tcsetattr-echo"); + return EXIT_FAILURE; + } + + if (debug){ + fprintf(stderr, "Waiting for input from stdin \n"); + } + while(true){ + if (quit_now){ + if(debug){ + fprintf(stderr, "Interrupted by signal, exiting.\n"); + } + status = EXIT_FAILURE; + break; + } + + if(prefix){ + fprintf(stderr, "%s ", prefix); + } + { + const char *cryptsource = getenv("cryptsource"); + const char *crypttarget = getenv("crypttarget"); + const char *const prompt + = "Enter passphrase to unlock the disk"; + if(cryptsource == NULL){ + if(crypttarget == NULL){ + fprintf(stderr, "%s: ", prompt); + } else { + fprintf(stderr, "%s (%s): ", prompt, crypttarget); + } + } else { + if(crypttarget == NULL){ + fprintf(stderr, "%s %s: ", prompt, cryptsource); + } else { + fprintf(stderr, "%s %s (%s): ", prompt, cryptsource, + crypttarget); + } + } + } + ret = getline(&buffer, &n, stdin); + if (ret > 0){ + status = EXIT_SUCCESS; + /* Make n = data size instead of allocated buffer size */ + n = (size_t)ret; + /* Strip final newline */ + if(n>0 and buffer[n-1] == '\n'){ + buffer[n-1] = '\0'; /* not strictly necessary */ + n--; + } + size_t written = 0; + while(written < n){ + ret = write(STDOUT_FILENO, buffer + written, n - written); + if(ret < 0){ + perror("write"); + status = EXIT_FAILURE; + break; + } + written += (size_t)ret; + } + break; + } + if (ret < 0){ + if (errno != EINTR and not feof(stdin)){ + perror("getline"); + status = EXIT_FAILURE; + break; + } + } + /* if(ret == 0), then the only sensible thing to do is to retry to + read from stdin */ + fputc('\n', stderr); + if(debug and not quit_now){ + /* If quit_now is true, we were interrupted by a signal, and + will print that later, so no need to show this too. */ + fprintf(stderr, "getline() returned 0, retrying.\n"); + } + } + + free(buffer); + + if (debug){ + fprintf(stderr, "Restoring terminal attributes\n"); + } + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){ + perror("tcsetattr+echo"); + } + + if (debug){ + fprintf(stderr, "%s is exiting with status %d\n", argv[0], + status); + } + if(status == EXIT_SUCCESS){ + fputc('\n', stderr); + } + + return status; +} === added file 'plugins.d/password-prompt.xml' --- plugins.d/password-prompt.xml 1970-01-01 00:00:00 +0000 +++ plugins.d/password-prompt.xml 2008-09-06 16:31:49 +0000 @@ -0,0 +1,304 @@ + + + + +]> + + + + Mandos Manual + + Mandos + &VERSION; + &TIMESTAMP; + + + Björn + Påhlsson +
+ belorn@fukt.bsnet.se +
+
+ + Teddy + Hogeborn +
+ teddy@fukt.bsnet.se +
+
+
+ + 2008 + Teddy Hogeborn + Björn Påhlsson + + +
+ + + &COMMANDNAME; + 8mandos + + + + &COMMANDNAME; + Prompt for a password and output it. + + + + + &COMMANDNAME; + + + PREFIX + + + + + + &COMMANDNAME; + + + + + + + &COMMANDNAME; + + + + &COMMANDNAME; + + + + + + + + + DESCRIPTION + + All &COMMANDNAME; does is prompt for a + password and output any given password to standard output. This + is not very useful on its own. This program is really meant to + run as a plugin in the Mandos + client-side system, where it is used as a fallback and + alternative to retrieving passwords from a Mandos server. + + + This program is little more than a getpass3 + wrapper, although actual use of that function is not guaranteed + or implied. + + + + + OPTIONS + + This program is commonly not invoked from the command line; it + is normally started by the Mandos + plugin runner, see plugin-runner8mandos + . Any command line options this program accepts + are therefore normally provided by the plugin runner, and not + directly. + + + + + + + + + Prefix string shown before the password prompt. + + + + + + + + + Enable debug mode. This will enable a lot of output to + standard error about what the program is doing. The + program will still perform all other functions normally. + + + + + + + + + + Gives a help message about options and their meanings. + + + + + + + + + Gives a short usage message. + + + + + + + + + + Prints the program version. + + + + + + + + EXIT STATUS + + If exit status is 0, the output from the program is the password + as it was read. Otherwise, if exit status is other than 0, the + program has encountered an error, and any output so far could be + corrupt and/or truncated, and should therefore be ignored. + + + + + ENVIRONMENT + + + cryptsource + crypttarget + + + If set, these environment variables will be assumed to + contain the source device name and the target device + mapper name, respectively, and will be shown as part of + the prompt. + + + These variables will normally be inherited from + plugin-runner + 8mandos, which will + normally have inherited them from + /scripts/local-top/cryptroot in the + initial RAM disk environment, which will + have set them from parsing kernel arguments and + /conf/conf.d/cryptroot (also in the + initial RAM disk environment), which in turn will have been + created when the initial RAM disk image was created by + /usr/share/initramfs-tools/hooks/cryptroot, by + extracting the information of the root file system from + /etc/crypttab. + + + This behavior is meant to exactly mirror the behavior of + askpass, the default password prompter. + + + + + + + + BUGS + + None are known at this time. + + + + + EXAMPLE + + Note that normally, command line options will not be given + directly, but via options for the Mandos plugin-runner + 8mandos. + + + + Normal invocation needs no options: + + + &COMMANDNAME; + + + + + Show a prefix before the prompt; in this case, a host name. + It might be useful to be reminded of which host needs a + password, in case of KVM switches, etc. + + + + +&COMMANDNAME; --prefix=host.example.org: + + + + + + Run in debug mode. + + + + &COMMANDNAME; --debug + + + + + + SECURITY + + On its own, this program is very simple, and does not exactly + present any security risks. The one thing that could be + considered worthy of note is this: This program is meant to be + run by plugin-runner8mandos + , and will, when run standalone, outside, in a + normal environment, immediately output on its standard output + any presumably secret password it just received. Therefore, + when running this program standalone (which should never + normally be done), take care not to type in any real secret + password by force of habit, since it would then immediately be + shown as output. + + + To further alleviate any risk of being locked out of a system, + the plugin-runner + 8mandos has a fallback + mode which does the same thing as this program, only with less + features. + + + + + SEE ALSO + + crypttab + 5 + mandos-client + 8mandos + plugin-runner + 8mandos, + + +
+ + + + + === added file 'plugins.d/usplash' --- plugins.d/usplash 1970-01-01 00:00:00 +0000 +++ plugins.d/usplash 2008-08-14 02:24:59 +0000 @@ -0,0 +1,42 @@ +#!/bin/sh -e + +# If not on a tty, then get rid of possibly disrupting stderr output +if ! tty -s; then + exec 2>/dev/null +fi + +test -x /sbin/usplash + +usplash="`pidof usplash -o $$`" +test -n "$usplash" + +# We get some variables from cryptsetup: +# $cryptsource the device node, like "/dev/sda3" +# $crypttarget the device mapper name, like "sda3_crypt". + +prompt="Enter passphrase to unlock" +if [ -n "$crypttarget" ]; then + prompt="$prompt the disk $crypttarget" +fi +if [ -n "$cryptsource" ]; then + prompt="$prompt ($cryptsource)" +fi + +splash_input_password(){ + test -p /dev/.initramfs/usplash_outfifo || return 1 + /sbin/usplash_write "INPUTQUIET $1" || return 1 + cat /dev/.initramfs/usplash_outfifo 2> /dev/null || return 1 +} + +# Usplash keeps waiting for input even if some other plugin provided +# the password, so we must kill it +trap "kill -TERM $usplash; sleep 2; kill -KILL $usplash; + kill -TERM $$" TERM HUP + +password="`splash_input_password \"$prompt: \" password`" + +trap - TERM + +/sbin/usplash_write "TIMEOUT 15" + +echo -n "$password" === removed file 'server.cpp' --- server.cpp 2007-10-28 17:59:38 +0000 +++ server.cpp 1970-01-01 00:00:00 +0000 @@ -1,361 +0,0 @@ -extern "C" { -#include //socket, setsockopt, bind, listen, accept, - // inet_ntop, -#include //socket, setsockopt, bind, listen, accept, - // inet_ntop -#include //ioctl, sockaddr_ll, ifreq -#include //write, close -#include // sockaddr_in -#include -#include // gnutls_x509_crt_init, gnutls_x509_crt_import, gnutls_x509_crt_get_dn -#include // inet_ntop, htons -#include //ifreq -} - -#include -#include -#include -#include // std::max -#include // exit() -#include // std::ifstream -#include // std::string -#include // std::map -#include // cout -#include // << - -#define SOCKET_ERR(err,s) if(err<0) {perror(s);exit(1);} - -#define PORT 49001 -#define KEYFILE "key.pem" -#define CERTFILE "cert.pem" -#define CAFILE "ca.pem" -#define CRLFILE "crl.pem" -#define DH_BITS 1024 - -using std::string; -using std::ifstream; -using std::map; -using std::cout; - -/* These are global */ -gnutls_certificate_credentials_t x509_cred; -map table; - -static gnutls_dh_params_t dh_params; - -static int -generate_dh_params () -{ - - /* Generate Diffie Hellman parameters - for use with DHE - * kx algorithms. These should be discarded and regenerated - * once a day, once a week or once a month. Depending on the - * security requirements. - */ - gnutls_dh_params_init (&dh_params); - gnutls_dh_params_generate2 (dh_params, DH_BITS); - - return 0; -} - -gnutls_session_t -initialize_tls_session () -{ - gnutls_session_t session; - - gnutls_global_init (); - - gnutls_certificate_allocate_credentials (&x509_cred); - gnutls_certificate_set_x509_trust_file (x509_cred, CAFILE, - GNUTLS_X509_FMT_PEM); - gnutls_certificate_set_x509_crl_file (x509_cred, CRLFILE, - GNUTLS_X509_FMT_PEM); - gnutls_certificate_set_x509_key_file (x509_cred, CERTFILE, KEYFILE, - GNUTLS_X509_FMT_PEM); - - generate_dh_params (); - gnutls_certificate_set_dh_params (x509_cred, dh_params); - - gnutls_init (&session, GNUTLS_SERVER); - gnutls_set_default_priority (session); - gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred); - - // request client certificate if any. - - gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST); - gnutls_dh_set_prime_bits (session, DH_BITS); - - return session; -} - - -void udpreply(int &sd){ - struct sockaddr_in6 sa_cli; - int ret; - char buffer[512]; - - { - socklen_t sa_cli_len = sizeof(sa_cli); - ret = recvfrom(sd, buffer, 512,0, - reinterpret_cast(& sa_cli), & sa_cli_len); - SOCKET_ERR (ret, "recvfrom"); - } - - if (strncmp(buffer,"Marco", 5) == 0){ - ret = sendto(sd, "Polo", 4, 0, reinterpret_cast(& sa_cli), - sizeof(sa_cli)); - SOCKET_ERR (ret, "sendto"); - } - -} - -void tcpreply(int sd, struct sockaddr_in6 *sa_cli, gnutls_session_t session){ - - int ret; - unsigned int status; - char buffer[512]; - int exit_status = 0; - char dn[128]; - -#define DIE(s){ exit_status = s; goto tcpreply_die; } - - printf ("- TCP connection from %s, port %d\n", - inet_ntop (AF_INET6, &(sa_cli->sin6_addr), buffer, - sizeof (buffer)), ntohs (sa_cli->sin6_port)); - - - gnutls_transport_set_ptr (session, reinterpret_cast (sd)); - - - ret = gnutls_handshake (session); - if (ret < 0) - { - close (sd); - gnutls_deinit (session); - fprintf (stderr, "*** Handshake has failed (%s)\n\n", - gnutls_strerror (ret)); - DIE(1); - } - printf ("- Handshake was completed\n"); - - //time to validate - - if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509){ - printf("Recived certificate not X.509\n"); - DIE(1); - } - { - const gnutls_datum_t *cert_list; - unsigned int cert_list_size = 0; - gnutls_x509_crt_t cert; - size_t size; - - cert_list = gnutls_certificate_get_peers (session, &cert_list_size); - - printf ("Peer provided %d certificates.\n", cert_list_size); - - if (cert_list_size == 0){ - printf("No certificates recived\n"); - DIE(1); - } - - gnutls_x509_crt_init (&cert); - - // XXX -Checking only first cert, might want to check them all - gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER); - - size = sizeof (dn); - gnutls_x509_crt_get_dn (cert, dn, &size); - - printf ("DN: %s\n", dn); - } - - ret = gnutls_certificate_verify_peers2 (session, &status); - - if (ret < 0){ - printf ("Verify failed\n"); - DIE(1); - } - - if (status & (GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_REVOKED)) { - if (status & GNUTLS_CERT_INVALID) { - printf ("The certificate is not trusted.\n"); - } - - if (status & GNUTLS_CERT_SIGNER_NOT_FOUND){ - printf ("The certificate hasn't got a known issuer.\n"); - } - - if (status & GNUTLS_CERT_REVOKED){ - printf ("The certificate has been revoked.\n"); - } - DIE(1); - } - - if (table.find(dn) != table.end()){ - gnutls_record_send (session, table[dn].c_str(), table[dn].size()); - printf("Password sent to client\n"); - } - else { - printf("dn not in list of allowed clients\n"); - } - - - tcpreply_die: - gnutls_bye (session, GNUTLS_SHUT_WR); - close(sd); - gnutls_deinit (session); - gnutls_certificate_free_credentials (x509_cred); - gnutls_global_deinit (); - exit(exit_status); -} - - -void badconfigparser(string file){ - - string dn; - string pw; - string pwfile; - ifstream infile (file.c_str()); - - while(infile){ - getline(infile, dn, '\n'); - if(not infile){ - break; - } - getline(infile, pw, '\n'); - if(not infile){ - break; - } - getline(infile, pwfile, '\n'); - if(not infile){ - break; - } - if(pw.empty()){ - ifstream pwf(pwfile.c_str()); - std::string tmp; - - while(true){ - getline(pwf,tmp); - if (not pwf){ - break; - } - pw = pw + tmp + '\n'; - } - - } - table[dn]=pw; - } - infile.close(); -} - - - -int main (){ - int ret, err, udp_listen_sd, tcp_listen_sd; - struct sockaddr_in6 sa_serv; - struct sockaddr_in6 sa_cli; - - int optval = 1; - socklen_t client_len; - - gnutls_session_t session; - - fd_set rfds_orig; - - badconfigparser(string("clients.conf")); - - session = initialize_tls_session (); - - //UDP IPv6 socket creation - udp_listen_sd = socket (PF_INET6, SOCK_DGRAM, 0); - SOCKET_ERR (udp_listen_sd, "socket"); - - memset (&sa_serv, '\0', sizeof (sa_serv)); - sa_serv.sin6_family = AF_INET6; - sa_serv.sin6_addr = in6addr_any; //XXX only listen to link local? - sa_serv.sin6_port = htons (PORT); /* Server Port number */ - - ret = setsockopt (udp_listen_sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof (optval)); - SOCKET_ERR(ret,"setsockopt reuseaddr"); - - ret = setsockopt(udp_listen_sd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5); - SOCKET_ERR(ret,"setsockopt bindtodevice"); - - { - int flag = 1; - ret = setsockopt(udp_listen_sd, SOL_SOCKET, SO_BROADCAST, & flag, sizeof(flag)); - SOCKET_ERR(ret,"setsockopt broadcast"); - } - - err = bind (udp_listen_sd, reinterpret_cast (& sa_serv), - sizeof (sa_serv)); - SOCKET_ERR (err, "bind"); - - //UDP socket creation done - - - //TCP IPv6 socket creation - - tcp_listen_sd = socket(PF_INET6, SOCK_STREAM, 0); - SOCKET_ERR(tcp_listen_sd,"socket"); - - setsockopt(tcp_listen_sd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 5); - SOCKET_ERR(ret,"setsockopt bindtodevice"); - - ret = setsockopt (tcp_listen_sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof (optval)); - SOCKET_ERR(ret,"setsockopt reuseaddr"); - - err = bind (tcp_listen_sd, reinterpret_cast (& sa_serv), - sizeof (sa_serv)); - SOCKET_ERR (err, "bind"); - - err = listen (tcp_listen_sd, 1024); - SOCKET_ERR (err, "listen"); - - //TCP IPv6 sockets creation done - - FD_ZERO(&rfds_orig); - FD_SET(udp_listen_sd, &rfds_orig); - FD_SET(tcp_listen_sd, &rfds_orig); - - printf ("Server ready. Listening to port '%d' on UDP and TCP.\n\n", PORT); - - for(;;){ - fd_set rfds = rfds_orig; - - ret = select(std::max(udp_listen_sd, tcp_listen_sd)+1, &rfds, 0, 0, 0); - SOCKET_ERR(ret,"select"); - - if (FD_ISSET(udp_listen_sd, &rfds)){ - udpreply(udp_listen_sd); - } - - if (FD_ISSET(tcp_listen_sd, &rfds)){ - client_len = sizeof(sa_cli); - int sd = accept (tcp_listen_sd, - reinterpret_cast (& sa_cli), - &client_len); - SOCKET_ERR(sd,"accept"); //xxx not dieing when just connection abort - switch(fork()){ - case 0: - tcpreply(sd, &sa_cli, session); - return 0; - break; - case -1: - perror("fork"); - close(tcp_listen_sd); - close(udp_listen_sd); - return 1; - break; - default: - break; - } - } - } - - close(tcp_listen_sd); - close(udp_listen_sd); - return 0; - -}