=== added directory '.bzr-builddeb'
=== added file '.bzr-builddeb/default.conf'
--- .bzr-builddeb/default.conf 1970-01-01 00:00:00 +0000
+++ .bzr-builddeb/default.conf 2008-09-17 00:34:09 +0000
@@ -0,0 +1,2 @@
+[BUILDDEB]
+split = True
=== added file '.bzrignore'
--- .bzrignore 1970-01-01 00:00:00 +0000
+++ .bzrignore 2008-10-03 09:32:30 +0000
@@ -0,0 +1,14 @@
+*.5
+*.8
+*.8mandos
+confdir
+debian/po/messages.mo
+debian/po/templates.pot
+keydir
+man
+plugin-runner
+plugins.d/askpass-fifo
+plugins.d/mandos-client
+plugins.d/password-prompt
+plugins.d/splashy
+plugins.d/usplash
=== 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 2009-02-15 09:28:06 +0000
@@ -0,0 +1,133 @@
+-*- org -*-
+
+* Prerequisites
+
+** Operating System
+
+ Debian 5.0 "lenny" or Ubuntu 8.04 "Hardy Heron".
+
+ This is mostly for the support scripts which make sure that the
+ client is installed and started in the initial RAM disk environment
+ and that the initrd.img file is automatically made unreadable. The
+ server and client programs themselves *could* be run in other
+ distributions, but they *are* specific to GNU/Linux systems, and
+ are not intended to be portable to other Unixes.
+
+** Libraries
+
+ The following libraries and packages are needed. (It is possible
+ that it might work with older versions of some of these, but these
+ versions are confirmed to work. Newer versions are almost
+ certainly OK.)
+
+*** Documentation
+ These are required to build the manual pages for both the server
+ and client:
+
+ + DocBook 4.5 http://www.docbook.org/
+ Note: DocBook 5.0 is not compatible.
+ + DocBook XSL stylesheets 1.71.0
+ http://wiki.docbook.org/topic/DocBookXslStylesheets
+
+ Package names:
+ docbook docbook-xsl
+
+ To build just the documentation, run the command "make doc". Then
+ the manual page "mandos.8", for example, can be read by running
+ "man -l mandos.8".
+
+*** Mandos Server
+ + GnuTLS 2.4 http://www.gnu.org/software/gnutls/
+ + Avahi 0.6.16 http://www.avahi.org/
+ + Python 2.5 http://www.python.org/
+ + Python-GnuTLS 1.1.5 http://pypi.python.org/pypi/python-gnutls/
+ + dbus-python 0.82.4 http://dbus.freedesktop.org/doc/dbus-python/
+ + python-ctypes 1.0.0 http://pypi.python.org/pypi/ctypes
+ + PyGObject 2.14.2 http://library.gnome.org/devel/pygobject/
+
+ Strongly recommended:
+ + fping 2.4b2-to-ipv6 http://www.fping.com/
+
+ Package names:
+ python-gnutls avahi-daemon python python-avahi python-dbus
+ python-ctypes python-gobject
+
+*** Mandos Client
+ + initramfs-tools 0.85i
+ http://packages.qa.debian.org/i/initramfs-tools.html
+ + GnuTLS 2.4 http://www.gnu.org/software/gnutls/
+ + Avahi 0.6.16 http://www.avahi.org/
+ + GnuPG 1.4.9 http://www.gnupg.org/
+ + GPGME 1.1.6 http://www.gnupg.org/related_software/gpgme/
+
+ Package names:
+ initramfs-tools libgnutls-dev libavahi-core-dev gnupg
+ libgpgme11-dev
+
+* Installing the Mandos server
+
+ 1. Do "make doc".
+
+ 2. On the computer to run as a Mandos server, run the following
+ command:
+ For Debian: su -c 'make install-server'
+ For Ubuntu: sudo make install-server
+
+ (This creates a configuration without any clients configured; you
+ need an actually configured client to do that; see below.)
+
+* Installing the Mandos client.
+
+ 1. Do "make all doc".
+
+ 2. On the computer to run as a Mandos client, run the following
+ command:
+ For Debian: su -c 'make install-client'
+ For Ubuntu: sudo make install-client
+
+ This will also create an OpenPGP key, which will take some time
+ and entropy, so be patient.
+
+ 3. Run the following command:
+ For Debian: su -c 'mandos-keygen --password'
+ For Ubuntu: sudo mandos-keygen --password
+
+ When prompted, enter the password/passphrase for the encrypted
+ root file system on this client computer. The command will
+ output a section of text, starting with a [section header]. Copy
+ and append this to the file "/etc/mandos/clients.conf" *on the
+ server computer*.
+
+ 4. Configure the client to use the correct network interface. The
+ default is "eth0", and if this needs to be adjusted, it will be
+ necessary to edit /etc/mandos/plugin-runner.conf to uncomment and
+ change the line there. If that file is changed, the initrd.img
+ file must be updated, possibly using the following command:
+
+ # update-initramfs -k all -u
+
+ 5. On the server computer, start the server by running the command
+ For Debian: su -c 'invoke-rc.d mandos start'
+ For Ubuntu: sudo invoke-rc.d mandos start
+
+ At this point, it is possible to verify that the correct password
+ will be received by the client by running the command:
+
+ # /usr/lib/mandos/plugins.d/mandos-client \
+ --pubkey=/etc/keys/mandos/pubkey.txt \
+ --seckey=/etc/keys/mandos/seckey.txt; echo
+
+ This command should retrieve the password from the server,
+ decrypt it, and output it to standard output.
+
+ After this, the client computer should be able to reboot without
+ needing a password entered on the console, as long as it does not
+ take more than an hour to reboot.
+
+* Further customizations
+
+ You may want to tighten or loosen the timeouts in the server
+ configuration files; see mandos.conf(5) and mandos-clients.conf(5).
+ If IPsec is not used, it is suggested that a more cryptographically
+ secure checker program is used and configured, since without IPsec
+ ping packets can be faked.
=== modified file 'Makefile'
--- Makefile 2007-10-28 17:59:38 +0000
+++ Makefile 2009-05-23 05:59:52 +0000
@@ -1,7 +1,361 @@
-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=1 -Wextra -Wfloat-equal -Wundef -Wshadow \
+ -Wunsafe-loop-optimizations -Wpointer-arith \
+ -Wbad-function-cast -Wcast-qual -Wcast-align -Wwrite-strings \
+ -Wconversion -Wstrict-prototypes -Wold-style-definition \
+ -Wpacked -Wnested-externs -Winline -Wvolatile-register-var
+# -Wunreachable-code
+#DEBUG=-ggdb3
+# For info about _FORTIFY_SOURCE, see
+#
+FORTIFY=-D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIC -fPIE
+LINK_FORTIFY_LD=-z relro -fPIE
+LINK_FORTIFY=-pie
+#COVERAGE=--coverage
+OPTIMIZE=-Os
+LANGUAGE=-std=gnu99
+htmldir=man
+version=1.0.11
+SED=sed
+
+## Use these settings for a traditional /usr/local install
+# PREFIX=$(DESTDIR)/usr/local
+# CONFDIR=$(DESTDIR)/etc/mandos
+# KEYDIR=$(DESTDIR)/etc/mandos/keys
+# MANDIR=$(PREFIX)/man
+# INITRAMFSTOOLS=$(DESTDIR)/etc/initramfs-tools
+##
+
+## These settings are for a package-type install
+PREFIX=$(DESTDIR)/usr
+CONFDIR=$(DESTDIR)/etc/mandos
+KEYDIR=$(DESTDIR)/etc/keys/mandos
+MANDIR=$(PREFIX)/share/man
+INITRAMFSTOOLS=$(DESTDIR)/usr/share/initramfs-tools
+##
+
+GNUTLS_CFLAGS=$(shell pkg-config --cflags-only-I gnutls)
+GNUTLS_LIBS=$(shell pkg-config --libs gnutls)
+AVAHI_CFLAGS=$(shell pkg-config --cflags-only-I avahi-core)
+AVAHI_LIBS=$(shell pkg-config --libs avahi-core)
+GPGME_CFLAGS=$(shell gpgme-config --cflags; getconf LFS_CFLAGS)
+GPGME_LIBS=$(shell gpgme-config --libs; getconf LFS_LIBS; \
+ getconf LFS_LDFLAGS)
+
+# Do not change these two
+CFLAGS=$(WARN) $(DEBUG) $(FORTIFY) $(COVERAGE) $(OPTIMIZE) \
+ $(LANGUAGE) $(GNUTLS_CFLAGS) $(AVAHI_CFLAGS) $(GPGME_CFLAGS) \
+ -DVERSION='"$(version)"'
+LDFLAGS=$(COVERAGE) $(LINK_FORTIFY) $(foreach flag,$(LINK_FORTIFY_LD),-Xlinker $(flag))
+
+# Commands to format a DocBook document into a manual page
+DOCBOOKTOMAN=cd $(dir $<); xsltproc --nonet --xinclude \
+ --param man.charmap.use.subset 0 \
+ --param make.year.ranges 1 \
+ --param make.single.year.ranges 1 \
+ --param man.output.quietly 1 \
+ --param man.authors.section.enabled 0 \
+ /usr/share/xml/docbook/stylesheet/nwalsh/manpages/docbook.xsl \
+ $(notdir $<); \
+ $(MANPOST) $(notdir $@)
+# DocBook-to-man post-processing to fix a '\n' escape bug
+MANPOST=$(SED) --in-place --expression='s,\\\\en,\\en,g;s,\\n,\\en,g'
+
+DOCBOOKTOHTML=xsltproc --nonet --xinclude \
+ --param make.year.ranges 1 \
+ --param make.single.year.ranges 1 \
+ --param man.output.quietly 1 \
+ --param man.authors.section.enabled 0 \
+ --param citerefentry.link 1 \
+ --output $@ \
+ /usr/share/xml/docbook/stylesheet/nwalsh/xhtml/docbook.xsl \
+ $<; $(HTMLPOST) $@
+# Fix citerefentry links
+HTMLPOST=$(SED) --in-place \
+ --expression='s/\(\)\([^<]*\)\(<\/span>(\)\([^)]*\)\()<\/span><\/a>\)/\1\3.\5\2\3\4\5\6/g'
+
+PLUGINS=plugins.d/password-prompt plugins.d/mandos-client \
+ plugins.d/usplash plugins.d/splashy plugins.d/askpass-fifo
+CPROGS=plugin-runner $(PLUGINS)
+PROGS=mandos mandos-keygen mandos-ctl $(CPROGS)
+DOCS=mandos.8 plugin-runner.8mandos mandos-keygen.8 \
+ plugins.d/mandos-client.8mandos \
+ plugins.d/password-prompt.8mandos mandos.conf.5 \
+ plugins.d/usplash.8mandos plugins.d/splashy.8mandos \
+ plugins.d/askpass-fifo.8mandos mandos-clients.conf.5
+
+htmldocs=$(addsuffix .xhtml,$(DOCS))
+
+objects=$(addsuffix .o,$(CPROGS))
+
+all: $(PROGS) mandos.lsm
+
+doc: $(DOCS)
+
+html: $(htmldocs)
+
+%.5: %.xml common.ent legalnotice.xml
+ $(DOCBOOKTOMAN)
+%.5.xhtml: %.xml common.ent legalnotice.xml
+ $(DOCBOOKTOHTML)
+
+%.8: %.xml common.ent legalnotice.xml
+ $(DOCBOOKTOMAN)
+%.8.xhtml: %.xml common.ent legalnotice.xml
+ $(DOCBOOKTOHTML)
+
+%.8mandos: %.xml common.ent legalnotice.xml
+ $(DOCBOOKTOMAN)
+%.8mandos.xhtml: %.xml common.ent legalnotice.xml
+ $(DOCBOOKTOHTML)
+
+mandos.8: mandos.xml common.ent mandos-options.xml overview.xml \
+ legalnotice.xml
+ $(DOCBOOKTOMAN)
+mandos.8.xhtml: mandos.xml common.ent mandos-options.xml \
+ overview.xml legalnotice.xml
+ $(DOCBOOKTOHTML)
+
+mandos-keygen.8: mandos-keygen.xml common.ent overview.xml \
+ legalnotice.xml
+ $(DOCBOOKTOMAN)
+mandos-keygen.8.xhtml: mandos-keygen.xml common.ent overview.xml \
+ legalnotice.xml
+ $(DOCBOOKTOHTML)
+
+mandos.conf.5: mandos.conf.xml common.ent mandos-options.xml \
+ legalnotice.xml
+ $(DOCBOOKTOMAN)
+mandos.conf.5.xhtml: mandos.conf.xml common.ent mandos-options.xml \
+ legalnotice.xml
+ $(DOCBOOKTOHTML)
+
+plugin-runner.8mandos: plugin-runner.xml common.ent overview.xml \
+ legalnotice.xml
+ $(DOCBOOKTOMAN)
+plugin-runner.8mandos.xhtml: plugin-runner.xml common.ent \
+ overview.xml legalnotice.xml
+ $(DOCBOOKTOHTML)
+
+plugins.d/mandos-client.8mandos: plugins.d/mandos-client.xml \
+ common.ent \
+ mandos-options.xml \
+ overview.xml legalnotice.xml
+ $(DOCBOOKTOMAN)
+plugins.d/mandos-client.8mandos.xhtml: plugins.d/mandos-client.xml \
+ common.ent \
+ mandos-options.xml \
+ overview.xml legalnotice.xml
+ $(DOCBOOKTOHTML)
+
+# Update all these files with version number $(version)
+common.ent: Makefile
+ $(SED) --in-place \
+ --expression='s/^\($$/\1$(version)">/' \
+ $@
+
+mandos: Makefile
+ $(SED) --in-place \
+ --expression='s/^\(version = "\)[^"]*"$$/\1$(version)"/' \
+ $@
+
+mandos-keygen: Makefile
+ $(SED) --in-place \
+ --expression='s/^\(VERSION="\)[^"]*"$$/\1$(version)"/' \
+ $@
+
+mandos-ctl: Makefile
+ $(SED) --in-place \
+ --expression='s/^\(version = "\)[^"]*"$$/\1$(version)"/' \
+ $@
+
+mandos.lsm: Makefile
+ $(SED) --in-place \
+ --expression='s/^\(Version:\).*/\1\t$(version)/' \
+ $@
+ $(SED) --in-place \
+ --expression='s/^\(Entered-date:\).*/\1\t$(shell date --rfc-3339=date --reference=Makefile)/' \
+ $@
+ $(SED) --in-place \
+ --expression='s/\(mandos_\)[0-9.]\+\(\.orig\.tar\.gz\)/\1$(version)\2/' \
+ $@
+
+plugins.d/mandos-client: plugins.d/mandos-client.o
+ $(LINK.o) $(GNUTLS_LIBS) $(AVAHI_LIBS) $(GPGME_LIBS) \
+ $(COMMON) $^ $(LOADLIBES) $(LDLIBS) -o $@
+
+.PHONY : all doc html clean distclean run-client run-server install \
+ install-server install-client uninstall uninstall-server \
+ uninstall-client purge purge-server purge-client
clean:
- rm -f server client
+ -rm --force $(CPROGS) $(objects) $(htmldocs) $(DOCS) core
+
+distclean: clean
+mostlyclean: clean
+maintainer-clean: clean
+ -rm --force --recursive keydir confdir
+
+check: all
+ ./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 \
+ $(CLIENTARGS)
+
+# Used by run-client
+keydir/seckey.txt keydir/pubkey.txt: mandos-keygen
+ install --directory keydir
+ ./mandos-keygen --dir keydir --force
+
+# Run the server with a local config
+run-server: confdir/mandos.conf confdir/clients.conf
+ ./mandos --debug --no-dbus --configdir=confdir $(SERVERARGS)
+
+# Used by run-server
+confdir/mandos.conf: mandos.conf
+ install --directory confdir
+ install --mode=u=rw,go=r $^ $@
+confdir/clients.conf: clients.conf keydir/seckey.txt
+ install --directory confdir
+ install --mode=u=rw $< $@
+# Add a client password
+ ./mandos-keygen --dir keydir --password >> $@
+
+install: install-server install-client-nokey
+
+install-html: html
+ install --directory $(htmldir)
+ install --mode=u=rw,go=r --target-directory=$(htmldir) \
+ $(htmldocs)
+
+install-server: doc
+ install --directory $(CONFDIR)
+ install --mode=u=rwx,go=rx mandos $(PREFIX)/sbin/mandos
+ install --mode=u=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
+ if [ -z $(DESTDIR) ]; then \
+ update-rc.d mandos defaults 25 15;\
+ fi
+ gzip --best --to-stdout mandos.8 \
+ > $(MANDIR)/man8/mandos.8.gz
+ gzip --best --to-stdout mandos.conf.5 \
+ > $(MANDIR)/man5/mandos.conf.5.gz
+ gzip --best --to-stdout mandos-clients.conf.5 \
+ > $(MANDIR)/man5/mandos-clients.conf.5.gz
+
+install-client-nokey: all doc
+ install --directory $(PREFIX)/lib/mandos $(CONFDIR)
+ install --directory --mode=u=rwx $(KEYDIR) \
+ $(PREFIX)/lib/mandos/plugins.d
+ if [ "$(CONFDIR)" != "$(PREFIX)/lib/mandos" ]; then \
+ install --mode=u=rwx \
+ --directory "$(CONFDIR)/plugins.d"; \
+ fi
+ install --mode=u=rwx,go=rx \
+ --target-directory=$(PREFIX)/lib/mandos plugin-runner
+ install --mode=u=rwx,go=rx --target-directory=$(PREFIX)/sbin \
+ mandos-keygen
+ install --mode=u=rwx,go=rx \
+ --target-directory=$(PREFIX)/lib/mandos/plugins.d \
+ plugins.d/password-prompt
+ install --mode=u=rwxs,go=rx \
+ --target-directory=$(PREFIX)/lib/mandos/plugins.d \
+ plugins.d/mandos-client
+ install --mode=u=rwxs,go=rx \
+ --target-directory=$(PREFIX)/lib/mandos/plugins.d \
+ plugins.d/usplash
+ install --mode=u=rwxs,go=rx \
+ --target-directory=$(PREFIX)/lib/mandos/plugins.d \
+ plugins.d/splashy
+ install --mode=u=rwxs,go=rx \
+ --target-directory=$(PREFIX)/lib/mandos/plugins.d \
+ plugins.d/askpass-fifo
+ install initramfs-tools-hook \
+ $(INITRAMFSTOOLS)/hooks/mandos
+ install --mode=u=rw,go=r initramfs-tools-hook-conf \
+ $(INITRAMFSTOOLS)/conf-hooks.d/mandos
+ install initramfs-tools-script \
+ $(INITRAMFSTOOLS)/scripts/init-premount/mandos
+ install --mode=u=rw,go=r plugin-runner.conf $(CONFDIR)
+ gzip --best --to-stdout mandos-keygen.8 \
+ > $(MANDIR)/man8/mandos-keygen.8.gz
+ gzip --best --to-stdout plugin-runner.8mandos \
+ > $(MANDIR)/man8/plugin-runner.8mandos.gz
+ gzip --best --to-stdout plugins.d/password-prompt.8mandos \
+ > $(MANDIR)/man8/password-prompt.8mandos.gz
+ gzip --best --to-stdout plugins.d/mandos-client.8mandos \
+ > $(MANDIR)/man8/mandos-client.8mandos.gz
+ gzip --best --to-stdout plugins.d/usplash.8mandos \
+ > $(MANDIR)/man8/usplash.8mandos.gz
+ gzip --best --to-stdout plugins.d/splashy.8mandos \
+ > $(MANDIR)/man8/splashy.8mandos.gz
+ gzip --best --to-stdout plugins.d/askpass-fifo.8mandos \
+ > $(MANDIR)/man8/askpass-fifo.8mandos.gz
+
+install-client: install-client-nokey
+# Post-installation stuff
+ -$(PREFIX)/sbin/mandos-keygen --dir "$(KEYDIR)"
+ update-initramfs -k all -u
+ echo "Now run mandos-keygen --password --dir $(KEYDIR)"
+
+uninstall: uninstall-server uninstall-client
+
+uninstall-server:
+ -rm --force $(PREFIX)/sbin/mandos \
+ $(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 \
+ $(PREFIX)/lib/mandos/plugins.d/splashy \
+ $(PREFIX)/lib/mandos/plugins.d/askpass-fifo \
+ $(INITRAMFSTOOLS)/hooks/mandos \
+ $(INITRAMFSTOOLS)/conf-hooks.d/mandos \
+ $(INITRAMFSTOOLS)/scripts/init-premount/mandos \
+ $(MANDIR)/man8/plugin-runner.8mandos.gz \
+ $(MANDIR)/man8/mandos-keygen.8.gz \
+ $(MANDIR)/man8/password-prompt.8mandos.gz \
+ $(MANDIR)/man8/usplash.8mandos.gz \
+ $(MANDIR)/man8/splashy.8mandos.gz \
+ $(MANDIR)/man8/askpass-fifo.8mandos.gz \
+ $(MANDIR)/man8/mandos-client.8mandos.gz
+ -rmdir $(PREFIX)/lib/mandos/plugins.d $(CONFDIR)/plugins.d \
+ $(PREFIX)/lib/mandos $(CONFDIR) $(KEYDIR)
+ update-initramfs -k all -u
+
+purge: purge-server purge-client
+
+purge-server: uninstall-server
+ -rm --force $(CONFDIR)/mandos.conf $(CONFDIR)/clients.conf \
+ $(DESTDIR)/etc/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 'NEWS'
--- NEWS 1970-01-01 00:00:00 +0000
+++ NEWS 2009-05-23 05:59:52 +0000
@@ -0,0 +1,102 @@
+This NEWS file records noteworthy changes, very tersely.
+See the manual for detailed information.
+
+Version 1.0.11 (2009-05-23)
+* Client
+** Bug fix: Use "pkg-config" instead of old "libgnutls-config".
+
+Version 1.0.10 (2009-05-17)
+* Client
+** Security bug fix: Fix permissions on initrd.img-*.bak files when
+ upgrading from older versions.
+
+Version 1.0.9 (2009-05-17)
+* Client
+** Security bug fix: Fix permissions on initrd.img file when
+ installing new linux-image-* packages calling mkinitramfs-kpkg (all
+ version lower than 2.6.28-1-* does this).
+
+Version 1.0.8 (2009-02-25)
+* Client
+** Bug fix: Fix missing quote characters in initramfs-tools-hook.
+
+Version 1.0.7 (2009-02-24)
+* Client
+** Bug fix: Do not depend on GNU awk.
+
+Version 1.0.6 (2009-02-13)
+* Server
+** Fix bug where server would stop responding, with a zombie checker
+** Support for disabling IPv6 (only for advanced users)
+** Fix bug which made server not change group ID
+
+* Client
+** Bug fix: Fix permission for /lib64 (on relevant architechtures).
+** Add support for IPv4 addresses.
+** Add support in mandos-client for not bringing up a network
+ interface by specifying an empty string to "--interface".
+** Make password prompt on boot not be mangled by kernel log messages
+ about network interface.
+** Get network interface from initramfs.conf and/or from kernel
+ command line.
+** If set by "ip=" kernel command line, configure network on boot.
+** Support connecting directly using "mandos=connect" kernel command.
+ line option, provided network is configured using "ip=".
+** Fix bug which made plugin-runner and mandos-client not change group
+ ID.
+** Fix bug where the "--options-for" option of plugin-runner would
+ truncate the value at the first colon character.
+** Fix bug where plugin-runner would not go to fallback if all plugins
+ failed.
+** Fix bug where mandos-client would not clean temporary directory on
+ a signal or on certain file systems.
+** Bug fix: remove bashism in /bin/sh script "mandos-keygen".
+
+Version 1.0.5 (2009-01-17)
+* Client
+** Fix small memory leak in plugin-runner.
+
+Version 1.0.4 (2009-01-15)
+* Server
+** Only find matched user/group pairs when searching for suitable
+ nonprivileged user/group to switch to.
+
+* Client
+** New kernel parameter "mandos=off" makes client not run at boot.
+** Fix linking errors and compilation warnings on AMD64.
+** Parse numbers in command line options better.
+** The splashy and usplash plugins are more robust while traversing
+ /proc, and will not abort if a process suddenly disappears.
+
+Version 1.0.3 (2009-01-06)
+* Server
+** Now tries to change to user and group "_mandos" before falling back
+ to trying the old values "mandos", "nobody:nogroup", and "65534".
+** Now does not abort on startup even if no clients are defined in
+ clients.conf.
+
+* Client
+** Plugins named "*.dpkg-bak" are now ignored.
+** Hopefully fixed compilation failure on some architectures where the
+ C compiler does not recognize the "-z" option as a linker option.
+
+Version 1.0.2 (2008-10-17)
+* mandos-keygen now signs the encrypted key blobs. This signature is
+ not currently verified by mandos-client, but this may change in the
+ future.
+
+Version 1.0.1 (2008-10-07)
+* Server
+** Expand environment variables and ~user in clients.conf's "secfile"
+ The "secfile" option in /etc/mandos/clients.conf now expands
+ "~user/foo" and "$ENVVAR" strings.
+
+* Client (plugin-runner, plugins, etc.)
+** Manual pages for the usplash, splashy, and askpass-fifo plugins.
+ All plugins now have man pages.
+** More secure compilation and linking flags.
+ All programs are now compiled with "-fstack-protector-all -fPIE
+ -pie", and linked using "-z relro -pie" for additional security.
+
+* There is now a "NEWS" file (this one), giving a history of
+ noteworthy changes.
=== added file 'README'
--- README 1970-01-01 00:00:00 +0000
+++ README 2009-02-23 11:52:42 +0000
@@ -0,0 +1,180 @@
+-*- org -*-
+
+* Mandos
+ - Have your cake and eat it too!
+
+ You know how it is. You’ve heard of it happening. The Man comes
+ and takes away your servers, your friends’ servers, the servers of
+ everybody in the same hosting facility. The servers of their
+ neighbors, and their neighbors’ friends. The servers of people who
+ owe them money. And like *that*, they’re gone. And you doubt
+ you’ll ever see them again.
+
+ That is why your servers have encrypted root file systems. However,
+ there’s a downside. There’s no going around it: rebooting is a
+ pain. Dragging out that rarely-used keyboard and screen and
+ unraveling cables behind your servers to plug them in to type in
+ that password is messy, especially if you have many servers. There
+ are some people who do clever things like using serial line consoles
+ and daisy-chain it to the next server, and keep all the servers
+ connected in a ring with serial cables, which will work, if your
+ servers are physically close enough. There are also other
+ out-of-band management solutions, but with *all* these, you still
+ have to be on hand and manually type in the password at boot time.
+ Otherwise the server just sits there, waiting for a password.
+
+ Wouldn’t it be great if you could have the security of encrypted
+ root file systems and still have servers that could boot up
+ automatically if there was a short power outage while you were
+ asleep? That you could reboot at will, without having someone run
+ over to the server to type in the password?
+
+ Well, with Mandos, you (almost) can! The gain in convenience will
+ only be offset by a small loss in security. The setup is as
+ follows:
+
+ The server will still have its encrypted root file system. The
+ password to this file system will be stored on another computer
+ (henceforth known as the Mandos server) on the same local network.
+ The password will *not* be stored in plaintext, but encrypted with
+ OpenPGP. To decrypt this password, a key is needed. This key (the
+ Mandos client key) will not be stored there, but back on the
+ original server (henceforth known as the Mandos client) in the
+ initial RAM disk image. Oh, and all network Mandos client/server
+ communications will be encrypted, using TLS (SSL).
+
+ So, at boot time, the Mandos client will ask for its encrypted data
+ over the network, decrypt it to get the password, use it to decrypt
+ the root file, and continue booting.
+
+ Now, of course the initial RAM disk image is not on the encrypted
+ root file system, so anyone who had physical access could take the
+ Mandos client computer offline and read the disk with their own
+ tools to get the authentication keys used by a client. *But*, by
+ then the Mandos server should notice that the original server has
+ been offline for too long, and will no longer give out the encrypted
+ key. The timing here is the only real weak point, and the method,
+ frequency and timeout of the server’s checking can be adjusted to
+ any desired level of paranoia
+
+ (The encrypted keys on the Mandos server is on its normal file
+ system, so those are safe, provided the root file system of *that*
+ server is encrypted.)
+
+* FAQ - couldn’t the security be defeated by...
+
+** Grabbing the Mandos client key from the initrd *really quickly*?
+ This, as mentioned above, is the only real weak point. But if you
+ set the timing values tight enough, this will be really difficult
+ to do. An attacker would have to physically disassemble the client
+ computer, extract the key from the initial RAM disk image, and then
+ connect to a *still online* Mandos server to get the encrypted key,
+ and do all this *before* the Mandos server timeout kicks in and the
+ Mandos server refuses to give out the key to anyone.
+
+ Now, as the typical procedure seems to be to barge in and turn off
+ and grab *all* computers, to maybe look at them months later, this
+ is not likely. If someone does that, the whole system *will* lock
+ itself up completely, since Mandos servers are no longer running.
+
+ For sophisticated attackers who *could* do the clever thing, *and*
+ had physical access to the server for enough time, it would be
+ simpler to get a key for an encrypted file system by using hardware
+ memory scanners and reading it right off the memory bus.
+
+** Replay attacks?
+ Nope, the network stuff is all done over TLS, which provides
+ protection against that.
+
+** Man-in-the-middle?
+ No. The server only gives out the passwords to clients which have
+ *in the TLS handshake* proven that they do indeed hold the OpenPGP
+ private key corresponding to that client.
+
+** Physically grabbing the Mandos server computer?
+ You could protect *that* computer the old-fashioned way, with a
+ must-type-in-the-password-at-boot method. Or you could have two
+ computers be the Mandos server for each other.
+
+ Multiple Mandos servers can coexist on a network without any
+ trouble. They do not clash, and clients will try all available
+ servers. This means that if just one reboots then the other can
+ bring it back up, but if both 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.
+
+* The Plugin System
+ In the early designs, the mandos-client(8mandos) program (which
+ retrieves a password from the Mandos server) also prompted for a
+ password on the terminal, in case a Mandos server could not be
+ found. Other ways of retrieving a password could easily be
+ envisoned, but this multiplicity of purpose was seen to be too
+ complex to be a viable way to continue. Instead, the original
+ program was separated into mandos-client(8mandos) and
+ password-prompt(8mandos), and a plugin-runner(8mandos) exist to run
+ them both in parallel, allowing the first successful plugin to
+ provide the password. This opened up for any number of additional
+ plugins to run, all competing to be the first to find a password and
+ provide it to the plugin runner.
+
+ Three additional plugins are provided:
+ * usplash(8mandos)
+ This prompts for a password when using usplash(8).
+ * splashy(8mandos)
+ This prompts for a password when using splashy(8).
+ * askpass-fifo(8mandos)
+ To provide compatibility with the "askpass" program from
+ cryptsetup, this plugin listens to the same FIFO as askpass would
+ do.
+
+ More plugins can easily be written and added by the system
+ administrator; see the section called "WRITING PLUGINS" in
+ plugin-runner(8mandos) to learn the plugin requirements.
+
+* Copyright
+
+ Copyright © 2008,2009 Teddy Hogeborn
+ Copyright © 2008,2009 Björn Påhlsson
+
+** License:
+
+ This program is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ .
=== added file 'TODO'
--- TODO 1970-01-01 00:00:00 +0000
+++ TODO 2009-09-17 11:42:12 +0000
@@ -0,0 +1,90 @@
+-*- org -*-
+
+* mandos-client
+** TODO [#B] use scandir(3) instead of readdir(3)
+** TODO [#B] Prefix all debug output with argv[0]
+** TODO [#B] Retry a server which has a non-definite reply:
+*** A closed connection during the TLS handshake
+*** A TCP timeout
+** TODO [#B] Use capabilities instead of seteuid().
+
+* splashy
+** TODO [#B] use scandir(3) instead of readdir(3)
+** TODO [#B] Prefix all debug output with "Mandos plugin " + argv[0]
+
+* usplash
+** TODO [#B] use scandir(3) instead of readdir(3)
+** TODO [#B] Prefix all debug output with "Mandos plugin " + argv[0]
+
+* askpass-fifo
+** TODO [#B] Prefix all debug output with "Mandos plugin " + argv[0]
+** TODO [#B] Drop privileges after opening FIFO.
+
+* password-prompt
+** TODO [#B] Prefix all debug output with "Mandos plugin " + argv[0]
+
+* plugin-runner
+** TODO [#B] use scandir(3) instead of readdir(3)
+** TODO [#C] use same file name rules as run-parts(8)
+
+* mandos (server)
+** TODO [#B] Log level :BUGS:
+** TODO /etc/mandos/clients.d/*.conf
+ Watch this directory and add/remove/update clients?
+** TODO config for TXT record
+** TODO [#B] Run-time communication with server :BUGS:
+ Probably using D-Bus
+*** Client class
+*** Main server
+ + SetLogLevel
+ syslogger.setLevel(logging.WARNING)
+ + [[http://log.ometer.com/2007-05.html][Best D-Bus practices]]
+** TODO Implement --foreground :BUGS:
+ [[info:standards:Option%20Table][Table of Long Options]]
+** TODO Implement --socket
+ [[info:standards:Option%20Table][Table of Long Options]]
+** TODO Date+time on console log messages :BUGS:
+ Is this the default?
+** TODO DBusServiceObjectUsingSuper
+** Global enable/disable flag
+** By-client countdown on secrets given
+** Fix problem with fsck taking a really long time
+ Whenever a client successfully gets a secret it could get a
+ one-time timeout boost to allow for an fsck-incurred delay
+
+* mandos.xml
+** [[file:mandos.xml::XXX][Document D-Bus interface]]
+
+* Provide and install /etc/dbus-1/system.d/mandos.conf
+
+* mandos-ctl
+*** Handle "no D-Bus server" and/or "no Mandos server found" better
+*** [#B] --dump option
+
+* mandos-monitor
+** D-Bus mail loop w/ signal receiver
+** Snack/Newt client data displayer
+*** Client Widgets
+*** Properties popup
+
+* mandos-keygen
+** TODO Loop until passwords match when run interactively
+** TODO "--secfile" option
+ Using the "secfile" option instead of "secret"
+** TODO [#B] "--test" option
+ For testing decryption before rebooting.
+
+* Makefile
+** Implement DEB_BUILD_OPTIONS
+ http://www.debian.org/doc/debian-policy/ch-source.html#s-debianrules-options
+
+* Package
+** /usr/share/initramfs-tools/hooks/mandos
+*** TODO [#C] use same file name rules as run-parts(8)
+*** TODO [#C] Do not install in initrd.img if configured not to.
+ Use "/etc/initramfs-tools/hooksconf.d/mandos"?
+** TODO [#C] /etc/bash_completion.d/mandos
+ From XML sources directly?
+
+
+#+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-----
-MIIHEzCCBPugAwIBAgIJAMCSQPxm3Tm7MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYD
-VQQGEwJTRTELMAkGA1UECBMCQkwxEDAOBgNVBAcTB1Jvbm5lYnkxITAfBgNVBAoT
-GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEWMBQGA1UEAxMNQnJheGVuIHVuaXRl
-ZDEjMCEGCSqGSIb3DQEJARYUYmVsb3JuQGZ1a3QuYnNuZXQuc2UwHhcNMDcxMDE1
-MTczNDQ1WhcNMTcxMDEyMTczNDQ1WjCBjDELMAkGA1UEBhMCU0UxCzAJBgNVBAgT
-AkJMMRAwDgYDVQQHEwdSb25uZWJ5MSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRz
-IFB0eSBMdGQxFjAUBgNVBAMTDUJyYXhlbiB1bml0ZWQxIzAhBgkqhkiG9w0BCQEW
-FGJlbG9ybkBmdWt0LmJzbmV0LnNlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
-CgKCAgEAxrMpGUiDUI0X3XgYovgRGaKaSIoCL7RlNVRHxWLEaNIEmHuhQv40fksa
-ndO7KcbxBRTMvF/XNYyEEyks+D3jQrBmWBjsOSdZkLhP64HLnx2eYLd1HEAXIhB8
-wip180m9tfT6XeXX6cOh5AowMOGHv364xcZXJvYJgxVuMW1vlh9F79N1bnnR2rJ6
-4nmebJ91QW7ecSwA6h0fnx6GF3d5PfEC24/Fys/1NbdYiy8EYsP+lIEHQC4oD0Za
-kjclNWT3O1I8lnLdm4F1KdgDIva7aCyoAI4rLsxFxnnaqQcPo+8gGhusfnbOSymd
-m2tdz91x3Z2rSoo/cgc2TaPYKYuiJScsw1FftYGOZLiXbAedCqmoEC9XdMlZrbPn
-AWhjSvbFY8B2LCoy23OFpV2h+QrnKXOzsxyfxexwkjytIn9Msy5DlcCQz93vO/c9
-CGN/GpXJf5ebNk4oTE7pXNMM27shvubtXa6BL7A+S7yB8xmQ3NA+CABsQsKHcgVy
-2+SLD65Ms6OpwXn9BPlT1aJxbTiCXlSIQ7OIDPE1okwjxLTHc9dygoYuqjLG9Dkx
-WOqZWNxyj3kU40mX96qbOhJG5Zgq9J9fUT4ZJETO754YIFGKltlT6RSqeLrNOuLg
-9cYNGOLqoCVIyUi5HiTR7iIukNDeZy4YN6BiQH+TnYjC5+FwZbsCAwEAAaOCAXQw
-ggFwMB0GA1UdDgQWBBT4/xxxSVgFPLkDVNc8Ocdvodu3JTCBwQYDVR0jBIG5MIG2
-gBT4/xxxSVgFPLkDVNc8Ocdvodu3JaGBkqSBjzCBjDELMAkGA1UEBhMCU0UxCzAJ
-BgNVBAgTAkJMMRAwDgYDVQQHEwdSb25uZWJ5MSEwHwYDVQQKExhJbnRlcm5ldCBX
-aWRnaXRzIFB0eSBMdGQxFjAUBgNVBAMTDUJyYXhlbiB1bml0ZWQxIzAhBgkqhkiG
-9w0BCQEWFGJlbG9ybkBmdWt0LmJzbmV0LnNlggkAwJJA/GbdObswDwYDVR0TAQH/
-BAUwAwEB/zARBglghkgBhvhCAQEEBAMCAQYwCQYDVR0SBAIwADArBglghkgBhvhC
-AQ0EHhYcVGlueUNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAfBgNVHREEGDAWgRRi
-ZWxvcm5AZnVrdC5ic25ldC5zZTAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEF
-BQADggIBAD/AJL0dKb4CcVEBzYsKV5JAxZQBTFYXm4TlMzbNJnl6C3FH636xSfvE
-qWWPrIplbmNn0tyXNf1dvb2Xy4QVFTo5lnp6PKwhqpV5JK7+4/LiDZj5R4VNl36B
-RUleXSSGV/+PJ1hRC9J/s+oziK1025hPpDB+n13sFrBv9DyqP/3GDSVoi13Mldf1
-vTWRnvugi7XlMhFdemB1Vz0eyIde0FtFpPrd3HeFrXQX8617XfcEYJmCU2aDT1fy
-77RHFG+3dL286UcK2D85bqNCntP9W3zP6rwerkO1pGmT2cBn9AO6Qt2MI7klTJmw
-SRqbeBcF+gaBRGY8iZHcqow7vqTvo65p/ZH9AUqMQh41W40FbepUA3354xGmwAlB
-LBXRuVjaDvKyargh936dOyzIDr+fZtr6BEthmt6BghgOZlezAV/P60P56DlFvpK8
-4wHyT5KI9LOqbg+W2ToB/GdRZz4p7K90JljrgVlnQhYIQ1YT/5OzPu6Uq5+4Q4hc
-ORb5hRCWHwre95kgEzUPqrteqV/g99XaOgqpK6KhY/dpmb7Z5I9f6d61XYYFfxYm
-iuNJiTLCQlLyfXFSRlJubWD8hNHo2HNRYdiWF3cttuGL9aC0uSIk4xq/Oi/guqIn
-sZlrO3I4MaiUnUXntMLU4zh0o/RPqhoT3lY1VubwgmpkOw8p7ggE
------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
=== added file 'clients.conf'
--- clients.conf 1970-01-01 00:00:00 +0000
+++ clients.conf 2009-01-08 03:54:06 +0000
@@ -0,0 +1,65 @@
+# 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.bin
+;
+;# An IP address for host is also fine, if the checker accepts it.
+;host = 192.0.2.3
+;
+;# Parameters from the [DEFAULT] section can be overridden per client.
+;interval = 5m
+;####
=== added file 'common.ent'
--- common.ent 1970-01-01 00:00:00 +0000
+++ common.ent 2009-05-23 05:59:52 +0000
@@ -0,0 +1,3 @@
+
+
+
=== 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 directory 'debian'
=== added file 'debian/changelog'
--- debian/changelog 1970-01-01 00:00:00 +0000
+++ debian/changelog 2009-05-23 05:59:52 +0000
@@ -0,0 +1,124 @@
+mandos (1.0.11-1) unstable; urgency=low
+
+ * debian/control (Standards-Version): Changed to "3.8.1".
+ * Makefile (GNUTLS_CFLAGS, GNUTLS_CFLAGS): Use "pkg-config" instead of
+ the old "libgnutls-config" script. (Closes: #529836)
+
+ -- Teddy Hogeborn Sat, 23 May 2009 07:12:20 +0200
+
+mandos (1.0.10-1) unstable; urgency=low
+
+ * New upstream release.
+ * debian/mandos-client.postinst (update_initramfs): Fix permissions of
+ old initrd.img-*.bak files.
+
+ -- Teddy Hogeborn Sun, 17 May 2009 04:56:35 +0200
+
+mandos (1.0.9-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Teddy Hogeborn Sun, 17 May 2009 02:59:45 +0200
+
+mandos (1.0.8-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Teddy Hogeborn Wed, 25 Feb 2009 02:26:57 +0100
+
+mandos (1.0.7-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Teddy Hogeborn Tue, 24 Feb 2009 12:58:06 +0100
+
+mandos (1.0.6-1) unstable; urgency=low
+
+ * New upstream release.
+ * debian/mandos-client.postinst: Converted to Bourne shell. Also
+ minor message change.
+ * debian/mandos-client.postrm: Minor message change.
+ * debian/mandos.postinst: Converted to Bourne shell. Also minor
+ message change.
+ * debian/mandos.prerm: Minor message change.
+ * debian/rules (install-indep): Removed "--no-start" from
+ dh_installinit.
+ * debian/mandos-client.lintian-overrides: Remove obsolete override for
+ unbreakable line in plugin-runner manual page.
+ * debian/control (mandos/Depends): Added "python-gobject".
+ * debian/mandos-client.dirs: Change
+ "usr/share/initramfs-tools/scripts/local-top" to
+ "usr/share/initramfs-tools/scripts/init-premount".
+ * debian/mandos-client.README.Debian: Add reference to initramfs.conf
+ and nfsroot.txt. New section about the new non-local connection
+ feature.
+
+ -- Teddy Hogeborn Fri, 13 Feb 2009 09:27:25 +0100
+
+mandos (1.0.5-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Teddy Hogeborn Sat, 17 Jan 2009 02:26:00 +0100
+
+mandos (1.0.4-1) unstable; urgency=low
+
+ * New upstream release.
+ * debian/watch: New file.
+ * debian/mandos-client.README.Debian: Document new "mandos=off" kernel
+ parameter.
+
+ -- Teddy Hogeborn Thu, 15 Jan 2009 05:49:22 +0100
+
+mandos (1.0.3-2) unstable; urgency=low
+
+ * Removed some now-unused debconf files.
+ * Changed postinst scripts to not source debconf/confmodule.
+ * Removed po-debconf from build-depends.
+
+ -- Teddy Hogeborn Tue, 06 Jan 2009 21:28:20 +0100
+
+mandos (1.0.3-1) unstable; urgency=low
+
+ * New upstream release.
+ * Add -Xlinker to linker flags to fix FTBFS for some architectures.
+ Thanks to Thiemo Seufer for the report and
+ fix. (Closes: #509398)
+ * Remove debconf use altogether, thereby stopping debconf abuse. Thanks
+ to Christian Perrier . (Closes: #509653)
+ * Add NEWS file to /usr/share/doc directories.
+ * Use and create "_mandos" user+group. Rename old user+group created by
+ older versions of this package.
+ * Fix manual pages by adding build-depend on "docbook-xml".
+
+ -- Teddy Hogeborn Tue, 06 Jan 2009 01:21:20 +0100
+
+mandos (1.0.2-1) unstable; urgency=low
+
+ * New upstream release.
+ * debian/copyright: Rewritten to conform to
+ .
+
+ -- Teddy Hogeborn Fri, 17 Oct 2008 20:42:12 +0200
+
+mandos (1.0.1-1) unstable; urgency=low
+
+ * New upstream release.
+ * Separate /usr/share/doc/mandos-client/README.Debian into sections with
+ headlines. Add instructions on how to test the server and verify the
+ password.
+
+ -- Teddy Hogeborn Tue, 07 Oct 2008 23:07:23 +0200
+
+mandos (1.0-2) unstable; urgency=low
+
+ * Added comments in debian/*.lintian-overrides files. Added Debian
+ revison number to version number.
+
+ -- Teddy Hogeborn Wed, 01 Oct 2008 17:23:35 +0200
+
+mandos (1.0-1) unstable; urgency=low
+
+ * Initial Release. (Closes: #500727).
+
+ -- Teddy Hogeborn Tue, 30 Sep 2008 21:58:43 +0200
=== added file 'debian/compat'
--- debian/compat 1970-01-01 00:00:00 +0000
+++ debian/compat 2008-09-17 00:34:09 +0000
@@ -0,0 +1,1 @@
+7
=== added file 'debian/control'
--- debian/control 1970-01-01 00:00:00 +0000
+++ debian/control 2009-09-17 01:21:27 +0000
@@ -0,0 +1,53 @@
+Source: mandos
+Section: admin
+Priority: extra
+Maintainer: Mandos Maintainers
+Uploaders: Teddy Hogeborn ,
+ Björn Påhlsson
+Build-Depends: debhelper (>= 7), docbook-xml, docbook-xsl,
+ libavahi-core-dev, libgpgme11-dev, libgnutls-dev, xsltproc,
+ pkg-config
+Standards-Version: 3.8.3
+Vcs-Bzr: http://ftp.fukt.bsnet.se/pub/mandos/trunk
+Vcs-Browser: http://bzr.fukt.bsnet.se/loggerhead/mandos/trunk/files
+Homepage: http://www.fukt.bsnet.se/mandos
+
+Package: mandos
+Architecture: all
+Depends: ${misc:Depends}, python (>=2.5), python-gnutls, python-dbus,
+ python-avahi, python-gobject, avahi-daemon, gnupg (< 2),
+ adduser
+Recommends: fping
+Description: a server giving encrypted passwords to Mandos clients
+ This is the server part of the Mandos system, which allows
+ computers to have encrypted root file systems and at the
+ same time be capable of remote and/or unattended reboots.
+ .
+ The computers run a small client program in the initial RAM
+ disk environment which will communicate with a server over a
+ network. All network communication is encrypted using TLS.
+ The clients are identified by the server using an OpenPGP
+ key; each client has one unique to it. The server sends the
+ clients an encrypted password. The encrypted password is
+ decrypted by the clients using the same OpenPGP key, and the
+ password is then used to unlock the root file system,
+ whereupon the computers can continue booting normally.
+
+Package: mandos-client
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, cryptsetup
+Enhances: cryptsetup
+Description: do unattended reboots with an encrypted root file system
+ This is the client part of the Mandos system, which allows
+ computers to have encrypted root file systems and at the
+ same time be capable of remote and/or unattended reboots.
+ .
+ The computers run a small client program in the initial RAM
+ disk environment which will communicate with a server over a
+ network. All network communication is encrypted using TLS.
+ The clients are identified by the server using an OpenPGP
+ key; each client has one unique to it. The server sends the
+ clients an encrypted password. The encrypted password is
+ decrypted by the clients using the same OpenPGP key, and the
+ password is then used to unlock the root file system,
+ whereupon the computers can continue booting normally.
=== added file 'debian/copyright'
--- debian/copyright 1970-01-01 00:00:00 +0000
+++ debian/copyright 2009-01-04 21:54:55 +0000
@@ -0,0 +1,26 @@
+Format-Specification:
+ http://wiki.debian.org/Proposals/CopyrightFormat?action=recall&rev=233
+Upstream-Name: Mandos
+Upstream-Maintainer: Mandos Maintainers
+Upstream-Source:
+
+Files: *
+Copyright: Copyright © 2008,2009 Teddy Hogeborn
+Copyright: Copyright © 2008,2009 Björn Påhlsson
+License: GPL-3+
+ This program is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+ .
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ .
+ .
+ On Debian systems, the complete text of the GNU General Public
+ License can be found in "/usr/share/common-licenses/GPL".
=== added file 'debian/mandos-client.README.Debian'
--- debian/mandos-client.README.Debian 1970-01-01 00:00:00 +0000
+++ debian/mandos-client.README.Debian 2009-09-08 06:28:20 +0000
@@ -0,0 +1,87 @@
+* Choose the Client Network Interface
+
+ You MUST make sure that the correct network interface is specified
+ in the DEVICE setting in the "/etc/initramfs-tools/initramfs.conf"
+ file. *If* this is changed, it will be necessary to update the
+ initrd image by running the command
+
+ update-initramfs -k all -u
+
+ The device can be overridden at boot time on the Linux kernel
+ command line using the sixth colon-separated field of the "ip="
+ option; for exact syntax, read the documentation in the file
+ "/usr/share/doc/linux-doc-*/Documentation/filesystems/nfsroot.txt",
+ available in the "linux-doc-*" package.
+
+ Note that since this network interface is used in the initial RAM
+ disk environment, the network interface *must* exist at that stage.
+ Thus, the interface can *not* be a pseudo-interface such as "br0" or
+ "tun0"; instead, a real interface (such as "eth0") must be used.
+
+* Adding a Client Password to the Server
+
+ The server must be given a password to give back to the client on
+ boot time. This password must be a one which can be used to unlock
+ the root file system device. On the *client*, run this command:
+
+ mandos-keygen --password
+
+ It will prompt for a password and output a config file section.
+ This output should be copied to the Mandos server and added to the
+ file "/etc/mandos/clients.conf" there.
+
+* Testing that it Works (Without Rebooting)
+
+ After the server has been started with this client's key added, it
+ is possible to verify that the correct password will be received by
+ this client by running the command, on the client:
+
+ /usr/lib/mandos/plugins.d/mandos-client \
+ --pubkey=/etc/keys/mandos/pubkey.txt \
+ --seckey=/etc/keys/mandos/seckey.txt; echo
+
+ This command should retrieve the password from the server, decrypt
+ it, and output it to standard output. There it can be verified to
+ be the correct password, before rebooting.
+
+* User-Supplied Plugins
+
+ Any plugins found in "/etc/mandos/plugins.d" will override and add
+ to the normal Mandos plugins. When adding or changing plugins, do
+ not forget to update the initital RAM disk image:
+
+ update-initramfs -k all -u
+
+* Do *NOT* Edit "/etc/crypttab"
+
+ It is NOT necessary to edit "/etc/crypttab" to specify
+ "/usr/lib/mandos/plugin-runner" as a keyscript for the root file
+ system; if no keyscript is given for the root file system, the
+ Mandos client will be the new default way for getting a password for
+ the root file system when booting.
+
+* Emergency Escape
+
+ If it ever should be necessary, the Mandos client can be temporarily
+ prevented from running at startup by passing the parameter
+ "mandos=off" to the kernel.
+
+* Non-local Connection (Not Using ZeroConf)
+
+ If the "ip=" kernel command line option is used to specify a
+ complete IP address and device name, as noted above, it then becomes
+ possible to specify a specific IP address and port to connect to,
+ instead of using ZeroConf. The syntax for doing this is
+ "mandos=connect::".
+
+ Warning: this will cause the client to make exactly one attempt at
+ connecting, and then fail if it does not succeed.
+
+ For very advanced users, it it possible to specify simply
+ "mandos=connect" on the kernel command line to make the system only
+ set up the network (using the data in the "ip=" option) and not pass
+ any extra "--connect" options to mandos-client at boot. For this to
+ work, "--options-for=mandos-client:--connect=:" needs
+ to be manually added to the file "/etc/mandos/plugin-runner.conf".
+
+ -- Teddy Hogeborn , Tue, 8 Sep 2009 08:25:58 +0200
=== added file 'debian/mandos-client.dirs'
--- debian/mandos-client.dirs 1970-01-01 00:00:00 +0000
+++ debian/mandos-client.dirs 2009-02-07 04:50:39 +0000
@@ -0,0 +1,5 @@
+usr/share/man/man8
+usr/sbin
+usr/share/initramfs-tools/hooks
+usr/share/initramfs-tools/conf-hooks.d
+usr/share/initramfs-tools/scripts/init-premount
=== added file 'debian/mandos-client.docs'
--- debian/mandos-client.docs 1970-01-01 00:00:00 +0000
+++ debian/mandos-client.docs 2008-10-18 11:17:22 +0000
@@ -0,0 +1,3 @@
+NEWS
+README
+TODO
=== added file 'debian/mandos-client.links'
--- debian/mandos-client.links 1970-01-01 00:00:00 +0000
+++ debian/mandos-client.links 2008-09-19 13:50:22 +0000
@@ -0,0 +1,1 @@
+usr/share/man/man8/plugin-runner.8mandos.gz usr/share/man/man5/plugin-runner.conf.5mandos.gz
=== added file 'debian/mandos-client.lintian-overrides'
--- debian/mandos-client.lintian-overrides 1970-01-01 00:00:00 +0000
+++ debian/mandos-client.lintian-overrides 2009-01-18 06:41:57 +0000
@@ -0,0 +1,27 @@
+# This directory contains secret client key files.
+#
+mandos-client binary: non-standard-dir-perm etc/keys/mandos/ 0700 != 0755
+
+# The directory /usr/lib/mandos/plugins.d contains setuid binaries
+# which are not meant to be run outside an initial RAM disk
+# environment (except for test purposes). It would be insecure to
+# allow anyone to run them.
+#
+mandos-client binary: non-standard-dir-perm usr/lib/mandos/plugins.d/ 0700 != 0755
+
+# These binaries must be setuid root, since they need root powers, but
+# are started by plugin-runner(8mandos), which runs all plugins as
+# user/group "_mandos". These binaries are not run in a running
+# system, but in an initial RAM disk environment. Here they are
+# protected from non-root access by the directory permissions, above.
+#
+mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/mandos-client 4755 root/root
+mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/askpass-fifo 4755 root/root
+mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/splashy 4755 root/root
+mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/usplash 4755 root/root
+
+# The directory /etc/mandos/plugins.d can be used by local system
+# administrators to place plugins in, overriding and complementing
+# /usr/lib/mandos/plugins.d, and must be likewise protected.
+#
+mandos-client binary: non-standard-dir-perm etc/mandos/plugins.d/ 0700 != 0755
=== added file 'debian/mandos-client.postinst'
--- debian/mandos-client.postinst 1970-01-01 00:00:00 +0000
+++ debian/mandos-client.postinst 2009-05-24 23:36:15 +0000
@@ -0,0 +1,81 @@
+#!/bin/sh -e
+# This script can be called in the following ways:
+#
+# After the package was installed:
+# configure
+#
+#
+# If prerm fails during upgrade or fails on failed upgrade:
+# abort-upgrade
+#
+# If prerm fails during deconfiguration of a package:
+# abort-deconfigure in-favour
+# removing
+#
+# If prerm fails during replacement due to conflict:
+# abort-remove in-favour
+
+# Update the initial RAM file system image
+update_initramfs()
+{
+ if [ -x /usr/sbin/update-initramfs ]; then
+ update-initramfs -u -k all
+ fi
+
+ if dpkg --compare-versions "$2" lt-nl "1.0.10-1"; then
+ # Make old initrd.img files unreadable too, in case they were
+ # created with mandos-client 1.0.8 or older.
+ find /boot -maxdepth 1 -type f -name "initrd.img-*.bak" \
+ -print0 | xargs --null --no-run-if-empty chmod o-r
+ fi
+}
+
+# Add user and group
+add_mandos_user(){
+ # Rename old "mandos" user and group
+ if dpkg --compare-versions "$2" lt "1.0.3-1"; then
+ case "`getent passwd mandos`" in
+ *:Mandos\ password\ system,,,:/nonexistent:/bin/false)
+ usermod --login _mandos mandos
+ groupmod --new-name _mandos mandos
+ return
+ ;;
+ esac
+ fi
+ # Create new user and group
+ if ! getent passwd _mandos >/dev/null; then
+ adduser --system --force-badname --quiet --home /nonexistent \
+ --no-create-home --group --disabled-password \
+ --gecos "Mandos password system" _mandos
+ fi
+}
+
+# Create client key pair
+create_key(){
+ if [ -r /etc/keys/mandos/pubkey.txt \
+ -a -r /etc/keys/mandos/seckey.txt ]; then
+ return 0
+ fi
+ if [ -x /usr/sbin/mandos-keygen ]; then
+ mandos-keygen
+ fi
+}
+
+case "$1" in
+ configure)
+ add_mandos_user "$@"
+ create_key "$@"
+ update_initramfs "$@"
+ ;;
+ abort-upgrade|abort-deconfigure|abort-remove)
+ ;;
+
+ *)
+ echo "$0 called with unknown argument '$1'" 1>&2
+ exit 1
+ ;;
+esac
+
+#DEBHELPER#
+
+exit 0
=== added file 'debian/mandos-client.postrm'
--- debian/mandos-client.postrm 1970-01-01 00:00:00 +0000
+++ debian/mandos-client.postrm 2009-01-18 00:16:57 +0000
@@ -0,0 +1,60 @@
+#!/bin/sh -e
+# This script can be called in the following ways:
+#
+# After the package was removed:
+# remove
+#
+# After the package was purged:
+# purge
+#
+# After the package was upgraded:
+# upgrade
+# if that fails:
+# failed-upgrade
+#
+#
+# After all of the packages files have been replaced:
+# disappear
+#
+#
+# If preinst fails during install:
+# abort-install
+#
+# If preinst fails during upgrade of removed package:
+# abort-install
+#
+# If preinst fails during upgrade:
+# abort-upgrade
+
+
+# Update the initial RAM file system image
+update_initramfs()
+{
+ if [ -x /usr/sbin/update-initramfs ]; then
+ update-initramfs -u -k all
+ fi
+}
+
+case "$1" in
+ remove)
+ update_initramfs
+ ;;
+
+ purge)
+ shred --remove /etc/keys/mandos/seckey.txt 2>/dev/null || :
+ rm --force /etc/mandos/plugin-runner.conf \
+ /etc/keys/mandos/pubkey.txt \
+ /etc/keys/mandos/seckey.txt 2>/dev/null
+ ;;
+ upgrade|failed-upgrade|disappear|abort-install|abort-upgrade)
+ ;;
+
+ *)
+ echo "$0 called with unknown argument '$1'" 1>&2
+ exit 1
+ ;;
+esac
+
+#DEBHELPER#
+
+exit 0
=== added file 'debian/mandos.README.Debian'
--- debian/mandos.README.Debian 1970-01-01 00:00:00 +0000
+++ debian/mandos.README.Debian 2009-09-08 06:28:20 +0000
@@ -0,0 +1,10 @@
+The Mandos server is useless without at least one configured client in
+/etc/mandos/clients.conf. To create one, install the "mandos-client"
+package on a client computer, and run the command
+
+ # mandos-keygen --password
+
+there to get a config file stanza. Append the output of that command
+to the file "/etc/mandos/clients.conf" on the Mandos server.
+
+ -- Teddy Hogeborn , Tue, 8 Sep 2009 06:57:45 +0200
=== added file 'debian/mandos.dirs'
--- debian/mandos.dirs 1970-01-01 00:00:00 +0000
+++ debian/mandos.dirs 2008-09-17 00:34:09 +0000
@@ -0,0 +1,5 @@
+usr/share/man/man5
+usr/share/man/man8
+etc/init.d
+etc/default
+usr/sbin
=== added file 'debian/mandos.docs'
--- debian/mandos.docs 1970-01-01 00:00:00 +0000
+++ debian/mandos.docs 2008-10-18 11:17:22 +0000
@@ -0,0 +1,3 @@
+NEWS
+README
+TODO
=== added file 'debian/mandos.lintian-overrides'
--- debian/mandos.lintian-overrides 1970-01-01 00:00:00 +0000
+++ debian/mandos.lintian-overrides 2008-10-01 15:29:01 +0000
@@ -0,0 +1,4 @@
+# This config file will normally have encrypted secret client keys in
+# it, so it must be kept unreadable for non-root users.
+#
+mandos binary: non-standard-file-perm etc/mandos/clients.conf 0600 != 0644
=== added file 'debian/mandos.postinst'
--- debian/mandos.postinst 1970-01-01 00:00:00 +0000
+++ debian/mandos.postinst 2009-05-24 23:28:04 +0000
@@ -0,0 +1,49 @@
+#!/bin/sh -e
+# This script can be called in the following ways:
+#
+# After the package was installed:
+# configure
+#
+#
+# If prerm fails during upgrade or fails on failed upgrade:
+# abort-upgrade
+#
+# If prerm fails during deconfiguration of a package:
+# abort-deconfigure in-favour
+# removing
+#
+# If prerm fails during replacement due to conflict:
+# abort-remove in-favour
+
+case "$1" in
+ configure)
+ # Rename old "mandos" user and group
+ if dpkg --compare-versions "$2" lt "1.0.3-1"; then
+ case "`getent passwd mandos`" in
+ *:Mandos\ password\ system,,,:/nonexistent:/bin/false)
+ usermod --login _mandos mandos
+ groupmod --new-name _mandos mandos
+ ;;
+ esac
+ fi
+ # Create new user and group
+ if ! getent passwd _mandos >/dev/null; then
+ adduser --system --force-badname --quiet \
+ --home /nonexistent --no-create-home --group \
+ --disabled-password --gecos "Mandos password system" \
+ _mandos
+ fi
+ ;;
+
+ abort-upgrade|abort-deconfigure|abort-remove)
+ ;;
+
+ *)
+ echo "$0 called with unknown argument '$1'" 1>&2
+ exit 1
+ ;;
+esac
+
+#DEBHELPER#
+
+exit 0
=== added file 'debian/mandos.prerm'
--- debian/mandos.prerm 1970-01-01 00:00:00 +0000
+++ debian/mandos.prerm 2009-01-18 00:16:57 +0000
@@ -0,0 +1,38 @@
+#! /bin/sh
+# prerm script for mandos
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * 'remove'
+# * 'upgrade'
+# * 'failed-upgrade'
+# * 'remove' 'in-favour'
+# * 'deconfigure' 'in-favour'
+# 'removing'
+#
+# for details, see /usr/share/doc/packaging-manual/
+
+case "$1" in
+ remove|deconfigure)
+ if [ -x /etc/init.d/mandos ]; then
+ if [ -x /usr/sbin/invoke-rc.d ]; then
+ invoke-rc.d mandos stop
+ else
+ /etc/init.d/mandos stop
+ fi
+ fi
+ ;;
+ upgrade|failed-upgrade)
+ ;;
+ *)
+ echo "prerm called with unknown argument '$1'" >&2
+ exit 0
+ ;;
+esac
+
+#DEBHELPER#
+
+exit 0
=== added directory 'debian/po'
=== added file 'debian/rules'
--- debian/rules 1970-01-01 00:00:00 +0000
+++ debian/rules 2009-01-18 00:18:50 +0000
@@ -0,0 +1,92 @@
+#!/usr/bin/make -f
+# Sample debian/rules that uses debhelper.
+#
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+#
+# Modified to make a template file for a multi-binary package with separated
+# build-arch and build-indep targets by Bill Allombert 2001
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# This has to be exported to make some magic below work.
+export DH_OPTIONS
+
+configure: configure-stamp
+configure-stamp:
+ dh_testdir
+ touch configure-stamp
+
+build: build-arch build-indep
+
+build-arch: build-arch-stamp
+build-arch-stamp: configure-stamp
+ dh_auto_build -- all doc
+ touch $@
+
+build-indep: build-indep-stamp
+build-indep-stamp: configure-stamp
+ dh_auto_build -- doc
+ touch $@
+
+clean:
+ dh_testdir
+ dh_testroot
+ rm -f build-arch-stamp build-indep-stamp configure-stamp
+ dh_auto_clean
+ dh_clean
+
+install: install-indep install-arch
+install-indep:
+ dh_testdir
+ dh_testroot
+ dh_prep
+ dh_installdirs --indep
+ $(MAKE) DESTDIR=$(CURDIR)/debian/mandos install-server
+ dh_lintian
+ dh_installinit --onlyscripts \
+ --update-rcd-params="defaults 25 15"
+ dh_install --indep
+
+install-arch:
+ dh_testdir
+ dh_testroot
+ dh_prep
+ dh_installdirs --same-arch
+ $(MAKE) DESTDIR=$(CURDIR)/debian/mandos-client install-client-nokey
+ dh_lintian
+ dh_install --same-arch
+
+binary-common:
+ dh_testdir
+ dh_testroot
+ dh_installchangelogs
+ dh_installdocs
+ dh_link
+ dh_strip
+ dh_compress
+ dh_fixperms --exclude etc/keys/mandos \
+ --exclude etc/mandos/clients.conf \
+ --exclude etc/mandos/plugins.d \
+ --exclude usr/lib/mandos/plugins.d
+ dh_installdeb
+ dh_shlibdeps
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+# Build architecture independant packages using the common target.
+binary-indep: build-indep install-indep
+ $(MAKE) -f debian/rules DH_OPTIONS=--indep binary-common
+
+# Build architecture dependant packages using the common target.
+binary-arch: build-arch install-arch
+ $(MAKE) -f debian/rules DH_OPTIONS=--same-arch binary-common
+
+binary: binary-arch binary-indep
+
+.PHONY: build clean binary-indep binary-arch binary install \
+ install-indep install-arch configure
=== added file 'debian/watch'
--- debian/watch 1970-01-01 00:00:00 +0000
+++ debian/watch 2009-01-15 02:52:02 +0000
@@ -0,0 +1,2 @@
+version=3
+ftp://ftp.fukt.bsnet.se/pub/mandos/mandos[-_]([^\s]+?)(?:\.orig)?\.tar\.(?:gz|bz2|7z)
=== added file 'default-mandos'
--- default-mandos 1970-01-01 00:00:00 +0000
+++ default-mandos 2008-09-17 00:34:09 +0000
@@ -0,0 +1,7 @@
+# Directory where configuration files are located. Default is
+# "/etc/mandos".
+#
+#CONFIGDIR=/etc/mandos
+
+# Additional options that are passed to the Daemon.
+DAEMON_ARGS=""
=== added file 'init.d-mandos'
--- init.d-mandos 1970-01-01 00:00:00 +0000
+++ init.d-mandos 2009-09-16 23:28:39 +0000
@@ -0,0 +1,159 @@
+#! /bin/sh
+### BEGIN INIT INFO
+# Provides: mandos
+# Required-Start: $remote_fs $syslog avahi
+# Required-Stop: $remote_fs $syslog avahi
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Mandos server
+# Description: Gives encrypted passwords to Mandos clients
+### END INIT INFO
+
+# Author: Teddy Hogeborn
+# Author: Björn Påhlsson
+#
+# Please remove the "Author" lines above and replace them
+# with your own name if you copy and modify this script.
+
+# Do NOT "set -e"
+
+# PATH should only include /usr/* if it runs after the mountnfs.sh script
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC="Mandos root file system password server"
+NAME=mandos
+DAEMON=/usr/sbin/$NAME
+DAEMON_ARGS=""
+PIDFILE=/var/run/$NAME.pid
+SCRIPTNAME=/etc/init.d/$NAME
+
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
+# Read configuration variable file if it is present
+[ -r /etc/default/$NAME ] && . /etc/default/$NAME
+
+if [ -n "$CONFIGDIR" ]; then
+ DAEMON_ARGS="$DAEMON_ARGS --configdir $CONFIGDIR"
+fi
+
+# Load the VERBOSE setting and other rcS variables
+. /lib/init/vars.sh
+
+# Define LSB log_* functions.
+# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
+. /lib/lsb/init-functions
+
+#
+# Function that starts the daemon/service
+#
+do_start()
+{
+ # Return
+ # 0 if daemon has been started
+ # 1 if daemon was already running
+ # 2 if daemon could not be started
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
+ || return 1
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
+ $DAEMON_ARGS \
+ || return 2
+ # Add code here, if necessary, that waits for the process to be ready
+ # to handle requests from services started subsequently which depend
+ # on this one. As a last resort, sleep for some time.
+}
+
+#
+# Function that stops the daemon/service
+#
+do_stop()
+{
+ # Return
+ # 0 if daemon has been stopped
+ # 1 if daemon was already stopped
+ # 2 if daemon could not be stopped
+ # other if a failure occurred
+ start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
+ RETVAL="$?"
+ [ "$RETVAL" = 2 ] && return 2
+ # Wait for children to finish too if this is a daemon that forks
+ # and if the daemon is only ever run from this initscript.
+ # If the above conditions are not satisfied then add some other code
+ # that waits for the process to drop all resources that could be
+ # needed by services started subsequently. A last resort is to
+ # sleep for some time.
+ start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
+ [ "$?" = 2 ] && return 2
+ # Many daemons don't delete their pidfiles when they exit.
+ rm -f $PIDFILE
+ return "$RETVAL"
+}
+
+#
+# Function that sends a SIGHUP to the daemon/service
+#
+do_reload() {
+ #
+ # If the daemon can reload its configuration without
+ # restarting (for example, when it is sent a SIGHUP),
+ # then implement that here.
+ #
+ start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
+ return 0
+}
+
+case "$1" in
+ start)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
+ do_start
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ stop)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ #reload|force-reload)
+ #
+ # If do_reload() is not implemented then leave this commented out
+ # and leave 'force-reload' as an alias for 'restart'.
+ #
+ #log_daemon_msg "Reloading $DESC" "$NAME"
+ #do_reload
+ #log_end_msg $?
+ #;;
+ restart|force-reload)
+ #
+ # If the "reload" option is implemented then remove the
+ # 'force-reload' alias
+ #
+ log_daemon_msg "Restarting $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1)
+ do_start
+ case "$?" in
+ 0) log_end_msg 0 ;;
+ 1) log_end_msg 1 ;; # Old process is still running
+ *) log_end_msg 1 ;; # Failed to start
+ esac
+ ;;
+ *)
+ # Failed to stop
+ log_end_msg 1
+ ;;
+ esac
+ ;;
+ *)
+ #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
+ echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
+ exit 3
+ ;;
+esac
+
+:
=== added file 'initramfs-tools-hook'
--- initramfs-tools-hook 1970-01-01 00:00:00 +0000
+++ initramfs-tools-hook 2009-09-07 23:48:17 +0000
@@ -0,0 +1,175 @@
+#!/bin/sh
+
+# This script will be run by 'mkinitramfs' when it creates the image.
+# Its job is to decide which files to install, then install them into
+# the staging area, where the initramfs is being created. This
+# happens when a new 'linux-image' package is installed, or when the
+# administrator runs 'update-initramfs' by hand to update an initramfs
+# image.
+
+# The environment contains at least:
+#
+# DESTDIR -- The staging directory where the image is being built.
+
+# No initramfs pre-requirements
+PREREQ="cryptroot"
+
+prereqs()
+{
+ echo "$PREREQ"
+}
+
+case $1 in
+# get pre-requisites
+prereqs)
+ prereqs
+ exit 0
+ ;;
+esac
+
+. /usr/share/initramfs-tools/hook-functions
+
+for d in /usr /usr/local; do
+ if [ -d "$d"/lib/mandos ]; then
+ prefix="$d"
+ break
+ fi
+done
+if [ -z "$prefix" ]; then
+ # Mandos not found
+ exit 1
+fi
+
+for d in /etc/keys/mandos /etc/mandos/keys; do
+ if [ -d "$d" ]; then
+ keydir="$d"
+ break
+ fi
+done
+if [ -z "$keydir" ]; then
+ # Mandos key directory not found
+ exit 1
+fi
+
+set `{ getent passwd _mandos \
+ || getent passwd nobody \
+ || echo ::65534:65534:::; } \
+ | cut --delimiter=: --fields=3,4 --only-delimited \
+ --output-delimiter=" "`
+mandos_user="$1"
+mandos_group="$2"
+
+# The Mandos network client uses the network
+auto_add_modules net
+# The Mandos network client uses IPv6
+force_load ipv6
+
+# These are directories inside the initrd
+CONFDIR="/conf/conf.d/mandos"
+MANDOSDIR="/lib/mandos"
+PLUGINDIR="${MANDOSDIR}/plugins.d"
+
+# Make directories
+install --directory --mode=u=rwx,go=rx "${DESTDIR}${CONFDIR}" \
+ "${DESTDIR}${MANDOSDIR}"
+install --owner=${mandos_user} --group=${mandos_group} --directory \
+ --mode=u=rwx "${DESTDIR}${PLUGINDIR}"
+
+# Copy the Mandos plugin runner
+copy_exec "$prefix"/lib/mandos/plugin-runner "${MANDOSDIR}"
+
+# Copy the plugins
+
+# Copy the packaged plugins
+for file in "$prefix"/lib/mandos/plugins.d/*; do
+ base="`basename \"$file\"`"
+ # Is this plugin overridden?
+ if [ -e "/etc/mandos/plugins.d/$base" ]; then
+ continue
+ fi
+ case "$base" in
+ *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert)
+ : ;;
+ "*") echo "W: Mandos client plugin directory is empty." >&2 ;;
+ *) copy_exec "$file" "${PLUGINDIR}" ;;
+ esac
+done
+
+# Copy any user-supplied plugins
+for file in /etc/mandos/plugins.d/*; do
+ base="`basename \"$file\"`"
+ case "$base" in
+ *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert)
+ : ;;
+ "*") : ;;
+ *) copy_exec "$file" "${PLUGINDIR}" ;;
+ esac
+done
+
+# GPGME needs /usr/bin/gpg
+if [ ! -e "${DESTDIR}/usr/bin/gpg" \
+ -a -n "`ls \"${DESTDIR}\"/usr/lib/libgpgme.so* \
+ 2>/dev/null`" ]; then
+ copy_exec /usr/bin/gpg
+fi
+
+# Config files
+for file in /etc/mandos/*; do
+ if [ -d "$file" ]; then
+ continue
+ fi
+ cp --archive --sparse=always "$file" "${DESTDIR}${CONFDIR}"
+done
+
+if [ ${mandos_user} != 65534 ]; then
+ sed --in-place --expression="1i--userid=${mandos_user}" \
+ "${DESTDIR}${CONFDIR}/plugin-runner.conf"
+fi
+
+if [ ${mandos_group} != 65534 ]; then
+ sed --in-place --expression="1i--groupid=${mandos_group}" \
+ "${DESTDIR}${CONFDIR}/plugin-runner.conf"
+fi
+
+# Key files
+for file in "$keydir"/*; do
+ if [ -d "$file" ]; then
+ continue
+ fi
+ cp --archive --sparse=always "$file" "${DESTDIR}${CONFDIR}"
+ chown ${mandos_user}:${mandos_group} \
+ "${DESTDIR}${CONFDIR}/`basename \"$file\"`"
+done
+
+# /lib/mandos/plugin-runner will drop priviliges, but needs access to
+# its plugin directory and its config file. However, since almost all
+# files in initrd have been created with umask 027, this opening of
+# permissions is needed.
+#
+# (The umask is not really intended to affect the files inside the
+# initrd; it is intended to affect the initrd.img file itself, since
+# it now contains secret key files. There is, however, no other way
+# to set the permission of the initrd.img file without a race
+# condition. This umask is set by "initramfs-tools-hook-conf",
+# installed as "/usr/share/initramfs-tools/conf-hooks.d/mandos".)
+#
+for full in "${MANDOSDIR}" "${CONFDIR}"; do
+ while [ "$full" != "/" ]; do
+ chmod a+rX "${DESTDIR}$full"
+ full="`dirname \"$full\"`"
+ done
+done
+
+# Reset some other things to sane permissions which we have
+# inadvertently affected with our umask setting.
+for dir in / /bin /etc /keyscripts /sbin /scripts /usr /usr/bin; do
+ if [ -d "${DESTDIR}$dir" ]; then
+ chmod a+rX "${DESTDIR}$dir"
+ fi
+done
+for dir in "${DESTDIR}"/lib* "${DESTDIR}"/usr/lib*; do
+ if [ -d "$dir" ]; then
+ find "$dir" \! -perm -u+rw,g+r -prune -or -print0 \
+ | xargs --null --no-run-if-empty chmod a+rX
+ fi
+done
=== added file 'initramfs-tools-hook-conf'
--- initramfs-tools-hook-conf 1970-01-01 00:00:00 +0000
+++ initramfs-tools-hook-conf 2009-05-17 00:50:09 +0000
@@ -0,0 +1,13 @@
+# -*- shell-script -*-
+
+# if mkinitramfs is started by mkinitramfs-kpkg, mkinitramfs-kpkg has
+# already touched the initrd file with umask 022 before we had a
+# chance to affect it. We cannot allow a readable initrd file,
+# therefore we must fix this now.
+if [ -e "${outfile}" ] \
+ && [ `stat --format=%s "${outfile}"` -eq 0 ]; then
+ rm "${outfile}"
+ (umask 027; touch "${outfile}")
+fi
+
+UMASK=027
=== added file 'initramfs-tools-script'
--- initramfs-tools-script 1970-01-01 00:00:00 +0000
+++ initramfs-tools-script 2009-09-16 23:28:39 +0000
@@ -0,0 +1,151 @@
+#!/bin/sh -e
+#
+# This script will run in the initrd environment at boot and edit
+# /conf/conf.d/cryptroot to set /lib/mandos/plugin-runner as keyscript
+# when no other keyscript is set, before cryptsetup.
+#
+
+# This script should be installed as
+# "/usr/share/initramfs-tools/scripts/init-premount/mandos" which will
+# eventually be "/scripts/init-premount/mandos" in the initrd.img
+# file.
+
+PREREQ="udev"
+prereqs()
+{
+ echo "$PREREQ"
+}
+
+case $1 in
+prereqs)
+ prereqs
+ exit 0
+ ;;
+esac
+
+. /scripts/functions
+
+for param in `cat /proc/cmdline`; do
+ case "$param" in
+ ip=*) IPOPTS="${param#ip=}" ;;
+ mandos=*)
+ # Split option line on commas
+ old_ifs="$IFS"
+ IFS="$IFS,"
+ for mpar in ${param#mandos=}; do
+ IFS="$old_ifs"
+ case "$mpar" in
+ off) exit 0 ;;
+ connect) connect="" ;;
+ connect:*) connect="${mpar#connect:}" ;;
+ *) log_warning_msg "$0: Bad option ${mpar}" ;;
+ esac
+ done
+ unset mpar
+ IFS="$old_ifs"
+ unset old_ifs
+ ;;
+ esac
+done
+unset param
+
+chmod a=rwxt /tmp
+
+test -r /conf/conf.d/cryptroot
+test -w /conf/conf.d
+
+# Get DEVICE from /conf/initramfs.conf and other files
+. /conf/initramfs.conf
+for conf in /conf/conf.d/*; do
+ [ -f ${conf} ] && . ${conf}
+done
+if [ -e /conf/param.conf ]; then
+ . /conf/param.conf
+fi
+
+# Override DEVICE from sixth field of ip= kernel option, if passed
+case "$IPOPTS" in
+ *:*:*:*:*:*) # At least six fields
+ # Remove the first five fields
+ device="${IPOPTS#*:*:*:*:*:}"
+ # Remove all fields except the first one
+ DEVICE="${device%%:*}"
+ ;;
+esac
+
+# Add device setting (if any) to plugin-runner.conf
+if [ "${DEVICE+set}" = set ]; then
+ # Did we get the device from an ip= option?
+ if [ "${device+set}" = set ]; then
+ # Let ip= option override local config; append:
+ cat <<-EOF >>/conf/conf.d/mandos/plugin-runner.conf
+
+ --options-for=mandos-client:--interface=${DEVICE}
+EOF
+ else
+ # Prepend device setting so any later options would override:
+ sed -i -e \
+ '1i--options-for=mandos-client:--interface='"${DEVICE}" \
+ /conf/conf.d/mandos/plugin-runner.conf
+ fi
+fi
+unset device
+
+# If we are connecting directly, run "configure_networking" (from
+# /scripts/functions); it needs IPOPTS and DEVICE
+if [ "${connect+set}" = set ]; then
+ configure_networking
+ if [ -n "$connect" ]; then
+ cat <<-EOF >>/conf/conf.d/mandos/plugin-runner.conf
+
+ --options-for=mandos-client:--connect=${connect}
+EOF
+ fi
+fi
+
+# Do not replace cryptroot file unless we need to.
+replace_cryptroot=no
+
+# Our keyscript
+mandos=/lib/mandos/plugin-runner
+
+# parse /conf/conf.d/cryptroot. Format:
+# target=sda2_crypt,source=/dev/sda2,key=none,keyscript=/foo/bar/baz
+exec 3>/conf/conf.d/cryptroot.mandos
+while read options; do
+ newopts=""
+ # Split option line on commas
+ old_ifs="$IFS"
+ IFS="$IFS,"
+ for opt in $options; do
+ # Find the keyscript option, if any
+ case "$opt" in
+ keyscript=*)
+ keyscript="${opt#keyscript=}"
+ newopts="$newopts,$opt"
+ ;;
+ "") : ;;
+ *)
+ newopts="$newopts,$opt"
+ ;;
+ esac
+ done
+ IFS="$old_ifs"
+ unset old_ifs
+ # If there was no keyscript option, add one.
+ if [ -z "$keyscript" ]; then
+ replace_cryptroot=yes
+ newopts="$newopts,keyscript=$mandos"
+ fi
+ newopts="${newopts#,}"
+ echo "$newopts" >&3
+done < /conf/conf.d/cryptroot
+exec 3>&-
+
+# If we need to, replace the old cryptroot file with the new file.
+if [ "$replace_cryptroot" = yes ]; then
+ mv /conf/conf.d/cryptroot /conf/conf.d/cryptroot.mandos-old
+ mv /conf/conf.d/cryptroot.mandos /conf/conf.d/cryptroot
+else
+ rm /conf/conf.d/cryptroot.mandos
+fi
=== removed file '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/.
+
+
=== added file 'mandos'
--- mandos 1970-01-01 00:00:00 +0000
+++ mandos 2009-08-30 03:10:29 +0000
@@ -0,0 +1,1501 @@
+#!/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", "remove", "server_state_changed",
+# "entry_group_state_changed", "cleanup", and "activate" in the
+# "AvahiService" class, and some lines in "main".
+#
+# Everything else is
+# Copyright © 2008,2009 Teddy Hogeborn
+# Copyright © 2008,2009 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, with_statement, absolute_import
+
+import SocketServer as socketserver
+import socket
+import optparse
+import datetime
+import errno
+import gnutls.crypto
+import gnutls.connection
+import gnutls.errors
+import gnutls.library.functions
+import gnutls.library.constants
+import gnutls.library.types
+import ConfigParser as configparser
+import sys
+import re
+import os
+import signal
+import subprocess
+import atexit
+import stat
+import logging
+import logging.handlers
+import pwd
+from contextlib import closing
+import struct
+import fcntl
+import functools
+
+import dbus
+import dbus.service
+import gobject
+import avahi
+from dbus.mainloop.glib import DBusGMainLoop
+import ctypes
+import ctypes.util
+
+try:
+ SO_BINDTODEVICE = socket.SO_BINDTODEVICE
+except AttributeError:
+ try:
+ from IN import SO_BINDTODEVICE
+ except ImportError:
+ SO_BINDTODEVICE = None
+
+
+version = "1.0.11"
+
+logger = logging.Logger(u'mandos')
+syslogger = (logging.handlers.SysLogHandler
+ (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
+ address = "/dev/log"))
+syslogger.setFormatter(logging.Formatter
+ (u'Mandos [%(process)d]: %(levelname)s:'
+ u' %(message)s'))
+logger.addHandler(syslogger)
+
+console = logging.StreamHandler()
+console.setFormatter(logging.Formatter(u'%(name)s [%(process)d]:'
+ u' %(levelname)s:'
+ u' %(message)s'))
+logger.addHandler(console)
+
+class AvahiError(Exception):
+ def __init__(self, value, *args, **kwargs):
+ self.value = value
+ super(AvahiError, self).__init__(value, *args, **kwargs)
+ def __unicode__(self):
+ return unicode(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: u'Mandos'
+ type: string; Example: u'_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
+ group: D-Bus Entry Group
+ server: D-Bus Server
+ bus: dbus.SystemBus()
+ """
+ def __init__(self, interface = avahi.IF_UNSPEC, name = None,
+ servicetype = None, port = None, TXT = None,
+ domain = u"", host = u"", max_renames = 32768,
+ protocol = avahi.PROTO_UNSPEC, bus = None):
+ self.interface = interface
+ self.name = name
+ self.type = servicetype
+ self.port = port
+ self.TXT = TXT if TXT is not None else []
+ self.domain = domain
+ self.host = host
+ self.rename_count = 0
+ self.max_renames = max_renames
+ self.protocol = protocol
+ self.group = None # our entry group
+ self.server = None
+ self.bus = bus
+ def rename(self):
+ """Derived from the Avahi example code"""
+ if self.rename_count >= self.max_renames:
+ logger.critical(u"No suitable Zeroconf service name found"
+ u" after %i retries, exiting.",
+ self.rename_count)
+ raise AvahiServiceError(u"Too many renames")
+ self.name = self.server.GetAlternativeServiceName(self.name)
+ logger.info(u"Changing Zeroconf service name to %r ...",
+ unicode(self.name))
+ syslogger.setFormatter(logging.Formatter
+ (u'Mandos (%s) [%%(process)d]:'
+ u' %%(levelname)s: %%(message)s'
+ % self.name))
+ self.remove()
+ self.add()
+ self.rename_count += 1
+ def remove(self):
+ """Derived from the Avahi example code"""
+ if self.group is not None:
+ self.group.Reset()
+ def add(self):
+ """Derived from the Avahi example code"""
+ if self.group is None:
+ self.group = dbus.Interface(
+ self.bus.get_object(avahi.DBUS_NAME,
+ self.server.EntryGroupNew()),
+ avahi.DBUS_INTERFACE_ENTRY_GROUP)
+ self.group.connect_to_signal('StateChanged',
+ self.entry_group_state_changed)
+ logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
+ self.name, self.type)
+ self.group.AddService(
+ self.interface,
+ self.protocol,
+ dbus.UInt32(0), # flags
+ self.name, self.type,
+ self.domain, self.host,
+ dbus.UInt16(self.port),
+ avahi.string_array_to_txt_array(self.TXT))
+ self.group.Commit()
+ def entry_group_state_changed(self, state, error):
+ """Derived from the Avahi example code"""
+ logger.debug(u"Avahi state change: %i", state)
+
+ if state == avahi.ENTRY_GROUP_ESTABLISHED:
+ logger.debug(u"Zeroconf service established.")
+ elif state == avahi.ENTRY_GROUP_COLLISION:
+ logger.warning(u"Zeroconf service name collision.")
+ self.rename()
+ elif state == avahi.ENTRY_GROUP_FAILURE:
+ logger.critical(u"Avahi: Error in group state changed %s",
+ unicode(error))
+ raise AvahiGroupError(u"State changed: %s"
+ % unicode(error))
+ def cleanup(self):
+ """Derived from the Avahi example code"""
+ if self.group is not None:
+ self.group.Free()
+ self.group = None
+ def server_state_changed(self, state):
+ """Derived from the Avahi example code"""
+ if state == avahi.SERVER_COLLISION:
+ logger.error(u"Zeroconf server name collision")
+ self.remove()
+ elif state == avahi.SERVER_RUNNING:
+ self.add()
+ def activate(self):
+ """Derived from the Avahi example code"""
+ if self.server is None:
+ self.server = dbus.Interface(
+ self.bus.get_object(avahi.DBUS_NAME,
+ avahi.DBUS_PATH_SERVER),
+ avahi.DBUS_INTERFACE_SERVER)
+ self.server.connect_to_signal(u"StateChanged",
+ self.server_state_changed)
+ self.server_state_changed(self.server.GetState())
+
+
+class Client(object):
+ """A representation of a client host served by this server.
+
+ Attributes:
+ name: string; from the config file, used in log messages and
+ D-Bus identifiers
+ 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(); (UTC) object creation
+ last_enabled: datetime.datetime(); (UTC)
+ enabled: bool()
+ last_checked_ok: datetime.datetime(); (UTC) or None
+ timeout: datetime.timedelta(); How long from last_checked_ok
+ until this client is invalid
+ interval: datetime.timedelta(); How often to start a new checker
+ disable_hook: If set, called by disable() as disable_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
+ disable_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.
+ current_checker_command: string; current running checker_command
+ """
+
+ @staticmethod
+ def _datetime_to_milliseconds(dt):
+ "Convert a datetime.datetime() to milliseconds"
+ return ((dt.days * 24 * 60 * 60 * 1000)
+ + (dt.seconds * 1000)
+ + (dt.microseconds // 1000))
+
+ def timeout_milliseconds(self):
+ "Return the 'timeout' attribute in milliseconds"
+ return self._datetime_to_milliseconds(self.timeout)
+
+ def interval_milliseconds(self):
+ "Return the 'interval' attribute in milliseconds"
+ return self._datetime_to_milliseconds(self.interval)
+
+ def __init__(self, name = None, disable_hook=None, config=None):
+ """Note: the 'checker' key in 'config' sets the
+ 'checker_command' attribute and *not* the 'checker'
+ attribute."""
+ self.name = name
+ if config is None:
+ config = {}
+ logger.debug(u"Creating client %r", self.name)
+ # Uppercase and remove spaces from fingerprint for later
+ # comparison purposes with return value from the fingerprint()
+ # function
+ self.fingerprint = (config[u"fingerprint"].upper()
+ .replace(u" ", u""))
+ logger.debug(u" Fingerprint: %s", self.fingerprint)
+ if u"secret" in config:
+ self.secret = config[u"secret"].decode(u"base64")
+ elif u"secfile" in config:
+ with closing(open(os.path.expanduser
+ (os.path.expandvars
+ (config[u"secfile"])))) as secfile:
+ self.secret = secfile.read()
+ else:
+ raise TypeError(u"No secret or secfile for client %s"
+ % self.name)
+ self.host = config.get(u"host", u"")
+ self.created = datetime.datetime.utcnow()
+ self.enabled = False
+ self.last_enabled = None
+ self.last_checked_ok = None
+ self.timeout = string_to_delta(config[u"timeout"])
+ self.interval = string_to_delta(config[u"interval"])
+ self.disable_hook = disable_hook
+ self.checker = None
+ self.checker_initiator_tag = None
+ self.disable_initiator_tag = None
+ self.checker_callback_tag = None
+ self.checker_command = config[u"checker"]
+ self.current_checker_command = None
+ self.last_connect = None
+
+ def enable(self):
+ """Start this client's checker and timeout hooks"""
+ if getattr(self, u"enabled", False):
+ # Already enabled
+ return
+ self.last_enabled = datetime.datetime.utcnow()
+ # Schedule a new checker to be started an 'interval' from now,
+ # and every interval from then on.
+ self.checker_initiator_tag = (gobject.timeout_add
+ (self.interval_milliseconds(),
+ self.start_checker))
+ # Also start a new checker *right now*.
+ self.start_checker()
+ # Schedule a disable() when 'timeout' has passed
+ self.disable_initiator_tag = (gobject.timeout_add
+ (self.timeout_milliseconds(),
+ self.disable))
+ self.enabled = True
+
+ def disable(self):
+ """Disable this client."""
+ if not getattr(self, "enabled", False):
+ return False
+ logger.info(u"Disabling client %s", self.name)
+ if getattr(self, u"disable_initiator_tag", False):
+ gobject.source_remove(self.disable_initiator_tag)
+ self.disable_initiator_tag = None
+ if getattr(self, u"checker_initiator_tag", False):
+ gobject.source_remove(self.checker_initiator_tag)
+ self.checker_initiator_tag = None
+ self.stop_checker()
+ if self.disable_hook:
+ self.disable_hook(self)
+ self.enabled = False
+ # Do not run this again if called by a gobject.timeout_add
+ return False
+
+ def __del__(self):
+ self.disable_hook = None
+ self.disable()
+
+ def checker_callback(self, pid, condition, command):
+ """The checker has completed, so take appropriate actions."""
+ self.checker_callback_tag = None
+ self.checker = None
+ if os.WIFEXITED(condition):
+ exitstatus = os.WEXITSTATUS(condition)
+ if exitstatus == 0:
+ logger.info(u"Checker for %(name)s succeeded",
+ vars(self))
+ self.checked_ok()
+ else:
+ logger.info(u"Checker for %(name)s failed",
+ vars(self))
+ else:
+ logger.warning(u"Checker for %(name)s crashed?",
+ vars(self))
+
+ def checked_ok(self):
+ """Bump up the timeout for this client.
+
+ This should only be called when the client has been seen,
+ alive and well.
+ """
+ self.last_checked_ok = datetime.datetime.utcnow()
+ gobject.source_remove(self.disable_initiator_tag)
+ self.disable_initiator_tag = (gobject.timeout_add
+ (self.timeout_milliseconds(),
+ self.disable))
+
+ def 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 a checker exists, make sure it is not a zombie
+ if self.checker is not None:
+ pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
+ if pid:
+ logger.warning(u"Checker was a zombie")
+ gobject.source_remove(self.checker_callback_tag)
+ self.checker_callback(pid, status,
+ self.current_checker_command)
+ # Start a new checker if needed
+ if self.checker is None:
+ try:
+ # In case checker_command has exactly one % operator
+ command = self.checker_command % self.host
+ except TypeError:
+ # Escape attributes for the shell
+ escaped_attrs = dict((key,
+ re.escape(unicode(str(val),
+ errors=
+ u'replace')))
+ for key, val in
+ vars(self).iteritems())
+ try:
+ command = self.checker_command % escaped_attrs
+ except TypeError, error:
+ logger.error(u'Could not format string "%s":'
+ u' %s', self.checker_command, error)
+ return True # Try again later
+ self.current_checker_command = command
+ try:
+ logger.info(u"Starting checker %r for %s",
+ command, self.name)
+ # We don't need to redirect stdout and stderr, since
+ # in normal mode, that is already done by daemon(),
+ # and in debug mode we don't want to. (Stdin is
+ # always replaced by /dev/null.)
+ self.checker = subprocess.Popen(command,
+ close_fds=True,
+ shell=True, cwd=u"/")
+ self.checker_callback_tag = (gobject.child_watch_add
+ (self.checker.pid,
+ self.checker_callback,
+ data=command))
+ # The checker may have completed before the gobject
+ # watch was added. Check for this.
+ pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
+ if pid:
+ gobject.source_remove(self.checker_callback_tag)
+ self.checker_callback(pid, status, command)
+ except OSError, error:
+ logger.error(u"Failed to start subprocess: %s",
+ error)
+ # Re-run this periodically if run by gobject.timeout_add
+ return True
+
+ def stop_checker(self):
+ """Force the checker process, if any, to stop."""
+ if self.checker_callback_tag:
+ gobject.source_remove(self.checker_callback_tag)
+ self.checker_callback_tag = None
+ if getattr(self, u"checker", None) is None:
+ return
+ logger.debug(u"Stopping checker for %(name)s", vars(self))
+ try:
+ 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?"""
+ if not getattr(self, u"enabled", False):
+ return False
+ now = datetime.datetime.utcnow()
+ if self.last_checked_ok is None:
+ return now < (self.created + self.timeout)
+ else:
+ return now < (self.last_checked_ok + self.timeout)
+
+
+class ClientDBus(Client, dbus.service.Object):
+ """A Client class using D-Bus
+
+ Attributes:
+ dbus_object_path: dbus.ObjectPath
+ bus: dbus.SystemBus()
+ """
+ # dbus.service.Object doesn't use super(), so we can't either.
+
+ def __init__(self, bus = None, *args, **kwargs):
+ self.bus = bus
+ Client.__init__(self, *args, **kwargs)
+ # Only now, when this client is initialized, can it show up on
+ # the D-Bus
+ self.dbus_object_path = (dbus.ObjectPath
+ (u"/clients/"
+ + self.name.replace(u".", u"_")))
+ dbus.service.Object.__init__(self, self.bus,
+ self.dbus_object_path)
+
+ @staticmethod
+ def _datetime_to_dbus(dt, variant_level=0):
+ """Convert a UTC datetime.datetime() to a D-Bus type."""
+ return dbus.String(dt.isoformat(),
+ variant_level=variant_level)
+
+ def enable(self):
+ oldstate = getattr(self, u"enabled", False)
+ r = Client.enable(self)
+ if oldstate != self.enabled:
+ # Emit D-Bus signals
+ self.PropertyChanged(dbus.String(u"enabled"),
+ dbus.Boolean(True, variant_level=1))
+ self.PropertyChanged(
+ dbus.String(u"last_enabled"),
+ self._datetime_to_dbus(self.last_enabled,
+ variant_level=1))
+ return r
+
+ def disable(self, signal = True):
+ oldstate = getattr(self, u"enabled", False)
+ r = Client.disable(self)
+ if signal and oldstate != self.enabled:
+ # Emit D-Bus signal
+ self.PropertyChanged(dbus.String(u"enabled"),
+ dbus.Boolean(False, variant_level=1))
+ return r
+
+ def __del__(self, *args, **kwargs):
+ try:
+ self.remove_from_connection()
+ except LookupError:
+ pass
+ if hasattr(dbus.service.Object, u"__del__"):
+ dbus.service.Object.__del__(self, *args, **kwargs)
+ Client.__del__(self, *args, **kwargs)
+
+ def checker_callback(self, pid, condition, command,
+ *args, **kwargs):
+ self.checker_callback_tag = None
+ self.checker = None
+ # Emit D-Bus signal
+ self.PropertyChanged(dbus.String(u"checker_running"),
+ dbus.Boolean(False, variant_level=1))
+ if os.WIFEXITED(condition):
+ exitstatus = os.WEXITSTATUS(condition)
+ # Emit D-Bus signal
+ self.CheckerCompleted(dbus.Int16(exitstatus),
+ dbus.Int64(condition),
+ dbus.String(command))
+ else:
+ # Emit D-Bus signal
+ self.CheckerCompleted(dbus.Int16(-1),
+ dbus.Int64(condition),
+ dbus.String(command))
+
+ return Client.checker_callback(self, pid, condition, command,
+ *args, **kwargs)
+
+ def checked_ok(self, *args, **kwargs):
+ r = Client.checked_ok(self, *args, **kwargs)
+ # Emit D-Bus signal
+ self.PropertyChanged(
+ dbus.String(u"last_checked_ok"),
+ (self._datetime_to_dbus(self.last_checked_ok,
+ variant_level=1)))
+ return r
+
+ def start_checker(self, *args, **kwargs):
+ old_checker = self.checker
+ if self.checker is not None:
+ old_checker_pid = self.checker.pid
+ else:
+ old_checker_pid = None
+ r = Client.start_checker(self, *args, **kwargs)
+ # Only if new checker process was started
+ if (self.checker is not None
+ and old_checker_pid != self.checker.pid):
+ # Emit D-Bus signal
+ self.CheckerStarted(self.current_checker_command)
+ self.PropertyChanged(
+ dbus.String(u"checker_running"),
+ dbus.Boolean(True, variant_level=1))
+ return r
+
+ def stop_checker(self, *args, **kwargs):
+ old_checker = getattr(self, u"checker", None)
+ r = Client.stop_checker(self, *args, **kwargs)
+ if (old_checker is not None
+ and getattr(self, u"checker", None) is None):
+ self.PropertyChanged(dbus.String(u"checker_running"),
+ dbus.Boolean(False, variant_level=1))
+ return r
+
+ ## D-Bus methods & signals
+ _interface = u"se.bsnet.fukt.Mandos.Client"
+
+ # CheckedOK - method
+ @dbus.service.method(_interface)
+ def CheckedOK(self):
+ return self.checked_ok()
+
+ # CheckerCompleted - signal
+ @dbus.service.signal(_interface, signature=u"nxs")
+ def CheckerCompleted(self, exitcode, waitstatus, command):
+ "D-Bus signal"
+ pass
+
+ # CheckerStarted - signal
+ @dbus.service.signal(_interface, signature=u"s")
+ def CheckerStarted(self, command):
+ "D-Bus signal"
+ pass
+
+ # GetAllProperties - method
+ @dbus.service.method(_interface, out_signature=u"a{sv}")
+ def GetAllProperties(self):
+ "D-Bus method"
+ return dbus.Dictionary({
+ dbus.String(u"name"):
+ dbus.String(self.name, variant_level=1),
+ dbus.String(u"fingerprint"):
+ dbus.String(self.fingerprint, variant_level=1),
+ dbus.String(u"host"):
+ dbus.String(self.host, variant_level=1),
+ dbus.String(u"created"):
+ self._datetime_to_dbus(self.created,
+ variant_level=1),
+ dbus.String(u"last_enabled"):
+ (self._datetime_to_dbus(self.last_enabled,
+ variant_level=1)
+ if self.last_enabled is not None
+ else dbus.Boolean(False, variant_level=1)),
+ dbus.String(u"enabled"):
+ dbus.Boolean(self.enabled, variant_level=1),
+ dbus.String(u"last_checked_ok"):
+ (self._datetime_to_dbus(self.last_checked_ok,
+ variant_level=1)
+ if self.last_checked_ok is not None
+ else dbus.Boolean (False, variant_level=1)),
+ dbus.String(u"timeout"):
+ dbus.UInt64(self.timeout_milliseconds(),
+ variant_level=1),
+ dbus.String(u"interval"):
+ dbus.UInt64(self.interval_milliseconds(),
+ variant_level=1),
+ dbus.String(u"checker"):
+ dbus.String(self.checker_command,
+ variant_level=1),
+ dbus.String(u"checker_running"):
+ dbus.Boolean(self.checker is not None,
+ variant_level=1),
+ dbus.String(u"object_path"):
+ dbus.ObjectPath(self.dbus_object_path,
+ variant_level=1)
+ }, signature=u"sv")
+
+ # IsStillValid - method
+ @dbus.service.method(_interface, out_signature=u"b")
+ def IsStillValid(self):
+ return self.still_valid()
+
+ # PropertyChanged - signal
+ @dbus.service.signal(_interface, signature=u"sv")
+ def PropertyChanged(self, property, value):
+ "D-Bus signal"
+ pass
+
+ # ReceivedSecret - signal
+ @dbus.service.signal(_interface)
+ def ReceivedSecret(self):
+ "D-Bus signal"
+ pass
+
+ # Rejected - signal
+ @dbus.service.signal(_interface)
+ def Rejected(self):
+ "D-Bus signal"
+ pass
+
+ # SetChecker - method
+ @dbus.service.method(_interface, in_signature=u"s")
+ def SetChecker(self, checker):
+ "D-Bus setter method"
+ self.checker_command = checker
+ # Emit D-Bus signal
+ self.PropertyChanged(dbus.String(u"checker"),
+ dbus.String(self.checker_command,
+ variant_level=1))
+
+ # SetHost - method
+ @dbus.service.method(_interface, in_signature=u"s")
+ def SetHost(self, host):
+ "D-Bus setter method"
+ self.host = host
+ # Emit D-Bus signal
+ self.PropertyChanged(dbus.String(u"host"),
+ dbus.String(self.host, variant_level=1))
+
+ # SetInterval - method
+ @dbus.service.method(_interface, in_signature=u"t")
+ def SetInterval(self, milliseconds):
+ self.interval = datetime.timedelta(0, 0, 0, milliseconds)
+ # Emit D-Bus signal
+ self.PropertyChanged(dbus.String(u"interval"),
+ (dbus.UInt64(self.interval_milliseconds(),
+ variant_level=1)))
+
+ # SetSecret - method
+ @dbus.service.method(_interface, in_signature=u"ay",
+ byte_arrays=True)
+ def SetSecret(self, secret):
+ "D-Bus setter method"
+ self.secret = str(secret)
+
+ # SetTimeout - method
+ @dbus.service.method(_interface, in_signature=u"t")
+ def SetTimeout(self, milliseconds):
+ self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
+ # Emit D-Bus signal
+ self.PropertyChanged(dbus.String(u"timeout"),
+ (dbus.UInt64(self.timeout_milliseconds(),
+ variant_level=1)))
+
+ # Enable - method
+ @dbus.service.method(_interface)
+ def Enable(self):
+ "D-Bus method"
+ self.enable()
+
+ # StartChecker - method
+ @dbus.service.method(_interface)
+ def StartChecker(self):
+ "D-Bus method"
+ self.start_checker()
+
+ # Disable - method
+ @dbus.service.method(_interface)
+ def Disable(self):
+ "D-Bus method"
+ self.disable()
+
+ # StopChecker - method
+ @dbus.service.method(_interface)
+ def StopChecker(self):
+ self.stop_checker()
+
+ del _interface
+
+
+class ClientHandler(socketserver.BaseRequestHandler, object):
+ """A class to handle client connections.
+
+ Instantiated once for each connection to handle it.
+ Note: This will run in its own forked process."""
+
+ def handle(self):
+ logger.info(u"TCP connection from: %s",
+ unicode(self.client_address))
+ logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
+ # Open IPC pipe to parent process
+ with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
+ 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 = u':'.join((u"NONE", u"+VERS-TLS1.1",
+ # u"+AES-256-CBC", u"+SHA1",
+ # u"+COMP-NULL", u"+CTYPE-OPENPGP",
+ # u"+DHE-DSS"))
+ # Use a fallback default, since this MUST be set.
+ priority = self.server.gnutls_priority
+ if priority is None:
+ priority = u"NORMAL"
+ (gnutls.library.functions
+ .gnutls_priority_set_direct(session._c_object,
+ priority, None))
+
+ try:
+ session.handshake()
+ except gnutls.errors.GNUTLSError, error:
+ logger.warning(u"Handshake failed: %s", error)
+ # Do not run session.bye() here: the session is not
+ # established. Just abandon the request.
+ return
+ logger.debug(u"Handshake succeeded")
+ try:
+ fpr = self.fingerprint(self.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)
+
+ for c in self.server.clients:
+ if c.fingerprint == fpr:
+ client = c
+ break
+ else:
+ ipc.write(u"NOTFOUND %s %s\n"
+ % (fpr, unicode(self.client_address)))
+ 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():
+ ipc.write(u"INVALID %s\n" % client.name)
+ session.bye()
+ return
+ ipc.write(u"SENDING %s\n" % client.name)
+ 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()
+
+ @staticmethod
+ def peer_certificate(session):
+ "Return the peer's OpenPGP certificate as a bytestring"
+ # If not an OpenPGP certificate...
+ if (gnutls.library.functions
+ .gnutls_certificate_type_get(session._c_object)
+ != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
+ # ...do the normal thing
+ return session.peer_certificate
+ list_size = ctypes.c_uint(1)
+ cert_list = (gnutls.library.functions
+ .gnutls_certificate_get_peers
+ (session._c_object, ctypes.byref(list_size)))
+ if not bool(cert_list) and list_size.value != 0:
+ raise gnutls.errors.GNUTLSError(u"error getting peer"
+ u" certificate")
+ if list_size.value == 0:
+ return None
+ cert = cert_list[0]
+ return ctypes.string_at(cert.data, cert.size)
+
+ @staticmethod
+ def fingerprint(openpgp):
+ "Convert an OpenPGP bytestring to a hexdigit fingerprint"
+ # New GnuTLS "datum" with the OpenPGP public key
+ datum = (gnutls.library.types
+ .gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
+ ctypes.POINTER
+ (ctypes.c_ubyte)),
+ ctypes.c_uint(len(openpgp))))
+ # New empty GnuTLS certificate
+ crt = gnutls.library.types.gnutls_openpgp_crt_t()
+ (gnutls.library.functions
+ .gnutls_openpgp_crt_init(ctypes.byref(crt)))
+ # Import the OpenPGP public key into the certificate
+ (gnutls.library.functions
+ .gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
+ gnutls.library.constants
+ .GNUTLS_OPENPGP_FMT_RAW))
+ # Verify the self signature in the key
+ crtverify = ctypes.c_uint()
+ (gnutls.library.functions
+ .gnutls_openpgp_crt_verify_self(crt, 0,
+ ctypes.byref(crtverify)))
+ if crtverify.value != 0:
+ gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
+ raise (gnutls.errors.CertificateSecurityError
+ (u"Verify failed"))
+ # New buffer for the fingerprint
+ buf = ctypes.create_string_buffer(20)
+ buf_len = ctypes.c_size_t()
+ # Get the fingerprint from the certificate into the buffer
+ (gnutls.library.functions
+ .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
+ ctypes.byref(buf_len)))
+ # Deinit the certificate
+ gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
+ # Convert the buffer to a Python bytestring
+ fpr = ctypes.string_at(buf, buf_len.value)
+ # Convert the bytestring to hexadecimal notation
+ hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
+ return hex_fpr
+
+
+class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
+ """Like socketserver.ForkingMixIn, but also pass a pipe."""
+ def process_request(self, request, client_address):
+ """Overrides and wraps the original process_request().
+
+ This function creates a new pipe in self.pipe
+ """
+ self.pipe = os.pipe()
+ super(ForkingMixInWithPipe,
+ self).process_request(request, client_address)
+ os.close(self.pipe[1]) # close write end
+ self.add_pipe(self.pipe[0])
+ def add_pipe(self, pipe):
+ """Dummy function; override as necessary"""
+ os.close(pipe)
+
+
+class IPv6_TCPServer(ForkingMixInWithPipe,
+ socketserver.TCPServer, object):
+ """IPv6-capable TCP server. Accepts 'None' as address and/or port
+
+ Attributes:
+ enabled: Boolean; whether this server is activated yet
+ interface: None or a network interface name (string)
+ use_ipv6: Boolean; to use IPv6 or not
+ """
+ def __init__(self, server_address, RequestHandlerClass,
+ interface=None, use_ipv6=True):
+ self.interface = interface
+ if use_ipv6:
+ self.address_family = socket.AF_INET6
+ socketserver.TCPServer.__init__(self, server_address,
+ RequestHandlerClass)
+ def server_bind(self):
+ """This overrides the normal server_bind() function
+ to bind to an interface if one was specified, and also NOT to
+ bind to an address or port if they were not specified."""
+ if self.interface is not None:
+ if SO_BINDTODEVICE is None:
+ logger.error(u"SO_BINDTODEVICE does not exist;"
+ u" cannot bind to interface %s",
+ self.interface)
+ else:
+ try:
+ self.socket.setsockopt(socket.SOL_SOCKET,
+ SO_BINDTODEVICE,
+ str(self.interface
+ + u'\0'))
+ except socket.error, error:
+ if error[0] == errno.EPERM:
+ logger.error(u"No permission to"
+ u" bind to interface %s",
+ self.interface)
+ elif error[0] == errno.ENOPROTOOPT:
+ logger.error(u"SO_BINDTODEVICE not available;"
+ u" cannot bind to interface %s",
+ self.interface)
+ else:
+ raise
+ # Only bind(2) the socket if we really need to.
+ if self.server_address[0] or self.server_address[1]:
+ if not self.server_address[0]:
+ if self.address_family == socket.AF_INET6:
+ any_address = u"::" # in6addr_any
+ else:
+ any_address = socket.INADDR_ANY
+ self.server_address = (any_address,
+ self.server_address[1])
+ elif not self.server_address[1]:
+ self.server_address = (self.server_address[0],
+ 0)
+# if self.interface:
+# self.server_address = (self.server_address[0],
+# 0, # port
+# 0, # flowinfo
+# if_nametoindex
+# (self.interface))
+ return socketserver.TCPServer.server_bind(self)
+
+
+class MandosServer(IPv6_TCPServer):
+ """Mandos server.
+
+ Attributes:
+ clients: set of Client objects
+ gnutls_priority GnuTLS priority string
+ use_dbus: Boolean; to emit D-Bus signals or not
+ clients: set of Client objects
+ gnutls_priority GnuTLS priority string
+ use_dbus: Boolean; to emit D-Bus signals or not
+
+ Assumes a gobject.MainLoop event loop.
+ """
+ def __init__(self, server_address, RequestHandlerClass,
+ interface=None, use_ipv6=True, clients=None,
+ gnutls_priority=None, use_dbus=True):
+ self.enabled = False
+ self.clients = clients
+ if self.clients is None:
+ self.clients = set()
+ self.use_dbus = use_dbus
+ self.gnutls_priority = gnutls_priority
+ IPv6_TCPServer.__init__(self, server_address,
+ RequestHandlerClass,
+ interface = interface,
+ use_ipv6 = use_ipv6)
+ def server_activate(self):
+ if self.enabled:
+ return socketserver.TCPServer.server_activate(self)
+ def enable(self):
+ self.enabled = True
+ def add_pipe(self, pipe):
+ # Call "handle_ipc" for both data and EOF events
+ gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
+ self.handle_ipc)
+ def handle_ipc(self, source, condition, file_objects={}):
+ condition_names = {
+ gobject.IO_IN: u"IN", # There is data to read.
+ gobject.IO_OUT: u"OUT", # Data can be written (without
+ # blocking).
+ gobject.IO_PRI: u"PRI", # There is urgent data to read.
+ gobject.IO_ERR: u"ERR", # Error condition.
+ gobject.IO_HUP: u"HUP" # Hung up (the connection has been
+ # broken, usually for pipes and
+ # sockets).
+ }
+ conditions_string = ' | '.join(name
+ for cond, name in
+ condition_names.iteritems()
+ if cond & condition)
+ logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
+ conditions_string)
+
+ # Turn the pipe file descriptor into a Python file object
+ if source not in file_objects:
+ file_objects[source] = os.fdopen(source, u"r", 1)
+
+ # Read a line from the file object
+ cmdline = file_objects[source].readline()
+ if not cmdline: # Empty line means end of file
+ # close the IPC pipe
+ file_objects[source].close()
+ del file_objects[source]
+
+ # Stop calling this function
+ return False
+
+ logger.debug(u"IPC command: %r", cmdline)
+
+ # Parse and act on command
+ cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
+
+ if cmd == u"NOTFOUND":
+ logger.warning(u"Client not found for fingerprint: %s",
+ args)
+ if self.use_dbus:
+ # Emit D-Bus signal
+ mandos_dbus_service.ClientNotFound(args)
+ elif cmd == u"INVALID":
+ for client in self.clients:
+ if client.name == args:
+ logger.warning(u"Client %s is invalid", args)
+ if self.use_dbus:
+ # Emit D-Bus signal
+ client.Rejected()
+ break
+ else:
+ logger.error(u"Unknown client %s is invalid", args)
+ elif cmd == u"SENDING":
+ for client in self.clients:
+ if client.name == args:
+ logger.info(u"Sending secret to %s", client.name)
+ client.checked_ok()
+ if self.use_dbus:
+ # Emit D-Bus signal
+ client.ReceivedSecret()
+ break
+ else:
+ logger.error(u"Sending secret to unknown client %s",
+ args)
+ else:
+ logger.error(u"Unknown IPC command: %r", cmdline)
+
+ # Keep calling this function
+ return True
+
+
+def string_to_delta(interval):
+ """Parse a string and return a datetime.timedelta
+
+ >>> string_to_delta(u'7d')
+ datetime.timedelta(7)
+ >>> string_to_delta(u'60s')
+ datetime.timedelta(0, 60)
+ >>> string_to_delta(u'60m')
+ datetime.timedelta(0, 3600)
+ >>> string_to_delta(u'24h')
+ datetime.timedelta(1)
+ >>> string_to_delta(u'1w')
+ datetime.timedelta(7)
+ >>> string_to_delta(u'5m 30s')
+ datetime.timedelta(0, 330)
+ """
+ timevalue = datetime.timedelta(0)
+ for s in interval.split():
+ try:
+ suffix = unicode(s[-1])
+ value = int(s[:-1])
+ if suffix == u"d":
+ delta = datetime.timedelta(value)
+ elif suffix == u"s":
+ delta = datetime.timedelta(0, value)
+ elif suffix == u"m":
+ delta = datetime.timedelta(0, 0, 0, 0, value)
+ elif suffix == u"h":
+ delta = datetime.timedelta(0, 0, 0, 0, 0, value)
+ elif suffix == u"w":
+ delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
+ else:
+ raise ValueError
+ except (ValueError, IndexError):
+ raise ValueError
+ timevalue += delta
+ return timevalue
+
+
+def if_nametoindex(interface):
+ """Call the C function if_nametoindex(), or equivalent
+
+ Note: This function cannot accept a unicode string."""
+ global if_nametoindex
+ try:
+ if_nametoindex = (ctypes.cdll.LoadLibrary
+ (ctypes.util.find_library(u"c"))
+ .if_nametoindex)
+ except (OSError, AttributeError):
+ logger.warning(u"Doing if_nametoindex the hard way")
+ def if_nametoindex(interface):
+ "Get an interface index the hard way, i.e. using fcntl()"
+ SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
+ with closing(socket.socket()) as s:
+ ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
+ struct.pack(str(u"16s16x"),
+ interface))
+ interface_index = struct.unpack(str(u"I"),
+ ifreq[16:20])[0]
+ return interface_index
+ return if_nametoindex(interface)
+
+
+def daemon(nochdir = False, noclose = False):
+ """See daemon(3). Standard BSD Unix function.
+
+ This should really exist as os.daemon, but it doesn't (yet)."""
+ if os.fork():
+ sys.exit()
+ os.setsid()
+ if not nochdir:
+ os.chdir(u"/")
+ 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,
+ u"/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():
+
+ ######################################################################
+ # Parsing of options, both command line and config file
+
+ parser = optparse.OptionParser(version = "%%prog %s" % version)
+ parser.add_option("-i", u"--interface", type=u"string",
+ metavar="IF", help=u"Bind to interface IF")
+ parser.add_option("-a", u"--address", type=u"string",
+ help=u"Address to listen for requests on")
+ parser.add_option("-p", u"--port", type=u"int",
+ help=u"Port number to receive requests on")
+ parser.add_option("--check", action=u"store_true",
+ help=u"Run self-test")
+ parser.add_option("--debug", action=u"store_true",
+ help=u"Debug mode; run in foreground and log to"
+ u" terminal")
+ parser.add_option("--priority", type=u"string", help=u"GnuTLS"
+ u" priority string (see GnuTLS documentation)")
+ parser.add_option("--servicename", type=u"string",
+ metavar=u"NAME", help=u"Zeroconf service name")
+ parser.add_option("--configdir", type=u"string",
+ default=u"/etc/mandos", metavar=u"DIR",
+ help=u"Directory to search for configuration"
+ u" files")
+ parser.add_option("--no-dbus", action=u"store_false",
+ dest=u"use_dbus", help=u"Do not provide D-Bus"
+ u" system bus interface")
+ parser.add_option("--no-ipv6", action=u"store_false",
+ dest=u"use_ipv6", help=u"Do not use IPv6")
+ options = parser.parse_args()[0]
+
+ if options.check:
+ import doctest
+ doctest.testmod()
+ sys.exit()
+
+ # Default values for config file for server-global settings
+ server_defaults = { u"interface": u"",
+ u"address": u"",
+ u"port": u"",
+ u"debug": u"False",
+ u"priority":
+ u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
+ u"servicename": u"Mandos",
+ u"use_dbus": u"True",
+ u"use_ipv6": u"True",
+ }
+
+ # Parse config file for server-global settings
+ server_config = configparser.SafeConfigParser(server_defaults)
+ del server_defaults
+ server_config.read(os.path.join(options.configdir,
+ u"mandos.conf"))
+ # Convert the SafeConfigParser object to a dict
+ server_settings = server_config.defaults()
+ # Use the appropriate methods on the non-string config options
+ for option in (u"debug", u"use_dbus", u"use_ipv6"):
+ server_settings[option] = server_config.getboolean(u"DEFAULT",
+ option)
+ if server_settings["port"]:
+ server_settings["port"] = server_config.getint(u"DEFAULT",
+ u"port")
+ del server_config
+
+ # Override the settings from the config file with command line
+ # options, if set.
+ for option in (u"interface", u"address", u"port", u"debug",
+ u"priority", u"servicename", u"configdir",
+ u"use_dbus", u"use_ipv6"):
+ value = getattr(options, option)
+ if value is not None:
+ server_settings[option] = value
+ del options
+ # Force all strings to be unicode
+ for option in server_settings.keys():
+ if type(server_settings[option]) is str:
+ server_settings[option] = unicode(server_settings[option])
+ # Now we have our good server settings in "server_settings"
+
+ ##################################################################
+
+ # For convenience
+ debug = server_settings[u"debug"]
+ use_dbus = server_settings[u"use_dbus"]
+ use_ipv6 = server_settings[u"use_ipv6"]
+
+ if not debug:
+ syslogger.setLevel(logging.WARNING)
+ console.setLevel(logging.WARNING)
+
+ if server_settings[u"servicename"] != u"Mandos":
+ syslogger.setFormatter(logging.Formatter
+ (u'Mandos (%s) [%%(process)d]:'
+ u' %%(levelname)s: %%(message)s'
+ % server_settings[u"servicename"]))
+
+ # Parse config file with clients
+ client_defaults = { u"timeout": u"1h",
+ u"interval": u"5m",
+ u"checker": u"fping -q -- %%(host)s",
+ u"host": u"",
+ }
+ client_config = configparser.SafeConfigParser(client_defaults)
+ client_config.read(os.path.join(server_settings[u"configdir"],
+ u"clients.conf"))
+
+ global mandos_dbus_service
+ mandos_dbus_service = None
+
+ tcp_server = MandosServer((server_settings[u"address"],
+ server_settings[u"port"]),
+ ClientHandler,
+ interface=server_settings[u"interface"],
+ use_ipv6=use_ipv6,
+ gnutls_priority=
+ server_settings[u"priority"],
+ use_dbus=use_dbus)
+ pidfilename = u"/var/run/mandos.pid"
+ try:
+ pidfile = open(pidfilename, u"w")
+ except IOError:
+ logger.error(u"Could not open file %r", pidfilename)
+
+ try:
+ uid = pwd.getpwnam(u"_mandos").pw_uid
+ gid = pwd.getpwnam(u"_mandos").pw_gid
+ except KeyError:
+ try:
+ uid = pwd.getpwnam(u"mandos").pw_uid
+ gid = pwd.getpwnam(u"mandos").pw_gid
+ except KeyError:
+ try:
+ uid = pwd.getpwnam(u"nobody").pw_uid
+ gid = pwd.getpwnam(u"nobody").pw_gid
+ except KeyError:
+ uid = 65534
+ gid = 65534
+ try:
+ os.setgid(gid)
+ os.setuid(uid)
+ except OSError, error:
+ if error[0] != errno.EPERM:
+ raise error
+
+ # Enable all possible GnuTLS debugging
+ if debug:
+ # "Use a log level over 10 to enable all debugging options."
+ # - GnuTLS manual
+ gnutls.library.functions.gnutls_global_set_log_level(11)
+
+ @gnutls.library.types.gnutls_log_func
+ def debug_gnutls(level, string):
+ logger.debug(u"GnuTLS: %s", string[:-1])
+
+ (gnutls.library.functions
+ .gnutls_global_set_log_function(debug_gnutls))
+
+ global main_loop
+ # From the Avahi example code
+ DBusGMainLoop(set_as_default=True )
+ main_loop = gobject.MainLoop()
+ bus = dbus.SystemBus()
+ # End of Avahi example code
+ if use_dbus:
+ bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
+ protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
+ service = AvahiService(name = server_settings[u"servicename"],
+ servicetype = u"_mandos._tcp",
+ protocol = protocol, bus = bus)
+ if server_settings["interface"]:
+ service.interface = (if_nametoindex
+ (str(server_settings[u"interface"])))
+
+ client_class = Client
+ if use_dbus:
+ client_class = functools.partial(ClientDBus, bus = bus)
+ tcp_server.clients.update(set(
+ client_class(name = section,
+ config= dict(client_config.items(section)))
+ for section in client_config.sections()))
+ if not tcp_server.clients:
+ logger.warning(u"No clients defined")
+
+ 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:
+ with closing(pidfile):
+ pid = os.getpid()
+ pidfile.write(str(pid) + "\n")
+ del pidfile
+ except IOError:
+ logger.error(u"Could not write to file %r with PID %d",
+ pidfilename, pid)
+ except NameError:
+ # "pidfile" was never created
+ pass
+ del pidfilename
+
+ def cleanup():
+ "Cleanup function; run on exit"
+ service.cleanup()
+
+ while tcp_server.clients:
+ client = tcp_server.clients.pop()
+ client.disable_hook = None
+ client.disable()
+
+ 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())
+
+ if use_dbus:
+ class MandosDBusService(dbus.service.Object):
+ """A D-Bus proxy object"""
+ def __init__(self):
+ dbus.service.Object.__init__(self, bus, u"/")
+ _interface = u"se.bsnet.fukt.Mandos"
+
+ @dbus.service.signal(_interface, signature=u"oa{sv}")
+ def ClientAdded(self, objpath, properties):
+ "D-Bus signal"
+ pass
+
+ @dbus.service.signal(_interface, signature=u"s")
+ def ClientNotFound(self, fingerprint):
+ "D-Bus signal"
+ pass
+
+ @dbus.service.signal(_interface, signature=u"os")
+ def ClientRemoved(self, objpath, name):
+ "D-Bus signal"
+ pass
+
+ @dbus.service.method(_interface, out_signature=u"ao")
+ def GetAllClients(self):
+ "D-Bus method"
+ return dbus.Array(c.dbus_object_path
+ for c in tcp_server.clients)
+
+ @dbus.service.method(_interface,
+ out_signature=u"a{oa{sv}}")
+ def GetAllClientsWithProperties(self):
+ "D-Bus method"
+ return dbus.Dictionary(
+ ((c.dbus_object_path, c.GetAllProperties())
+ for c in tcp_server.clients),
+ signature=u"oa{sv}")
+
+ @dbus.service.method(_interface, in_signature=u"o")
+ def RemoveClient(self, object_path):
+ "D-Bus method"
+ for c in tcp_server.clients:
+ if c.dbus_object_path == object_path:
+ tcp_server.clients.remove(c)
+ c.remove_from_connection()
+ # Don't signal anything except ClientRemoved
+ c.disable(signal=False)
+ # Emit D-Bus signal
+ self.ClientRemoved(object_path, c.name)
+ return
+ raise KeyError
+
+ del _interface
+
+ mandos_dbus_service = MandosDBusService()
+
+ for client in tcp_server.clients:
+ if use_dbus:
+ # Emit D-Bus signal
+ mandos_dbus_service.ClientAdded(client.dbus_object_path,
+ client.GetAllProperties())
+ client.enable()
+
+ tcp_server.enable()
+ tcp_server.server_activate()
+
+ # Find out what port we got
+ service.port = tcp_server.socket.getsockname()[1]
+ if use_ipv6:
+ logger.info(u"Now listening on address %r, port %d,"
+ " flowinfo %d, scope_id %d"
+ % tcp_server.socket.getsockname())
+ else: # IPv4
+ logger.info(u"Now listening on address %r, port %d"
+ % tcp_server.socket.getsockname())
+
+ #service.interface = tcp_server.socket.getsockname()[3]
+
+ try:
+ # From the Avahi example code
+ try:
+ service.activate()
+ 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.run()
+ except AvahiError, error:
+ logger.critical(u"AvahiError: %s", error)
+ sys.exit(1)
+ except KeyboardInterrupt:
+ if debug:
+ print >> sys.stderr
+ logger.debug(u"Server received KeyboardInterrupt")
+ logger.debug(u"Server exiting")
+
+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 2009-09-17 01:21:27 +0000
@@ -0,0 +1,404 @@
+
+
+/etc/mandos/clients.conf">
+
+
+%common;
+]>
+
+
+
+ Mandos Manual
+
+ Mandos
+ &version;
+ &TIMESTAMP;
+
+
+ Björn
+ Påhlsson
+
+ belorn@fukt.bsnet.se
+
+
+
+ Teddy
+ Hogeborn
+
+ teddy@fukt.bsnet.se
+
+
+
+
+ 2008
+ 2009
+ 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 either a
+ successful checker run or a client receiving its secret)
+ 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.
+
+
+ File names of the form ~user/foo/bar
+ and $ENVVAR/foo/bar
+ are supported.
+
+
+
+
+
+
+
+
+ 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-ctl'
--- mandos-ctl 1970-01-01 00:00:00 +0000
+++ mandos-ctl 2009-08-30 03:10:29 +0000
@@ -0,0 +1,193 @@
+#!/usr/bin/python
+# -*- mode: python; coding: utf-8 -*-
+
+from __future__ import division
+import sys
+import dbus
+from optparse import OptionParser
+import locale
+import datetime
+import re
+
+locale.setlocale(locale.LC_ALL, u'')
+
+tablewords = {
+ 'name': u'Name',
+ 'enabled': u'Enabled',
+ 'timeout': u'Timeout',
+ 'last_checked_ok': u'Last Successful Check',
+ 'created': u'Created',
+ 'interval': u'Interval',
+ 'host': u'Host',
+ 'fingerprint': u'Fingerprint',
+ 'checker_running': u'Check Is Running',
+ 'last_enabled': u'Last Enabled',
+ 'checker': u'Checker',
+ }
+defaultkeywords = ('name', 'enabled', 'timeout', 'last_checked_ok',
+ 'checker')
+domain = 'se.bsnet.fukt'
+busname = domain + '.Mandos'
+server_path = '/'
+server_interface = domain + '.Mandos'
+client_interface = domain + '.Mandos.Client'
+version = "1.0.11"
+bus = dbus.SystemBus()
+mandos_dbus_objc = bus.get_object(busname, server_path)
+mandos_serv = dbus.Interface(mandos_dbus_objc,
+ dbus_interface = server_interface)
+mandos_clients = mandos_serv.GetAllClientsWithProperties()
+
+def datetime_to_milliseconds(dt):
+ "Return the 'timeout' attribute in milliseconds"
+ return ((dt.days * 24 * 60 * 60 * 1000)
+ + (dt.seconds * 1000)
+ + (dt.microseconds // 1000))
+
+def milliseconds_to_string(ms):
+ td = datetime.timedelta(0, 0, 0, ms)
+ return "%s%02d:%02d:%02d" % (("%dT" % td.days) if td.days else "", # days
+ td.seconds // 3600, # hours
+ (td.seconds % 3600) // 60, # minutes
+ (td.seconds % 60)) # seconds
+
+
+def string_to_delta(interval):
+ """Parse a string and return a datetime.timedelta
+
+ >>> string_to_delta('7d')
+ datetime.timedelta(7)
+ >>> string_to_delta('60s')
+ datetime.timedelta(0, 60)
+ >>> string_to_delta('60m')
+ datetime.timedelta(0, 3600)
+ >>> string_to_delta('24h')
+ datetime.timedelta(1)
+ >>> string_to_delta(u'1w')
+ datetime.timedelta(7)
+ >>> string_to_delta('5m 30s')
+ datetime.timedelta(0, 330)
+ """
+ timevalue = datetime.timedelta(0)
+ regexp = re.compile("\d+[dsmhw]")
+
+ for s in regexp.findall(interval):
+ try:
+ suffix = unicode(s[-1])
+ value = int(s[:-1])
+ if suffix == u"d":
+ delta = datetime.timedelta(value)
+ elif suffix == u"s":
+ delta = datetime.timedelta(0, value)
+ elif suffix == u"m":
+ delta = datetime.timedelta(0, 0, 0, 0, value)
+ elif suffix == u"h":
+ delta = datetime.timedelta(0, 0, 0, 0, 0, value)
+ elif suffix == u"w":
+ delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
+ else:
+ raise ValueError
+ except (ValueError, IndexError):
+ raise ValueError
+ timevalue += delta
+ return timevalue
+
+def print_clients(clients):
+ def valuetostring(value, keyword):
+ if type(value) is dbus.Boolean:
+ return u"Yes" if value else u"No"
+ if keyword in ("timeout", "interval"):
+ return milliseconds_to_string(value)
+ return unicode(value)
+
+ format_string = u' '.join(u'%%-%ds' %
+ max(len(tablewords[key]),
+ max(len(valuetostring(client[key], key))
+ for client in
+ clients))
+ for key in keywords)
+ print format_string % tuple(tablewords[key] for key in keywords)
+ for client in clients:
+ print format_string % tuple(valuetostring(client[key], key)
+ for key in keywords)
+
+parser = OptionParser(version = "%%prog %s" % version)
+parser.add_option("-a", "--all", action="store_true",
+ help="Print all fields")
+parser.add_option("-e", "--enable", action="store_true",
+ help="Enable client")
+parser.add_option("-d", "--disable", action="store_true",
+ help="disable client")
+parser.add_option("-b", "--bump-timeout", action="store_true",
+ help="Bump timeout for client")
+parser.add_option("--start-checker", action="store_true",
+ help="Start checker for client")
+parser.add_option("--stop-checker", action="store_true",
+ help="Stop checker for client")
+parser.add_option("-V", "--is-valid", action="store_true",
+ help="Check if client is still valid")
+parser.add_option("-r", "--remove", action="store_true",
+ help="Remove client")
+parser.add_option("-c", "--checker", type="string",
+ help="Set checker command for client")
+parser.add_option("-t", "--timeout", type="string",
+ help="Set timeout for client")
+parser.add_option("-i", "--interval", type="string",
+ help="Set checker interval for client")
+parser.add_option("-H", "--host", type="string",
+ help="Set host for client")
+parser.add_option("-s", "--secret", type="string",
+ help="Set password blob (file) for client")
+options, client_names = parser.parse_args()
+
+# Compile list of clients to process
+clients=[]
+for name in client_names:
+ for path, client in mandos_clients.iteritems():
+ if client['name'] == name:
+ client_objc = bus.get_object(busname, path)
+ clients.append(dbus.Interface(client_objc,
+ dbus_interface
+ = client_interface))
+ break
+ else:
+ print >> sys.stderr, "Client not found on server: %r" % name
+ sys.exit(1)
+
+if not clients and mandos_clients.values():
+ keywords = defaultkeywords
+ if options.all:
+ keywords = ('name', 'enabled', 'timeout', 'last_checked_ok',
+ 'created', 'interval', 'host', 'fingerprint',
+ 'checker_running', 'last_enabled', 'checker')
+ print_clients(mandos_clients.values())
+
+# Process each client in the list by all selected options
+for client in clients:
+ if options.remove:
+ mandos_serv.RemoveClient(client.__dbus_object_path__)
+ if options.enable:
+ client.Enable()
+ if options.disable:
+ client.Disable()
+ if options.bump_timeout:
+ client.BumpTimeout()
+ if options.start_checker:
+ client.StartChecker()
+ if options.stop_checker:
+ client.StopChecker()
+ if options.is_valid:
+ sys.exit(0 if client.IsStillValid() else 1)
+ if options.checker:
+ client.SetChecker(options.checker)
+ if options.host:
+ client.SetHost(options.host)
+ if options.interval:
+ client.SetInterval(datetime_to_milliseconds
+ (string_to_delta(options.interval)))
+ if options.timeout:
+ client.SetTimeout(datetime_to_milliseconds
+ (string_to_delta(options.timeout)))
+ if options.secret:
+ client.SetSecret(dbus.ByteArray(open(options.secret, 'rb').read()))
+
=== added file 'mandos-keygen'
--- mandos-keygen 1970-01-01 00:00:00 +0000
+++ mandos-keygen 2009-05-23 05:59:52 +0000
@@ -0,0 +1,326 @@
+#!/bin/sh -e
+#
+# Mandos key generator - create a new OpenPGP key for a Mandos client
+#
+# Copyright © 2008,2009 Teddy Hogeborn
+# Copyright © 2008,2009 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.11"
+
+KEYDIR="/etc/keys/mandos"
+KEYTYPE=DSA
+KEYLENGTH=2048
+SUBKEYTYPE=ELG-E
+SUBKEYLENGTH=2048
+KEYNAME="`hostname --fqdn 2>/dev/null || hostname`"
+KEYEMAIL=""
+KEYCOMMENT="Mandos client key"
+KEYEXPIRE=0
+FORCE=no
+KEYCOMMENT_ORIG="$KEYCOMMENT"
+mode=keygen
+
+if [ ! -d "$KEYDIR" ]; then
+ KEYDIR="/etc/mandos/keys"
+fi
+
+# Parse options
+TEMP=`getopt --options vhpF:d:t:l:s:L:n:e:c:x:f \
+ --longoptions version,help,password,passfile:,dir:,type:,length:,subtype:,sublength:,name:,email:,comment:,expire:,force \
+ --name "$0" -- "$@"`
+
+help(){
+basename="`basename $0`"
+cat <&2; exit 1;;
+ esac
+done
+if [ "$#" -gt 0 ]; then
+ echo "Unknown arguments: '$@'" >&2
+ exit 1
+fi
+
+SECKEYFILE="$KEYDIR/seckey.txt"
+PUBKEYFILE="$KEYDIR/pubkey.txt"
+
+# Check for some invalid values
+if [ ! -d "$KEYDIR" ]; then
+ echo "$KEYDIR not a directory" >&2
+ exit 1
+fi
+if [ ! -r "$KEYDIR" ]; then
+ echo "Directory $KEYDIR not readable" >&2
+ exit 1
+fi
+
+if [ "$mode" = keygen ]; then
+ if [ ! -w "$KEYDIR" ]; then
+ echo "Directory $KEYDIR not writeable" >&2
+ exit 1
+ fi
+ if [ -z "$KEYTYPE" ]; then
+ echo "Empty key type" >&2
+ exit 1
+ fi
+
+ if [ -z "$KEYNAME" ]; then
+ echo "Empty key name" >&2
+ exit 1
+ fi
+
+ if [ -z "$KEYLENGTH" ] || [ "$KEYLENGTH" -lt 512 ]; then
+ echo "Invalid key length" >&2
+ exit 1
+ fi
+
+ if [ -z "$KEYEXPIRE" ]; then
+ echo "Empty key expiration" >&2
+ exit 1
+ fi
+
+ # Make FORCE be 0 or 1
+ case "$FORCE" in
+ [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) FORCE=1;;
+ [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) FORCE=0;;
+ esac
+
+ if [ \( -e "$SECKEYFILE" -o -e "$PUBKEYFILE" \) \
+ -a "$FORCE" -eq 0 ]; then
+ echo "Refusing to overwrite old key files; use --force" >&2
+ exit 1
+ fi
+
+ # Set lines for GnuPG batch file
+ if [ -n "$KEYCOMMENT" ]; then
+ KEYCOMMENTLINE="Name-Comment: $KEYCOMMENT"
+ fi
+ if [ -n "$KEYEMAIL" ]; then
+ KEYEMAILLINE="Name-Email: $KEYEMAIL"
+ fi
+
+ # Create temporary gpg batch file
+ BATCHFILE="`mktemp -t mandos-keygen-batch.XXXXXXXXXX`"
+fi
+
+if [ "$mode" = password ]; then
+ # Create temporary encrypted password file
+ SECFILE="`mktemp -t mandos-keygen-secfile.XXXXXXXXXX`"
+fi
+
+# Create temporary key ring directory
+RINGDIR="`mktemp -d -t mandos-keygen-keyrings.XXXXXXXXXX`"
+
+# Remove temporary files on exit
+trap "
+set +e; \
+test -n \"$SECFILE\" && shred --remove \"$SECFILE\"; \
+shred --remove \"$RINGDIR\"/sec*;
+test -n \"$BATCHFILE\" && rm --force \"$BATCHFILE\"; \
+rm --recursive --force \"$RINGDIR\";
+stty echo; \
+" EXIT
+
+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 key from key rings to key files
+ gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
+ --homedir "$RINGDIR" --armor --export-options export-minimal \
+ --comment "$FILECOMMENT" --output "$SECKEYFILE" \
+ --export-secret-keys
+ gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
+ --homedir "$RINGDIR" --armor --export-options export-minimal \
+ --comment "$FILECOMMENT" --output "$PUBKEYFILE" --export
+fi
+
+if [ "$mode" = password ]; then
+ # Import key into temporary key rings
+ gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
+ --homedir "$RINGDIR" --trust-model always --armor \
+ --import "$SECKEYFILE"
+ gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
+ --homedir "$RINGDIR" --trust-model always --armor \
+ --import "$PUBKEYFILE"
+
+ # Get fingerprint of key
+ FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \
+ --enable-dsa2 --homedir \"$RINGDIR\" --trust-model always \
+ --fingerprint --with-colons \
+ | sed --quiet \
+ --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`"
+
+ test -n "$FINGERPRINT"
+
+ FILECOMMENT="Encrypted password for a Mandos client"
+
+ if [ -n "$PASSFILE" ]; then
+ cat "$PASSFILE"
+ else
+ stty -echo
+ echo -n "Enter passphrase: " >&2
+ first="$(head --lines=1 | tr --delete '\n')"
+ echo -n -e "\nRepeat passphrase: " >&2
+ second="$(head --lines=1 | tr --delete '\n')"
+ echo >&2
+ stty echo
+ if [ "$first" != "$second" ]; then
+ echo -e "Passphrase mismatch" >&2
+ touch "$RINGDIR"/mismatch
+ else
+ echo -n "$first"
+ fi
+ fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
+ --homedir "$RINGDIR" --trust-model always --armor --encrypt \
+ --sign --recipient "$FINGERPRINT" --comment "$FILECOMMENT" \
+ > "$SECFILE"
+ if [ -e "$RINGDIR"/mismatch ]; then
+ rm --force "$RINGDIR"/mismatch
+ exit 1
+ fi
+
+ cat <<-EOF
+ [$KEYNAME]
+ host = $KEYNAME
+ fingerprint = $FINGERPRINT
+ secret =
+ EOF
+ sed --quiet --expression='
+ /^-----BEGIN PGP MESSAGE-----$/,/^-----END PGP MESSAGE-----$/{
+ /^$/,${
+ # Remove 24-bit Radix-64 checksum
+ s/=....$//
+ # Indent four spaces
+ /^[^-]/s/^/ /p
+ }
+ }' < "$SECFILE"
+fi
+
+trap - EXIT
+
+set +e
+# Remove the password file, if any
+if [ -n "$SECFILE" ]; then
+ shred --remove "$SECFILE"
+fi
+# Remove the key rings
+shred --remove "$RINGDIR"/sec*
+rm --recursive --force "$RINGDIR"
=== added file 'mandos-keygen.xml'
--- mandos-keygen.xml 1970-01-01 00:00:00 +0000
+++ mandos-keygen.xml 2009-01-04 21:54:55 +0000
@@ -0,0 +1,511 @@
+
+
+
+
+%common;
+]>
+
+
+
+ Mandos Manual
+
+ Mandos
+ &version;
+ &TIMESTAMP;
+
+
+ Björn
+ Påhlsson
+
+ belorn@fukt.bsnet.se
+
+
+
+ Teddy
+ Hogeborn
+
+ teddy@fukt.bsnet.se
+
+
+
+
+ 2008
+ 2009
+ Teddy Hogeborn
+ Björn Påhlsson
+
+
+
+
+
+ &COMMANDNAME;
+ 8
+
+
+
+ &COMMANDNAME;
+
+ Generate key and password for Mandos client and server.
+
+
+
+
+
+ &COMMANDNAME;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ &COMMANDNAME;
+
+
+
+
+
+ FILE
+
+
+
+
+
+
+
+
+
+
+
+
+
+ &COMMANDNAME;
+
+
+
+
+
+
+ &COMMANDNAME;
+
+
+
+
+
+
+
+
+ DESCRIPTION
+
+ &COMMANDNAME; is a program to generate the
+ OpenPGP key used by
+ mandos-client
+ 8mandos. The key is
+ normally written to /etc/mandos for later installation into the
+ initrd image, but this, and most other things, can be changed
+ with command line options.
+
+
+ This program can also be used with the
+ or
+ options to generate a ready-made section for
+ clients.conf (see
+ mandos-clients.conf
+ 5).
+
+
+
+
+ PURPOSE
+
+ The purpose of this is to enable remote and unattended
+ rebooting of client host computer with an
+ encrypted root file system. See for details.
+
+
+
+
+ OPTIONS
+
+
+
+
+
+
+
+ Show a help message and exit
+
+
+
+
+
+
+
+
+
+ Target directory for key files. Default is
+ /etc/mandos.
+
+
+
+
+
+
+
+
+
+ Key type. Default is DSA
.
+
+
+
+
+
+
+
+
+
+ Key length in bits. Default is 2048.
+
+
+
+
+
+
+
+
+
+ Subkey type. Default is ELG-E
(Elgamal
+ encryption-only).
+
+
+
+
+
+
+
+
+
+ Subkey length in bits. Default is 2048.
+
+
+
+
+
+
+
+
+
+ Email address of key. Default is empty.
+
+
+
+
+
+
+
+
+
+ Comment field for key. The default value is
+ Mandos client key
.
+
+
+
+
+
+
+
+
+
+ Key expire time. Default is no expiration. See
+ gpg
+ 1 for syntax.
+
+
+
+
+
+
+
+
+
+ Force overwriting old key.
+
+
+
+
+
+
+
+
+ Prompt for a password and encrypt it with the key already
+ present in either /etc/mandos or the
+ directory specified with the
+ option. Outputs, on standard output, a section suitable
+ for inclusion in mandos-clients.conf8. The host name or the name
+ specified with the option is used
+ for the section header. All other options are ignored,
+ and no key is created.
+
+
+
+
+
+
+
+
+ The same as , but read from
+ FILE, not the terminal.
+
+
+
+
+
+
+
+ OVERVIEW
+
+
+ This program is a small utility to generate new OpenPGP keys for
+ new Mandos clients, and to generate sections for inclusion in
+ clients.conf on the server.
+
+
+
+
+ EXIT STATUS
+
+ The exit status will be 0 if a new key (or password, if the
+ option was used) was successfully
+ created, otherwise not.
+
+
+
+
+ ENVIRONMENT
+
+
+ TMPDIR
+
+
+ If set, temporary files will be created here. See
+ mktemp
+ 1.
+
+
+
+
+
+
+
+ FILES
+
+ Use the option to change where
+ &COMMANDNAME; will write the key files. The
+ default file names are shown here.
+
+
+
+ /etc/mandos/seckey.txt
+
+
+ OpenPGP secret key file which will be created or
+ overwritten.
+
+
+
+
+ /etc/mandos/pubkey.txt
+
+
+ OpenPGP public key file which will be created or
+ overwritten.
+
+
+
+
+ /tmp
+
+
+ Temporary files will be written here if
+ TMPDIR is not set.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ EXAMPLE
+
+
+ Normal invocation needs no options:
+
+
+ &COMMANDNAME;
+
+
+
+
+ Create key in another directory and of another type. Force
+ overwriting old key files:
+
+
+
+
+&COMMANDNAME; --dir ~/keydir --type RSA --force
+
+
+
+
+
+ Prompt for a password, encrypt it with the key in
+ /etc/mandos and output a section suitable
+ for clients.conf.
+
+
+ &COMMANDNAME; --password
+
+
+
+
+ Prompt for a password, encrypt it with the key in the
+ client-key directory and output a section
+ suitable for clients.conf.
+
+
+
+
+&COMMANDNAME; --password --dir client-key
+
+
+
+
+
+
+ SECURITY
+
+ The , ,
+ , and
+ options can be used to create keys of low security. If in
+ doubt, leave them to the default values.
+
+
+ The key expire time is not guaranteed to be
+ honored by mandos
+ 8.
+
+
+
+
+ SEE ALSO
+
+ gpg
+ 1,
+ mandos-clients.conf
+ 5,
+ mandos
+ 8,
+ mandos-client
+ 8mandos
+
+
+
+
+
+
+
+
+
=== added file 'mandos-options.xml'
--- mandos-options.xml 1970-01-01 00:00:00 +0000
+++ mandos-options.xml 2009-02-13 05:38:21 +0000
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+ 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 normally be an IPv6
+ address; an IPv4 address can only be specified using IPv4-mapped
+ IPv6 address syntax: ::FFFF:192.0.2.3
. (Only if IPv6 usage is
+ disabled (see below) must this be an IPv4
+ address.)
+
+
+
+ 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 if for some reason is would be necessary to run more than
+ one server on the same host. This 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.
+
+
+
+ This option controls whether the server will provide a D-Bus
+ system bus interface. The default is to provide such an
+ interface.
+
+
+
+ This option controls whether the server will use IPv6 sockets and
+ addresses. The default is to use IPv6. This option should
+ never normally be turned off, even in
+ IPv4-only environments. This is because
+ mandos-client
+ 8mandos will normally use
+ IPv6 link-local addresses, and will not be able to find or connect
+ to the server if this option is turned off. Only
+ advanced users should consider changing this option.
+
+
+
=== added file 'mandos.conf'
--- mandos.conf 1970-01-01 00:00:00 +0000
+++ mandos.conf 2009-02-13 05:38:21 +0000
@@ -0,0 +1,44 @@
+# This file must have exactly one section named "DEFAULT".
+[DEFAULT]
+
+# These are the default values for the server, uncomment and change
+# them if needed.
+
+
+# If "interface" is set, the server will only listen to a specific
+# network interface.
+;interface =
+
+
+# If "address" is set, the server will only listen to a specific
+# address. This must currently be an IPv6 address; an IPv4 address
+# can be specified using the "::FFFF:192.0.2.3" syntax. Also, if this
+# is a link-local address, an interface should be set above.
+;address =
+
+
+# If "port" is set, the server to bind to that port. By default, the
+# server will listen to an arbitrary port.
+;port =
+
+
+# If "debug" is true, the server will run in the foreground and print
+# a lot of debugging information.
+;debug = False
+
+
+# GnuTLS priority for the TLS handshake. See gnutls_priority_init(3).
+;priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP
+
+
+# Zeroconf service name. You need to change this if you for some
+# reason want to run more than one server on the same *host*.
+# If there are name collisions on the same *network*, the server will
+# rename itself to "Mandos #2", etc.
+;servicename = Mandos
+
+# Whether to provide a D-Bus system bus interface or not
+;use_dbus = True
+
+# Whether to use IPv6. (Changing this is NOT recommended.)
+;use_ipv6 = True
=== added file 'mandos.conf.xml'
--- mandos.conf.xml 1970-01-01 00:00:00 +0000
+++ mandos.conf.xml 2009-02-25 01:14:29 +0000
@@ -0,0 +1,266 @@
+
+
+/etc/mandos/mandos.conf">
+
+
+%common;
+]>
+
+
+
+ Mandos Manual
+
+ Mandos
+ &version;
+ &TIMESTAMP;
+
+
+ Björn
+ Påhlsson
+
+ belorn@fukt.bsnet.se
+
+
+
+ Teddy
+ Hogeborn
+
+ teddy@fukt.bsnet.se
+
+
+
+
+ 2008
+ 2009
+ Teddy Hogeborn
+ Björn Påhlsson
+
+
+
+
+
+ &CONFNAME;
+ 5
+
+
+
+ &CONFNAME;
+
+ Configuration file for the Mandos server
+
+
+
+
+ &CONFPATH;
+
+
+
+ DESCRIPTION
+
+ The file &CONFPATH; is a simple configuration file for
+ mandos
+ 8, and is read by it at
+ startup. The configuration file starts with [DEFAULT]
on a line by itself, followed by
+ any number of option=value
entries,
+ with continuations in the style of RFC 822. option: value
is also accepted. Note that
+ leading whitespace is removed from values. Lines beginning with
+ #
or ;
are ignored and may be used
+ to provide comments.
+
+
+
+
+ OPTIONS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ FILES
+
+ The file described here is &CONFPATH;
+
+
+
+
+ BUGS
+
+ The [DEFAULT] is necessary because the Python
+ built-in module ConfigParser
+ requires it.
+
+
+
+
+ EXAMPLE
+
+
+ No options are actually required:
+
+
+[DEFAULT]
+
+
+
+
+ An example using all the options:
+
+
+[DEFAULT]
+# A configuration example
+interface = eth0
+address = fe80::aede:48ff:fe71:f6f2
+port = 1025
+debug = true
+priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP
+servicename = Daena
+use_dbus = False
+use_ipv6 = True
+
+
+
+
+
+ SEE ALSO
+
+ gnutls_priority_init3,
+ mandos
+ 8,
+ mandos-clients.conf
+ 5
+
+
+
+
+
+ RFC 4291: IP Version 6 Addressing
+ Architecture
+
+
+
+
+ Section 2.2: Text Representation of
+ Addresses
+
+
+
+ Section 2.5.5.2: IPv4-Mapped IPv6
+ Address
+
+
+
+ Section 2.5.6, Link-Local IPv6 Unicast
+ Addresses
+
+
+ The clients use IPv6 link-local addresses, which are
+ immediately usable since a link-local addresses is
+ automatically assigned to a network interface when it
+ is brought up.
+
+
+
+
+
+
+
+
+ Zeroconf
+
+
+
+ Zeroconf is the network protocol standard used by clients
+ for finding the Mandos server on the local network.
+
+
+
+
+
+
+
+
+
+
+
=== added file 'mandos.lsm'
--- mandos.lsm 1970-01-01 00:00:00 +0000
+++ mandos.lsm 2009-05-23 05:59:52 +0000
@@ -0,0 +1,22 @@
+Begin4
+Title: Mandos
+Version: 1.0.11
+Entered-date: 2009-05-23
+Description: The Mandos system allows computers to have encrypted
+root file systems and at the same time be capable of remote and/or
+unattended reboots.
+Keywords: boot, encryption, luks, cryptsetup, network, openpgp,
+tls, dm-crypt
+Author: teddy@fukt.bsnet.se (Teddy Hogeborn),
+ belorn@fukt.bsnet.se (Björn Påhlsson)
+Maintained-by: teddy@fukt.bsnet.se (Teddy Hogeborn),
+ belorn@fukt.bsnet.se (Björn Påhlsson)
+Primary-site: http://www.fukt.bsnet.se/mandos
+ 99K mandos_1.0.11.orig.tar.gz
+Alternate-site: ftp://ftp.fukt.bsnet.se/pub/mandos
+ 99K mandos_1.0.11.orig.tar.gz
+Platforms: Requires GCC, GNU libC, Avahi, GnuPG, Python 2.5, and
+various other libraries. While made for Debian GNU/Linux, it is
+probably portable to other distributions, but not other Unixes.
+Copying-policy: GNU General Public License version 3.0 or later
+End
=== added file 'mandos.xml'
--- mandos.xml 1970-01-01 00:00:00 +0000
+++ mandos.xml 2009-09-17 01:21:27 +0000
@@ -0,0 +1,695 @@
+
+
+
+
+%common;
+]>
+
+
+
+ Mandos Manual
+
+ Mandos
+ &version;
+ &TIMESTAMP;
+
+
+ Björn
+ Påhlsson
+
+ belorn@fukt.bsnet.se
+
+
+
+ Teddy
+ Hogeborn
+
+ teddy@fukt.bsnet.se
+
+
+
+
+ 2008
+ 2009
+ Teddy Hogeborn
+ Björn Påhlsson
+
+
+
+
+
+ &COMMANDNAME;
+ 8
+
+
+
+ &COMMANDNAME;
+
+ 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.
+
+
+
+
+
+
+
+
+
+ See also .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ OVERVIEW
+
+
+ This program is the server part. It is a normal server program
+ and will run in a normal system environment, not in an initial
+ RAM disk environment.
+
+
+
+
+ NETWORK PROTOCOL
+
+ The Mandos server announces itself as a Zeroconf service of type
+ _mandos._tcp
. The Mandos
+ client connects to the announced address and port, and sends a
+ line of text where the first whitespace-separated field is the
+ protocol version, which currently is
+ 1
. The client and server then
+ start a TLS protocol handshake with a slight quirk: the Mandos
+ server program acts as a TLS client
while the
+ connecting Mandos client acts as a TLS server
.
+ The Mandos client must supply an OpenPGP certificate, and the
+ fingerprint of this certificate is used by the Mandos server to
+ look up (in a list read from clients.conf
+ at start time) which binary blob to give the client. No other
+ authentication or authorization is done by the server.
+
+
+ Mandos Protocol (Version 1)
+
+ Mandos Client
+ Direction
+ Mandos Server
+
+
+
+ Connect
+ ->
+
+
+ 1\r\n
+ ->
+
+
+ TLS handshake as TLS server
+
+ <->
+ TLS handshake as TLS client
+
+
+
+ OpenPGP public key (part of TLS handshake)
+ ->
+
+
+
+ <-
+ Binary blob (client will assume OpenPGP data)
+
+
+
+ <-
+ Close
+
+
+
+
+
+ CHECKING
+
+ The server will, by default, continually check that the clients
+ are still up. If a client has not been confirmed as being up
+ for some time, the client is assumed to be compromised and is no
+ longer eligible to receive the encrypted password. (Manual
+ intervention is required to re-enable a client.) The timeout,
+ checker program, and interval between checks can be configured
+ both globally and per client; see
+ mandos-clients.conf
+ 5. A client successfully
+ receiving its password will also be treated as a successful
+ checker run.
+
+
+
+
+ LOGGING
+
+ The server will send log message with various severity levels to
+ /dev/log. With the
+ option, it will log even more messages,
+ and also show them on the console.
+
+
+
+
+ D-BUS INTERFACE
+
+ The server will by default provide a D-Bus system bus interface.
+ This interface will only be accessible by the root user or a
+ Mandos-specific user, if such a user exists.
+
+
+
+
+
+ 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 do not show a time stamp.
+
+
+ This server does not check the expire time of clients’ OpenPGP
+ keys.
+
+
+
+
+ EXAMPLE
+
+
+ Normal invocation needs no options:
+
+
+ &COMMANDNAME;
+
+
+
+
+ Run the server in debug mode, read configuration files from
+ the ~/mandos directory, and use the
+ Zeroconf service name Test
to not collide with
+ any other official Mandos server on this host:
+
+
+
+
+&COMMANDNAME; --debug --configdir ~/mandos --servicename Test
+
+
+
+
+
+ Run the server normally, but only listen to one interface and
+ only on the link-local address on that interface:
+
+
+
+
+&COMMANDNAME; --interface eth7 --address fe80::aede:48ff:fe71:f6f2
+
+
+
+
+
+
+ SECURITY
+
+ SERVER
+
+ Running this &COMMANDNAME; server program
+ should not in itself present any security risk to the host
+ computer running it. The program switches to a non-root user
+ soon after startup.
+
+
+
+ CLIENTS
+
+ The server only gives out its stored data to clients which
+ does have the OpenPGP key of the stored fingerprint. This is
+ guaranteed by the fact that the client sends its OpenPGP
+ public key in the TLS handshake; this ensures it to be
+ genuine. The server computes the fingerprint of the key
+ itself and looks up the fingerprint in its list of
+ clients. The clients.conf file (see
+ mandos-clients.conf
+ 5)
+ must be made non-readable by anyone
+ except the user starting the server (usually root).
+
+
+ As detailed in , the status of all
+ client computers will continually be checked and be assumed
+ compromised if they are gone for too long.
+
+
+ If a client is compromised, its downtime should be duly noted
+ by the server which would therefore 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-13 15:36:18 +0000
@@ -0,0 +1,17 @@
+
+
+
+ This is part of the Mandos system for allowing computers to have
+ encrypted root file systems and at the same time be capable of
+ remote and/or unattended reboots. The computers run a small client
+ program in the initial RAM disk environment which
+ will communicate with a server over a network. All network
+ communication is encrypted using TLS. The
+ clients are identified by the server using an OpenPGP key; each
+ client has one unique to it. The server sends the clients an
+ encrypted password. The encrypted password is decrypted by the
+ clients using the same OpenPGP key, and the password is then used to
+ unlock the root file system, whereupon the computers can continue
+ booting normally.
+
=== added file 'plugin-runner.c'
--- plugin-runner.c 1970-01-01 00:00:00 +0000
+++ plugin-runner.c 2009-09-10 06:28:14 +0000
@@ -0,0 +1,1178 @@
+/* -*- coding: utf-8; mode: c; mode: orgtbl -*- */
+/*
+ * Mandos plugin runner - Run Mandos plugins
+ *
+ * Copyright © 2008,2009 Teddy Hogeborn
+ * Copyright © 2008,2009 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, 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(), WTERMSIG(),
+ WCOREDUMP() */
+#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(),
+ strsignal() */
+#include /* errno */
+#include /* struct argp_option, struct
+ argp_state, struct argp,
+ argp_parse(), ARGP_ERR_UNKNOWN,
+ ARGP_KEY_END, ARGP_KEY_ARG,
+ error_t */
+#include /* struct sigaction, sigemptyset(),
+ sigaddset(), sigaction(),
+ sigprocmask(), SIG_BLOCK, SIGCHLD,
+ SIG_UNBLOCK, kill(), sig_atomic_t
+ */
+#include /* errno, EBADF */
+#include /* intmax_t, PRIdMAX, strtoimax() */
+
+#define BUFFER_SIZE 256
+
+#define PDIR "/lib/mandos/plugins.d"
+#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
+
+const char *argp_program_version = "plugin-runner " VERSION;
+const char *argp_program_bug_address = "";
+
+typedef struct plugin{
+ char *name; /* can be NULL or any plugin name */
+ char **argv;
+ int argc;
+ char **environ;
+ int envc;
+ bool disabled;
+
+ /* Variables used for running processes*/
+ pid_t pid;
+ int fd;
+ char *buffer;
+ size_t buffer_size;
+ size_t buffer_length;
+ bool eof;
+ volatile sig_atomic_t completed;
+ 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 = NULL;
+ do {
+ new_plugin = malloc(sizeof(plugin));
+ } while(new_plugin == NULL and errno == EINTR);
+ if(new_plugin == NULL){
+ return NULL;
+ }
+ char *copy_name = NULL;
+ if(name != NULL){
+ do {
+ copy_name = strdup(name);
+ } while(copy_name == NULL and errno == EINTR);
+ if(copy_name == NULL){
+ free(new_plugin);
+ return NULL;
+ }
+ }
+
+ *new_plugin = (plugin){ .name = copy_name,
+ .argc = 1,
+ .disabled = false,
+ .next = plugin_list };
+
+ do {
+ new_plugin->argv = malloc(sizeof(char *) * 2);
+ } while(new_plugin->argv == NULL and errno == EINTR);
+ if(new_plugin->argv == NULL){
+ free(copy_name);
+ free(new_plugin);
+ return NULL;
+ }
+ new_plugin->argv[0] = copy_name;
+ new_plugin->argv[1] = NULL;
+
+ do {
+ new_plugin->environ = malloc(sizeof(char *));
+ } while(new_plugin->environ == NULL and errno == EINTR);
+ if(new_plugin->environ == NULL){
+ 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 */
+ do {
+ *array = realloc(*array, sizeof(char *)
+ * (size_t) ((*len) + 2));
+ } while(*array == NULL and errno == EINTR);
+ /* Malloc check */
+ if(*array == NULL){
+ return false;
+ }
+ /* Make a copy of the new string */
+ char *copy;
+ do {
+ copy = strdup(new);
+ } while(copy == NULL and errno == EINTR);
+ if(copy == NULL){
+ return false;
+ }
+ /* Insert the copy */
+ (*array)[*len] = copy;
+ (*len)++;
+ /* Add a new terminating NULL pointer to the last element */
+ (*array)[*len] = NULL;
+ return true;
+}
+
+/* Add to a plugin's argument vector */
+static bool add_argument(plugin *p, const char *arg){
+ if(p == NULL){
+ return false;
+ }
+ return add_to_char_array(arg, &(p->argv), &(p->argc));
+}
+
+/* Add to a plugin's environment */
+static bool add_environment(plugin *p, const char *def, bool replace){
+ if(p == NULL){
+ return false;
+ }
+ /* namelen = length of name of environment variable */
+ size_t namelen = (size_t)(strchrnul(def, '=') - def);
+ /* Search for this environment variable */
+ for(char **e = p->environ; *e != NULL; e++){
+ if(strncmp(*e, def, namelen + 1) == 0){
+ /* It already exists */
+ if(replace){
+ char *new;
+ do {
+ new = realloc(*e, strlen(def) + 1);
+ } while(new == NULL and errno == EINTR);
+ if(new == NULL){
+ return false;
+ }
+ *e = new;
+ strcpy(*e, def);
+ }
+ return true;
+ }
+ }
+ return add_to_char_array(def, &(p->environ), &(p->envc));
+}
+
+/*
+ * Based on the example in the GNU LibC manual chapter 13.13 "File
+ * Descriptor Flags".
+ | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
+ */
+static int set_cloexec_flag(int fd){
+ int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
+ /* If reading the flags failed, return error indication now. */
+ if(ret < 0){
+ return ret;
+ }
+ /* Store modified flag word in the descriptor. */
+ return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
+ ret | FD_CLOEXEC));
+}
+
+
+/* Mark processes as completed when they exit, and save their exit
+ status. */
+static void handle_sigchld(__attribute__((unused)) int sig){
+ int old_errno = errno;
+ while(true){
+ plugin *proc = plugin_list;
+ int status;
+ pid_t pid = waitpid(-1, &status, WNOHANG);
+ if(pid == 0){
+ /* Only still running child processes */
+ break;
+ }
+ if(pid == -1){
+ if(errno == ECHILD){
+ /* No child processes */
+ break;
+ }
+ perror("waitpid");
+ }
+
+ /* A child exited, find it in process_list */
+ while(proc != NULL and proc->pid != pid){
+ proc = proc->next;
+ }
+ if(proc == NULL){
+ /* Process not found in process list */
+ continue;
+ }
+ proc->status = status;
+ proc->completed = 1;
+ }
+ errno = old_errno;
+}
+
+/* Prints out a password to stdout */
+static bool print_out_password(const char *buffer, size_t length){
+ ssize_t ret;
+ for(size_t written = 0; written < length; written += (size_t)ret){
+ ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
+ length - written));
+ if(ret < 0){
+ return false;
+ }
+ }
+ return true;
+}
+
+/* Removes and free a plugin from the plugin list */
+static void free_plugin(plugin *plugin_node){
+
+ for(char **arg = plugin_node->argv; *arg != NULL; arg++){
+ free(*arg);
+ }
+ free(plugin_node->argv);
+ for(char **env = plugin_node->environ; *env != NULL; env++){
+ free(*env);
+ }
+ free(plugin_node->environ);
+ free(plugin_node->buffer);
+
+ /* Removes the plugin from the singly-linked list */
+ if(plugin_node == plugin_list){
+ /* First one - simple */
+ plugin_list = plugin_list->next;
+ } else {
+ /* Second one or later */
+ for(plugin *p = plugin_list; p != NULL; p = p->next){
+ if(p->next == plugin_node){
+ p->next = plugin_node->next;
+ break;
+ }
+ }
+ }
+
+ free(plugin_node);
+}
+
+static void free_plugin_list(void){
+ while(plugin_list != NULL){
+ free_plugin(plugin_list);
+ }
+}
+
+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;
+ ssize_t sret;
+ 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){
+ char *tmp;
+ intmax_t tmpmax;
+ case 'g': /* --global-options */
+ if(arg != NULL){
+ char *plugin_option;
+ while((plugin_option = strsep(&arg, ",")) != NULL){
+ if(plugin_option[0] == '\0'){
+ continue;
+ }
+ if(not add_argument(getplugin(NULL), plugin_option)){
+ 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 *plugin_name = strsep(&arg, ":");
+ if(plugin_name[0] == '\0'){
+ break;
+ }
+ char *plugin_option;
+ while((plugin_option = strsep(&arg, ",")) != NULL){
+ if(not add_argument(getplugin(plugin_name), plugin_option)){
+ 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 */
+ errno = 0;
+ tmpmax = strtoimax(arg, &tmp, 10);
+ if(errno != 0 or tmp == arg or *tmp != '\0'
+ or tmpmax != (uid_t)tmpmax){
+ fprintf(stderr, "Bad user ID number: \"%s\", using %"
+ PRIdMAX "\n", arg, (intmax_t)uid);
+ } else {
+ uid = (uid_t)tmpmax;
+ }
+ break;
+ case 131: /* --groupid */
+ errno = 0;
+ tmpmax = strtoimax(arg, &tmp, 10);
+ if(errno != 0 or tmp == arg or *tmp != '\0'
+ or tmpmax != (gid_t)tmpmax){
+ fprintf(stderr, "Bad group ID number: \"%s\", using %"
+ PRIdMAX "\n", arg, (intmax_t)gid);
+ } else {
+ gid = (gid_t)tmpmax;
+ }
+ break;
+ case 132: /* --debug */
+ debug = true;
+ break;
+/*
+ * When adding more options before this line, remember to also add a
+ * "case" to the "parse_opt_config_file" function below.
+ */
+ case ARGP_KEY_ARG:
+ /* Cryptsetup always passes an argument, which is an empty
+ string if "none" was specified in /etc/crypttab. So if
+ argument was empty, we ignore it silently. */
+ if(arg[0] != '\0'){
+ 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 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;
+ 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;
+ }
+ }
+ do {
+ ret = fclose(conffp);
+ } while(ret == EOF and errno == EINTR);
+ if(ret == EOF){
+ perror("fclose");
+ exitstatus = EXIT_FAILURE;
+ goto fallback;
+ }
+ free(org_line);
+ } else {
+ /* Check for harmful errors and go to fallback. Other errors might
+ not affect opening plugins */
+ if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
+ 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 %d environment variables\n", p->envc);
+ for(char **a = p->environ; *a != NULL; a++){
+ fprintf(stderr, "\t%s\n", *a);
+ }
+ }
+ }
+
+ /* Strip permissions down to nobody */
+ setgid(gid);
+ if(ret == -1){
+ perror("setgid");
+ }
+ ret = setuid(uid);
+ if(ret == -1){
+ perror("setuid");
+ }
+
+ 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){
+ do {
+ dirst = readdir(dir);
+ } while(dirst == NULL and errno == EINTR);
+
+ /* 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-bak",
+ ".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 = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
+ dirst->d_name));
+ } else {
+ ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
+ plugindir,
+ dirst->d_name));
+ }
+ if(ret < 0){
+ perror("asprintf");
+ continue;
+ }
+
+ ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
+ if(ret == -1){
+ perror("stat");
+ free(filename);
+ continue;
+ }
+
+ /* Ignore non-executable files */
+ if(not S_ISREG(st.st_mode)
+ or (TEMP_FAILURE_RETRY(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 = (int)TEMP_FAILURE_RETRY(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 = (int)TEMP_FAILURE_RETRY(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;
+ do {
+ pid = fork();
+ } while(pid == -1 and errno == EINTR);
+ 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 */
+ TEMP_FAILURE_RETRY(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 = (int)(TEMP_FAILURE_RETRY
+ (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 = (int)TEMP_FAILURE_RETRY(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;
+ }
+ }
+
+ TEMP_FAILURE_RETRY(closedir(dir));
+ dir = NULL;
+ free_plugin(getplugin(NULL));
+
+ for(plugin *p = plugin_list; p != NULL; p = p->next){
+ if(p->pid != 0){
+ break;
+ }
+ if(p->next == NULL){
+ fprintf(stderr, "No plugin processes started. Incorrect plugin"
+ " directory?\n");
+ free_plugin_list();
+ }
+ }
+
+ /* 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 and errno != EINTR){
+ 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->completed and proc->eof){
+ /* Only accept the plugin output if it exited cleanly */
+ if(not WIFEXITED(proc->status)
+ or WEXITSTATUS(proc->status) != 0){
+ /* Bad exit by plugin */
+
+ if(debug){
+ if(WIFEXITED(proc->status)){
+ fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
+ " status %d\n", proc->name,
+ (intmax_t) (proc->pid),
+ WEXITSTATUS(proc->status));
+ } else if(WIFSIGNALED(proc->status)){
+ fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
+ " signal %d: %s\n", proc->name,
+ (intmax_t) (proc->pid),
+ WTERMSIG(proc->status),
+ strsignal(WTERMSIG(proc->status)));
+ } else if(WCOREDUMP(proc->status)){
+ fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
+ " core\n", proc->name, (intmax_t) (proc->pid));
+ }
+ }
+
+ /* Remove the plugin */
+ FD_CLR(proc->fd, &rfds_all);
+
+ /* Block signal while modifying process_list */
+ ret = (int)TEMP_FAILURE_RETRY(sigprocmask
+ (SIG_BLOCK,
+ &sigchld_action.sa_mask,
+ NULL));
+ if(ret < 0){
+ perror("sigprocmask");
+ exitstatus = EXIT_FAILURE;
+ goto fallback;
+ }
+
+ plugin *next_plugin = proc->next;
+ free_plugin(proc);
+ proc = next_plugin;
+
+ /* We are done modifying process list, so unblock signal */
+ ret = (int)(TEMP_FAILURE_RETRY
+ (sigprocmask(SIG_UNBLOCK,
+ &sigchld_action.sa_mask, NULL)));
+ if(ret < 0){
+ perror("sigprocmask");
+ exitstatus = EXIT_FAILURE;
+ goto fallback;
+ }
+
+ if(plugin_list == NULL){
+ break;
+ }
+
+ 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 */
+ sret = TEMP_FAILURE_RETRY(read(proc->fd,
+ proc->buffer
+ + proc->buffer_length,
+ BUFFER_SIZE));
+ if(sret < 0){
+ /* Read error from this process; ignore the error */
+ proc = proc->next;
+ continue;
+ }
+ if(sret == 0){
+ /* got EOF */
+ proc->eof = true;
+ } else {
+ proc->buffer_length += (size_t) sret;
+ }
+ }
+ }
+
+
+ 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);
+ }
+
+ /* 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 2009-04-17 08:26:17 +0000
@@ -0,0 +1,10 @@
+## This is the configuration file for plugin-runner(8mandos). This
+## file should be installed as "/etc/mandos/plugin-runner.conf", and
+## will be copied to "/conf/conf.d/mandos/plugin-runner.conf" in the
+## initrd.img file.
+##
+## After editing this file, the initrd image file must be updated for
+## the changes to take effect!
+
+## Example:
+#--options-for=mandos-client:--debug
=== added file 'plugin-runner.xml'
--- plugin-runner.xml 1970-01-01 00:00:00 +0000
+++ plugin-runner.xml 2009-01-18 06:41:57 +0000
@@ -0,0 +1,640 @@
+
+
+
+
+%common;
+]>
+
+
+
+ Mandos Manual
+
+ Mandos
+ &version;
+ &TIMESTAMP;
+
+
+ Björn
+ Påhlsson
+
+ belorn@fukt.bsnet.se
+
+
+
+ Teddy
+ Hogeborn
+
+ teddy@fukt.bsnet.se
+
+
+
+
+ 2008
+ 2009
+ Teddy Hogeborn
+ Björn Påhlsson
+
+
+
+
+
+ &COMMANDNAME;
+ 8mandos
+
+
+
+ &COMMANDNAME;
+
+ Run Mandos plugins, pass data from first to succeed.
+
+
+
+
+
+ &COMMANDNAME;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ &COMMANDNAME;
+
+
+
+
+
+
+ &COMMANDNAME;
+
+
+
+ &COMMANDNAME;
+
+
+
+
+
+
+
+
+ DESCRIPTION
+
+ &COMMANDNAME; is a program which is meant to
+ be specified as a keyscript
for the root disk in
+ crypttab
+ 5. The aim of this
+ program is therefore to output a password, which then
+ cryptsetup
+ 8 will use to unlock the
+ root disk.
+
+
+ This program is not meant to be invoked directly, but can be in
+ order to test it. Note that any password obtained will simply
+ be output on standard output.
+
+
+
+
+ PURPOSE
+
+ The purpose of this is to enable remote and unattended
+ rebooting of client host computer with an
+ encrypted root file system. See for details.
+
+
+
+
+ OPTIONS
+
+
+
+
+
+
+ This option will add an environment variable setting to
+ all plugins. This will override any inherited environment
+ variable.
+
+
+
+
+
+
+
+
+
+ This option will add an environment variable setting to
+ the PLUGIN plugin. This will
+ override any inherited environment variables or
+ environment variables specified using
+ .
+
+
+
+
+
+
+
+
+
+ Pass some options to all plugins.
+ OPTIONS is a comma separated
+ list of options. This is not a very useful option, except
+ for specifying the
+ option to all plugins.
+
+
+
+
+
+
+
+
+
+ Pass some options to a specific plugin. PLUGIN is the name (file basename) of a
+ plugin, and OPTIONS is a comma
+ separated list of options.
+
+
+ Note that since options are not split on whitespace, the
+ way to pass, to the plugin
+ foo
, the option
+ with the option argument
+ baz
is either
+ --options-for=foo:--bar=baz or
+ --options-for=foo:--bar,baz. Using
+ --options-for="foo:--bar baz". will
+ not work.
+
+
+
+
+
+
+
+
+
+ Disable the plugin named
+ PLUGIN. The plugin will not be
+ started.
+
+
+
+
+
+
+
+
+
+ Re-enable the plugin named
+ PLUGIN. This is only useful to
+ undo a previous option, maybe
+ from the configuration file.
+
+
+
+
+
+
+
+
+ Change to group ID ID on
+ startup. The default is 65534. All plugins will be
+ started using this group ID. Note:
+ This must be a number, not a name.
+
+
+
+
+
+
+
+
+ Change to user ID ID on
+ startup. The default is 65534. All plugins will be
+ started using this user ID. Note:
+ This must be a number, not a name.
+
+
+
+
+
+
+
+
+ Specify a different plugin directory. The default is
+ /lib/mandos/plugins.d, which will
+ exist in the initial RAM disk
+ environment.
+
+
+
+
+
+
+
+
+ Specify a different file to read additional options from.
+ See . Other command line options
+ will override options specified in the file.
+
+
+
+
+
+
+
+
+ Enable debug mode. This will enable a lot of output to
+ standard error about what the program is doing. The
+ program will still perform all other functions normally.
+ The default is to not run in debug
+ mode.
+
+
+ The plugins will not be affected by
+ this option. Use
+
+ if complete debugging eruption is desired.
+
+
+
+
+
+
+
+
+
+ Gives a help message about options and their meanings.
+
+
+
+
+
+
+
+
+ Gives a short usage message.
+
+
+
+
+
+
+
+
+
+ Prints the program version.
+
+
+
+
+
+
+
+ OVERVIEW
+
+
+ This program will run on the client side in the initial
+ RAM disk environment, and is responsible for
+ getting a password. It does this by running plugins, one of
+ which will normally be the actual client program communicating
+ with the server.
+
+
+
+ PLUGINS
+
+ This program will get a password by running a number of
+ plugins, which are simply executable
+ programs in a directory in the initial RAM
+ disk environment. The default directory is
+ /lib/mandos/plugins.d, but this can be
+ changed with the option. The
+ plugins are started in parallel, and the first plugin to output
+ a password and exit with a successful exit
+ code will make this plugin-runner output the password from that
+ plugin, stop any other plugins, and exit.
+
+
+
+ WRITING PLUGINS
+
+ A plugin is simply a program which prints a password to its
+ standard output and then exits with a successful (zero) exit
+ status. If the exit status is not zero, any output on
+ standard output will be ignored by the plugin runner. Any
+ output on its standard error channel will simply be passed to
+ the standard error of the plugin runner, usually the system
+ console.
+
+
+ If the password is a single-line, manually entered passprase,
+ a final trailing newline character should
+ not be printed.
+
+
+ The plugin will run in the initial RAM disk environment, so
+ care must be taken not to depend on any files or running
+ services not available there.
+
+
+ The plugin must exit cleanly and free all allocated resources
+ upon getting the TERM signal, since this is what the plugin
+ runner uses to stop all other plugins when one plugin has
+ output a password and exited cleanly.
+
+
+ The plugin must not use resources, like for instance reading
+ from the standard input, without knowing that no other plugin
+ is also using it.
+
+
+ It is useful, but not required, for the plugin to take the
+ option.
+
+
+
+
+
+ FALLBACK
+
+ If no plugins succeed, this program will, as a fallback, ask for
+ a password on the console using getpass3,
+ and output it. This is not meant to be the normal mode of
+ operation, as there is a separate plugin for getting a password
+ from the console.
+
+
+
+
+ EXIT STATUS
+
+ Exit status of this program is zero if no errors were
+ encountered, and otherwise not. The fallback (see ) may or may not have succeeded in either
+ case.
+
+
+
+
+ ENVIRONMENT
+
+ This program does not use any environment variables itself, it
+ only passes on its environment to all the plugins. The
+ environment passed to plugins can be modified using the
+ and
+ options.
+
+
+
+
+ FILES
+
+
+
+ /conf/conf.d/mandos/plugin-runner.conf
+
+
+ Since this program will be run as a keyscript, there is
+ little to no opportunity to pass command line arguments
+ to it. Therefore, it will also
+ read this file and use its contents as
+ whitespace-separated command line options. Also,
+ everything from a #
character to the end
+ of a line is ignored.
+
+
+ This program is meant to run in the initial RAM disk
+ environment, so that is where this file is assumed to
+ exist. The file does not need to exist in the normal
+ file system.
+
+
+ This file will be processed before
+ the normal command line options, so the latter can
+ override the former, if need be.
+
+
+ This file name is the default; the file to read for
+ arguments can be changed using the
+ option.
+
+
+
+
+
+
+
+
+ BUGS
+
+ The option is ignored when
+ specified from within a configuration file.
+
+
+
+
+ EXAMPLE
+
+
+ Normal invocation needs no options:
+
+
+ &COMMANDNAME;
+
+
+
+
+ Run the program, but not the plugins, in debug mode:
+
+
+
+
+ &COMMANDNAME; --debug
+
+
+
+
+
+ Run all plugins, but run the foo
plugin in
+ debug mode:
+
+
+
+
+ &COMMANDNAME; --options-for=foo:--debug
+
+
+
+
+
+ Run all plugins, but not the program, in debug mode:
+
+
+
+
+ &COMMANDNAME; --global-options=--debug
+
+
+
+
+
+ Run plugins from a different directory, read a different
+ configuration file, and add two options to the
+ mandos-client
+ 8mandos plugin:
+
+
+
+
+cd /etc/keys/mandos; &COMMANDNAME; --config-file=/etc/mandos/plugin-runner.conf --plugin-dir /usr/lib/mandos/plugins.d --options-for=mandos-client:--pubkey=pubkey.txt,--seckey=seckey.txt
+
+
+
+
+
+ SECURITY
+
+ This program will, when starting, try to switch to another user.
+ If it is started as root, it will succeed, and will by default
+ switch to user and group 65534, which are assumed to be
+ non-privileged. This user and group is then what all plugins
+ will be started as. Therefore, the only way to run a plugin as
+ a privileged user is to have the set-user-ID or set-group-ID bit
+ set on the plugin executable file (see
+ execve2
+ ).
+
+
+ If this program is used as a keyscript in crypttab5
+ , there is a slight risk that if this program
+ fails to work, there might be no way to boot the system except
+ for booting from another media and editing the initial RAM disk
+ image to not run this program. This is, however, unlikely,
+ since the password-prompt8mandos
+ plugin will read a password from the console in
+ case of failure of the other plugins, and this plugin runner
+ will also, in case of catastrophic failure, itself fall back to
+ asking and outputting a password on the console (see ).
+
+
+
+
+ SEE ALSO
+
+ cryptsetup
+ 8,
+ crypttab
+ 5,
+ execve
+ 2,
+ mandos
+ 8,
+ password-prompt
+ 8mandos,
+ mandos-client
+ 8mandos
+
+
+
+
+
+
+
+
+
=== added directory 'plugins.d'
=== added file 'plugins.d/askpass-fifo.c'
--- plugins.d/askpass-fifo.c 1970-01-01 00:00:00 +0000
+++ plugins.d/askpass-fifo.c 2009-09-16 23:28:39 +0000
@@ -0,0 +1,102 @@
+/* -*- coding: utf-8 -*- */
+/*
+ * Askpass-FIFO - Read a password from a FIFO and output it
+ *
+ * Copyright © 2008,2009 Teddy Hogeborn
+ * Copyright © 2008,2009 Björn Påhlsson
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * .
+ *
+ * Contact the authors at .
+ */
+
+#define _GNU_SOURCE /* TEMP_FAILURE_RETRY() */
+#include /* ssize_t */
+#include /* mkfifo(), S_IRUSR, S_IWUSR */
+#include /* and */
+#include /* errno, EEXIST */
+#include /* perror() */
+#include /* EXIT_FAILURE, NULL, size_t, free(),
+ realloc(), EXIT_SUCCESS */
+#include /* open(), O_RDONLY */
+#include /* read(), close(), write(),
+ STDOUT_FILENO */
+
+
+int main(__attribute__((unused))int argc,
+ __attribute__((unused))char **argv){
+ int ret = 0;
+ ssize_t sret;
+
+ /* Create FIFO */
+ const char passfifo[] = "/lib/cryptsetup/passfifo";
+ ret = mkfifo(passfifo, S_IRUSR | S_IWUSR);
+ if(ret == -1 and errno != EEXIST){
+ perror("mkfifo");
+ return EXIT_FAILURE;
+ }
+
+ /* Open FIFO */
+ int fifo_fd = open(passfifo, O_RDONLY);
+ if(fifo_fd == -1){
+ perror("open");
+ return EXIT_FAILURE;
+ }
+
+ /* Read from FIFO */
+ char *buf = NULL;
+ size_t buf_len = 0;
+ {
+ size_t buf_allocated = 0;
+ const size_t blocksize = 1024;
+ do {
+ if(buf_len + blocksize > buf_allocated){
+ char *tmp = realloc(buf, buf_allocated + blocksize);
+ if(tmp == NULL){
+ perror("realloc");
+ free(buf);
+ return EXIT_FAILURE;
+ }
+ buf = tmp;
+ buf_allocated += blocksize;
+ }
+ sret = read(fifo_fd, buf + buf_len, buf_allocated - buf_len);
+ if(sret == -1){
+ perror("read");
+ free(buf);
+ return EXIT_FAILURE;
+ }
+ buf_len += (size_t)sret;
+ } while(sret != 0);
+ }
+
+ /* Close FIFO */
+ close(fifo_fd);
+
+ /* Print password to stdout */
+ size_t written = 0;
+ while(written < buf_len){
+ sret = write(STDOUT_FILENO, buf + written, buf_len - written);
+ if(sret == -1){
+ perror("write");
+ free(buf);
+ return EXIT_FAILURE;
+ }
+ written += (size_t)sret;
+ }
+ free(buf);
+
+ return EXIT_SUCCESS;
+}
=== added file 'plugins.d/askpass-fifo.xml'
--- plugins.d/askpass-fifo.xml 1970-01-01 00:00:00 +0000
+++ plugins.d/askpass-fifo.xml 2009-01-04 21:54:55 +0000
@@ -0,0 +1,162 @@
+
+
+
+
+%common;
+]>
+
+
+
+ Mandos Manual
+
+ Mandos
+ &version;
+ &TIMESTAMP;
+
+
+ Björn
+ Påhlsson
+
+ belorn@fukt.bsnet.se
+
+
+
+ Teddy
+ Hogeborn
+
+ teddy@fukt.bsnet.se
+
+
+
+
+ 2008
+ 2009
+ Teddy Hogeborn
+ Björn Påhlsson
+
+
+
+
+
+ &COMMANDNAME;
+ 8mandos
+
+
+
+ &COMMANDNAME;
+ Mandos plugin to get a password from a
+ FIFO.
+
+
+
+
+ &COMMANDNAME;
+
+
+
+
+ DESCRIPTION
+
+ This program reads a password from a FIFO and
+ outputs it to standard output.
+
+
+ This program is not very useful on its own. This program is
+ really meant to run as a plugin in the Mandos client-side system, where it is used as a
+ fallback and alternative to retrieving passwords from a
+ Mandos server.
+
+
+ This program is meant to be imitate a feature of the
+ askpass program, so that programs written to
+ interface with it can keep working under the
+ Mandos system.
+
+
+
+
+ OPTIONS
+
+ This program takes no options.
+
+
+
+
+ EXIT STATUS
+
+ If exit status is 0, the output from the program is the password
+ as it was read. Otherwise, if exit status is other than 0, the
+ program was interrupted or encountered an error, and any output
+ so far could be corrupt and/or truncated, and should therefore
+ be ignored.
+
+
+
+
+ FILES
+
+
+ /lib/cryptsetup/passfifo
+
+
+ This is the FIFO where this program
+ will read the password. If it does not exist, it will be
+ created.
+
+
+
+
+
+
+
+ EXAMPLE
+
+ Note that normally, this program will not be invoked directly,
+ but instead started by the Mandos plugin-runner8mandos
+ .
+
+
+
+ This program takes no options.
+
+
+ &COMMANDNAME;
+
+
+
+
+
+ SECURITY
+
+ The only thing that could be considered worthy of note is
+ this: This program is meant to be run by
+ plugin-runner8mandos, and will, when run
+ standalone, outside, in a normal environment, immediately output
+ on its standard output any presumably secret password it just
+ received. Therefore, when running this program standalone
+ (which should never normally be done), take care not to type in
+ any real secret password by force of habit, since it would then
+ immediately be shown as output.
+
+
+
+
+ SEE ALSO
+
+ fifo
+ 7,
+ plugin-runner
+ 8mandos
+
+
+
+
+
+
+
+
=== added file 'plugins.d/mandos-client.c'
--- plugins.d/mandos-client.c 1970-01-01 00:00:00 +0000
+++ plugins.d/mandos-client.c 2009-09-17 11:22:28 +0000
@@ -0,0 +1,1616 @@
+/* -*- 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 © 2008,2009 Teddy Hogeborn
+ * Copyright © 2008,2009 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() */
+#ifndef _LARGEFILE_SOURCE
+#define _LARGEFILE_SOURCE
+#endif
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+#endif
+
+#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
+
+#include /* fprintf(), stderr, fwrite(),
+ stdout, ferror(), remove() */
+#include /* uint16_t, uint32_t */
+#include /* NULL, size_t, ssize_t */
+#include /* free(), EXIT_SUCCESS, EXIT_FAILURE,
+ srand(), strtof(), abort() */
+#include /* bool, false, true */
+#include /* memset(), strcmp(), strlen(),
+ strerror(), asprintf(), strcpy() */
+#include /* ioctl */
+#include /* socket(), inet_pton(), sockaddr,
+ sockaddr_in6, PF_INET6,
+ SOCK_STREAM, uid_t, gid_t, open(),
+ opendir(), DIR */
+#include /* open() */
+#include /* socket(), struct sockaddr_in6,
+ inet_pton(), connect() */
+#include /* open() */
+#include /* opendir(), struct dirent, readdir()
+ */
+#include /* PRIu16, PRIdMAX, intmax_t,
+ strtoimax() */
+#include /* assert() */
+#include /* perror(), errno */
+#include /* nanosleep(), time() */
+#include /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
+ SIOCSIFFLAGS, if_indextoname(),
+ if_nametoindex(), IF_NAMESIZE */
+#include /* IN6_IS_ADDR_LINKLOCAL,
+ INET_ADDRSTRLEN, INET6_ADDRSTRLEN
+ */
+#include /* close(), SEEK_SET, off_t, write(),
+ getuid(), getgid(), seteuid(),
+ setgid(), pause() */
+#include /* inet_pton(), htons */
+#include /* not, or, and */
+#include /* struct argp_option, error_t, struct
+ argp_state, struct argp,
+ argp_parse(), ARGP_KEY_ARG,
+ ARGP_KEY_END, ARGP_ERR_UNKNOWN */
+#include /* sigemptyset(), sigaddset(),
+ sigaction(), SIGTERM, sig_atomic_t,
+ raise() */
+
+#ifdef __linux__
+#include /* klogctl() */
+#endif /* __linux__ */
+
+/* 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 SECKEY "seckey.txt"
+#define PUBKEY "pubkey.txt"
+
+bool debug = false;
+static const char mandos_protocol_version[] = "1";
+const char *argp_program_version = "mandos-client " VERSION;
+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;
+
+/* global context so signal handler can reach it*/
+mandos_context mc = { .simple_poll = NULL, .server = NULL,
+ .dh_bits = 1024, .priority = "SECURE256"
+ ":!CTYPE-X.509:+CTYPE-OPENPGP" };
+
+sig_atomic_t quit_now = 0;
+int signal_received = 0;
+
+/*
+ * Make additional room in "buffer" for at least BUFFER_SIZE more
+ * bytes. "buffer_capacity" is how much is currently allocated,
+ * "buffer_length" is how much is already used.
+ */
+size_t incbuffer(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(const char *seckey,
+ const char *pubkey, const char *tempdir){
+ gpgme_error_t rc;
+ gpgme_engine_info_t engine_info;
+
+
+ /*
+ * Helper function to insert pub and seckey to the engine keyring.
+ */
+ bool import_key(const char *filename){
+ int ret;
+ int fd;
+ gpgme_data_t pgp_data;
+
+ fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
+ if(fd == -1){
+ perror("open");
+ return false;
+ }
+
+ rc = gpgme_data_new_from_fd(&pgp_data, fd);
+ if(rc != GPG_ERR_NO_ERROR){
+ fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
+ gpgme_strsource(rc), gpgme_strerror(rc));
+ return false;
+ }
+
+ rc = gpgme_op_import(mc.ctx, pgp_data);
+ if(rc != GPG_ERR_NO_ERROR){
+ fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
+ gpgme_strsource(rc), gpgme_strerror(rc));
+ return false;
+ }
+
+ ret = (int)TEMP_FAILURE_RETRY(close(fd));
+ if(ret == -1){
+ perror("close");
+ }
+ gpgme_data_release(pgp_data);
+ return true;
+ }
+
+ if(debug){
+ fprintf(stderr, "Initializing GPGME\n");
+ }
+
+ /* 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 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;
+ 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 = incbuffer(plaintext,
+ (size_t)plaintext_length,
+ plaintext_capacity);
+ if(plaintext_capacity == 0){
+ perror("incbuffer");
+ 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 from
+ -Wunreachable-code */
+ 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(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
+ from
+ -Wunreachable-code
+ */
+ 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(gnutls_session_t *session){
+ int ret;
+ /* GnuTLS session creation */
+ do {
+ ret = gnutls_init(session, GNUTLS_SERVER);
+ if(quit_now){
+ return -1;
+ }
+ } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
+ if(ret != GNUTLS_E_SUCCESS){
+ fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
+ safer_gnutls_strerror(ret));
+ }
+
+ {
+ const char *err;
+ do {
+ ret = gnutls_priority_set_direct(*session, mc.priority, &err);
+ if(quit_now){
+ gnutls_deinit(*session);
+ return -1;
+ }
+ } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
+ if(ret != GNUTLS_E_SUCCESS){
+ fprintf(stderr, "Syntax error at: %s\n", err);
+ fprintf(stderr, "GnuTLS error: %s\n",
+ safer_gnutls_strerror(ret));
+ gnutls_deinit(*session);
+ return -1;
+ }
+ }
+
+ do {
+ ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
+ mc.cred);
+ if(quit_now){
+ gnutls_deinit(*session);
+ return -1;
+ }
+ } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
+ if(ret != GNUTLS_E_SUCCESS){
+ 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,
+ int af){
+ int ret, tcp_sd = -1;
+ ssize_t sret;
+ union {
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+ } to;
+ char *buffer = NULL;
+ char *decrypted_buffer = NULL;
+ size_t buffer_length = 0;
+ size_t buffer_capacity = 0;
+ size_t written;
+ int retval = -1;
+ gnutls_session_t session;
+ int pf; /* Protocol family */
+
+ if(quit_now){
+ return -1;
+ }
+
+ switch(af){
+ case AF_INET6:
+ pf = PF_INET6;
+ break;
+ case AF_INET:
+ pf = PF_INET;
+ break;
+ default:
+ fprintf(stderr, "Bad address family: %d\n", af);
+ return -1;
+ }
+
+ ret = init_gnutls_session(&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, SOCK_STREAM, 0);
+ if(tcp_sd < 0){
+ perror("socket");
+ goto mandos_end;
+ }
+
+ if(quit_now){
+ goto mandos_end;
+ }
+
+ memset(&to, 0, sizeof(to));
+ if(af == AF_INET6){
+ to.in6.sin6_family = (sa_family_t)af;
+ ret = inet_pton(af, ip, &to.in6.sin6_addr);
+ } else { /* IPv4 */
+ to.in.sin_family = (sa_family_t)af;
+ ret = inet_pton(af, ip, &to.in.sin_addr);
+ }
+ if(ret < 0 ){
+ perror("inet_pton");
+ goto mandos_end;
+ }
+ if(ret == 0){
+ fprintf(stderr, "Bad address: %s\n", ip);
+ goto mandos_end;
+ }
+ if(af == AF_INET6){
+ to.in6.sin6_port = htons(port); /* Spurious warnings from
+ -Wconversion and
+ -Wunreachable-code */
+
+ if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
+ (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
+ -Wunreachable-code*/
+ if(if_index == AVAHI_IF_UNSPEC){
+ fprintf(stderr, "An IPv6 link-local address is incomplete"
+ " without a network interface\n");
+ goto mandos_end;
+ }
+ /* Set the network interface number as scope */
+ to.in6.sin6_scope_id = (uint32_t)if_index;
+ }
+ } else {
+ to.in.sin_port = htons(port); /* Spurious warnings from
+ -Wconversion and
+ -Wunreachable-code */
+ }
+
+ if(quit_now){
+ goto mandos_end;
+ }
+
+ if(debug){
+ if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
+ char interface[IF_NAMESIZE];
+ if(if_indextoname((unsigned int)if_index, interface) == NULL){
+ perror("if_indextoname");
+ } else {
+ fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
+ ip, interface, port);
+ }
+ } else {
+ fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
+ port);
+ }
+ char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
+ INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
+ const char *pcret;
+ if(af == AF_INET6){
+ pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
+ sizeof(addrstr));
+ } else {
+ pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
+ sizeof(addrstr));
+ }
+ if(pcret == NULL){
+ perror("inet_ntop");
+ } else {
+ if(strcmp(addrstr, ip) != 0){
+ fprintf(stderr, "Canonical address form: %s\n", addrstr);
+ }
+ }
+ }
+
+ if(quit_now){
+ goto mandos_end;
+ }
+
+ if(af == AF_INET6){
+ ret = connect(tcp_sd, &to.in6, sizeof(to));
+ } else {
+ ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
+ }
+ if(ret < 0){
+ perror("connect");
+ goto mandos_end;
+ }
+
+ if(quit_now){
+ goto mandos_end;
+ }
+
+ const char *out = mandos_protocol_version;
+ written = 0;
+ while(true){
+ size_t out_size = strlen(out);
+ ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
+ out_size - written));
+ if(ret == -1){
+ perror("write");
+ 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(quit_now){
+ goto mandos_end;
+ }
+ }
+
+ if(debug){
+ fprintf(stderr, "Establishing TLS session with %s\n", ip);
+ }
+
+ if(quit_now){
+ goto mandos_end;
+ }
+
+ gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
+
+ if(quit_now){
+ goto mandos_end;
+ }
+
+ do {
+ ret = gnutls_handshake(session);
+ if(quit_now){
+ goto mandos_end;
+ }
+ } 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);
+ }
+ goto mandos_end;
+ }
+
+ /* Read OpenPGP packet that contains the wanted password */
+
+ if(debug){
+ fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
+ ip);
+ }
+
+ while(true){
+
+ if(quit_now){
+ goto mandos_end;
+ }
+
+ buffer_capacity = incbuffer(&buffer, buffer_length,
+ buffer_capacity);
+ if(buffer_capacity == 0){
+ perror("incbuffer");
+ goto mandos_end;
+ }
+
+ if(quit_now){
+ goto mandos_end;
+ }
+
+ sret = gnutls_record_recv(session, buffer+buffer_length,
+ BUFFER_SIZE);
+ if(sret == 0){
+ break;
+ }
+ if(sret < 0){
+ switch(sret){
+ case GNUTLS_E_INTERRUPTED:
+ case GNUTLS_E_AGAIN:
+ break;
+ case GNUTLS_E_REHANDSHAKE:
+ do {
+ ret = gnutls_handshake(session);
+
+ if(quit_now){
+ goto mandos_end;
+ }
+ } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
+ if(ret < 0){
+ fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
+ gnutls_perror(ret);
+ goto mandos_end;
+ }
+ break;
+ default:
+ fprintf(stderr, "Unknown error while reading data from"
+ " encrypted session with Mandos server\n");
+ gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ goto mandos_end;
+ }
+ } else {
+ buffer_length += (size_t) sret;
+ }
+ }
+
+ if(debug){
+ fprintf(stderr, "Closing TLS session\n");
+ }
+
+ if(quit_now){
+ goto mandos_end;
+ }
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ if(quit_now){
+ goto mandos_end;
+ }
+ } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
+
+ if(buffer_length > 0){
+ ssize_t decrypted_buffer_size;
+ decrypted_buffer_size = pgp_packet_decrypt(buffer,
+ buffer_length,
+ &decrypted_buffer);
+ if(decrypted_buffer_size >= 0){
+
+ written = 0;
+ while(written < (size_t) decrypted_buffer_size){
+ if(quit_now){
+ goto mandos_end;
+ }
+
+ 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));
+ }
+ goto mandos_end;
+ }
+ written += (size_t)ret;
+ }
+ retval = 0;
+ }
+ }
+
+ /* Shutdown procedure */
+
+ mandos_end:
+ free(decrypted_buffer);
+ free(buffer);
+ if(tcp_sd >= 0){
+ ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
+ }
+ if(ret == -1){
+ perror("close");
+ }
+ gnutls_deinit(session);
+ if(quit_now){
+ retval = -1;
+ }
+ return retval;
+}
+
+static void resolve_callback(AvahiSServiceResolver *r,
+ AvahiIfIndex interface,
+ AvahiProtocol proto,
+ 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,
+ AVAHI_GCC_UNUSED void* userdata){
+ assert(r);
+
+ /* Called whenever a service has been resolved successfully or
+ timed out */
+
+ if(quit_now){
+ return;
+ }
+
+ 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, %"
+ PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
+ ip, (intmax_t)interface, port);
+ }
+ int ret = start_mandos_communication(ip, port, interface,
+ avahi_proto_to_af(proto));
+ if(ret == 0){
+ avahi_simple_poll_quit(mc.simple_poll);
+ }
+ }
+ }
+ avahi_s_service_resolver_free(r);
+}
+
+static void browse_callback(AvahiSServiceBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags
+ flags,
+ AVAHI_GCC_UNUSED void* userdata){
+ assert(b);
+
+ /* Called whenever a new services becomes available on the LAN or
+ is removed from the LAN */
+
+ if(quit_now){
+ return;
+ }
+
+ 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, protocol, 0,
+ resolve_callback, NULL) == NULL)
+ 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;
+ }
+}
+
+/* stop main loop after sigterm has been called */
+static void handle_sigterm(int sig){
+ if(quit_now){
+ return;
+ }
+ quit_now = 1;
+ signal_received = sig;
+ int old_errno = errno;
+ if(mc.simple_poll != NULL){
+ avahi_simple_poll_quit(mc.simple_poll);
+ }
+ errno = old_errno;
+}
+
+int main(int argc, char *argv[]){
+ AvahiSServiceBrowser *sb = NULL;
+ int error;
+ int ret;
+ intmax_t tmpmax;
+ char *tmp;
+ int exitcode = EXIT_SUCCESS;
+ const char *interface = "eth0";
+ struct ifreq network;
+ int sd = -1;
+ bool take_down_interface = false;
+ uid_t uid;
+ gid_t gid;
+ char *connect_to = NULL;
+ char tempdir[] = "/tmp/mandosXXXXXX";
+ bool tempdir_created = false;
+ AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
+ const char *seckey = PATHDIR "/" SECKEY;
+ const char *pubkey = PATHDIR "/" PUBKEY;
+
+ bool gnutls_initialized = false;
+ bool gpgme_initialized = false;
+ float delay = 2.5f;
+
+ struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
+ struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
+
+ uid = getuid();
+ gid = getgid();
+
+ /* Lower any group privileges we might have, just to be safe */
+ errno = 0;
+ ret = setgid(gid);
+ if(ret == -1){
+ perror("setgid");
+ }
+
+ /* Lower user privileges (temporarily) */
+ errno = 0;
+ ret = seteuid(uid);
+ if(ret == -1){
+ perror("seteuid");
+ }
+
+ if(quit_now){
+ goto end;
+ }
+
+ {
+ struct argp_option options[] = {
+ { .name = "debug", .key = 128,
+ .doc = "Debug mode", .group = 3 },
+ { .name = "connect", .key = 'c',
+ .arg = "ADDRESS:PORT",
+ .doc = "Connect directly to a specific Mandos server",
+ .group = 1 },
+ { .name = "interface", .key = 'i',
+ .arg = "NAME",
+ .doc = "Network interface that will be used to search for"
+ " Mandos servers",
+ .group = 1 },
+ { .name = "seckey", .key = 's',
+ .arg = "FILE",
+ .doc = "OpenPGP secret key file base name",
+ .group = 1 },
+ { .name = "pubkey", .key = 'p',
+ .arg = "FILE",
+ .doc = "OpenPGP public key file base name",
+ .group = 2 },
+ { .name = "dh-bits", .key = 129,
+ .arg = "BITS",
+ .doc = "Bit length of the prime number used in the"
+ " Diffie-Hellman key exchange",
+ .group = 2 },
+ { .name = "priority", .key = 130,
+ .arg = "STRING",
+ .doc = "GnuTLS priority string for the TLS handshake",
+ .group = 1 },
+ { .name = "delay", .key = 131,
+ .arg = "SECONDS",
+ .doc = "Maximum delay to wait for interface startup",
+ .group = 2 },
+ { .name = NULL }
+ };
+
+ error_t parse_opt(int key, char *arg,
+ struct argp_state *state){
+ switch(key){
+ case 128: /* --debug */
+ debug = true;
+ break;
+ case 'c': /* --connect */
+ connect_to = arg;
+ break;
+ case 'i': /* --interface */
+ interface = arg;
+ break;
+ case 's': /* --seckey */
+ seckey = arg;
+ break;
+ case 'p': /* --pubkey */
+ pubkey = arg;
+ break;
+ case 129: /* --dh-bits */
+ errno = 0;
+ tmpmax = strtoimax(arg, &tmp, 10);
+ if(errno != 0 or tmp == arg or *tmp != '\0'
+ or tmpmax != (typeof(mc.dh_bits))tmpmax){
+ fprintf(stderr, "Bad number of DH bits\n");
+ exit(EXIT_FAILURE);
+ }
+ mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
+ break;
+ case 130: /* --priority */
+ mc.priority = arg;
+ break;
+ case 131: /* --delay */
+ errno = 0;
+ delay = strtof(arg, &tmp);
+ if(errno != 0 or tmp == arg or *tmp != '\0'){
+ fprintf(stderr, "Bad delay\n");
+ exit(EXIT_FAILURE);
+ }
+ 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(not debug){
+ avahi_set_log_function(empty_log);
+ }
+
+ /* Initialize Avahi early so avahi_simple_poll_quit() can be called
+ from the signal handler */
+ /* Initialize the pseudo-RNG for Avahi */
+ srand((unsigned int) time(NULL));
+ mc.simple_poll = avahi_simple_poll_new();
+ if(mc.simple_poll == NULL){
+ fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+
+ sigemptyset(&sigterm_action.sa_mask);
+ ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
+ if(ret == -1){
+ perror("sigaddset");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+ ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
+ if(ret == -1){
+ perror("sigaddset");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+ ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
+ if(ret == -1){
+ perror("sigaddset");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+ /* Need to check if the handler is SIG_IGN before handling:
+ | [[info:libc:Initial Signal Actions]] |
+ | [[info:libc:Basic Signal Handling]] |
+ */
+ ret = sigaction(SIGINT, NULL, &old_sigterm_action);
+ if(ret == -1){
+ perror("sigaction");
+ return EXIT_FAILURE;
+ }
+ if(old_sigterm_action.sa_handler != SIG_IGN){
+ ret = sigaction(SIGINT, &sigterm_action, NULL);
+ if(ret == -1){
+ perror("sigaction");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+ }
+ ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
+ if(ret == -1){
+ perror("sigaction");
+ return EXIT_FAILURE;
+ }
+ if(old_sigterm_action.sa_handler != SIG_IGN){
+ ret = sigaction(SIGHUP, &sigterm_action, NULL);
+ if(ret == -1){
+ perror("sigaction");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+ }
+ ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
+ if(ret == -1){
+ perror("sigaction");
+ return EXIT_FAILURE;
+ }
+ if(old_sigterm_action.sa_handler != SIG_IGN){
+ ret = sigaction(SIGTERM, &sigterm_action, NULL);
+ if(ret == -1){
+ perror("sigaction");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+ }
+
+ /* If the interface is down, bring it up */
+ if(interface[0] != '\0'){
+ if_index = (AvahiIfIndex) if_nametoindex(interface);
+ if(if_index == 0){
+ fprintf(stderr, "No such interface: \"%s\"\n", interface);
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+
+ if(quit_now){
+ goto end;
+ }
+
+ /* Re-raise priviliges */
+ errno = 0;
+ ret = seteuid(0);
+ if(ret == -1){
+ perror("seteuid");
+ }
+
+#ifdef __linux__
+ /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
+ messages to mess up the prompt */
+ ret = klogctl(8, NULL, 5);
+ bool restore_loglevel = true;
+ if(ret == -1){
+ restore_loglevel = false;
+ perror("klogctl");
+ }
+#endif /* __linux__ */
+
+ sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
+ if(sd < 0){
+ perror("socket");
+ exitcode = EXIT_FAILURE;
+#ifdef __linux__
+ if(restore_loglevel){
+ ret = klogctl(7, NULL, 0);
+ if(ret == -1){
+ perror("klogctl");
+ }
+ }
+#endif /* __linux__ */
+ /* Lower privileges */
+ errno = 0;
+ ret = seteuid(uid);
+ if(ret == -1){
+ perror("seteuid");
+ }
+ goto end;
+ }
+ strcpy(network.ifr_name, interface);
+ ret = ioctl(sd, SIOCGIFFLAGS, &network);
+ if(ret == -1){
+ perror("ioctl SIOCGIFFLAGS");
+#ifdef __linux__
+ if(restore_loglevel){
+ ret = klogctl(7, NULL, 0);
+ if(ret == -1){
+ perror("klogctl");
+ }
+ }
+#endif /* __linux__ */
+ exitcode = EXIT_FAILURE;
+ /* Lower privileges */
+ errno = 0;
+ ret = seteuid(uid);
+ if(ret == -1){
+ perror("seteuid");
+ }
+ goto end;
+ }
+ if((network.ifr_flags & IFF_UP) == 0){
+ network.ifr_flags |= IFF_UP;
+ take_down_interface = true;
+ ret = ioctl(sd, SIOCSIFFLAGS, &network);
+ if(ret == -1){
+ take_down_interface = false;
+ perror("ioctl SIOCSIFFLAGS");
+ exitcode = EXIT_FAILURE;
+#ifdef __linux__
+ if(restore_loglevel){
+ ret = klogctl(7, NULL, 0);
+ if(ret == -1){
+ perror("klogctl");
+ }
+ }
+#endif /* __linux__ */
+ /* Lower privileges */
+ errno = 0;
+ ret = seteuid(uid);
+ if(ret == -1){
+ perror("seteuid");
+ }
+ goto end;
+ }
+ }
+ /* sleep checking until interface is running */
+ for(int i=0; i < delay * 4; i++){
+ ret = ioctl(sd, SIOCGIFFLAGS, &network);
+ if(ret == -1){
+ perror("ioctl SIOCGIFFLAGS");
+ } else if(network.ifr_flags & IFF_RUNNING){
+ break;
+ }
+ struct timespec sleeptime = { .tv_nsec = 250000000 };
+ ret = nanosleep(&sleeptime, NULL);
+ if(ret == -1 and errno != EINTR){
+ perror("nanosleep");
+ }
+ }
+ if(not take_down_interface){
+ /* We won't need the socket anymore */
+ ret = (int)TEMP_FAILURE_RETRY(close(sd));
+ if(ret == -1){
+ perror("close");
+ }
+ }
+#ifdef __linux__
+ if(restore_loglevel){
+ /* Restores kernel loglevel to default */
+ ret = klogctl(7, NULL, 0);
+ if(ret == -1){
+ perror("klogctl");
+ }
+ }
+#endif /* __linux__ */
+ /* Lower privileges */
+ errno = 0;
+ if(take_down_interface){
+ /* Lower privileges */
+ ret = seteuid(uid);
+ if(ret == -1){
+ perror("seteuid");
+ }
+ } else {
+ /* Lower privileges permanently */
+ ret = setuid(uid);
+ if(ret == -1){
+ perror("setuid");
+ }
+ }
+ }
+
+ if(quit_now){
+ goto end;
+ }
+
+ ret = init_gnutls_global(pubkey, seckey);
+ if(ret == -1){
+ fprintf(stderr, "init_gnutls_global failed\n");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ } else {
+ gnutls_initialized = true;
+ }
+
+ if(quit_now){
+ goto end;
+ }
+
+ tempdir_created = true;
+ if(mkdtemp(tempdir) == NULL){
+ tempdir_created = false;
+ perror("mkdtemp");
+ goto end;
+ }
+
+ if(quit_now){
+ goto end;
+ }
+
+ if(not init_gpgme(pubkey, seckey, tempdir)){
+ fprintf(stderr, "init_gpgme failed\n");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ } else {
+ gpgme_initialized = true;
+ }
+
+ if(quit_now){
+ goto end;
+ }
+
+ if(connect_to != NULL){
+ /* Connect directly, do not use Zeroconf */
+ /* (Mainly meant for debugging) */
+ char *address = strrchr(connect_to, ':');
+ if(address == NULL){
+ fprintf(stderr, "No colon in address\n");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+
+ if(quit_now){
+ goto end;
+ }
+
+ uint16_t port;
+ errno = 0;
+ tmpmax = strtoimax(address+1, &tmp, 10);
+ if(errno != 0 or tmp == address+1 or *tmp != '\0'
+ or tmpmax != (uint16_t)tmpmax){
+ fprintf(stderr, "Bad port number\n");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+
+ if(quit_now){
+ goto end;
+ }
+
+ port = (uint16_t)tmpmax;
+ *address = '\0';
+ address = connect_to;
+ /* Colon in address indicates IPv6 */
+ int af;
+ if(strchr(address, ':') != NULL){
+ af = AF_INET6;
+ } else {
+ af = AF_INET;
+ }
+
+ if(quit_now){
+ goto end;
+ }
+
+ ret = start_mandos_communication(address, port, if_index, af);
+ if(ret < 0){
+ exitcode = EXIT_FAILURE;
+ } else {
+ exitcode = EXIT_SUCCESS;
+ }
+ goto end;
+ }
+
+ if(quit_now){
+ goto end;
+ }
+
+ {
+ AvahiServerConfig config;
+ /* Do not publish any local Zeroconf records */
+ avahi_server_config_init(&config);
+ config.publish_hinfo = 0;
+ config.publish_addresses = 0;
+ config.publish_workstation = 0;
+ config.publish_domain = 0;
+
+ /* Allocate a new server */
+ mc.server = avahi_server_new(avahi_simple_poll_get
+ (mc.simple_poll), &config, NULL,
+ NULL, &error);
+
+ /* Free the Avahi configuration data */
+ avahi_server_config_free(&config);
+ }
+
+ /* Check if creating the Avahi server object succeeded */
+ if(mc.server == NULL){
+ fprintf(stderr, "Failed to create Avahi server: %s\n",
+ avahi_strerror(error));
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+
+ if(quit_now){
+ goto end;
+ }
+
+ /* Create the Avahi service browser */
+ sb = avahi_s_service_browser_new(mc.server, if_index,
+ AVAHI_PROTO_UNSPEC, "_mandos._tcp",
+ NULL, 0, browse_callback, NULL);
+ if(sb == NULL){
+ fprintf(stderr, "Failed to create service browser: %s\n",
+ avahi_strerror(avahi_server_errno(mc.server)));
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+
+ if(quit_now){
+ goto end;
+ }
+
+ /* Run the main loop */
+
+ if(debug){
+ fprintf(stderr, "Starting Avahi loop search\n");
+ }
+
+ avahi_simple_poll_loop(mc.simple_poll);
+
+ end:
+
+ if(debug){
+ fprintf(stderr, "%s exiting\n", argv[0]);
+ }
+
+ /* Cleanup things */
+ if(sb != NULL)
+ avahi_s_service_browser_free(sb);
+
+ if(mc.server != NULL)
+ avahi_server_free(mc.server);
+
+ if(mc.simple_poll != NULL)
+ avahi_simple_poll_free(mc.simple_poll);
+
+ if(gnutls_initialized){
+ gnutls_certificate_free_credentials(mc.cred);
+ gnutls_global_deinit();
+ gnutls_dh_params_deinit(mc.dh_params);
+ }
+
+ if(gpgme_initialized){
+ gpgme_release(mc.ctx);
+ }
+
+ /* Take down the network interface */
+ if(take_down_interface){
+ /* Re-raise priviliges */
+ errno = 0;
+ ret = seteuid(0);
+ if(ret == -1){
+ perror("seteuid");
+ }
+ if(geteuid() == 0){
+ ret = ioctl(sd, SIOCGIFFLAGS, &network);
+ if(ret == -1){
+ perror("ioctl SIOCGIFFLAGS");
+ } else if(network.ifr_flags & IFF_UP) {
+ network.ifr_flags &= ~IFF_UP; /* clear flag */
+ ret = ioctl(sd, SIOCSIFFLAGS, &network);
+ if(ret == -1){
+ perror("ioctl SIOCSIFFLAGS");
+ }
+ }
+ ret = (int)TEMP_FAILURE_RETRY(close(sd));
+ if(ret == -1){
+ perror("close");
+ }
+ /* Lower privileges permanently */
+ errno = 0;
+ ret = setuid(uid);
+ if(ret == -1){
+ perror("setuid");
+ }
+ }
+ }
+
+ /* Removes the temp directory used by GPGME */
+ if(tempdir_created){
+ DIR *d;
+ struct dirent *direntry;
+ d = opendir(tempdir);
+ if(d == NULL){
+ if(errno != ENOENT){
+ perror("opendir");
+ }
+ } else {
+ while(true){
+ direntry = readdir(d);
+ if(direntry == NULL){
+ break;
+ }
+ /* Skip "." and ".." */
+ if(direntry->d_name[0] == '.'
+ and (direntry->d_name[1] == '\0'
+ or (direntry->d_name[1] == '.'
+ and direntry->d_name[2] == '\0'))){
+ continue;
+ }
+ char *fullname = NULL;
+ ret = asprintf(&fullname, "%s/%s", tempdir,
+ direntry->d_name);
+ if(ret < 0){
+ perror("asprintf");
+ continue;
+ }
+ ret = remove(fullname);
+ if(ret == -1){
+ fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
+ strerror(errno));
+ }
+ free(fullname);
+ }
+ closedir(d);
+ }
+ ret = rmdir(tempdir);
+ if(ret == -1 and errno != ENOENT){
+ perror("rmdir");
+ }
+ }
+
+ if(quit_now){
+ sigemptyset(&old_sigterm_action.sa_mask);
+ old_sigterm_action.sa_handler = SIG_DFL;
+ ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
+ &old_sigterm_action,
+ NULL));
+ if(ret == -1){
+ perror("sigaction");
+ }
+ do {
+ ret = raise(signal_received);
+ } while(ret != 0 and errno == EINTR);
+ if(ret != 0){
+ perror("raise");
+ abort();
+ }
+ TEMP_FAILURE_RETRY(pause());
+ }
+
+ return exitcode;
+}
=== added file 'plugins.d/mandos-client.xml'
--- plugins.d/mandos-client.xml 1970-01-01 00:00:00 +0000
+++ plugins.d/mandos-client.xml 2009-02-09 02:01:13 +0000
@@ -0,0 +1,652 @@
+
+
+
+
+%common;
+]>
+
+
+
+ Mandos Manual
+
+ Mandos
+ &version;
+ &TIMESTAMP;
+
+
+ Björn
+ Påhlsson
+
+ belorn@fukt.bsnet.se
+
+
+
+ Teddy
+ Hogeborn
+
+ teddy@fukt.bsnet.se
+
+
+
+
+ 2008
+ 2009
+ Teddy Hogeborn
+ Björn Påhlsson
+
+
+
+
+
+ &COMMANDNAME;
+ 8mandos
+
+
+
+ &COMMANDNAME;
+
+ Client for Mandos
+
+
+
+
+
+ &COMMANDNAME;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ &COMMANDNAME;
+
+
+
+
+
+
+ &COMMANDNAME;
+
+
+
+ &COMMANDNAME;
+
+
+
+
+
+
+
+
+ DESCRIPTION
+
+ &COMMANDNAME; is a client program that
+ communicates with mandos8
+ to get a password. In slightly more detail, this client program
+ brings up a network interface, uses the interface’s IPv6
+ link-local address to get network connectivity, uses Zeroconf to
+ find servers on the local network, and communicates with servers
+ using TLS with an OpenPGP key to ensure authenticity and
+ confidentiality. This client program keeps running, trying all
+ servers on the network, until it receives a satisfactory reply
+ or a TERM signal is received. If no servers are found, or after
+ all servers have been tried, it waits indefinitely for new
+ servers to appear.
+
+
+ This program is not meant to be run directly; it is really meant
+ to run as a plugin of the Mandos
+ plugin-runner
+ 8mandos, which runs in the
+ initial RAM disk environment because it is
+ specified as a keyscript
in the
+ crypttab5
+ file.
+
+
+
+
+ PURPOSE
+
+ The purpose of this is to enable remote and unattended
+ rebooting of client host computer with an
+ encrypted root file system. See for details.
+
+
+
+
+ OPTIONS
+
+ This program is commonly not invoked from the command line; it
+ is normally started by the Mandos
+ plugin runner, see plugin-runner8mandos
+ . Any command line options this program accepts
+ are therefore normally provided by the plugin runner, and not
+ directly.
+
+
+
+
+
+
+
+
+ Do not use Zeroconf to locate servers. Connect directly
+ to only one specified Mandos
+ server. Note that an IPv6 address has colon characters in
+ it, so the last colon character is
+ assumed to separate the address from the port number.
+
+
+ This option is normally only useful for testing and
+ debugging.
+
+
+
+
+
+
+
+
+
+ Network interface that will be brought up and scanned for
+ Mandos servers to connect to. The default is
+ eth0
.
+
+
+ If the option is used, this
+ specifies the interface to use to connect to the address
+ given.
+
+
+ Note that since this program will normally run in the
+ initial RAM disk environment, the interface must be an
+ interface which exists at that stage. Thus, the interface
+ can not be a pseudo-interface such as br0
+ or tun0
; such interfaces will not exist
+ until much later in the boot process, and can not be used
+ by this program.
+
+
+ NAME can be the empty string;
+ this will not use any specific interface, and will not
+ bring up an interface on startup. This is not
+ recommended, and only meant for advanced users.
+
+
+
+
+
+
+
+
+
+ OpenPGP public key file name. The default name is
+ /conf/conf.d/mandos/pubkey.txt
.
+
+
+
+
+
+
+
+
+
+ OpenPGP secret key file name. The default name is
+ /conf/conf.d/mandos/seckey.txt
.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sets the number of bits to use for the prime number in the
+ TLS Diffie-Hellman key exchange. Default is 1024.
+
+
+
+
+
+
+
+
+ After bringing the network interface up, the program waits
+ for the interface to arrive in a running
+ state before proceeding. During this time, the kernel log
+ level will be lowered to reduce clutter on the system
+ console, alleviating any other plugins which might be
+ using the system console. This option sets the upper
+ limit of seconds to wait. The default is 2.5 seconds.
+
+
+
+
+
+
+
+
+ Enable debug mode. This will enable a lot of output to
+ standard error about what the program is doing. The
+ program will still perform all other functions normally.
+
+
+ It will also enable debug mode in the Avahi and GnuTLS
+ libraries, making them print large amounts of debugging
+ output.
+
+
+
+
+
+
+
+
+
+ Gives a help message about options and their meanings.
+
+
+
+
+
+
+
+
+ Gives a short usage message.
+
+
+
+
+
+
+
+
+
+ Prints the program version.
+
+
+
+
+
+
+
+ OVERVIEW
+
+
+ This program is the client part. It is a plugin started by
+ plugin-runner
+ 8mandos which will run in
+ an initial RAM disk environment.
+
+
+ This program could, theoretically, be used as a keyscript in
+ /etc/crypttab, but it would then be
+ impossible to enter a password for the encrypted root disk at
+ the console, since this program does not read from the console
+ at all. This is why a separate plugin runner (
+ plugin-runner
+ 8mandos) is used to run
+ both this program and others in in parallel,
+ one of which will prompt for passwords on
+ the system console.
+
+
+
+
+ EXIT STATUS
+
+ This program will exit with a successful (zero) exit status if a
+ server could be found and the password received from it could be
+ successfully decrypted and output on standard output. The
+ program will exit with a non-zero exit status only if a critical
+ error occurs. Otherwise, it will forever connect to new
+ Mandos servers as they appear, trying
+ to get a decryptable password and print it.
+
+
+
+
+ ENVIRONMENT
+
+ This program does not use any environment variables, not even
+ the ones provided by cryptsetup8
+ .
+
+
+
+
+ FILES
+
+
+ /conf/conf.d/mandos/pubkey.txt
+ /conf/conf.d/mandos/seckey.txt
+
+
+ OpenPGP public and private key files, in ASCII
+ Armor
format. These are the default file names,
+ they can be changed with the and
+ options.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ EXAMPLE
+
+ Note that normally, command line options will not be given
+ directly, but via options for the Mandos plugin-runner
+ 8mandos.
+
+
+
+ Normal invocation needs no options, if the network interface
+ is eth0
:
+
+
+ &COMMANDNAME;
+
+
+
+
+ Search for Mandos servers (and connect to them) using another
+ interface:
+
+
+
+ &COMMANDNAME; --interface eth1
+
+
+
+
+ Run in debug mode, and use a custom key:
+
+
+
+
+&COMMANDNAME; --debug --pubkey keydir/pubkey.txt --seckey keydir/seckey.txt
+
+
+
+
+
+ Run in debug mode, with a custom key, and do not use Zeroconf
+ to locate a server; connect directly to the IPv6 link-local
+ address fe80::aede:48ff:fe71:f6f2
, port 4711,
+ using interface eth2:
+
+
+
+
+&COMMANDNAME; --debug --pubkey keydir/pubkey.txt --seckey keydir/seckey.txt --connect fe80::aede:48ff:fe71:f6f2:4711 --interface eth2
+
+
+
+
+
+
+ SECURITY
+
+ This program is set-uid to root, but will switch back to the
+ original (and presumably non-privileged) user and group after
+ bringing up the network interface.
+
+
+ To use this program for its intended purpose (see ), the password for the root file system will
+ have to be given out to be stored in a server computer, after
+ having been encrypted using an OpenPGP key. This encrypted data
+ which will be stored in a server can only be decrypted by the
+ OpenPGP key, and the data will only be given out to those
+ clients who can prove they actually have that key. This key,
+ however, is stored unencrypted on the client side in its initial
+ RAM disk image file system. This is normally
+ readable by all, but this is normally fixed during installation
+ of this program; file permissions are set so that no-one is able
+ to read that file.
+
+
+ The only remaining weak point is that someone with physical
+ access to the client hard drive might turn off the client
+ computer, read the OpenPGP keys directly from the hard drive,
+ and communicate with the server. To safeguard against this, the
+ server is supposed to notice the client disappearing and stop
+ giving out the encrypted data. Therefore, it is important to
+ set the timeout and checker interval values tightly on the
+ server. See mandos8.
+
+
+ It will also help if the checker program on the server is
+ configured to request something from the client which can not be
+ spoofed by someone else on the network, unlike unencrypted
+ ICMP echo (ping
) replies.
+
+
+ Note: This makes it completely insecure to
+ have Mandos clients which dual-boot
+ to another operating system which is not
+ trusted to keep the initial RAM disk image
+ confidential.
+
+
+
+
+ SEE ALSO
+
+ cryptsetup
+ 8,
+ crypttab
+ 5,
+ mandos
+ 8,
+ password-prompt
+ 8mandos,
+ plugin-runner
+ 8mandos
+
+
+
+
+ Zeroconf
+
+
+
+ Zeroconf is the network protocol standard used for finding
+ Mandos servers on the local network.
+
+
+
+
+
+ Avahi
+
+
+
+ Avahi is the library this program calls to find Zeroconf
+ services.
+
+
+
+
+
+ GnuTLS
+
+
+
+ GnuTLS is the library this client uses to implement TLS for
+ communicating securely with the server, and at the same time
+ send the public OpenPGP key to the server.
+
+
+
+
+
+ GPGME
+
+
+
+ GPGME is the library used to decrypt the OpenPGP data sent
+ by the server.
+
+
+
+
+
+ RFC 4291: IP Version 6 Addressing
+ Architecture
+
+
+
+
+ Section 2.2: Text Representation of
+ Addresses
+
+
+
+ Section 2.5.5.2: IPv4-Mapped IPv6
+ Address
+
+
+
+ Section 2.5.6, Link-Local IPv6 Unicast
+ Addresses
+
+
+ This client uses IPv6 link-local addresses, which are
+ immediately usable since a link-local addresses is
+ automatically assigned to a network interfaces when it
+ is brought up.
+
+
+
+
+
+
+
+
+ RFC 4346: The Transport Layer Security (TLS)
+ Protocol Version 1.1
+
+
+
+ TLS 1.1 is the protocol implemented by GnuTLS.
+
+
+
+
+
+ RFC 4880: OpenPGP Message Format
+
+
+
+ The data received from the server is binary encrypted
+ OpenPGP data.
+
+
+
+
+
+ RFC 5081: Using OpenPGP Keys for Transport Layer
+ Security
+
+
+
+ This is implemented by GnuTLS and used by this program so
+ that OpenPGP keys can be used.
+
+
+
+
+
+
+
+
+
+
+
+
=== added file 'plugins.d/password-prompt.c'
--- plugins.d/password-prompt.c 1970-01-01 00:00:00 +0000
+++ plugins.d/password-prompt.c 2009-09-07 07:48:59 +0000
@@ -0,0 +1,301 @@
+/* -*- coding: utf-8; mode: c; mode: orgtbl -*- */
+/*
+ * Password-prompt - Read a password from the terminal and print it
+ *
+ * Copyright © 2008,2009 Teddy Hogeborn
+ * Copyright © 2008,2009 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 /* 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,
+ raise() */
+#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 sig_atomic_t quit_now = 0;
+int signal_received;
+bool debug = false;
+const char *argp_program_version = "password-prompt " VERSION;
+const char *argp_program_bug_address = "";
+
+static void termination_handler(int signum){
+ if(quit_now){
+ return;
+ }
+ quit_now = 1;
+ signal_received = signum;
+}
+
+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){
+ 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);
+ ret = sigaddset(&new_action.sa_mask, SIGINT);
+ if(ret == -1){
+ perror("sigaddset");
+ return EXIT_FAILURE;
+ }
+ ret = sigaddset(&new_action.sa_mask, SIGHUP);
+ if(ret == -1){
+ perror("sigaddset");
+ return EXIT_FAILURE;
+ }
+ ret = sigaddset(&new_action.sa_mask, SIGTERM);
+ if(ret == -1){
+ perror("sigaddset");
+ return EXIT_FAILURE;
+ }
+ /* Need to check if the handler is SIG_IGN before handling:
+ | [[info:libc:Initial Signal Actions]] |
+ | [[info:libc:Basic Signal Handling]] |
+ */
+ ret = sigaction(SIGINT, NULL, &old_action);
+ if(ret == -1){
+ 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 nonzero, we were interrupted by a signal, and
+ will print that later, so no need to show this too. */
+ fprintf(stderr, "getline() returned 0, retrying.\n");
+ }
+ }
+
+ free(buffer);
+
+ if(debug){
+ fprintf(stderr, "Restoring terminal attributes\n");
+ }
+ if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
+ perror("tcsetattr+echo");
+ }
+
+ if(quit_now){
+ sigemptyset(&old_action.sa_mask);
+ old_action.sa_handler = SIG_DFL;
+ ret = sigaction(signal_received, &old_action, NULL);
+ if(ret == -1){
+ perror("sigaction");
+ }
+ raise(signal_received);
+ }
+
+ 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 2009-01-04 21:54:55 +0000
@@ -0,0 +1,308 @@
+
+
+
+
+%common;
+]>
+
+
+
+ Mandos Manual
+
+ Mandos
+ &version;
+ &TIMESTAMP;
+
+
+ Björn
+ Påhlsson
+
+ belorn@fukt.bsnet.se
+
+
+
+ Teddy
+ Hogeborn
+
+ teddy@fukt.bsnet.se
+
+
+
+
+ 2008
+ 2009
+ Teddy Hogeborn
+ Björn Påhlsson
+
+
+
+
+
+ &COMMANDNAME;
+ 8mandos
+
+
+
+ &COMMANDNAME;
+ Prompt for a password and output it.
+
+
+
+
+ &COMMANDNAME;
+
+
+ PREFIX
+
+
+
+
+
+ &COMMANDNAME;
+
+
+
+
+
+
+ &COMMANDNAME;
+
+
+
+ &COMMANDNAME;
+
+
+
+
+
+
+
+
+ DESCRIPTION
+
+ All &COMMANDNAME; does is prompt for a
+ password and output any given password to standard output.
+
+
+ This program is not very useful on its own. This program is
+ really meant to run as a plugin in the Mandos client-side system, where it is used as a
+ fallback and alternative to retrieving passwords from a
+ Mandos server.
+
+
+ This program is little more than a getpass3
+ wrapper, although actual use of that function is not guaranteed
+ or implied.
+
+
+
+
+ OPTIONS
+
+ This program is commonly not invoked from the command line; it
+ is normally started by the Mandos
+ plugin runner, see plugin-runner8mandos
+ . Any command line options this program accepts
+ are therefore normally provided by the plugin runner, and not
+ directly.
+
+
+
+
+
+
+
+
+ Prefix string shown before the password prompt.
+
+
+
+
+
+
+
+
+ Enable debug mode. This will enable a lot of output to
+ standard error about what the program is doing. The
+ program will still perform all other functions normally.
+
+
+
+
+
+
+
+
+
+ Gives a help message about options and their meanings.
+
+
+
+
+
+
+
+
+ Gives a short usage message.
+
+
+
+
+
+
+
+
+
+ Prints the program version.
+
+
+
+
+
+
+
+ EXIT STATUS
+
+ If exit status is 0, the output from the program is the password
+ as it was read. Otherwise, if exit status is other than 0, the
+ program has encountered an error, and any output so far could be
+ corrupt and/or truncated, and should therefore be ignored.
+
+
+
+
+ ENVIRONMENT
+
+
+ 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/splashy.c'
--- plugins.d/splashy.c 1970-01-01 00:00:00 +0000
+++ plugins.d/splashy.c 2009-09-16 23:28:39 +0000
@@ -0,0 +1,352 @@
+/* -*- coding: utf-8 -*- */
+/*
+ * Splashy - Read a password from splashy and output it
+ *
+ * Copyright © 2008,2009 Teddy Hogeborn
+ * Copyright © 2008,2009 Björn Påhlsson
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * .
+ *
+ * Contact the authors at .
+ */
+
+#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
+#include /* sig_atomic_t, struct sigaction,
+ sigemptyset(), sigaddset(), SIGINT,
+ SIGHUP, SIGTERM, sigaction,
+ SIG_IGN, kill(), SIGKILL */
+#include /* NULL */
+#include /* getenv() */
+#include /* asprintf(), perror() */
+#include /* EXIT_FAILURE, free(),
+ EXIT_SUCCESS */
+#include /* pid_t, DIR, struct dirent,
+ ssize_t */
+#include /* opendir(), readdir(), closedir() */
+#include /* intmax_t, strtoimax() */
+#include /* struct stat, lstat(), S_ISLNK */
+#include /* not, or, and */
+#include /* readlink(), fork(), execl(),
+ sleep(), dup2() STDERR_FILENO,
+ STDOUT_FILENO, _exit(),
+ pause() */
+#include /* memcmp() */
+#include /* errno */
+#include /* waitpid(), WIFEXITED(),
+ WEXITSTATUS() */
+
+sig_atomic_t interrupted_by_signal = 0;
+int signal_received;
+
+static void termination_handler(int signum){
+ if(interrupted_by_signal){
+ return;
+ }
+ interrupted_by_signal = 1;
+ signal_received = signum;
+}
+
+int main(__attribute__((unused))int argc,
+ __attribute__((unused))char **argv){
+ int ret = 0;
+ char *prompt = NULL;
+ DIR *proc_dir = NULL;
+ pid_t splashy_pid = 0;
+ pid_t splashy_command_pid = 0;
+
+ /* Create prompt string */
+ {
+ const char *const cryptsource = getenv("cryptsource");
+ const char *const crypttarget = getenv("crypttarget");
+ const char *const prompt_start = "getpass "
+ "Enter passphrase to unlock the disk";
+
+ if(cryptsource == NULL){
+ if(crypttarget == NULL){
+ ret = asprintf(&prompt, "%s: ", prompt_start);
+ } else {
+ ret = asprintf(&prompt, "%s (%s): ", prompt_start,
+ crypttarget);
+ }
+ } else {
+ if(crypttarget == NULL){
+ ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
+ } else {
+ ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
+ cryptsource, crypttarget);
+ }
+ }
+ if(ret == -1){
+ prompt = NULL;
+ goto failure;
+ }
+ }
+
+ /* Find splashy process */
+ {
+ const char splashy_name[] = "/sbin/splashy";
+ proc_dir = opendir("/proc");
+ if(proc_dir == NULL){
+ perror("opendir");
+ goto failure;
+ }
+ for(struct dirent *proc_ent = readdir(proc_dir);
+ proc_ent != NULL;
+ proc_ent = readdir(proc_dir)){
+ pid_t pid;
+ {
+ intmax_t tmpmax;
+ char *tmp;
+ errno = 0;
+ tmpmax = strtoimax(proc_ent->d_name, &tmp, 10);
+ if(errno != 0 or tmp == proc_ent->d_name or *tmp != '\0'
+ or tmpmax != (pid_t)tmpmax){
+ /* Not a process */
+ continue;
+ }
+ pid = (pid_t)tmpmax;
+ }
+ /* Find the executable name by doing readlink() on the
+ /proc//exe link */
+ char exe_target[sizeof(splashy_name)];
+ ssize_t sret;
+ {
+ char *exe_link;
+ ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
+ if(ret == -1){
+ perror("asprintf");
+ goto failure;
+ }
+
+ /* Check that it refers to a symlink owned by root:root */
+ struct stat exe_stat;
+ ret = lstat(exe_link, &exe_stat);
+ if(ret == -1){
+ if(errno == ENOENT){
+ free(exe_link);
+ continue;
+ }
+ perror("lstat");
+ free(exe_link);
+ goto failure;
+ }
+ if(not S_ISLNK(exe_stat.st_mode)
+ or exe_stat.st_uid != 0
+ or exe_stat.st_gid != 0){
+ free(exe_link);
+ continue;
+ }
+
+ sret = readlink(exe_link, exe_target, sizeof(exe_target));
+ free(exe_link);
+ }
+ if((sret == ((ssize_t)sizeof(exe_target)-1))
+ and (memcmp(splashy_name, exe_target,
+ sizeof(exe_target)-1) == 0)){
+ splashy_pid = pid;
+ break;
+ }
+ }
+ closedir(proc_dir);
+ proc_dir = NULL;
+ }
+ if(splashy_pid == 0){
+ goto failure;
+ }
+
+ /* Set up the signal handler */
+ {
+ struct sigaction old_action,
+ new_action = { .sa_handler = termination_handler,
+ .sa_flags = 0 };
+ sigemptyset(&new_action.sa_mask);
+ ret = sigaddset(&new_action.sa_mask, SIGINT);
+ if(ret == -1){
+ perror("sigaddset");
+ goto failure;
+ }
+ ret = sigaddset(&new_action.sa_mask, SIGHUP);
+ if(ret == -1){
+ perror("sigaddset");
+ goto failure;
+ }
+ ret = sigaddset(&new_action.sa_mask, SIGTERM);
+ if(ret == -1){
+ perror("sigaddset");
+ goto failure;
+ }
+ ret = sigaction(SIGINT, NULL, &old_action);
+ if(ret == -1){
+ perror("sigaction");
+ goto failure;
+ }
+ if(old_action.sa_handler != SIG_IGN){
+ ret = sigaction(SIGINT, &new_action, NULL);
+ if(ret == -1){
+ perror("sigaction");
+ goto failure;
+ }
+ }
+ ret = sigaction(SIGHUP, NULL, &old_action);
+ if(ret == -1){
+ perror("sigaction");
+ goto failure;
+ }
+ if(old_action.sa_handler != SIG_IGN){
+ ret = sigaction(SIGHUP, &new_action, NULL);
+ if(ret == -1){
+ perror("sigaction");
+ goto failure;
+ }
+ }
+ ret = sigaction(SIGTERM, NULL, &old_action);
+ if(ret == -1){
+ perror("sigaction");
+ goto failure;
+ }
+ if(old_action.sa_handler != SIG_IGN){
+ ret = sigaction(SIGTERM, &new_action, NULL);
+ if(ret == -1){
+ perror("sigaction");
+ goto failure;
+ }
+ }
+ }
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+
+ /* Fork off the splashy command to prompt for password */
+ splashy_command_pid = fork();
+ if(splashy_command_pid != 0 and interrupted_by_signal){
+ goto failure;
+ }
+ if(splashy_command_pid == -1){
+ perror("fork");
+ goto failure;
+ }
+ /* Child */
+ if(splashy_command_pid == 0){
+ if(not interrupted_by_signal){
+ const char splashy_command[] = "/sbin/splashy_update";
+ execl(splashy_command, splashy_command, prompt, (char *)NULL);
+ perror("execl");
+ }
+ free(prompt);
+ _exit(EXIT_FAILURE);
+ }
+
+ /* Parent */
+ free(prompt);
+ prompt = NULL;
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+
+ /* Wait for command to complete */
+ {
+ int status;
+ do {
+ ret = waitpid(splashy_command_pid, &status, 0);
+ } while(ret == -1 and errno == EINTR
+ and not interrupted_by_signal);
+ if(interrupted_by_signal){
+ goto failure;
+ }
+ if(ret == -1){
+ perror("waitpid");
+ if(errno == ECHILD){
+ splashy_command_pid = 0;
+ }
+ } else {
+ /* The child process has exited */
+ splashy_command_pid = 0;
+ if(WIFEXITED(status) and WEXITSTATUS(status) == 0){
+ return EXIT_SUCCESS;
+ }
+ }
+ }
+
+ failure:
+
+ free(prompt);
+
+ if(proc_dir != NULL){
+ TEMP_FAILURE_RETRY(closedir(proc_dir));
+ }
+
+ if(splashy_command_pid != 0){
+ TEMP_FAILURE_RETRY(kill(splashy_command_pid, SIGTERM));
+
+ TEMP_FAILURE_RETRY(kill(splashy_pid, SIGTERM));
+ sleep(2);
+ while(TEMP_FAILURE_RETRY(kill(splashy_pid, 0)) == 0){
+ TEMP_FAILURE_RETRY(kill(splashy_pid, SIGKILL));
+ sleep(1);
+ }
+ pid_t new_splashy_pid = (pid_t)TEMP_FAILURE_RETRY(fork());
+ if(new_splashy_pid == 0){
+ /* Child; will become new splashy process */
+
+ /* Make the effective user ID (root) the only user ID instead of
+ the real user ID (_mandos) */
+ ret = setuid(geteuid());
+ if(ret == -1){
+ perror("setuid");
+ }
+
+ setsid();
+ ret = chdir("/");
+ if(ret == -1){
+ perror("chdir");
+ }
+/* if(fork() != 0){ */
+/* _exit(EXIT_SUCCESS); */
+/* } */
+ ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace stdout */
+ if(ret == -1){
+ perror("dup2");
+ _exit(EXIT_FAILURE);
+ }
+
+ execl("/sbin/splashy", "/sbin/splashy", "boot", (char *)NULL);
+ perror("execl");
+ _exit(EXIT_FAILURE);
+ }
+ }
+
+ if(interrupted_by_signal){
+ struct sigaction signal_action;
+ sigemptyset(&signal_action.sa_mask);
+ signal_action.sa_handler = SIG_DFL;
+ ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
+ &signal_action, NULL));
+ if(ret == -1){
+ perror("sigaction");
+ }
+ do {
+ ret = raise(signal_received);
+ } while(ret != 0 and errno == EINTR);
+ if(ret != 0){
+ perror("raise");
+ abort();
+ }
+ TEMP_FAILURE_RETRY(pause());
+ }
+
+ return EXIT_FAILURE;
+}
=== added file 'plugins.d/splashy.xml'
--- plugins.d/splashy.xml 1970-01-01 00:00:00 +0000
+++ plugins.d/splashy.xml 2009-01-04 21:54:55 +0000
@@ -0,0 +1,283 @@
+
+
+
+
+%common;
+]>
+
+
+
+ Mandos Manual
+
+ Mandos
+ &version;
+ &TIMESTAMP;
+
+
+ Björn
+ Påhlsson
+
+ belorn@fukt.bsnet.se
+
+
+
+ Teddy
+ Hogeborn
+
+ teddy@fukt.bsnet.se
+
+
+
+
+ 2008
+ 2009
+ Teddy Hogeborn
+ Björn Påhlsson
+
+
+
+
+
+ &COMMANDNAME;
+ 8mandos
+
+
+
+ &COMMANDNAME;
+ Mandos plugin to use splashy to get a
+ password.
+
+
+
+
+ &COMMANDNAME;
+
+
+
+
+ DESCRIPTION
+
+ This program prompts for a password using
+ splashy_update
+ 8 and outputs any given
+ password to standard output. If no splashy8
+ process can be found, this program will immediately exit with an
+ exit code indicating failure.
+
+
+ This program is not very useful on its own. This program is
+ really meant to run as a plugin in the Mandos client-side system, where it is used as a
+ fallback and alternative to retrieving passwords from a
+ Mandos server.
+
+
+ If this program is killed (presumably by
+ plugin-runner
+ 8mandos because some other
+ plugin provided the password), it cannot tell
+ splashy8
+ to abort requesting a password, because
+ splashy
+ 8 does not support this.
+ Therefore, this program will then kill the
+ running splashy
+ 8 process and start a
+ new one, using boot
as the only argument.
+
+
+
+
+ OPTIONS
+
+ This program takes no options.
+
+
+
+
+ EXIT STATUS
+
+ If exit status is 0, the output from the program is the password
+ as it was read. Otherwise, if exit status is other than 0, the
+ program was interrupted or encountered an error, and any output
+ so far could be corrupt and/or truncated, and should therefore
+ be ignored.
+
+
+
+
+ ENVIRONMENT
+
+
+ cryptsource
+ crypttarget
+
+
+ If set, these environment variables will be assumed to
+ contain the source device name and the target device
+ mapper name, respectively, and will be shown as part of
+ the prompt.
+
+
+ These variables will normally be inherited from
+ plugin-runner
+ 8mandos, which will
+ normally have inherited them from
+ /scripts/local-top/cryptroot in the
+ initial RAM disk environment, which will
+ have set them from parsing kernel arguments and
+ /conf/conf.d/cryptroot (also in the
+ initial RAM disk environment), which in turn will have been
+ created when the initial RAM disk image was created by
+ /usr/share/initramfs-tools/hooks/cryptroot, by
+ extracting the information of the root file system from
+ /etc/crypttab.
+
+
+ This behavior is meant to exactly mirror the behavior of
+ askpass, the default password prompter.
+
+
+
+
+
+
+
+ FILES
+
+
+ /sbin/splashy_update
+
+
+ This is the command run to retrieve a password from
+ splashy
+ 8. See
+ splashy_update8
+ .
+
+
+
+
+ /proc
+
+
+ To find the running splashy8
+ , this directory will be searched for
+ numeric entries which will be assumed to be directories.
+ In all those directories, the exe
+ entry will be used to determine the name of the running
+ binary and the effective user and group
+ ID of the process. See
+ proc5.
+
+
+
+
+ /sbin/splashy
+
+
+ This is the name of the binary which will be searched for
+ in the process list. See splashy8
+ .
+
+
+
+
+
+
+
+ BUGS
+
+ Killing splashy
+ 8 and starting a new one
+ is ugly, but necessary as long as it does not support aborting a
+ password request.
+
+
+
+
+ EXAMPLE
+
+ Note that normally, this program will not be invoked directly,
+ but instead started by the Mandos plugin-runner8mandos
+ .
+
+
+
+ This program takes no options.
+
+
+ &COMMANDNAME;
+
+
+
+
+
+ SECURITY
+
+ If this program is killed by a signal, it will kill the process
+ ID which at the start of this program was
+ determined to run splashy8
+ as root (see also ). There is a very
+ slight risk that, in the time between those events, that process
+ ID was freed and then taken up by another
+ process; the wrong process would then be killed. Now, this
+ program can only be killed by the user who started it; see
+ plugin-runner
+ 8mandos. This program
+ should therefore be started by a completely separate
+ non-privileged user, and no other programs should be allowed to
+ run as that special user. This means that it is not recommended
+ to use the user "nobody" to start this program, as other
+ possibly less trusted programs could be running as "nobody", and
+ they would then be able to kill this program, triggering the
+ killing of the process ID which may or may not
+ be splashy
+ 8.
+
+
+ The only other thing that could be considered worthy of note is
+ this: This program is meant to be run by
+ plugin-runner8mandos, and will, when run
+ standalone, outside, in a normal environment, immediately output
+ on its standard output any presumably secret password it just
+ received. Therefore, when running this program standalone
+ (which should never normally be done), take care not to type in
+ any real secret password by force of habit, since it would then
+ immediately be shown as output.
+
+
+
+
+ SEE ALSO
+
+ crypttab
+ 5,
+ plugin-runner
+ 8mandos,
+ proc
+ 5,
+ splashy
+ 8,
+ splashy_update
+ 8
+
+
+
+
+
+
+
+
=== added file 'plugins.d/usplash.c'
--- plugins.d/usplash.c 1970-01-01 00:00:00 +0000
+++ plugins.d/usplash.c 2009-09-17 04:16:32 +0000
@@ -0,0 +1,642 @@
+/* -*- coding: utf-8 -*- */
+/*
+ * Usplash - Read a password from usplash and output it
+ *
+ * Copyright © 2008,2009 Teddy Hogeborn
+ * Copyright © 2008,2009 Björn Påhlsson
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * .
+ *
+ * Contact the authors at .
+ */
+
+#define _GNU_SOURCE /* asprintf(), TEMP_FAILURE_RETRY() */
+#include /* sig_atomic_t, struct sigaction,
+ sigemptyset(), sigaddset(), SIGINT,
+ SIGHUP, SIGTERM, sigaction(),
+ SIG_IGN, kill(), SIGKILL */
+#include /* bool, false, true */
+#include /* open(), O_WRONLY, O_RDONLY */
+#include /* and, or, not*/
+#include /* errno, EINTR */
+#include /* size_t, ssize_t, pid_t, DIR, struct
+ dirent */
+#include /* NULL */
+#include /* strlen(), memcmp() */
+#include /* asprintf(), perror() */
+#include /* close(), write(), readlink(),
+ read(), STDOUT_FILENO, sleep(),
+ fork(), setuid(), geteuid(),
+ setsid(), chdir(), dup2(),
+ STDERR_FILENO, execv() */
+#include /* free(), EXIT_FAILURE, realloc(),
+ EXIT_SUCCESS, malloc(), _exit() */
+#include /* getenv() */
+#include /* opendir(), readdir(), closedir() */
+#include /* intmax_t, strtoimax() */
+#include /* struct stat, lstat(), S_ISLNK */
+
+sig_atomic_t interrupted_by_signal = 0;
+int signal_received;
+const char usplash_name[] = "/sbin/usplash";
+
+static void termination_handler(int signum){
+ if(interrupted_by_signal){
+ return;
+ }
+ interrupted_by_signal = 1;
+ signal_received = signum;
+}
+
+static bool usplash_write(int *fifo_fd_r,
+ const char *cmd, const char *arg){
+ /*
+ * usplash_write(&fd, "TIMEOUT", "15") will write "TIMEOUT 15\0"
+ * usplash_write(&fd, "PULSATE", NULL) will write "PULSATE\0"
+ * SEE ALSO
+ * usplash_write(8)
+ */
+ int ret;
+ if(*fifo_fd_r == -1){
+ ret = open("/dev/.initramfs/usplash_fifo", O_WRONLY);
+ if(ret == -1){
+ return false;
+ }
+ *fifo_fd_r = ret;
+ }
+
+ const char *cmd_line;
+ size_t cmd_line_len;
+ char *cmd_line_alloc = NULL;
+ if(arg == NULL){
+ cmd_line = cmd;
+ cmd_line_len = strlen(cmd) + 1;
+ } else {
+ do {
+ ret = asprintf(&cmd_line_alloc, "%s %s", cmd, arg);
+ if(ret == -1){
+ int e = errno;
+ TEMP_FAILURE_RETRY(close(*fifo_fd_r));
+ errno = e;
+ return false;
+ }
+ } while(ret == -1);
+ cmd_line = cmd_line_alloc;
+ cmd_line_len = (size_t)ret + 1;
+ }
+
+ size_t written = 0;
+ ssize_t sret = 0;
+ while(written < cmd_line_len){
+ sret = write(*fifo_fd_r, cmd_line + written,
+ cmd_line_len - written);
+ if(sret == -1){
+ int e = errno;
+ TEMP_FAILURE_RETRY(close(*fifo_fd_r));
+ free(cmd_line_alloc);
+ errno = e;
+ return false;
+ }
+ written += (size_t)sret;
+ }
+ free(cmd_line_alloc);
+
+ return true;
+}
+
+/* Create prompt string */
+char *makeprompt(void){
+ int ret = 0;
+ char *prompt;
+ const char *const cryptsource = getenv("cryptsource");
+ const char *const crypttarget = getenv("crypttarget");
+ const char prompt_start[] = "Enter passphrase to unlock the disk";
+
+ if(cryptsource == NULL){
+ if(crypttarget == NULL){
+ ret = asprintf(&prompt, "%s: ", prompt_start);
+ } else {
+ ret = asprintf(&prompt, "%s (%s): ", prompt_start,
+ crypttarget);
+ }
+ } else {
+ if(crypttarget == NULL){
+ ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
+ } else {
+ ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
+ cryptsource, crypttarget);
+ }
+ }
+ if(ret == -1){
+ return NULL;
+ }
+ return prompt;
+}
+
+pid_t find_usplash(char **cmdline_r, size_t *cmdline_len_r){
+ int ret = 0;
+ ssize_t sret = 0;
+ char *cmdline = NULL;
+ size_t cmdline_len = 0;
+ DIR *proc_dir = opendir("/proc");
+ if(proc_dir == NULL){
+ perror("opendir");
+ return -1;
+ }
+ errno = 0;
+ for(struct dirent *proc_ent = readdir(proc_dir);
+ proc_ent != NULL;
+ proc_ent = readdir(proc_dir)){
+ pid_t pid;
+ {
+ intmax_t tmpmax;
+ char *tmp;
+ tmpmax = strtoimax(proc_ent->d_name, &tmp, 10);
+ if(errno != 0 or tmp == proc_ent->d_name or *tmp != '\0'
+ or tmpmax != (pid_t)tmpmax){
+ /* Not a process */
+ errno = 0;
+ continue;
+ }
+ pid = (pid_t)tmpmax;
+ }
+ /* Find the executable name by doing readlink() on the
+ /proc//exe link */
+ char exe_target[sizeof(usplash_name)];
+ {
+ /* create file name string */
+ char *exe_link;
+ ret = asprintf(&exe_link, "/proc/%s/exe", proc_ent->d_name);
+ if(ret == -1){
+ perror("asprintf");
+ goto fail_find_usplash;
+ }
+
+ /* Check that it refers to a symlink owned by root:root */
+ struct stat exe_stat;
+ ret = lstat(exe_link, &exe_stat);
+ if(ret == -1){
+ if(errno == ENOENT){
+ free(exe_link);
+ continue;
+ }
+ perror("lstat");
+ free(exe_link);
+ goto fail_find_usplash;
+ }
+ if(not S_ISLNK(exe_stat.st_mode)
+ or exe_stat.st_uid != 0
+ or exe_stat.st_gid != 0){
+ free(exe_link);
+ continue;
+ }
+
+ sret = readlink(exe_link, exe_target, sizeof(exe_target));
+ free(exe_link);
+ }
+ /* Compare executable name */
+ if((sret != ((ssize_t)sizeof(exe_target)-1))
+ or (memcmp(usplash_name, exe_target,
+ sizeof(exe_target)-1) != 0)){
+ /* Not it */
+ continue;
+ }
+ /* Found usplash */
+ /* Read and save the command line of usplash in "cmdline" */
+ {
+ /* Open /proc//cmdline */
+ int cl_fd;
+ {
+ char *cmdline_filename;
+ ret = asprintf(&cmdline_filename, "/proc/%s/cmdline",
+ proc_ent->d_name);
+ if(ret == -1){
+ perror("asprintf");
+ goto fail_find_usplash;
+ }
+ cl_fd = open(cmdline_filename, O_RDONLY);
+ free(cmdline_filename);
+ if(cl_fd == -1){
+ perror("open");
+ goto fail_find_usplash;
+ }
+ }
+ size_t cmdline_allocated = 0;
+ char *tmp;
+ const size_t blocksize = 1024;
+ do {
+ /* Allocate more space? */
+ if(cmdline_len + blocksize > cmdline_allocated){
+ tmp = realloc(cmdline, cmdline_allocated + blocksize);
+ if(tmp == NULL){
+ perror("realloc");
+ close(cl_fd);
+ goto fail_find_usplash;
+ }
+ cmdline = tmp;
+ cmdline_allocated += blocksize;
+ }
+ /* Read data */
+ sret = read(cl_fd, cmdline + cmdline_len,
+ cmdline_allocated - cmdline_len);
+ if(sret == -1){
+ perror("read");
+ close(cl_fd);
+ goto fail_find_usplash;
+ }
+ cmdline_len += (size_t)sret;
+ } while(sret != 0);
+ ret = close(cl_fd);
+ if(ret == -1){
+ perror("close");
+ goto fail_find_usplash;
+ }
+ }
+ /* Close directory */
+ ret = closedir(proc_dir);
+ if(ret == -1){
+ perror("closedir");
+ goto fail_find_usplash;
+ }
+ /* Success */
+ *cmdline_r = cmdline;
+ *cmdline_len_r = cmdline_len;
+ return pid;
+ }
+
+ fail_find_usplash:
+
+ free(cmdline);
+ if(proc_dir != NULL){
+ int e = errno;
+ closedir(proc_dir);
+ errno = e;
+ }
+ return 0;
+}
+
+int main(__attribute__((unused))int argc,
+ __attribute__((unused))char **argv){
+ int ret = 0;
+ ssize_t sret;
+ int fifo_fd = -1;
+ int outfifo_fd = -1;
+ char *buf = NULL;
+ size_t buf_len = 0;
+ pid_t usplash_pid = -1;
+ bool usplash_accessed = false;
+
+ char *prompt = makeprompt();
+ if(prompt == NULL){
+ goto failure;
+ }
+
+ /* Find usplash process */
+ char *cmdline = NULL;
+ size_t cmdline_len = 0;
+ usplash_pid = find_usplash(&cmdline, &cmdline_len);
+ if(usplash_pid == 0){
+ goto failure;
+ }
+
+ /* Set up the signal handler */
+ {
+ struct sigaction old_action,
+ new_action = { .sa_handler = termination_handler,
+ .sa_flags = 0 };
+ sigemptyset(&new_action.sa_mask);
+ ret = sigaddset(&new_action.sa_mask, SIGINT);
+ if(ret == -1){
+ perror("sigaddset");
+ goto failure;
+ }
+ ret = sigaddset(&new_action.sa_mask, SIGHUP);
+ if(ret == -1){
+ perror("sigaddset");
+ goto failure;
+ }
+ ret = sigaddset(&new_action.sa_mask, SIGTERM);
+ if(ret == -1){
+ perror("sigaddset");
+ goto failure;
+ }
+ ret = sigaction(SIGINT, NULL, &old_action);
+ if(ret == -1){
+ if(errno != EINTR){
+ perror("sigaction");
+ }
+ goto failure;
+ }
+ if(old_action.sa_handler != SIG_IGN){
+ ret = sigaction(SIGINT, &new_action, NULL);
+ if(ret == -1){
+ if(errno != EINTR){
+ perror("sigaction");
+ }
+ goto failure;
+ }
+ }
+ ret = sigaction(SIGHUP, NULL, &old_action);
+ if(ret == -1){
+ if(errno != EINTR){
+ perror("sigaction");
+ }
+ goto failure;
+ }
+ if(old_action.sa_handler != SIG_IGN){
+ ret = sigaction(SIGHUP, &new_action, NULL);
+ if(ret == -1){
+ if(errno != EINTR){
+ perror("sigaction");
+ }
+ goto failure;
+ }
+ }
+ ret = sigaction(SIGTERM, NULL, &old_action);
+ if(ret == -1){
+ if(errno != EINTR){
+ perror("sigaction");
+ }
+ goto failure;
+ }
+ if(old_action.sa_handler != SIG_IGN){
+ ret = sigaction(SIGTERM, &new_action, NULL);
+ if(ret == -1){
+ if(errno != EINTR){
+ perror("sigaction");
+ }
+ goto failure;
+ }
+ }
+ }
+
+ usplash_accessed = true;
+ /* Write command to FIFO */
+ if(not usplash_write(&fifo_fd, "TIMEOUT", "0")){
+ if(errno != EINTR){
+ perror("usplash_write");
+ }
+ goto failure;
+ }
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+
+ if(not usplash_write(&fifo_fd, "INPUTQUIET", prompt)){
+ if(errno != EINTR){
+ perror("usplash_write");
+ }
+ goto failure;
+ }
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+
+ free(prompt);
+ prompt = NULL;
+
+ /* Read reply from usplash */
+ /* Open FIFO */
+ outfifo_fd = open("/dev/.initramfs/usplash_outfifo", O_RDONLY);
+ if(outfifo_fd == -1){
+ if(errno != EINTR){
+ perror("open");
+ }
+ goto failure;
+ }
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+
+ /* Read from FIFO */
+ size_t buf_allocated = 0;
+ const size_t blocksize = 1024;
+ do {
+ /* Allocate more space */
+ if(buf_len + blocksize > buf_allocated){
+ char *tmp = realloc(buf, buf_allocated + blocksize);
+ if(tmp == NULL){
+ if(errno != EINTR){
+ perror("realloc");
+ }
+ goto failure;
+ }
+ buf = tmp;
+ buf_allocated += blocksize;
+ }
+ sret = read(outfifo_fd, buf + buf_len,
+ buf_allocated - buf_len);
+ if(sret == -1){
+ if(errno != EINTR){
+ perror("read");
+ }
+ TEMP_FAILURE_RETRY(close(outfifo_fd));
+ goto failure;
+ }
+ if(interrupted_by_signal){
+ break;
+ }
+
+ buf_len += (size_t)sret;
+ } while(sret != 0);
+ ret = close(outfifo_fd);
+ if(ret == -1){
+ if(errno != EINTR){
+ perror("close");
+ }
+ goto failure;
+ }
+ outfifo_fd = -1;
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+
+ if(not usplash_write(&fifo_fd, "TIMEOUT", "15")){
+ if(errno != EINTR){
+ perror("usplash_write");
+ }
+ goto failure;
+ }
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+
+ ret = close(fifo_fd);
+ if(ret == -1){
+ if(errno != EINTR){
+ perror("close");
+ }
+ goto failure;
+ }
+ fifo_fd = -1;
+
+ /* Print password to stdout */
+ size_t written = 0;
+ while(written < buf_len){
+ do {
+ sret = write(STDOUT_FILENO, buf + written, buf_len - written);
+ if(sret == -1){
+ if(errno != EINTR){
+ perror("write");
+ }
+ goto failure;
+ }
+ } while(sret == -1);
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+ written += (size_t)sret;
+ }
+ free(buf);
+ buf = NULL;
+
+ if(interrupted_by_signal){
+ goto failure;
+ }
+
+ free(cmdline);
+ return EXIT_SUCCESS;
+
+ failure:
+
+ free(buf);
+
+ free(prompt);
+
+ /* If usplash was never accessed, we can stop now */
+ if(not usplash_accessed){
+ return EXIT_FAILURE;
+ }
+
+ /* Close FIFO */
+ if(fifo_fd != -1){
+ ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
+ if(ret == -1 and errno != EINTR){
+ perror("close");
+ }
+ fifo_fd = -1;
+ }
+
+ /* Close output FIFO */
+ if(outfifo_fd != -1){
+ ret = (int)TEMP_FAILURE_RETRY(close(outfifo_fd));
+ if(ret == -1){
+ perror("close");
+ }
+ }
+
+ /* Create argc and argv for new usplash*/
+ int cmdline_argc = 0;
+ char **cmdline_argv = malloc(sizeof(char *));
+ {
+ size_t position = 0;
+ while(position < cmdline_len){
+ char **tmp = realloc(cmdline_argv,
+ (sizeof(char *)
+ * (size_t)(cmdline_argc + 2)));
+ if(tmp == NULL){
+ perror("realloc");
+ free(cmdline_argv);
+ return EXIT_FAILURE;
+ }
+ cmdline_argv = tmp;
+ cmdline_argv[cmdline_argc] = cmdline + position;
+ cmdline_argc++;
+ position += strlen(cmdline + position) + 1;
+ }
+ cmdline_argv[cmdline_argc] = NULL;
+ }
+ /* Kill old usplash */
+ kill(usplash_pid, SIGTERM);
+ sleep(2);
+ while(kill(usplash_pid, 0) == 0){
+ kill(usplash_pid, SIGKILL);
+ sleep(1);
+ }
+
+ pid_t new_usplash_pid = fork();
+ if(new_usplash_pid == 0){
+ /* Child; will become new usplash process */
+
+ /* Make the effective user ID (root) the only user ID instead of
+ the real user ID (_mandos) */
+ ret = setuid(geteuid());
+ if(ret == -1){
+ perror("setuid");
+ }
+
+ setsid();
+ ret = chdir("/");
+/* if(fork() != 0){ */
+/* _exit(EXIT_SUCCESS); */
+/* } */
+ ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
+ if(ret == -1){
+ perror("dup2");
+ _exit(EXIT_FAILURE);
+ }
+
+ execv(usplash_name, cmdline_argv);
+ if(not interrupted_by_signal){
+ perror("execv");
+ }
+ free(cmdline);
+ free(cmdline_argv);
+ _exit(EXIT_FAILURE);
+ }
+ free(cmdline);
+ free(cmdline_argv);
+ sleep(2);
+ if(not usplash_write(&fifo_fd, "PULSATE", NULL)){
+ if(errno != EINTR){
+ perror("usplash_write");
+ }
+ }
+
+ /* Close FIFO (again) */
+ if(fifo_fd != -1){
+ ret = (int)TEMP_FAILURE_RETRY(close(fifo_fd));
+ if(ret == -1 and errno != EINTR){
+ perror("close");
+ }
+ fifo_fd = -1;
+ }
+
+ if(interrupted_by_signal){
+ struct sigaction signal_action = { .sa_handler = SIG_DFL };
+ sigemptyset(&signal_action.sa_mask);
+ ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
+ &signal_action, NULL));
+ if(ret == -1){
+ perror("sigaction");
+ }
+ do {
+ ret = raise(signal_received);
+ } while(ret != 0 and errno == EINTR);
+ if(ret != 0){
+ perror("raise");
+ abort();
+ }
+ TEMP_FAILURE_RETRY(pause());
+ }
+
+ return EXIT_FAILURE;
+}
=== added file 'plugins.d/usplash.xml'
--- plugins.d/usplash.xml 1970-01-01 00:00:00 +0000
+++ plugins.d/usplash.xml 2009-01-04 21:54:55 +0000
@@ -0,0 +1,297 @@
+
+
+
+
+%common;
+]>
+
+
+
+ Mandos Manual
+
+ Mandos
+ &version;
+ &TIMESTAMP;
+
+
+ Björn
+ Påhlsson
+
+ belorn@fukt.bsnet.se
+
+
+
+ Teddy
+ Hogeborn
+
+ teddy@fukt.bsnet.se
+
+
+
+
+ 2008
+ 2009
+ Teddy Hogeborn
+ Björn Påhlsson
+
+
+
+
+
+ &COMMANDNAME;
+ 8mandos
+
+
+
+ &COMMANDNAME;
+ Mandos plugin to use usplash to get a
+ password.
+
+
+
+
+ &COMMANDNAME;
+
+
+
+
+ DESCRIPTION
+
+ This program prompts for a password using
+ usplash8
+ and outputs any given password to standard
+ output. If no usplash8
+ process can be found, this program will immediately exit with an
+ exit code indicating failure.
+
+
+ This program is not very useful on its own. This program is
+ really meant to run as a plugin in the Mandos client-side system, where it is used as a
+ fallback and alternative to retrieving passwords from a
+ Mandos server.
+
+
+ If this program is killed (presumably by
+ plugin-runner
+ 8mandos because some other
+ plugin provided the password), it cannot tell
+ usplash8
+ to abort requesting a password, because
+ usplash
+ 8 does not support this.
+ Therefore, this program will then kill the
+ running usplash
+ 8 process and start a
+ new one using the same command line
+ arguments as the old one was using.
+
+
+
+
+ OPTIONS
+
+ This program takes no options.
+
+
+
+
+ EXIT STATUS
+
+ If exit status is 0, the output from the program is the password
+ as it was read. Otherwise, if exit status is other than 0, the
+ program was interrupted or encountered an error, and any output
+ so far could be corrupt and/or truncated, and should therefore
+ be ignored.
+
+
+
+
+ ENVIRONMENT
+
+
+ cryptsource
+ crypttarget
+
+
+ If set, these environment variables will be assumed to
+ contain the source device name and the target device
+ mapper name, respectively, and will be shown as part of
+ the prompt.
+
+
+ These variables will normally be inherited from
+ plugin-runner
+ 8mandos, which will
+ normally have inherited them from
+ /scripts/local-top/cryptroot in the
+ initial RAM disk environment, which will
+ have set them from parsing kernel arguments and
+ /conf/conf.d/cryptroot (also in the
+ initial RAM disk environment), which in turn will have been
+ created when the initial RAM disk image was created by
+ /usr/share/initramfs-tools/hooks/cryptroot, by
+ extracting the information of the root file system from
+ /etc/crypttab.
+
+
+ This behavior is meant to exactly mirror the behavior of
+ askpass, the default password prompter.
+
+
+
+
+
+
+
+ FILES
+
+
+ /dev/.initramfs/usplash_fifo
+
+
+ This is the FIFO to where this program
+ will write the commands for usplash8
+ . See fifo7
+ .
+
+
+
+
+ /dev/.initramfs/usplash_outfifo
+
+
+ This is the FIFO where this program
+ will read the password from usplash8
+ . See fifo7
+ .
+
+
+
+
+ /proc
+
+
+ To find the running usplash8
+ , this directory will be searched for
+ numeric entries which will be assumed to be directories.
+ In all those directories, the exe and
+ cmdline entries will be used to
+ determine the name of the running binary, effective user
+ and group ID, and the command line
+ arguments. See proc5
+ .
+
+
+
+
+ /sbin/usplash
+
+
+ This is the name of the binary which will be searched for
+ in the process list. See usplash8
+ .
+
+
+
+
+
+
+
+ BUGS
+
+ Killing usplash
+ 8 and starting a new one
+ is ugly, but necessary as long as it does not support aborting a
+ password request.
+
+
+
+
+ EXAMPLE
+
+ Note that normally, this program will not be invoked directly,
+ but instead started by the Mandos plugin-runner8mandos
+ .
+
+
+
+ This program takes no options.
+
+
+ &COMMANDNAME;
+
+
+
+
+
+ SECURITY
+
+ If this program is killed by a signal, it will kill the process
+ ID which at the start of this program was
+ determined to run usplash8
+ as root (see also ). There is a very
+ slight risk that, in the time between those events, that process
+ ID was freed and then taken up by another
+ process; the wrong process would then be killed. Now, this
+ program can only be killed by the user who started it; see
+ plugin-runner
+ 8mandos. This program
+ should therefore be started by a completely separate
+ non-privileged user, and no other programs should be allowed to
+ run as that special user. This means that it is not recommended
+ to use the user "nobody" to start this program, as other
+ possibly less trusted programs could be running as "nobody", and
+ they would then be able to kill this program, triggering the
+ killing of the process ID which may or may not
+ be usplash
+ 8.
+
+
+ The only other thing that could be considered worthy of note is
+ this: This program is meant to be run by
+ plugin-runner8mandos, and will, when run
+ standalone, outside, in a normal environment, immediately output
+ on its standard output any presumably secret password it just
+ received. Therefore, when running this program standalone
+ (which should never normally be done), take care not to type in
+ any real secret password by force of habit, since it would then
+ immediately be shown as output.
+
+
+
+
+ SEE ALSO
+
+ crypttab
+ 5,
+ fifo
+ 7,
+ plugin-runner
+ 8mandos,
+ proc
+ 5,
+ usplash
+ 8
+
+
+
+
+
+
+
+
=== 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