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