=== removed directory '.bzr-builddeb' === removed file '.bzr-builddeb/default.conf' --- .bzr-builddeb/default.conf 2008-09-17 00:34:09 +0000 +++ .bzr-builddeb/default.conf 1970-01-01 00:00:00 +0000 @@ -1,2 +0,0 @@ -[BUILDDEB] -split = True === removed file '.bzrignore' --- .bzrignore 2010-09-30 06:24:20 +0000 +++ .bzrignore 1970-01-01 00:00:00 +0000 @@ -1,13 +0,0 @@ -*.5 -*.8 -*.8mandos -confdir -keydir -man -plugin-runner -plugins.d/askpass-fifo -plugins.d/mandos-client -plugins.d/password-prompt -plugins.d/splashy -plugins.d/usplash -plugins.d/plymouth === removed file 'COPYING' --- COPYING 2008-08-15 20:17:32 +0000 +++ COPYING 1970-01-01 00:00:00 +0000 @@ -1,676 +0,0 @@ - - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. - === removed file 'DBUS-API' --- DBUS-API 2010-09-26 18:32:58 +0000 +++ DBUS-API 1970-01-01 00:00:00 +0000 @@ -1,172 +0,0 @@ - -*- mode: org; coding: utf-8 -*- - - Mandos Server D-Bus Interface - -This file documents the D-Bus interface to the Mandos server. - -* Bus: System bus - Bus name: "se.bsnet.fukt.Mandos" - - -* Object Paths: - - | Path | Object | - |-----------------------+-------------------| - | "/" | The Mandos Server | - | "/clients/CLIENTNAME" | Mandos Client | - - -* Mandos Server Interface: - Interface name: "se.bsnet.fukt.Mandos" - -** Methods: -*** GetAllClients() → (ao: Clients) - Returns an array of all client D-Bus object paths - -*** GetAllClientsWithProperties() → (a{oa{sv}}: ClientProperties) - Returns an array of all clients and all their properties - -*** RemoveClient(o: ObjectPath) → nothing - Removes a client - -** Signals: -*** ClientAdded(o: ObjectPath) - A new client was added. - -*** ClientNotFound(s: Fingerprint, s: Address) - A client connected from Address using Fingerprint, but was - rejected because it was not found in the server. The fingerprint - is represented as a string of hexadecimal digits. The address is - an IPv4 or IPv6 address in its normal string format. - -*** ClientRemoved(o: ObjectPath, s: Name) - A client named Name on ObjectPath was removed. - - -* Mandos Client Interface: - Interface name: "se.bsnet.fukt.Mandos.Client" - -** Methods -*** Approve(b: Approve) → nothing - Approve or deny a connected client waiting for approval. If - denied, a client will not be sent its secret. - -*** CheckedOK() → nothing - Assert that this client has been checked and found to be alive. - This will restart the timeout before disabling this client. See - also the "LastCheckedOK" property. - -*** Disable() → nothing - Disable this client. See also the "Enabled" property. - -*** Enable() → nothing - Enable this client. See also the "Enabled" property. - -*** StartChecker() → nothing - Start a new checker for this client, if none is currently - running. See also the "CheckerRunning" property. - -*** StopChecker() → nothing - Abort a running checker process for this client, if any. See also - the "CheckerRunning" property. - -** Properties - - Note: Many of these properties directly correspond to a setting in - "clients.conf", in which case they are fully documented in - mandos-clients.conf(5). - - | Name | Type | Access | clients.conf | - |-------------------------+------+------------+---------------------| - | ApprovedByDefault | b | Read/Write | approved_by_default | - | ApprovalDelay (a) | t | Read/Write | approval_delay | - | ApprovalDuration (a) | t | Read/Write | approval_duration | - | ApprovalPending (b) | b | Read | N/A | - | Checker | s | Read/Write | checker | - | CheckerRunning (c) | b | Read/Write | N/A | - | Created (d) | s | Read | N/A | - | Enabled (e) | b | Read/Write | N/A | - | Fingerprint | s | Read | fingerprint | - | Host | s | Read/Write | host | - | Interval (a) | t | Read/Write | interval | - | LastApprovalRequest (f) | s | Read | N/A | - | LastCheckedOK (g) | s | Read/Write | N/A | - | LastEnabled (h) | s | Read | N/A | - | Name | s | Read | (Section name) | - | ObjectPath | o | Read | N/A | - | Secret (i) | ay | Write | secret (or secfile) | - | Timeout (a) | t | Read/Write | timeout | - - a) Represented as milliseconds. - - b) An approval is currently pending. - - c) Setting this property is equivalent to calling StartChecker() or - StopChecker(). - - d) The creation time of this client object, as a RFC 3339 string. - - e) Setting this property is equivalent to calling Enable() or - Disable(). - - f) The time of the last approval request, as a RFC 3339 string, or - an empty string if this has not happened. - - g) The last time a checker was successful, as a RFC 3339 string, or - an empty string if this has not happened. Setting this property - is equivalent to calling CheckedOK(), i.e. the current time is - set, regardless of the string sent. Please always use an empty - string when setting this property, to allow for possible future - expansion. - - h) The last time this client was enabled, as a RFC 3339 string, or - an empty string if this has not happened. - - i) A raw byte array, not hexadecimal digits. - -** Signals -*** CheckerCompleted(n: Exitcode, x: Waitstatus, s: Command) - A checker (Command) has completed. Exitcode is either the exit - code or -1 for abnormal exit. In any case, the full Waitstatus - (as from wait(2)) is also available. - -*** CheckerStarted(s: Command) - A checker command (Command) has just been started. - -*** GotSecret() - This client has been sent its secret. - -*** NeedApproval(t: Timeout, b: ApprovedByDefault) - This client will be approved or denied in exactly Timeout - milliseconds, depending on ApprovedByDefault. Approve() can now - usefully be called on this client object. - -*** PropertyChanged(s: Property, v: Value) - The Property on this client has changed to Value. - -*** Rejected(s: Reason) - This client was not given its secret for a specified Reason. - -* Copyright - - Copyright © 2010 Teddy Hogeborn - Copyright © 2010 Björn Påhlsson - -** License: - - 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 - . - - -#+STARTUP: showall === removed file 'INSTALL' --- INSTALL 2009-02-15 09:28:06 +0000 +++ INSTALL 1970-01-01 00:00:00 +0000 @@ -1,133 +0,0 @@ --*- 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.5 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 - + PyGObject 2.14.2 http://library.gnome.org/devel/pygobject/ - - Strongly recommended: - + fping 2.4b2-to-ipv6 http://www.fping.com/ - - Package names: - python-gnutls avahi-daemon python python-avahi python-dbus - python-ctypes python-gobject - -*** 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. Configure the client to use the correct network interface. The - default is "eth0", and if this needs to be adjusted, it will be - necessary to edit /etc/mandos/plugin-runner.conf to uncomment and - change the line there. If that file is changed, the initrd.img - file must be updated, possibly using the following command: - - # update-initramfs -k all -u - - 5. 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 - - At this point, it is possible to verify that the correct password - will be received by the client by running the command: - - # /usr/lib/mandos/plugins.d/mandos-client \ - --pubkey=/etc/keys/mandos/pubkey.txt \ - --seckey=/etc/keys/mandos/seckey.txt; echo - - This command should retrieve the password from the server, - decrypt it, and output it to standard output. - - 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). - If 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 2010-09-28 18:57:31 +0000 +++ Makefile 2008-08-07 21:45:41 +0000 @@ -1,428 +1,45 @@ -WARN=-O -Wall -Wformat=2 -Winit-self -Wmissing-include-dirs \ - -Wswitch-default -Wswitch-enum -Wunused-parameter \ - -Wstrict-aliasing=1 -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 +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 -Wunreachable-code -Winline -Wvolatile-register-var +DEBUG=-ggdb3 # For info about _FORTIFY_SOURCE, see -# -# and . -FORTIFY=-D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIC -LINK_FORTIFY_LD=-z relro -z now -LINK_FORTIFY= - -# If BROKEN_PIE is set, do not build with -pie -ifndef BROKEN_PIE -FORTIFY += -fPIE -LINK_FORTIFY += -pie -endif +# +FORTIFY=-D_FORTIFY_SOURCE=2 # -fstack-protector-all #COVERAGE=--coverage OPTIMIZE=-Os LANGUAGE=-std=gnu99 -htmldir=man -version=1.2 -SED=sed - -## 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 pkg-config --cflags-only-I gnutls) -GNUTLS_LIBS=$(shell pkg-config --libs gnutls) -AVAHI_CFLAGS=$(shell pkg-config --cflags-only-I avahi-core) -AVAHI_LIBS=$(shell pkg-config --libs avahi-core) -GPGME_CFLAGS=$(shell gpgme-config --cflags; getconf LFS_CFLAGS) -GPGME_LIBS=$(shell gpgme-config --libs; getconf LFS_LIBS; \ - getconf LFS_LDFLAGS) # Do not change these two -CFLAGS=$(WARN) $(DEBUG) $(FORTIFY) $(COVERAGE) $(OPTIMIZE) \ - $(LANGUAGE) $(GNUTLS_CFLAGS) $(AVAHI_CFLAGS) $(GPGME_CFLAGS) \ - -DVERSION='"$(version)"' -LDFLAGS=$(COVERAGE) $(LINK_FORTIFY) $(foreach flag,$(LINK_FORTIFY_LD),-Xlinker $(flag)) - -# Commands to format a DocBook document into a manual page -DOCBOOKTOMAN=$(strip 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' - -DOCBOOKTOHTML=$(strip xsltproc --nonet --xinclude \ - --param make.year.ranges 1 \ - --param make.single.year.ranges 1 \ - --param man.output.quietly 1 \ - --param man.authors.section.enabled 0 \ - --param citerefentry.link 1 \ - --output $@ \ - /usr/share/xml/docbook/stylesheet/nwalsh/xhtml/docbook.xsl \ - $<; $(HTMLPOST) $@) -# Fix citerefentry links -HTMLPOST=$(SED) --in-place \ - --expression='s/\(\)\([^<]*\)\(<\/span>(\)\([^)]*\)\()<\/span><\/a>\)/\1\3.\5\2\3\4\5\6/g' - -PLUGINS=plugins.d/password-prompt plugins.d/mandos-client \ - plugins.d/usplash plugins.d/splashy plugins.d/askpass-fifo \ - plugins.d/plymouth -CPROGS=plugin-runner $(PLUGINS) -PROGS=mandos mandos-keygen mandos-ctl mandos-monitor $(CPROGS) -DOCS=mandos.8 mandos-keygen.8 mandos-monitor.8 mandos-ctl.8 \ - mandos.conf.5 mandos-clients.conf.5 plugin-runner.8mandos \ - plugins.d/mandos-client.8mandos \ - plugins.d/password-prompt.8mandos plugins.d/usplash.8mandos \ - plugins.d/splashy.8mandos plugins.d/askpass-fifo.8mandos \ - plugins.d/plymouth.8mandos - -htmldocs=$(addsuffix .xhtml,$(DOCS)) - -objects=$(addsuffix .o,$(CPROGS)) - -all: $(PROGS) mandos.lsm - -doc: $(DOCS) - -html: $(htmldocs) - -%.5: %.xml common.ent legalnotice.xml - $(DOCBOOKTOMAN) -%.5.xhtml: %.xml common.ent legalnotice.xml - $(DOCBOOKTOHTML) - -%.8: %.xml common.ent legalnotice.xml - $(DOCBOOKTOMAN) -%.8.xhtml: %.xml common.ent legalnotice.xml - $(DOCBOOKTOHTML) - -%.8mandos: %.xml common.ent legalnotice.xml - $(DOCBOOKTOMAN) -%.8mandos.xhtml: %.xml common.ent legalnotice.xml - $(DOCBOOKTOHTML) - -mandos.8: mandos.xml common.ent mandos-options.xml overview.xml \ - legalnotice.xml - $(DOCBOOKTOMAN) -mandos.8.xhtml: mandos.xml common.ent mandos-options.xml \ - overview.xml legalnotice.xml - $(DOCBOOKTOHTML) - -mandos-keygen.8: mandos-keygen.xml common.ent overview.xml \ - legalnotice.xml - $(DOCBOOKTOMAN) -mandos-keygen.8.xhtml: mandos-keygen.xml common.ent overview.xml \ - legalnotice.xml - $(DOCBOOKTOHTML) - -mandos-monitor.8: mandos-monitor.xml common.ent overview.xml \ - legalnotice.xml - $(DOCBOOKTOMAN) -mandos-monitor.8.xhtml: mandos-monitor.xml common.ent overview.xml \ - legalnotice.xml - $(DOCBOOKTOHTML) - -mandos-ctl.8: mandos-ctl.xml common.ent overview.xml \ - legalnotice.xml - $(DOCBOOKTOMAN) -mandos-ctl.8.xhtml: mandos-ctl.xml common.ent overview.xml \ - legalnotice.xml - $(DOCBOOKTOHTML) - -mandos.conf.5: mandos.conf.xml common.ent mandos-options.xml \ - legalnotice.xml - $(DOCBOOKTOMAN) -mandos.conf.5.xhtml: mandos.conf.xml common.ent mandos-options.xml \ - legalnotice.xml - $(DOCBOOKTOHTML) - -plugin-runner.8mandos: plugin-runner.xml common.ent overview.xml \ - legalnotice.xml - $(DOCBOOKTOMAN) -plugin-runner.8mandos.xhtml: plugin-runner.xml common.ent \ - overview.xml legalnotice.xml - $(DOCBOOKTOHTML) - -plugins.d/mandos-client.8mandos: plugins.d/mandos-client.xml \ - common.ent \ - mandos-options.xml \ - overview.xml legalnotice.xml - $(DOCBOOKTOMAN) -plugins.d/mandos-client.8mandos.xhtml: plugins.d/mandos-client.xml \ - common.ent \ - mandos-options.xml \ - overview.xml legalnotice.xml - $(DOCBOOKTOHTML) - -# Update all these files with version number $(version) -common.ent: Makefile - $(strip $(SED) --in-place \ - --expression='s/^\($$/\1$(version)">/' \ - $@) - -mandos: Makefile - $(strip $(SED) --in-place \ - --expression='s/^\(version = "\)[^"]*"$$/\1$(version)"/' \ - $@) - -mandos-keygen: Makefile - $(strip $(SED) --in-place \ - --expression='s/^\(VERSION="\)[^"]*"$$/\1$(version)"/' \ - $@) - -mandos-ctl: Makefile - $(strip $(SED) --in-place \ - --expression='s/^\(version = "\)[^"]*"$$/\1$(version)"/' \ - $@) - -mandos-monitor: Makefile - $(strip $(SED) --in-place \ - --expression='s/^\(version = "\)[^"]*"$$/\1$(version)"/' \ - $@) - -mandos.lsm: Makefile - $(strip $(SED) --in-place \ - --expression='s/^\(Version:\).*/\1\t$(version)/' \ - $@) - $(strip $(SED) --in-place \ - --expression='s/^\(Entered-date:\).*/\1\t$(shell date --rfc-3339=date --reference=Makefile)/' \ - $@) - $(strip $(SED) --in-place \ - --expression='s/\(mandos_\)[0-9.]\+\(\.orig\.tar\.gz\)/\1$(version)\2/' \ - $@) - -plugins.d/mandos-client: plugins.d/mandos-client.c - $(LINK.c) $(GNUTLS_LIBS) $(AVAHI_LIBS) $(GPGME_LIBS) $(strip\ - ) $(COMMON) $^ $(LOADLIBES) $(LDLIBS) -o $@ - -.PHONY : all doc html clean distclean run-client run-server install \ - install-server install-client uninstall uninstall-server \ - uninstall-client purge purge-server purge-client +CFLAGS=$(WARN) $(DEBUG) $(FORTIFY) $(COVERAGE) $(OPTIMIZE) $(LANGUAGE) +LDFLAGS=$(COVERAGE) + +PROGS=mandos-client plugins.d/password-request plugins.d/password-prompt + +objects=$(shell for p in $(PROGS); do echo $${p}.o; done) + +all: $(PROGS) + +mandos-client: mandos-client.o + $(LINK.o) -lgnutls $(COMMON) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +plugins.d/password-request: plugins.d/password-request.o + $(LINK.o) -lgnutls -lavahi-core -lgpgme $(COMMON) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +plugins.d/password-prompt: plugins.d/password-prompt.o + $(LINK.o) $(COMMON) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +.PHONY : all clean distclean run-client run-server clean: - -rm --force $(CPROGS) $(objects) $(htmldocs) $(DOCS) core + -rm --force $(PROGS) $(objects) core distclean: clean mostlyclean: clean maintainer-clean: clean - -rm --force --recursive keydir confdir -check: all +check: all ./mandos --check -# Run the client with a local config and key -run-client: all keydir/seckey.txt keydir/pubkey.txt - @echo "###################################################################" - @echo "# The following error messages are harmless and can be safely #" - @echo "# ignored. The messages are caused by not running as root, but #" - @echo "# you should NOT run \"make run-client\" as root unless you also #" - @echo "# unpacked and compiled Mandos as root, which is NOT recommended. #" - @echo "# From plugin-runner: setuid: Operation not permitted #" - @echo "# From askpass-fifo: mkfifo: Permission denied #" - @echo "# From mandos-client: setuid: Operation not permitted #" - @echo "# seteuid: Operation not permitted #" - @echo "# klogctl: Operation not permitted #" - @echo "###################################################################" - ./plugin-runner --plugin-dir=plugins.d \ - --config-file=plugin-runner.conf \ - --options-for=mandos-client:--seckey=keydir/seckey.txt,--pubkey=keydir/pubkey.txt \ - $(CLIENTARGS) - -# 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 - @echo "#################################################################" - @echo "# NOTE: Please IGNORE the error about \"Could not open file #" - @echo "# u'/var/run/mandos.pid'\" - it is harmless and is caused by #" - @echo "# the server not running as root. Do NOT run \"make run-server\" #" - @echo "# server as root if you didn't also unpack and compile it thus. #" - @echo "#################################################################" - ./mandos --debug --no-dbus --configdir=confdir $(SERVERARGS) - -# 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-nokey - -install-html: html - install --directory $(htmldir) - install --mode=u=rw,go=r --target-directory=$(htmldir) \ - $(htmldocs) - -install-server: doc - install --directory $(CONFDIR) - install --mode=u=rwx,go=rx mandos $(PREFIX)/sbin/mandos - install --mode=u=rwx,go=rx --target-directory=$(PREFIX)/sbin \ - mandos-ctl - install --mode=u=rwx,go=rx --target-directory=$(PREFIX)/sbin \ - mandos-monitor - install --mode=u=rw,go=r --target-directory=$(CONFDIR) \ - mandos.conf - install --mode=u=rw --target-directory=$(CONFDIR) \ - clients.conf - install --mode=u=rw,go=r dbus-mandos.conf \ - $(DESTDIR)/etc/dbus-1/system.d/mandos.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 - if [ -z $(DESTDIR) ]; then \ - update-rc.d mandos defaults 25 15;\ - fi - gzip --best --to-stdout mandos.8 \ - > $(MANDIR)/man8/mandos.8.gz - gzip --best --to-stdout mandos-monitor.8 \ - > $(MANDIR)/man8/mandos-monitor.8.gz - gzip --best --to-stdout mandos-ctl.8 \ - > $(MANDIR)/man8/mandos-ctl.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-nokey: all doc - install --directory $(PREFIX)/lib/mandos $(CONFDIR) - install --directory --mode=u=rwx $(KEYDIR) \ - $(PREFIX)/lib/mandos/plugins.d - if [ "$(CONFDIR)" != "$(PREFIX)/lib/mandos" ]; then \ - install --mode=u=rwx \ - --directory "$(CONFDIR)/plugins.d"; \ - 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=rwxs,go=rx \ - --target-directory=$(PREFIX)/lib/mandos/plugins.d \ - plugins.d/usplash - install --mode=u=rwxs,go=rx \ - --target-directory=$(PREFIX)/lib/mandos/plugins.d \ - plugins.d/splashy - install --mode=u=rwxs,go=rx \ - --target-directory=$(PREFIX)/lib/mandos/plugins.d \ - plugins.d/askpass-fifo - install --mode=u=rwxs,go=rx \ - --target-directory=$(PREFIX)/lib/mandos/plugins.d \ - plugins.d/plymouth - install initramfs-tools-hook \ - $(INITRAMFSTOOLS)/hooks/mandos - install --mode=u=rw,go=r initramfs-tools-hook-conf \ - $(INITRAMFSTOOLS)/conf-hooks.d/mandos - install initramfs-tools-script \ - $(INITRAMFSTOOLS)/scripts/init-premount/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/mandos-client.8mandos \ - > $(MANDIR)/man8/mandos-client.8mandos.gz - gzip --best --to-stdout plugins.d/password-prompt.8mandos \ - > $(MANDIR)/man8/password-prompt.8mandos.gz - gzip --best --to-stdout plugins.d/usplash.8mandos \ - > $(MANDIR)/man8/usplash.8mandos.gz - gzip --best --to-stdout plugins.d/splashy.8mandos \ - > $(MANDIR)/man8/splashy.8mandos.gz - gzip --best --to-stdout plugins.d/askpass-fifo.8mandos \ - > $(MANDIR)/man8/askpass-fifo.8mandos.gz - gzip --best --to-stdout plugins.d/plymouth.8mandos \ - > $(MANDIR)/man8/plymouth.8mandos.gz - -install-client: install-client-nokey -# 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 \ - $(PREFIX)/sbin/mandos-ctl \ - $(PREFIX)/sbin/mandos-monitor \ - $(MANDIR)/man8/mandos.8.gz \ - $(MANDIR)/man8/mandos-monitor.8.gz \ - $(MANDIR)/man8/mandos-ctl.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 \ - $(PREFIX)/lib/mandos/plugins.d/splashy \ - $(PREFIX)/lib/mandos/plugins.d/askpass-fifo \ - $(PREFIX)/lib/mandos/plugins.d/plymouth \ - $(INITRAMFSTOOLS)/hooks/mandos \ - $(INITRAMFSTOOLS)/conf-hooks.d/mandos \ - $(INITRAMFSTOOLS)/scripts/init-premount/mandos \ - $(MANDIR)/man8/mandos-keygen.8.gz \ - $(MANDIR)/man8/plugin-runner.8mandos.gz \ - $(MANDIR)/man8/mandos-client.8mandos.gz - $(MANDIR)/man8/password-prompt.8mandos.gz \ - $(MANDIR)/man8/usplash.8mandos.gz \ - $(MANDIR)/man8/splashy.8mandos.gz \ - $(MANDIR)/man8/askpass-fifo.8mandos.gz \ - $(MANDIR)/man8/plymouth.8mandos.gz \ - -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/dbus-1/system.d/mandos.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) +run-client: all + ./mandos-client --plugin-dir=plugins.d --options-for=password-request:--keydir=keydir + +run-server: all + ./mandos --debug --configdir=. === removed file 'NEWS' --- NEWS 2010-09-28 18:57:31 +0000 +++ NEWS 1970-01-01 00:00:00 +0000 @@ -1,154 +0,0 @@ -This NEWS file records noteworthy changes, very tersely. -See the manual for detailed information. - -Version 1.2 (2010-09-28) -* Client: -** New "plymouth" plugin to ask for a password using the Plymouth - graphical boot system. -** The Mandos client now automatically chooses a network interface if - the DEVICE setting in /etc/initramfs-tools/initramfs.conf is set to - the empty string. This is also the new default instead of "eth0". -** The Mandos client --connect option now loops indefinitely until a - password is received from the specified server. -** Bug fix: Quote directory correctly in mandos-keygen with --password -** Bug fix: don't use "echo -e" in mandos-keygen; unsupported by dash. -* Server: -** Terminology change: clients are now "ENABLED" or "DISABLED", not - "valid" or "invalid". -** New D-Bus API; see the file "DBUS-API". -** New control utilities using the new D-Bus API: - + mandos-ctl A command-line based utility - + mandos-monitor A text-based GUI interface -** New feature: manual interactive approval or denying of clients on a - case-by-case basis. -** New --debuglevel option to control logging -** Will not write PID file if --debug is passed -** Bug fix: Avoid race conditions with short "interval" values or - fast checkers. -** Bug fix: Don't try to bind to a network interface when none is - specified - -Version 1.0.14 (2009-10-25) -Enable building without -pie and -fPIE if BROKEN_PIE is set. - -Version 1.0.13 (2009-10-22) -* Client -** Security bug fix: If Mandos server is also installed, do not copy - its config files (with encrypted passwords) into the initrd.img-* - files. - -Version 1.0.12 (2009-09-17) -* Client -** Bug fix: Allow network interface renaming by "udev" by taking down - the network interface after using it. -** Bug fix: User-supplied plugins are now installed correctly. -** Bug fix: If usplash was used but the password was instead provided - by the Mandos server, the usplash daemon used to ignore the first - command passed to it. This has been fixed. -** Bug fix: Make the "--userid" and "--groupid" options in - "plugin-runner.conf" work. -* Server -** Bug fix: Fix the LSB header in the init.d script to make dependency - based booting work. -** A client receiving its password now also counts as if a checker was - run successfully (i.e. the timeout timer is reset). - -Version 1.0.11 (2009-05-23) -* Client -** Bug fix: Use "pkg-config" instead of old "libgnutls-config". - -Version 1.0.10 (2009-05-17) -* Client -** Security bug fix: Fix permissions on initrd.img-*.bak files when - upgrading from older versions. - -Version 1.0.9 (2009-05-17) -* Client -** Security bug fix: Fix permissions on initrd.img file when - installing new linux-image-* packages calling mkinitramfs-kpkg (all - version lower than 2.6.28-1-* does this). - -Version 1.0.8 (2009-02-25) -* Client -** Bug fix: Fix missing quote characters in initramfs-tools-hook. - -Version 1.0.7 (2009-02-24) -* Client -** Bug fix: Do not depend on GNU awk. - -Version 1.0.6 (2009-02-13) -* Server -** Fix bug where server would stop responding, with a zombie checker -** Support for disabling IPv6 (only for advanced users) -** Fix bug which made server not change group ID - -* Client -** Bug fix: Fix permission for /lib64 (on relevant architechtures). -** Add support for IPv4 addresses. -** Add support in mandos-client for not bringing up a network - interface by specifying an empty string to "--interface". -** Make password prompt on boot not be mangled by kernel log messages - about network interface. -** Get network interface from initramfs.conf and/or from kernel - command line. -** If set by "ip=" kernel command line, configure network on boot. -** Support connecting directly using "mandos=connect" kernel command. - line option, provided network is configured using "ip=". -** Fix bug which made plugin-runner and mandos-client not change group - ID. -** Fix bug where the "--options-for" option of plugin-runner would - truncate the value at the first colon character. -** Fix bug where plugin-runner would not go to fallback if all plugins - failed. -** Fix bug where mandos-client would not clean temporary directory on - a signal or on certain file systems. -** Bug fix: remove bashism in /bin/sh script "mandos-keygen". - -Version 1.0.5 (2009-01-17) -* Client -** Fix small memory leak in plugin-runner. - -Version 1.0.4 (2009-01-15) -* Server -** Only find matched user/group pairs when searching for suitable - nonprivileged user/group to switch to. - -* Client -** New kernel parameter "mandos=off" makes client not run at boot. -** Fix linking errors and compilation warnings on AMD64. -** Parse numbers in command line options better. -** The splashy and usplash plugins are more robust while traversing - /proc, and will not abort if a process suddenly disappears. - -Version 1.0.3 (2009-01-06) -* Server -** Now tries to change to user and group "_mandos" before falling back - to trying the old values "mandos", "nobody:nogroup", and "65534". -** Now does not abort on startup even if no clients are defined in - clients.conf. - -* Client -** Plugins named "*.dpkg-bak" are now ignored. -** Hopefully fixed compilation failure on some architectures where the - C compiler does not recognize the "-z" option as a linker option. - -Version 1.0.2 (2008-10-17) -* mandos-keygen now signs the encrypted key blobs. This signature is - not currently verified by mandos-client, but this may change in the - future. - -Version 1.0.1 (2008-10-07) -* Server -** Expand environment variables and ~user in clients.conf's "secfile" - The "secfile" option in /etc/mandos/clients.conf now expands - "~user/foo" and "$ENVVAR" strings. - -* Client (plugin-runner, plugins, etc.) -** Manual pages for the usplash, splashy, and askpass-fifo plugins. - All plugins now have man pages. -** More secure compilation and linking flags. - All programs are now compiled with "-fstack-protector-all -fPIE - -pie", and linked using "-z relro -pie" for additional security. - -* There is now a "NEWS" file (this one), giving a history of - noteworthy changes. === removed file 'README' --- README 2010-09-26 18:32:58 +0000 +++ README 1970-01-01 00:00:00 +0000 @@ -1,182 +0,0 @@ --*- 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 - Mandos client computer 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, - and do all this *before* the Mandos server timeout kicks in and the - Mandos server refuses to give out the key to anyone. - - Now, as the typical procedure 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 reboot 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. - -* The Plugin System - In the early designs, the mandos-client(8mandos) program (which - retrieves a password from the Mandos server) also prompted for a - password on the terminal, in case a Mandos server could not be - found. Other ways of retrieving a password could easily be - envisoned, but this multiplicity of purpose was seen to be too - complex to be a viable way to continue. Instead, the original - program was separated into mandos-client(8mandos) and - password-prompt(8mandos), and a plugin-runner(8mandos) exist to run - them both in parallel, allowing the first successful plugin to - provide the password. This opened up for any number of additional - plugins to run, all competing to be the first to find a password and - provide it to the plugin runner. - - Four additional plugins are provided: - * plymouth(8mandos) - This prompts for a password when using plymouth(8). - * usplash(8mandos) - This prompts for a password when using usplash(8). - * splashy(8mandos) - This prompts for a password when using splashy(8). - * askpass-fifo(8mandos) - To provide compatibility with the "askpass" program from - cryptsetup, this plugin listens to the same FIFO as askpass would - do. - - More plugins can easily be written and added by the system - administrator; see the section called "WRITING PLUGINS" in - plugin-runner(8mandos) to learn the plugin requirements. - -* Copyright - - Copyright © 2008-2010 Teddy Hogeborn - Copyright © 2008-2010 Björn Påhlsson - -** License: - - 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 - . === modified file 'TODO' --- TODO 2010-09-26 18:32:58 +0000 +++ TODO 2008-08-07 21:45:41 +0000 @@ -1,134 +1,60 @@ -*- org -*- -* Use _attribute_((nonnull)) wherever possible. - -* mandos-client -** TODO [#B] use scandir(3) instead of readdir(3) -** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name -** TODO [#B] Retry a server which has a non-definite reply: -*** A closed connection during the TLS handshake -*** A TCP timeout -** TODO [#B] Use capabilities instead of seteuid(). -** TODO [#A] Retry --connect forever - -* splashy -** TODO [#B] use scandir(3) instead of readdir(3) -** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name - -* usplash -** TODO [#A] Make it work again -** TODO [#B] use scandir(3) instead of readdir(3) -** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name -** TODO Use [[info:libc:Argz%20Functions][argz_extract]] - -* askpass-fifo -** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name -** TODO [#B] Drop privileges after opening FIFO. - -* password-prompt -** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name -** TODO [#B] lock stdin (with flock()?) - -* TODO [#B] passdev - -* plugin-runner -** TODO [#B] use scandir(3) instead of readdir(3) -** TODO [#C] use same file name rules as run-parts(8) -** kernel command line option for debug info -** TODO [#B] Use openat() - -* mandos (server) -** TODO [#B] Log level :BUGS: -** TODO Persistent state :BUGS: - /var/lib/mandos/* -*** TODO /etc/mandos/clients.d/*.conf - Watch this directory and add/remove/update clients? -** TODO [#C] config for TXT record -** TODO Log level option - syslogger.setLevel(logging.WARNING) - + SetLogLevel D-Bus call -** TODO Implement --foreground :BUGS: - [[info:standards:Option%20Table][Table of Long Options]] -** TODO Implement --socket - [[info:standards:Option%20Table][Table of Long Options]] -** TODO Date+time on console log messages :BUGS: - Is this the default? -** TODO [#C] DBusServiceObjectUsingSuper -** TODO [#B] Global enable/disable flag -** TODO [#B] By-client countdown on secrets given -** TODO [#B] Fix problem with fsck taking a really long time - Whenever a client successfully gets a secret it could get a - one-time timeout boost to allow for an fsck-incurred delay -** TODO [#A] Delay before client receives key - This would give an operator opportunity to cancel the request if - desired. -** TODO [#A] Client manual approval mode - A client needs manual approval on the server before it gets the - secret -** TODO [#B] Support RFC 3339 time duration syntax -** More D-Bus methods -*** NeedsApproval(50, True) -> timeout, default approve - Default approval is configurable, but True by default - + Approve(True) -> approve sending saved - + Approve(False) -> Close client connection immediately -*** NeedsPassword(50) - Timeout, default disapprove - + SetPass(u"gazonk", True) -> Approval, persistent - + Approve(False) -> Close client connection immediately -** TODO [#C] python-parsedatetime -** TODO [#C] systemd/launchd - http://0pointer.de/blog/projects/systemd.html -** TODO Separate logging logic to own object -** TODO make clients to a dict! -** TODO [#A] Limit approval_delay to max gnutls/tls timeout value -** TODO [#B] break the wait on approval_delay if connection dies -** TODO Generate Client.runtime_expansions from client options + extra -** TODO Allow %%(checker)s as a runtime expansion - -* mandos.xml -** Add mandos contact info in manual pages - -* mandos-ctl -*** Handle "no D-Bus server" and/or "no Mandos server found" better -*** [#B] --dump option -** TODO Support RFC 3339 time duration syntax - -* TODO mandos-dispatch - Listens for specified D-Bus signals and spawns shell commands with - arguments. - -* mandos-monitor -** TODO help should be toggable -** Urwid client data displayer - Better view of client data in the listing -*** Properties popup -** Nicer crashes. Stack traces Messes up shell. -*** Print a nice "We are sorry" message, save stack trace to log. -** Show timeout countdown for approval - -* mandos-keygen -** TODO Loop until passwords match when run interactively -** TODO "--secfile" option - Using the "secfile" option instead of "secret" -** TODO [#B] "--test" option - For testing decryption before rebooting. - -* Makefile -** TODO Add "--Xlinker --as-needed" - http://udrepper.livejournal.com/19395.html -** TODO [#C] Implement DEB_BUILD_OPTIONS - http://www.debian.org/doc/debian-policy/ch-source.html#s-debianrules-options - -* Package -** /usr/share/initramfs-tools/hooks/mandos -*** TODO [#C] use same file name rules as run-parts(8) -*** TODO [#C] Do not install in initrd.img if configured not to. - Use "/etc/initramfs-tools/hooksconf.d/mandos"? -** TODO [#C] /etc/bash_completion.d/mandos - From XML sources directly? - -* Side Stuff -** TODO Locate which packet move the other bin/sh when busy box is deactivated -** TODO contact owner of packet, and ask them to have that shell static in position regardless of busybox +* README file + +* COPYING file + [[file:/usr/share/common-licenses/GPL-3][GPLv3]] + +* Mandos-client +** [#A] Man page: man8/mandos-client.8mandos +** [#A] check return codes of all system calls +** [#B] header files/symbols tally +** use strsep instead of strtok? +** use config file in addition to arguments +** pass things in environment, like device name, etc + +* Password-request +** [#A] Man page: man8/password-request.8mandos +** [#A] check return codes of all system calls +** [#B] header files/symbols tally +** IPv4 support +** use strsep instead of strtok? +** Do not depend on GPG key rings on disk + This would mean creating new GPG key rings with GPGME by importing + the key files from scratch on every program start. + +* Password-prompt +** [#A] Man page: man8/password-prompt.8mandos +** Use getpass(3)? + [[info:libc:getpass][GNU LibC Manual: Reading Passwords]] + +* Server +** [#A] Command man page: man8/mandos.8 +** [#A] Config file man page: man5/mandos.conf (mandos.conf) +** [#A] Config file man page: man5/mandos-clients.conf (clients.conf) +** [#A] write PID file +** [#A] /etc/init.d/mandos-server +** Better comments in config files +** Log level +** /etc/mandos/clients.d/*.conf + Watch this directory and add/remove/update clients? +** config for TXT record +** Run-time communication with server + probably using D-Bus +** Implement --foreground + [[info:standards:Option%20Table][Table of Long Options]] +** Implement --socket + [[info:standards:Option%20Table][Table of Long Options]] + +* Mandos-tools/utilities + All of this probably using D-Bus +** List clients +** Enable client +** Disable client + +* Installer +** [#A] Change initrd.img file to not be publically readable +** [#A] Create GPG key ring files in initrd #+STARTUP: showall === modified file 'clients.conf' --- clients.conf 2010-09-12 03:00:40 +0000 +++ clients.conf 2008-07-29 03:35:39 +0000 @@ -1,79 +1,33 @@ -# 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 disabled and not be allowed 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 disabled, -# and any running checker killed. -;interval = 5m - -# What command to run as "the checker". -;checker = fping -q -- %%(host)s - -# Whether to approve a client by default after the approval delay. -;approved_by_default = True - -# How long to wait for approval. -;approval_delay = 0s - -# How long one approval will last. -;approval_duration = 1s - - -;#### -;# 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.bin -; -;# 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 -; -;# This client requires manual approval before it receives its secret. -;approved_by_default = False -;# Require approval within 30 seconds. -;approval_delay = 30s -;#### +timeout = 1h +interval = 5m +checker = fping -q -- %%(fqdn)s + +# Example +[foo] +fingerprint = 3e393aeaefb84c7e89e2f547b3a107558fca3a27 +secret = Base+64+encoded+OpenPGP+encrypted+data/= +# secfile = /etc/mandos/foo-secret.txt.asc +fqdn = foo.example.org +checker = fping -q -- %%(fqdn)s +timeout = 10m + +[braxen_client] +fingerprint = 7788 2722 5BA7 DE53 9C5A 7CFA 59CF F7CD BD9A 5920 +secret = + hQIOA6QdEjBs2L/HEAf/TCyrDe5Xnm9esa+Pb/vWF9CUqfn4srzVgSu234REJMVv + 7lBSrPE2132Lmd2gqF1HeLKDJRSVxJpt6xoWOChGHg+TMyXDxK+NXl89vGvdU1Xf + hKkVm9MDLOgT5ECDPysDGHFPDhqHOSu3Kaw2DWMV/iH9vz3Z20erVNbdcvyBnuoj + coWO/6yfB5EQO0BXp7kcyy00USA3CjD5FGZdoQGITb8A/ar0tVA5crSQmaSotm6K + mNLhrFnZ5BxX+TiE+eTUTqSloWRY6VAvqWQHC7OASxK5E6RXPBuFH5IohUA2Qbk5 + AHt99pYvsIPX88j2rWauOokoiKZot/9leJ8VxO5l3wf/U64IH8bkPIoWmWZfd/nq + h4uwGNbCgKMyT+AnvH7kMJ3i7DivfWl2mKLV0PyPHUNva0VQxX6yYjcOhj1R6fCr + /at8/NSLe2OhLchzdC+Ls9h+kvJXgF8Sisv+Wk/1RadPLFmraRlqvJwt6Ww21Lpi + XqXHV2mIgqWnR98YgSvUi3TJHrUQiNc9YyBzuRo0AjgG2C9qiE3FM+Y28+iQ/sR3 + +bFszYuZKVTObqiIslwXu7imO0cvvFRgJF/6u3HNFQ4LUTGhiM3FQmC6NNlF3/vJ + M2hwRDMcJqDd54Twx90Wh+tYz0z7QMsK4ANXWHHWHR0JchnLWmenzbtW5MHdW9AY + sNJZAQSOpirE4Xi31CSlWAi9KV+cUCmWF5zOFy1x23P6PjdaRm4T2zw4dxS5NswX + WU0sVEXxjs6PYxuIiCTL7vdpx8QjBkrPWDrAbcMyBr2OQlnHIvPzEArRQLo= + =iHhv +fqdn = localhost +interval = 5m === removed file 'common.ent' --- common.ent 2010-09-28 18:57:31 +0000 +++ common.ent 1970-01-01 00:00:00 +0000 @@ -1,3 +0,0 @@ - - - === removed file 'dbus-mandos.conf' --- dbus-mandos.conf 2009-11-09 07:35:16 +0000 +++ dbus-mandos.conf 1970-01-01 00:00:00 +0000 @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - === removed directory 'debian' === removed file 'debian/changelog' --- debian/changelog 2010-09-28 18:57:31 +0000 +++ debian/changelog 1970-01-01 00:00:00 +0000 @@ -1,174 +0,0 @@ -mandos (1.2-1) unstable; urgency=low - - * New upstream release. - * Makefile (LINK_FORTIFY_LD): Remove "-fPIE". (Closes: #557076) - * debian/control: Add gnupg dependency to "mandos-client" and removed it - from "mandos". Added dependency on "python-urwid" "mandos" since the - new "mandos-monitor" utility needs it, and on "python (>=2.6) | - python-multiprocessing" since the Mandos server now uses it. - * debian/rules: Set BROKEN_PIE on mips and mipsel if a known buggy - version of binutils is used. - * debian/mandos.docs: Also install "/usr/share/doc/mandos/DBUS-API". - * debian/mandos.dirs: Added "etc/dbus-1/system.d". - * debian/mandos-client.README.Debian: Update info about DEVICE setting - of initramfs.conf. - * debian/mandos-client.README.Debian: Remove warning about --connect not - looping, since it now does. - - -- Teddy Hogeborn Tue, 28 Sep 2010 20:46:11 +0200 - -mandos (1.0.14-1) unstable; urgency=low (HIGH on mips and mipsel) - - * New upstream release. - * debian/rules: Build with BROKEN_PIE set on mips and mipsel - architectures - fixes FTBFS there. - - -- Teddy Hogeborn Sun, 25 Oct 2009 20:10:09 +0100 - -mandos (1.0.13-1) unstable; urgency=high - - * New upstream release. - * Do not copy unnecessary files to initrd (Closes: #551907) - - -- Teddy Hogeborn Thu, 22 Oct 2009 00:53:21 +0200 - -mandos (1.0.12-1) unstable; urgency=low - - * New upstream release. - * init.d-mandos: Correct dependencies (Closes: #546928) - * debian/control (Standards-Version): Changed to "3.8.3". - * debian/mandos-client.README.Debian: Improved wording and formatting. - Updated location of nfsroot.txt. - * debian/mandos.README.Debian: Improved wording and formatting. - * debian/mandos-client.postinst (configure): Don't look for user and - group with the old name if upgrading from a new enough version. - * debian/mandos.postinst (configure): - '' - - * debian/mandos-client.README.Debian: Added text about non-usability of - pseudo-network interfaces. - - -- Teddy Hogeborn Thu, 17 Sep 2009 15:03:59 +0200 - -mandos (1.0.11-1) unstable; urgency=low - - * debian/control (Standards-Version): Changed to "3.8.1". - * Makefile (GNUTLS_CFLAGS, GNUTLS_CFLAGS): Use "pkg-config" instead of - the old "libgnutls-config" script. (Closes: #529836) - - -- Teddy Hogeborn Sat, 23 May 2009 07:12:20 +0200 - -mandos (1.0.10-1) unstable; urgency=low - - * New upstream release. - * debian/mandos-client.postinst (update_initramfs): Fix permissions of - old initrd.img-*.bak files. - - -- Teddy Hogeborn Sun, 17 May 2009 04:56:35 +0200 - -mandos (1.0.9-1) unstable; urgency=low - - * New upstream release. - - -- Teddy Hogeborn Sun, 17 May 2009 02:59:45 +0200 - -mandos (1.0.8-1) unstable; urgency=low - - * New upstream release. - - -- Teddy Hogeborn Wed, 25 Feb 2009 02:26:57 +0100 - -mandos (1.0.7-1) unstable; urgency=low - - * New upstream release. - - -- Teddy Hogeborn Tue, 24 Feb 2009 12:58:06 +0100 - -mandos (1.0.6-1) unstable; urgency=low - - * New upstream release. - * debian/mandos-client.postinst: Converted to Bourne shell. Also - minor message change. - * debian/mandos-client.postrm: Minor message change. - * debian/mandos.postinst: Converted to Bourne shell. Also minor - message change. - * debian/mandos.prerm: Minor message change. - * debian/rules (install-indep): Removed "--no-start" from - dh_installinit. - * debian/mandos-client.lintian-overrides: Remove obsolete override for - unbreakable line in plugin-runner manual page. - * debian/control (mandos/Depends): Added "python-gobject". - * debian/mandos-client.dirs: Change - "usr/share/initramfs-tools/scripts/local-top" to - "usr/share/initramfs-tools/scripts/init-premount". - * debian/mandos-client.README.Debian: Add reference to initramfs.conf - and nfsroot.txt. New section about the new non-local connection - feature. - - -- Teddy Hogeborn Fri, 13 Feb 2009 09:27:25 +0100 - -mandos (1.0.5-1) unstable; urgency=low - - * New upstream release. - - -- Teddy Hogeborn Sat, 17 Jan 2009 02:26:00 +0100 - -mandos (1.0.4-1) unstable; urgency=low - - * New upstream release. - * debian/watch: New file. - * debian/mandos-client.README.Debian: Document new "mandos=off" kernel - parameter. - - -- Teddy Hogeborn Thu, 15 Jan 2009 05:49:22 +0100 - -mandos (1.0.3-2) unstable; urgency=low - - * Removed some now-unused debconf files. - * Changed postinst scripts to not source debconf/confmodule. - * Removed po-debconf from build-depends. - - -- Teddy Hogeborn Tue, 06 Jan 2009 21:28:20 +0100 - -mandos (1.0.3-1) unstable; urgency=low - - * New upstream release. - * Add -Xlinker to linker flags to fix FTBFS for some architectures. - Thanks to Thiemo Seufer for the report and - fix. (Closes: #509398) - * Remove debconf use altogether, thereby stopping debconf abuse. Thanks - to Christian Perrier . (Closes: #509653) - * Add NEWS file to /usr/share/doc directories. - * Use and create "_mandos" user+group. Rename old user+group created by - older versions of this package. - * Fix manual pages by adding build-depend on "docbook-xml". - - -- Teddy Hogeborn Tue, 06 Jan 2009 01:21:20 +0100 - -mandos (1.0.2-1) unstable; urgency=low - - * New upstream release. - * debian/copyright: Rewritten to conform to - . - - -- Teddy Hogeborn Fri, 17 Oct 2008 20:42:12 +0200 - -mandos (1.0.1-1) unstable; urgency=low - - * New upstream release. - * Separate /usr/share/doc/mandos-client/README.Debian into sections with - headlines. Add instructions on how to test the server and verify the - password. - - -- Teddy Hogeborn Tue, 07 Oct 2008 23:07:23 +0200 - -mandos (1.0-2) unstable; urgency=low - - * Added comments in debian/*.lintian-overrides files. Added Debian - revison number to version number. - - -- Teddy Hogeborn Wed, 01 Oct 2008 17:23:35 +0200 - -mandos (1.0-1) unstable; urgency=low - - * Initial Release. (Closes: #500727). - - -- Teddy Hogeborn Tue, 30 Sep 2008 21:58:43 +0200 === removed file 'debian/compat' --- debian/compat 2008-09-17 00:34:09 +0000 +++ debian/compat 1970-01-01 00:00:00 +0000 @@ -1,1 +0,0 @@ -7 === removed file 'debian/control' --- debian/control 2010-10-01 18:40:55 +0000 +++ debian/control 1970-01-01 00:00:00 +0000 @@ -1,54 +0,0 @@ -Source: mandos -Section: admin -Priority: extra -Maintainer: Mandos Maintainers -Uploaders: Teddy Hogeborn , - Björn Påhlsson -Build-Depends: debhelper (>= 7), docbook-xml, docbook-xsl, - libavahi-core-dev, libgpgme11-dev, libgnutls-dev, xsltproc, - pkg-config -Standards-Version: 3.9.1 -Vcs-Bzr: http://ftp.fukt.bsnet.se/pub/mandos/trunk -Vcs-Browser: http://bzr.fukt.bsnet.se/loggerhead/mandos/trunk/files -Homepage: http://www.fukt.bsnet.se/mandos - -Package: mandos -Architecture: all -Depends: ${misc:Depends}, python (>=2.5), python-gnutls, python-dbus, - python-avahi, python-gobject, avahi-daemon, adduser, - python-urwid, python (>=2.6) | python-multiprocessing -Recommends: fping -Description: a server giving encrypted passwords to Mandos clients - This is the server part of the Mandos system, which allows - 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. All network communication is encrypted using TLS. - The clients are identified by the server using an 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. - -Package: mandos-client -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, cryptsetup, - gnupg (<< 2) -Enhances: cryptsetup -Description: do unattended reboots with an encrypted root file system - This is the client part of the Mandos system, which allows - 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. All network communication is encrypted using TLS. - The clients are identified by the server using an 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. === removed file 'debian/copyright' --- debian/copyright 2010-09-26 18:32:58 +0000 +++ debian/copyright 1970-01-01 00:00:00 +0000 @@ -1,26 +0,0 @@ -Format-Specification: - http://wiki.debian.org/Proposals/CopyrightFormat?action=recall&rev=233 -Upstream-Name: Mandos -Upstream-Maintainer: Mandos Maintainers -Upstream-Source: - -Files: * -Copyright: Copyright © 2008-2010 Teddy Hogeborn -Copyright: Copyright © 2008-2010 Björn Påhlsson -License: GPL-3+ - 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 - . - . - On Debian systems, the complete text of the GNU General Public - License can be found in "/usr/share/common-licenses/GPL". === removed file 'debian/mandos-client.README.Debian' --- debian/mandos-client.README.Debian 2010-09-27 17:53:53 +0000 +++ debian/mandos-client.README.Debian 1970-01-01 00:00:00 +0000 @@ -1,86 +0,0 @@ -* Choose the Client Network Interface - - Please make sure that the correct network interface is specified in - the DEVICE setting in the "/etc/initramfs-tools/initramfs.conf" - file. If the setting is empty, the interface will be autodetected - at boot time, which may not be correct. *If* the DEVICE setting is - changed, it will be necessary to update the initrd image by running - the command - - update-initramfs -k all -u - - The device can be overridden at boot time on the Linux kernel - command line using the sixth colon-separated field of the "ip=" - option; for exact syntax, read the documentation in the file - "/usr/share/doc/linux-doc-*/Documentation/filesystems/nfsroot.txt", - available in the "linux-doc-*" package. - - Note that since this network interface is used in the initial RAM - disk environment, the network interface *must* exist at that stage. - Thus, the interface can *not* be a pseudo-interface such as "br0" or - "tun0"; instead, a real interface (such as "eth0") must be used. - -* Adding a Client Password to the Server - - The server must be given a password to give back to the client on - boot time. This password must be a one which can be used to unlock - the root file system device. On the *client*, run this command: - - mandos-keygen --password - - It will prompt for a password and output a config file section. - This output should be copied to the Mandos server and added to the - file "/etc/mandos/clients.conf" there. - -* Testing that it Works (Without Rebooting) - - After the server has been started with this client's key added, it - is possible to verify that the correct password will be received by - this client by running the command, on the client: - - /usr/lib/mandos/plugins.d/mandos-client \ - --pubkey=/etc/keys/mandos/pubkey.txt \ - --seckey=/etc/keys/mandos/seckey.txt; echo - - This command should retrieve the password from the server, decrypt - it, and output it to standard output. There it can be verified to - be the correct password, before rebooting. - -* User-Supplied Plugins - - Any plugins found 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 - -* Do *NOT* Edit "/etc/crypttab" - - It is NOT necessary to edit "/etc/crypttab" to specify - "/usr/lib/mandos/plugin-runner" as a keyscript for the root file - system; if no keyscript is given for the root file system, the - Mandos client will be the new default way for getting a password for - the root file system when booting. - -* Emergency Escape - - If it ever should be necessary, the Mandos client can be temporarily - prevented from running at startup by passing the parameter - "mandos=off" to the kernel. - -* Non-local Connection (Not Using ZeroConf) - - If the "ip=" kernel command line option is used to specify a - complete IP address and device name, as noted above, it then becomes - possible to specify a specific IP address and port to connect to, - instead of using ZeroConf. The syntax for doing this is - "mandos=connect::". - - For very advanced users, it it possible to specify simply - "mandos=connect" on the kernel command line to make the system only - set up the network (using the data in the "ip=" option) and not pass - any extra "--connect" options to mandos-client at boot. For this to - work, "--options-for=mandos-client:--connect=
:" needs - to be manually added to the file "/etc/mandos/plugin-runner.conf". - - -- Teddy Hogeborn , Mon, 27 Sep 2010 19:53:21 +0200 === removed file 'debian/mandos-client.dirs' --- debian/mandos-client.dirs 2009-02-07 04:50:39 +0000 +++ debian/mandos-client.dirs 1970-01-01 00:00:00 +0000 @@ -1,5 +0,0 @@ -usr/share/man/man8 -usr/sbin -usr/share/initramfs-tools/hooks -usr/share/initramfs-tools/conf-hooks.d -usr/share/initramfs-tools/scripts/init-premount === removed file 'debian/mandos-client.docs' --- debian/mandos-client.docs 2008-10-18 11:17:22 +0000 +++ debian/mandos-client.docs 1970-01-01 00:00:00 +0000 @@ -1,3 +0,0 @@ -NEWS -README -TODO === removed file 'debian/mandos-client.links' --- debian/mandos-client.links 2008-09-19 13:50:22 +0000 +++ debian/mandos-client.links 1970-01-01 00:00:00 +0000 @@ -1,1 +0,0 @@ -usr/share/man/man8/plugin-runner.8mandos.gz usr/share/man/man5/plugin-runner.conf.5mandos.gz === removed file 'debian/mandos-client.lintian-overrides' --- debian/mandos-client.lintian-overrides 2009-01-18 06:41:57 +0000 +++ debian/mandos-client.lintian-overrides 1970-01-01 00:00:00 +0000 @@ -1,27 +0,0 @@ -# This directory contains secret client key files. -# -mandos-client binary: non-standard-dir-perm etc/keys/mandos/ 0700 != 0755 - -# The directory /usr/lib/mandos/plugins.d contains setuid binaries -# which are not meant to be run outside an initial RAM disk -# environment (except for test purposes). It would be insecure to -# allow anyone to run them. -# -mandos-client binary: non-standard-dir-perm usr/lib/mandos/plugins.d/ 0700 != 0755 - -# These binaries must be setuid root, since they need root powers, but -# are started by plugin-runner(8mandos), which runs all plugins as -# user/group "_mandos". These binaries are not run in a running -# system, but in an initial RAM disk environment. Here they are -# protected from non-root access by the directory permissions, above. -# -mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/mandos-client 4755 root/root -mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/askpass-fifo 4755 root/root -mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/splashy 4755 root/root -mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/usplash 4755 root/root - -# The directory /etc/mandos/plugins.d can be used by local system -# administrators to place plugins in, overriding and complementing -# /usr/lib/mandos/plugins.d, and must be likewise protected. -# -mandos-client binary: non-standard-dir-perm etc/mandos/plugins.d/ 0700 != 0755 === removed file 'debian/mandos-client.postinst' --- debian/mandos-client.postinst 2009-05-24 23:36:15 +0000 +++ debian/mandos-client.postinst 1970-01-01 00:00:00 +0000 @@ -1,81 +0,0 @@ -#!/bin/sh -e -# This script can be called in the following ways: -# -# After the package was installed: -# configure -# -# -# If prerm fails during upgrade or fails on failed upgrade: -# abort-upgrade -# -# If prerm fails during deconfiguration of a package: -# abort-deconfigure in-favour -# removing -# -# If prerm fails during replacement due to conflict: -# abort-remove in-favour - -# Update the initial RAM file system image -update_initramfs() -{ - if [ -x /usr/sbin/update-initramfs ]; then - update-initramfs -u -k all - fi - - if dpkg --compare-versions "$2" lt-nl "1.0.10-1"; then - # Make old initrd.img files unreadable too, in case they were - # created with mandos-client 1.0.8 or older. - find /boot -maxdepth 1 -type f -name "initrd.img-*.bak" \ - -print0 | xargs --null --no-run-if-empty chmod o-r - fi -} - -# Add user and group -add_mandos_user(){ - # Rename old "mandos" user and group - if dpkg --compare-versions "$2" lt "1.0.3-1"; then - case "`getent passwd mandos`" in - *:Mandos\ password\ system,,,:/nonexistent:/bin/false) - usermod --login _mandos mandos - groupmod --new-name _mandos mandos - return - ;; - esac - fi - # Create new user and group - if ! getent passwd _mandos >/dev/null; then - adduser --system --force-badname --quiet --home /nonexistent \ - --no-create-home --group --disabled-password \ - --gecos "Mandos password system" _mandos - fi -} - -# Create client key pair -create_key(){ - if [ -r /etc/keys/mandos/pubkey.txt \ - -a -r /etc/keys/mandos/seckey.txt ]; then - return 0 - fi - if [ -x /usr/sbin/mandos-keygen ]; then - mandos-keygen - fi -} - -case "$1" in - configure) - add_mandos_user "$@" - create_key "$@" - update_initramfs "$@" - ;; - abort-upgrade|abort-deconfigure|abort-remove) - ;; - - *) - echo "$0 called with unknown argument '$1'" 1>&2 - exit 1 - ;; -esac - -#DEBHELPER# - -exit 0 === removed file 'debian/mandos-client.postrm' --- debian/mandos-client.postrm 2009-01-18 00:16:57 +0000 +++ debian/mandos-client.postrm 1970-01-01 00:00:00 +0000 @@ -1,60 +0,0 @@ -#!/bin/sh -e -# This script can be called in the following ways: -# -# After the package was removed: -# remove -# -# After the package was purged: -# purge -# -# After the package was upgraded: -# upgrade -# if that fails: -# failed-upgrade -# -# -# After all of the packages files have been replaced: -# disappear -# -# -# If preinst fails during install: -# abort-install -# -# If preinst fails during upgrade of removed package: -# abort-install -# -# If preinst fails during upgrade: -# abort-upgrade - - -# Update the initial RAM file system image -update_initramfs() -{ - if [ -x /usr/sbin/update-initramfs ]; then - update-initramfs -u -k all - fi -} - -case "$1" in - remove) - update_initramfs - ;; - - purge) - shred --remove /etc/keys/mandos/seckey.txt 2>/dev/null || : - rm --force /etc/mandos/plugin-runner.conf \ - /etc/keys/mandos/pubkey.txt \ - /etc/keys/mandos/seckey.txt 2>/dev/null - ;; - upgrade|failed-upgrade|disappear|abort-install|abort-upgrade) - ;; - - *) - echo "$0 called with unknown argument '$1'" 1>&2 - exit 1 - ;; -esac - -#DEBHELPER# - -exit 0 === removed file 'debian/mandos.README.Debian' --- debian/mandos.README.Debian 2009-09-08 06:28:20 +0000 +++ debian/mandos.README.Debian 1970-01-01 00:00:00 +0000 @@ -1,10 +0,0 @@ -The Mandos server is useless without at least one configured client in -/etc/mandos/clients.conf. To create one, install the "mandos-client" -package on a client computer, and run the command - - # mandos-keygen --password - -there to get a config file stanza. Append the output of that command -to the file "/etc/mandos/clients.conf" on the Mandos server. - - -- Teddy Hogeborn , Tue, 8 Sep 2009 06:57:45 +0200 === removed file 'debian/mandos.dirs' --- debian/mandos.dirs 2010-09-15 17:33:14 +0000 +++ debian/mandos.dirs 1970-01-01 00:00:00 +0000 @@ -1,6 +0,0 @@ -usr/share/man/man5 -usr/share/man/man8 -etc/init.d -etc/default -etc/dbus-1/system.d -usr/sbin === removed file 'debian/mandos.docs' --- debian/mandos.docs 2010-09-12 03:00:40 +0000 +++ debian/mandos.docs 1970-01-01 00:00:00 +0000 @@ -1,4 +0,0 @@ -NEWS -README -TODO -DBUS-API === removed file 'debian/mandos.lintian-overrides' --- debian/mandos.lintian-overrides 2008-10-01 15:29:01 +0000 +++ debian/mandos.lintian-overrides 1970-01-01 00:00:00 +0000 @@ -1,4 +0,0 @@ -# This config file will normally have encrypted secret client keys in -# it, so it must be kept unreadable for non-root users. -# -mandos binary: non-standard-file-perm etc/mandos/clients.conf 0600 != 0644 === removed file 'debian/mandos.postinst' --- debian/mandos.postinst 2009-05-24 23:28:04 +0000 +++ debian/mandos.postinst 1970-01-01 00:00:00 +0000 @@ -1,49 +0,0 @@ -#!/bin/sh -e -# This script can be called in the following ways: -# -# After the package was installed: -# configure -# -# -# If prerm fails during upgrade or fails on failed upgrade: -# abort-upgrade -# -# If prerm fails during deconfiguration of a package: -# abort-deconfigure in-favour -# removing -# -# If prerm fails during replacement due to conflict: -# abort-remove in-favour - -case "$1" in - configure) - # Rename old "mandos" user and group - if dpkg --compare-versions "$2" lt "1.0.3-1"; then - case "`getent passwd mandos`" in - *:Mandos\ password\ system,,,:/nonexistent:/bin/false) - usermod --login _mandos mandos - groupmod --new-name _mandos mandos - ;; - esac - fi - # Create new user and group - if ! getent passwd _mandos >/dev/null; then - adduser --system --force-badname --quiet \ - --home /nonexistent --no-create-home --group \ - --disabled-password --gecos "Mandos password system" \ - _mandos - fi - ;; - - abort-upgrade|abort-deconfigure|abort-remove) - ;; - - *) - echo "$0 called with unknown argument '$1'" 1>&2 - exit 1 - ;; -esac - -#DEBHELPER# - -exit 0 === removed file 'debian/mandos.prerm' --- debian/mandos.prerm 2009-01-18 00:16:57 +0000 +++ debian/mandos.prerm 1970-01-01 00:00:00 +0000 @@ -1,38 +0,0 @@ -#! /bin/sh -# prerm script for mandos -# -# see: dh_installdeb(1) - -set -e - -# summary of how this script can be called: -# * 'remove' -# * 'upgrade' -# * 'failed-upgrade' -# * 'remove' 'in-favour' -# * 'deconfigure' 'in-favour' -# 'removing' -# -# for details, see /usr/share/doc/packaging-manual/ - -case "$1" in - remove|deconfigure) - if [ -x /etc/init.d/mandos ]; then - if [ -x /usr/sbin/invoke-rc.d ]; then - invoke-rc.d mandos stop - else - /etc/init.d/mandos stop - fi - fi - ;; - upgrade|failed-upgrade) - ;; - *) - echo "prerm called with unknown argument '$1'" >&2 - exit 0 - ;; -esac - -#DEBHELPER# - -exit 0 === removed directory 'debian/po' === removed file 'debian/rules' --- debian/rules 2010-09-09 18:16:14 +0000 +++ debian/rules 1970-01-01 00:00:00 +0000 @@ -1,106 +0,0 @@ -#!/usr/bin/make -f -# Sample debian/rules that uses debhelper. -# -# This file was originally written by Joey Hess and Craig Small. -# As a special exception, when this file is copied by dh-make into a -# dh-make output file, you may use that output file without restriction. -# This special exception was added by Craig Small in version 0.37 of dh-make. -# -# Modified to make a template file for a multi-binary package with separated -# build-arch and build-indep targets by Bill Allombert 2001 - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -# This has to be exported to make some magic below work. -export DH_OPTIONS - -# -pie was broken briefly on the mips and mipsel architectures, see -# -BINUTILS_V := $(shell dpkg-query --showformat='$${Version}' \ - --show binutils) -ifeq (yes,$(shell dpkg --compare-versions $(BINUTILS_V) lt 2.20-3 \ - && dpkg --compare-versions $(BINUTILS_V) ge 2.19.1-1 \ - && echo yes)) - ifneq (,$(strip $(findstring :$(DEB_HOST_ARCH):,:mips:mipsel:) \ - $(findstring :$(DEB_BUILD_ARCH):,:mips:mipsel:))) - BROKEN_PIE := yes - export BROKEN_PIE - endif -endif - -configure: configure-stamp -configure-stamp: - dh_testdir - touch configure-stamp - -build: build-arch build-indep - -build-arch: build-arch-stamp -build-arch-stamp: configure-stamp - dh_auto_build -- all doc - touch $@ - -build-indep: build-indep-stamp -build-indep-stamp: configure-stamp - dh_auto_build -- doc - touch $@ - -clean: - dh_testdir - dh_testroot - rm -f build-arch-stamp build-indep-stamp configure-stamp - dh_auto_clean - dh_clean - -install: install-indep install-arch -install-indep: - dh_testdir - dh_testroot - dh_prep - dh_installdirs --indep - $(MAKE) DESTDIR=$(CURDIR)/debian/mandos install-server - dh_lintian - dh_installinit --onlyscripts \ - --update-rcd-params="defaults 25 15" - dh_install --indep - -install-arch: - dh_testdir - dh_testroot - dh_prep - dh_installdirs --same-arch - $(MAKE) DESTDIR=$(CURDIR)/debian/mandos-client install-client-nokey - dh_lintian - dh_install --same-arch - -binary-common: - dh_testdir - dh_testroot - dh_installchangelogs - dh_installdocs - dh_link - dh_strip - dh_compress - dh_fixperms --exclude etc/keys/mandos \ - --exclude etc/mandos/clients.conf \ - --exclude etc/mandos/plugins.d \ - --exclude usr/lib/mandos/plugins.d - dh_installdeb - dh_shlibdeps - dh_gencontrol - dh_md5sums - dh_builddeb - -# Build architecture independant packages using the common target. -binary-indep: build-indep install-indep - $(MAKE) -f debian/rules DH_OPTIONS=--indep binary-common - -# Build architecture dependant packages using the common target. -binary-arch: build-arch install-arch - $(MAKE) -f debian/rules DH_OPTIONS=--same-arch binary-common - -binary: binary-arch binary-indep - -.PHONY: build clean binary-indep binary-arch binary install \ - install-indep install-arch configure === removed file 'debian/watch' --- debian/watch 2010-09-15 17:17:46 +0000 +++ debian/watch 1970-01-01 00:00:00 +0000 @@ -1,2 +0,0 @@ -version=3 -ftp://ftp.fukt.bsnet.se/pub/mandos/mandos[-_]([^\s]+?)(?:\.orig)?\.tar\.(?:gz|bz2|7z|xz) === removed file 'default-mandos' --- default-mandos 2008-09-17 00:34:09 +0000 +++ default-mandos 1970-01-01 00:00:00 +0000 @@ -1,7 +0,0 @@ -# Directory where configuration files are located. Default is -# "/etc/mandos". -# -#CONFIGDIR=/etc/mandos - -# Additional options that are passed to the Daemon. -DAEMON_ARGS="" === removed file 'init.d-mandos' --- init.d-mandos 2009-09-16 23:28:39 +0000 +++ init.d-mandos 1970-01-01 00:00:00 +0000 @@ -1,159 +0,0 @@ -#! /bin/sh -### BEGIN INIT INFO -# Provides: mandos -# Required-Start: $remote_fs $syslog avahi -# Required-Stop: $remote_fs $syslog avahi -# 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 - -: === removed file 'initramfs-tools-hook' --- initramfs-tools-hook 2010-09-09 18:16:14 +0000 +++ initramfs-tools-hook 1970-01-01 00:00:00 +0000 @@ -1,175 +0,0 @@ -#!/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 - -set `{ getent passwd _mandos \ - || getent passwd nobody \ - || echo ::65534:65534:::; } \ - | cut --delimiter=: --fields=3,4 --only-delimited \ - --output-delimiter=" "` -mandos_user="$1" -mandos_group="$2" - -# 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-bak|*.dpkg-new|*.dpkg-divert) - : ;; - "*") echo "W: Mandos client plugin directory is empty." >&2 ;; - *) 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-bak|*.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/plugin-runner.conf; do - if [ -d "$file" ]; then - continue - fi - cp --archive --sparse=always "$file" "${DESTDIR}${CONFDIR}" -done - -if [ ${mandos_user} != 65534 ]; then - sed --in-place --expression="1i--userid=${mandos_user}" \ - "${DESTDIR}${CONFDIR}/plugin-runner.conf" -fi - -if [ ${mandos_group} != 65534 ]; then - sed --in-place --expression="1i--groupid=${mandos_group}" \ - "${DESTDIR}${CONFDIR}/plugin-runner.conf" -fi - -# 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 "${DESTDIR}"/lib* "${DESTDIR}"/usr/lib*; do - if [ -d "$dir" ]; then - find "$dir" \! -perm -u+rw,g+r -prune -or -print0 \ - | xargs --null --no-run-if-empty chmod a+rX - fi -done === removed file 'initramfs-tools-hook-conf' --- initramfs-tools-hook-conf 2009-05-17 00:50:09 +0000 +++ initramfs-tools-hook-conf 1970-01-01 00:00:00 +0000 @@ -1,13 +0,0 @@ -# -*- shell-script -*- - -# if mkinitramfs is started by mkinitramfs-kpkg, mkinitramfs-kpkg has -# already touched the initrd file with umask 022 before we had a -# chance to affect it. We cannot allow a readable initrd file, -# therefore we must fix this now. -if [ -e "${outfile}" ] \ - && [ `stat --format=%s "${outfile}"` -eq 0 ]; then - rm "${outfile}" - (umask 027; touch "${outfile}") -fi - -UMASK=027 === removed file 'initramfs-tools-script' --- initramfs-tools-script 2009-09-16 23:28:39 +0000 +++ initramfs-tools-script 1970-01-01 00:00:00 +0000 @@ -1,151 +0,0 @@ -#!/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/init-premount/mandos" which will -# eventually be "/scripts/init-premount/mandos" in the initrd.img -# file. - -PREREQ="udev" -prereqs() -{ - echo "$PREREQ" -} - -case $1 in -prereqs) - prereqs - exit 0 - ;; -esac - -. /scripts/functions - -for param in `cat /proc/cmdline`; do - case "$param" in - ip=*) IPOPTS="${param#ip=}" ;; - mandos=*) - # Split option line on commas - old_ifs="$IFS" - IFS="$IFS," - for mpar in ${param#mandos=}; do - IFS="$old_ifs" - case "$mpar" in - off) exit 0 ;; - connect) connect="" ;; - connect:*) connect="${mpar#connect:}" ;; - *) log_warning_msg "$0: Bad option ${mpar}" ;; - esac - done - unset mpar - IFS="$old_ifs" - unset old_ifs - ;; - esac -done -unset param - -chmod a=rwxt /tmp - -test -r /conf/conf.d/cryptroot -test -w /conf/conf.d - -# Get DEVICE from /conf/initramfs.conf and other files -. /conf/initramfs.conf -for conf in /conf/conf.d/*; do - [ -f ${conf} ] && . ${conf} -done -if [ -e /conf/param.conf ]; then - . /conf/param.conf -fi - -# Override DEVICE from sixth field of ip= kernel option, if passed -case "$IPOPTS" in - *:*:*:*:*:*) # At least six fields - # Remove the first five fields - device="${IPOPTS#*:*:*:*:*:}" - # Remove all fields except the first one - DEVICE="${device%%:*}" - ;; -esac - -# Add device setting (if any) to plugin-runner.conf -if [ "${DEVICE+set}" = set ]; then - # Did we get the device from an ip= option? - if [ "${device+set}" = set ]; then - # Let ip= option override local config; append: - cat <<-EOF >>/conf/conf.d/mandos/plugin-runner.conf - - --options-for=mandos-client:--interface=${DEVICE} -EOF - else - # Prepend device setting so any later options would override: - sed -i -e \ - '1i--options-for=mandos-client:--interface='"${DEVICE}" \ - /conf/conf.d/mandos/plugin-runner.conf - fi -fi -unset device - -# If we are connecting directly, run "configure_networking" (from -# /scripts/functions); it needs IPOPTS and DEVICE -if [ "${connect+set}" = set ]; then - configure_networking - if [ -n "$connect" ]; then - cat <<-EOF >>/conf/conf.d/mandos/plugin-runner.conf - - --options-for=mandos-client:--connect=${connect} -EOF - fi -fi - -# 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 'legalnotice.xml' --- legalnotice.xml 2008-09-06 17:24:58 +0000 +++ legalnotice.xml 1970-01-01 00:00:00 +0000 @@ -1,27 +0,0 @@ - - - - - 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/. - - === modified file 'mandos' --- mandos 2010-09-28 18:57:31 +0000 +++ mandos 2008-08-08 01:17:17 +0000 @@ -6,13 +6,12 @@ # This program is partly derived from an example program for an Avahi # service publisher, downloaded from # . This includes the -# methods "add", "remove", "server_state_changed", -# "entry_group_state_changed", "cleanup", and "activate" in the -# "AvahiService" class, and some lines in "main". +# 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 © 2008-2010 Teddy Hogeborn -# Copyright © 2008-2010 Björn Påhlsson +# 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 @@ -25,17 +24,17 @@ # 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 -# . +# along with this program. If not, see . # # Contact the authors at . # -from __future__ import division, with_statement, absolute_import +from __future__ import division -import SocketServer as socketserver +import SocketServer import socket -import optparse +import select +from optparse import OptionParser import datetime import errno import gnutls.crypto @@ -44,67 +43,39 @@ import gnutls.library.functions import gnutls.library.constants import gnutls.library.types -import ConfigParser as configparser +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 contextlib -import struct -import fcntl -import functools -import cPickle as pickle -import multiprocessing import dbus -import dbus.service import gobject import avahi from dbus.mainloop.glib import DBusGMainLoop import ctypes -import ctypes.util -import xml.dom.minidom -import inspect - -try: - SO_BINDTODEVICE = socket.SO_BINDTODEVICE -except AttributeError: - try: - from IN import SO_BINDTODEVICE - except ImportError: - SO_BINDTODEVICE = None - - -version = "1.2" - -#logger = logging.getLogger(u'mandos') -logger = logging.Logger(u'mandos') -syslogger = (logging.handlers.SysLogHandler - (facility = logging.handlers.SysLogHandler.LOG_DAEMON, - address = "/dev/log")) -syslogger.setFormatter(logging.Formatter - (u'Mandos [%(process)d]: %(levelname)s:' - u' %(message)s')) + + +logger = logging.Logger('mandos') +syslogger = logging.handlers.SysLogHandler\ + (facility = logging.handlers.SysLogHandler.LOG_DAEMON) +syslogger.setFormatter(logging.Formatter\ + ('%(levelname)s: %(message)s')) logger.addHandler(syslogger) +del syslogger -console = logging.StreamHandler() -console.setFormatter(logging.Formatter(u'%(name)s [%(process)d]:' - u' %(levelname)s:' - u' %(message)s')) -logger.addHandler(console) class AvahiError(Exception): - def __init__(self, value, *args, **kwargs): + def __init__(self, value): self.value = value - super(AvahiError, self).__init__(value, *args, **kwargs) - def __unicode__(self): - return unicode(repr(self.value)) + def __str__(self): + return repr(self.value) class AvahiServiceError(AvahiError): pass @@ -115,12 +86,11 @@ 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: u'Mandos' - type: string; Example: u'_mandos._tcp'. + 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 @@ -129,307 +99,207 @@ max_renames: integer; maximum number of renames rename_count: integer; counter so we only rename after collisions a sensible number of times - group: D-Bus Entry Group - server: D-Bus Server - bus: dbus.SystemBus() """ def __init__(self, interface = avahi.IF_UNSPEC, name = None, - servicetype = None, port = None, TXT = None, - domain = u"", host = u"", max_renames = 32768, - protocol = avahi.PROTO_UNSPEC, bus = None): + type = None, port = None, TXT = None, domain = "", + host = "", max_renames = 12): self.interface = interface self.name = name - self.type = servicetype + self.type = type self.port = port - self.TXT = TXT if TXT is not None else [] + 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 - self.protocol = protocol - self.group = None # our entry group - self.server = None - self.bus = bus 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.", - self.rename_count) - raise AvahiServiceError(u"Too many renames") - self.name = unicode(self.server.GetAlternativeServiceName(self.name)) - logger.info(u"Changing Zeroconf service name to %r ...", - self.name) - syslogger.setFormatter(logging.Formatter - (u'Mandos (%s) [%%(process)d]:' - u' %%(levelname)s: %%(message)s' - % self.name)) + logger.critical(u"No suitable service name found after %i" + u" retries, exiting.", rename_count) + raise AvahiServiceError("Too many renames") + name = server.GetAlternativeServiceName(name) + logger.error(u"Changing name to %r ...", name) self.remove() - try: - self.add() - except dbus.exceptions.DBusException, error: - logger.critical(u"DBusException: %s", error) - self.cleanup() - os._exit(1) + self.add() self.rename_count += 1 def remove(self): """Derived from the Avahi example code""" - if self.group is not None: - self.group.Reset() + if group is not None: + group.Reset() def add(self): """Derived from the Avahi example code""" - if self.group is None: - self.group = dbus.Interface( - self.bus.get_object(avahi.DBUS_NAME, - self.server.EntryGroupNew()), - avahi.DBUS_INTERFACE_ENTRY_GROUP) - self.group.connect_to_signal('StateChanged', - self - .entry_group_state_changed) - logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...", - self.name, self.type) - self.group.AddService( - self.interface, - self.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)) - self.group.Commit() - def entry_group_state_changed(self, state, error): - """Derived from the Avahi example code""" - logger.debug(u"Avahi entry group 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.") - self.rename() - elif state == avahi.ENTRY_GROUP_FAILURE: - logger.critical(u"Avahi: Error in group state changed %s", - unicode(error)) - raise AvahiGroupError(u"State changed: %s" - % unicode(error)) - def cleanup(self): - """Derived from the Avahi example code""" - if self.group is not None: - self.group.Free() - self.group = None - def server_state_changed(self, state): - """Derived from the Avahi example code""" - logger.debug(u"Avahi server state change: %i", state) - if state == avahi.SERVER_COLLISION: - logger.error(u"Zeroconf server name collision") - self.remove() - elif state == avahi.SERVER_RUNNING: - self.add() - def activate(self): - """Derived from the Avahi example code""" - if self.server is None: - self.server = dbus.Interface( - self.bus.get_object(avahi.DBUS_NAME, - avahi.DBUS_PATH_SERVER), - avahi.DBUS_INTERFACE_SERVER) - self.server.connect_to_signal(u"StateChanged", - self.server_state_changed) - self.server_state_changed(self.server.GetState()) + 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 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): """A representation of a client host served by this server. - Attributes: - _approved: bool(); 'None' if not yet approved/disapproved - approval_delay: datetime.timedelta(); Time to wait for approval - approval_duration: datetime.timedelta(); Duration of one approval - checker: subprocess.Popen(); a running checker process used - to see if the client lives. - 'None' if no process is running. - checker_callback_tag: a gobject event source tag, or None - checker_command: string; External command which is run to check - if client lives. %() expansions are done at + 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 + fqdn: string (FQDN); 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. - checker_initiator_tag: a gobject event source tag, or None - created: datetime.datetime(); (UTC) object creation - current_checker_command: string; current running checker_command - disable_hook: If set, called by disable() as disable_hook(self) - disable_initiator_tag: a gobject event source tag, or None - enabled: bool() - fingerprint: string (40 or 32 hexadecimal digits); used to - uniquely identify the client - host: string; available for use by the checker command - interval: datetime.timedelta(); How often to start a new checker - last_approval_request: datetime.datetime(); (UTC) or None - last_checked_ok: datetime.datetime(); (UTC) or None - last_enabled: datetime.datetime(); (UTC) - name: string; from the config file, used in log messages and - D-Bus identifiers - secret: bytestring; sent verbatim (over TLS) to client - timeout: datetime.timedelta(); How long from last_checked_ok - until this client is disabled - runtime_expansions: Allowed attributes for runtime expansion. + Private attibutes: + _timeout: Real variable for 'timeout' + _interval: Real variable for 'interval' + _timeout_milliseconds: Used when calling gobject.timeout_add() + _interval_milliseconds: - '' - """ - - runtime_expansions = (u"approval_delay", u"approval_duration", - u"created", u"enabled", u"fingerprint", - u"host", u"interval", u"last_checked_ok", - u"last_enabled", u"name", u"timeout") - - @staticmethod - def _timedelta_to_milliseconds(td): - "Convert a datetime.timedelta() to milliseconds" - return ((td.days * 24 * 60 * 60 * 1000) - + (td.seconds * 1000) - + (td.microseconds // 1000)) - - def timeout_milliseconds(self): - "Return the 'timeout' attribute in milliseconds" - return self._timedelta_to_milliseconds(self.timeout) - - def interval_milliseconds(self): - "Return the 'interval' attribute in milliseconds" - return self._timedelta_to_milliseconds(self.interval) - - def approval_delay_milliseconds(self): - return self._timedelta_to_milliseconds(self.approval_delay) - - def __init__(self, name = None, disable_hook=None, config=None): + 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 - if config is None: - config = {} 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[u"fingerprint"].upper() - .replace(u" ", u"")) + self.fingerprint = config["fingerprint"].upper()\ + .replace(u" ", u"") logger.debug(u" Fingerprint: %s", self.fingerprint) - if u"secret" in config: - self.secret = config[u"secret"].decode(u"base64") - elif u"secfile" in config: - with open(os.path.expanduser(os.path.expandvars - (config[u"secfile"])), - "rb") as secfile: - self.secret = secfile.read() + 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(u"host", u"") - self.created = datetime.datetime.utcnow() - self.enabled = False - self.last_approval_request = None - self.last_enabled = None + self.fqdn = config.get("fqdn", "") + self.created = datetime.datetime.now() self.last_checked_ok = None - self.timeout = string_to_delta(config[u"timeout"]) - self.interval = string_to_delta(config[u"interval"]) - self.disable_hook = disable_hook + 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.disable_initiator_tag = None + self.stop_initiator_tag = None self.checker_callback_tag = None - self.checker_command = config[u"checker"] - self.current_checker_command = None - self.last_connect = None - self._approved = None - self.approved_by_default = config.get(u"approved_by_default", - True) - self.approvals_pending = 0 - self.approval_delay = string_to_delta( - config[u"approval_delay"]) - self.approval_duration = string_to_delta( - config[u"approval_duration"]) - self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock()) - - def send_changedstate(self): - self.changedstate.acquire() - self.changedstate.notify_all() - self.changedstate.release() - - def enable(self): + self.check_command = config["checker"] + def start(self): """Start this client's checker and timeout hooks""" - if getattr(self, u"enabled", False): - # Already enabled - return - self.send_changedstate() - self.last_enabled = datetime.datetime.utcnow() # 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)) - # Schedule a disable() when 'timeout' has passed - self.disable_initiator_tag = (gobject.timeout_add - (self.timeout_milliseconds(), - self.disable)) - self.enabled = True + self.checker_initiator_tag = gobject.timeout_add\ + (self._interval_milliseconds, + self.start_checker) # Also start a new checker *right now*. self.start_checker() - - def disable(self, quiet=True): - """Disable this client.""" - if not getattr(self, "enabled", False): + # 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 self.secret: + logger.info(u"Stopping client %s", self.name) + self.secret = None + else: return False - if not quiet: - self.send_changedstate() - if not quiet: - logger.info(u"Disabling client %s", self.name) - if getattr(self, u"disable_initiator_tag", False): - gobject.source_remove(self.disable_initiator_tag) - self.disable_initiator_tag = None - if getattr(self, u"checker_initiator_tag", 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.disable_hook: - self.disable_hook(self) - self.enabled = False + 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.disable_hook = None - self.disable() - - def checker_callback(self, pid, condition, command): + 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): - exitstatus = os.WEXITSTATUS(condition) - if exitstatus == 0: - logger.info(u"Checker for %(name)s succeeded", - vars(self)) - self.checked_ok() - else: - logger.info(u"Checker for %(name)s failed", - vars(self)) - else: + 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)) - - def checked_ok(self): - """Bump up the timeout for this client. - - This should only be called when the client has been seen, - alive and well. - """ - self.last_checked_ok = datetime.datetime.utcnow() - gobject.source_remove(self.disable_initiator_tag) - self.disable_initiator_tag = (gobject.timeout_add - (self.timeout_milliseconds(), - self.disable)) - - def need_approval(self): - self.last_approval_request = datetime.datetime.utcnow() - + 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 @@ -438,1219 +308,329 @@ # 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 disabled, which is as it - # should be. - - # If a checker exists, make sure it is not a zombie - try: - pid, status = os.waitpid(self.checker.pid, os.WNOHANG) - except (AttributeError, OSError), error: - if (isinstance(error, OSError) - and error.errno != errno.ECHILD): - raise error - else: - if pid: - logger.warning(u"Checker was a zombie") - gobject.source_remove(self.checker_callback_tag) - self.checker_callback(pid, status, - self.current_checker_command) - # Start a new checker if needed + # than 'timeout' for the client to be declared invalid, which + # is as it should be. if self.checker is None: try: - # In case checker_command has exactly one % operator - command = self.checker_command % self.host + # In case check_command has exactly one % operator + command = self.check_command % self.fqdn except TypeError: # Escape attributes for the shell - escaped_attrs = dict( - (attr, - re.escape(unicode(str(getattr(self, attr, u"")), - errors= - u'replace'))) - for attr in - self.runtime_expansions) - + escaped_attrs = dict((key, re.escape(str(val))) + for key, val in + vars(self).iteritems()) try: - command = self.checker_command % escaped_attrs + command = self.check_command % escaped_attrs except TypeError, error: logger.error(u'Could not format string "%s":' - u' %s', self.checker_command, error) + u' %s', self.check_command, error) return True # Try again later - self.current_checker_command = command 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=u"/") - self.checker_callback_tag = (gobject.child_watch_add - (self.checker.pid, - self.checker_callback, - data=command)) - # The checker may have completed before the gobject - # watch was added. Check for this. - pid, status = os.waitpid(self.checker.pid, os.WNOHANG) - if pid: - gobject.source_remove(self.checker_callback_tag) - self.checker_callback(pid, status, command) - except OSError, error: + shell=True, cwd="/") + self.checker_callback_tag = gobject.child_watch_add\ + (self.checker.pid, + self.checker_callback) + except subprocess.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, u"checker", None) is None: + if getattr(self, "checker", None) is None: return - logger.debug(u"Stopping checker for %(name)s", vars(self)) + logger.debug("Stopping checker for %(name)s", vars(self)) try: os.kill(self.checker.pid, signal.SIGTERM) - #time.sleep(0.5) + #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 dbus_service_property(dbus_interface, signature=u"v", - access=u"readwrite", byte_arrays=False): - """Decorators for marking methods of a DBusObjectWithProperties to - become properties on the D-Bus. - - The decorated method will be called with no arguments by "Get" - and with one argument by "Set". - - The parameters, where they are supported, are the same as - dbus.service.method, except there is only "signature", since the - type from Get() and the type sent to Set() is the same. - """ - # Encoding deeply encoded byte arrays is not supported yet by the - # "Set" method, so we fail early here: - if byte_arrays and signature != u"ay": - raise ValueError(u"Byte arrays not supported for non-'ay'" - u" signature %r" % signature) - def decorator(func): - func._dbus_is_property = True - func._dbus_interface = dbus_interface - func._dbus_signature = signature - func._dbus_access = access - func._dbus_name = func.__name__ - if func._dbus_name.endswith(u"_dbus_property"): - func._dbus_name = func._dbus_name[:-14] - func._dbus_get_args_options = {u'byte_arrays': byte_arrays } - return func - return decorator - - -class DBusPropertyException(dbus.exceptions.DBusException): - """A base class for D-Bus property-related exceptions - """ - def __unicode__(self): - return unicode(str(self)) - - -class DBusPropertyAccessException(DBusPropertyException): - """A property's access permissions disallows an operation. - """ - pass - - -class DBusPropertyNotFound(DBusPropertyException): - """An attempt was made to access a non-existing property. - """ - pass - - -class DBusObjectWithProperties(dbus.service.Object): - """A D-Bus object with properties. - - Classes inheriting from this can use the dbus_service_property - decorator to expose methods as D-Bus properties. It exposes the - standard Get(), Set(), and GetAll() methods on the D-Bus. - """ - - @staticmethod - def _is_dbus_property(obj): - return getattr(obj, u"_dbus_is_property", False) - - def _get_all_dbus_properties(self): - """Returns a generator of (name, attribute) pairs - """ - return ((prop._dbus_name, prop) - for name, prop in - inspect.getmembers(self, self._is_dbus_property)) - - def _get_dbus_property(self, interface_name, property_name): - """Returns a bound method if one exists which is a D-Bus - property with the specified name and interface. - """ - for name in (property_name, - property_name + u"_dbus_property"): - prop = getattr(self, name, None) - if (prop is None - or not self._is_dbus_property(prop) - or prop._dbus_name != property_name - or (interface_name and prop._dbus_interface - and interface_name != prop._dbus_interface)): - continue - return prop - # No such property - raise DBusPropertyNotFound(self.dbus_object_path + u":" - + interface_name + u"." - + property_name) - - @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss", - out_signature=u"v") - def Get(self, interface_name, property_name): - """Standard D-Bus property Get() method, see D-Bus standard. - """ - prop = self._get_dbus_property(interface_name, property_name) - if prop._dbus_access == u"write": - raise DBusPropertyAccessException(property_name) - value = prop() - if not hasattr(value, u"variant_level"): - return value - return type(value)(value, variant_level=value.variant_level+1) - - @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv") - def Set(self, interface_name, property_name, value): - """Standard D-Bus property Set() method, see D-Bus standard. - """ - prop = self._get_dbus_property(interface_name, property_name) - if prop._dbus_access == u"read": - raise DBusPropertyAccessException(property_name) - if prop._dbus_get_args_options[u"byte_arrays"]: - # The byte_arrays option is not supported yet on - # signatures other than "ay". - if prop._dbus_signature != u"ay": - raise ValueError - value = dbus.ByteArray(''.join(unichr(byte) - for byte in value)) - prop(value) - - @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s", - out_signature=u"a{sv}") - def GetAll(self, interface_name): - """Standard D-Bus property GetAll() method, see D-Bus - standard. - - Note: Will not include properties with access="write". - """ - all = {} - for name, prop in self._get_all_dbus_properties(): - if (interface_name - and interface_name != prop._dbus_interface): - # Interface non-empty but did not match - continue - # Ignore write-only properties - if prop._dbus_access == u"write": - continue - value = prop() - if not hasattr(value, u"variant_level"): - all[name] = value - continue - all[name] = type(value)(value, variant_level= - value.variant_level+1) - return dbus.Dictionary(all, signature=u"sv") - - @dbus.service.method(dbus.INTROSPECTABLE_IFACE, - out_signature=u"s", - path_keyword='object_path', - connection_keyword='connection') - def Introspect(self, object_path, connection): - """Standard D-Bus method, overloaded to insert property tags. - """ - xmlstring = dbus.service.Object.Introspect(self, object_path, - connection) - try: - document = xml.dom.minidom.parseString(xmlstring) - def make_tag(document, name, prop): - e = document.createElement(u"property") - e.setAttribute(u"name", name) - e.setAttribute(u"type", prop._dbus_signature) - e.setAttribute(u"access", prop._dbus_access) - return e - for if_tag in document.getElementsByTagName(u"interface"): - for tag in (make_tag(document, name, prop) - for name, prop - in self._get_all_dbus_properties() - if prop._dbus_interface - == if_tag.getAttribute(u"name")): - if_tag.appendChild(tag) - # Add the names to the return values for the - # "org.freedesktop.DBus.Properties" methods - if (if_tag.getAttribute(u"name") - == u"org.freedesktop.DBus.Properties"): - for cn in if_tag.getElementsByTagName(u"method"): - if cn.getAttribute(u"name") == u"Get": - for arg in cn.getElementsByTagName(u"arg"): - if (arg.getAttribute(u"direction") - == u"out"): - arg.setAttribute(u"name", u"value") - elif cn.getAttribute(u"name") == u"GetAll": - for arg in cn.getElementsByTagName(u"arg"): - if (arg.getAttribute(u"direction") - == u"out"): - arg.setAttribute(u"name", u"props") - xmlstring = document.toxml(u"utf-8") - document.unlink() - except (AttributeError, xml.dom.DOMException, - xml.parsers.expat.ExpatError), error: - logger.error(u"Failed to override Introspection method", - error) - return xmlstring - - -class ClientDBus(Client, DBusObjectWithProperties): - """A Client class using D-Bus - - Attributes: - dbus_object_path: dbus.ObjectPath - bus: dbus.SystemBus() - """ - - runtime_expansions = (Client.runtime_expansions - + (u"dbus_object_path",)) - - # dbus.service.Object doesn't use super(), so we can't either. - - def __init__(self, bus = None, *args, **kwargs): - self._approvals_pending = 0 - self.bus = bus - Client.__init__(self, *args, **kwargs) - # Only now, when this client is initialized, can it show up on - # the D-Bus - client_object_name = unicode(self.name).translate( - {ord(u"."): ord(u"_"), - ord(u"-"): ord(u"_")}) - self.dbus_object_path = (dbus.ObjectPath - (u"/clients/" + client_object_name)) - DBusObjectWithProperties.__init__(self, self.bus, - self.dbus_object_path) - - def _get_approvals_pending(self): - return self._approvals_pending - def _set_approvals_pending(self, value): - old_value = self._approvals_pending - self._approvals_pending = value - bval = bool(value) - if (hasattr(self, "dbus_object_path") - and bval is not bool(old_value)): - dbus_bool = dbus.Boolean(bval, variant_level=1) - self.PropertyChanged(dbus.String(u"ApprovalPending"), - dbus_bool) - - approvals_pending = property(_get_approvals_pending, - _set_approvals_pending) - del _get_approvals_pending, _set_approvals_pending - - @staticmethod - def _datetime_to_dbus(dt, variant_level=0): - """Convert a UTC datetime.datetime() to a D-Bus type.""" - return dbus.String(dt.isoformat(), - variant_level=variant_level) - - def enable(self): - oldstate = getattr(self, u"enabled", False) - r = Client.enable(self) - if oldstate != self.enabled: - # Emit D-Bus signals - self.PropertyChanged(dbus.String(u"Enabled"), - dbus.Boolean(True, variant_level=1)) - self.PropertyChanged( - dbus.String(u"LastEnabled"), - self._datetime_to_dbus(self.last_enabled, - variant_level=1)) - return r - - def disable(self, quiet = False): - oldstate = getattr(self, u"enabled", False) - r = Client.disable(self, quiet=quiet) - if not quiet and oldstate != self.enabled: - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"Enabled"), - dbus.Boolean(False, variant_level=1)) - return r - - def __del__(self, *args, **kwargs): - try: - self.remove_from_connection() - except LookupError: - pass - if hasattr(DBusObjectWithProperties, u"__del__"): - DBusObjectWithProperties.__del__(self, *args, **kwargs) - Client.__del__(self, *args, **kwargs) - - def checker_callback(self, pid, condition, command, - *args, **kwargs): - self.checker_callback_tag = None - self.checker = None - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"CheckerRunning"), - dbus.Boolean(False, variant_level=1)) - if os.WIFEXITED(condition): - exitstatus = os.WEXITSTATUS(condition) - # Emit D-Bus signal - self.CheckerCompleted(dbus.Int16(exitstatus), - dbus.Int64(condition), - dbus.String(command)) - else: - # Emit D-Bus signal - self.CheckerCompleted(dbus.Int16(-1), - dbus.Int64(condition), - dbus.String(command)) - - return Client.checker_callback(self, pid, condition, command, - *args, **kwargs) - - def checked_ok(self, *args, **kwargs): - r = Client.checked_ok(self, *args, **kwargs) - # Emit D-Bus signal - self.PropertyChanged( - dbus.String(u"LastCheckedOK"), - (self._datetime_to_dbus(self.last_checked_ok, - variant_level=1))) - return r - - def need_approval(self, *args, **kwargs): - r = Client.need_approval(self, *args, **kwargs) - # Emit D-Bus signal - self.PropertyChanged( - dbus.String(u"LastApprovalRequest"), - (self._datetime_to_dbus(self.last_approval_request, - variant_level=1))) - return r - - def start_checker(self, *args, **kwargs): - old_checker = self.checker - if self.checker is not None: - old_checker_pid = self.checker.pid - else: - old_checker_pid = None - r = Client.start_checker(self, *args, **kwargs) - # Only if new checker process was started - if (self.checker is not None - and old_checker_pid != self.checker.pid): - # Emit D-Bus signal - self.CheckerStarted(self.current_checker_command) - self.PropertyChanged( - dbus.String(u"CheckerRunning"), - dbus.Boolean(True, variant_level=1)) - return r - - def stop_checker(self, *args, **kwargs): - old_checker = getattr(self, u"checker", None) - r = Client.stop_checker(self, *args, **kwargs) - if (old_checker is not None - and getattr(self, u"checker", None) is None): - self.PropertyChanged(dbus.String(u"CheckerRunning"), - dbus.Boolean(False, variant_level=1)) - return r - - def _reset_approved(self): - self._approved = None - return False - - def approve(self, value=True): - self.send_changedstate() - self._approved = value - gobject.timeout_add(self._timedelta_to_milliseconds - (self.approval_duration), - self._reset_approved) - - - ## D-Bus methods, signals & properties - _interface = u"se.bsnet.fukt.Mandos.Client" - - ## Signals - - # CheckerCompleted - signal - @dbus.service.signal(_interface, signature=u"nxs") - def CheckerCompleted(self, exitcode, waitstatus, command): - "D-Bus signal" - pass - - # CheckerStarted - signal - @dbus.service.signal(_interface, signature=u"s") - def CheckerStarted(self, command): - "D-Bus signal" - pass - - # PropertyChanged - signal - @dbus.service.signal(_interface, signature=u"sv") - def PropertyChanged(self, property, value): - "D-Bus signal" - pass - - # GotSecret - signal - @dbus.service.signal(_interface) - def GotSecret(self): - """D-Bus signal - Is sent after a successful transfer of secret from the Mandos - server to mandos-client - """ - pass - - # Rejected - signal - @dbus.service.signal(_interface, signature=u"s") - def Rejected(self, reason): - "D-Bus signal" - pass - - # NeedApproval - signal - @dbus.service.signal(_interface, signature=u"tb") - def NeedApproval(self, timeout, default): - "D-Bus signal" - return self.need_approval() - - ## Methods - - # Approve - method - @dbus.service.method(_interface, in_signature=u"b") - def Approve(self, value): - self.approve(value) - - # CheckedOK - method - @dbus.service.method(_interface) - def CheckedOK(self): - return self.checked_ok() - - # Enable - method - @dbus.service.method(_interface) - def Enable(self): - "D-Bus method" - self.enable() - - # StartChecker - method - @dbus.service.method(_interface) - def StartChecker(self): - "D-Bus method" - self.start_checker() - - # Disable - method - @dbus.service.method(_interface) - def Disable(self): - "D-Bus method" - self.disable() - - # StopChecker - method - @dbus.service.method(_interface) - def StopChecker(self): - self.stop_checker() - - ## Properties - - # ApprovalPending - property - @dbus_service_property(_interface, signature=u"b", access=u"read") - def ApprovalPending_dbus_property(self): - return dbus.Boolean(bool(self.approvals_pending)) - - # ApprovedByDefault - property - @dbus_service_property(_interface, signature=u"b", - access=u"readwrite") - def ApprovedByDefault_dbus_property(self, value=None): - if value is None: # get - return dbus.Boolean(self.approved_by_default) - self.approved_by_default = bool(value) - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"ApprovedByDefault"), - dbus.Boolean(value, variant_level=1)) - - # ApprovalDelay - property - @dbus_service_property(_interface, signature=u"t", - access=u"readwrite") - def ApprovalDelay_dbus_property(self, value=None): - if value is None: # get - return dbus.UInt64(self.approval_delay_milliseconds()) - self.approval_delay = datetime.timedelta(0, 0, 0, value) - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"ApprovalDelay"), - dbus.UInt64(value, variant_level=1)) - - # ApprovalDuration - property - @dbus_service_property(_interface, signature=u"t", - access=u"readwrite") - def ApprovalDuration_dbus_property(self, value=None): - if value is None: # get - return dbus.UInt64(self._timedelta_to_milliseconds( - self.approval_duration)) - self.approval_duration = datetime.timedelta(0, 0, 0, value) - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"ApprovalDuration"), - dbus.UInt64(value, variant_level=1)) - - # Name - property - @dbus_service_property(_interface, signature=u"s", access=u"read") - def Name_dbus_property(self): - return dbus.String(self.name) - - # Fingerprint - property - @dbus_service_property(_interface, signature=u"s", access=u"read") - def Fingerprint_dbus_property(self): - return dbus.String(self.fingerprint) - - # Host - property - @dbus_service_property(_interface, signature=u"s", - access=u"readwrite") - def Host_dbus_property(self, value=None): - if value is None: # get - return dbus.String(self.host) - self.host = value - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"Host"), - dbus.String(value, variant_level=1)) - - # Created - property - @dbus_service_property(_interface, signature=u"s", access=u"read") - def Created_dbus_property(self): - return dbus.String(self._datetime_to_dbus(self.created)) - - # LastEnabled - property - @dbus_service_property(_interface, signature=u"s", access=u"read") - def LastEnabled_dbus_property(self): - if self.last_enabled is None: - return dbus.String(u"") - return dbus.String(self._datetime_to_dbus(self.last_enabled)) - - # Enabled - property - @dbus_service_property(_interface, signature=u"b", - access=u"readwrite") - def Enabled_dbus_property(self, value=None): - if value is None: # get - return dbus.Boolean(self.enabled) - if value: - self.enable() - else: - self.disable() - - # LastCheckedOK - property - @dbus_service_property(_interface, signature=u"s", - access=u"readwrite") - def LastCheckedOK_dbus_property(self, value=None): - if value is not None: - self.checked_ok() - return + 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 dbus.String(u"") - return dbus.String(self._datetime_to_dbus(self - .last_checked_ok)) - - # LastApprovalRequest - property - @dbus_service_property(_interface, signature=u"s", access=u"read") - def LastApprovalRequest_dbus_property(self): - if self.last_approval_request is None: - return dbus.String(u"") - return dbus.String(self. - _datetime_to_dbus(self - .last_approval_request)) - - # Timeout - property - @dbus_service_property(_interface, signature=u"t", - access=u"readwrite") - def Timeout_dbus_property(self, value=None): - if value is None: # get - return dbus.UInt64(self.timeout_milliseconds()) - self.timeout = datetime.timedelta(0, 0, 0, value) - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"Timeout"), - dbus.UInt64(value, variant_level=1)) - if getattr(self, u"disable_initiator_tag", None) is None: - return - # Reschedule timeout - gobject.source_remove(self.disable_initiator_tag) - self.disable_initiator_tag = None - time_to_die = (self. - _timedelta_to_milliseconds((self - .last_checked_ok - + self.timeout) - - datetime.datetime - .utcnow())) - if time_to_die <= 0: - # The timeout has passed - self.disable() - else: - self.disable_initiator_tag = (gobject.timeout_add - (time_to_die, self.disable)) - - # Interval - property - @dbus_service_property(_interface, signature=u"t", - access=u"readwrite") - def Interval_dbus_property(self, value=None): - if value is None: # get - return dbus.UInt64(self.interval_milliseconds()) - self.interval = datetime.timedelta(0, 0, 0, value) - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"Interval"), - dbus.UInt64(value, variant_level=1)) - if getattr(self, u"checker_initiator_tag", None) is None: - return - # Reschedule checker run - gobject.source_remove(self.checker_initiator_tag) - self.checker_initiator_tag = (gobject.timeout_add - (value, self.start_checker)) - self.start_checker() # Start one now, too - - # Checker - property - @dbus_service_property(_interface, signature=u"s", - access=u"readwrite") - def Checker_dbus_property(self, value=None): - if value is None: # get - return dbus.String(self.checker_command) - self.checker_command = value - # Emit D-Bus signal - self.PropertyChanged(dbus.String(u"Checker"), - dbus.String(self.checker_command, - variant_level=1)) - - # CheckerRunning - property - @dbus_service_property(_interface, signature=u"b", - access=u"readwrite") - def CheckerRunning_dbus_property(self, value=None): - if value is None: # get - return dbus.Boolean(self.checker is not None) - if value: - self.start_checker() - else: - self.stop_checker() - - # ObjectPath - property - @dbus_service_property(_interface, signature=u"o", access=u"read") - def ObjectPath_dbus_property(self): - return self.dbus_object_path # is already a dbus.ObjectPath - - # Secret = property - @dbus_service_property(_interface, signature=u"ay", - access=u"write", byte_arrays=True) - def Secret_dbus_property(self, value): - self.secret = str(value) - - del _interface - - -class ProxyClient(object): - def __init__(self, child_pipe, fpr, address): - self._pipe = child_pipe - self._pipe.send(('init', fpr, address)) - if not self._pipe.recv(): - raise KeyError() - - def __getattribute__(self, name): - if(name == '_pipe'): - return super(ProxyClient, self).__getattribute__(name) - self._pipe.send(('getattr', name)) - data = self._pipe.recv() - if data[0] == 'data': - return data[1] - if data[0] == 'function': - def func(*args, **kwargs): - self._pipe.send(('funcall', name, args, kwargs)) - return self._pipe.recv()[1] - return func - - def __setattr__(self, name, value): - if(name == '_pipe'): - return super(ProxyClient, self).__setattr__(name, value) - self._pipe.send(('setattr', name, value)) - - -class ClientHandler(socketserver.BaseRequestHandler, object): - """A class to handle client connections. - - Instantiated once for each connection to handle it. + 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) + # 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): - with contextlib.closing(self.server.child_pipe) as child_pipe: - logger.info(u"TCP connection from: %s", - unicode(self.client_address)) - logger.debug(u"Pipe FD: %d", - self.server.child_pipe.fileno()) - - session = (gnutls.connection - .ClientSession(self.request, - gnutls.connection - .X509Credentials())) - - # 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 = u':'.join((u"NONE", u"+VERS-TLS1.1", - # u"+AES-256-CBC", u"+SHA1", - # u"+COMP-NULL", u"+CTYPE-OPENPGP", - # u"+DHE-DSS")) - # Use a fallback default, since this MUST be set. - priority = self.server.gnutls_priority - if priority is None: - priority = u"NORMAL" - (gnutls.library.functions - .gnutls_priority_set_direct(session._c_object, - priority, None)) - - # Start communication using the Mandos protocol - # Get protocol number - 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 - - # Start GnuTLS connection - 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 - logger.debug(u"Handshake succeeded") - - approval_required = False - try: - try: - fpr = self.fingerprint(self.peer_certificate - (session)) - except (TypeError, gnutls.errors.GNUTLSError), error: - logger.warning(u"Bad certificate: %s", error) - return - logger.debug(u"Fingerprint: %s", fpr) - - try: - client = ProxyClient(child_pipe, fpr, - self.client_address) - except KeyError: - return - - if client.approval_delay: - delay = client.approval_delay - client.approvals_pending += 1 - approval_required = True - - while True: - if not client.enabled: - logger.warning(u"Client %s is disabled", - client.name) - if self.server.use_dbus: - # Emit D-Bus signal - client.Rejected("Disabled") - return - - if client._approved or not client.approval_delay: - #We are approved or approval is disabled - break - elif client._approved is None: - logger.info(u"Client %s needs approval", - client.name) - if self.server.use_dbus: - # Emit D-Bus signal - client.NeedApproval( - client.approval_delay_milliseconds(), - client.approved_by_default) - else: - logger.warning(u"Client %s was not approved", - client.name) - if self.server.use_dbus: - # Emit D-Bus signal - client.Rejected("Denied") - return - - #wait until timeout or approved - #x = float(client._timedelta_to_milliseconds(delay)) - time = datetime.datetime.now() - client.changedstate.acquire() - client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000)) - client.changedstate.release() - time2 = datetime.datetime.now() - if (time2 - time) >= delay: - if not client.approved_by_default: - logger.warning("Client %s timed out while" - " waiting for approval", - client.name) - if self.server.use_dbus: - # Emit D-Bus signal - client.Rejected("Approval timed out") - return - else: - break - else: - delay -= time2 - time - - sent_size = 0 - while sent_size < len(client.secret): - try: - sent = session.send(client.secret[sent_size:]) - except (gnutls.errors.GNUTLSError), error: - logger.warning("gnutls send failed") - return - logger.debug(u"Sent: %d, remaining: %d", - sent, len(client.secret) - - (sent_size + sent)) - sent_size += sent - - logger.info(u"Sending secret to %s", client.name) - # bump the timeout as if seen - client.checked_ok() - if self.server.use_dbus: - # Emit D-Bus signal - client.GotSecret() - - finally: - if approval_required: - client.approvals_pending -= 1 - try: - session.bye() - except (gnutls.errors.GNUTLSError), error: - logger.warning("GnuTLS bye failed") - - @staticmethod - 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(1) - cert_list = (gnutls.library.functions - .gnutls_certificate_get_peers - (session._c_object, ctypes.byref(list_size))) - if not bool(cert_list) and list_size.value != 0: - raise gnutls.errors.GNUTLSError(u"error getting peer" - u" certificate") - if list_size.value == 0: - return None - cert = cert_list[0] - return ctypes.string_at(cert.data, cert.size) - - @staticmethod - def fingerprint(openpgp): - "Convert an OpenPGP bytestring to a hexdigit fingerprint" - # 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 - (u"Verify failed")) - # New buffer for the fingerprint - buf = ctypes.create_string_buffer(20) - buf_len = ctypes.c_size_t() - # Get the fingerprint from the certificate into the buffer - (gnutls.library.functions - .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf), - ctypes.byref(buf_len))) - # Deinit the certificate - gnutls.library.functions.gnutls_openpgp_crt_deinit(crt) - # Convert the buffer to a Python bytestring - fpr = ctypes.string_at(buf, buf_len.value) - # Convert the bytestring to hexadecimal notation - hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr) - return hex_fpr - - -class MultiprocessingMixIn(object): - """Like socketserver.ThreadingMixIn, but with multiprocessing""" - def sub_process_main(self, request, address): - try: - self.finish_request(request, address) - except: - self.handle_error(request, address) - self.close_request(request) - - def process_request(self, request, address): - """Start a new process to process the request.""" - multiprocessing.Process(target = self.sub_process_main, - args = (request, address)).start() - -class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object): - """ adds a pipe to the MixIn """ - def process_request(self, request, client_address): - """Overrides and wraps the original process_request(). - - This function creates a new pipe in self.pipe - """ - parent_pipe, self.child_pipe = multiprocessing.Pipe() - - super(MultiprocessingMixInWithPipe, - self).process_request(request, client_address) - self.child_pipe.close() - self.add_pipe(parent_pipe) - - def add_pipe(self, parent_pipe): - """Dummy function; override as necessary""" - pass - -class IPv6_TCPServer(MultiprocessingMixInWithPipe, - socketserver.TCPServer, object): - """IPv6-capable TCP server. Accepts 'None' as address and/or port - + 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): + """IPv6 TCP server. Accepts 'None' as address and/or port. Attributes: - enabled: Boolean; whether this server is activated yet - interface: None or a network interface name (string) - use_ipv6: Boolean; to use IPv6 or not + settings: Server settings + clients: Set() of Client objects """ - def __init__(self, server_address, RequestHandlerClass, - interface=None, use_ipv6=True): - self.interface = interface - if use_ipv6: - self.address_family = socket.AF_INET6 - socketserver.TCPServer.__init__(self, server_address, - RequestHandlerClass) + address_family = socket.AF_INET6 + 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"] + 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.interface is not None: - if SO_BINDTODEVICE is None: - logger.error(u"SO_BINDTODEVICE does not exist;" - u" cannot bind to interface %s", - self.interface) - else: - try: - self.socket.setsockopt(socket.SOL_SOCKET, - SO_BINDTODEVICE, - str(self.interface - + u'\0')) - except socket.error, error: - if error[0] == errno.EPERM: - logger.error(u"No permission to" - u" bind to interface %s", - self.interface) - elif error[0] == errno.ENOPROTOOPT: - logger.error(u"SO_BINDTODEVICE not available;" - u" cannot bind to interface %s", - self.interface) - else: - raise + 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]: - if self.address_family == socket.AF_INET6: - any_address = u"::" # in6addr_any - else: - any_address = socket.INADDR_ANY - self.server_address = (any_address, + 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.interface: +# if self.settings["interface"]: # self.server_address = (self.server_address[0], # 0, # port # 0, # flowinfo # if_nametoindex -# (self.interface)) - return socketserver.TCPServer.server_bind(self) - - -class MandosServer(IPv6_TCPServer): - """Mandos server. - - Attributes: - clients: set of Client objects - gnutls_priority GnuTLS priority string - use_dbus: Boolean; to emit D-Bus signals or not - - Assumes a gobject.MainLoop event loop. - """ - def __init__(self, server_address, RequestHandlerClass, - interface=None, use_ipv6=True, clients=None, - gnutls_priority=None, use_dbus=True): - self.enabled = False - self.clients = clients - if self.clients is None: - self.clients = set() - self.use_dbus = use_dbus - self.gnutls_priority = gnutls_priority - IPv6_TCPServer.__init__(self, server_address, - RequestHandlerClass, - interface = interface, - use_ipv6 = use_ipv6) - def server_activate(self): - if self.enabled: - return socketserver.TCPServer.server_activate(self) - def enable(self): - self.enabled = True - def add_pipe(self, parent_pipe): - # Call "handle_ipc" for both data and EOF events - gobject.io_add_watch(parent_pipe.fileno(), - gobject.IO_IN | gobject.IO_HUP, - functools.partial(self.handle_ipc, - parent_pipe = parent_pipe)) - - def handle_ipc(self, source, condition, parent_pipe=None, - client_object=None): - condition_names = { - gobject.IO_IN: u"IN", # There is data to read. - gobject.IO_OUT: u"OUT", # Data can be written (without - # blocking). - gobject.IO_PRI: u"PRI", # There is urgent data to read. - gobject.IO_ERR: u"ERR", # Error condition. - gobject.IO_HUP: u"HUP" # Hung up (the connection has been - # broken, usually for pipes and - # sockets). - } - conditions_string = ' | '.join(name - for cond, name in - condition_names.iteritems() - if cond & condition) - # error or the other end of multiprocessing.Pipe has closed - if condition & (gobject.IO_ERR | condition & gobject.IO_HUP): - return False - - # Read a request from the child - request = parent_pipe.recv() - command = request[0] - - if command == 'init': - fpr = request[1] - address = request[2] - - for c in self.clients: - if c.fingerprint == fpr: - client = c - break - else: - logger.warning(u"Client not found for fingerprint: %s, ad" - u"dress: %s", fpr, address) - if self.use_dbus: - # Emit D-Bus signal - mandos_dbus_service.ClientNotFound(fpr, address[0]) - parent_pipe.send(False) - return False - - gobject.io_add_watch(parent_pipe.fileno(), - gobject.IO_IN | gobject.IO_HUP, - functools.partial(self.handle_ipc, - parent_pipe = parent_pipe, - client_object = client)) - parent_pipe.send(True) - # remove the old hook in favor of the new above hook on same fileno - return False - if command == 'funcall': - funcname = request[1] - args = request[2] - kwargs = request[3] - - parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs))) - - if command == 'getattr': - attrname = request[1] - if callable(client_object.__getattribute__(attrname)): - parent_pipe.send(('function',)) - else: - parent_pipe.send(('data', client_object.__getattribute__(attrname))) - - if command == 'setattr': - attrname = request[1] - value = request[2] - setattr(client_object, attrname, value) - - return True +# (self.settings +# ["interface"])) + return super(type(self), self).server_bind() def string_to_delta(interval): """Parse a string and return a datetime.timedelta - - >>> string_to_delta(u'7d') + + >>> string_to_delta('7d') datetime.timedelta(7) - >>> string_to_delta(u'60s') + >>> string_to_delta('60s') datetime.timedelta(0, 60) - >>> string_to_delta(u'60m') + >>> string_to_delta('60m') datetime.timedelta(0, 3600) - >>> string_to_delta(u'24h') + >>> string_to_delta('24h') datetime.timedelta(1) >>> string_to_delta(u'1w') datetime.timedelta(7) - >>> string_to_delta(u'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(u"Unknown suffix %r" % suffix) - except (ValueError, IndexError), e: - raise ValueError(e.message) - timevalue += delta - return timevalue - + try: + suffix=unicode(interval[-1]) + value=int(interval[:-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 + return delta + + +def server_state_changed(state): + """Derived from the Avahi example code""" + if state == avahi.SERVER_COLLISION: + logger.error(u"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"state change: %i", state) + + if state == avahi.ENTRY_GROUP_ESTABLISHED: + logger.debug(u"Service established.") + elif state == avahi.ENTRY_GROUP_COLLISION: + logger.warning(u"Service name collision.") + service.rename() + elif state == avahi.ENTRY_GROUP_FAILURE: + logger.critical(u"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 - - Note: This function cannot accept a unicode string.""" + """Call the C function if_nametoindex(), or equivalent""" global if_nametoindex try: - if_nametoindex = (ctypes.cdll.LoadLibrary - (ctypes.util.find_library(u"c")) - .if_nametoindex) + 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): - logger.warning(u"Doing if_nametoindex the hard way") + 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 - with contextlib.closing(socket.socket()) as s: - ifreq = fcntl.ioctl(s, SIOCGIFINDEX, - struct.pack(str(u"16s16x"), - interface)) - interface_index = struct.unpack(str(u"I"), - ifreq[16:20])[0] + 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(u"/") + os.chdir("/") if os.fork(): sys.exit() if not noclose: @@ -1658,8 +638,7 @@ 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, - u"%s not a character device" - % os.path.devnull) + "/dev/null not a character device") os.dup2(null, sys.stdin.fileno()) os.dup2(null, sys.stdout.fileno()) os.dup2(null, sys.stderr.fileno()) @@ -1668,38 +647,30 @@ def main(): - - ################################################################## - # Parsing of options, both command line and config file - - parser = optparse.OptionParser(version = "%%prog %s" % version) - parser.add_option("-i", u"--interface", type=u"string", - metavar="IF", help=u"Bind to interface IF") - parser.add_option("-a", u"--address", type=u"string", - help=u"Address to listen for requests on") - parser.add_option("-p", u"--port", type=u"int", - help=u"Port number to receive requests on") - parser.add_option("--check", action=u"store_true", - help=u"Run self-test") - parser.add_option("--debug", action=u"store_true", - help=u"Debug mode; run in foreground and log to" - u" terminal") - parser.add_option("--debuglevel", type=u"string", metavar="LEVEL", - help=u"Debug level for stdout output") - parser.add_option("--priority", type=u"string", help=u"GnuTLS" - u" priority string (see GnuTLS documentation)") - parser.add_option("--servicename", type=u"string", - metavar=u"NAME", help=u"Zeroconf service name") - parser.add_option("--configdir", type=u"string", - default=u"/etc/mandos", metavar=u"DIR", - help=u"Directory to search for configuration" - u" files") - parser.add_option("--no-dbus", action=u"store_false", - dest=u"use_dbus", help=u"Do not provide D-Bus" - u" system bus interface") - parser.add_option("--no-ipv6", action=u"store_false", - dest=u"use_ipv6", help=u"Do not use IPv6") - options = parser.parse_args()[0] + global main_loop_started + main_loop_started = False + + parser = OptionParser() + parser.add_option("-i", "--interface", type="string", + 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("--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() if options.check: import doctest @@ -1707,341 +678,150 @@ sys.exit() # Default values for config file for server-global settings - server_defaults = { u"interface": u"", - u"address": u"", - u"port": u"", - u"debug": u"False", - u"priority": - u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP", - u"servicename": u"Mandos", - u"use_dbus": u"True", - u"use_ipv6": u"True", - u"debuglevel": u"", + 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) + server_config = ConfigParser.SafeConfigParser(server_defaults) del server_defaults - server_config.read(os.path.join(options.configdir, - u"mandos.conf")) + server_config.read(os.path.join(options.configdir, "mandos.conf")) + server_section = "server" # Convert the SafeConfigParser object to a dict - server_settings = server_config.defaults() - # Use the appropriate methods on the non-string config options - for option in (u"debug", u"use_dbus", u"use_ipv6"): - server_settings[option] = server_config.getboolean(u"DEFAULT", - option) - if server_settings["port"]: - server_settings["port"] = server_config.getint(u"DEFAULT", - u"port") + server_settings = dict(server_config.items(server_section)) + # Use getboolean on the boolean config option + server_settings["debug"] = server_config.getboolean\ + (server_section, "debug") del server_config # Override the settings from the config file with command line # options, if set. - for option in (u"interface", u"address", u"port", u"debug", - u"priority", u"servicename", u"configdir", - u"use_dbus", u"use_ipv6", u"debuglevel"): + 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 - # Force all strings to be unicode - for option in server_settings.keys(): - if type(server_settings[option]) is str: - server_settings[option] = unicode(server_settings[option]) # Now we have our good server settings in "server_settings" - ################################################################## - - # For convenience - debug = server_settings[u"debug"] - debuglevel = server_settings[u"debuglevel"] - use_dbus = server_settings[u"use_dbus"] - use_ipv6 = server_settings[u"use_ipv6"] - - if server_settings[u"servicename"] != u"Mandos": - syslogger.setFormatter(logging.Formatter - (u'Mandos (%s) [%%(process)d]:' - u' %%(levelname)s: %%(message)s' - % server_settings[u"servicename"])) - # Parse config file with clients - client_defaults = { u"timeout": u"1h", - u"interval": u"5m", - u"checker": u"fping -q -- %%(host)s", - u"host": u"", - u"approval_delay": u"0s", - u"approval_duration": u"1s", + client_defaults = { "timeout": "1h", + "interval": "5m", + "checker": "fping -q -- %%(fqdn)s", } - client_config = configparser.SafeConfigParser(client_defaults) - client_config.read(os.path.join(server_settings[u"configdir"], - u"clients.conf")) - - global mandos_dbus_service - mandos_dbus_service = None - - tcp_server = MandosServer((server_settings[u"address"], - server_settings[u"port"]), - ClientHandler, - interface=(server_settings[u"interface"] - or None), - use_ipv6=use_ipv6, - gnutls_priority= - server_settings[u"priority"], - use_dbus=use_dbus) - if not debug: - pidfilename = u"/var/run/mandos.pid" - try: - pidfile = open(pidfilename, u"w") - except IOError: - logger.error(u"Could not open file %r", pidfilename) - - try: - uid = pwd.getpwnam(u"_mandos").pw_uid - gid = pwd.getpwnam(u"_mandos").pw_gid - except KeyError: - try: - uid = pwd.getpwnam(u"mandos").pw_uid - gid = pwd.getpwnam(u"mandos").pw_gid - except KeyError: - try: - uid = pwd.getpwnam(u"nobody").pw_uid - gid = pwd.getpwnam(u"nobody").pw_gid - except KeyError: - uid = 65534 - gid = 65534 - try: - os.setgid(gid) - os.setuid(uid) - except OSError, error: - if error[0] != errno.EPERM: - raise error - - if not debug and not debuglevel: - syslogger.setLevel(logging.WARNING) - console.setLevel(logging.WARNING) - if debuglevel: - level = getattr(logging, debuglevel.upper()) - syslogger.setLevel(level) - console.setLevel(level) - - if debug: - # Enable all possible GnuTLS debugging - - # "Use a log level over 10 to enable all debugging options." - # - GnuTLS manual - gnutls.library.functions.gnutls_global_set_log_level(11) - - @gnutls.library.types.gnutls_log_func - def debug_gnutls(level, string): - logger.debug(u"GnuTLS: %s", string[:-1]) - - (gnutls.library.functions - .gnutls_global_set_log_function(debug_gnutls)) - - # 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) - + client_config = ConfigParser.SafeConfigParser(client_defaults) + client_config.read(os.path.join(server_settings["configdir"], + "clients.conf")) + + 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 - if use_dbus: - try: - bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", - bus, do_not_queue=True) - except dbus.exceptions.NameExistsException, e: - logger.error(unicode(e) + u", disabling D-Bus") - use_dbus = False - server_settings[u"use_dbus"] = False - tcp_server.use_dbus = False - protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET - service = AvahiService(name = server_settings[u"servicename"], - servicetype = u"_mandos._tcp", - protocol = protocol, bus = bus) - if server_settings["interface"]: - service.interface = (if_nametoindex - (str(server_settings[u"interface"]))) - + + debug = server_settings["debug"] + + if debug: + console = logging.StreamHandler() + # console.setLevel(logging.DEBUG) + console.setFormatter(logging.Formatter\ + ('%(levelname)s: %(message)s')) + logger.addHandler(console) + del console + + clients = Set() + 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 debug: - # Close all input and output, do double fork, etc. daemon() - - global multiprocessing_manager - multiprocessing_manager = multiprocessing.Manager() - - client_class = Client - if use_dbus: - client_class = functools.partial(ClientDBus, bus = bus) - def client_config_items(config, section): - special_settings = { - "approved_by_default": - lambda: config.getboolean(section, - "approved_by_default"), - } - for name, value in config.items(section): - try: - yield (name, special_settings[name]()) - except KeyError: - yield (name, value) - - tcp_server.clients.update(set( - client_class(name = section, - config= dict(client_config_items( - client_config, section))) - for section in client_config.sections())) - if not tcp_server.clients: - logger.warning(u"No clients defined") - + + 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: - try: - with pidfile: - pid = os.getpid() - pidfile.write(str(pid) + "\n") - del pidfile - except IOError: - logger.error(u"Could not write to file %r with PID %d", - pidfilename, pid) - except NameError: - # "pidfile" was never created - pass - del pidfilename - 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()) - if use_dbus: - class MandosDBusService(dbus.service.Object): - """A D-Bus proxy object""" - def __init__(self): - dbus.service.Object.__init__(self, bus, u"/") - _interface = u"se.bsnet.fukt.Mandos" - - @dbus.service.signal(_interface, signature=u"o") - def ClientAdded(self, objpath): - "D-Bus signal" - pass - - @dbus.service.signal(_interface, signature=u"ss") - def ClientNotFound(self, fingerprint, address): - "D-Bus signal" - pass - - @dbus.service.signal(_interface, signature=u"os") - def ClientRemoved(self, objpath, name): - "D-Bus signal" - pass - - @dbus.service.method(_interface, out_signature=u"ao") - def GetAllClients(self): - "D-Bus method" - return dbus.Array(c.dbus_object_path - for c in tcp_server.clients) - - @dbus.service.method(_interface, - out_signature=u"a{oa{sv}}") - def GetAllClientsWithProperties(self): - "D-Bus method" - return dbus.Dictionary( - ((c.dbus_object_path, c.GetAll(u"")) - for c in tcp_server.clients), - signature=u"oa{sv}") - - @dbus.service.method(_interface, in_signature=u"o") - def RemoveClient(self, object_path): - "D-Bus method" - for c in tcp_server.clients: - if c.dbus_object_path == object_path: - tcp_server.clients.remove(c) - c.remove_from_connection() - # Don't signal anything except ClientRemoved - c.disable(quiet=True) - # Emit D-Bus signal - self.ClientRemoved(object_path, c.name) - return - raise KeyError(object_path) - - del _interface - - mandos_dbus_service = MandosDBusService() - - def cleanup(): - "Cleanup function; run on exit" - service.cleanup() - - while tcp_server.clients: - client = tcp_server.clients.pop() - if use_dbus: - client.remove_from_connection() - client.disable_hook = None - # Don't signal anything except ClientRemoved - client.disable(quiet=True) - if use_dbus: - # Emit D-Bus signal - mandos_dbus_service.ClientRemoved(client.dbus_object_path, - client.name) - - atexit.register(cleanup) - - for client in tcp_server.clients: - if use_dbus: - # Emit D-Bus signal - mandos_dbus_service.ClientAdded(client.dbus_object_path) - client.enable() - - tcp_server.enable() - tcp_server.server_activate() - + for client in clients: + client.start() + + tcp_server = IPv6_TCPServer((server_settings["address"], + server_settings["port"]), + tcp_handler, + settings=server_settings, + clients=clients) # Find out what port we got service.port = tcp_server.socket.getsockname()[1] - if use_ipv6: - logger.info(u"Now listening on address %r, port %d," - " flowinfo %d, scope_id %d" - % tcp_server.socket.getsockname()) - else: # IPv4 - logger.info(u"Now listening on address %r, port %d" - % tcp_server.socket.getsockname()) + 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: - service.activate() + server_state_changed(server.GetState()) except dbus.exceptions.DBusException, error: logger.critical(u"DBusException: %s", error) - cleanup() 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)) + tcp_server.handle_request\ + (*args[2:], **kwargs) or True) - logger.debug(u"Starting main loop") + logger.debug("Starting main loop") + main_loop_started = True main_loop.run() except AvahiError, error: - logger.critical(u"AvahiError: %s", error) - cleanup() + logger.critical(u"AvahiError: %s" + unicode(error)) sys.exit(1) except KeyboardInterrupt: if debug: - print >> sys.stderr - logger.debug(u"Server received KeyboardInterrupt") - logger.debug(u"Server exiting") - # Must run before the D-Bus bus name gets deregistered - cleanup() + print if __name__ == '__main__': main() === renamed file 'plugin-runner.c' => 'mandos-client.c' --- plugin-runner.c 2010-09-26 21:27:28 +0000 +++ mandos-client.c 2008-08-07 21:45:41 +0000 @@ -1,9 +1,8 @@ -/* -*- coding: utf-8; mode: c; mode: orgtbl -*- */ +/* -*- coding: utf-8 -*- */ /* * Mandos plugin runner - Run Mandos plugins * - * Copyright © 2008-2010 Teddy Hogeborn - * Copyright © 2008-2010 Björn Påhlsson + * 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 @@ -22,329 +21,145 @@ * Contact the authors at . */ -#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), getline(), - asprintf(), O_CLOEXEC */ -#include /* size_t, NULL */ -#include /* malloc(), exit(), EXIT_SUCCESS, - realloc() */ -#include /* bool, true, false */ -#include /* fileno(), fprintf(), +#define _GNU_SOURCE /* TEMP_FAILURE_RETRY() */ + +#include /* popen(), fileno(), fprintf(), stderr, STDOUT_FILENO */ -#include /* DIR, fdopendir(), 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(), WTERMSIG(), - WCOREDUMP() */ +#include /* and, or, not */ +#include /* DIR, opendir(), stat(), + struct stat, waitpid(), + WIFEXITED(), WEXITSTATUS(), + wait() */ +#include /* wait() */ +#include /* DIR, struct dirent, opendir(), + readdir(), closedir() */ #include /* struct stat, stat(), S_ISREG() */ -#include /* and, or, not */ -#include /* DIR, struct dirent, fdopendir(), - 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(), - strsignal(), strcmp(), strncmp() */ + fcntl() */ +#include /* fcntl() */ +#include /* NULL */ +#include /* EXIT_FAILURE */ +#include /* fd_set, select(), FD_ZERO(), + FD_SET(), FD_ISSET() */ +#include /* strlen(), strcpy(), strcat() */ +#include /* true */ +#include /* waitpid(), WIFEXITED(), + WEXITSTATUS() */ #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(), sig_atomic_t - */ -#include /* errno, EBADF */ -#include /* intmax_t, PRIdMAX, strtoimax() */ -#include /* EX_OSERR, EX_USAGE, EX_IOERR, - EX_CONFIG, EX_UNAVAILABLE, EX_OK */ -#include /* errno */ -#include /* error() */ +#include /* struct argp_option, + struct argp_state, struct argp, + argp_parse() */ #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 " VERSION; -const char *argp_program_bug_address = ""; - -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*/ +struct process; + +typedef struct process{ pid_t pid; int fd; char *buffer; size_t buffer_size; size_t buffer_length; bool eof; - volatile sig_atomic_t completed; + bool completed; int status; + struct process *next; +} process; + +typedef struct plugin{ + char *name; /* can be NULL or any plugin name */ + char **argv; + int argc; + bool disabled; 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 existing 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))){ +static plugin *getplugin(char *name, plugin **plugin_list){ + 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 = NULL; - do { - new_plugin = malloc(sizeof(plugin)); - } while(new_plugin == NULL and errno == EINTR); - if(new_plugin == NULL){ - return NULL; - } - char *copy_name = NULL; - if(name != NULL){ - do { - copy_name = strdup(name); - } while(copy_name == NULL and errno == EINTR); - if(copy_name == NULL){ - int e = errno; - free(new_plugin); - errno = e; - return NULL; - } - } - - *new_plugin = (plugin){ .name = copy_name, - .argc = 1, - .disabled = false, - .next = plugin_list }; - - do { - new_plugin->argv = malloc(sizeof(char *) * 2); - } while(new_plugin->argv == NULL and errno == EINTR); - if(new_plugin->argv == NULL){ - int e = errno; - free(copy_name); - free(new_plugin); - errno = e; - return NULL; - } - new_plugin->argv[0] = copy_name; + plugin *new_plugin = malloc(sizeof(plugin)); + if (new_plugin == NULL){ + perror("malloc"); + exit(EXIT_FAILURE); + } + new_plugin->name = name; + new_plugin->argv = malloc(sizeof(char *) * 2); + if (new_plugin->argv == NULL){ + perror("malloc"); + exit(EXIT_FAILURE); + } + new_plugin->argv[0] = name; new_plugin->argv[1] = NULL; - - do { - new_plugin->environ = malloc(sizeof(char *)); - } while(new_plugin->environ == NULL and errno == EINTR); - if(new_plugin->environ == NULL){ - int e = errno; - free(copy_name); - free(new_plugin->argv); - free(new_plugin); - errno = e; - return NULL; - } - new_plugin->environ[0] = NULL; - + new_plugin->argc = 1; + new_plugin->disabled = false; + new_plugin->next = *plugin_list; /* Append the new plugin to the list */ - plugin_list = new_plugin; + *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 */ - do { - *array = realloc(*array, sizeof(char *) - * (size_t) ((*len) + 2)); - } while(*array == NULL and errno == EINTR); - /* Malloc check */ - if(*array == NULL){ - return false; - } - /* Make a copy of the new string */ - char *copy; - do { - copy = strdup(new); - } while(copy == NULL and errno == EINTR); - 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; - do { - new = realloc(*e, strlen(def) + 1); - } while(new == NULL and errno == EINTR); - if(new == NULL){ - return false; - } - *e = new; - strcpy(*e, def); - } - return true; - } - } - return add_to_char_array(def, &(p->environ), &(p->envc)); +static void addargument(plugin *p, char *arg){ + p->argv[p->argc] = arg; + p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2)); + if (p->argv == NULL){ + perror("malloc"); + exit(EXIT_FAILURE); + } + p->argc++; + p->argv[p->argc] = NULL; } /* * Based on the example in the GNU LibC manual chapter 13.13 "File * Descriptor Flags". - | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] | + * *Note File Descriptor Flags:(libc)Descriptor Flags. */ -static int set_cloexec_flag(int fd){ - int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0)); +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 (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD, - ret | FD_CLOEXEC)); + return fcntl(fd, F_SETFD, ret | FD_CLOEXEC); } - -/* Mark processes as completed when they exit, and save their exit +const char *argp_program_version = "plugbasedclient 0.9"; +const char *argp_program_bug_address = ""; + +process *process_list = NULL; + +/* Mark a process as completed when it exits, and save its exit status. */ -static void handle_sigchld(__attribute__((unused)) int sig){ - int old_errno = errno; - 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){ - /* No child processes */ - break; - } - error(0, errno, "waitpid"); - } - - /* 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 = 1; - } - errno = old_errno; -} - -/* Prints out a password to stdout */ -static 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); - } +void handle_sigchld(__attribute__((unused)) int sig){ + process *proc = process_list; + int status; + pid_t pid = wait(&status); + while(proc != NULL and proc->pid != pid){ + proc = proc->next; + } + if(proc == NULL){ + /* Process not found in process list */ + return; + } + proc->status = status; + proc->completed = true; } int main(int argc, char *argv[]){ - char *plugindir = NULL; - char *argfile = NULL; - FILE *conffp; + const char *plugindir = "/conf/conf.d/mandos/plugins.d"; size_t d_name_len; DIR *dir = NULL; struct dirent *dirst; struct stat st; fd_set rfds_all; int ret, maxfd = 0; - ssize_t sret; uid_t uid = 65534; gid_t gid = 65534; bool debug = false; @@ -352,22 +167,20 @@ 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; + char *plus_options = NULL; + char **plus_argv = NULL; /* Establish a signal handler */ sigemptyset(&sigchld_action.sa_mask); ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD); - if(ret == -1){ - error(0, errno, "sigaddset"); - exitstatus = EX_OSERR; - goto fallback; + if(ret < 0){ + perror("sigaddset"); + exit(EXIT_FAILURE); } ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action); - if(ret == -1){ - error(0, errno, "sigaction"); - exitstatus = EX_OSERR; - goto fallback; + if(ret < 0){ + perror("sigaction"); + exit(EXIT_FAILURE); } /* The options we understand. */ @@ -375,357 +188,123 @@ { .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 }, - /* - * These reproduce what we would get without ARGP_NO_HELP - */ - { .name = "help", .key = '?', - .doc = "Give this help list", .group = -1 }, - { .name = "usage", .key = -3, - .doc = "Give a short usage message", .group = -1 }, - { .name = "version", .key = 'V', - .doc = "Print program version", .group = -1 }, + { .name = "userid", .key = 129, + .arg = "ID", .flags = 0, + .doc = "User ID the plugins will run as", .group = 2 }, + { .name = "groupid", .key = 130, + .arg = "ID", .flags = 0, + .doc = "Group ID the plugins will run as", .group = 2 }, + { .name = "debug", .key = 131, + .doc = "Debug mode", .group = 3 }, { .name = NULL } }; - error_t parse_opt(int key, char *arg, struct argp_state *state){ - errno = 0; - switch(key){ - char *tmp; - intmax_t tmpmax; - case 'g': /* --global-options */ - { - char *plugin_option; - while((plugin_option = strsep(&arg, ",")) != NULL){ - if(not add_argument(getplugin(NULL), plugin_option)){ - break; - } - } - } - break; - case 'G': /* --global-env */ - add_environment(getplugin(NULL), arg, true); - break; - case 'o': /* --options-for */ - { - char *option_list = strchr(arg, ':'); - if(option_list == NULL){ - argp_error(state, "No colon in \"%s\"", arg); - errno = EINVAL; - break; - } - *option_list = '\0'; - option_list++; - if(arg[0] == '\0'){ - argp_error(state, "Empty plugin name"); - errno = EINVAL; - break; - } - char *option; - while((option = strsep(&option_list, ",")) != NULL){ - if(not add_argument(getplugin(arg), option)){ - break; - } - } - } - break; - case 'E': /* --env-for */ - { - char *envdef = strchr(arg, ':'); - if(envdef == NULL){ - argp_error(state, "No colon in \"%s\"", arg); - errno = EINVAL; - break; - } - *envdef = '\0'; - envdef++; - if(arg[0] == '\0'){ - argp_error(state, "Empty plugin name"); - errno = EINVAL; - break; - } - add_environment(getplugin(arg), envdef, true); - } - break; - case 'd': /* --disable */ - { - plugin *p = getplugin(arg); - if(p != NULL){ - p->disabled = true; - } - } - break; - case 'e': /* --enable */ - { - plugin *p = getplugin(arg); - if(p != NULL){ - p->disabled = false; - } - } - break; - case 128: /* --plugin-dir */ - free(plugindir); - plugindir = strdup(arg); - break; - case 129: /* --config-file */ - /* This is already done by parse_opt_config_file() */ - break; - case 130: /* --userid */ - tmpmax = strtoimax(arg, &tmp, 10); - if(errno != 0 or tmp == arg or *tmp != '\0' - or tmpmax != (uid_t)tmpmax){ - argp_error(state, "Bad user ID number: \"%s\", using %" - PRIdMAX, arg, (intmax_t)uid); - break; - } - uid = (uid_t)tmpmax; - break; - case 131: /* --groupid */ - tmpmax = strtoimax(arg, &tmp, 10); - if(errno != 0 or tmp == arg or *tmp != '\0' - or tmpmax != (gid_t)tmpmax){ - argp_error(state, "Bad group ID number: \"%s\", using %" - PRIdMAX, arg, (intmax_t)gid); - break; - } - gid = (gid_t)tmpmax; - break; - case 132: /* --debug */ + 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. */ + plugin **plugins = state->input; + switch (key) { + case 'g': + if (arg != NULL){ + char *p = strtok(arg, ","); + do{ + addargument(getplugin(NULL, plugins), p); + p = strtok(NULL, ","); + } while (p != NULL); + } + break; + case 'o': + if (arg != NULL){ + char *name = strtok(arg, ":"); + char *p = strtok(NULL, ":"); + if(p != NULL){ + p = strtok(p, ","); + do{ + addargument(getplugin(name, plugins), p); + p = strtok(NULL, ","); + } while (p != NULL); + } + } + break; + case 'd': + if (arg != NULL){ + getplugin(arg, plugins)->disabled = true; + } + break; + case 128: + plugindir = arg; + break; + case 129: + uid = (uid_t)strtol(arg, NULL, 10); + break; + case 130: + gid = (gid_t)strtol(arg, NULL, 10); + break; + case 131: debug = true; break; - /* - * These reproduce what we would get without ARGP_NO_HELP - */ - case '?': /* --help */ - state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */ - argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP); - case -3: /* --usage */ - state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */ - argp_state_help(state, state->out_stream, - ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK); - case 'V': /* --version */ - fprintf(state->out_stream, "%s\n", argp_program_version); - exit(EXIT_SUCCESS); - break; -/* - * When adding more options before this line, remember to also add a - * "case" to the "parse_opt_config_file" function below. - */ 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'){ - break; + if(plus_options != NULL or arg == NULL or arg[0] != '+'){ + argp_usage (state); } - default: - return ARGP_ERR_UNKNOWN; - } - return errno; /* Set to 0 at start */ - } - - /* 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){ - errno = 0; - 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); - break; - case 130: /* --userid */ - case 131: /* --groupid */ - case 132: /* --debug */ - case '?': /* --help */ - case -3: /* --usage */ - case 'V': /* --version */ - case ARGP_KEY_ARG: - break; - default: - return ARGP_ERR_UNKNOWN; - } - return errno; - } - - struct argp argp = { .options = options, - .parser = parse_opt_config_file, - .args_doc = "", + plus_options = arg; + break; + case ARGP_KEY_END: + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; + } + + plugin *plugin_list = NULL; + + struct argp argp = { .options = options, .parser = parse_opt, + .args_doc = "[+PLUS_SEPARATED_OPTIONS]", .doc = "Mandos plugin runner -- Run plugins" }; - /* Parse using parse_opt_config_file() in order to get the custom - config file location, if any. */ - ret = argp_parse(&argp, argc, argv, - ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP, - NULL, NULL); - switch(ret){ - case 0: - break; - case ENOMEM: - default: - errno = ret; - error(0, errno, "argp_parse"); - exitstatus = EX_OSERR; - goto fallback; - case EINVAL: - exitstatus = EX_USAGE; - 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; - 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){ - error(0, errno, "malloc"); - exitstatus = EX_OSERR; - 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){ - error(0, errno, "strdup"); - exitstatus = EX_OSERR; - free(org_line); - goto fallback; - } - - custom_argc += 1; - custom_argv = realloc(custom_argv, sizeof(char *) - * ((unsigned int) custom_argc + 1)); - if(custom_argv == NULL){ - error(0, errno, "realloc"); - exitstatus = EX_OSERR; - free(org_line); - goto fallback; - } - custom_argv[custom_argc-1] = new_arg; - custom_argv[custom_argc] = NULL; - } - } - do { - ret = fclose(conffp); - } while(ret == EOF and errno == EINTR); - if(ret == EOF){ - error(0, errno, "fclose"); - exitstatus = EX_IOERR; - goto fallback; - } - 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){ - error(0, errno, "fopen"); - exitstatus = EX_OSERR; - goto fallback; - } - } - /* If there were any arguments from the configuration file, pass - them to parser as command line arguments */ - if(custom_argv != NULL){ - ret = argp_parse(&argp, custom_argc, custom_argv, - ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP, - NULL, NULL); - switch(ret){ - case 0: - break; - case ENOMEM: - default: - errno = ret; - error(0, errno, "argp_parse"); - exitstatus = EX_OSERR; - goto fallback; - case EINVAL: - exitstatus = EX_CONFIG; - goto fallback; - } - } - - /* Parse actual command line arguments, to let them override the - config file */ - ret = argp_parse(&argp, argc, argv, - ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP, - NULL, NULL); - switch(ret){ - case 0: - break; - case ENOMEM: - default: - errno = ret; - error(0, errno, "argp_parse"); - exitstatus = EX_OSERR; - goto fallback; - case EINVAL: - exitstatus = EX_USAGE; - goto fallback; + argp_parse (&argp, argc, argv, 0, 0, &plugin_list); + + if(plus_options){ + /* This is a mangled argument in the form of + "+--option+--other-option=parameter+--yet-another-option", etc */ + /* Make new argc and argv vars, and call argp_parse() again. */ + plus_options++; /* skip the first '+' character */ + const char delims[] = "+"; + char *arg; + int new_argc = 1; + plus_argv = malloc(sizeof(char*) * 2); + if(plus_argv == NULL){ + perror("malloc"); + exitstatus = EXIT_FAILURE; + goto end; + } + plus_argv[0] = argv[0]; + plus_argv[1] = NULL; + arg = strtok(plus_options, delims); /* Get first argument */ + while(arg != NULL){ + new_argc++; + plus_argv = realloc(plus_argv, sizeof(char *) + * ((unsigned int) new_argc + 1)); + if(plus_argv == NULL){ + perror("malloc"); + exitstatus = EXIT_FAILURE; + goto end; + } + plus_argv[new_argc-1] = arg; + plus_argv[new_argc] = NULL; + arg = strtok(NULL, delims); /* Get next argument */ + } + argp_parse (&argp, new_argc, plus_argv, 0, 0, &plugin_list); } if(debug){ @@ -735,90 +314,52 @@ for(char **a = p->argv; *a != NULL; a++){ fprintf(stderr, "\tArg: %s\n", *a); } - fprintf(stderr, "...and %d 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){ - error(0, errno, "setgid"); - } - ret = setuid(uid); - if(ret == -1){ - error(0, errno, "setuid"); - } - - /* Open plugin directory with close_on_exec flag */ + if (ret == -1){ + perror("setgid"); + } + + dir = opendir(plugindir); + if(dir == NULL){ + perror("Could not open plugin dir"); + exitstatus = EXIT_FAILURE; + goto end; + } + + /* Set the FD_CLOEXEC flag on the directory, if possible */ { - int dir_fd = -1; - if(plugindir == NULL){ - dir_fd = open(PDIR, O_RDONLY | -#ifdef O_CLOEXEC - O_CLOEXEC -#else /* not O_CLOEXEC */ - 0 -#endif /* not O_CLOEXEC */ - ); - } else { - dir_fd = open(plugindir, O_RDONLY | -#ifdef O_CLOEXEC - O_CLOEXEC -#else /* not O_CLOEXEC */ - 0 -#endif /* not O_CLOEXEC */ - ); - } - if(dir_fd == -1){ - error(0, errno, "Could not open plugin dir"); - exitstatus = EX_UNAVAILABLE; - goto fallback; - } - -#ifndef O_CLOEXEC - /* Set the FD_CLOEXEC flag on the directory */ - ret = set_cloexec_flag(dir_fd); - if(ret < 0){ - error(0, errno, "set_cloexec_flag"); - TEMP_FAILURE_RETRY(close(dir_fd)); - exitstatus = EX_OSERR; - goto fallback; - } -#endif /* O_CLOEXEC */ - - dir = fdopendir(dir_fd); - if(dir == NULL){ - error(0, errno, "Could not open plugin dir"); - TEMP_FAILURE_RETRY(close(dir_fd)); - exitstatus = EX_OSERR; - goto fallback; + 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 end; + } } } FD_ZERO(&rfds_all); - /* Read and execute any executable in the plugin directory*/ while(true){ - do { - dirst = readdir(dir); - } while(dirst == NULL and errno == EINTR); + dirst = readdir(dir); - /* All directory entries have been processed */ + // All directory entries have been processed if(dirst == NULL){ - if(errno == EBADF){ - error(0, errno, "readdir"); - exitstatus = EX_IOERR; - goto fallback; - } break; } d_name_len = strlen(dirst->d_name); - /* Ignore dotfiles, backup files and other junk */ + // Ignore dotfiles, backup files and other junk { bool bad_name = false; @@ -826,7 +367,6 @@ const char const *bad_suffixes[] = { "~", "#", ".dpkg-new", ".dpkg-old", - ".dpkg-bak", ".dpkg-divert", NULL }; for(const char **pre = bad_prefixes; *pre != NULL; pre++){ size_t pre_len = strlen(*pre); @@ -840,13 +380,15 @@ 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) + and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf) == 0)){ if(debug){ fprintf(stderr, "Ignoring plugin dir entry \"%s\"" @@ -862,30 +404,19 @@ } } - char *filename; - if(plugindir == NULL){ - ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s", - dirst->d_name)); - } else { - ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s", - plugindir, - dirst->d_name)); - } - if(ret < 0){ - error(0, errno, "asprintf"); - continue; - } - - ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st)); - if(ret == -1){ - error(0, errno, "stat"); - free(filename); - continue; - } - - /* Ignore non-executable files */ - if(not S_ISREG(st.st_mode) - or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){ + char *filename = malloc(d_name_len + strlen(plugindir) + 2); + if (filename == NULL){ + perror("malloc"); + exitstatus = EXIT_FAILURE; + goto end; + } + strcpy(filename, plugindir); /* Spurious warning */ + strcat(filename, "/"); /* Spurious warning */ + strcat(filename, dirst->d_name); /* Spurious warning */ + + stat(filename, &st); + + 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); @@ -893,14 +424,7 @@ free(filename); continue; } - - plugin *p = getplugin(dirst->d_name); - if(p == NULL){ - error(0, errno, "getplugin"); - free(filename); - continue; - } - if(p->disabled){ + if(getplugin(dirst->d_name, &plugin_list)->disabled){ if(debug){ fprintf(stderr, "Ignoring disabled plugin \"%s\"\n", dirst->d_name); @@ -908,351 +432,265 @@ free(filename); continue; } + plugin *p = getplugin(dirst->d_name, &plugin_list); { /* 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)){ - error(0, errno, "add_argument"); - } - } - /* Add global environment variables */ - for(char **e = g->environ; *e != NULL; e++){ - if(not add_environment(p, *e, false)){ - error(0, errno, "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)){ - error(0, errno, "add_environment"); - } - } - } - - int pipefd[2]; - ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd)); - if(ret == -1){ - error(0, errno, "pipe"); - exitstatus = EX_OSERR; - goto fallback; - } - /* Ask OS to automatic close the pipe on exec */ + plugin *g = getplugin(NULL, &plugin_list); + for(char **a = g->argv + 1; *a != NULL; a++){ + addargument(p, *a); + } + } + int pipefd[2]; + ret = pipe(pipefd); + if (ret == -1){ + perror("pipe"); + exitstatus = EXIT_FAILURE; + goto end; + } ret = set_cloexec_flag(pipefd[0]); if(ret < 0){ - error(0, errno, "set_cloexec_flag"); - exitstatus = EX_OSERR; - goto fallback; + perror("set_cloexec_flag"); + exitstatus = EXIT_FAILURE; + goto end; } ret = set_cloexec_flag(pipefd[1]); if(ret < 0){ - error(0, errno, "set_cloexec_flag"); - exitstatus = EX_OSERR; - goto fallback; + perror("set_cloexec_flag"); + exitstatus = EXIT_FAILURE; + goto end; } /* Block SIGCHLD until process is safely in process list */ - ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK, - &sigchld_action.sa_mask, - NULL)); + ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL); if(ret < 0){ - error(0, errno, "sigprocmask"); - exitstatus = EX_OSERR; - goto fallback; - } - /* Starting a new process to be watched */ - pid_t pid; - do { - pid = fork(); - } while(pid == -1 and errno == EINTR); - if(pid == -1){ - error(0, errno, "fork"); - exitstatus = EX_OSERR; - goto fallback; - } + perror("sigprocmask"); + exitstatus = EXIT_FAILURE; + goto end; + } + // Starting a new process to be watched + pid_t pid = fork(); if(pid == 0){ /* this is the child process */ ret = sigaction(SIGCHLD, &old_sigchld_action, NULL); if(ret < 0){ - error(0, errno, "sigaction"); - _exit(EX_OSERR); + perror("sigaction"); + _exit(EXIT_FAILURE); } - ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); + ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); if(ret < 0){ - error(0, errno, "sigprocmask"); - _exit(EX_OSERR); - } - - ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */ - if(ret == -1){ - error(0, errno, "dup2"); - _exit(EX_OSERR); - } + perror("sigprocmask"); + _exit(EXIT_FAILURE); + } + dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */ 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){ - error(0, errno, "execv for %s", filename); - _exit(EX_OSERR); - } - } else { - if(execve(filename, p->argv, p->environ) < 0){ - error(0, errno, "execve for %s", filename); - _exit(EX_OSERR); - } + if(execv(filename, p->argv) < 0){ + perror("execv"); + _exit(EXIT_FAILURE); } /* no return */ } - /* Parent process */ - TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of - pipe */ + /* parent process */ free(filename); - plugin *new_plugin = getplugin(dirst->d_name); - if(new_plugin == NULL){ - error(0, errno, "getplugin"); - ret = (int)(TEMP_FAILURE_RETRY - (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, - NULL))); + close(pipefd[1]); /* close unused write end of pipe */ + process *new_process = malloc(sizeof(process)); + if (new_process == NULL){ + perror("malloc"); + ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); if(ret < 0){ - error(0, errno, "sigprocmask"); + perror("sigprocmask"); } - exitstatus = EX_OSERR; - goto fallback; + exitstatus = EXIT_FAILURE; + goto end; } - new_plugin->pid = pid; - new_plugin->fd = pipefd[0]; - + *new_process = (struct process){ .pid = pid, + .fd = pipefd[0], + .next = process_list }; + // List handling + process_list = new_process; /* Unblock SIGCHLD so signal handler can be run if this process has already completed */ - ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK, - &sigchld_action.sa_mask, - NULL)); + ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL); if(ret < 0){ - error(0, errno, "sigprocmask"); - exitstatus = EX_OSERR; - goto fallback; - } - - FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from - -Wconversion */ - - if(maxfd < new_plugin->fd){ - maxfd = new_plugin->fd; - } - } - - TEMP_FAILURE_RETRY(closedir(dir)); + perror("sigprocmask"); + exitstatus = EXIT_FAILURE; + goto end; + } + + FD_SET(new_process->fd, &rfds_all); + + if (maxfd < new_process->fd){ + maxfd = new_process->fd; + } + + } + + /* Free the plugin list */ + for(plugin *next; plugin_list != NULL; plugin_list = next){ + next = plugin_list->next; + free(plugin_list->argv); + free(plugin_list); + } + + closedir(dir); dir = NULL; - free_plugin(getplugin(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(); - } + if (process_list == NULL){ + fprintf(stderr, "No plugin processes started, exiting\n"); + exitstatus = EXIT_FAILURE; + goto end; } - - /* Main loop while running plugins exist */ - while(plugin_list){ + while(process_list){ fd_set rfds = rfds_all; int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL); - if(select_ret == -1 and errno != EINTR){ - error(0, errno, "select"); - exitstatus = EX_OSERR; - goto fallback; + if (select_ret == -1){ + perror("select"); + exitstatus = EXIT_FAILURE; + goto end; } /* OK, now either a process completed, or something can be read from one of them */ - for(plugin *proc = plugin_list; proc != NULL;){ + for(process *proc = process_list; proc ; proc = proc->next){ /* Is this process completely done? */ - if(proc->completed and proc->eof){ + 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 %s [%" PRIdMAX "] exited with" - " status %d\n", proc->name, - (intmax_t) (proc->pid), - WEXITSTATUS(proc->status)); - } else if(WIFSIGNALED(proc->status)){ - fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by" - " signal %d: %s\n", proc->name, - (intmax_t) (proc->pid), - WTERMSIG(proc->status), - strsignal(WTERMSIG(proc->status))); + fprintf(stderr, "Plugin %d exited with status %d\n", + proc->pid, WEXITSTATUS(proc->status)); + } else if(WIFSIGNALED(proc->status)) { + fprintf(stderr, "Plugin %d killed by signal %d\n", + proc->pid, WTERMSIG(proc->status)); } else if(WCOREDUMP(proc->status)){ - fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped" - " core\n", proc->name, (intmax_t) (proc->pid)); + fprintf(stderr, "Plugin %d dumped core\n", proc->pid); } } - /* Remove the plugin */ - FD_CLR(proc->fd, &rfds_all); /* Spurious warning from - -Wconversion */ - + FD_CLR(proc->fd, &rfds_all); /* Block signal while modifying process_list */ - ret = (int)TEMP_FAILURE_RETRY(sigprocmask - (SIG_BLOCK, - &sigchld_action.sa_mask, - NULL)); + ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL); if(ret < 0){ - error(0, errno, "sigprocmask"); - exitstatus = EX_OSERR; - goto fallback; - } - - plugin *next_plugin = proc->next; - free_plugin(proc); - proc = next_plugin; - + perror("sigprocmask"); + exitstatus = EXIT_FAILURE; + goto end; + } + /* Delete this process entry from the list */ + if(process_list == proc){ + /* First one - simple */ + process_list = proc->next; + } else { + /* Second one or later */ + for(process *p = process_list; p != NULL; p = p->next){ + if(p->next == proc){ + p->next = proc->next; + break; + } + } + } /* We are done modifying process list, so unblock signal */ - ret = (int)(TEMP_FAILURE_RETRY - (sigprocmask(SIG_UNBLOCK, - &sigchld_action.sa_mask, NULL))); + ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, + NULL); if(ret < 0){ - error(0, errno, "sigprocmask"); - exitstatus = EX_OSERR; - goto fallback; - } - - if(plugin_list == NULL){ - break; - } - - continue; + perror("sigprocmask"); + } + free(proc->buffer); + free(proc); + /* We deleted this process from the list, so we can't go + proc->next. Therefore, start over from the beginning of + the process list */ + break; } - /* This process exited nicely, so print its buffer */ - - bool bret = print_out_password(proc->buffer, - proc->buffer_length); - if(not bret){ - error(0, errno, "print_out_password"); - exitstatus = EX_IOERR; + for(size_t written = 0; written < proc->buffer_length; + written += (size_t)ret){ + ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, + proc->buffer + written, + proc->buffer_length + - written)); + if(ret < 0){ + perror("write"); + exitstatus = EXIT_FAILURE; + goto end; + } } - goto fallback; + goto end; } - /* This process has not completed. Does it have any output? */ - if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious - warning from - -Wconversion */ + 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){ - error(0, errno, "malloc"); - exitstatus = EX_OSERR; - goto fallback; + if (proc->buffer == NULL){ + perror("malloc"); + exitstatus = EXIT_FAILURE; + goto end; } proc->buffer_size += BUFFER_SIZE; } /* Read from the process */ - sret = TEMP_FAILURE_RETRY(read(proc->fd, - proc->buffer - + proc->buffer_length, - BUFFER_SIZE)); - if(sret < 0){ + 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(sret == 0){ + if(ret == 0){ /* got EOF */ proc->eof = true; } else { - proc->buffer_length += (size_t) sret; + proc->buffer_length += (size_t) ret; } } } - - - fallback: - - if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS - and exitstatus != EX_OK)){ - /* 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){ - error(0, errno, "print_out_password"); - exitstatus = EX_IOERR; - } + if(process_list == NULL){ + fprintf(stderr, "All plugin processes failed, exiting\n"); + exitstatus = EXIT_FAILURE; } + end: /* Restore old signal handler */ - ret = sigaction(SIGCHLD, &old_sigchld_action, NULL); - if(ret == -1){ - error(0, errno, "sigaction"); - exitstatus = EX_OSERR; - } - - if(custom_argv != NULL){ - for(char **arg = custom_argv+1; *arg != NULL; arg++){ - free(*arg); - } - free(custom_argv); + sigaction(SIGCHLD, &old_sigchld_action, NULL); + + free(plus_argv); + + /* Free the plugin list */ + for(plugin *next; plugin_list != NULL; plugin_list = next){ + next = plugin_list->next; + free(plugin_list->argv); + free(plugin_list); } if(dir != NULL){ closedir(dir); } - /* 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 */ - error(0, errno, "kill"); - } - } + /* Free the process list and kill the processes */ + for(process *next; process_list != NULL; process_list = next){ + next = process_list->next; + close(process_list->fd); + kill(process_list->pid, SIGTERM); + free(process_list->buffer); + free(process_list); } /* Wait for any remaining child processes to terminate */ - do { + do{ ret = wait(NULL); } while(ret >= 0); if(errno != ECHILD){ - error(0, errno, "wait"); + perror("wait"); } - free_plugin_list(); - - free(plugindir); - free(argfile); - return exitstatus; } === removed file 'mandos-clients.conf.xml' --- mandos-clients.conf.xml 2010-09-27 18:57:12 +0000 +++ mandos-clients.conf.xml 1970-01-01 00:00:00 +0000 @@ -1,475 +0,0 @@ - - -/etc/mandos/clients.conf"> - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2008 - 2009 - 2010 - 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 enabled, - even if a client was disabled 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. - - - How long to wait for external approval before resorting to - use the value. The - default is 0s, i.e. not to wait. - - - The format of TIME is the same - as for timeout below. - - - - - - - - - This option is optional. - - - How long an external approval lasts. The default is 1 - second. - - - The format of TIME is the same - as for timeout below. - - - - - - - - - Whether to approve a client by default after - the . The default - is True. - - - - - - - - - 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. - - - - - - - - - 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. - - - - - - - - - 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 below - timeout occurs, at which - time the client will be disabled, and any running checker - killed. The default interval is 5 minutes. - - - The format of TIME is the same - as for timeout below. - - - - - - - - - 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. - - - File names of the form ~user/foo/bar - and $ENVVAR/foo/bar - are supported. - - - - - - - - - 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 optional. - - - The timeout is how long the server will wait (for either a - successful checker run or a client receiving its secret) - until a client is disabled and not allowed 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. - - - - - - - - - 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 in the - Mandos server. The currently allowed values for - foo are: - approval_delay, - approval_duration, - created, - enabled, - fingerprint, - host, - interval, - last_approval_request, - last_checked_ok, - last_enabled, - name, - timeout, and, if using - D-Bus, dbus_object_path. - See the source code for details. Currently, none of these attributes - except host are guaranteed - to be valid in future versions. Therefore, please - 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 -approved_by_default = False -approval_delay = 30s - - - - - - SEE ALSO - - mandos-keygen - 8, - mandos.conf - 5, - mandos - 8 - - -
- - - - - === removed file 'mandos-ctl' --- mandos-ctl 2010-09-28 18:57:31 +0000 +++ mandos-ctl 1970-01-01 00:00:00 +0000 @@ -1,337 +0,0 @@ -#!/usr/bin/python -# -*- mode: python; coding: utf-8 -*- -# -# Mandos Monitor - Control and monitor the Mandos server -# -# Copyright © 2008-2010 Teddy Hogeborn -# Copyright © 2008-2010 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 sys -import dbus -from optparse import OptionParser -import locale -import datetime -import re -import os - -locale.setlocale(locale.LC_ALL, u'') - -tablewords = { - 'Name': u'Name', - 'Enabled': u'Enabled', - 'Timeout': u'Timeout', - 'LastCheckedOK': u'Last Successful Check', - 'LastApprovalRequest': u'Last Approval Request', - 'Created': u'Created', - 'Interval': u'Interval', - 'Host': u'Host', - 'Fingerprint': u'Fingerprint', - 'CheckerRunning': u'Check Is Running', - 'LastEnabled': u'Last Enabled', - 'ApprovalPending': u'Approval Is Pending', - 'ApprovedByDefault': u'Approved By Default', - 'ApprovalDelay': u"Approval Delay", - 'ApprovalDuration': u"Approval Duration", - 'Checker': u'Checker', - } -defaultkeywords = ('Name', 'Enabled', 'Timeout', 'LastCheckedOK') -domain = 'se.bsnet.fukt' -busname = domain + '.Mandos' -server_path = '/' -server_interface = domain + '.Mandos' -client_interface = domain + '.Mandos.Client' -version = "1.2" - -def timedelta_to_milliseconds(td): - "Convert a datetime.timedelta object to milliseconds" - return ((td.days * 24 * 60 * 60 * 1000) - + (td.seconds * 1000) - + (td.microseconds // 1000)) - -def milliseconds_to_string(ms): - td = datetime.timedelta(0, 0, 0, ms) - return (u"%(days)s%(hours)02d:%(minutes)02d:%(seconds)02d" - % { "days": "%dT" % td.days if td.days else "", - "hours": td.seconds // 3600, - "minutes": (td.seconds % 3600) // 60, - "seconds": td.seconds % 60, - }) - - -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) - regexp = re.compile("\d+[dsmhw]") - - for s in regexp.findall(interval): - 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 print_clients(clients, keywords): - def valuetostring(value, keyword): - if type(value) is dbus.Boolean: - return u"Yes" if value else u"No" - if keyword in (u"Timeout", u"Interval", u"ApprovalDelay", - u"ApprovalDuration"): - return milliseconds_to_string(value) - return unicode(value) - - # Create format string to print table rows - format_string = u' '.join(u'%%-%ds' % - max(len(tablewords[key]), - max(len(valuetostring(client[key], - key)) - for client in - clients)) - for key in keywords) - # Print header line - print format_string % tuple(tablewords[key] for key in keywords) - for client in clients: - print format_string % tuple(valuetostring(client[key], key) - for key in keywords) - -def has_actions(options): - return any((options.enable, - options.disable, - options.bump_timeout, - options.start_checker, - options.stop_checker, - options.is_enabled, - options.remove, - options.checker is not None, - options.timeout is not None, - options.interval is not None, - options.approved_by_default is not None, - options.approval_delay is not None, - options.approval_duration is not None, - options.host is not None, - options.secret is not None, - options.approve, - options.deny)) - -def main(): - parser = OptionParser(version = "%%prog %s" % version) - parser.add_option("-a", "--all", action="store_true", - help="Select all clients") - parser.add_option("-v", "--verbose", action="store_true", - help="Print all fields") - parser.add_option("-e", "--enable", action="store_true", - help="Enable client") - parser.add_option("-d", "--disable", action="store_true", - help="disable client") - parser.add_option("-b", "--bump-timeout", action="store_true", - help="Bump timeout for client") - parser.add_option("--start-checker", action="store_true", - help="Start checker for client") - parser.add_option("--stop-checker", action="store_true", - help="Stop checker for client") - parser.add_option("-V", "--is-enabled", action="store_true", - help="Check if client is enabled") - parser.add_option("-r", "--remove", action="store_true", - help="Remove client") - parser.add_option("-c", "--checker", type="string", - help="Set checker command for client") - parser.add_option("-t", "--timeout", type="string", - help="Set timeout for client") - parser.add_option("-i", "--interval", type="string", - help="Set checker interval for client") - parser.add_option("--approve-by-default", action="store_true", - dest=u"approved_by_default", - help="Set client to be approved by default") - parser.add_option("--deny-by-default", action="store_false", - dest=u"approved_by_default", - help="Set client to be denied by default") - parser.add_option("--approval-delay", type="string", - help="Set delay before client approve/deny") - parser.add_option("--approval-duration", type="string", - help="Set duration of one client approval") - parser.add_option("-H", "--host", type="string", - help="Set host for client") - parser.add_option("-s", "--secret", type="string", - help="Set password blob (file) for client") - parser.add_option("-A", "--approve", action="store_true", - help="Approve any current client request") - parser.add_option("-D", "--deny", action="store_true", - help="Deny any current client request") - options, client_names = parser.parse_args() - - if has_actions(options) and not client_names and not options.all: - parser.error('Options require clients names or --all.') - if options.verbose and has_actions(options): - parser.error('--verbose can only be used alone or with' - ' --all.') - if options.all and not has_actions(options): - parser.error('--all requires an action.') - - try: - bus = dbus.SystemBus() - mandos_dbus_objc = bus.get_object(busname, server_path) - except dbus.exceptions.DBusException: - print >> sys.stderr, "Could not connect to Mandos server" - sys.exit(1) - - mandos_serv = dbus.Interface(mandos_dbus_objc, - dbus_interface = server_interface) - - #block stderr since dbus library prints to stderr - null = os.open(os.path.devnull, os.O_RDWR) - stderrcopy = os.dup(sys.stderr.fileno()) - os.dup2(null, sys.stderr.fileno()) - os.close(null) - try: - try: - mandos_clients = mandos_serv.GetAllClientsWithProperties() - finally: - #restore stderr - os.dup2(stderrcopy, sys.stderr.fileno()) - os.close(stderrcopy) - except dbus.exceptions.DBusException, e: - print >> sys.stderr, "Access denied: Accessing mandos server through dbus." - sys.exit(1) - - # Compile dict of (clients: properties) to process - clients={} - - if options.all or not client_names: - clients = dict((bus.get_object(busname, path), properties) - for path, properties in - mandos_clients.iteritems()) - else: - for name in client_names: - for path, client in mandos_clients.iteritems(): - if client['Name'] == name: - client_objc = bus.get_object(busname, path) - clients[client_objc] = client - break - else: - print >> sys.stderr, "Client not found on server: %r" % name - sys.exit(1) - - if not has_actions(options) and clients: - if options.verbose: - keywords = ('Name', 'Enabled', 'Timeout', - 'LastCheckedOK', 'Created', 'Interval', - 'Host', 'Fingerprint', 'CheckerRunning', - 'LastEnabled', 'ApprovalPending', - 'ApprovedByDefault', - 'LastApprovalRequest', 'ApprovalDelay', - 'ApprovalDuration', 'Checker') - else: - keywords = defaultkeywords - - print_clients(clients.values(), keywords) - else: - # Process each client in the list by all selected options - for client in clients: - if options.remove: - mandos_serv.RemoveClient(client.__dbus_object_path__) - if options.enable: - client.Enable(dbus_interface=client_interface) - if options.disable: - client.Disable(dbus_interface=client_interface) - if options.bump_timeout: - client.CheckedOK(dbus_interface=client_interface) - if options.start_checker: - client.StartChecker(dbus_interface=client_interface) - if options.stop_checker: - client.StopChecker(dbus_interface=client_interface) - if options.is_enabled: - sys.exit(0 if client.Get(client_interface, - u"Enabled", - dbus_interface=dbus.PROPERTIES_IFACE) - else 1) - if options.checker: - client.Set(client_interface, u"Checker", options.checker, - dbus_interface=dbus.PROPERTIES_IFACE) - if options.host: - client.Set(client_interface, u"Host", options.host, - dbus_interface=dbus.PROPERTIES_IFACE) - if options.interval: - client.Set(client_interface, u"Interval", - timedelta_to_milliseconds - (string_to_delta(options.interval)), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.approval_delay: - client.Set(client_interface, u"ApprovalDelay", - timedelta_to_milliseconds - (string_to_delta(options. - approval_delay)), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.approval_duration: - client.Set(client_interface, u"ApprovalDuration", - timedelta_to_milliseconds - (string_to_delta(options. - approval_duration)), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.timeout: - client.Set(client_interface, u"Timeout", - timedelta_to_milliseconds - (string_to_delta(options.timeout)), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.secret: - client.Set(client_interface, u"Secret", - dbus.ByteArray(open(options.secret, - u'rb').read()), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.approved_by_default is not None: - client.Set(client_interface, u"ApprovedByDefault", - dbus.Boolean(options - .approved_by_default), - dbus_interface=dbus.PROPERTIES_IFACE) - if options.approve: - client.Approve(dbus.Boolean(True), - dbus_interface=client_interface) - elif options.deny: - client.Approve(dbus.Boolean(False), - dbus_interface=client_interface) - -if __name__ == '__main__': - main() === removed file 'mandos-ctl.xml' --- mandos-ctl.xml 2010-09-25 23:52:17 +0000 +++ mandos-ctl.xml 1970-01-01 00:00:00 +0000 @@ -1,570 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2010 - Teddy Hogeborn - Björn Påhlsson - - -
- - - &COMMANDNAME; - 8 - - - - &COMMANDNAME; - - Control the operation of the Mandos server - - - - - - &COMMANDNAME; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CLIENT - - - - - &COMMANDNAME; - - - - - - - CLIENT - - - - - &COMMANDNAME; - - - - - CLIENT - - - &COMMANDNAME; - - - - - - - &COMMANDNAME; - - - - - - - - - DESCRIPTION - - &COMMANDNAME; is a program to control the - operation of the Mandos server mandos8. - - - This program can be used to change client settings, approve or - deny client requests, and to remove clients from the server. - - - - - 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 - - - - - - - - - - Enable client(s). An enabled client will be eligble to - receive its secret. - - - - - - - - - - Disable client(s). A disabled client will not be eligble - to receive its secret, and no checkers will be started for - it. - - - - - - - - - Bump the timeout of the specified client(s), just as if a - checker had completed successfully for it/them. - - - - - - - - - Start a new checker now for the specified client(s). - - - - - - - - - Stop any running checker for the specified client(s). - - - - - - - - - - Remove the specified client(s) from the server. - - - - - - - - - - Set the checker option of the specified - client(s); see mandos-clients.conf5. - - - - - - - - - - Set the timeout option of the specified - client(s); see mandos-clients.conf5. - - - - - - - - - - Set the interval option of the - specified client(s); see mandos-clients.conf5. - - - - - - - - - - Set the approved_by_default option of - the specified client(s) to True or - False, respectively; see - mandos-clients.conf5. - - - - - - - - - Set the approval_delay option of the - specified client(s); see mandos-clients.conf5. - - - - - - - - - Set the approval_duration option of the - specified client(s); see mandos-clients.conf5. - - - - - - - - - - Set the host option of the specified - client(s); see mandos-clients.conf5. - - - - - - - - - - Set the secfile option of the specified - client(s); see mandos-clients.conf5. - - - - - - - - - - Approve client(s) if currently waiting for approval. - - - - - - - - - - Deny client(s) if currently waiting for approval. - - - - - - - - - - Make the client-modifying options modify all clients. - - - - - - - - - - Show all client settings, not just a subset. - - - - - - - - - - Check if a single client is enabled or not, and exit with - a successful exit status only if the client is enabled. - - - - - - - - - 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 - - If the option is used, the exit - status will be 0 only if the specified client is enabled. - - - - - - - - - - - EXAMPLE - - - To list all clients: - - - &COMMANDNAME; - - - - - - To list all settings for the clients - named foo1.example.org and foo2.example.org: - - - - -&COMMANDNAME; --verbose foo1.example.org foo2.example.org - - - - - - - To enable all clients: - - - &COMMANDNAME; --enable --all - - - - - - To change timeout and interval value for the clients - named foo1.example.org and foo2.example.org: - - - - -&COMMANDNAME; --timeout="5m" --interval="1m" foo1.example.org foo2.example.org - - - - - - - To approve all clients currently waiting for it: - - - &COMMANDNAME; --approve --all - - - - - - SECURITY - - This program must be permitted to access the Mandos server via - the D-Bus interface. This normally requires the root user, but - could be configured otherwise by reconfiguring the D-Bus server. - - - - - SEE ALSO - - mandos - 8, - mandos-clients.conf - 5, - mandos-monitor - 8 - - - -
- - - - - === removed file 'mandos-keygen' --- mandos-keygen 2010-10-01 18:40:55 +0000 +++ mandos-keygen 1970-01-01 00:00:00 +0000 @@ -1,344 +0,0 @@ -#!/bin/sh -e -# -# Mandos key generator - create a new OpenPGP key for a Mandos client -# -# Copyright © 2008-2010 Teddy Hogeborn -# Copyright © 2008-2010 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.2" - -KEYDIR="/etc/keys/mandos" -KEYTYPE=DSA -KEYLENGTH=2048 -SUBKEYTYPE=ELG-E -SUBKEYLENGTH=2048 -KEYNAME="`hostname --fqdn 2>/dev/null || hostname`" -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 vhpF:d:t:l:s:L:n:e:c:x:f \ - --longoptions version,help,password,passfile:,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 - -set -e - -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 - - if tty --quiet; then - cat <<-EOF - Note: Due to entropy requirements, key generation could take - anything from a few minutes to SEVERAL HOURS. Please be - patient and/or supply the system with more entropy if needed. - EOF - echo -n "Started: " - date - fi - - # 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" - - if tty --quiet; then - echo -n "Finished: " - date - fi - - # 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 key 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 key 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" - - if [ -n "$PASSFILE" ]; then - cat "$PASSFILE" - else - stty -echo - echo -n "Enter passphrase: " >&2 - first="$(head --lines=1 | tr --delete '\n')" - echo >&2 - echo -n "Repeat passphrase: " >&2 - second="$(head --lines=1 | tr --delete '\n')" - echo >&2 - stty echo - if [ "$first" != "$second" ]; then - echo "Passphrase mismatch" >&2 - touch "$RINGDIR"/mismatch - else - echo -n "$first" - fi - fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \ - --homedir "$RINGDIR" --trust-model always --armor --encrypt \ - --sign --recipient "$FINGERPRINT" --comment "$FILECOMMENT" \ - > "$SECFILE" - if [ -e "$RINGDIR"/mismatch ]; then - rm --force "$RINGDIR"/mismatch - exit 1 - fi - - 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" === removed file 'mandos-keygen.xml' --- mandos-keygen.xml 2009-01-04 21:54:55 +0000 +++ mandos-keygen.xml 1970-01-01 00:00:00 +0000 @@ -1,511 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2008 - 2009 - Teddy Hogeborn - Björn Påhlsson - - -
- - - &COMMANDNAME; - 8 - - - - &COMMANDNAME; - - Generate key and password for Mandos client and server. - - - - - - &COMMANDNAME; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - &COMMANDNAME; - - - - - - FILE - - - - - - - - - - - - - - &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 - or - options 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. - - - - - - - - - The same as , but read from - FILE, not the terminal. - - - - - - - - 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 - - - -
- - - - - === removed file 'mandos-monitor' --- mandos-monitor 2010-09-28 18:57:31 +0000 +++ mandos-monitor 1970-01-01 00:00:00 +0000 @@ -1,733 +0,0 @@ -#!/usr/bin/python -# -*- mode: python; coding: utf-8 -*- -# -# Mandos Monitor - Control and monitor the Mandos server -# -# Copyright © 2009,2010 Teddy Hogeborn -# Copyright © 2009,2010 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, absolute_import, with_statement - -import sys -import os -import signal - -import datetime - -import urwid.curses_display -import urwid - -from dbus.mainloop.glib import DBusGMainLoop -import gobject - -import dbus - -import UserList - -import locale - -locale.setlocale(locale.LC_ALL, u'') - -import logging -logging.getLogger('dbus.proxies').setLevel(logging.CRITICAL) - -# Some useful constants -domain = 'se.bsnet.fukt' -server_interface = domain + '.Mandos' -client_interface = domain + '.Mandos.Client' -version = "1.2" - -# Always run in monochrome mode -urwid.curses_display.curses.has_colors = lambda : False - -# Urwid doesn't support blinking, but we want it. Since we have no -# use for underline on its own, we make underline also always blink. -urwid.curses_display.curses.A_UNDERLINE |= ( - urwid.curses_display.curses.A_BLINK) - -def isoformat_to_datetime(iso): - "Parse an ISO 8601 date string to a datetime.datetime()" - if not iso: - return None - d, t = iso.split(u"T", 1) - year, month, day = d.split(u"-", 2) - hour, minute, second = t.split(u":", 2) - second, fraction = divmod(float(second), 1) - return datetime.datetime(int(year), - int(month), - int(day), - int(hour), - int(minute), - int(second), # Whole seconds - int(fraction*1000000)) # Microseconds - -class MandosClientPropertyCache(object): - """This wraps a Mandos Client D-Bus proxy object, caches the - properties and calls a hook function when any of them are - changed. - """ - def __init__(self, proxy_object=None, *args, **kwargs): - self.proxy = proxy_object # Mandos Client proxy object - - self.properties = dict() - self.proxy.connect_to_signal(u"PropertyChanged", - self.property_changed, - client_interface, - byte_arrays=True) - - self.properties.update( - self.proxy.GetAll(client_interface, - dbus_interface = dbus.PROPERTIES_IFACE)) - - #XXX This break good super behaviour! -# super(MandosClientPropertyCache, self).__init__( -# *args, **kwargs) - - def property_changed(self, property=None, value=None): - """This is called whenever we get a PropertyChanged signal - It updates the changed property in the "properties" dict. - """ - # Update properties dict with new value - self.properties[property] = value - - -class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache): - """A Mandos Client which is visible on the screen. - """ - - def __init__(self, server_proxy_object=None, update_hook=None, - delete_hook=None, logger=None, *args, **kwargs): - # Called on update - self.update_hook = update_hook - # Called on delete - self.delete_hook = delete_hook - # Mandos Server proxy object - self.server_proxy_object = server_proxy_object - # Logger - self.logger = logger - - self._update_timer_callback_tag = None - self._update_timer_callback_lock = 0 - self.last_checker_failed = False - - # The widget shown normally - self._text_widget = urwid.Text(u"") - # The widget shown when we have focus - self._focus_text_widget = urwid.Text(u"") - super(MandosClientWidget, self).__init__( - update_hook=update_hook, delete_hook=delete_hook, - *args, **kwargs) - self.update() - self.opened = False - - last_checked_ok = isoformat_to_datetime(self.properties - [u"LastCheckedOK"]) - if last_checked_ok is None: - self.last_checker_failed = True - else: - self.last_checker_failed = ((datetime.datetime.utcnow() - - last_checked_ok) - > datetime.timedelta - (milliseconds= - self.properties - [u"Interval"])) - - if self.last_checker_failed: - self.using_timer(True) - - if self.need_approval: - self.using_timer(True) - - self.proxy.connect_to_signal(u"CheckerCompleted", - self.checker_completed, - client_interface, - byte_arrays=True) - self.proxy.connect_to_signal(u"CheckerStarted", - self.checker_started, - client_interface, - byte_arrays=True) - self.proxy.connect_to_signal(u"GotSecret", - self.got_secret, - client_interface, - byte_arrays=True) - self.proxy.connect_to_signal(u"NeedApproval", - self.need_approval, - client_interface, - byte_arrays=True) - self.proxy.connect_to_signal(u"Rejected", - self.rejected, - client_interface, - byte_arrays=True) - - def property_changed(self, property=None, value=None): - super(self, MandosClientWidget).property_changed(property, - value) - if property == u"ApprovalPending": - using_timer(bool(value)) - - def using_timer(self, flag): - """Call this method with True or False when timer should be - activated or deactivated. - """ - old = self._update_timer_callback_lock - if flag: - self._update_timer_callback_lock += 1 - else: - self._update_timer_callback_lock -= 1 - if old == 0 and self._update_timer_callback_lock: - self._update_timer_callback_tag = (gobject.timeout_add - (1000, - self.update_timer)) - elif old and self._update_timer_callback_lock == 0: - gobject.source_remove(self._update_timer_callback_tag) - self._update_timer_callback_tag = None - - def checker_completed(self, exitstatus, condition, command): - if exitstatus == 0: - if self.last_checker_failed: - self.last_checker_failed = False - self.using_timer(False) - #self.logger(u'Checker for client %s (command "%s")' - # u' was successful' - # % (self.properties[u"Name"], command)) - self.update() - return - # Checker failed - if not self.last_checker_failed: - self.last_checker_failed = True - self.using_timer(True) - if os.WIFEXITED(condition): - self.logger(u'Checker for client %s (command "%s")' - u' failed with exit code %s' - % (self.properties[u"Name"], command, - os.WEXITSTATUS(condition))) - elif os.WIFSIGNALED(condition): - self.logger(u'Checker for client %s (command "%s")' - u' was killed by signal %s' - % (self.properties[u"Name"], command, - os.WTERMSIG(condition))) - elif os.WCOREDUMP(condition): - self.logger(u'Checker for client %s (command "%s")' - u' dumped core' - % (self.properties[u"Name"], command)) - else: - self.logger(u'Checker for client %s completed' - u' mysteriously') - self.update() - - def checker_started(self, command): - #self.logger(u'Client %s started checker "%s"' - # % (self.properties[u"Name"], unicode(command))) - pass - - def got_secret(self): - self.last_checker_failed = False - self.logger(u'Client %s received its secret' - % self.properties[u"Name"]) - - def need_approval(self, timeout, default): - if not default: - message = u'Client %s needs approval within %s seconds' - else: - message = u'Client %s will get its secret in %s seconds' - self.logger(message - % (self.properties[u"Name"], timeout/1000)) - self.using_timer(True) - - def rejected(self, reason): - self.logger(u'Client %s was rejected; reason: %s' - % (self.properties[u"Name"], reason)) - - def selectable(self): - """Make this a "selectable" widget. - This overrides the method from urwid.FlowWidget.""" - return True - - def rows(self, (maxcol,), focus=False): - """How many rows this widget will occupy might depend on - whether we have focus or not. - This overrides the method from urwid.FlowWidget""" - return self.current_widget(focus).rows((maxcol,), focus=focus) - - def current_widget(self, focus=False): - if focus or self.opened: - return self._focus_widget - return self._widget - - def update(self): - "Called when what is visible on the screen should be updated." - # How to add standout mode to a style - with_standout = { u"normal": u"standout", - u"bold": u"bold-standout", - u"underline-blink": - u"underline-blink-standout", - u"bold-underline-blink": - u"bold-underline-blink-standout", - } - - # Rebuild focus and non-focus widgets using current properties - - # Base part of a client. Name! - base = (u'%(name)s: ' - % {u"name": self.properties[u"Name"]}) - if not self.properties[u"Enabled"]: - message = u"DISABLED" - elif self.properties[u"ApprovalPending"]: - timeout = datetime.timedelta(milliseconds - = self.properties - [u"ApprovalDelay"]) - last_approval_request = isoformat_to_datetime( - self.properties[u"LastApprovalRequest"]) - if last_approval_request is not None: - timer = timeout - (datetime.datetime.utcnow() - - last_approval_request) - else: - timer = datetime.timedelta() - if self.properties[u"ApprovedByDefault"]: - message = u"Approval in %s. (d)eny?" - else: - message = u"Denial in %s. (a)pprove?" - message = message % unicode(timer).rsplit(".", 1)[0] - elif self.last_checker_failed: - timeout = datetime.timedelta(milliseconds - = self.properties - [u"Timeout"]) - last_ok = isoformat_to_datetime( - max((self.properties[u"LastCheckedOK"] - or self.properties[u"Created"]), - self.properties[u"LastEnabled"])) - timer = timeout - (datetime.datetime.utcnow() - last_ok) - message = (u'A checker has failed! Time until client' - u' gets disabled: %s' - % unicode(timer).rsplit(".", 1)[0]) - else: - message = u"enabled" - self._text = "%s%s" % (base, message) - - if not urwid.supports_unicode(): - self._text = self._text.encode("ascii", "replace") - textlist = [(u"normal", self._text)] - self._text_widget.set_text(textlist) - self._focus_text_widget.set_text([(with_standout[text[0]], - text[1]) - if isinstance(text, tuple) - else text - for text in textlist]) - self._widget = self._text_widget - self._focus_widget = urwid.AttrWrap(self._focus_text_widget, - "standout") - # Run update hook, if any - if self.update_hook is not None: - self.update_hook() - - def update_timer(self): - "called by gobject" - self.update() - return True # Keep calling this - - def delete(self): - if self._update_timer_callback_tag is not None: - gobject.source_remove(self._update_timer_callback_tag) - self._update_timer_callback_tag = None - if self.delete_hook is not None: - self.delete_hook(self) - - def render(self, (maxcol,), focus=False): - """Render differently if we have focus. - This overrides the method from urwid.FlowWidget""" - return self.current_widget(focus).render((maxcol,), - focus=focus) - - def keypress(self, (maxcol,), key): - """Handle keys. - This overrides the method from urwid.FlowWidget""" - if key == u"+": - self.proxy.Enable(dbus_interface = client_interface) - elif key == u"-": - self.proxy.Disable(dbus_interface = client_interface) - elif key == u"a": - self.proxy.Approve(dbus.Boolean(True, variant_level=1), - dbus_interface = client_interface) - elif key == u"d": - self.proxy.Approve(dbus.Boolean(False, variant_level=1), - dbus_interface = client_interface) - elif key == u"R" or key == u"_" or key == u"ctrl k": - self.server_proxy_object.RemoveClient(self.proxy - .object_path) - elif key == u"s": - self.proxy.StartChecker(dbus_interface = client_interface) - elif key == u"S": - self.proxy.StopChecker(dbus_interface = client_interface) - elif key == u"C": - self.proxy.CheckedOK(dbus_interface = client_interface) - # xxx -# elif key == u"p" or key == "=": -# self.proxy.pause() -# elif key == u"u" or key == ":": -# self.proxy.unpause() -# elif key == u"RET": -# self.open() - else: - return key - - def property_changed(self, property=None, value=None, - *args, **kwargs): - """Call self.update() if old value is not new value. - This overrides the method from MandosClientPropertyCache""" - property_name = unicode(property) - old_value = self.properties.get(property_name) - super(MandosClientWidget, self).property_changed( - property=property, value=value, *args, **kwargs) - if self.properties.get(property_name) != old_value: - self.update() - - -class ConstrainedListBox(urwid.ListBox): - """Like a normal urwid.ListBox, but will consume all "up" or - "down" key presses, thus not allowing any containing widgets to - use them as an excuse to shift focus away from this widget. - """ - def keypress(self, (maxcol, maxrow), key): - ret = super(ConstrainedListBox, self).keypress((maxcol, - maxrow), key) - if ret in (u"up", u"down"): - return - return ret - - -class UserInterface(object): - """This is the entire user interface - the whole screen - with boxes, lists of client widgets, etc. - """ - def __init__(self, max_log_length=1000): - DBusGMainLoop(set_as_default=True) - - self.screen = urwid.curses_display.Screen() - - self.screen.register_palette(( - (u"normal", - u"default", u"default", None), - (u"bold", - u"default", u"default", u"bold"), - (u"underline-blink", - u"default", u"default", u"underline"), - (u"standout", - u"default", u"default", u"standout"), - (u"bold-underline-blink", - u"default", u"default", (u"bold", u"underline")), - (u"bold-standout", - u"default", u"default", (u"bold", u"standout")), - (u"underline-blink-standout", - u"default", u"default", (u"underline", u"standout")), - (u"bold-underline-blink-standout", - u"default", u"default", (u"bold", u"underline", - u"standout")), - )) - - if urwid.supports_unicode(): - self.divider = u"─" # \u2500 - #self.divider = u"━" # \u2501 - else: - #self.divider = u"-" # \u002d - self.divider = u"_" # \u005f - - self.screen.start() - - self.size = self.screen.get_cols_rows() - - self.clients = urwid.SimpleListWalker([]) - self.clients_dict = {} - - # We will add Text widgets to this list - self.log = [] - self.max_log_length = max_log_length - - # We keep a reference to the log widget so we can remove it - # from the ListWalker without it getting destroyed - self.logbox = ConstrainedListBox(self.log) - - # This keeps track of whether self.uilist currently has - # self.logbox in it or not - self.log_visible = True - self.log_wrap = u"any" - - self.rebuild() - self.log_message_raw((u"bold", - u"Mandos Monitor version " + version)) - self.log_message_raw((u"bold", - u"q: Quit ?: Help")) - - self.busname = domain + '.Mandos' - self.main_loop = gobject.MainLoop() - self.bus = dbus.SystemBus() - mandos_dbus_objc = self.bus.get_object( - self.busname, u"/", follow_name_owner_changes=True) - self.mandos_serv = dbus.Interface(mandos_dbus_objc, - dbus_interface - = server_interface) - try: - mandos_clients = (self.mandos_serv - .GetAllClientsWithProperties()) - except dbus.exceptions.DBusException: - mandos_clients = dbus.Dictionary() - - (self.mandos_serv - .connect_to_signal(u"ClientRemoved", - self.find_and_remove_client, - dbus_interface=server_interface, - byte_arrays=True)) - (self.mandos_serv - .connect_to_signal(u"ClientAdded", - self.add_new_client, - dbus_interface=server_interface, - byte_arrays=True)) - (self.mandos_serv - .connect_to_signal(u"ClientNotFound", - self.client_not_found, - dbus_interface=server_interface, - byte_arrays=True)) - for path, client in mandos_clients.iteritems(): - client_proxy_object = self.bus.get_object(self.busname, - path) - self.add_client(MandosClientWidget(server_proxy_object - =self.mandos_serv, - proxy_object - =client_proxy_object, - properties=client, - update_hook - =self.refresh, - delete_hook - =self.remove_client, - logger - =self.log_message), - path=path) - - def client_not_found(self, fingerprint, address): - self.log_message((u"Client with address %s and fingerprint %s" - u" could not be found" % (address, - fingerprint))) - - def rebuild(self): - """This rebuilds the User Interface. - Call this when the widget layout needs to change""" - self.uilist = [] - #self.uilist.append(urwid.ListBox(self.clients)) - self.uilist.append(urwid.Frame(ConstrainedListBox(self. - clients), - #header=urwid.Divider(), - header=None, - footer= - urwid.Divider(div_char= - self.divider))) - if self.log_visible: - self.uilist.append(self.logbox) - pass - self.topwidget = urwid.Pile(self.uilist) - - def log_message(self, message): - timestamp = datetime.datetime.now().isoformat() - self.log_message_raw(timestamp + u": " + message) - - def log_message_raw(self, markup): - """Add a log message to the log buffer.""" - self.log.append(urwid.Text(markup, wrap=self.log_wrap)) - if (self.max_log_length - and len(self.log) > self.max_log_length): - del self.log[0:len(self.log)-self.max_log_length-1] - self.logbox.set_focus(len(self.logbox.body.contents), - coming_from=u"above") - self.refresh() - - def toggle_log_display(self): - """Toggle visibility of the log buffer.""" - self.log_visible = not self.log_visible - self.rebuild() - #self.log_message(u"Log visibility changed to: " - # + unicode(self.log_visible)) - - def change_log_display(self): - """Change type of log display. - Currently, this toggles wrapping of text lines.""" - if self.log_wrap == u"clip": - self.log_wrap = u"any" - else: - self.log_wrap = u"clip" - for textwidget in self.log: - textwidget.set_wrap_mode(self.log_wrap) - #self.log_message(u"Wrap mode: " + self.log_wrap) - - def find_and_remove_client(self, path, name): - """Find an client from its object path and remove it. - - This is connected to the ClientRemoved signal from the - Mandos server object.""" - try: - client = self.clients_dict[path] - except KeyError: - # not found? - return - self.remove_client(client, path) - - def add_new_client(self, path): - client_proxy_object = self.bus.get_object(self.busname, path) - self.add_client(MandosClientWidget(server_proxy_object - =self.mandos_serv, - proxy_object - =client_proxy_object, - update_hook - =self.refresh, - delete_hook - =self.remove_client, - logger - =self.log_message), - path=path) - - def add_client(self, client, path=None): - self.clients.append(client) - if path is None: - path = client.proxy.object_path - self.clients_dict[path] = client - self.clients.sort(None, lambda c: c.properties[u"Name"]) - self.refresh() - - def remove_client(self, client, path=None): - self.clients.remove(client) - if path is None: - path = client.proxy.object_path - del self.clients_dict[path] - if not self.clients_dict: - # Work around bug in Urwid 0.9.8.3 - if a SimpleListWalker - # is completely emptied, we need to recreate it. - self.clients = urwid.SimpleListWalker([]) - self.rebuild() - self.refresh() - - def refresh(self): - """Redraw the screen""" - canvas = self.topwidget.render(self.size, focus=True) - self.screen.draw_screen(self.size, canvas) - - def run(self): - """Start the main loop and exit when it's done.""" - self.refresh() - self._input_callback_tag = (gobject.io_add_watch - (sys.stdin.fileno(), - gobject.IO_IN, - self.process_input)) - self.main_loop.run() - # Main loop has finished, we should close everything now - gobject.source_remove(self._input_callback_tag) - self.screen.stop() - - def stop(self): - self.main_loop.quit() - - def process_input(self, source, condition): - keys = self.screen.get_input() - translations = { u"ctrl n": u"down", # Emacs - u"ctrl p": u"up", # Emacs - u"ctrl v": u"page down", # Emacs - u"meta v": u"page up", # Emacs - u" ": u"page down", # less - u"f": u"page down", # less - u"b": u"page up", # less - u"j": u"down", # vi - u"k": u"up", # vi - } - for key in keys: - try: - key = translations[key] - except KeyError: # :-) - pass - - if key == u"q" or key == u"Q": - self.stop() - break - elif key == u"window resize": - self.size = self.screen.get_cols_rows() - self.refresh() - elif key == u"\f": # Ctrl-L - self.refresh() - elif key == u"l" or key == u"D": - self.toggle_log_display() - self.refresh() - elif key == u"w" or key == u"i": - self.change_log_display() - self.refresh() - elif key == u"?" or key == u"f1" or key == u"esc": - if not self.log_visible: - self.log_visible = True - self.rebuild() - self.log_message_raw((u"bold", - u" ". - join((u"q: Quit", - u"?: Help", - u"l: Log window toggle", - u"TAB: Switch window", - u"w: Wrap (log)")))) - self.log_message_raw((u"bold", - u" " - .join((u"Clients:", - u"+: Enable", - u"-: Disable", - u"R: Remove", - u"s: Start new checker", - u"S: Stop checker", - u"C: Checker OK", - u"a: Approve", - u"d: Deny")))) - self.refresh() - elif key == u"tab": - if self.topwidget.get_focus() is self.logbox: - self.topwidget.set_focus(0) - else: - self.topwidget.set_focus(self.logbox) - self.refresh() - #elif (key == u"end" or key == u"meta >" or key == u"G" - # or key == u">"): - # pass # xxx end-of-buffer - #elif (key == u"home" or key == u"meta <" or key == u"g" - # or key == u"<"): - # pass # xxx beginning-of-buffer - #elif key == u"ctrl e" or key == u"$": - # pass # xxx move-end-of-line - #elif key == u"ctrl a" or key == u"^": - # pass # xxx move-beginning-of-line - #elif key == u"ctrl b" or key == u"meta (" or key == u"h": - # pass # xxx left - #elif key == u"ctrl f" or key == u"meta )" or key == u"l": - # pass # xxx right - #elif key == u"a": - # pass # scroll up log - #elif key == u"z": - # pass # scroll down log - elif self.topwidget.selectable(): - self.topwidget.keypress(self.size, key) - self.refresh() - return True - -ui = UserInterface() -try: - ui.run() -except KeyboardInterrupt: - ui.screen.stop() -except Exception, e: - ui.log_message(unicode(e)) - ui.screen.stop() - raise === removed file 'mandos-monitor.xml' --- mandos-monitor.xml 2010-09-30 06:24:20 +0000 +++ mandos-monitor.xml 1970-01-01 00:00:00 +0000 @@ -1,233 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2010 - Teddy Hogeborn - Björn Påhlsson - - -
- - - &COMMANDNAME; - 8 - - - - &COMMANDNAME; - - Text-based GUI to control the Mandos server. - - - - - - &COMMANDNAME; - - - - - DESCRIPTION - - &COMMANDNAME; is an interactive program to - monitor and control the operations of the Mandos server (see - mandos8). - - - - - 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. - - - - - OVERVIEW - - - This program is used to monitor and control the Mandos server. - In particular, it can be used to approve Mandos clients which - have been configured to require approval. It also shows all - significant events reported by the Mandos server. - - - - - KEYS - - This program is used to monitor and control the Mandos server. - In particular, it can be used to approve Mandos clients which - have been configured to require approval. It also shows all - significant events reported by the Mandos server. - - - Global Keys - - Keys - Function - - - - q, Q - Quit - - - Ctrl-L - Redraw screen - - - ?, F1 - Show help - - - l, D - Toggle log window - - - TAB - Switch window - - - w, i - Toggle log window line wrap - - - Up, Ctrl-P, k - Move up a line - - - Down, Ctrl-N, j - Move down a line - - - PageUp, Meta-V, b - Move up a page - - - PageDown, Ctrl-V, SPACE, f - Move down a page - -
- - Client List Keys - - Keys - Function - - - - + - Enable client - - - - - Disable client - - - a - Approve client - - - d - Deny client - - - R, _, Ctrl-K - Remove client - - - s - Start checker for client - - - S - Stop checker for client - - - C - Force a successful check for this client. - -
-
- - - BUGS - - This program can currently only be used to monitor and control a - Mandos server with the default D-Bus service name of - Mandos. - - - - - EXAMPLE - - - This program takes no options: - - - &COMMANDNAME; - - - - - - SECURITY - - This program must be permitted to access the Mandos server via - the D-Bus interface. This normally requires the root user, but - could be configured otherwise by reconfiguring the D-Bus server. - - - - - SEE ALSO - - mandos - 8, - mandos-ctl - 8 - - - -
- - - - - === removed file 'mandos-options.xml' --- mandos-options.xml 2009-02-13 05:38:21 +0000 +++ mandos-options.xml 1970-01-01 00:00:00 +0000 @@ -1,89 +0,0 @@ - - - - - -
- - - <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 normally 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>. (Only if IPv6 usage is - <emphasis>disabled</emphasis> (see below) must this be an IPv4 - address.) - </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 if for some reason is would be necessary to run more than - one server on the same <emphasis>host</emphasis>. This 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> - - <para id="dbus"> - This option controls whether the server will provide a D-Bus - system bus interface. The default is to provide such an - interface. - </para> - - <para id="ipv6"> - This option controls whether the server will use IPv6 sockets and - addresses. The default is to use IPv6. This option should - <emphasis>never</emphasis> normally be turned off, <emphasis>even in - IPv4-only environments</emphasis>. This is because <citerefentry> - <refentrytitle>mandos-client</refentrytitle> - <manvolnum>8mandos</manvolnum></citerefentry> will normally use - IPv6 link-local addresses, and will not be able to find or connect - to the server if this option is turned off. <emphasis>Only - advanced users should consider changing this option</emphasis>. - </para> - -</section> === modified file 'mandos.conf' --- mandos.conf 2009-02-13 05:38:21 +0000 +++ mandos.conf 2008-08-08 01:17:17 +0000 @@ -1,44 +1,7 @@ -# 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 - -# Whether to provide a D-Bus system bus interface or not -;use_dbus = True - -# Whether to use IPv6. (Changing this is NOT recommended.) -;use_ipv6 = True +[server] +#interface = +#address = +#port = +#debug = False +#priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP +#servicename = Mandos === removed file 'mandos.conf.xml' --- mandos.conf.xml 2009-02-25 01:14:29 +0000 +++ mandos.conf.xml 1970-01-01 00:00:00 +0000 @@ -1,266 +0,0 @@ -<?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 CONFNAME "mandos.conf"> -<!ENTITY CONFPATH "<filename>/etc/mandos/mandos.conf</filename>"> -<!ENTITY TIMESTAMP "2009-02-25"> -<!ENTITY % common SYSTEM "common.ent"> -%common; -]> - -<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 - 2009 - 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 = fe80::aede:48ff:fe71:f6f2 -port = 1025 -debug = true -priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP -servicename = Daena -use_dbus = False -use_ipv6 = True - - - - - - 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 interface when it - is brought up. - - - - - - - - - Zeroconf - - - - Zeroconf is the network protocol standard used by clients - for finding the Mandos server on the local network. - - - - - - - - - - - === removed file 'mandos.lsm' --- mandos.lsm 2010-09-28 18:57:31 +0000 +++ mandos.lsm 1970-01-01 00:00:00 +0000 @@ -1,22 +0,0 @@ -Begin4 -Title: Mandos -Version: 1.2 -Entered-date: 2010-09-28 -Description: The Mandos system allows computers to have encrypted -root file systems and at the same time be capable of remote and/or -unattended reboots. -Keywords: boot, encryption, luks, cryptsetup, network, openpgp, -tls, dm-crypt -Author: teddy@fukt.bsnet.se (Teddy Hogeborn), - belorn@fukt.bsnet.se (Björn Påhlsson) -Maintained-by: teddy@fukt.bsnet.se (Teddy Hogeborn), - belorn@fukt.bsnet.se (Björn Påhlsson) -Primary-site: http://www.fukt.bsnet.se/mandos - 132K mandos_1.2.orig.tar.gz -Alternate-site: ftp://ftp.fukt.bsnet.se/pub/mandos - 132K mandos_1.2.orig.tar.gz -Platforms: Requires GCC, GNU libC, Avahi, GnuPG, Python 2.5, and -various other libraries. While made for Debian GNU/Linux, it is -probably portable to other distributions, but not other Unixes. -Copying-policy: GNU General Public License version 3.0 or later -End === removed file 'mandos.xml' --- mandos.xml 2010-09-27 17:39:03 +0000 +++ mandos.xml 1970-01-01 00:00:00 +0000 @@ -1,730 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2008 - 2009 - 2010 - 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. - - - - - - - - - - - - - - - - Set the debugging log level. - LEVEL is a string, one of - CRITICAL, - ERROR, - WARNING, - INFO, or - DEBUG, in order of - increasing verbosity. The default level is - WARNING. - - - - - - - - - - - - - - - - - - - - - - - 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. - - - - - - - - - - See also . - - - - - - - - - - - - - - - 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. (Manual - intervention is required to re-enable a client.) The timeout, - checker program, and interval between checks can be configured - both globally and per client; see - mandos-clients.conf - 5. A client successfully - receiving its password will also be treated as a successful - checker run. - - - - - APPROVAL - - The server can be configured to require manual approval for a - client before it is sent its secret. The delay to wait for such - approval and the default action (approve or deny) can be - configured both globally and per client; see - mandos-clients.conf - 5. By default all clients - will be approved immediately without delay. - - - This can be used to deny a client its secret if not manually - approved within a specified time. It can also be used to make - the server delay before giving a client its secret, allowing - optional manual denying of this specific client. - - - - - - 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. - - - - - D-BUS INTERFACE - - The server will by default provide a D-Bus system bus interface. - This interface will only be accessible by the root user or a - Mandos-specific user, if such a user exists. For documentation - of the D-Bus API, see the file DBUS-API. - - - - - 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 the - &COMMANDNAME; process started last. - - - - - /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 disabled due to having timed out, the - server does not record this fact onto permanent storage. This - has some security implications, see . - - - There is no fine-grained control over logging and debug output. - - - Debug mode is conflated with running in the foreground. - - - The console log messages do 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 starting the server (usually root). - - - 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 disable the client. 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 enabled, 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 'network-protocol.txt' --- network-protocol.txt 1970-01-01 00:00:00 +0000 +++ network-protocol.txt 2008-08-07 22:30:45 +0000 @@ -0,0 +1,19 @@ +The Mandos server announces itself as a Zeroconf service of type +"_mandos._tcp". The Mandos client 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 a file at start time) which binary blob to give the client. No +other authentication or authorization is done by the server. + +| Mandos Client | | Mandos Server | +|--------------------------------------------+-----+---------------| +| Connect | | | +| "1\r\n" | -> | | +| TLS handshake | <-> | TLS handshake | +| OpenPGP public key (part of TLS handshake) | -> | | +| | <- | Binary blob | +| | | Close | === removed file 'overview.xml' --- overview.xml 2008-09-13 15:36:18 +0000 +++ overview.xml 1970-01-01 00:00:00 +0000 @@ -1,17 +0,0 @@ - - - - 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. All network - communication is encrypted using TLS. The - clients are identified by the server using an 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. - === removed file 'plugin-runner.conf' --- plugin-runner.conf 2009-04-17 08:26:17 +0000 +++ plugin-runner.conf 1970-01-01 00:00:00 +0000 @@ -1,10 +0,0 @@ -## This is the configuration file for plugin-runner(8mandos). This -## file should be installed as "/etc/mandos/plugin-runner.conf", and -## will be copied to "/conf/conf.d/mandos/plugin-runner.conf" in the -## initrd.img file. -## -## After editing this file, the initrd image file must be updated for -## the changes to take effect! - -## Example: -#--options-for=mandos-client:--debug === removed file 'plugin-runner.xml' --- plugin-runner.xml 2009-01-18 06:41:57 +0000 +++ plugin-runner.xml 1970-01-01 00:00:00 +0000 @@ -1,640 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2008 - 2009 - 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: - - - - -cd /etc/keys/mandos; &COMMANDNAME; --config-file=/etc/mandos/plugin-runner.conf --plugin-dir /usr/lib/mandos/plugins.d --options-for=mandos-client:--pubkey=pubkey.txt,--seckey=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 - - - -
- - - - - === removed file 'plugins.d/askpass-fifo.c' --- plugins.d/askpass-fifo.c 2010-09-26 18:32:58 +0000 +++ plugins.d/askpass-fifo.c 1970-01-01 00:00:00 +0000 @@ -1,174 +0,0 @@ -/* -*- coding: utf-8 -*- */ -/* - * Askpass-FIFO - Read a password from a FIFO and output it - * - * Copyright © 2008-2010 Teddy Hogeborn - * Copyright © 2008-2010 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() */ -#include /* ssize_t */ -#include /* mkfifo(), S_IRUSR, S_IWUSR */ -#include /* and */ -#include /* errno, EACCES, ENOTDIR, ELOOP, - ENAMETOOLONG, ENOSPC, EROFS, - ENOENT, EEXIST, EFAULT, EMFILE, - ENFILE, ENOMEM, EBADF, EINVAL, EIO, - EISDIR, EFBIG */ -#include /* error() */ -#include /* EXIT_FAILURE, NULL, size_t, free(), - realloc(), EXIT_SUCCESS */ -#include /* open(), O_RDONLY */ -#include /* read(), close(), write(), - STDOUT_FILENO */ -#include /* EX_OSERR, EX_OSFILE, - EX_UNAVAILABLE, EX_IOERR */ - - -int main(__attribute__((unused))int argc, - __attribute__((unused))char **argv){ - int ret = 0; - ssize_t sret; - - /* Create FIFO */ - const char passfifo[] = "/lib/cryptsetup/passfifo"; - ret = mkfifo(passfifo, S_IRUSR | S_IWUSR); - if(ret == -1){ - int e = errno; - error(0, errno, "mkfifo"); - switch(e){ - case EACCES: - case ENOTDIR: - case ELOOP: - return EX_OSFILE; - case ENAMETOOLONG: - case ENOSPC: - case EROFS: - default: - return EX_OSERR; - case ENOENT: - return EX_UNAVAILABLE; /* no "/lib/cryptsetup"? */ - case EEXIST: - break; /* not an error */ - } - } - - /* Open FIFO */ - int fifo_fd = open(passfifo, O_RDONLY); - if(fifo_fd == -1){ - int e = errno; - error(0, errno, "open"); - switch(e){ - case EACCES: - case ENOENT: - case EFAULT: - return EX_UNAVAILABLE; - case ENAMETOOLONG: - case EMFILE: - case ENFILE: - case ENOMEM: - default: - return EX_OSERR; - case ENOTDIR: - case ELOOP: - return EX_OSFILE; - } - } - - /* Read from FIFO */ - char *buf = NULL; - size_t buf_len = 0; - { - size_t buf_allocated = 0; - const size_t blocksize = 1024; - do { - if(buf_len + blocksize > buf_allocated){ - char *tmp = realloc(buf, buf_allocated + blocksize); - if(tmp == NULL){ - error(0, errno, "realloc"); - free(buf); - return EX_OSERR; - } - buf = tmp; - buf_allocated += blocksize; - } - sret = read(fifo_fd, buf + buf_len, buf_allocated - buf_len); - if(sret == -1){ - int e = errno; - free(buf); - errno = e; - error(0, errno, "read"); - switch(e){ - case EBADF: - case EFAULT: - case EINVAL: - default: - return EX_OSERR; - case EIO: - return EX_IOERR; - case EISDIR: - return EX_UNAVAILABLE; - } - } - buf_len += (size_t)sret; - } while(sret != 0); - } - - /* Close FIFO */ - close(fifo_fd); - - /* Print password to stdout */ - size_t written = 0; - while(written < buf_len){ - sret = write(STDOUT_FILENO, buf + written, buf_len - written); - if(sret == -1){ - int e = errno; - free(buf); - errno = e; - error(0, errno, "write"); - switch(e){ - case EBADF: - case EFAULT: - case EINVAL: - return EX_OSFILE; - case EFBIG: - case EIO: - case ENOSPC: - default: - return EX_IOERR; - } - } - written += (size_t)sret; - } - free(buf); - - ret = close(STDOUT_FILENO); - if(ret == -1){ - int e = errno; - error(0, errno, "close"); - switch(e){ - case EBADF: - return EX_OSFILE; - case EIO: - default: - return EX_IOERR; - } - } - return EXIT_SUCCESS; -} === removed file 'plugins.d/askpass-fifo.xml' --- plugins.d/askpass-fifo.xml 2009-01-04 21:54:55 +0000 +++ plugins.d/askpass-fifo.xml 1970-01-01 00:00:00 +0000 @@ -1,162 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2008 - 2009 - Teddy Hogeborn - Björn Påhlsson - - -
- - - &COMMANDNAME; - 8mandos - - - - &COMMANDNAME; - Mandos plugin to get a password from a - FIFO. - - - - - &COMMANDNAME; - - - - - DESCRIPTION - - This program reads a password from a FIFO and - outputs it to standard output. - - - This program 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 meant to be imitate a feature of the - askpass program, so that programs written to - interface with it can keep working under the - Mandos system. - - - - - OPTIONS - - This program takes no options. - - - - - 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 was interrupted or encountered an error, and any output - so far could be corrupt and/or truncated, and should therefore - be ignored. - - - - - FILES - - - /lib/cryptsetup/passfifo - - - This is the FIFO where this program - will read the password. If it does not exist, it will be - created. - - - - - - - - EXAMPLE - - Note that normally, this program will not be invoked directly, - but instead started by the Mandos plugin-runner8mandos - . - - - - This program takes no options. - - - &COMMANDNAME; - - - - - - SECURITY - - The only 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. - - - - - SEE ALSO - - fifo - 7, - plugin-runner - 8mandos - - -
- - - - - === removed file 'plugins.d/mandos-client.xml' --- plugins.d/mandos-client.xml 2010-09-26 18:32:58 +0000 +++ plugins.d/mandos-client.xml 1970-01-01 00:00:00 +0000 @@ -1,654 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2008 - 2009 - 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. In slightly more detail, this client program - brings up a network interface, uses the interface’s IPv6 - link-local address to get network connectivity, uses Zeroconf to - find servers on the local network, and communicates with servers - using TLS with an OpenPGP key to ensure authenticity and - confidentiality. This client program keeps running, trying all - servers on the network, until it receives a satisfactory reply - or a TERM signal is received. If no servers are found, or after - all servers have been tried, it waits indefinitely for new - servers to appear. - - - 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 is the empty - string, which will automatically choose an appropriate - interface. - - - If the option is used, this - specifies the interface to use to connect to the address - given. - - - Note that since this program will normally run in the - initial RAM disk environment, the interface must be an - interface which exists at that stage. Thus, the interface - can not be a pseudo-interface such as br0 - or tun0; such interfaces will not exist - until much later in the boot process, and can not be used - by this program. - - - NAME can be the string - none; this will not use - any specific interface, and will not bring up an interface - on startup. This is not recommended, and only meant for - advanced users. - - - - - - - - - - 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. - - - - - - - - - After bringing the network interface up, the program waits - for the interface to arrive in a running - state before proceeding. During this time, the kernel log - level will be lowered to reduce clutter on the system - console, alleviating any other plugins which might be - using the system console. This option sets the upper - limit of seconds to wait. The default is 2.5 seconds. - - - - - - - - - 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 link-local - address fe80::aede:48ff:fe71:f6f2, port 4711, - using interface eth2: - - - - -&COMMANDNAME; --debug --pubkey keydir/pubkey.txt --seckey keydir/seckey.txt --connect fe80::aede:48ff:fe71:f6f2: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. To safeguard against this, the - server is supposed to notice the client disappearing and 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. - - - - - -
- - - - - - === modified file 'plugins.d/password-prompt.c' --- plugins.d/password-prompt.c 2010-09-26 18:32:58 +0000 +++ plugins.d/password-prompt.c 2008-08-07 21:45:41 +0000 @@ -1,9 +1,8 @@ -/* -*- coding: utf-8; mode: c; mode: orgtbl -*- */ +/* -*- coding: utf-8 -*- */ /* - * Password-prompt - Read a password from the terminal and print it - * - * Copyright © 2008-2010 Teddy Hogeborn - * Copyright © 2008-2010 Björn Påhlsson + * 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 @@ -19,7 +18,8 @@ * along with this program. If not, see * . * - * Contact the authors at . + * Contact the authors at and + * . */ #define _GNU_SOURCE /* getline() */ @@ -32,47 +32,33 @@ #include /* sig_atomic_t, raise(), struct sigaction, sigemptyset(), sigaction(), sigaddset(), SIGINT, - SIGQUIT, SIGHUP, SIGTERM, - raise() */ -#include /* NULL, size_t, ssize_t */ + SIGQUIT, SIGHUP, SIGTERM */ +#include /* NULL, size_t */ #include /* ssize_t */ #include /* EXIT_SUCCESS, EXIT_FAILURE, - getenv() */ + getopt_long */ #include /* fprintf(), stderr, getline(), - stdin, feof(), fputc() - */ -#include /* errno, EBADF, ENOTTY, EINVAL, - EFAULT, EFBIG, EIO, ENOSPC, EINTR - */ -#include /* error() */ + stdin, feof(), perror(), fputc(), + stdout, getopt_long */ +#include /* errno, EINVAL */ #include /* or, not */ #include /* bool, false, true */ -#include /* strlen, rindex */ -#include /* struct argp_option, struct - argp_state, struct argp, - argp_parse(), error_t, - ARGP_KEY_ARG, ARGP_KEY_END, - ARGP_ERR_UNKNOWN */ -#include /* EX_SOFTWARE, EX_OSERR, - EX_UNAVAILABLE, EX_IOERR, EX_OK */ +#include /* strlen, rindex, strncmp, strcmp */ +#include /* struct argp_option, + struct argp_state, struct argp, + argp_parse() */ -volatile sig_atomic_t quit_now = 0; -int signal_received; +volatile bool quit_now = false; bool debug = false; -const char *argp_program_version = "password-prompt " VERSION; +const char *argp_program_version = "passprompt 0.9"; const char *argp_program_bug_address = ""; -static void termination_handler(int signum){ - if(quit_now){ - return; - } - quit_now = 1; - signal_received = signum; +static void termination_handler(__attribute__((unused))int signum){ + quit_now = true; } int main(int argc, char **argv){ - ssize_t sret; - int ret; + ssize_t ret; size_t n; struct termios t_new, t_old; char *buffer = NULL; @@ -85,310 +71,117 @@ struct argp_option options[] = { { .name = "prefix", .key = 'p', .arg = "PREFIX", .flags = 0, - .doc = "Prefix shown before the prompt", .group = 2 }, + .doc = "Prefix used before the passprompt", .group = 2 }, { .name = "debug", .key = 128, .doc = "Debug mode", .group = 3 }, - /* - * These reproduce what we would get without ARGP_NO_HELP - */ - { .name = "help", .key = '?', - .doc = "Give this help list", .group = -1 }, - { .name = "usage", .key = -3, - .doc = "Give a short usage message", .group = -1 }, - { .name = "version", .key = 'V', - .doc = "Print program version", .group = -1 }, { .name = NULL } }; - - error_t parse_opt (int key, char *arg, struct argp_state *state){ - errno = 0; - switch (key){ + + 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; - /* - * These reproduce what we would get without ARGP_NO_HELP - */ - case '?': /* --help */ - argp_state_help(state, state->out_stream, - (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR) - & ~(unsigned int)ARGP_HELP_EXIT_OK); - case -3: /* --usage */ - argp_state_help(state, state->out_stream, - ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR); - case 'V': /* --version */ - fprintf(state->out_stream, "%s\n", argp_program_version); - exit(argp_err_exit_status); + case ARGP_KEY_ARG: + argp_usage (state); + break; + case ARGP_KEY_END: break; default: return ARGP_ERR_UNKNOWN; } - return errno; + 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, - ARGP_IN_ORDER | ARGP_NO_HELP, NULL, NULL); - switch(ret){ - case 0: - break; - case ENOMEM: - default: - errno = ret; - error(0, errno, "argp_parse"); - return EX_OSERR; - case EINVAL: - return EX_USAGE; - } + .doc = "Mandos Passprompt -- Provides a passprompt" }; + argp_parse (&argp, argc, argv, 0, 0, NULL); } - - if(debug){ + + if (debug){ fprintf(stderr, "Starting %s\n", argv[0]); } - if(debug){ + if (debug){ fprintf(stderr, "Storing current terminal attributes\n"); } - if(tcgetattr(STDIN_FILENO, &t_old) != 0){ - int e = errno; - error(0, errno, "tcgetattr"); - switch(e){ - case EBADF: - case ENOTTY: - return EX_UNAVAILABLE; - default: - return EX_OSERR; - } + if (tcgetattr(STDIN_FILENO, &t_old) != 0){ + return EXIT_FAILURE; } sigemptyset(&new_action.sa_mask); - ret = sigaddset(&new_action.sa_mask, SIGINT); - if(ret == -1){ - error(0, errno, "sigaddset"); - return EX_OSERR; - } - ret = sigaddset(&new_action.sa_mask, SIGHUP); - if(ret == -1){ - error(0, errno, "sigaddset"); - return EX_OSERR; - } - ret = sigaddset(&new_action.sa_mask, SIGTERM); - if(ret == -1){ - error(0, errno, "sigaddset"); - return EX_OSERR; - } - /* Need to check if the handler is SIG_IGN before handling: - | [[info:libc:Initial Signal Actions]] | - | [[info:libc:Basic Signal Handling]] | - */ - ret = sigaction(SIGINT, NULL, &old_action); - if(ret == -1){ - error(0, errno, "sigaction"); - return EX_OSERR; - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGINT, &new_action, NULL); - if(ret == -1){ - error(0, errno, "sigaction"); - return EX_OSERR; - } - } - ret = sigaction(SIGHUP, NULL, &old_action); - if(ret == -1){ - error(0, errno, "sigaction"); - return EX_OSERR; - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGHUP, &new_action, NULL); - if(ret == -1){ - error(0, errno, "sigaction"); - return EX_OSERR; - } - } - ret = sigaction(SIGTERM, NULL, &old_action); - if(ret == -1){ - error(0, errno, "sigaction"); - return EX_OSERR; - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGTERM, &new_action, NULL); - if(ret == -1){ - error(0, errno, "sigaction"); - return EX_OSERR; - } - } - - - if(debug){ + sigaddset(&new_action.sa_mask, SIGINT); + sigaddset(&new_action.sa_mask, SIGHUP); + sigaddset(&new_action.sa_mask, SIGTERM); + sigaction(SIGINT, NULL, &old_action); + if (old_action.sa_handler != SIG_IGN) + sigaction(SIGINT, &new_action, NULL); + sigaction(SIGHUP, NULL, &old_action); + if (old_action.sa_handler != SIG_IGN) + sigaction(SIGHUP, &new_action, NULL); + sigaction(SIGTERM, NULL, &old_action); + if (old_action.sa_handler != SIG_IGN) + sigaction(SIGTERM, &new_action, NULL); + + + if (debug){ fprintf(stderr, "Removing echo flag from terminal attributes\n"); } t_new = t_old; - t_new.c_lflag &= ~(tcflag_t)ECHO; - if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){ - int e = errno; - error(0, errno, "tcsetattr-echo"); - switch(e){ - case EBADF: - case ENOTTY: - return EX_UNAVAILABLE; - case EINVAL: - default: - return EX_OSERR; - } + t_new.c_lflag &= ~ECHO; + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){ + perror("tcsetattr-echo"); + return EXIT_FAILURE; } - - if(debug){ + + if (debug){ fprintf(stderr, "Waiting for input from stdin \n"); } while(true){ - if(quit_now){ - if(debug){ - fprintf(stderr, "Interrupted by signal, exiting.\n"); - } + if (quit_now){ status = EXIT_FAILURE; break; } if(prefix){ - fprintf(stderr, "%s ", prefix); - } - { - const char *cryptsource = getenv("CRYPTTAB_SOURCE"); - const char *crypttarget = getenv("CRYPTTAB_NAME"); - /* Before cryptsetup 1.1.0~rc2 */ - if(cryptsource == NULL){ - cryptsource = getenv("cryptsource"); - } - if(crypttarget == NULL){ - crypttarget = getenv("crypttarget"); - } - const char *const prompt1 = "Unlocking the disk"; - const char *const prompt2 = "Enter passphrase"; - if(cryptsource == NULL){ - if(crypttarget == NULL){ - fprintf(stderr, "%s to unlock the disk: ", prompt2); - } else { - fprintf(stderr, "%s (%s)\n%s: ", prompt1, crypttarget, - prompt2); - } - } else { - if(crypttarget == NULL){ - fprintf(stderr, "%s %s\n%s: ", prompt1, cryptsource, - prompt2); - } else { - fprintf(stderr, "%s %s (%s)\n%s: ", prompt1, cryptsource, - crypttarget, prompt2); - } - } - } - sret = getline(&buffer, &n, stdin); - if(sret > 0){ + fprintf(stderr, "%s Password: ", prefix); + } else { + fprintf(stderr, "Password: "); + } + ret = getline(&buffer, &n, stdin); + if (ret > 0){ + fprintf(stdout, "%s", buffer); status = EXIT_SUCCESS; - /* Make n = data size instead of allocated buffer size */ - n = (size_t)sret; - /* 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){ - sret = write(STDOUT_FILENO, buffer + written, n - written); - if(sret < 0){ - int e = errno; - error(0, errno, "write"); - switch(e){ - case EBADF: - case EFAULT: - case EINVAL: - case EFBIG: - case EIO: - case ENOSPC: - default: - status = EX_IOERR; - break; - case EINTR: - status = EXIT_FAILURE; - break; - } - break; - } - written += (size_t)sret; - } - sret = close(STDOUT_FILENO); - if(sret == -1){ - int e = errno; - error(0, errno, "close"); - switch(e){ - case EBADF: - status = EX_OSFILE; - break; - case EIO: - default: - status = EX_IOERR; - break; - } - } break; } - if(sret < 0){ - int e = errno; - if(errno != EINTR and not feof(stdin)){ - error(0, errno, "getline"); - switch(e){ - case EBADF: - status = EX_UNAVAILABLE; - case EIO: - case EINVAL: - default: - status = EX_IOERR; - break; - } + if (ret < 0){ + if (errno != EINTR and not feof(stdin)){ + perror("getline"); + status = EXIT_FAILURE; break; } } - /* if(sret == 0), then the only sensible thing to do is to retry to + /* 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 nonzero, 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){ + if (debug){ fprintf(stderr, "Restoring terminal attributes\n"); } - if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){ - error(0, errno, "tcsetattr+echo"); - } - - if(quit_now){ - sigemptyset(&old_action.sa_mask); - old_action.sa_handler = SIG_DFL; - ret = sigaction(signal_received, &old_action, NULL); - if(ret == -1){ - error(0, errno, "sigaction"); - } - raise(signal_received); - } - - if(debug){ - fprintf(stderr, "%s is exiting with status %d\n", argv[0], - status); - } - if(status == EXIT_SUCCESS or status == EX_OK){ - fputc('\n', stderr); + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){ + perror("tcsetattr+echo"); + } + + if (debug){ + fprintf(stderr, "%s is exiting\n", argv[0]); } return status; === removed file 'plugins.d/password-prompt.xml' --- plugins.d/password-prompt.xml 2009-10-30 16:23:43 +0000 +++ plugins.d/password-prompt.xml 1970-01-01 00:00:00 +0000 @@ -1,308 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2008 - 2009 - 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 program 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 - - - CRYPTTAB_SOURCE - CRYPTTAB_NAME - - - 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, - - -
- - - - - === renamed file 'plugins.d/mandos-client.c' => 'plugins.d/password-request.c' --- plugins.d/mandos-client.c 2010-09-26 21:27:28 +0000 +++ plugins.d/password-request.c 2008-08-07 21:45:41 +0000 @@ -1,6 +1,6 @@ /* -*- coding: utf-8 -*- */ /* - * Mandos-client - get and decrypt data from a Mandos server + * 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 @@ -9,8 +9,7 @@ * "browse_callback", and parts of "main". * * Everything else is - * Copyright © 2008-2010 Teddy Hogeborn - * Copyright © 2008-2010 Björn Påhlsson + * 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 @@ -30,69 +29,21 @@ */ /* Needed by GPGME, specifically gpgme_data_seek() */ -#ifndef _LARGEFILE_SOURCE #define _LARGEFILE_SOURCE -#endif -#ifndef _FILE_OFFSET_BITS #define _FILE_OFFSET_BITS 64 -#endif - -#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */ - -#include /* fprintf(), stderr, fwrite(), - stdout, ferror(), remove() */ -#include /* uint16_t, uint32_t */ -#include /* NULL, size_t, ssize_t */ -#include /* free(), EXIT_SUCCESS, srand(), - strtof(), abort() */ -#include /* bool, false, true */ -#include /* memset(), strcmp(), strlen(), - strerror(), asprintf(), strcpy() */ -#include /* ioctl */ -#include /* socket(), inet_pton(), sockaddr, - sockaddr_in6, PF_INET6, - SOCK_STREAM, uid_t, gid_t, open(), - opendir(), DIR */ -#include /* open() */ -#include /* socket(), struct sockaddr_in6, - inet_pton(), connect() */ -#include /* open() */ -#include /* opendir(), struct dirent, readdir() - */ -#include /* PRIu16, PRIdMAX, intmax_t, - strtoimax() */ -#include /* assert() */ -#include /* perror(), errno */ -#include /* nanosleep(), time(), sleep() */ + +#define _GNU_SOURCE /* TEMP_FAILURE_RETRY() */ + +#include +#include +#include +#include +#include /* if_nametoindex */ +#include /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, + SIOCSIFFLAGS */ #include /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP, - SIOCSIFFLAGS, if_indextoname(), - if_nametoindex(), IF_NAMESIZE */ -#include /* IN6_IS_ADDR_LINKLOCAL, - INET_ADDRSTRLEN, INET6_ADDRSTRLEN - */ -#include /* close(), SEEK_SET, off_t, write(), - getuid(), getgid(), seteuid(), - setgid(), pause() */ -#include /* inet_pton(), htons */ -#include /* not, or, and */ -#include /* struct argp_option, error_t, struct - argp_state, struct argp, - argp_parse(), ARGP_KEY_ARG, - ARGP_KEY_END, ARGP_ERR_UNKNOWN */ -#include /* sigemptyset(), sigaddset(), - sigaction(), SIGTERM, sig_atomic_t, - raise() */ -#include /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE, - EX_NOHOST, EX_IOERR, EX_PROTOCOL */ - -#ifdef __linux__ -#include /* klogctl() */ -#endif /* __linux__ */ - -/* Avahi */ -/* All Avahi types, constants and functions - Avahi*, avahi_*, - AVAHI_* */ + SIOCSIFFLAGS */ + #include #include #include @@ -100,35 +51,34 @@ #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 */ +/* Mandos client part */ +#include /* socket(), inet_pton() */ +#include /* socket(), struct sockaddr_in6, + struct in6_addr, inet_pton() */ +#include /* All GnuTLS stuff */ +#include /* GnuTLS with openpgp stuff */ +#include /* close() */ +#include +#include /* true */ +#include /* memset */ +#include /* inet_pton() */ +#include /* not */ +#include /* IF_NAMESIZE */ +#include /* struct argp_option, + struct argp_state, struct argp, + argp_parse() */ /* GPGME */ -#include /* All GPGME types, constants and - functions: - gpgme_* - GPGME_PROTOCOL_OpenPGP, - GPG_ERR_NO_* */ +#include /* perror() */ +#include #define BUFFER_SIZE 256 -#define PATHDIR "/conf/conf.d/mandos" -#define SECKEY "seckey.txt" -#define PUBKEY "pubkey.txt" - bool debug = false; +static const char *keydir = "/conf/conf.d/mandos"; static const char mandos_protocol_version[] = "1"; -const char *argp_program_version = "mandos-client " VERSION; +const char *argp_program_version = "mandosclient 0.9"; const char *argp_program_bug_address = ""; -static const char sys_class_net[] = "/sys/class/net"; -char *connect_to = NULL; /* Used for passing in values through the Avahi callback functions */ typedef struct { @@ -138,27 +88,18 @@ unsigned int dh_bits; gnutls_dh_params_t dh_params; const char *priority; - gpgme_ctx_t ctx; } mandos_context; -/* global context so signal handler can reach it*/ -mandos_context mc = { .simple_poll = NULL, .server = NULL, - .dh_bits = 1024, .priority = "SECURE256" - ":!CTYPE-X.509:+CTYPE-OPENPGP" }; - -sig_atomic_t quit_now = 0; -int signal_received = 0; - /* - * Make additional room in "buffer" for at least BUFFER_SIZE more - * bytes. "buffer_capacity" is how much is currently allocated, + * 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 incbuffer(char **buffer, size_t buffer_length, +size_t adjustbuffer(char **buffer, size_t buffer_length, size_t buffer_capacity){ - if(buffer_length + BUFFER_SIZE > buffer_capacity){ + if (buffer_length + BUFFER_SIZE > buffer_capacity){ *buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE); - if(buffer == NULL){ + if (buffer == NULL){ return 0; } buffer_capacity += BUFFER_SIZE; @@ -167,119 +108,58 @@ } /* - * Initialize GPGME. + * Decrypt OpenPGP data using keyrings in HOMEDIR. + * Returns -1 on error */ -static bool init_gpgme(const char *seckey, - const char *pubkey, const char *tempdir){ +static ssize_t pgp_packet_decrypt (const char *cryptotext, + size_t crypto_size, + char **plaintext, + const char *homedir){ + gpgme_data_t dh_crypto, dh_plain; + gpgme_ctx_t ctx; gpgme_error_t rc; + ssize_t ret; + size_t plaintext_capacity = 0; + ssize_t plaintext_length = 0; gpgme_engine_info_t engine_info; - - /* - * Helper function to insert pub and seckey to the engine keyring. - */ - bool import_key(const char *filename){ - int ret; - int fd; - gpgme_data_t pgp_data; - - fd = (int)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 = (int)TEMP_FAILURE_RETRY(close(fd)); - if(ret == -1){ - perror("close"); - } - gpgme_data_release(pgp_data); - return true; - } - - if(debug){ - fprintf(stderr, "Initializing GPGME\n"); + if (debug){ + fprintf(stderr, "Trying to decrypt OpenPGP data\n"); } /* Init GPGME */ gpgme_check_version(NULL); rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP); - if(rc != GPG_ERR_NO_ERROR){ + if (rc != GPG_ERR_NO_ERROR){ fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n", gpgme_strsource(rc), gpgme_strerror(rc)); - return false; + return -1; } - /* Set GPGME home directory for the OpenPGP engine only */ - rc = gpgme_get_engine_info(&engine_info); - if(rc != GPG_ERR_NO_ERROR){ + /* 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; + return -1; } while(engine_info != NULL){ if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){ gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, - engine_info->file_name, tempdir); + engine_info->file_name, homedir); 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 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"); + fprintf(stderr, "Could not set GPGME home dir to %s\n", homedir); + return -1; } /* 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){ + 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; @@ -287,35 +167,52 @@ /* Create new empty GPGME data buffer for the plaintext */ rc = gpgme_data_new(&dh_plain); - if(rc != GPG_ERR_NO_ERROR){ + 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; } + /* Create new GPGME "context" */ + rc = gpgme_new(&ctx); + if (rc != GPG_ERR_NO_ERROR){ + fprintf(stderr, "bad gpgme_new: %s: %s\n", + gpgme_strsource(rc), gpgme_strerror(rc)); + plaintext_length = -1; + goto decrypt_end; + } + /* 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){ + rc = gpgme_op_decrypt(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; + goto decrypt_end; + } + + if(debug){ + fprintf(stderr, "Decryption of OpenPGP data succeeded\n"); + } + + if (debug){ + gpgme_decrypt_result_t result; + result = gpgme_op_decrypt_result(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: %d\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)); @@ -327,27 +224,22 @@ } } } - 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"); + if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){ + perror("pgpme_data_seek"); plaintext_length = -1; goto decrypt_end; } *plaintext = NULL; while(true){ - plaintext_capacity = incbuffer(plaintext, + plaintext_capacity = adjustbuffer(plaintext, (size_t)plaintext_length, plaintext_capacity); - if(plaintext_capacity == 0){ - perror("incbuffer"); + if (plaintext_capacity == 0){ + perror("adjustbuffer"); plaintext_length = -1; goto decrypt_end; } @@ -355,7 +247,7 @@ ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length, BUFFER_SIZE); /* Print the data, if any */ - if(ret == 0){ + if (ret == 0){ /* EOF */ break; } @@ -366,7 +258,7 @@ } plaintext_length += ret; } - + if(debug){ fprintf(stderr, "Decrypted password is: "); for(ssize_t i = 0; i < plaintext_length; i++){ @@ -385,10 +277,9 @@ return plaintext_length; } -static const char * safer_gnutls_strerror(int value){ - const char *ret = gnutls_strerror(value); /* Spurious warning from - -Wunreachable-code */ - if(ret == NULL) +static const char * safer_gnutls_strerror (int value) { + const char *ret = gnutls_strerror (value); + if (ret == NULL) ret = "(unknown)"; return ret; } @@ -399,22 +290,23 @@ fprintf(stderr, "GnuTLS: %s", string); } -static int init_gnutls_global(const char *pubkeyfilename, - const char *seckeyfilename){ +static int init_gnutls_global(mandos_context *mc, + const char *pubkeyfile, + const char *seckeyfile){ 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)); + + if ((ret = gnutls_global_init ()) + != GNUTLS_E_SUCCESS) { + fprintf (stderr, "GnuTLS global_init: %s\n", + safer_gnutls_strerror(ret)); return -1; } - if(debug){ + if (debug){ /* "Use a log level over 10 to enable all debugging options." * - GnuTLS manual */ @@ -423,112 +315,93 @@ } /* OpenPGP credentials */ - gnutls_certificate_allocate_credentials(&mc.cred); - if(ret != GNUTLS_E_SUCCESS){ - fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning - from - -Wunreachable-code - */ - safer_gnutls_strerror(ret)); - gnutls_global_deinit(); + if ((ret = gnutls_certificate_allocate_credentials (&mc->cred)) + != GNUTLS_E_SUCCESS) { + fprintf (stderr, "GnuTLS memory error: %s\n", + 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); + fprintf(stderr, "Attempting to use OpenPGP certificate %s" + " and keyfile %s as GnuTLS credentials\n", pubkeyfile, + seckeyfile); } ret = gnutls_certificate_set_openpgp_key_file - (mc.cred, pubkeyfilename, seckeyfilename, - GNUTLS_OPENPGP_FMT_BASE64); - if(ret != GNUTLS_E_SUCCESS){ + (mc->cred, pubkeyfile, seckeyfile, 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", + " '%s')\n", ret, pubkeyfile, seckeyfile); + fprintf(stdout, "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); - + 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); + + gnutls_certificate_free_credentials (mc->cred); + gnutls_global_deinit (); return -1; + } -static int init_gnutls_session(gnutls_session_t *session){ +static int init_gnutls_session(mandos_context *mc, + gnutls_session_t *session){ int ret; /* GnuTLS session creation */ - do { - ret = gnutls_init(session, GNUTLS_SERVER); - if(quit_now){ - return -1; - } - } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN); - if(ret != GNUTLS_E_SUCCESS){ + 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; - do { - ret = gnutls_priority_set_direct(*session, mc.priority, &err); - if(quit_now){ - gnutls_deinit(*session); - return -1; - } - } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN); - if(ret != GNUTLS_E_SUCCESS){ + 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); + gnutls_deinit (*session); return -1; } } - do { - ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE, - mc.cred); - if(quit_now){ - gnutls_deinit(*session); - return -1; - } - } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN); - if(ret != GNUTLS_E_SUCCESS){ + 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); + gnutls_deinit (*session); return -1; } /* ignore client certificate if any. */ - gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE); + gnutls_certificate_server_set_request (*session, + GNUTLS_CERT_IGNORE); - gnutls_dh_set_prime_bits(*session, mc.dh_bits); + gnutls_dh_set_prime_bits (*session, mc->dh_bits); return 0; } @@ -540,137 +413,65 @@ /* Called when a Mandos server is found */ static int start_mandos_communication(const char *ip, uint16_t port, AvahiIfIndex if_index, - int af){ - int ret, tcp_sd = -1; - ssize_t sret; - union { - struct sockaddr_in in; - struct sockaddr_in6 in6; - } to; + mandos_context *mc){ + int ret, tcp_sd; + union { struct sockaddr in; struct sockaddr_in6 in6; } to; char *buffer = NULL; - char *decrypted_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 = -1; + int retval = 0; + char interface[IF_NAMESIZE]; gnutls_session_t session; - int pf; /* Protocol family */ - - errno = 0; - - if(quit_now){ - errno = EINTR; - return -1; - } - - switch(af){ - case AF_INET6: - pf = PF_INET6; - break; - case AF_INET: - pf = PF_INET; - break; - default: - fprintf(stderr, "Bad address family: %d\n", af); - errno = EINVAL; - return -1; - } - - ret = init_gnutls_session(&session); - if(ret != 0){ + + 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); + fprintf(stderr, "Setting up a tcp connection to %s, port %d\n", + ip, port); } - tcp_sd = socket(pf, SOCK_STREAM, 0); - if(tcp_sd < 0){ - int e = errno; + tcp_sd = socket(PF_INET6, SOCK_STREAM, 0); + if(tcp_sd < 0) { perror("socket"); - errno = e; - goto mandos_end; - } - - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - memset(&to, 0, sizeof(to)); - if(af == AF_INET6){ - to.in6.sin6_family = (sa_family_t)af; - ret = inet_pton(af, ip, &to.in6.sin6_addr); - } else { /* IPv4 */ - to.in.sin_family = (sa_family_t)af; - ret = inet_pton(af, ip, &to.in.sin_addr); - } - if(ret < 0 ){ - int e = errno; + 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)); /* Spurious warning */ + 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"); - errno = e; - goto mandos_end; + return -1; } if(ret == 0){ - int e = errno; fprintf(stderr, "Bad address: %s\n", ip); - errno = e; - goto mandos_end; - } - if(af == AF_INET6){ - to.in6.sin6_port = htons(port); /* Spurious warnings from - -Wconversion and - -Wunreachable-code */ - - if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */ - (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and - -Wunreachable-code*/ - if(if_index == AVAHI_IF_UNSPEC){ - fprintf(stderr, "An IPv6 link-local address is incomplete" - " without a network interface\n"); - errno = EINVAL; - goto mandos_end; - } - /* Set the network interface number as scope */ - to.in6.sin6_scope_id = (uint32_t)if_index; - } - } else { - to.in.sin_port = htons(port); /* Spurious warnings from - -Wconversion and - -Wunreachable-code */ - } + return -1; + } + to.in6.sin6_port = htons(port); /* Spurious warning */ - if(quit_now){ - errno = EINTR; - goto mandos_end; - } + to.in6.sin6_scope_id = (uint32_t)if_index; if(debug){ - if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){ - char interface[IF_NAMESIZE]; - if(if_indextoname((unsigned int)if_index, interface) == NULL){ - perror("if_indextoname"); - } else { - fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n", - ip, interface, port); - } - } else { - fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip, - port); - } - char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ? - INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = ""; - const char *pcret; - if(af == AF_INET6){ - pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr, - sizeof(addrstr)); - } else { - pcret = inet_ntop(af, &(to.in.sin_addr), addrstr, - sizeof(addrstr)); - } - if(pcret == NULL){ + fprintf(stderr, "Connection to: %s, port %d\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){ @@ -679,156 +480,97 @@ } } - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - if(af == AF_INET6){ - ret = connect(tcp_sd, &to.in6, sizeof(to)); - } else { - ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */ - } - if(ret < 0){ - if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){ - int e = errno; - perror("connect"); - errno = e; - } - goto mandos_end; - } - - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - + 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){ + while (true){ size_t out_size = strlen(out); - ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written, + ret = TEMP_FAILURE_RETRY(write(tcp_sd, out + written, out_size - written)); - if(ret == -1){ - int e = errno; + if (ret == -1){ perror("write"); - errno = e; + retval = -1; goto mandos_end; } written += (size_t)ret; if(written < out_size){ continue; } else { - if(out == mandos_protocol_version){ + if (out == mandos_protocol_version){ written = 0; out = "\r\n"; } else { break; } } - - if(quit_now){ - errno = EINTR; - goto mandos_end; - } } - + if(debug){ fprintf(stderr, "Establishing TLS session with %s\n", ip); } - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd); - - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - do { - ret = gnutls_handshake(session); - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED); - - if(ret != GNUTLS_E_SUCCESS){ + gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd); + + ret = gnutls_handshake (session); + + if (ret != GNUTLS_E_SUCCESS){ if(debug){ fprintf(stderr, "*** GnuTLS Handshake failed ***\n"); - gnutls_perror(ret); + gnutls_perror (ret); } - errno = EPROTO; + retval = -1; goto mandos_end; } /* Read OpenPGP packet that contains the wanted password */ if(debug){ - fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n", + fprintf(stderr, "Retrieving pgp encrypted password from %s\n", ip); } - + while(true){ - - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - buffer_capacity = incbuffer(&buffer, buffer_length, + buffer_capacity = adjustbuffer(&buffer, buffer_length, buffer_capacity); - if(buffer_capacity == 0){ - int e = errno; - perror("incbuffer"); - errno = e; - goto mandos_end; - } - - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - sret = gnutls_record_recv(session, buffer+buffer_length, - BUFFER_SIZE); - if(sret == 0){ + 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(sret < 0){ - switch(sret){ + if (ret < 0){ + switch(ret){ case GNUTLS_E_INTERRUPTED: case GNUTLS_E_AGAIN: break; case GNUTLS_E_REHANDSHAKE: - do { - ret = gnutls_handshake(session); - - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED); - if(ret < 0){ + ret = gnutls_handshake (session); + if (ret < 0){ fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n"); - gnutls_perror(ret); - errno = EPROTO; + gnutls_perror (ret); + retval = -1; goto mandos_end; } break; default: fprintf(stderr, "Unknown error while reading data from" " encrypted session with Mandos server\n"); - gnutls_bye(session, GNUTLS_SHUT_RDWR); - errno = EIO; + retval = -1; + gnutls_bye (session, GNUTLS_SHUT_RDWR); goto mandos_end; } } else { - buffer_length += (size_t) sret; + buffer_length += (size_t) ret; } } @@ -836,80 +578,47 @@ fprintf(stderr, "Closing TLS session\n"); } - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - do { - ret = gnutls_bye(session, GNUTLS_SHUT_RDWR); - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED); - - if(buffer_length > 0){ - ssize_t decrypted_buffer_size; + gnutls_bye (session, GNUTLS_SHUT_RDWR); + + if (buffer_length > 0){ decrypted_buffer_size = pgp_packet_decrypt(buffer, buffer_length, - &decrypted_buffer); - if(decrypted_buffer_size >= 0){ - + &decrypted_buffer, + keydir); + if (decrypted_buffer_size >= 0){ written = 0; while(written < (size_t) decrypted_buffer_size){ - if(quit_now){ - errno = EINTR; - goto mandos_end; - } - - ret = (int)fwrite(decrypted_buffer + written, 1, - (size_t)decrypted_buffer_size - written, - stdout); + ret = (int)fwrite (decrypted_buffer + written, 1, + (size_t)decrypted_buffer_size - written, + stdout); if(ret == 0 and ferror(stdout)){ - int e = errno; if(debug){ fprintf(stderr, "Error writing encrypted data: %s\n", strerror(errno)); } - errno = e; - goto mandos_end; + retval = -1; + break; } written += (size_t)ret; } - retval = 0; + free(decrypted_buffer); + } else { + retval = -1; } } /* Shutdown procedure */ mandos_end: - { - int e = errno; - free(decrypted_buffer); - free(buffer); - if(tcp_sd >= 0){ - ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd)); - } - if(ret == -1){ - if(e == 0){ - e = errno; - } - perror("close"); - } - gnutls_deinit(session); - if(quit_now){ - e = EINTR; - retval = -1; - } - errno = e; - } + free(buffer); + close(tcp_sd); + gnutls_deinit (session); return retval; } static void resolve_callback(AvahiSServiceResolver *r, AvahiIfIndex interface, - AvahiProtocol proto, + AVAHI_GCC_UNUSED AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, @@ -920,22 +629,19 @@ AVAHI_GCC_UNUSED AvahiStringList *txt, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, - AVAHI_GCC_UNUSED void* userdata){ - assert(r); + void* userdata) { + mandos_context *mc = userdata; + assert(r); /* Spurious warning */ /* Called whenever a service has been resolved successfully or timed out */ - if(quit_now){ - return; - } - - switch(event){ + 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))); + avahi_strerror(avahi_server_errno(mc->server))); break; case AVAHI_RESOLVER_FOUND: @@ -943,46 +649,41 @@ char ip[AVAHI_ADDRESS_STR_MAX]; avahi_address_snprint(ip, sizeof(ip), address); if(debug){ - fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %" - PRIdMAX ") on port %" PRIu16 "\n", name, host_name, - ip, (intmax_t)interface, port); + fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %d) on" + " port %d\n", name, host_name, ip, interface, port); } - int ret = start_mandos_communication(ip, port, interface, - avahi_proto_to_af(proto)); - if(ret == 0){ - avahi_simple_poll_quit(mc.simple_poll); + int ret = start_mandos_communication(ip, port, interface, mc); + if (ret == 0){ + exit(EXIT_SUCCESS); } } } 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, - AVAHI_GCC_UNUSED void* userdata){ - assert(b); +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); /* Spurious warning */ /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ - if(quit_now){ - return; - } - - switch(event){ + 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); + avahi_strerror(avahi_server_errno(mc->server))); + avahi_simple_poll_quit(mc->simple_poll); return; case AVAHI_BROWSER_NEW: @@ -991,11 +692,12 @@ 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, protocol, 0, - resolve_callback, NULL) == NULL) + 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))); + name, avahi_strerror(avahi_server_errno(mc->server))); break; case AVAHI_BROWSER_REMOVE: @@ -1010,809 +712,310 @@ } } -/* stop main loop after sigterm has been called */ -static void handle_sigterm(int sig){ - if(quit_now){ - return; - } - quit_now = 1; - signal_received = sig; - int old_errno = errno; - if(mc.simple_poll != NULL){ - avahi_simple_poll_quit(mc.simple_poll); - } - errno = old_errno; +/* Combines file name and path and returns the malloced new + string. some sane checks could/should be added */ +static const char *combinepath(const char *first, const char *second){ + size_t f_len = strlen(first); + size_t s_len = strlen(second); + char *tmp = malloc(f_len + s_len + 2); + if (tmp == NULL){ + return NULL; + } + if(f_len > 0){ + memcpy(tmp, first, f_len); /* Spurious warning */ + } + tmp[f_len] = '/'; + if(s_len > 0){ + memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */ + } + tmp[f_len + 1 + s_len] = '\0'; + return tmp; } -/* - * This function determines if a directory entry in /sys/class/net - * corresponds to an acceptable network device. - * (This function is passed to scandir(3) as a filter function.) - */ -int good_interface(const struct dirent *if_entry){ - ssize_t ssret; - char *flagname = NULL; - int ret = asprintf(&flagname, "%s/%s/flags", sys_class_net, - if_entry->d_name); - if(ret < 0){ - perror("asprintf"); - return 0; - } - if(if_entry->d_name[0] == '.'){ - return 0; - } - int flags_fd = (int)TEMP_FAILURE_RETRY(open(flagname, O_RDONLY)); - if(flags_fd == -1){ - perror("open"); - return 0; - } - typedef short ifreq_flags; /* ifreq.ifr_flags in netdevice(7) */ - /* read line from flags_fd */ - ssize_t to_read = (sizeof(ifreq_flags)*2)+3; /* "0x1003\n" */ - char *flagstring = malloc((size_t)to_read+1); /* +1 for final \0 */ - flagstring[(size_t)to_read] = '\0'; - if(flagstring == NULL){ - perror("malloc"); - close(flags_fd); - return 0; - } - while(to_read > 0){ - ssret = (ssize_t)TEMP_FAILURE_RETRY(read(flags_fd, flagstring, - (size_t)to_read)); - if(ssret == -1){ - perror("read"); - free(flagstring); - close(flags_fd); - return 0; - } - to_read -= ssret; - if(ssret == 0){ - break; - } - } - close(flags_fd); - intmax_t tmpmax; - char *tmp; - errno = 0; - tmpmax = strtoimax(flagstring, &tmp, 0); - if(errno != 0 or tmp == flagstring or (*tmp != '\0' - and not (isspace(*tmp))) - or tmpmax != (ifreq_flags)tmpmax){ - if(debug){ - fprintf(stderr, "Invalid flags \"%s\" for interface \"%s\"\n", - flagstring, if_entry->d_name); - } - free(flagstring); - return 0; - } - free(flagstring); - ifreq_flags flags = (ifreq_flags)tmpmax; - /* Reject the loopback device */ - if(flags & IFF_LOOPBACK){ - if(debug){ - fprintf(stderr, "Rejecting loopback interface \"%s\"\n", - if_entry->d_name); - } - return 0; - } - /* Accept point-to-point devices only if connect_to is specified */ - if(connect_to != NULL and (flags & IFF_POINTOPOINT)){ - if(debug){ - fprintf(stderr, "Accepting point-to-point interface \"%s\"\n", - if_entry->d_name); - } - return 1; - } - /* Otherwise, reject non-broadcast-capable devices */ - if(not (flags & IFF_BROADCAST)){ - if(debug){ - fprintf(stderr, "Rejecting non-broadcast interface \"%s\"\n", - if_entry->d_name); - } - return 0; - } - /* Accept this device */ - if(debug){ - fprintf(stderr, "Interface \"%s\" is acceptable\n", - if_entry->d_name); - } - return 1; -} int main(int argc, char *argv[]){ - AvahiSServiceBrowser *sb = NULL; - int error; - int ret; - intmax_t tmpmax; - char *tmp; - int exitcode = EXIT_SUCCESS; - const char *interface = ""; - struct ifreq network; - int sd = -1; - bool take_down_interface = false; - uid_t uid; - gid_t gid; - char tempdir[] = "/tmp/mandosXXXXXX"; - bool tempdir_created = false; - AvahiIfIndex if_index = AVAHI_IF_UNSPEC; - const char *seckey = PATHDIR "/" SECKEY; - const char *pubkey = PATHDIR "/" PUBKEY; - - bool gnutls_initialized = false; - bool gpgme_initialized = false; - float delay = 2.5f; - - struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL }; - struct sigaction sigterm_action = { .sa_handler = handle_sigterm }; - - uid = getuid(); - gid = getgid(); - - /* Lower any group privileges we might have, just to be safe */ - errno = 0; - ret = setgid(gid); - if(ret == -1){ - perror("setgid"); - } - - /* Lower user privileges (temporarily) */ - errno = 0; - ret = seteuid(uid); - if(ret == -1){ - perror("seteuid"); - } - - if(quit_now){ - goto end; - } - - { - 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 = "Network 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 = "delay", .key = 131, - .arg = "SECONDS", - .doc = "Maximum delay to wait for interface startup", - .group = 2 }, - /* - * These reproduce what we would get without ARGP_NO_HELP - */ - { .name = "help", .key = '?', - .doc = "Give this help list", .group = -1 }, - { .name = "usage", .key = -3, - .doc = "Give a short usage message", .group = -1 }, - { .name = "version", .key = 'V', - .doc = "Print program version", .group = -1 }, - { .name = NULL } - }; - - error_t parse_opt(int key, char *arg, - struct argp_state *state){ - errno = 0; - 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; - tmpmax = strtoimax(arg, &tmp, 10); - if(errno != 0 or tmp == arg or *tmp != '\0' - or tmpmax != (typeof(mc.dh_bits))tmpmax){ - argp_error(state, "Bad number of DH bits"); - } - mc.dh_bits = (typeof(mc.dh_bits))tmpmax; - break; - case 130: /* --priority */ - mc.priority = arg; - break; - case 131: /* --delay */ - errno = 0; - delay = strtof(arg, &tmp); - if(errno != 0 or tmp == arg or *tmp != '\0'){ - argp_error(state, "Bad delay"); - } - break; - /* - * These reproduce what we would get without ARGP_NO_HELP - */ - case '?': /* --help */ - argp_state_help(state, state->out_stream, - (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR) - & ~(unsigned int)ARGP_HELP_EXIT_OK); - case -3: /* --usage */ - argp_state_help(state, state->out_stream, - ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR); - case 'V': /* --version */ - fprintf(state->out_stream, "%s\n", argp_program_version); - exit(argp_err_exit_status); - break; - default: - return ARGP_ERR_UNKNOWN; - } - return errno; - } - - 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, - ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL); - switch(ret){ - case 0: - break; - case ENOMEM: - default: - errno = ret; - perror("argp_parse"); - exitcode = EX_OSERR; - goto end; - case EINVAL: - exitcode = EX_USAGE; - goto end; - } - } - - if(not debug){ - avahi_set_log_function(empty_log); - } - - if(interface[0] == '\0'){ - struct dirent **direntries; - ret = scandir(sys_class_net, &direntries, good_interface, - alphasort); - if(ret >= 1){ - /* Pick the first good interface */ - interface = strdup(direntries[0]->d_name); - if(debug){ - fprintf(stderr, "Using interface \"%s\"\n", interface); - } - if(interface == NULL){ - perror("malloc"); - free(direntries); - exitcode = EXIT_FAILURE; - goto end; - } - free(direntries); - } else { - free(direntries); - fprintf(stderr, "Could not find a network interface\n"); + 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; + AvahiIfIndex if_index = AVAHI_IF_UNSPEC; + const char *pubkeyfile = "pubkey.txt"; + const char *seckeyfile = "seckey.txt"; + mandos_context mc = { .simple_poll = NULL, .server = NULL, + .dh_bits = 1024, .priority = "SECURE256"}; + bool gnutls_initalized = false; + + { + struct argp_option options[] = { + { .name = "debug", .key = 128, + .doc = "Debug mode", .group = 3 }, + { .name = "connect", .key = 'c', + .arg = "IP", + .doc = "Connect directly to a sepcified mandos server", + .group = 1 }, + { .name = "interface", .key = 'i', + .arg = "INTERFACE", + .doc = "Interface that Avahi will conntect through", + .group = 1 }, + { .name = "keydir", .key = 'd', + .arg = "KEYDIR", + .doc = "Directory where the openpgp keyring is", + .group = 1 }, + { .name = "seckey", .key = 's', + .arg = "SECKEY", + .doc = "Secret openpgp key for gnutls authentication", + .group = 1 }, + { .name = "pubkey", .key = 'p', + .arg = "PUBKEY", + .doc = "Public openpgp key for gnutls authentication", + .group = 2 }, + { .name = "dh-bits", .key = 129, + .arg = "BITS", + .doc = "dh-bits to use in gnutls communication", + .group = 2 }, + { .name = "priority", .key = 130, + .arg = "PRIORITY", + .doc = "GNUTLS priority", .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 = true; + break; + case 'c': + connect_to = arg; + break; + case 'i': + interface = arg; + break; + case 'd': + keydir = arg; + break; + case 's': + seckeyfile = arg; + break; + case 'p': + pubkeyfile = arg; + break; + case 129: + errno = 0; + mc.dh_bits = (unsigned int) strtol(arg, NULL, 10); + if (errno){ + perror("strtol"); + exit(EXIT_FAILURE); + } + break; + case 130: + mc.priority = arg; + 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 client -- Get and decrypt" + " passwords from mandos server" }; + argp_parse (&argp, argc, argv, 0, 0, NULL); + } + + pubkeyfile = combinepath(keydir, pubkeyfile); + if (pubkeyfile == NULL){ + perror("combinepath"); exitcode = EXIT_FAILURE; goto end; } - } - - /* Initialize Avahi early so avahi_simple_poll_quit() can be called - from the signal handler */ - /* Initialize the pseudo-RNG for Avahi */ - srand((unsigned int) time(NULL)); - mc.simple_poll = avahi_simple_poll_new(); - if(mc.simple_poll == NULL){ - fprintf(stderr, "Avahi: Failed to create simple poll object.\n"); - exitcode = EX_UNAVAILABLE; - goto end; - } - - sigemptyset(&sigterm_action.sa_mask); - ret = sigaddset(&sigterm_action.sa_mask, SIGINT); - if(ret == -1){ - perror("sigaddset"); - exitcode = EX_OSERR; - goto end; - } - ret = sigaddset(&sigterm_action.sa_mask, SIGHUP); - if(ret == -1){ - perror("sigaddset"); - exitcode = EX_OSERR; - goto end; - } - ret = sigaddset(&sigterm_action.sa_mask, SIGTERM); - if(ret == -1){ - perror("sigaddset"); - exitcode = EX_OSERR; - goto end; - } - /* Need to check if the handler is SIG_IGN before handling: - | [[info:libc:Initial Signal Actions]] | - | [[info:libc:Basic Signal Handling]] | - */ - ret = sigaction(SIGINT, NULL, &old_sigterm_action); - if(ret == -1){ - perror("sigaction"); - return EX_OSERR; - } - if(old_sigterm_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGINT, &sigterm_action, NULL); - if(ret == -1){ - perror("sigaction"); - exitcode = EX_OSERR; - goto end; - } - } - ret = sigaction(SIGHUP, NULL, &old_sigterm_action); - if(ret == -1){ - perror("sigaction"); - return EX_OSERR; - } - if(old_sigterm_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGHUP, &sigterm_action, NULL); - if(ret == -1){ - perror("sigaction"); - exitcode = EX_OSERR; - goto end; - } - } - ret = sigaction(SIGTERM, NULL, &old_sigterm_action); - if(ret == -1){ - perror("sigaction"); - return EX_OSERR; - } - if(old_sigterm_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGTERM, &sigterm_action, NULL); - if(ret == -1){ - perror("sigaction"); - exitcode = EX_OSERR; - goto end; - } - } - - /* If the interface is down, bring it up */ - if(strcmp(interface, "none") != 0){ + + seckeyfile = combinepath(keydir, seckeyfile); + if (seckeyfile == NULL){ + perror("combinepath"); + goto end; + } + + ret = init_gnutls_global(&mc, pubkeyfile, seckeyfile); + if (ret == -1){ + fprintf(stderr, "init_gnutls_global\n"); + goto end; + } else { + gnutls_initalized = true; + } + + uid = getuid(); + gid = getgid(); + + ret = setuid(uid); + if (ret == -1){ + perror("setuid"); + } + + setgid(gid); + if (ret == -1){ + perror("setgid"); + } + if_index = (AvahiIfIndex) if_nametoindex(interface); if(if_index == 0){ fprintf(stderr, "No such interface: \"%s\"\n", interface); - exitcode = EX_UNAVAILABLE; - goto end; - } - - if(quit_now){ - goto end; - } - - /* Re-raise priviliges */ - errno = 0; - ret = seteuid(0); - if(ret == -1){ - perror("seteuid"); - } - -#ifdef __linux__ - /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO - messages about the network interface to mess up the prompt */ - ret = klogctl(8, NULL, 5); - bool restore_loglevel = true; - if(ret == -1){ - restore_loglevel = false; - perror("klogctl"); - } -#endif /* __linux__ */ - - sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); - if(sd < 0){ - perror("socket"); - exitcode = EX_OSERR; -#ifdef __linux__ - if(restore_loglevel){ - ret = klogctl(7, NULL, 0); - if(ret == -1){ - perror("klogctl"); - } - } -#endif /* __linux__ */ - /* Lower privileges */ - errno = 0; - ret = seteuid(uid); - if(ret == -1){ - perror("seteuid"); - } - goto end; - } - strcpy(network.ifr_name, interface); - ret = ioctl(sd, SIOCGIFFLAGS, &network); - if(ret == -1){ - perror("ioctl SIOCGIFFLAGS"); -#ifdef __linux__ - if(restore_loglevel){ - ret = klogctl(7, NULL, 0); - if(ret == -1){ - perror("klogctl"); - } - } -#endif /* __linux__ */ - exitcode = EX_OSERR; - /* Lower privileges */ - errno = 0; - ret = seteuid(uid); - if(ret == -1){ - perror("seteuid"); - } - goto end; - } - if((network.ifr_flags & IFF_UP) == 0){ - network.ifr_flags |= IFF_UP; - take_down_interface = true; - ret = ioctl(sd, SIOCSIFFLAGS, &network); - if(ret == -1){ - take_down_interface = false; - perror("ioctl SIOCSIFFLAGS +IFF_UP"); - exitcode = EX_OSERR; -#ifdef __linux__ - if(restore_loglevel){ - ret = klogctl(7, NULL, 0); - if(ret == -1){ - perror("klogctl"); - } - } -#endif /* __linux__ */ - /* Lower privileges */ - errno = 0; - ret = seteuid(uid); - if(ret == -1){ - perror("seteuid"); - } - goto end; - } - } - /* sleep checking until interface is running */ - for(int i=0; i < delay * 4; i++){ + 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 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); /* Spurious warning */ ret = ioctl(sd, SIOCGIFFLAGS, &network); if(ret == -1){ perror("ioctl SIOCGIFFLAGS"); - } else if(network.ifr_flags & IFF_RUNNING){ - break; - } - struct timespec sleeptime = { .tv_nsec = 250000000 }; - ret = nanosleep(&sleeptime, NULL); - if(ret == -1 and errno != EINTR){ - perror("nanosleep"); - } - } - if(not take_down_interface){ - /* We won't need the socket anymore */ - ret = (int)TEMP_FAILURE_RETRY(close(sd)); - if(ret == -1){ - perror("close"); - } - } -#ifdef __linux__ - if(restore_loglevel){ - /* Restores kernel loglevel to default */ - ret = klogctl(7, NULL, 0); - if(ret == -1){ - perror("klogctl"); - } - } -#endif /* __linux__ */ - /* Lower privileges */ - errno = 0; - if(take_down_interface){ - /* Lower privileges */ - ret = seteuid(uid); - if(ret == -1){ - perror("seteuid"); - } - } else { - /* Lower privileges permanently */ - ret = setuid(uid); - if(ret == -1){ - perror("setuid"); - } - } - } - - if(quit_now){ - goto end; - } - - ret = init_gnutls_global(pubkey, seckey); - if(ret == -1){ - fprintf(stderr, "init_gnutls_global failed\n"); - exitcode = EX_UNAVAILABLE; - goto end; - } else { - gnutls_initialized = true; - } - - if(quit_now){ - goto end; - } - - tempdir_created = true; - if(mkdtemp(tempdir) == NULL){ - tempdir_created = false; - perror("mkdtemp"); - goto end; - } - - if(quit_now){ - goto end; - } - - if(not init_gpgme(pubkey, seckey, tempdir)){ - fprintf(stderr, "init_gpgme failed\n"); - exitcode = EX_UNAVAILABLE; - goto end; - } else { - gpgme_initialized = true; - } - - if(quit_now){ - goto end; - } - - 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 = EX_USAGE; - goto end; - } - - if(quit_now){ - goto end; - } - - uint16_t port; - errno = 0; - tmpmax = strtoimax(address+1, &tmp, 10); - if(errno != 0 or tmp == address+1 or *tmp != '\0' - or tmpmax != (uint16_t)tmpmax){ - fprintf(stderr, "Bad port number\n"); - exitcode = EX_USAGE; - goto end; - } - - if(quit_now){ - goto end; - } - - port = (uint16_t)tmpmax; - *address = '\0'; - address = connect_to; - /* Colon in address indicates IPv6 */ - int af; - if(strchr(address, ':') != NULL){ - af = AF_INET6; - } else { - af = AF_INET; - } - - if(quit_now){ - goto end; - } - - while(not quit_now){ - ret = start_mandos_communication(address, port, if_index, af); - if(quit_now or ret == 0){ - break; - } - sleep(15); - }; - - if (not quit_now){ - exitcode = EXIT_SUCCESS; - } - - goto end; - } - - if(quit_now){ - 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 = EX_UNAVAILABLE; - goto end; - } - - if(quit_now){ - goto end; - } - - /* Create the Avahi service browser */ - sb = avahi_s_service_browser_new(mc.server, if_index, - AVAHI_PROTO_UNSPEC, "_mandos._tcp", - NULL, 0, browse_callback, NULL); - if(sb == NULL){ - fprintf(stderr, "Failed to create service browser: %s\n", - avahi_strerror(avahi_server_errno(mc.server))); - exitcode = EX_UNAVAILABLE; - goto end; - } - - if(quit_now){ - goto end; - } - - /* Run the main loop */ - - if(debug){ - fprintf(stderr, "Starting Avahi loop search\n"); - } - - avahi_simple_poll_loop(mc.simple_poll); - + 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; + } + } + close(sd); + } + + 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_initialized){ - gnutls_certificate_free_credentials(mc.cred); - gnutls_global_deinit(); - gnutls_dh_params_deinit(mc.dh_params); - } - - if(gpgme_initialized){ - gpgme_release(mc.ctx); - } - - /* Take down the network interface */ - if(take_down_interface){ - /* Re-raise priviliges */ - errno = 0; - ret = seteuid(0); - if(ret == -1){ - perror("seteuid"); - } - if(geteuid() == 0){ - ret = ioctl(sd, SIOCGIFFLAGS, &network); - if(ret == -1){ - perror("ioctl SIOCGIFFLAGS"); - } else if(network.ifr_flags & IFF_UP) { - network.ifr_flags &= ~(short)IFF_UP; /* clear flag */ - ret = ioctl(sd, SIOCSIFFLAGS, &network); - if(ret == -1){ - perror("ioctl SIOCSIFFLAGS -IFF_UP"); - } - } - ret = (int)TEMP_FAILURE_RETRY(close(sd)); - if(ret == -1){ - perror("close"); - } - /* Lower privileges permanently */ - errno = 0; - ret = setuid(uid); - if(ret == -1){ - perror("setuid"); - } - } - } - - /* Removes the temp directory used by GPGME */ - if(tempdir_created){ - DIR *d; - struct dirent *direntry; - d = opendir(tempdir); - if(d == NULL){ - if(errno != ENOENT){ - perror("opendir"); - } - } else { - while(true){ - direntry = readdir(d); - if(direntry == NULL){ - break; - } - /* Skip "." and ".." */ - if(direntry->d_name[0] == '.' - and (direntry->d_name[1] == '\0' - or (direntry->d_name[1] == '.' - and direntry->d_name[2] == '\0'))){ - continue; - } - char *fullname = NULL; - ret = asprintf(&fullname, "%s/%s", tempdir, - direntry->d_name); - if(ret < 0){ - perror("asprintf"); - continue; - } - ret = remove(fullname); - if(ret == -1){ - fprintf(stderr, "remove(\"%s\"): %s\n", fullname, - strerror(errno)); - } - free(fullname); - } - closedir(d); - } - ret = rmdir(tempdir); - if(ret == -1 and errno != ENOENT){ - perror("rmdir"); - } - } - - if(quit_now){ - sigemptyset(&old_sigterm_action.sa_mask); - old_sigterm_action.sa_handler = SIG_DFL; - ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received, - &old_sigterm_action, - NULL)); - if(ret == -1){ - perror("sigaction"); - } - do { - ret = raise(signal_received); - } while(ret != 0 and errno == EINTR); - if(ret != 0){ - perror("raise"); - abort(); - } - TEMP_FAILURE_RETRY(pause()); - } - - return exitcode; + + 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); + free(pubkeyfile); + free(seckeyfile); + + if (gnutls_initalized){ + gnutls_certificate_free_credentials (mc.cred); + gnutls_global_deinit (); + } + + return exitcode; } === removed file 'plugins.d/plymouth.c' --- plugins.d/plymouth.c 2010-09-26 18:32:58 +0000 +++ plugins.d/plymouth.c 1970-01-01 00:00:00 +0000 @@ -1,431 +0,0 @@ -/* -*- coding: utf-8 -*- */ -/* - * Usplash - Read a password from usplash and output it - * - * Copyright © 2010 Teddy Hogeborn - * Copyright © 2010 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 /* asprintf(), TEMP_FAILURE_RETRY() */ -#include /* sig_atomic_t, struct sigaction, - sigemptyset(), sigaddset(), SIGINT, - SIGHUP, SIGTERM, sigaction(), - kill(), SIG_IGN */ -#include /* bool, false, true */ -#include /* open(), O_RDONLY */ -#include /* and, or, not*/ -#include /* size_t, ssize_t, pid_t, struct - dirent, waitpid() */ -#include /* waitpid() */ -#include /* NULL */ -#include /* strchr(), memcmp() */ -#include /* asprintf(), perror(), fopen(), - fscanf() */ -#include /* close(), readlink(), read(), - fork(), setsid(), chdir(), dup2(), - STDERR_FILENO, execv(), access() */ -#include /* free(), EXIT_FAILURE, realloc(), - EXIT_SUCCESS, malloc(), _exit(), - getenv() */ -#include /* scandir(), alphasort() */ -#include /* intmax_t, strtoumax(), SCNuMAX */ -#include /* struct stat, lstat() */ -#include /* EX_OSERR, EX_UNAVAILABLE */ -#include /* error() */ -#include /* TEMP_FAILURE_RETRY */ -#include /* argz_count(), argz_extract() */ - -sig_atomic_t interrupted_by_signal = 0; -const char plymouth_pid[] = "/dev/.initramfs/plymouth.pid"; -const char plymouth_path[] = "/bin/plymouth"; -const char plymouthd_path[] = "/sbin/plymouthd"; -const char *plymouthd_default_argv[] = {"/sbin/plymouthd", - "--mode=boot", - "--attach-to-session", - "--pid-file=" - "/dev/.initramfs/" - "plymouth.pid", - NULL }; - -static void termination_handler(__attribute__((unused))int signum){ - if(interrupted_by_signal){ - return; - } - interrupted_by_signal = 1; -} - -/* Create prompt string */ -char *makeprompt(void){ - int ret = 0; - char *prompt; - const char *const cryptsource = getenv("cryptsource"); - const char *const crypttarget = getenv("crypttarget"); - const char prompt_start[] = "Enter passphrase to unlock the disk"; - - if(cryptsource == NULL){ - if(crypttarget == NULL){ - ret = asprintf(&prompt, "%s: ", prompt_start); - } else { - ret = asprintf(&prompt, "%s (%s): ", prompt_start, - crypttarget); - } - } else { - if(crypttarget == NULL){ - ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource); - } else { - ret = asprintf(&prompt, "%s %s (%s): ", prompt_start, - cryptsource, crypttarget); - } - } - if(ret == -1){ - return NULL; - } - return prompt; -} - -void kill_and_wait(pid_t pid){ - TEMP_FAILURE_RETRY(kill(pid, SIGTERM)); - TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0)); -} - -bool become_a_daemon(void){ - int ret = setuid(geteuid()); - if(ret == -1){ - error(0, errno, "setuid"); - } - - setsid(); - ret = chdir("/"); - if(ret == -1){ - error(0, errno, "chdir"); - return false; - } - ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */ - if(ret == -1){ - error(0, errno, "dup2"); - return false; - } - return true; -} - -bool exec_and_wait(pid_t *pid_return, const char *path, - const char **argv, bool interruptable, - bool daemonize){ - int status; - int ret; - pid_t pid; - pid = fork(); - if(pid == -1){ - error(0, errno, "fork"); - return false; - } - if(pid == 0){ - /* Child */ - if(daemonize){ - if(not become_a_daemon()){ - _exit(EX_OSERR); - } - } - - char **new_argv = NULL; - char *tmp; - int i = 0; - for (; argv[i]!=(char *)NULL; i++){ - tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1)); - if (tmp == NULL){ - error(0, errno, "realloc"); - free(new_argv); - _exit(EX_OSERR); - } - new_argv = (char **)tmp; - new_argv[i] = strdup(argv[i]); - } - new_argv[i] = (char *) NULL; - - execv(path, (char *const *)new_argv); - error(0, errno, "execv"); - _exit(EXIT_FAILURE); - } - if(pid_return != NULL){ - *pid_return = pid; - } - do { - ret = waitpid(pid, &status, 0); - } while(ret == -1 and errno == EINTR - and ((not interrupted_by_signal) - or (not interruptable))); - if(interrupted_by_signal and interruptable){ - return false; - } - if(ret == -1){ - error(0, errno, "waitpid"); - return false; - } - if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){ - return true; - } - return false; -} - -int is_plymouth(const struct dirent *proc_entry){ - int ret; - { - uintmax_t maxvalue; - char *tmp; - errno = 0; - maxvalue = strtoumax(proc_entry->d_name, &tmp, 10); - - if(errno != 0 or *tmp != '\0' - or maxvalue != (uintmax_t)((pid_t)maxvalue)){ - return 0; - } - } - char exe_target[sizeof(plymouth_path)]; - char *exe_link; - ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name); - if(ret == -1){ - error(0, errno, "asprintf"); - return 0; - } - - struct stat exe_stat; - ret = lstat(exe_link, &exe_stat); - if(ret == -1){ - free(exe_link); - if(errno != ENOENT){ - error(0, errno, "lstat"); - } - return 0; - } - - if(not S_ISLNK(exe_stat.st_mode) - or exe_stat.st_uid != 0 - or exe_stat.st_gid != 0){ - free(exe_link); - return 0; - } - - ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target)); - free(exe_link); - if((sret != (ssize_t)sizeof(plymouth_path)-1) or - (memcmp(plymouth_path, exe_target, - sizeof(plymouth_path)-1) != 0)){ - return 0; - } - return 1; -} - -pid_t get_pid(void){ - int ret; - FILE *pidfile = fopen(plymouth_pid, "r"); - uintmax_t maxvalue = 0; - if(pidfile != NULL){ - ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue); - if(ret != 1){ - maxvalue = 0; - } - fclose(pidfile); - } - if(maxvalue == 0){ - struct dirent **direntries; - ret = scandir("/proc", &direntries, is_plymouth, alphasort); - sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue); - } - pid_t pid; - pid = (pid_t)maxvalue; - if((uintmax_t)pid == maxvalue){ - return pid; - } - - return 0; -} - -const char **getargv(pid_t pid){ - int cl_fd; - char *cmdline_filename; - ssize_t sret; - int ret; - - ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline", - (uintmax_t)pid); - if(ret == -1){ - error(0, errno, "asprintf"); - return NULL; - } - - /* Open /proc//cmdline */ - cl_fd = open(cmdline_filename, O_RDONLY); - free(cmdline_filename); - if(cl_fd == -1){ - error(0, errno, "open"); - return NULL; - } - - size_t cmdline_allocated = 0; - size_t cmdline_len = 0; - char *cmdline = NULL; - char *tmp; - const size_t blocksize = 1024; - do { - /* Allocate more space? */ - if(cmdline_len + blocksize > cmdline_allocated){ - tmp = realloc(cmdline, cmdline_allocated + blocksize); - if(tmp == NULL){ - error(0, errno, "realloc"); - free(cmdline); - close(cl_fd); - return NULL; - } - cmdline = tmp; - cmdline_allocated += blocksize; - } - - /* Read data */ - sret = read(cl_fd, cmdline + cmdline_len, - cmdline_allocated - cmdline_len); - if(sret == -1){ - error(0, errno, "read"); - free(cmdline); - close(cl_fd); - return NULL; - } - cmdline_len += (size_t)sret; - } while(sret != 0); - ret = close(cl_fd); - if(ret == -1){ - error(0, errno, "close"); - free(cmdline); - return NULL; - } - - /* we got cmdline and cmdline_len, ignore rest... */ - char **argv = malloc((argz_count(cmdline, cmdline_len) + 1) - * sizeof(char *)); /* Get number of args */ - if(argv == NULL){ - error(0, errno, "argv = malloc()"); - free(cmdline); - return NULL; - } - argz_extract(cmdline, cmdline_len, argv); /* Create argv */ - return (const char **)argv; -} - -int main(__attribute__((unused))int argc, - __attribute__((unused))char **argv){ - char *prompt; - char *prompt_arg; - pid_t plymouth_command_pid; - int ret; - bool bret; - - /* test -x /bin/plymouth */ - ret = access(plymouth_path, X_OK); - if(ret == -1){ - /* Plymouth is probably not installed. Don't print an error - message, just exit. */ - exit(EX_UNAVAILABLE); - } - - { /* Add signal handlers */ - struct sigaction old_action, - new_action = { .sa_handler = termination_handler, - .sa_flags = 0 }; - sigemptyset(&new_action.sa_mask); - for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 }; - *sig != 0; sig++){ - ret = sigaddset(&new_action.sa_mask, *sig); - if(ret == -1){ - error(EX_OSERR, errno, "sigaddset"); - } - ret = sigaction(*sig, NULL, &old_action); - if(ret == -1){ - error(EX_OSERR, errno, "sigaction"); - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(*sig, &new_action, NULL); - if(ret == -1){ - error(EX_OSERR, errno, "sigaction"); - } - } - } - } - - /* plymouth --ping */ - bret = exec_and_wait(&plymouth_command_pid, plymouth_path, - (const char *[]) - { plymouth_path, "--ping", NULL }, - true, false); - if(not bret){ - if(interrupted_by_signal){ - kill_and_wait(plymouth_command_pid); - exit(EXIT_FAILURE); - } - /* Plymouth is probably not running. Don't print an error - message, just exit. */ - exit(EX_UNAVAILABLE); - } - - prompt = makeprompt(); - ret = asprintf(&prompt_arg, "--prompt=%s", prompt); - free(prompt); - if(ret == -1){ - error(EX_OSERR, errno, "asprintf"); - } - - /* plymouth ask-for-password --prompt="$prompt" */ - bret = exec_and_wait(&plymouth_command_pid, - plymouth_path, (const char *[]) - { plymouth_path, "ask-for-password", - prompt_arg, NULL }, - true, false); - free(prompt_arg); - if(bret){ - exit(EXIT_SUCCESS); - } - if(not interrupted_by_signal){ - /* exec_and_wait failed for some other reason */ - exit(EXIT_FAILURE); - } - kill_and_wait(plymouth_command_pid); - - const char **plymouthd_argv; - pid_t pid = get_pid(); - if(pid == 0){ - error(0, 0, "plymouthd pid not found"); - plymouthd_argv = plymouthd_default_argv; - } else { - plymouthd_argv = getargv(pid); - } - - bret = exec_and_wait(NULL, plymouth_path, (const char *[]) - { plymouth_path, "quit", NULL }, - false, false); - if(not bret){ - exit(EXIT_FAILURE); - } - bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv, - false, true); - if(not bret){ - exit(EXIT_FAILURE); - } - exec_and_wait(NULL, plymouth_path, (const char *[]) - { plymouth_path, "show-splash", NULL }, - false, false); - exit(EXIT_FAILURE); -} === removed file 'plugins.d/plymouth.xml' --- plugins.d/plymouth.xml 2010-09-26 18:32:58 +0000 +++ plugins.d/plymouth.xml 1970-01-01 00:00:00 +0000 @@ -1,278 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2010 - Teddy Hogeborn - Björn Påhlsson - - -
- - - &COMMANDNAME; - 8mandos - - - - &COMMANDNAME; - Mandos plugin to use plymouth to get a - password. - - - - - &COMMANDNAME; - - - - - DESCRIPTION - - This program prompts for a password using - plymouth8 - and outputs any given password to standard - output. If no plymouth8 - process can be found, this program will immediately exit with an - exit code indicating failure. - - - This program 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. - - - If this program is killed (presumably by - plugin-runner - 8mandos because some other - plugin provided the password), it cannot tell - plymouth8 - to abort requesting a password, because - plymouth - 8 does not support this. - Therefore, this program will then kill the - running plymouth - 8 process and start a - new one using the same command line - arguments as the old one was using. - - - - - OPTIONS - - This program takes no options. - - - - - 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 was interrupted or 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. - - - - - - - - FILES - - - /bin/plymouth - - - This is the command run to retrieve a password from - plymouth - 8. - - - - - /proc - - - To find the running plymouth8 - , this directory will be searched for - numeric entries which will be assumed to be directories. - In all those directories, the exe and - cmdline entries will be used to - determine the name of the running binary, effective user - and group ID, and the command line - arguments. See proc5 - . - - - - - /sbin/plymouthd - - - This is the name of the binary which will be searched for - in the process list. See plymouth8 - . - - - - - - - - BUGS - - Killing the plymouth8 - daemon and starting a new one is ugly, but necessary as long as - it does not support aborting a password request. - - - - - EXAMPLE - - Note that normally, this program will not be invoked directly, - but instead started by the Mandos plugin-runner8mandos - . - - - - This program takes no options. - - - &COMMANDNAME; - - - - - - SECURITY - - If this program is killed by a signal, it will kill the process - ID which at the start of this program was - determined to run plymouth8 - as root (see also ). There is a very - slight risk that, in the time between those events, that process - ID was freed and then taken up by another - process; the wrong process would then be killed. Now, this - program can only be killed by the user who started it; see - plugin-runner - 8mandos. This program - should therefore be started by a completely separate - non-privileged user, and no other programs should be allowed to - run as that special user. This means that it is not recommended - to use the user "nobody" to start this program, as other - possibly less trusted programs could be running as "nobody", and - they would then be able to kill this program, triggering the - killing of the process ID which may or may not - be plymouth - 8. - - - The only other 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. - - - - - SEE ALSO - - crypttab - 5, - plugin-runner - 8mandos, - proc - 5, - plymouth - 8 - - -
- - - - - === removed file 'plugins.d/splashy.c' --- plugins.d/splashy.c 2010-09-26 18:32:58 +0000 +++ plugins.d/splashy.c 1970-01-01 00:00:00 +0000 @@ -1,442 +0,0 @@ -/* -*- coding: utf-8 -*- */ -/* - * Splashy - Read a password from splashy and output it - * - * Copyright © 2008-2010 Teddy Hogeborn - * Copyright © 2008-2010 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(), asprintf() */ -#include /* sig_atomic_t, struct sigaction, - sigemptyset(), sigaddset(), SIGINT, - SIGHUP, SIGTERM, sigaction, - SIG_IGN, kill(), SIGKILL */ -#include /* NULL */ -#include /* getenv() */ -#include /* asprintf() */ -#include /* EXIT_FAILURE, free(), - EXIT_SUCCESS */ -#include /* pid_t, DIR, struct dirent, - ssize_t */ -#include /* opendir(), readdir(), closedir() */ -#include /* intmax_t, strtoimax() */ -#include /* struct stat, lstat(), S_ISLNK */ -#include /* not, or, and */ -#include /* readlink(), fork(), execl(), - sleep(), dup2() STDERR_FILENO, - STDOUT_FILENO, _exit(), - pause() */ -#include /* memcmp() */ -#include /* errno, EACCES, ENOTDIR, ELOOP, - ENOENT, ENAMETOOLONG, EMFILE, - ENFILE, ENOMEM, ENOEXEC, EINVAL, - E2BIG, EFAULT, EIO, ETXTBSY, - EISDIR, ELIBBAD, EPERM, EINTR, - ECHILD */ -#include /* error() */ -#include /* waitpid(), WIFEXITED(), - WEXITSTATUS() */ -#include /* EX_OSERR, EX_OSFILE, - EX_UNAVAILABLE */ - -sig_atomic_t interrupted_by_signal = 0; -int signal_received; - -static void termination_handler(int signum){ - if(interrupted_by_signal){ - return; - } - interrupted_by_signal = 1; - signal_received = signum; -} - -int main(__attribute__((unused))int argc, - __attribute__((unused))char **argv){ - int ret = 0; - char *prompt = NULL; - DIR *proc_dir = NULL; - pid_t splashy_pid = 0; - pid_t splashy_command_pid = 0; - int exitstatus = EXIT_FAILURE; - - /* Create prompt string */ - { - const char *const cryptsource = getenv("cryptsource"); - const char *const crypttarget = getenv("crypttarget"); - const char *const prompt_start = "getpass " - "Enter passphrase to unlock the disk"; - - if(cryptsource == NULL){ - if(crypttarget == NULL){ - ret = asprintf(&prompt, "%s: ", prompt_start); - } else { - ret = asprintf(&prompt, "%s (%s): ", prompt_start, - crypttarget); - } - } else { - if(crypttarget == NULL){ - ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource); - } else { - ret = asprintf(&prompt, "%s %s (%s): ", prompt_start, - cryptsource, crypttarget); - } - } - if(ret == -1){ - prompt = NULL; - exitstatus = EX_OSERR; - goto failure; - } - } - - /* Find splashy process */ - { - const char splashy_name[] = "/sbin/splashy"; - proc_dir = opendir("/proc"); - if(proc_dir == NULL){ - int e = errno; - error(0, errno, "opendir"); - switch(e){ - case EACCES: - case ENOTDIR: - case ELOOP: - case ENOENT: - default: - exitstatus = EX_OSFILE; - break; - case ENAMETOOLONG: - case EMFILE: - case ENFILE: - case ENOMEM: - exitstatus = EX_OSERR; - break; - } - goto failure; - } - for(struct dirent *proc_ent = readdir(proc_dir); - proc_ent != NULL; - proc_ent = readdir(proc_dir)){ - pid_t pid; - { - intmax_t tmpmax; - char *tmp; - errno = 0; - tmpmax = strtoimax(proc_ent->d_name, &tmp, 10); - if(errno != 0 or tmp == proc_ent->d_name or *tmp != '\0' - or tmpmax != (pid_t)tmpmax){ - /* Not a process */ - continue; - } - pid = (pid_t)tmpmax; - } - /* Find the executable name by doing readlink() on the - /proc//exe link */ - char exe_target[sizeof(splashy_name)]; - ssize_t sret; - { - char *exe_link; - ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name); - if(ret == -1){ - error(0, errno, "asprintf"); - exitstatus = EX_OSERR; - goto failure; - } - - /* Check that it refers to a symlink owned by root:root */ - struct stat exe_stat; - ret = lstat(exe_link, &exe_stat); - if(ret == -1){ - if(errno == ENOENT){ - free(exe_link); - continue; - } - int e = errno; - error(0, errno, "lstat"); - free(exe_link); - switch(e){ - case EACCES: - case ENOTDIR: - case ELOOP: - default: - exitstatus = EX_OSFILE; - break; - case ENAMETOOLONG: - exitstatus = EX_OSERR; - break; - } - goto failure; - } - if(not S_ISLNK(exe_stat.st_mode) - or exe_stat.st_uid != 0 - or exe_stat.st_gid != 0){ - free(exe_link); - continue; - } - - sret = readlink(exe_link, exe_target, sizeof(exe_target)); - free(exe_link); - } - if((sret == ((ssize_t)sizeof(exe_target)-1)) - and (memcmp(splashy_name, exe_target, - sizeof(exe_target)-1) == 0)){ - splashy_pid = pid; - break; - } - } - closedir(proc_dir); - proc_dir = NULL; - } - if(splashy_pid == 0){ - exitstatus = EX_UNAVAILABLE; - goto failure; - } - - /* Set up the signal handler */ - { - struct sigaction old_action, - new_action = { .sa_handler = termination_handler, - .sa_flags = 0 }; - sigemptyset(&new_action.sa_mask); - ret = sigaddset(&new_action.sa_mask, SIGINT); - if(ret == -1){ - error(0, errno, "sigaddset"); - exitstatus = EX_OSERR; - goto failure; - } - ret = sigaddset(&new_action.sa_mask, SIGHUP); - if(ret == -1){ - error(0, errno, "sigaddset"); - exitstatus = EX_OSERR; - goto failure; - } - ret = sigaddset(&new_action.sa_mask, SIGTERM); - if(ret == -1){ - error(0, errno, "sigaddset"); - exitstatus = EX_OSERR; - goto failure; - } - ret = sigaction(SIGINT, NULL, &old_action); - if(ret == -1){ - error(0, errno, "sigaction"); - exitstatus = EX_OSERR; - goto failure; - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGINT, &new_action, NULL); - if(ret == -1){ - error(0, errno, "sigaction"); - exitstatus = EX_OSERR; - goto failure; - } - } - ret = sigaction(SIGHUP, NULL, &old_action); - if(ret == -1){ - error(0, errno, "sigaction"); - exitstatus = EX_OSERR; - goto failure; - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGHUP, &new_action, NULL); - if(ret == -1){ - error(0, errno, "sigaction"); - exitstatus = EX_OSERR; - goto failure; - } - } - ret = sigaction(SIGTERM, NULL, &old_action); - if(ret == -1){ - error(0, errno, "sigaction"); - exitstatus = EX_OSERR; - goto failure; - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGTERM, &new_action, NULL); - if(ret == -1){ - error(0, errno, "sigaction"); - exitstatus = EX_OSERR; - goto failure; - } - } - } - - if(interrupted_by_signal){ - goto failure; - } - - /* Fork off the splashy command to prompt for password */ - splashy_command_pid = fork(); - if(splashy_command_pid != 0 and interrupted_by_signal){ - goto failure; - } - if(splashy_command_pid == -1){ - error(0, errno, "fork"); - exitstatus = EX_OSERR; - goto failure; - } - /* Child */ - if(splashy_command_pid == 0){ - if(not interrupted_by_signal){ - const char splashy_command[] = "/sbin/splashy_update"; - execl(splashy_command, splashy_command, prompt, (char *)NULL); - int e = errno; - error(0, errno, "execl"); - switch(e){ - case EACCES: - case ENOENT: - case ENOEXEC: - case EINVAL: - _exit(EX_UNAVAILABLE); - case ENAMETOOLONG: - case E2BIG: - case ENOMEM: - case EFAULT: - case EIO: - case EMFILE: - case ENFILE: - case ETXTBSY: - default: - _exit(EX_OSERR); - case ENOTDIR: - case ELOOP: - case EISDIR: - case ELIBBAD: - case EPERM: - _exit(EX_OSFILE); - } - } - free(prompt); - _exit(EXIT_FAILURE); - } - - /* Parent */ - free(prompt); - prompt = NULL; - - if(interrupted_by_signal){ - goto failure; - } - - /* Wait for command to complete */ - { - int status; - do { - ret = waitpid(splashy_command_pid, &status, 0); - } while(ret == -1 and errno == EINTR - and not interrupted_by_signal); - if(interrupted_by_signal){ - goto failure; - } - if(ret == -1){ - error(0, errno, "waitpid"); - if(errno == ECHILD){ - splashy_command_pid = 0; - } - } else { - /* The child process has exited */ - splashy_command_pid = 0; - if(WIFEXITED(status) and WEXITSTATUS(status) == 0){ - return EXIT_SUCCESS; - } - } - } - - failure: - - free(prompt); - - if(proc_dir != NULL){ - TEMP_FAILURE_RETRY(closedir(proc_dir)); - } - - if(splashy_command_pid != 0){ - TEMP_FAILURE_RETRY(kill(splashy_command_pid, SIGTERM)); - - TEMP_FAILURE_RETRY(kill(splashy_pid, SIGTERM)); - sleep(2); - while(TEMP_FAILURE_RETRY(kill(splashy_pid, 0)) == 0){ - TEMP_FAILURE_RETRY(kill(splashy_pid, SIGKILL)); - sleep(1); - } - pid_t new_splashy_pid = (pid_t)TEMP_FAILURE_RETRY(fork()); - if(new_splashy_pid == 0){ - /* Child; will become new splashy process */ - - /* Make the effective user ID (root) the only user ID instead of - the real user ID (_mandos) */ - ret = setuid(geteuid()); - if(ret == -1){ - error(0, errno, "setuid"); - } - - setsid(); - ret = chdir("/"); - if(ret == -1){ - error(0, errno, "chdir"); - } -/* if(fork() != 0){ */ -/* _exit(EXIT_SUCCESS); */ -/* } */ - ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace stdout */ - if(ret == -1){ - error(0, errno, "dup2"); - _exit(EX_OSERR); - } - - execl("/sbin/splashy", "/sbin/splashy", "boot", (char *)NULL); - { - int e = errno; - error(0, errno, "execl"); - switch(e){ - case EACCES: - case ENOENT: - case ENOEXEC: - default: - _exit(EX_UNAVAILABLE); - case ENAMETOOLONG: - case E2BIG: - case ENOMEM: - _exit(EX_OSERR); - case ENOTDIR: - case ELOOP: - _exit(EX_OSFILE); - } - } - } - } - - if(interrupted_by_signal){ - struct sigaction signal_action; - sigemptyset(&signal_action.sa_mask); - signal_action.sa_handler = SIG_DFL; - ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received, - &signal_action, NULL)); - if(ret == -1){ - error(0, errno, "sigaction"); - } - do { - ret = raise(signal_received); - } while(ret != 0 and errno == EINTR); - if(ret != 0){ - error(0, errno, "raise"); - abort(); - } - TEMP_FAILURE_RETRY(pause()); - } - - return exitstatus; -} === removed file 'plugins.d/splashy.xml' --- plugins.d/splashy.xml 2009-01-04 21:54:55 +0000 +++ plugins.d/splashy.xml 1970-01-01 00:00:00 +0000 @@ -1,283 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2008 - 2009 - Teddy Hogeborn - Björn Påhlsson - - -
- - - &COMMANDNAME; - 8mandos - - - - &COMMANDNAME; - Mandos plugin to use splashy to get a - password. - - - - - &COMMANDNAME; - - - - - DESCRIPTION - - This program prompts for a password using - splashy_update - 8 and outputs any given - password to standard output. If no splashy8 - process can be found, this program will immediately exit with an - exit code indicating failure. - - - This program 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. - - - If this program is killed (presumably by - plugin-runner - 8mandos because some other - plugin provided the password), it cannot tell - splashy8 - to abort requesting a password, because - splashy - 8 does not support this. - Therefore, this program will then kill the - running splashy - 8 process and start a - new one, using boot as the only argument. - - - - - OPTIONS - - This program takes no options. - - - - - 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 was interrupted or 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. - - - - - - - - FILES - - - /sbin/splashy_update - - - This is the command run to retrieve a password from - splashy - 8. See - splashy_update8 - . - - - - - /proc - - - To find the running splashy8 - , this directory will be searched for - numeric entries which will be assumed to be directories. - In all those directories, the exe - entry will be used to determine the name of the running - binary and the effective user and group - ID of the process. See - proc5. - - - - - /sbin/splashy - - - This is the name of the binary which will be searched for - in the process list. See splashy8 - . - - - - - - - - BUGS - - Killing splashy - 8 and starting a new one - is ugly, but necessary as long as it does not support aborting a - password request. - - - - - EXAMPLE - - Note that normally, this program will not be invoked directly, - but instead started by the Mandos plugin-runner8mandos - . - - - - This program takes no options. - - - &COMMANDNAME; - - - - - - SECURITY - - If this program is killed by a signal, it will kill the process - ID which at the start of this program was - determined to run splashy8 - as root (see also ). There is a very - slight risk that, in the time between those events, that process - ID was freed and then taken up by another - process; the wrong process would then be killed. Now, this - program can only be killed by the user who started it; see - plugin-runner - 8mandos. This program - should therefore be started by a completely separate - non-privileged user, and no other programs should be allowed to - run as that special user. This means that it is not recommended - to use the user "nobody" to start this program, as other - possibly less trusted programs could be running as "nobody", and - they would then be able to kill this program, triggering the - killing of the process ID which may or may not - be splashy - 8. - - - The only other 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. - - - - - SEE ALSO - - crypttab - 5, - plugin-runner - 8mandos, - proc - 5, - splashy - 8, - splashy_update - 8 - - -
- - - - - === removed file 'plugins.d/usplash.c' --- plugins.d/usplash.c 2010-09-26 18:32:58 +0000 +++ plugins.d/usplash.c 1970-01-01 00:00:00 +0000 @@ -1,658 +0,0 @@ -/* -*- coding: utf-8 -*- */ -/* - * Usplash - Read a password from usplash and output it - * - * Copyright © 2008-2010 Teddy Hogeborn - * Copyright © 2008-2010 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 /* asprintf(), TEMP_FAILURE_RETRY() */ -#include /* sig_atomic_t, struct sigaction, - sigemptyset(), sigaddset(), SIGINT, - SIGHUP, SIGTERM, sigaction(), - SIG_IGN, kill(), SIGKILL */ -#include /* bool, false, true */ -#include /* open(), O_WRONLY, O_RDONLY */ -#include /* and, or, not*/ -#include /* errno, EINTR */ -#include -#include /* size_t, ssize_t, pid_t, DIR, struct - dirent */ -#include /* NULL */ -#include /* strlen(), memcmp() */ -#include /* asprintf()*/ -#include /* close(), write(), readlink(), - read(), STDOUT_FILENO, sleep(), - fork(), setuid(), geteuid(), - setsid(), chdir(), dup2(), - STDERR_FILENO, execv() */ -#include /* free(), EXIT_FAILURE, realloc(), - EXIT_SUCCESS, malloc(), _exit(), - getenv() */ -#include /* opendir(), readdir(), closedir() */ -#include /* intmax_t, strtoimax() */ -#include /* struct stat, lstat(), S_ISLNK */ -#include /* EX_OSERR, EX_UNAVAILABLE */ -#include /* argz_count(), argz_extract() */ - -sig_atomic_t interrupted_by_signal = 0; -int signal_received; -const char usplash_name[] = "/sbin/usplash"; - -static void termination_handler(int signum){ - if(interrupted_by_signal){ - return; - } - interrupted_by_signal = 1; - signal_received = signum; -} - -static bool usplash_write(int *fifo_fd_r, - const char *cmd, const char *arg){ - /* - * usplash_write(&fd, "TIMEOUT", "15") will write "TIMEOUT 15\0" - * usplash_write(&fd, "PULSATE", NULL) will write "PULSATE\0" - * SEE ALSO - * usplash_write(8) - */ - int ret; - if(*fifo_fd_r == -1){ - ret = open("/dev/.initramfs/usplash_fifo", O_WRONLY); - if(ret == -1){ - return false; - } - *fifo_fd_r = ret; - } - - const char *cmd_line; - size_t cmd_line_len; - char *cmd_line_alloc = NULL; - if(arg == NULL){ - cmd_line = cmd; - cmd_line_len = strlen(cmd) + 1; - } else { - do { - ret = asprintf(&cmd_line_alloc, "%s %s", cmd, arg); - if(ret == -1){ - int e = errno; - TEMP_FAILURE_RETRY(close(*fifo_fd_r)); - errno = e; - return false; - } - } while(ret == -1); - cmd_line = cmd_line_alloc; - cmd_line_len = (size_t)ret + 1; - } - - size_t written = 0; - ssize_t sret = 0; - while(written < cmd_line_len){ - sret = write(*fifo_fd_r, cmd_line + written, - cmd_line_len - written); - if(sret == -1){ - int e = errno; - TEMP_FAILURE_RETRY(close(*fifo_fd_r)); - free(cmd_line_alloc); - errno = e; - return false; - } - written += (size_t)sret; - } - free(cmd_line_alloc); - - return true; -} - -/* Create prompt string */ -char *makeprompt(void){ - int ret = 0; - char *prompt; - const char *const cryptsource = getenv("cryptsource"); - const char *const crypttarget = getenv("crypttarget"); - const char prompt_start[] = "Enter passphrase to unlock the disk"; - - if(cryptsource == NULL){ - if(crypttarget == NULL){ - ret = asprintf(&prompt, "%s: ", prompt_start); - } else { - ret = asprintf(&prompt, "%s (%s): ", prompt_start, - crypttarget); - } - } else { - if(crypttarget == NULL){ - ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource); - } else { - ret = asprintf(&prompt, "%s %s (%s): ", prompt_start, - cryptsource, crypttarget); - } - } - if(ret == -1){ - return NULL; - } - return prompt; -} - -pid_t find_usplash(char **cmdline_r, size_t *cmdline_len_r){ - int ret = 0; - ssize_t sret = 0; - char *cmdline = NULL; - size_t cmdline_len = 0; - DIR *proc_dir = opendir("/proc"); - if(proc_dir == NULL){ - error(0, errno, "opendir"); - return -1; - } - errno = 0; - for(struct dirent *proc_ent = readdir(proc_dir); - proc_ent != NULL; - proc_ent = readdir(proc_dir)){ - pid_t pid; - { - intmax_t tmpmax; - char *tmp; - tmpmax = strtoimax(proc_ent->d_name, &tmp, 10); - if(errno != 0 or tmp == proc_ent->d_name or *tmp != '\0' - or tmpmax != (pid_t)tmpmax){ - /* Not a process */ - errno = 0; - continue; - } - pid = (pid_t)tmpmax; - } - /* Find the executable name by doing readlink() on the - /proc//exe link */ - char exe_target[sizeof(usplash_name)]; - { - /* create file name string */ - char *exe_link; - ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name); - if(ret == -1){ - error(0, errno, "asprintf"); - goto fail_find_usplash; - } - - /* Check that it refers to a symlink owned by root:root */ - struct stat exe_stat; - ret = lstat(exe_link, &exe_stat); - if(ret == -1){ - if(errno == ENOENT){ - free(exe_link); - continue; - } - error(0, errno, "lstat"); - free(exe_link); - goto fail_find_usplash; - } - if(not S_ISLNK(exe_stat.st_mode) - or exe_stat.st_uid != 0 - or exe_stat.st_gid != 0){ - free(exe_link); - continue; - } - - sret = readlink(exe_link, exe_target, sizeof(exe_target)); - free(exe_link); - } - /* Compare executable name */ - if((sret != ((ssize_t)sizeof(exe_target)-1)) - or (memcmp(usplash_name, exe_target, - sizeof(exe_target)-1) != 0)){ - /* Not it */ - continue; - } - /* Found usplash */ - /* Read and save the command line of usplash in "cmdline" */ - { - /* Open /proc//cmdline */ - int cl_fd; - { - char *cmdline_filename; - ret = asprintf(&cmdline_filename, "/proc/%s/cmdline", - proc_ent->d_name); - if(ret == -1){ - error(0, errno, "asprintf"); - goto fail_find_usplash; - } - cl_fd = open(cmdline_filename, O_RDONLY); - free(cmdline_filename); - if(cl_fd == -1){ - error(0, errno, "open"); - goto fail_find_usplash; - } - } - size_t cmdline_allocated = 0; - char *tmp; - const size_t blocksize = 1024; - do { - /* Allocate more space? */ - if(cmdline_len + blocksize > cmdline_allocated){ - tmp = realloc(cmdline, cmdline_allocated + blocksize); - if(tmp == NULL){ - error(0, errno, "realloc"); - close(cl_fd); - goto fail_find_usplash; - } - cmdline = tmp; - cmdline_allocated += blocksize; - } - /* Read data */ - sret = read(cl_fd, cmdline + cmdline_len, - cmdline_allocated - cmdline_len); - if(sret == -1){ - error(0, errno, "read"); - close(cl_fd); - goto fail_find_usplash; - } - cmdline_len += (size_t)sret; - } while(sret != 0); - ret = close(cl_fd); - if(ret == -1){ - error(0, errno, "close"); - goto fail_find_usplash; - } - } - /* Close directory */ - ret = closedir(proc_dir); - if(ret == -1){ - error(0, errno, "closedir"); - goto fail_find_usplash; - } - /* Success */ - *cmdline_r = cmdline; - *cmdline_len_r = cmdline_len; - return pid; - } - - fail_find_usplash: - - free(cmdline); - if(proc_dir != NULL){ - int e = errno; - closedir(proc_dir); - errno = e; - } - return 0; -} - -int main(__attribute__((unused))int argc, - __attribute__((unused))char **argv){ - int ret = 0; - ssize_t sret; - int fifo_fd = -1; - int outfifo_fd = -1; - char *buf = NULL; - size_t buf_len = 0; - pid_t usplash_pid = -1; - bool usplash_accessed = false; - int status = EXIT_FAILURE; /* Default failure exit status */ - - char *prompt = makeprompt(); - if(prompt == NULL){ - status = EX_OSERR; - goto failure; - } - - /* Find usplash process */ - char *cmdline = NULL; - size_t cmdline_len = 0; - usplash_pid = find_usplash(&cmdline, &cmdline_len); - if(usplash_pid == 0){ - status = EX_UNAVAILABLE; - goto failure; - } - - /* Set up the signal handler */ - { - struct sigaction old_action, - new_action = { .sa_handler = termination_handler, - .sa_flags = 0 }; - sigemptyset(&new_action.sa_mask); - ret = sigaddset(&new_action.sa_mask, SIGINT); - if(ret == -1){ - error(0, errno, "sigaddset"); - status = EX_OSERR; - goto failure; - } - ret = sigaddset(&new_action.sa_mask, SIGHUP); - if(ret == -1){ - error(0, errno, "sigaddset"); - status = EX_OSERR; - goto failure; - } - ret = sigaddset(&new_action.sa_mask, SIGTERM); - if(ret == -1){ - error(0, errno, "sigaddset"); - status = EX_OSERR; - goto failure; - } - ret = sigaction(SIGINT, NULL, &old_action); - if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "sigaction"); - status = EX_OSERR; - } - goto failure; - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGINT, &new_action, NULL); - if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "sigaction"); - status = EX_OSERR; - } - goto failure; - } - } - ret = sigaction(SIGHUP, NULL, &old_action); - if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "sigaction"); - status = EX_OSERR; - } - goto failure; - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGHUP, &new_action, NULL); - if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "sigaction"); - status = EX_OSERR; - } - goto failure; - } - } - ret = sigaction(SIGTERM, NULL, &old_action); - if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "sigaction"); - status = EX_OSERR; - } - goto failure; - } - if(old_action.sa_handler != SIG_IGN){ - ret = sigaction(SIGTERM, &new_action, NULL); - if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "sigaction"); - status = EX_OSERR; - } - goto failure; - } - } - } - - usplash_accessed = true; - /* Write command to FIFO */ - if(not usplash_write(&fifo_fd, "TIMEOUT", "0")){ - if(errno != EINTR){ - error(0, errno, "usplash_write"); - status = EX_OSERR; - } - goto failure; - } - - if(interrupted_by_signal){ - goto failure; - } - - if(not usplash_write(&fifo_fd, "INPUTQUIET", prompt)){ - if(errno != EINTR){ - error(0, errno, "usplash_write"); - status = EX_OSERR; - } - goto failure; - } - - if(interrupted_by_signal){ - goto failure; - } - - free(prompt); - prompt = NULL; - - /* Read reply from usplash */ - /* Open FIFO */ - outfifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY); - if(outfifo_fd == -1){ - if(errno != EINTR){ - error(0, errno, "open"); - status = EX_OSERR; - } - goto failure; - } - - if(interrupted_by_signal){ - goto failure; - } - - /* Read from FIFO */ - size_t buf_allocated = 0; - const size_t blocksize = 1024; - do { - /* Allocate more space */ - if(buf_len + blocksize > buf_allocated){ - char *tmp = realloc(buf, buf_allocated + blocksize); - if(tmp == NULL){ - if(errno != EINTR){ - error(0, errno, "realloc"); - status = EX_OSERR; - } - goto failure; - } - buf = tmp; - buf_allocated += blocksize; - } - sret = read(outfifo_fd, buf + buf_len, - buf_allocated - buf_len); - if(sret == -1){ - if(errno != EINTR){ - error(0, errno, "read"); - status = EX_OSERR; - } - TEMP_FAILURE_RETRY(close(outfifo_fd)); - goto failure; - } - if(interrupted_by_signal){ - break; - } - - buf_len += (size_t)sret; - } while(sret != 0); - ret = close(outfifo_fd); - if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "close"); - status = EX_OSERR; - } - goto failure; - } - outfifo_fd = -1; - - if(interrupted_by_signal){ - goto failure; - } - - if(not usplash_write(&fifo_fd, "TIMEOUT", "15")){ - if(errno != EINTR){ - error(0, errno, "usplash_write"); - status = EX_OSERR; - } - goto failure; - } - - if(interrupted_by_signal){ - goto failure; - } - - ret = close(fifo_fd); - if(ret == -1){ - if(errno != EINTR){ - error(0, errno, "close"); - status = EX_OSERR; - } - goto failure; - } - fifo_fd = -1; - - /* Print password to stdout */ - size_t written = 0; - while(written < buf_len){ - do { - sret = write(STDOUT_FILENO, buf + written, buf_len - written); - if(sret == -1){ - if(errno != EINTR){ - error(0, errno, "write"); - status = EX_OSERR; - } - goto failure; - } - } while(sret == -1); - - if(interrupted_by_signal){ - goto failure; - } - written += (size_t)sret; - } - free(buf); - buf = NULL; - - if(interrupted_by_signal){ - goto failure; - } - - free(cmdline); - return EXIT_SUCCESS; - - failure: - - free(buf); - - free(prompt); - - /* If usplash was never accessed, we can stop now */ - if(not usplash_accessed){ - return status; - } - - /* Close FIFO */ - if(fifo_fd != -1){ - ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd)); - if(ret == -1 and errno != EINTR){ - error(0, errno, "close"); - } - fifo_fd = -1; - } - - /* Close output FIFO */ - if(outfifo_fd != -1){ - ret = (int)TEMP_FAILURE_RETRY(close(outfifo_fd)); - if(ret == -1){ - error(0, errno, "close"); - } - } - - /* Create argv for new usplash*/ - char **cmdline_argv = malloc((argz_count(cmdline, cmdline_len) + 1) - * sizeof(char *)); /* Count args */ - if(cmdline_argv == NULL){ - error(0, errno, "malloc"); - return status; - } - argz_extract(cmdline, cmdline_len, cmdline_argv); /* Create argv */ - - /* Kill old usplash */ - kill(usplash_pid, SIGTERM); - sleep(2); - while(kill(usplash_pid, 0) == 0){ - kill(usplash_pid, SIGKILL); - sleep(1); - } - - pid_t new_usplash_pid = fork(); - if(new_usplash_pid == 0){ - /* Child; will become new usplash process */ - - /* Make the effective user ID (root) the only user ID instead of - the real user ID (_mandos) */ - ret = setuid(geteuid()); - if(ret == -1){ - error(0, errno, "setuid"); - } - - setsid(); - ret = chdir("/"); - if(ret == -1){ - error(0, errno, "chdir"); - _exit(EX_OSERR); - } -/* if(fork() != 0){ */ -/* _exit(EXIT_SUCCESS); */ -/* } */ - ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */ - if(ret == -1){ - error(0, errno, "dup2"); - _exit(EX_OSERR); - } - - execv(usplash_name, cmdline_argv); - if(not interrupted_by_signal){ - error(0, errno, "execv"); - } - free(cmdline); - free(cmdline_argv); - _exit(EX_OSERR); - } - free(cmdline); - free(cmdline_argv); - sleep(2); - if(not usplash_write(&fifo_fd, "PULSATE", NULL)){ - if(errno != EINTR){ - error(0, errno, "usplash_write"); - } - } - - /* Close FIFO (again) */ - if(fifo_fd != -1){ - ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd)); - if(ret == -1 and errno != EINTR){ - error(0, errno, "close"); - } - fifo_fd = -1; - } - - if(interrupted_by_signal){ - struct sigaction signal_action = { .sa_handler = SIG_DFL }; - sigemptyset(&signal_action.sa_mask); - ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received, - &signal_action, NULL)); - if(ret == -1){ - error(0, errno, "sigaction"); - } - do { - ret = raise(signal_received); - } while(ret != 0 and errno == EINTR); - if(ret != 0){ - error(0, errno, "raise"); - abort(); - } - TEMP_FAILURE_RETRY(pause()); - } - - return status; -} === removed file 'plugins.d/usplash.xml' --- plugins.d/usplash.xml 2009-01-04 21:54:55 +0000 +++ plugins.d/usplash.xml 1970-01-01 00:00:00 +0000 @@ -1,297 +0,0 @@ - - - - -%common; -]> - - - - Mandos Manual - - Mandos - &version; - &TIMESTAMP; - - - Björn - Påhlsson -
- belorn@fukt.bsnet.se -
-
- - Teddy - Hogeborn -
- teddy@fukt.bsnet.se -
-
-
- - 2008 - 2009 - Teddy Hogeborn - Björn Påhlsson - - -
- - - &COMMANDNAME; - 8mandos - - - - &COMMANDNAME; - Mandos plugin to use usplash to get a - password. - - - - - &COMMANDNAME; - - - - - DESCRIPTION - - This program prompts for a password using - usplash8 - and outputs any given password to standard - output. If no usplash8 - process can be found, this program will immediately exit with an - exit code indicating failure. - - - This program 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. - - - If this program is killed (presumably by - plugin-runner - 8mandos because some other - plugin provided the password), it cannot tell - usplash8 - to abort requesting a password, because - usplash - 8 does not support this. - Therefore, this program will then kill the - running usplash - 8 process and start a - new one using the same command line - arguments as the old one was using. - - - - - OPTIONS - - This program takes no options. - - - - - 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 was interrupted or 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. - - - - - - - - FILES - - - /dev/.initramfs/usplash_fifo - - - This is the FIFO to where this program - will write the commands for usplash8 - . See fifo7 - . - - - - - /dev/.initramfs/usplash_outfifo - - - This is the FIFO where this program - will read the password from usplash8 - . See fifo7 - . - - - - - /proc - - - To find the running usplash8 - , this directory will be searched for - numeric entries which will be assumed to be directories. - In all those directories, the exe and - cmdline entries will be used to - determine the name of the running binary, effective user - and group ID, and the command line - arguments. See proc5 - . - - - - - /sbin/usplash - - - This is the name of the binary which will be searched for - in the process list. See usplash8 - . - - - - - - - - BUGS - - Killing usplash - 8 and starting a new one - is ugly, but necessary as long as it does not support aborting a - password request. - - - - - EXAMPLE - - Note that normally, this program will not be invoked directly, - but instead started by the Mandos plugin-runner8mandos - . - - - - This program takes no options. - - - &COMMANDNAME; - - - - - - SECURITY - - If this program is killed by a signal, it will kill the process - ID which at the start of this program was - determined to run usplash8 - as root (see also ). There is a very - slight risk that, in the time between those events, that process - ID was freed and then taken up by another - process; the wrong process would then be killed. Now, this - program can only be killed by the user who started it; see - plugin-runner - 8mandos. This program - should therefore be started by a completely separate - non-privileged user, and no other programs should be allowed to - run as that special user. This means that it is not recommended - to use the user "nobody" to start this program, as other - possibly less trusted programs could be running as "nobody", and - they would then be able to kill this program, triggering the - killing of the process ID which may or may not - be usplash - 8. - - - The only other 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. - - - - - SEE ALSO - - crypttab - 5, - fifo - 7, - plugin-runner - 8mandos, - proc - 5, - usplash - 8 - - -
- - - - -