=== removed directory '.bzr-builddeb'
=== removed file '.bzr-builddeb/default.conf'
--- .bzr-builddeb/default.conf 2008-09-17 00:34:09 +0000
+++ .bzr-builddeb/default.conf 1970-01-01 00:00:00 +0000
@@ -1,2 +0,0 @@
-[BUILDDEB]
-split = True
=== removed file '.bzrignore'
--- .bzrignore 2010-09-30 06:24:20 +0000
+++ .bzrignore 1970-01-01 00:00:00 +0000
@@ -1,13 +0,0 @@
-*.5
-*.8
-*.8mandos
-confdir
-keydir
-man
-plugin-runner
-plugins.d/askpass-fifo
-plugins.d/mandos-client
-plugins.d/password-prompt
-plugins.d/splashy
-plugins.d/usplash
-plugins.d/plymouth
=== removed file 'COPYING'
--- COPYING 2008-08-15 20:17:32 +0000
+++ COPYING 1970-01-01 00:00:00 +0000
@@ -1,676 +0,0 @@
-
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
-
=== removed file 'DBUS-API'
--- DBUS-API 2010-09-26 18:32:58 +0000
+++ DBUS-API 1970-01-01 00:00:00 +0000
@@ -1,172 +0,0 @@
- -*- mode: org; coding: utf-8 -*-
-
- Mandos Server D-Bus Interface
-
-This file documents the D-Bus interface to the Mandos server.
-
-* Bus: System bus
- Bus name: "se.bsnet.fukt.Mandos"
-
-
-* Object Paths:
-
- | Path | Object |
- |-----------------------+-------------------|
- | "/" | The Mandos Server |
- | "/clients/CLIENTNAME" | Mandos Client |
-
-
-* Mandos Server Interface:
- Interface name: "se.bsnet.fukt.Mandos"
-
-** Methods:
-*** GetAllClients() → (ao: Clients)
- Returns an array of all client D-Bus object paths
-
-*** GetAllClientsWithProperties() → (a{oa{sv}}: ClientProperties)
- Returns an array of all clients and all their properties
-
-*** RemoveClient(o: ObjectPath) → nothing
- Removes a client
-
-** Signals:
-*** ClientAdded(o: ObjectPath)
- A new client was added.
-
-*** ClientNotFound(s: Fingerprint, s: Address)
- A client connected from Address using Fingerprint, but was
- rejected because it was not found in the server. The fingerprint
- is represented as a string of hexadecimal digits. The address is
- an IPv4 or IPv6 address in its normal string format.
-
-*** ClientRemoved(o: ObjectPath, s: Name)
- A client named Name on ObjectPath was removed.
-
-
-* Mandos Client Interface:
- Interface name: "se.bsnet.fukt.Mandos.Client"
-
-** Methods
-*** Approve(b: Approve) → nothing
- Approve or deny a connected client waiting for approval. If
- denied, a client will not be sent its secret.
-
-*** CheckedOK() → nothing
- Assert that this client has been checked and found to be alive.
- This will restart the timeout before disabling this client. See
- also the "LastCheckedOK" property.
-
-*** Disable() → nothing
- Disable this client. See also the "Enabled" property.
-
-*** Enable() → nothing
- Enable this client. See also the "Enabled" property.
-
-*** StartChecker() → nothing
- Start a new checker for this client, if none is currently
- running. See also the "CheckerRunning" property.
-
-*** StopChecker() → nothing
- Abort a running checker process for this client, if any. See also
- the "CheckerRunning" property.
-
-** Properties
-
- Note: Many of these properties directly correspond to a setting in
- "clients.conf", in which case they are fully documented in
- mandos-clients.conf(5).
-
- | Name | Type | Access | clients.conf |
- |-------------------------+------+------------+---------------------|
- | ApprovedByDefault | b | Read/Write | approved_by_default |
- | ApprovalDelay (a) | t | Read/Write | approval_delay |
- | ApprovalDuration (a) | t | Read/Write | approval_duration |
- | ApprovalPending (b) | b | Read | N/A |
- | Checker | s | Read/Write | checker |
- | CheckerRunning (c) | b | Read/Write | N/A |
- | Created (d) | s | Read | N/A |
- | Enabled (e) | b | Read/Write | N/A |
- | Fingerprint | s | Read | fingerprint |
- | Host | s | Read/Write | host |
- | Interval (a) | t | Read/Write | interval |
- | LastApprovalRequest (f) | s | Read | N/A |
- | LastCheckedOK (g) | s | Read/Write | N/A |
- | LastEnabled (h) | s | Read | N/A |
- | Name | s | Read | (Section name) |
- | ObjectPath | o | Read | N/A |
- | Secret (i) | ay | Write | secret (or secfile) |
- | Timeout (a) | t | Read/Write | timeout |
-
- a) Represented as milliseconds.
-
- b) An approval is currently pending.
-
- c) Setting this property is equivalent to calling StartChecker() or
- StopChecker().
-
- d) The creation time of this client object, as a RFC 3339 string.
-
- e) Setting this property is equivalent to calling Enable() or
- Disable().
-
- f) The time of the last approval request, as a RFC 3339 string, or
- an empty string if this has not happened.
-
- g) The last time a checker was successful, as a RFC 3339 string, or
- an empty string if this has not happened. Setting this property
- is equivalent to calling CheckedOK(), i.e. the current time is
- set, regardless of the string sent. Please always use an empty
- string when setting this property, to allow for possible future
- expansion.
-
- h) The last time this client was enabled, as a RFC 3339 string, or
- an empty string if this has not happened.
-
- i) A raw byte array, not hexadecimal digits.
-
-** Signals
-*** CheckerCompleted(n: Exitcode, x: Waitstatus, s: Command)
- A checker (Command) has completed. Exitcode is either the exit
- code or -1 for abnormal exit. In any case, the full Waitstatus
- (as from wait(2)) is also available.
-
-*** CheckerStarted(s: Command)
- A checker command (Command) has just been started.
-
-*** GotSecret()
- This client has been sent its secret.
-
-*** NeedApproval(t: Timeout, b: ApprovedByDefault)
- This client will be approved or denied in exactly Timeout
- milliseconds, depending on ApprovedByDefault. Approve() can now
- usefully be called on this client object.
-
-*** PropertyChanged(s: Property, v: Value)
- The Property on this client has changed to Value.
-
-*** Rejected(s: Reason)
- This client was not given its secret for a specified Reason.
-
-* Copyright
-
- Copyright © 2010 Teddy Hogeborn
- Copyright © 2010 Björn Påhlsson
-
-** License:
-
- This program is free software: you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see
- .
-
-
-#+STARTUP: showall
=== removed file 'INSTALL'
--- INSTALL 2009-02-15 09:28:06 +0000
+++ INSTALL 1970-01-01 00:00:00 +0000
@@ -1,133 +0,0 @@
--*- org -*-
-
-* Prerequisites
-
-** Operating System
-
- Debian 5.0 "lenny" or Ubuntu 8.04 "Hardy Heron".
-
- This is mostly for the support scripts which make sure that the
- client is installed and started in the initial RAM disk environment
- and that the initrd.img file is automatically made unreadable. The
- server and client programs themselves *could* be run in other
- distributions, but they *are* specific to GNU/Linux systems, and
- are not intended to be portable to other Unixes.
-
-** Libraries
-
- The following libraries and packages are needed. (It is possible
- that it might work with older versions of some of these, but these
- versions are confirmed to work. Newer versions are almost
- certainly OK.)
-
-*** Documentation
- These are required to build the manual pages for both the server
- and client:
-
- + DocBook 4.5 http://www.docbook.org/
- Note: DocBook 5.0 is not compatible.
- + DocBook XSL stylesheets 1.71.0
- http://wiki.docbook.org/topic/DocBookXslStylesheets
-
- Package names:
- docbook docbook-xsl
-
- To build just the documentation, run the command "make doc". Then
- the manual page "mandos.8", for example, can be read by running
- "man -l mandos.8".
-
-*** Mandos Server
- + GnuTLS 2.4 http://www.gnu.org/software/gnutls/
- + Avahi 0.6.16 http://www.avahi.org/
- + Python 2.5 http://www.python.org/
- + Python-GnuTLS 1.1.5 http://pypi.python.org/pypi/python-gnutls/
- + dbus-python 0.82.4 http://dbus.freedesktop.org/doc/dbus-python/
- + python-ctypes 1.0.0 http://pypi.python.org/pypi/ctypes
- + PyGObject 2.14.2 http://library.gnome.org/devel/pygobject/
-
- Strongly recommended:
- + fping 2.4b2-to-ipv6 http://www.fping.com/
-
- Package names:
- python-gnutls avahi-daemon python python-avahi python-dbus
- python-ctypes python-gobject
-
-*** Mandos Client
- + initramfs-tools 0.85i
- http://packages.qa.debian.org/i/initramfs-tools.html
- + GnuTLS 2.4 http://www.gnu.org/software/gnutls/
- + Avahi 0.6.16 http://www.avahi.org/
- + GnuPG 1.4.9 http://www.gnupg.org/
- + GPGME 1.1.6 http://www.gnupg.org/related_software/gpgme/
-
- Package names:
- initramfs-tools libgnutls-dev libavahi-core-dev gnupg
- libgpgme11-dev
-
-* Installing the Mandos server
-
- 1. Do "make doc".
-
- 2. On the computer to run as a Mandos server, run the following
- command:
- For Debian: su -c 'make install-server'
- For Ubuntu: sudo make install-server
-
- (This creates a configuration without any clients configured; you
- need an actually configured client to do that; see below.)
-
-* Installing the Mandos client.
-
- 1. Do "make all doc".
-
- 2. On the computer to run as a Mandos client, run the following
- command:
- For Debian: su -c 'make install-client'
- For Ubuntu: sudo make install-client
-
- This will also create an OpenPGP key, which will take some time
- and entropy, so be patient.
-
- 3. Run the following command:
- For Debian: su -c 'mandos-keygen --password'
- For Ubuntu: sudo mandos-keygen --password
-
- When prompted, enter the password/passphrase for the encrypted
- root file system on this client computer. The command will
- output a section of text, starting with a [section header]. Copy
- and append this to the file "/etc/mandos/clients.conf" *on the
- server computer*.
-
- 4. Configure the client to use the correct network interface. The
- default is "eth0", and if this needs to be adjusted, it will be
- necessary to edit /etc/mandos/plugin-runner.conf to uncomment and
- change the line there. If that file is changed, the initrd.img
- file must be updated, possibly using the following command:
-
- # update-initramfs -k all -u
-
- 5. On the server computer, start the server by running the command
- For Debian: su -c 'invoke-rc.d mandos start'
- For Ubuntu: sudo invoke-rc.d mandos start
-
- At this point, it is possible to verify that the correct password
- will be received by the client by running the command:
-
- # /usr/lib/mandos/plugins.d/mandos-client \
- --pubkey=/etc/keys/mandos/pubkey.txt \
- --seckey=/etc/keys/mandos/seckey.txt; echo
-
- This command should retrieve the password from the server,
- decrypt it, and output it to standard output.
-
- After this, the client computer should be able to reboot without
- needing a password entered on the console, as long as it does not
- take more than an hour to reboot.
-
-* Further customizations
-
- You may want to tighten or loosen the timeouts in the server
- configuration files; see mandos.conf(5) and mandos-clients.conf(5).
- If IPsec is not used, it is suggested that a more cryptographically
- secure checker program is used and configured, since without IPsec
- ping packets can be faked.
=== modified file 'Makefile'
--- Makefile 2010-09-28 18:57:31 +0000
+++ Makefile 2008-08-07 21:45:41 +0000
@@ -1,428 +1,45 @@
-WARN=-O -Wall -Wformat=2 -Winit-self -Wmissing-include-dirs \
- -Wswitch-default -Wswitch-enum -Wunused-parameter \
- -Wstrict-aliasing=1 -Wextra -Wfloat-equal -Wundef -Wshadow \
- -Wunsafe-loop-optimizations -Wpointer-arith \
- -Wbad-function-cast -Wcast-qual -Wcast-align -Wwrite-strings \
- -Wconversion -Wstrict-prototypes -Wold-style-definition \
- -Wpacked -Wnested-externs -Winline -Wvolatile-register-var
-# -Wunreachable-code
-#DEBUG=-ggdb3
+WARN=-O -Wall -Wformat=2 -Winit-self -Wmissing-include-dirs -Wswitch-default -Wswitch-enum -Wunused-parameter -Wstrict-aliasing=2 -Wextra -Wfloat-equal -Wundef -Wshadow -Wunsafe-loop-optimizations -Wpointer-arith -Wbad-function-cast -Wcast-qual -Wcast-align -Wwrite-strings -Wconversion -Wstrict-prototypes -Wold-style-definition -Wpacked -Wnested-externs -Wunreachable-code -Winline -Wvolatile-register-var
+DEBUG=-ggdb3
# For info about _FORTIFY_SOURCE, see
-#
-# and .
-FORTIFY=-D_FORTIFY_SOURCE=2 -fstack-protector-all -fPIC
-LINK_FORTIFY_LD=-z relro -z now
-LINK_FORTIFY=
-
-# If BROKEN_PIE is set, do not build with -pie
-ifndef BROKEN_PIE
-FORTIFY += -fPIE
-LINK_FORTIFY += -pie
-endif
+#
+FORTIFY=-D_FORTIFY_SOURCE=2 # -fstack-protector-all
#COVERAGE=--coverage
OPTIMIZE=-Os
LANGUAGE=-std=gnu99
-htmldir=man
-version=1.2
-SED=sed
-
-## Use these settings for a traditional /usr/local install
-# PREFIX=$(DESTDIR)/usr/local
-# CONFDIR=$(DESTDIR)/etc/mandos
-# KEYDIR=$(DESTDIR)/etc/mandos/keys
-# MANDIR=$(PREFIX)/man
-# INITRAMFSTOOLS=$(DESTDIR)/etc/initramfs-tools
-##
-
-## These settings are for a package-type install
-PREFIX=$(DESTDIR)/usr
-CONFDIR=$(DESTDIR)/etc/mandos
-KEYDIR=$(DESTDIR)/etc/keys/mandos
-MANDIR=$(PREFIX)/share/man
-INITRAMFSTOOLS=$(DESTDIR)/usr/share/initramfs-tools
-##
-
-GNUTLS_CFLAGS=$(shell pkg-config --cflags-only-I gnutls)
-GNUTLS_LIBS=$(shell pkg-config --libs gnutls)
-AVAHI_CFLAGS=$(shell pkg-config --cflags-only-I avahi-core)
-AVAHI_LIBS=$(shell pkg-config --libs avahi-core)
-GPGME_CFLAGS=$(shell gpgme-config --cflags; getconf LFS_CFLAGS)
-GPGME_LIBS=$(shell gpgme-config --libs; getconf LFS_LIBS; \
- getconf LFS_LDFLAGS)
# Do not change these two
-CFLAGS=$(WARN) $(DEBUG) $(FORTIFY) $(COVERAGE) $(OPTIMIZE) \
- $(LANGUAGE) $(GNUTLS_CFLAGS) $(AVAHI_CFLAGS) $(GPGME_CFLAGS) \
- -DVERSION='"$(version)"'
-LDFLAGS=$(COVERAGE) $(LINK_FORTIFY) $(foreach flag,$(LINK_FORTIFY_LD),-Xlinker $(flag))
-
-# Commands to format a DocBook document into a manual page
-DOCBOOKTOMAN=$(strip cd $(dir $<); xsltproc --nonet --xinclude \
- --param man.charmap.use.subset 0 \
- --param make.year.ranges 1 \
- --param make.single.year.ranges 1 \
- --param man.output.quietly 1 \
- --param man.authors.section.enabled 0 \
- /usr/share/xml/docbook/stylesheet/nwalsh/manpages/docbook.xsl \
- $(notdir $<); \
- $(MANPOST) $(notdir $@))
-# DocBook-to-man post-processing to fix a '\n' escape bug
-MANPOST=$(SED) --in-place --expression='s,\\\\en,\\en,g;s,\\n,\\en,g'
-
-DOCBOOKTOHTML=$(strip xsltproc --nonet --xinclude \
- --param make.year.ranges 1 \
- --param make.single.year.ranges 1 \
- --param man.output.quietly 1 \
- --param man.authors.section.enabled 0 \
- --param citerefentry.link 1 \
- --output $@ \
- /usr/share/xml/docbook/stylesheet/nwalsh/xhtml/docbook.xsl \
- $<; $(HTMLPOST) $@)
-# Fix citerefentry links
-HTMLPOST=$(SED) --in-place \
- --expression='s/\(\)\([^<]*\)\(<\/span>(\)\([^)]*\)\()<\/span><\/a>\)/\1\3.\5\2\3\4\5\6/g'
-
-PLUGINS=plugins.d/password-prompt plugins.d/mandos-client \
- plugins.d/usplash plugins.d/splashy plugins.d/askpass-fifo \
- plugins.d/plymouth
-CPROGS=plugin-runner $(PLUGINS)
-PROGS=mandos mandos-keygen mandos-ctl mandos-monitor $(CPROGS)
-DOCS=mandos.8 mandos-keygen.8 mandos-monitor.8 mandos-ctl.8 \
- mandos.conf.5 mandos-clients.conf.5 plugin-runner.8mandos \
- plugins.d/mandos-client.8mandos \
- plugins.d/password-prompt.8mandos plugins.d/usplash.8mandos \
- plugins.d/splashy.8mandos plugins.d/askpass-fifo.8mandos \
- plugins.d/plymouth.8mandos
-
-htmldocs=$(addsuffix .xhtml,$(DOCS))
-
-objects=$(addsuffix .o,$(CPROGS))
-
-all: $(PROGS) mandos.lsm
-
-doc: $(DOCS)
-
-html: $(htmldocs)
-
-%.5: %.xml common.ent legalnotice.xml
- $(DOCBOOKTOMAN)
-%.5.xhtml: %.xml common.ent legalnotice.xml
- $(DOCBOOKTOHTML)
-
-%.8: %.xml common.ent legalnotice.xml
- $(DOCBOOKTOMAN)
-%.8.xhtml: %.xml common.ent legalnotice.xml
- $(DOCBOOKTOHTML)
-
-%.8mandos: %.xml common.ent legalnotice.xml
- $(DOCBOOKTOMAN)
-%.8mandos.xhtml: %.xml common.ent legalnotice.xml
- $(DOCBOOKTOHTML)
-
-mandos.8: mandos.xml common.ent mandos-options.xml overview.xml \
- legalnotice.xml
- $(DOCBOOKTOMAN)
-mandos.8.xhtml: mandos.xml common.ent mandos-options.xml \
- overview.xml legalnotice.xml
- $(DOCBOOKTOHTML)
-
-mandos-keygen.8: mandos-keygen.xml common.ent overview.xml \
- legalnotice.xml
- $(DOCBOOKTOMAN)
-mandos-keygen.8.xhtml: mandos-keygen.xml common.ent overview.xml \
- legalnotice.xml
- $(DOCBOOKTOHTML)
-
-mandos-monitor.8: mandos-monitor.xml common.ent overview.xml \
- legalnotice.xml
- $(DOCBOOKTOMAN)
-mandos-monitor.8.xhtml: mandos-monitor.xml common.ent overview.xml \
- legalnotice.xml
- $(DOCBOOKTOHTML)
-
-mandos-ctl.8: mandos-ctl.xml common.ent overview.xml \
- legalnotice.xml
- $(DOCBOOKTOMAN)
-mandos-ctl.8.xhtml: mandos-ctl.xml common.ent overview.xml \
- legalnotice.xml
- $(DOCBOOKTOHTML)
-
-mandos.conf.5: mandos.conf.xml common.ent mandos-options.xml \
- legalnotice.xml
- $(DOCBOOKTOMAN)
-mandos.conf.5.xhtml: mandos.conf.xml common.ent mandos-options.xml \
- legalnotice.xml
- $(DOCBOOKTOHTML)
-
-plugin-runner.8mandos: plugin-runner.xml common.ent overview.xml \
- legalnotice.xml
- $(DOCBOOKTOMAN)
-plugin-runner.8mandos.xhtml: plugin-runner.xml common.ent \
- overview.xml legalnotice.xml
- $(DOCBOOKTOHTML)
-
-plugins.d/mandos-client.8mandos: plugins.d/mandos-client.xml \
- common.ent \
- mandos-options.xml \
- overview.xml legalnotice.xml
- $(DOCBOOKTOMAN)
-plugins.d/mandos-client.8mandos.xhtml: plugins.d/mandos-client.xml \
- common.ent \
- mandos-options.xml \
- overview.xml legalnotice.xml
- $(DOCBOOKTOHTML)
-
-# Update all these files with version number $(version)
-common.ent: Makefile
- $(strip $(SED) --in-place \
- --expression='s/^\($$/\1$(version)">/' \
- $@)
-
-mandos: Makefile
- $(strip $(SED) --in-place \
- --expression='s/^\(version = "\)[^"]*"$$/\1$(version)"/' \
- $@)
-
-mandos-keygen: Makefile
- $(strip $(SED) --in-place \
- --expression='s/^\(VERSION="\)[^"]*"$$/\1$(version)"/' \
- $@)
-
-mandos-ctl: Makefile
- $(strip $(SED) --in-place \
- --expression='s/^\(version = "\)[^"]*"$$/\1$(version)"/' \
- $@)
-
-mandos-monitor: Makefile
- $(strip $(SED) --in-place \
- --expression='s/^\(version = "\)[^"]*"$$/\1$(version)"/' \
- $@)
-
-mandos.lsm: Makefile
- $(strip $(SED) --in-place \
- --expression='s/^\(Version:\).*/\1\t$(version)/' \
- $@)
- $(strip $(SED) --in-place \
- --expression='s/^\(Entered-date:\).*/\1\t$(shell date --rfc-3339=date --reference=Makefile)/' \
- $@)
- $(strip $(SED) --in-place \
- --expression='s/\(mandos_\)[0-9.]\+\(\.orig\.tar\.gz\)/\1$(version)\2/' \
- $@)
-
-plugins.d/mandos-client: plugins.d/mandos-client.c
- $(LINK.c) $(GNUTLS_LIBS) $(AVAHI_LIBS) $(GPGME_LIBS) $(strip\
- ) $(COMMON) $^ $(LOADLIBES) $(LDLIBS) -o $@
-
-.PHONY : all doc html clean distclean run-client run-server install \
- install-server install-client uninstall uninstall-server \
- uninstall-client purge purge-server purge-client
+CFLAGS=$(WARN) $(DEBUG) $(FORTIFY) $(COVERAGE) $(OPTIMIZE) $(LANGUAGE)
+LDFLAGS=$(COVERAGE)
+
+PROGS=mandos-client plugins.d/password-request plugins.d/password-prompt
+
+objects=$(shell for p in $(PROGS); do echo $${p}.o; done)
+
+all: $(PROGS)
+
+mandos-client: mandos-client.o
+ $(LINK.o) -lgnutls $(COMMON) $^ $(LOADLIBES) $(LDLIBS) -o $@
+
+plugins.d/password-request: plugins.d/password-request.o
+ $(LINK.o) -lgnutls -lavahi-core -lgpgme $(COMMON) $^ $(LOADLIBES) $(LDLIBS) -o $@
+
+plugins.d/password-prompt: plugins.d/password-prompt.o
+ $(LINK.o) $(COMMON) $^ $(LOADLIBES) $(LDLIBS) -o $@
+
+.PHONY : all clean distclean run-client run-server
clean:
- -rm --force $(CPROGS) $(objects) $(htmldocs) $(DOCS) core
+ -rm --force $(PROGS) $(objects) core
distclean: clean
mostlyclean: clean
maintainer-clean: clean
- -rm --force --recursive keydir confdir
-check: all
+check: all
./mandos --check
-# Run the client with a local config and key
-run-client: all keydir/seckey.txt keydir/pubkey.txt
- @echo "###################################################################"
- @echo "# The following error messages are harmless and can be safely #"
- @echo "# ignored. The messages are caused by not running as root, but #"
- @echo "# you should NOT run \"make run-client\" as root unless you also #"
- @echo "# unpacked and compiled Mandos as root, which is NOT recommended. #"
- @echo "# From plugin-runner: setuid: Operation not permitted #"
- @echo "# From askpass-fifo: mkfifo: Permission denied #"
- @echo "# From mandos-client: setuid: Operation not permitted #"
- @echo "# seteuid: Operation not permitted #"
- @echo "# klogctl: Operation not permitted #"
- @echo "###################################################################"
- ./plugin-runner --plugin-dir=plugins.d \
- --config-file=plugin-runner.conf \
- --options-for=mandos-client:--seckey=keydir/seckey.txt,--pubkey=keydir/pubkey.txt \
- $(CLIENTARGS)
-
-# Used by run-client
-keydir/seckey.txt keydir/pubkey.txt: mandos-keygen
- install --directory keydir
- ./mandos-keygen --dir keydir --force
-
-# Run the server with a local config
-run-server: confdir/mandos.conf confdir/clients.conf
- @echo "#################################################################"
- @echo "# NOTE: Please IGNORE the error about \"Could not open file #"
- @echo "# u'/var/run/mandos.pid'\" - it is harmless and is caused by #"
- @echo "# the server not running as root. Do NOT run \"make run-server\" #"
- @echo "# server as root if you didn't also unpack and compile it thus. #"
- @echo "#################################################################"
- ./mandos --debug --no-dbus --configdir=confdir $(SERVERARGS)
-
-# Used by run-server
-confdir/mandos.conf: mandos.conf
- install --directory confdir
- install --mode=u=rw,go=r $^ $@
-confdir/clients.conf: clients.conf keydir/seckey.txt
- install --directory confdir
- install --mode=u=rw $< $@
-# Add a client password
- ./mandos-keygen --dir keydir --password >> $@
-
-install: install-server install-client-nokey
-
-install-html: html
- install --directory $(htmldir)
- install --mode=u=rw,go=r --target-directory=$(htmldir) \
- $(htmldocs)
-
-install-server: doc
- install --directory $(CONFDIR)
- install --mode=u=rwx,go=rx mandos $(PREFIX)/sbin/mandos
- install --mode=u=rwx,go=rx --target-directory=$(PREFIX)/sbin \
- mandos-ctl
- install --mode=u=rwx,go=rx --target-directory=$(PREFIX)/sbin \
- mandos-monitor
- install --mode=u=rw,go=r --target-directory=$(CONFDIR) \
- mandos.conf
- install --mode=u=rw --target-directory=$(CONFDIR) \
- clients.conf
- install --mode=u=rw,go=r dbus-mandos.conf \
- $(DESTDIR)/etc/dbus-1/system.d/mandos.conf
- install --mode=u=rwx,go=rx init.d-mandos \
- $(DESTDIR)/etc/init.d/mandos
- install --mode=u=rw,go=r default-mandos \
- $(DESTDIR)/etc/default/mandos
- if [ -z $(DESTDIR) ]; then \
- update-rc.d mandos defaults 25 15;\
- fi
- gzip --best --to-stdout mandos.8 \
- > $(MANDIR)/man8/mandos.8.gz
- gzip --best --to-stdout mandos-monitor.8 \
- > $(MANDIR)/man8/mandos-monitor.8.gz
- gzip --best --to-stdout mandos-ctl.8 \
- > $(MANDIR)/man8/mandos-ctl.8.gz
- gzip --best --to-stdout mandos.conf.5 \
- > $(MANDIR)/man5/mandos.conf.5.gz
- gzip --best --to-stdout mandos-clients.conf.5 \
- > $(MANDIR)/man5/mandos-clients.conf.5.gz
-
-install-client-nokey: all doc
- install --directory $(PREFIX)/lib/mandos $(CONFDIR)
- install --directory --mode=u=rwx $(KEYDIR) \
- $(PREFIX)/lib/mandos/plugins.d
- if [ "$(CONFDIR)" != "$(PREFIX)/lib/mandos" ]; then \
- install --mode=u=rwx \
- --directory "$(CONFDIR)/plugins.d"; \
- fi
- install --mode=u=rwx,go=rx \
- --target-directory=$(PREFIX)/lib/mandos plugin-runner
- install --mode=u=rwx,go=rx --target-directory=$(PREFIX)/sbin \
- mandos-keygen
- install --mode=u=rwx,go=rx \
- --target-directory=$(PREFIX)/lib/mandos/plugins.d \
- plugins.d/password-prompt
- install --mode=u=rwxs,go=rx \
- --target-directory=$(PREFIX)/lib/mandos/plugins.d \
- plugins.d/mandos-client
- install --mode=u=rwxs,go=rx \
- --target-directory=$(PREFIX)/lib/mandos/plugins.d \
- plugins.d/usplash
- install --mode=u=rwxs,go=rx \
- --target-directory=$(PREFIX)/lib/mandos/plugins.d \
- plugins.d/splashy
- install --mode=u=rwxs,go=rx \
- --target-directory=$(PREFIX)/lib/mandos/plugins.d \
- plugins.d/askpass-fifo
- install --mode=u=rwxs,go=rx \
- --target-directory=$(PREFIX)/lib/mandos/plugins.d \
- plugins.d/plymouth
- install initramfs-tools-hook \
- $(INITRAMFSTOOLS)/hooks/mandos
- install --mode=u=rw,go=r initramfs-tools-hook-conf \
- $(INITRAMFSTOOLS)/conf-hooks.d/mandos
- install initramfs-tools-script \
- $(INITRAMFSTOOLS)/scripts/init-premount/mandos
- install --mode=u=rw,go=r plugin-runner.conf $(CONFDIR)
- gzip --best --to-stdout mandos-keygen.8 \
- > $(MANDIR)/man8/mandos-keygen.8.gz
- gzip --best --to-stdout plugin-runner.8mandos \
- > $(MANDIR)/man8/plugin-runner.8mandos.gz
- gzip --best --to-stdout plugins.d/mandos-client.8mandos \
- > $(MANDIR)/man8/mandos-client.8mandos.gz
- gzip --best --to-stdout plugins.d/password-prompt.8mandos \
- > $(MANDIR)/man8/password-prompt.8mandos.gz
- gzip --best --to-stdout plugins.d/usplash.8mandos \
- > $(MANDIR)/man8/usplash.8mandos.gz
- gzip --best --to-stdout plugins.d/splashy.8mandos \
- > $(MANDIR)/man8/splashy.8mandos.gz
- gzip --best --to-stdout plugins.d/askpass-fifo.8mandos \
- > $(MANDIR)/man8/askpass-fifo.8mandos.gz
- gzip --best --to-stdout plugins.d/plymouth.8mandos \
- > $(MANDIR)/man8/plymouth.8mandos.gz
-
-install-client: install-client-nokey
-# Post-installation stuff
- -$(PREFIX)/sbin/mandos-keygen --dir "$(KEYDIR)"
- update-initramfs -k all -u
- echo "Now run mandos-keygen --password --dir $(KEYDIR)"
-
-uninstall: uninstall-server uninstall-client
-
-uninstall-server:
- -rm --force $(PREFIX)/sbin/mandos \
- $(PREFIX)/sbin/mandos-ctl \
- $(PREFIX)/sbin/mandos-monitor \
- $(MANDIR)/man8/mandos.8.gz \
- $(MANDIR)/man8/mandos-monitor.8.gz \
- $(MANDIR)/man8/mandos-ctl.8.gz \
- $(MANDIR)/man5/mandos.conf.5.gz \
- $(MANDIR)/man5/mandos-clients.conf.5.gz
- update-rc.d -f mandos remove
- -rmdir $(CONFDIR)
-
-uninstall-client:
-# Refuse to uninstall client if /etc/crypttab is explicitly configured
-# to use it.
- ! grep --regexp='^ *[^ #].*keyscript=[^,=]*/mandos/' \
- $(DESTDIR)/etc/crypttab
- -rm --force $(PREFIX)/sbin/mandos-keygen \
- $(PREFIX)/lib/mandos/plugin-runner \
- $(PREFIX)/lib/mandos/plugins.d/password-prompt \
- $(PREFIX)/lib/mandos/plugins.d/mandos-client \
- $(PREFIX)/lib/mandos/plugins.d/usplash \
- $(PREFIX)/lib/mandos/plugins.d/splashy \
- $(PREFIX)/lib/mandos/plugins.d/askpass-fifo \
- $(PREFIX)/lib/mandos/plugins.d/plymouth \
- $(INITRAMFSTOOLS)/hooks/mandos \
- $(INITRAMFSTOOLS)/conf-hooks.d/mandos \
- $(INITRAMFSTOOLS)/scripts/init-premount/mandos \
- $(MANDIR)/man8/mandos-keygen.8.gz \
- $(MANDIR)/man8/plugin-runner.8mandos.gz \
- $(MANDIR)/man8/mandos-client.8mandos.gz
- $(MANDIR)/man8/password-prompt.8mandos.gz \
- $(MANDIR)/man8/usplash.8mandos.gz \
- $(MANDIR)/man8/splashy.8mandos.gz \
- $(MANDIR)/man8/askpass-fifo.8mandos.gz \
- $(MANDIR)/man8/plymouth.8mandos.gz \
- -rmdir $(PREFIX)/lib/mandos/plugins.d $(CONFDIR)/plugins.d \
- $(PREFIX)/lib/mandos $(CONFDIR) $(KEYDIR)
- update-initramfs -k all -u
-
-purge: purge-server purge-client
-
-purge-server: uninstall-server
- -rm --force $(CONFDIR)/mandos.conf $(CONFDIR)/clients.conf \
- $(DESTDIR)/etc/dbus-1/system.d/mandos.conf
- $(DESTDIR)/etc/default/mandos \
- $(DESTDIR)/etc/init.d/mandos \
- $(DESTDIR)/var/run/mandos.pid
- -rmdir $(CONFDIR)
-
-purge-client: uninstall-client
- -shred --remove $(KEYDIR)/seckey.txt
- -rm --force $(CONFDIR)/plugin-runner.conf \
- $(KEYDIR)/pubkey.txt $(KEYDIR)/seckey.txt
- -rmdir $(KEYDIR) $(CONFDIR)/plugins.d $(CONFDIR)
+run-client: all
+ ./mandos-client --plugin-dir=plugins.d --options-for=password-request:--keydir=keydir
+
+run-server: all
+ ./mandos --debug --configdir=.
=== removed file 'NEWS'
--- NEWS 2010-09-28 18:57:31 +0000
+++ NEWS 1970-01-01 00:00:00 +0000
@@ -1,154 +0,0 @@
-This NEWS file records noteworthy changes, very tersely.
-See the manual for detailed information.
-
-Version 1.2 (2010-09-28)
-* Client:
-** New "plymouth" plugin to ask for a password using the Plymouth
- graphical boot system.
-** The Mandos client now automatically chooses a network interface if
- the DEVICE setting in /etc/initramfs-tools/initramfs.conf is set to
- the empty string. This is also the new default instead of "eth0".
-** The Mandos client --connect option now loops indefinitely until a
- password is received from the specified server.
-** Bug fix: Quote directory correctly in mandos-keygen with --password
-** Bug fix: don't use "echo -e" in mandos-keygen; unsupported by dash.
-* Server:
-** Terminology change: clients are now "ENABLED" or "DISABLED", not
- "valid" or "invalid".
-** New D-Bus API; see the file "DBUS-API".
-** New control utilities using the new D-Bus API:
- + mandos-ctl A command-line based utility
- + mandos-monitor A text-based GUI interface
-** New feature: manual interactive approval or denying of clients on a
- case-by-case basis.
-** New --debuglevel option to control logging
-** Will not write PID file if --debug is passed
-** Bug fix: Avoid race conditions with short "interval" values or
- fast checkers.
-** Bug fix: Don't try to bind to a network interface when none is
- specified
-
-Version 1.0.14 (2009-10-25)
-Enable building without -pie and -fPIE if BROKEN_PIE is set.
-
-Version 1.0.13 (2009-10-22)
-* Client
-** Security bug fix: If Mandos server is also installed, do not copy
- its config files (with encrypted passwords) into the initrd.img-*
- files.
-
-Version 1.0.12 (2009-09-17)
-* Client
-** Bug fix: Allow network interface renaming by "udev" by taking down
- the network interface after using it.
-** Bug fix: User-supplied plugins are now installed correctly.
-** Bug fix: If usplash was used but the password was instead provided
- by the Mandos server, the usplash daemon used to ignore the first
- command passed to it. This has been fixed.
-** Bug fix: Make the "--userid" and "--groupid" options in
- "plugin-runner.conf" work.
-* Server
-** Bug fix: Fix the LSB header in the init.d script to make dependency
- based booting work.
-** A client receiving its password now also counts as if a checker was
- run successfully (i.e. the timeout timer is reset).
-
-Version 1.0.11 (2009-05-23)
-* Client
-** Bug fix: Use "pkg-config" instead of old "libgnutls-config".
-
-Version 1.0.10 (2009-05-17)
-* Client
-** Security bug fix: Fix permissions on initrd.img-*.bak files when
- upgrading from older versions.
-
-Version 1.0.9 (2009-05-17)
-* Client
-** Security bug fix: Fix permissions on initrd.img file when
- installing new linux-image-* packages calling mkinitramfs-kpkg (all
- version lower than 2.6.28-1-* does this).
-
-Version 1.0.8 (2009-02-25)
-* Client
-** Bug fix: Fix missing quote characters in initramfs-tools-hook.
-
-Version 1.0.7 (2009-02-24)
-* Client
-** Bug fix: Do not depend on GNU awk.
-
-Version 1.0.6 (2009-02-13)
-* Server
-** Fix bug where server would stop responding, with a zombie checker
-** Support for disabling IPv6 (only for advanced users)
-** Fix bug which made server not change group ID
-
-* Client
-** Bug fix: Fix permission for /lib64 (on relevant architechtures).
-** Add support for IPv4 addresses.
-** Add support in mandos-client for not bringing up a network
- interface by specifying an empty string to "--interface".
-** Make password prompt on boot not be mangled by kernel log messages
- about network interface.
-** Get network interface from initramfs.conf and/or from kernel
- command line.
-** If set by "ip=" kernel command line, configure network on boot.
-** Support connecting directly using "mandos=connect" kernel command.
- line option, provided network is configured using "ip=".
-** Fix bug which made plugin-runner and mandos-client not change group
- ID.
-** Fix bug where the "--options-for" option of plugin-runner would
- truncate the value at the first colon character.
-** Fix bug where plugin-runner would not go to fallback if all plugins
- failed.
-** Fix bug where mandos-client would not clean temporary directory on
- a signal or on certain file systems.
-** Bug fix: remove bashism in /bin/sh script "mandos-keygen".
-
-Version 1.0.5 (2009-01-17)
-* Client
-** Fix small memory leak in plugin-runner.
-
-Version 1.0.4 (2009-01-15)
-* Server
-** Only find matched user/group pairs when searching for suitable
- nonprivileged user/group to switch to.
-
-* Client
-** New kernel parameter "mandos=off" makes client not run at boot.
-** Fix linking errors and compilation warnings on AMD64.
-** Parse numbers in command line options better.
-** The splashy and usplash plugins are more robust while traversing
- /proc, and will not abort if a process suddenly disappears.
-
-Version 1.0.3 (2009-01-06)
-* Server
-** Now tries to change to user and group "_mandos" before falling back
- to trying the old values "mandos", "nobody:nogroup", and "65534".
-** Now does not abort on startup even if no clients are defined in
- clients.conf.
-
-* Client
-** Plugins named "*.dpkg-bak" are now ignored.
-** Hopefully fixed compilation failure on some architectures where the
- C compiler does not recognize the "-z" option as a linker option.
-
-Version 1.0.2 (2008-10-17)
-* mandos-keygen now signs the encrypted key blobs. This signature is
- not currently verified by mandos-client, but this may change in the
- future.
-
-Version 1.0.1 (2008-10-07)
-* Server
-** Expand environment variables and ~user in clients.conf's "secfile"
- The "secfile" option in /etc/mandos/clients.conf now expands
- "~user/foo" and "$ENVVAR" strings.
-
-* Client (plugin-runner, plugins, etc.)
-** Manual pages for the usplash, splashy, and askpass-fifo plugins.
- All plugins now have man pages.
-** More secure compilation and linking flags.
- All programs are now compiled with "-fstack-protector-all -fPIE
- -pie", and linked using "-z relro -pie" for additional security.
-
-* There is now a "NEWS" file (this one), giving a history of
- noteworthy changes.
=== removed file 'README'
--- README 2010-09-26 18:32:58 +0000
+++ README 1970-01-01 00:00:00 +0000
@@ -1,182 +0,0 @@
--*- org -*-
-
-* Mandos
- - Have your cake and eat it too!
-
- You know how it is. You’ve heard of it happening. The Man comes
- and takes away your servers, your friends’ servers, the servers of
- everybody in the same hosting facility. The servers of their
- neighbors, and their neighbors’ friends. The servers of people who
- owe them money. And like *that*, they’re gone. And you doubt
- you’ll ever see them again.
-
- That is why your servers have encrypted root file systems. However,
- there’s a downside. There’s no going around it: rebooting is a
- pain. Dragging out that rarely-used keyboard and screen and
- unraveling cables behind your servers to plug them in to type in
- that password is messy, especially if you have many servers. There
- are some people who do clever things like using serial line consoles
- and daisy-chain it to the next server, and keep all the servers
- connected in a ring with serial cables, which will work, if your
- servers are physically close enough. There are also other
- out-of-band management solutions, but with *all* these, you still
- have to be on hand and manually type in the password at boot time.
- Otherwise the server just sits there, waiting for a password.
-
- Wouldn’t it be great if you could have the security of encrypted
- root file systems and still have servers that could boot up
- automatically if there was a short power outage while you were
- asleep? That you could reboot at will, without having someone run
- over to the server to type in the password?
-
- Well, with Mandos, you (almost) can! The gain in convenience will
- only be offset by a small loss in security. The setup is as
- follows:
-
- The server will still have its encrypted root file system. The
- password to this file system will be stored on another computer
- (henceforth known as the Mandos server) on the same local network.
- The password will *not* be stored in plaintext, but encrypted with
- OpenPGP. To decrypt this password, a key is needed. This key (the
- Mandos client key) will not be stored there, but back on the
- original server (henceforth known as the Mandos client) in the
- initial RAM disk image. Oh, and all network Mandos client/server
- communications will be encrypted, using TLS (SSL).
-
- So, at boot time, the Mandos client will ask for its encrypted data
- over the network, decrypt it to get the password, use it to decrypt
- the root file, and continue booting.
-
- Now, of course the initial RAM disk image is not on the encrypted
- root file system, so anyone who had physical access could take the
- Mandos client computer offline and read the disk with their own
- tools to get the authentication keys used by a client. *But*, by
- then the Mandos server should notice that the original server has
- been offline for too long, and will no longer give out the encrypted
- key. The timing here is the only real weak point, and the method,
- frequency and timeout of the server’s checking can be adjusted to
- any desired level of paranoia
-
- (The encrypted keys on the Mandos server is on its normal file
- system, so those are safe, provided the root file system of *that*
- server is encrypted.)
-
-* FAQ - couldn’t the security be defeated by...
-
-** Grabbing the Mandos client key from the initrd *really quickly*?
- This, as mentioned above, is the only real weak point. But if you
- set the timing values tight enough, this will be really difficult
- to do. An attacker would have to physically disassemble the client
- computer, extract the key from the initial RAM disk image, and then
- connect to a *still online* Mandos server to get the encrypted key,
- and do all this *before* the Mandos server timeout kicks in and the
- Mandos server refuses to give out the key to anyone.
-
- Now, as the typical procedure seems to be to barge in and turn off
- and grab *all* computers, to maybe look at them months later, this
- is not likely. If someone does that, the whole system *will* lock
- itself up completely, since Mandos servers are no longer running.
-
- For sophisticated attackers who *could* do the clever thing, *and*
- had physical access to the server for enough time, it would be
- simpler to get a key for an encrypted file system by using hardware
- memory scanners and reading it right off the memory bus.
-
-** Replay attacks?
- Nope, the network stuff is all done over TLS, which provides
- protection against that.
-
-** Man-in-the-middle?
- No. The server only gives out the passwords to clients which have
- *in the TLS handshake* proven that they do indeed hold the OpenPGP
- private key corresponding to that client.
-
-** Physically grabbing the Mandos server computer?
- You could protect *that* computer the old-fashioned way, with a
- must-type-in-the-password-at-boot method. Or you could have two
- computers be the Mandos server for each other.
-
- Multiple Mandos servers can coexist on a network without any
- trouble. They do not clash, and clients will try all available
- servers. This means that if just one reboots then the other can
- bring it back up, but if both reboot at the same time they will
- stay down until someone types in the password on one of them.
-
-** Faking ping replies?
- The default for the server is to use "fping", the replies to which
- could be faked to eliminate the timeout. But this could easily be
- changed to any shell command, with any security measures you like.
- It could, for instance, be changed to an SSH command with strict
- keychecking, which could not be faked. Or IPsec could be used for
- the ping packets, making them secure.
-
-* Security Summary
- So, in summary: The only weakness in the Mandos system is from
- people who have:
- 1. The power to come in and physically take your servers, *and*
- 2. The cunning and patience to do it carefully, one at a time, and
- *quickly*, faking Mandos client/server responses for each one
- before the timeout.
-
- While there are some who may be threatened by people who have *both*
- these attributes, they do not, probably, constitute the majority.
-
- If you *do* face such opponents, you must figure that they could
- just as well open your servers and read the file system keys right
- off the memory by running wires to the memory bus.
-
- What Mandos is designed to protect against is *not* such determined,
- focused, and competent attacks, but against the early morning knock
- on your door and the sudden absence of all the servers in your
- server room. Which it does nicely.
-
-* The Plugin System
- In the early designs, the mandos-client(8mandos) program (which
- retrieves a password from the Mandos server) also prompted for a
- password on the terminal, in case a Mandos server could not be
- found. Other ways of retrieving a password could easily be
- envisoned, but this multiplicity of purpose was seen to be too
- complex to be a viable way to continue. Instead, the original
- program was separated into mandos-client(8mandos) and
- password-prompt(8mandos), and a plugin-runner(8mandos) exist to run
- them both in parallel, allowing the first successful plugin to
- provide the password. This opened up for any number of additional
- plugins to run, all competing to be the first to find a password and
- provide it to the plugin runner.
-
- Four additional plugins are provided:
- * plymouth(8mandos)
- This prompts for a password when using plymouth(8).
- * usplash(8mandos)
- This prompts for a password when using usplash(8).
- * splashy(8mandos)
- This prompts for a password when using splashy(8).
- * askpass-fifo(8mandos)
- To provide compatibility with the "askpass" program from
- cryptsetup, this plugin listens to the same FIFO as askpass would
- do.
-
- More plugins can easily be written and added by the system
- administrator; see the section called "WRITING PLUGINS" in
- plugin-runner(8mandos) to learn the plugin requirements.
-
-* Copyright
-
- Copyright © 2008-2010 Teddy Hogeborn
- Copyright © 2008-2010 Björn Påhlsson
-
-** License:
-
- This program is free software: you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see
- .
=== modified file 'TODO'
--- TODO 2010-09-26 18:32:58 +0000
+++ TODO 2008-08-07 21:45:41 +0000
@@ -1,134 +1,60 @@
-*- org -*-
-* Use _attribute_((nonnull)) wherever possible.
-
-* mandos-client
-** TODO [#B] use scandir(3) instead of readdir(3)
-** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name
-** TODO [#B] Retry a server which has a non-definite reply:
-*** A closed connection during the TLS handshake
-*** A TCP timeout
-** TODO [#B] Use capabilities instead of seteuid().
-** TODO [#A] Retry --connect forever
-
-* splashy
-** TODO [#B] use scandir(3) instead of readdir(3)
-** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name
-
-* usplash
-** TODO [#A] Make it work again
-** TODO [#B] use scandir(3) instead of readdir(3)
-** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name
-** TODO Use [[info:libc:Argz%20Functions][argz_extract]]
-
-* askpass-fifo
-** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name
-** TODO [#B] Drop privileges after opening FIFO.
-
-* password-prompt
-** TODO [#B] Prefix all debug output with "Mandos plugin " + program_invocation_short_name
-** TODO [#B] lock stdin (with flock()?)
-
-* TODO [#B] passdev
-
-* plugin-runner
-** TODO [#B] use scandir(3) instead of readdir(3)
-** TODO [#C] use same file name rules as run-parts(8)
-** kernel command line option for debug info
-** TODO [#B] Use openat()
-
-* mandos (server)
-** TODO [#B] Log level :BUGS:
-** TODO Persistent state :BUGS:
- /var/lib/mandos/*
-*** TODO /etc/mandos/clients.d/*.conf
- Watch this directory and add/remove/update clients?
-** TODO [#C] config for TXT record
-** TODO Log level option
- syslogger.setLevel(logging.WARNING)
- + SetLogLevel D-Bus call
-** TODO Implement --foreground :BUGS:
- [[info:standards:Option%20Table][Table of Long Options]]
-** TODO Implement --socket
- [[info:standards:Option%20Table][Table of Long Options]]
-** TODO Date+time on console log messages :BUGS:
- Is this the default?
-** TODO [#C] DBusServiceObjectUsingSuper
-** TODO [#B] Global enable/disable flag
-** TODO [#B] By-client countdown on secrets given
-** TODO [#B] Fix problem with fsck taking a really long time
- Whenever a client successfully gets a secret it could get a
- one-time timeout boost to allow for an fsck-incurred delay
-** TODO [#A] Delay before client receives key
- This would give an operator opportunity to cancel the request if
- desired.
-** TODO [#A] Client manual approval mode
- A client needs manual approval on the server before it gets the
- secret
-** TODO [#B] Support RFC 3339 time duration syntax
-** More D-Bus methods
-*** NeedsApproval(50, True) -> timeout, default approve
- Default approval is configurable, but True by default
- + Approve(True) -> approve sending saved
- + Approve(False) -> Close client connection immediately
-*** NeedsPassword(50) - Timeout, default disapprove
- + SetPass(u"gazonk", True) -> Approval, persistent
- + Approve(False) -> Close client connection immediately
-** TODO [#C] python-parsedatetime
-** TODO [#C] systemd/launchd
- http://0pointer.de/blog/projects/systemd.html
-** TODO Separate logging logic to own object
-** TODO make clients to a dict!
-** TODO [#A] Limit approval_delay to max gnutls/tls timeout value
-** TODO [#B] break the wait on approval_delay if connection dies
-** TODO Generate Client.runtime_expansions from client options + extra
-** TODO Allow %%(checker)s as a runtime expansion
-
-* mandos.xml
-** Add mandos contact info in manual pages
-
-* mandos-ctl
-*** Handle "no D-Bus server" and/or "no Mandos server found" better
-*** [#B] --dump option
-** TODO Support RFC 3339 time duration syntax
-
-* TODO mandos-dispatch
- Listens for specified D-Bus signals and spawns shell commands with
- arguments.
-
-* mandos-monitor
-** TODO help should be toggable
-** Urwid client data displayer
- Better view of client data in the listing
-*** Properties popup
-** Nicer crashes. Stack traces Messes up shell.
-*** Print a nice "We are sorry" message, save stack trace to log.
-** Show timeout countdown for approval
-
-* mandos-keygen
-** TODO Loop until passwords match when run interactively
-** TODO "--secfile" option
- Using the "secfile" option instead of "secret"
-** TODO [#B] "--test" option
- For testing decryption before rebooting.
-
-* Makefile
-** TODO Add "--Xlinker --as-needed"
- http://udrepper.livejournal.com/19395.html
-** TODO [#C] Implement DEB_BUILD_OPTIONS
- http://www.debian.org/doc/debian-policy/ch-source.html#s-debianrules-options
-
-* Package
-** /usr/share/initramfs-tools/hooks/mandos
-*** TODO [#C] use same file name rules as run-parts(8)
-*** TODO [#C] Do not install in initrd.img if configured not to.
- Use "/etc/initramfs-tools/hooksconf.d/mandos"?
-** TODO [#C] /etc/bash_completion.d/mandos
- From XML sources directly?
-
-* Side Stuff
-** TODO Locate which packet move the other bin/sh when busy box is deactivated
-** TODO contact owner of packet, and ask them to have that shell static in position regardless of busybox
+* README file
+
+* COPYING file
+ [[file:/usr/share/common-licenses/GPL-3][GPLv3]]
+
+* Mandos-client
+** [#A] Man page: man8/mandos-client.8mandos
+** [#A] check return codes of all system calls
+** [#B] header files/symbols tally
+** use strsep instead of strtok?
+** use config file in addition to arguments
+** pass things in environment, like device name, etc
+
+* Password-request
+** [#A] Man page: man8/password-request.8mandos
+** [#A] check return codes of all system calls
+** [#B] header files/symbols tally
+** IPv4 support
+** use strsep instead of strtok?
+** Do not depend on GPG key rings on disk
+ This would mean creating new GPG key rings with GPGME by importing
+ the key files from scratch on every program start.
+
+* Password-prompt
+** [#A] Man page: man8/password-prompt.8mandos
+** Use getpass(3)?
+ [[info:libc:getpass][GNU LibC Manual: Reading Passwords]]
+
+* Server
+** [#A] Command man page: man8/mandos.8
+** [#A] Config file man page: man5/mandos.conf (mandos.conf)
+** [#A] Config file man page: man5/mandos-clients.conf (clients.conf)
+** [#A] write PID file
+** [#A] /etc/init.d/mandos-server
+** Better comments in config files
+** Log level
+** /etc/mandos/clients.d/*.conf
+ Watch this directory and add/remove/update clients?
+** config for TXT record
+** Run-time communication with server
+ probably using D-Bus
+** Implement --foreground
+ [[info:standards:Option%20Table][Table of Long Options]]
+** Implement --socket
+ [[info:standards:Option%20Table][Table of Long Options]]
+
+* Mandos-tools/utilities
+ All of this probably using D-Bus
+** List clients
+** Enable client
+** Disable client
+
+* Installer
+** [#A] Change initrd.img file to not be publically readable
+** [#A] Create GPG key ring files in initrd
#+STARTUP: showall
=== modified file 'clients.conf'
--- clients.conf 2010-09-12 03:00:40 +0000
+++ clients.conf 2008-07-29 03:35:39 +0000
@@ -1,79 +1,33 @@
-# Default settings for all clients. These values are the default
-# values, so uncomment and change them if you want different ones.
[DEFAULT]
-
-# How long until a client is disabled and not be allowed to get the
-# data this server holds.
-;timeout = 1h
-
-# How often to run the checker to confirm that a client is still up.
-# Note: a new checker will not be started if an old one is still
-# running. The server will wait for a checker to complete until the
-# above "timeout" occurs, at which time the client will be disabled,
-# and any running checker killed.
-;interval = 5m
-
-# What command to run as "the checker".
-;checker = fping -q -- %%(host)s
-
-# Whether to approve a client by default after the approval delay.
-;approved_by_default = True
-
-# How long to wait for approval.
-;approval_delay = 0s
-
-# How long one approval will last.
-;approval_duration = 1s
-
-
-;####
-;# Example client
-;[foo]
-;
-;# OpenPGP key fingerprint
-;fingerprint = 7788 2722 5BA7 DE53 9C5A 7CFA 59CF F7CD BD9A 5920
-;
-;# This is base64-encoded binary data. It will be decoded and sent to
-;# the client matching the above fingerprint. This should, of course,
-;# be OpenPGP encrypted data, decryptable only by the client.
-;secret =
-; hQIOA6QdEjBs2L/HEAf/TCyrDe5Xnm9esa+Pb/vWF9CUqfn4srzVgSu234
-; REJMVv7lBSrPE2132Lmd2gqF1HeLKDJRSVxJpt6xoWOChGHg+TMyXDxK+N
-; Xl89vGvdU1XfhKkVm9MDLOgT5ECDPysDGHFPDhqHOSu3Kaw2DWMV/iH9vz
-; 3Z20erVNbdcvyBnuojcoWO/6yfB5EQO0BXp7kcyy00USA3CjD5FGZdoQGI
-; Tb8A/ar0tVA5crSQmaSotm6KmNLhrFnZ5BxX+TiE+eTUTqSloWRY6VAvqW
-; QHC7OASxK5E6RXPBuFH5IohUA2Qbk5AHt99pYvsIPX88j2rWauOokoiKZo
-; t/9leJ8VxO5l3wf/U64IH8bkPIoWmWZfd/nqh4uwGNbCgKMyT+AnvH7kMJ
-; 3i7DivfWl2mKLV0PyPHUNva0VQxX6yYjcOhj1R6fCr/at8/NSLe2OhLchz
-; dC+Ls9h+kvJXgF8Sisv+Wk/1RadPLFmraRlqvJwt6Ww21LpiXqXHV2mIgq
-; WnR98YgSvUi3TJHrUQiNc9YyBzuRo0AjgG2C9qiE3FM+Y28+iQ/sR3+bFs
-; zYuZKVTObqiIslwXu7imO0cvvFRgJF/6u3HNFQ4LUTGhiM3FQmC6NNlF3/
-; vJM2hwRDMcJqDd54Twx90Wh+tYz0z7QMsK4ANXWHHWHR0JchnLWmenzbtW
-; 5MHdW9AYsNJZAQSOpirE4Xi31CSlWAi9KV+cUCmWF5zOFy1x23P6PjdaRm
-; 4T2zw4dxS5NswXWU0sVEXxjs6PYxuIiCTL7vdpx8QjBkrPWDrAbcMyBr2O
-; QlnHIvPzEArRQLo=
-;
-;# Host name; used only by the checker, not used by the server itself.
-;host = foo.example.org
-;####
-
-;####
-;# Another example client, named "bar".
-;[bar]
-;# The fingerprint is not space or case sensitive
-;fingerprint = 3e393aeaefb84c7e89e2f547b3a107558fca3a27
-;
-;# If "secret" is not specified, a file can be read for the data.
-;secfile = /etc/mandos/bar-secret.bin
-;
-;# An IP address for host is also fine, if the checker accepts it.
-;host = 192.0.2.3
-;
-;# Parameters from the [DEFAULT] section can be overridden per client.
-;interval = 5m
-;
-;# This client requires manual approval before it receives its secret.
-;approved_by_default = False
-;# Require approval within 30 seconds.
-;approval_delay = 30s
-;####
+timeout = 1h
+interval = 5m
+checker = fping -q -- %%(fqdn)s
+
+# Example
+[foo]
+fingerprint = 3e393aeaefb84c7e89e2f547b3a107558fca3a27
+secret = Base+64+encoded+OpenPGP+encrypted+data/=
+# secfile = /etc/mandos/foo-secret.txt.asc
+fqdn = foo.example.org
+checker = fping -q -- %%(fqdn)s
+timeout = 10m
+
+[braxen_client]
+fingerprint = 7788 2722 5BA7 DE53 9C5A 7CFA 59CF F7CD BD9A 5920
+secret =
+ hQIOA6QdEjBs2L/HEAf/TCyrDe5Xnm9esa+Pb/vWF9CUqfn4srzVgSu234REJMVv
+ 7lBSrPE2132Lmd2gqF1HeLKDJRSVxJpt6xoWOChGHg+TMyXDxK+NXl89vGvdU1Xf
+ hKkVm9MDLOgT5ECDPysDGHFPDhqHOSu3Kaw2DWMV/iH9vz3Z20erVNbdcvyBnuoj
+ coWO/6yfB5EQO0BXp7kcyy00USA3CjD5FGZdoQGITb8A/ar0tVA5crSQmaSotm6K
+ mNLhrFnZ5BxX+TiE+eTUTqSloWRY6VAvqWQHC7OASxK5E6RXPBuFH5IohUA2Qbk5
+ AHt99pYvsIPX88j2rWauOokoiKZot/9leJ8VxO5l3wf/U64IH8bkPIoWmWZfd/nq
+ h4uwGNbCgKMyT+AnvH7kMJ3i7DivfWl2mKLV0PyPHUNva0VQxX6yYjcOhj1R6fCr
+ /at8/NSLe2OhLchzdC+Ls9h+kvJXgF8Sisv+Wk/1RadPLFmraRlqvJwt6Ww21Lpi
+ XqXHV2mIgqWnR98YgSvUi3TJHrUQiNc9YyBzuRo0AjgG2C9qiE3FM+Y28+iQ/sR3
+ +bFszYuZKVTObqiIslwXu7imO0cvvFRgJF/6u3HNFQ4LUTGhiM3FQmC6NNlF3/vJ
+ M2hwRDMcJqDd54Twx90Wh+tYz0z7QMsK4ANXWHHWHR0JchnLWmenzbtW5MHdW9AY
+ sNJZAQSOpirE4Xi31CSlWAi9KV+cUCmWF5zOFy1x23P6PjdaRm4T2zw4dxS5NswX
+ WU0sVEXxjs6PYxuIiCTL7vdpx8QjBkrPWDrAbcMyBr2OQlnHIvPzEArRQLo=
+ =iHhv
+fqdn = localhost
+interval = 5m
=== removed file 'common.ent'
--- common.ent 2010-09-28 18:57:31 +0000
+++ common.ent 1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
-
-
-
=== removed file 'dbus-mandos.conf'
--- dbus-mandos.conf 2009-11-09 07:35:16 +0000
+++ dbus-mandos.conf 1970-01-01 00:00:00 +0000
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
=== removed directory 'debian'
=== removed file 'debian/changelog'
--- debian/changelog 2010-09-28 18:57:31 +0000
+++ debian/changelog 1970-01-01 00:00:00 +0000
@@ -1,174 +0,0 @@
-mandos (1.2-1) unstable; urgency=low
-
- * New upstream release.
- * Makefile (LINK_FORTIFY_LD): Remove "-fPIE". (Closes: #557076)
- * debian/control: Add gnupg dependency to "mandos-client" and removed it
- from "mandos". Added dependency on "python-urwid" "mandos" since the
- new "mandos-monitor" utility needs it, and on "python (>=2.6) |
- python-multiprocessing" since the Mandos server now uses it.
- * debian/rules: Set BROKEN_PIE on mips and mipsel if a known buggy
- version of binutils is used.
- * debian/mandos.docs: Also install "/usr/share/doc/mandos/DBUS-API".
- * debian/mandos.dirs: Added "etc/dbus-1/system.d".
- * debian/mandos-client.README.Debian: Update info about DEVICE setting
- of initramfs.conf.
- * debian/mandos-client.README.Debian: Remove warning about --connect not
- looping, since it now does.
-
- -- Teddy Hogeborn Tue, 28 Sep 2010 20:46:11 +0200
-
-mandos (1.0.14-1) unstable; urgency=low (HIGH on mips and mipsel)
-
- * New upstream release.
- * debian/rules: Build with BROKEN_PIE set on mips and mipsel
- architectures - fixes FTBFS there.
-
- -- Teddy Hogeborn Sun, 25 Oct 2009 20:10:09 +0100
-
-mandos (1.0.13-1) unstable; urgency=high
-
- * New upstream release.
- * Do not copy unnecessary files to initrd (Closes: #551907)
-
- -- Teddy Hogeborn Thu, 22 Oct 2009 00:53:21 +0200
-
-mandos (1.0.12-1) unstable; urgency=low
-
- * New upstream release.
- * init.d-mandos: Correct dependencies (Closes: #546928)
- * debian/control (Standards-Version): Changed to "3.8.3".
- * debian/mandos-client.README.Debian: Improved wording and formatting.
- Updated location of nfsroot.txt.
- * debian/mandos.README.Debian: Improved wording and formatting.
- * debian/mandos-client.postinst (configure): Don't look for user and
- group with the old name if upgrading from a new enough version.
- * debian/mandos.postinst (configure): - '' -
- * debian/mandos-client.README.Debian: Added text about non-usability of
- pseudo-network interfaces.
-
- -- Teddy Hogeborn Thu, 17 Sep 2009 15:03:59 +0200
-
-mandos (1.0.11-1) unstable; urgency=low
-
- * debian/control (Standards-Version): Changed to "3.8.1".
- * Makefile (GNUTLS_CFLAGS, GNUTLS_CFLAGS): Use "pkg-config" instead of
- the old "libgnutls-config" script. (Closes: #529836)
-
- -- Teddy Hogeborn Sat, 23 May 2009 07:12:20 +0200
-
-mandos (1.0.10-1) unstable; urgency=low
-
- * New upstream release.
- * debian/mandos-client.postinst (update_initramfs): Fix permissions of
- old initrd.img-*.bak files.
-
- -- Teddy Hogeborn Sun, 17 May 2009 04:56:35 +0200
-
-mandos (1.0.9-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Teddy Hogeborn Sun, 17 May 2009 02:59:45 +0200
-
-mandos (1.0.8-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Teddy Hogeborn Wed, 25 Feb 2009 02:26:57 +0100
-
-mandos (1.0.7-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Teddy Hogeborn Tue, 24 Feb 2009 12:58:06 +0100
-
-mandos (1.0.6-1) unstable; urgency=low
-
- * New upstream release.
- * debian/mandos-client.postinst: Converted to Bourne shell. Also
- minor message change.
- * debian/mandos-client.postrm: Minor message change.
- * debian/mandos.postinst: Converted to Bourne shell. Also minor
- message change.
- * debian/mandos.prerm: Minor message change.
- * debian/rules (install-indep): Removed "--no-start" from
- dh_installinit.
- * debian/mandos-client.lintian-overrides: Remove obsolete override for
- unbreakable line in plugin-runner manual page.
- * debian/control (mandos/Depends): Added "python-gobject".
- * debian/mandos-client.dirs: Change
- "usr/share/initramfs-tools/scripts/local-top" to
- "usr/share/initramfs-tools/scripts/init-premount".
- * debian/mandos-client.README.Debian: Add reference to initramfs.conf
- and nfsroot.txt. New section about the new non-local connection
- feature.
-
- -- Teddy Hogeborn Fri, 13 Feb 2009 09:27:25 +0100
-
-mandos (1.0.5-1) unstable; urgency=low
-
- * New upstream release.
-
- -- Teddy Hogeborn Sat, 17 Jan 2009 02:26:00 +0100
-
-mandos (1.0.4-1) unstable; urgency=low
-
- * New upstream release.
- * debian/watch: New file.
- * debian/mandos-client.README.Debian: Document new "mandos=off" kernel
- parameter.
-
- -- Teddy Hogeborn Thu, 15 Jan 2009 05:49:22 +0100
-
-mandos (1.0.3-2) unstable; urgency=low
-
- * Removed some now-unused debconf files.
- * Changed postinst scripts to not source debconf/confmodule.
- * Removed po-debconf from build-depends.
-
- -- Teddy Hogeborn Tue, 06 Jan 2009 21:28:20 +0100
-
-mandos (1.0.3-1) unstable; urgency=low
-
- * New upstream release.
- * Add -Xlinker to linker flags to fix FTBFS for some architectures.
- Thanks to Thiemo Seufer for the report and
- fix. (Closes: #509398)
- * Remove debconf use altogether, thereby stopping debconf abuse. Thanks
- to Christian Perrier . (Closes: #509653)
- * Add NEWS file to /usr/share/doc directories.
- * Use and create "_mandos" user+group. Rename old user+group created by
- older versions of this package.
- * Fix manual pages by adding build-depend on "docbook-xml".
-
- -- Teddy Hogeborn Tue, 06 Jan 2009 01:21:20 +0100
-
-mandos (1.0.2-1) unstable; urgency=low
-
- * New upstream release.
- * debian/copyright: Rewritten to conform to
- .
-
- -- Teddy Hogeborn Fri, 17 Oct 2008 20:42:12 +0200
-
-mandos (1.0.1-1) unstable; urgency=low
-
- * New upstream release.
- * Separate /usr/share/doc/mandos-client/README.Debian into sections with
- headlines. Add instructions on how to test the server and verify the
- password.
-
- -- Teddy Hogeborn Tue, 07 Oct 2008 23:07:23 +0200
-
-mandos (1.0-2) unstable; urgency=low
-
- * Added comments in debian/*.lintian-overrides files. Added Debian
- revison number to version number.
-
- -- Teddy Hogeborn Wed, 01 Oct 2008 17:23:35 +0200
-
-mandos (1.0-1) unstable; urgency=low
-
- * Initial Release. (Closes: #500727).
-
- -- Teddy Hogeborn Tue, 30 Sep 2008 21:58:43 +0200
=== removed file 'debian/compat'
--- debian/compat 2008-09-17 00:34:09 +0000
+++ debian/compat 1970-01-01 00:00:00 +0000
@@ -1,1 +0,0 @@
-7
=== removed file 'debian/control'
--- debian/control 2010-10-01 18:40:55 +0000
+++ debian/control 1970-01-01 00:00:00 +0000
@@ -1,54 +0,0 @@
-Source: mandos
-Section: admin
-Priority: extra
-Maintainer: Mandos Maintainers
-Uploaders: Teddy Hogeborn ,
- Björn Påhlsson
-Build-Depends: debhelper (>= 7), docbook-xml, docbook-xsl,
- libavahi-core-dev, libgpgme11-dev, libgnutls-dev, xsltproc,
- pkg-config
-Standards-Version: 3.9.1
-Vcs-Bzr: http://ftp.fukt.bsnet.se/pub/mandos/trunk
-Vcs-Browser: http://bzr.fukt.bsnet.se/loggerhead/mandos/trunk/files
-Homepage: http://www.fukt.bsnet.se/mandos
-
-Package: mandos
-Architecture: all
-Depends: ${misc:Depends}, python (>=2.5), python-gnutls, python-dbus,
- python-avahi, python-gobject, avahi-daemon, adduser,
- python-urwid, python (>=2.6) | python-multiprocessing
-Recommends: fping
-Description: a server giving encrypted passwords to Mandos clients
- This is the server part of the Mandos system, which allows
- computers to have encrypted root file systems and at the
- same time be capable of remote and/or unattended reboots.
- .
- The computers run a small client program in the initial RAM
- disk environment which will communicate with a server over a
- network. All network communication is encrypted using TLS.
- The clients are identified by the server using an OpenPGP
- key; each client has one unique to it. The server sends the
- clients an encrypted password. The encrypted password is
- decrypted by the clients using the same OpenPGP key, and the
- password is then used to unlock the root file system,
- whereupon the computers can continue booting normally.
-
-Package: mandos-client
-Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, cryptsetup,
- gnupg (<< 2)
-Enhances: cryptsetup
-Description: do unattended reboots with an encrypted root file system
- This is the client part of the Mandos system, which allows
- computers to have encrypted root file systems and at the
- same time be capable of remote and/or unattended reboots.
- .
- The computers run a small client program in the initial RAM
- disk environment which will communicate with a server over a
- network. All network communication is encrypted using TLS.
- The clients are identified by the server using an OpenPGP
- key; each client has one unique to it. The server sends the
- clients an encrypted password. The encrypted password is
- decrypted by the clients using the same OpenPGP key, and the
- password is then used to unlock the root file system,
- whereupon the computers can continue booting normally.
=== removed file 'debian/copyright'
--- debian/copyright 2010-09-26 18:32:58 +0000
+++ debian/copyright 1970-01-01 00:00:00 +0000
@@ -1,26 +0,0 @@
-Format-Specification:
- http://wiki.debian.org/Proposals/CopyrightFormat?action=recall&rev=233
-Upstream-Name: Mandos
-Upstream-Maintainer: Mandos Maintainers
-Upstream-Source:
-
-Files: *
-Copyright: Copyright © 2008-2010 Teddy Hogeborn
-Copyright: Copyright © 2008-2010 Björn Påhlsson
-License: GPL-3+
- This program is free software: you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
- .
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
- .
- You should have received a copy of the GNU General Public License
- along with this program. If not, see
- .
- .
- On Debian systems, the complete text of the GNU General Public
- License can be found in "/usr/share/common-licenses/GPL".
=== removed file 'debian/mandos-client.README.Debian'
--- debian/mandos-client.README.Debian 2010-09-27 17:53:53 +0000
+++ debian/mandos-client.README.Debian 1970-01-01 00:00:00 +0000
@@ -1,86 +0,0 @@
-* Choose the Client Network Interface
-
- Please make sure that the correct network interface is specified in
- the DEVICE setting in the "/etc/initramfs-tools/initramfs.conf"
- file. If the setting is empty, the interface will be autodetected
- at boot time, which may not be correct. *If* the DEVICE setting is
- changed, it will be necessary to update the initrd image by running
- the command
-
- update-initramfs -k all -u
-
- The device can be overridden at boot time on the Linux kernel
- command line using the sixth colon-separated field of the "ip="
- option; for exact syntax, read the documentation in the file
- "/usr/share/doc/linux-doc-*/Documentation/filesystems/nfsroot.txt",
- available in the "linux-doc-*" package.
-
- Note that since this network interface is used in the initial RAM
- disk environment, the network interface *must* exist at that stage.
- Thus, the interface can *not* be a pseudo-interface such as "br0" or
- "tun0"; instead, a real interface (such as "eth0") must be used.
-
-* Adding a Client Password to the Server
-
- The server must be given a password to give back to the client on
- boot time. This password must be a one which can be used to unlock
- the root file system device. On the *client*, run this command:
-
- mandos-keygen --password
-
- It will prompt for a password and output a config file section.
- This output should be copied to the Mandos server and added to the
- file "/etc/mandos/clients.conf" there.
-
-* Testing that it Works (Without Rebooting)
-
- After the server has been started with this client's key added, it
- is possible to verify that the correct password will be received by
- this client by running the command, on the client:
-
- /usr/lib/mandos/plugins.d/mandos-client \
- --pubkey=/etc/keys/mandos/pubkey.txt \
- --seckey=/etc/keys/mandos/seckey.txt; echo
-
- This command should retrieve the password from the server, decrypt
- it, and output it to standard output. There it can be verified to
- be the correct password, before rebooting.
-
-* User-Supplied Plugins
-
- Any plugins found in "/etc/mandos/plugins.d" will override and add
- to the normal Mandos plugins. When adding or changing plugins, do
- not forget to update the initital RAM disk image:
-
- update-initramfs -k all -u
-
-* Do *NOT* Edit "/etc/crypttab"
-
- It is NOT necessary to edit "/etc/crypttab" to specify
- "/usr/lib/mandos/plugin-runner" as a keyscript for the root file
- system; if no keyscript is given for the root file system, the
- Mandos client will be the new default way for getting a password for
- the root file system when booting.
-
-* Emergency Escape
-
- If it ever should be necessary, the Mandos client can be temporarily
- prevented from running at startup by passing the parameter
- "mandos=off" to the kernel.
-
-* Non-local Connection (Not Using ZeroConf)
-
- If the "ip=" kernel command line option is used to specify a
- complete IP address and device name, as noted above, it then becomes
- possible to specify a specific IP address and port to connect to,
- instead of using ZeroConf. The syntax for doing this is
- "mandos=connect::".
-
- For very advanced users, it it possible to specify simply
- "mandos=connect" on the kernel command line to make the system only
- set up the network (using the data in the "ip=" option) and not pass
- any extra "--connect" options to mandos-client at boot. For this to
- work, "--options-for=mandos-client:--connect=:" needs
- to be manually added to the file "/etc/mandos/plugin-runner.conf".
-
- -- Teddy Hogeborn , Mon, 27 Sep 2010 19:53:21 +0200
=== removed file 'debian/mandos-client.dirs'
--- debian/mandos-client.dirs 2009-02-07 04:50:39 +0000
+++ debian/mandos-client.dirs 1970-01-01 00:00:00 +0000
@@ -1,5 +0,0 @@
-usr/share/man/man8
-usr/sbin
-usr/share/initramfs-tools/hooks
-usr/share/initramfs-tools/conf-hooks.d
-usr/share/initramfs-tools/scripts/init-premount
=== removed file 'debian/mandos-client.docs'
--- debian/mandos-client.docs 2008-10-18 11:17:22 +0000
+++ debian/mandos-client.docs 1970-01-01 00:00:00 +0000
@@ -1,3 +0,0 @@
-NEWS
-README
-TODO
=== removed file 'debian/mandos-client.links'
--- debian/mandos-client.links 2008-09-19 13:50:22 +0000
+++ debian/mandos-client.links 1970-01-01 00:00:00 +0000
@@ -1,1 +0,0 @@
-usr/share/man/man8/plugin-runner.8mandos.gz usr/share/man/man5/plugin-runner.conf.5mandos.gz
=== removed file 'debian/mandos-client.lintian-overrides'
--- debian/mandos-client.lintian-overrides 2009-01-18 06:41:57 +0000
+++ debian/mandos-client.lintian-overrides 1970-01-01 00:00:00 +0000
@@ -1,27 +0,0 @@
-# This directory contains secret client key files.
-#
-mandos-client binary: non-standard-dir-perm etc/keys/mandos/ 0700 != 0755
-
-# The directory /usr/lib/mandos/plugins.d contains setuid binaries
-# which are not meant to be run outside an initial RAM disk
-# environment (except for test purposes). It would be insecure to
-# allow anyone to run them.
-#
-mandos-client binary: non-standard-dir-perm usr/lib/mandos/plugins.d/ 0700 != 0755
-
-# These binaries must be setuid root, since they need root powers, but
-# are started by plugin-runner(8mandos), which runs all plugins as
-# user/group "_mandos". These binaries are not run in a running
-# system, but in an initial RAM disk environment. Here they are
-# protected from non-root access by the directory permissions, above.
-#
-mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/mandos-client 4755 root/root
-mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/askpass-fifo 4755 root/root
-mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/splashy 4755 root/root
-mandos-client binary: setuid-binary usr/lib/mandos/plugins.d/usplash 4755 root/root
-
-# The directory /etc/mandos/plugins.d can be used by local system
-# administrators to place plugins in, overriding and complementing
-# /usr/lib/mandos/plugins.d, and must be likewise protected.
-#
-mandos-client binary: non-standard-dir-perm etc/mandos/plugins.d/ 0700 != 0755
=== removed file 'debian/mandos-client.postinst'
--- debian/mandos-client.postinst 2009-05-24 23:36:15 +0000
+++ debian/mandos-client.postinst 1970-01-01 00:00:00 +0000
@@ -1,81 +0,0 @@
-#!/bin/sh -e
-# This script can be called in the following ways:
-#
-# After the package was installed:
-# configure
-#
-#
-# If prerm fails during upgrade or fails on failed upgrade:
-# abort-upgrade
-#
-# If prerm fails during deconfiguration of a package:
-# abort-deconfigure in-favour
-# removing
-#
-# If prerm fails during replacement due to conflict:
-# abort-remove in-favour
-
-# Update the initial RAM file system image
-update_initramfs()
-{
- if [ -x /usr/sbin/update-initramfs ]; then
- update-initramfs -u -k all
- fi
-
- if dpkg --compare-versions "$2" lt-nl "1.0.10-1"; then
- # Make old initrd.img files unreadable too, in case they were
- # created with mandos-client 1.0.8 or older.
- find /boot -maxdepth 1 -type f -name "initrd.img-*.bak" \
- -print0 | xargs --null --no-run-if-empty chmod o-r
- fi
-}
-
-# Add user and group
-add_mandos_user(){
- # Rename old "mandos" user and group
- if dpkg --compare-versions "$2" lt "1.0.3-1"; then
- case "`getent passwd mandos`" in
- *:Mandos\ password\ system,,,:/nonexistent:/bin/false)
- usermod --login _mandos mandos
- groupmod --new-name _mandos mandos
- return
- ;;
- esac
- fi
- # Create new user and group
- if ! getent passwd _mandos >/dev/null; then
- adduser --system --force-badname --quiet --home /nonexistent \
- --no-create-home --group --disabled-password \
- --gecos "Mandos password system" _mandos
- fi
-}
-
-# Create client key pair
-create_key(){
- if [ -r /etc/keys/mandos/pubkey.txt \
- -a -r /etc/keys/mandos/seckey.txt ]; then
- return 0
- fi
- if [ -x /usr/sbin/mandos-keygen ]; then
- mandos-keygen
- fi
-}
-
-case "$1" in
- configure)
- add_mandos_user "$@"
- create_key "$@"
- update_initramfs "$@"
- ;;
- abort-upgrade|abort-deconfigure|abort-remove)
- ;;
-
- *)
- echo "$0 called with unknown argument '$1'" 1>&2
- exit 1
- ;;
-esac
-
-#DEBHELPER#
-
-exit 0
=== removed file 'debian/mandos-client.postrm'
--- debian/mandos-client.postrm 2009-01-18 00:16:57 +0000
+++ debian/mandos-client.postrm 1970-01-01 00:00:00 +0000
@@ -1,60 +0,0 @@
-#!/bin/sh -e
-# This script can be called in the following ways:
-#
-# After the package was removed:
-# remove
-#
-# After the package was purged:
-# purge
-#
-# After the package was upgraded:
-# upgrade
-# if that fails:
-# failed-upgrade
-#
-#
-# After all of the packages files have been replaced:
-# disappear
-#
-#
-# If preinst fails during install:
-# abort-install
-#
-# If preinst fails during upgrade of removed package:
-# abort-install
-#
-# If preinst fails during upgrade:
-# abort-upgrade
-
-
-# Update the initial RAM file system image
-update_initramfs()
-{
- if [ -x /usr/sbin/update-initramfs ]; then
- update-initramfs -u -k all
- fi
-}
-
-case "$1" in
- remove)
- update_initramfs
- ;;
-
- purge)
- shred --remove /etc/keys/mandos/seckey.txt 2>/dev/null || :
- rm --force /etc/mandos/plugin-runner.conf \
- /etc/keys/mandos/pubkey.txt \
- /etc/keys/mandos/seckey.txt 2>/dev/null
- ;;
- upgrade|failed-upgrade|disappear|abort-install|abort-upgrade)
- ;;
-
- *)
- echo "$0 called with unknown argument '$1'" 1>&2
- exit 1
- ;;
-esac
-
-#DEBHELPER#
-
-exit 0
=== removed file 'debian/mandos.README.Debian'
--- debian/mandos.README.Debian 2009-09-08 06:28:20 +0000
+++ debian/mandos.README.Debian 1970-01-01 00:00:00 +0000
@@ -1,10 +0,0 @@
-The Mandos server is useless without at least one configured client in
-/etc/mandos/clients.conf. To create one, install the "mandos-client"
-package on a client computer, and run the command
-
- # mandos-keygen --password
-
-there to get a config file stanza. Append the output of that command
-to the file "/etc/mandos/clients.conf" on the Mandos server.
-
- -- Teddy Hogeborn , Tue, 8 Sep 2009 06:57:45 +0200
=== removed file 'debian/mandos.dirs'
--- debian/mandos.dirs 2010-09-15 17:33:14 +0000
+++ debian/mandos.dirs 1970-01-01 00:00:00 +0000
@@ -1,6 +0,0 @@
-usr/share/man/man5
-usr/share/man/man8
-etc/init.d
-etc/default
-etc/dbus-1/system.d
-usr/sbin
=== removed file 'debian/mandos.docs'
--- debian/mandos.docs 2010-09-12 03:00:40 +0000
+++ debian/mandos.docs 1970-01-01 00:00:00 +0000
@@ -1,4 +0,0 @@
-NEWS
-README
-TODO
-DBUS-API
=== removed file 'debian/mandos.lintian-overrides'
--- debian/mandos.lintian-overrides 2008-10-01 15:29:01 +0000
+++ debian/mandos.lintian-overrides 1970-01-01 00:00:00 +0000
@@ -1,4 +0,0 @@
-# This config file will normally have encrypted secret client keys in
-# it, so it must be kept unreadable for non-root users.
-#
-mandos binary: non-standard-file-perm etc/mandos/clients.conf 0600 != 0644
=== removed file 'debian/mandos.postinst'
--- debian/mandos.postinst 2009-05-24 23:28:04 +0000
+++ debian/mandos.postinst 1970-01-01 00:00:00 +0000
@@ -1,49 +0,0 @@
-#!/bin/sh -e
-# This script can be called in the following ways:
-#
-# After the package was installed:
-# configure
-#
-#
-# If prerm fails during upgrade or fails on failed upgrade:
-# abort-upgrade
-#
-# If prerm fails during deconfiguration of a package:
-# abort-deconfigure in-favour
-# removing
-#
-# If prerm fails during replacement due to conflict:
-# abort-remove in-favour
-
-case "$1" in
- configure)
- # Rename old "mandos" user and group
- if dpkg --compare-versions "$2" lt "1.0.3-1"; then
- case "`getent passwd mandos`" in
- *:Mandos\ password\ system,,,:/nonexistent:/bin/false)
- usermod --login _mandos mandos
- groupmod --new-name _mandos mandos
- ;;
- esac
- fi
- # Create new user and group
- if ! getent passwd _mandos >/dev/null; then
- adduser --system --force-badname --quiet \
- --home /nonexistent --no-create-home --group \
- --disabled-password --gecos "Mandos password system" \
- _mandos
- fi
- ;;
-
- abort-upgrade|abort-deconfigure|abort-remove)
- ;;
-
- *)
- echo "$0 called with unknown argument '$1'" 1>&2
- exit 1
- ;;
-esac
-
-#DEBHELPER#
-
-exit 0
=== removed file 'debian/mandos.prerm'
--- debian/mandos.prerm 2009-01-18 00:16:57 +0000
+++ debian/mandos.prerm 1970-01-01 00:00:00 +0000
@@ -1,38 +0,0 @@
-#! /bin/sh
-# prerm script for mandos
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-# * 'remove'
-# * 'upgrade'
-# * 'failed-upgrade'
-# * 'remove' 'in-favour'
-# * 'deconfigure' 'in-favour'
-# 'removing'
-#
-# for details, see /usr/share/doc/packaging-manual/
-
-case "$1" in
- remove|deconfigure)
- if [ -x /etc/init.d/mandos ]; then
- if [ -x /usr/sbin/invoke-rc.d ]; then
- invoke-rc.d mandos stop
- else
- /etc/init.d/mandos stop
- fi
- fi
- ;;
- upgrade|failed-upgrade)
- ;;
- *)
- echo "prerm called with unknown argument '$1'" >&2
- exit 0
- ;;
-esac
-
-#DEBHELPER#
-
-exit 0
=== removed directory 'debian/po'
=== removed file 'debian/rules'
--- debian/rules 2010-09-09 18:16:14 +0000
+++ debian/rules 1970-01-01 00:00:00 +0000
@@ -1,106 +0,0 @@
-#!/usr/bin/make -f
-# Sample debian/rules that uses debhelper.
-#
-# This file was originally written by Joey Hess and Craig Small.
-# As a special exception, when this file is copied by dh-make into a
-# dh-make output file, you may use that output file without restriction.
-# This special exception was added by Craig Small in version 0.37 of dh-make.
-#
-# Modified to make a template file for a multi-binary package with separated
-# build-arch and build-indep targets by Bill Allombert 2001
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-# This has to be exported to make some magic below work.
-export DH_OPTIONS
-
-# -pie was broken briefly on the mips and mipsel architectures, see
-#
-BINUTILS_V := $(shell dpkg-query --showformat='$${Version}' \
- --show binutils)
-ifeq (yes,$(shell dpkg --compare-versions $(BINUTILS_V) lt 2.20-3 \
- && dpkg --compare-versions $(BINUTILS_V) ge 2.19.1-1 \
- && echo yes))
- ifneq (,$(strip $(findstring :$(DEB_HOST_ARCH):,:mips:mipsel:) \
- $(findstring :$(DEB_BUILD_ARCH):,:mips:mipsel:)))
- BROKEN_PIE := yes
- export BROKEN_PIE
- endif
-endif
-
-configure: configure-stamp
-configure-stamp:
- dh_testdir
- touch configure-stamp
-
-build: build-arch build-indep
-
-build-arch: build-arch-stamp
-build-arch-stamp: configure-stamp
- dh_auto_build -- all doc
- touch $@
-
-build-indep: build-indep-stamp
-build-indep-stamp: configure-stamp
- dh_auto_build -- doc
- touch $@
-
-clean:
- dh_testdir
- dh_testroot
- rm -f build-arch-stamp build-indep-stamp configure-stamp
- dh_auto_clean
- dh_clean
-
-install: install-indep install-arch
-install-indep:
- dh_testdir
- dh_testroot
- dh_prep
- dh_installdirs --indep
- $(MAKE) DESTDIR=$(CURDIR)/debian/mandos install-server
- dh_lintian
- dh_installinit --onlyscripts \
- --update-rcd-params="defaults 25 15"
- dh_install --indep
-
-install-arch:
- dh_testdir
- dh_testroot
- dh_prep
- dh_installdirs --same-arch
- $(MAKE) DESTDIR=$(CURDIR)/debian/mandos-client install-client-nokey
- dh_lintian
- dh_install --same-arch
-
-binary-common:
- dh_testdir
- dh_testroot
- dh_installchangelogs
- dh_installdocs
- dh_link
- dh_strip
- dh_compress
- dh_fixperms --exclude etc/keys/mandos \
- --exclude etc/mandos/clients.conf \
- --exclude etc/mandos/plugins.d \
- --exclude usr/lib/mandos/plugins.d
- dh_installdeb
- dh_shlibdeps
- dh_gencontrol
- dh_md5sums
- dh_builddeb
-
-# Build architecture independant packages using the common target.
-binary-indep: build-indep install-indep
- $(MAKE) -f debian/rules DH_OPTIONS=--indep binary-common
-
-# Build architecture dependant packages using the common target.
-binary-arch: build-arch install-arch
- $(MAKE) -f debian/rules DH_OPTIONS=--same-arch binary-common
-
-binary: binary-arch binary-indep
-
-.PHONY: build clean binary-indep binary-arch binary install \
- install-indep install-arch configure
=== removed file 'debian/watch'
--- debian/watch 2010-09-15 17:17:46 +0000
+++ debian/watch 1970-01-01 00:00:00 +0000
@@ -1,2 +0,0 @@
-version=3
-ftp://ftp.fukt.bsnet.se/pub/mandos/mandos[-_]([^\s]+?)(?:\.orig)?\.tar\.(?:gz|bz2|7z|xz)
=== removed file 'default-mandos'
--- default-mandos 2008-09-17 00:34:09 +0000
+++ default-mandos 1970-01-01 00:00:00 +0000
@@ -1,7 +0,0 @@
-# Directory where configuration files are located. Default is
-# "/etc/mandos".
-#
-#CONFIGDIR=/etc/mandos
-
-# Additional options that are passed to the Daemon.
-DAEMON_ARGS=""
=== removed file 'init.d-mandos'
--- init.d-mandos 2009-09-16 23:28:39 +0000
+++ init.d-mandos 1970-01-01 00:00:00 +0000
@@ -1,159 +0,0 @@
-#! /bin/sh
-### BEGIN INIT INFO
-# Provides: mandos
-# Required-Start: $remote_fs $syslog avahi
-# Required-Stop: $remote_fs $syslog avahi
-# Default-Start: 2 3 4 5
-# Default-Stop: 0 1 6
-# Short-Description: Mandos server
-# Description: Gives encrypted passwords to Mandos clients
-### END INIT INFO
-
-# Author: Teddy Hogeborn
-# Author: Björn Påhlsson
-#
-# Please remove the "Author" lines above and replace them
-# with your own name if you copy and modify this script.
-
-# Do NOT "set -e"
-
-# PATH should only include /usr/* if it runs after the mountnfs.sh script
-PATH=/sbin:/usr/sbin:/bin:/usr/bin
-DESC="Mandos root file system password server"
-NAME=mandos
-DAEMON=/usr/sbin/$NAME
-DAEMON_ARGS=""
-PIDFILE=/var/run/$NAME.pid
-SCRIPTNAME=/etc/init.d/$NAME
-
-# Exit if the package is not installed
-[ -x "$DAEMON" ] || exit 0
-
-# Read configuration variable file if it is present
-[ -r /etc/default/$NAME ] && . /etc/default/$NAME
-
-if [ -n "$CONFIGDIR" ]; then
- DAEMON_ARGS="$DAEMON_ARGS --configdir $CONFIGDIR"
-fi
-
-# Load the VERBOSE setting and other rcS variables
-. /lib/init/vars.sh
-
-# Define LSB log_* functions.
-# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
-. /lib/lsb/init-functions
-
-#
-# Function that starts the daemon/service
-#
-do_start()
-{
- # Return
- # 0 if daemon has been started
- # 1 if daemon was already running
- # 2 if daemon could not be started
- start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
- || return 1
- start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \
- $DAEMON_ARGS \
- || return 2
- # Add code here, if necessary, that waits for the process to be ready
- # to handle requests from services started subsequently which depend
- # on this one. As a last resort, sleep for some time.
-}
-
-#
-# Function that stops the daemon/service
-#
-do_stop()
-{
- # Return
- # 0 if daemon has been stopped
- # 1 if daemon was already stopped
- # 2 if daemon could not be stopped
- # other if a failure occurred
- start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
- RETVAL="$?"
- [ "$RETVAL" = 2 ] && return 2
- # Wait for children to finish too if this is a daemon that forks
- # and if the daemon is only ever run from this initscript.
- # If the above conditions are not satisfied then add some other code
- # that waits for the process to drop all resources that could be
- # needed by services started subsequently. A last resort is to
- # sleep for some time.
- start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
- [ "$?" = 2 ] && return 2
- # Many daemons don't delete their pidfiles when they exit.
- rm -f $PIDFILE
- return "$RETVAL"
-}
-
-#
-# Function that sends a SIGHUP to the daemon/service
-#
-do_reload() {
- #
- # If the daemon can reload its configuration without
- # restarting (for example, when it is sent a SIGHUP),
- # then implement that here.
- #
- start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
- return 0
-}
-
-case "$1" in
- start)
- [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
- do_start
- case "$?" in
- 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
- 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
- esac
- ;;
- stop)
- [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
- do_stop
- case "$?" in
- 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
- 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
- esac
- ;;
- #reload|force-reload)
- #
- # If do_reload() is not implemented then leave this commented out
- # and leave 'force-reload' as an alias for 'restart'.
- #
- #log_daemon_msg "Reloading $DESC" "$NAME"
- #do_reload
- #log_end_msg $?
- #;;
- restart|force-reload)
- #
- # If the "reload" option is implemented then remove the
- # 'force-reload' alias
- #
- log_daemon_msg "Restarting $DESC" "$NAME"
- do_stop
- case "$?" in
- 0|1)
- do_start
- case "$?" in
- 0) log_end_msg 0 ;;
- 1) log_end_msg 1 ;; # Old process is still running
- *) log_end_msg 1 ;; # Failed to start
- esac
- ;;
- *)
- # Failed to stop
- log_end_msg 1
- ;;
- esac
- ;;
- *)
- #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
- echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
- exit 3
- ;;
-esac
-
-:
=== removed file 'initramfs-tools-hook'
--- initramfs-tools-hook 2010-09-09 18:16:14 +0000
+++ initramfs-tools-hook 1970-01-01 00:00:00 +0000
@@ -1,175 +0,0 @@
-#!/bin/sh
-
-# This script will be run by 'mkinitramfs' when it creates the image.
-# Its job is to decide which files to install, then install them into
-# the staging area, where the initramfs is being created. This
-# happens when a new 'linux-image' package is installed, or when the
-# administrator runs 'update-initramfs' by hand to update an initramfs
-# image.
-
-# The environment contains at least:
-#
-# DESTDIR -- The staging directory where the image is being built.
-
-# No initramfs pre-requirements
-PREREQ="cryptroot"
-
-prereqs()
-{
- echo "$PREREQ"
-}
-
-case $1 in
-# get pre-requisites
-prereqs)
- prereqs
- exit 0
- ;;
-esac
-
-. /usr/share/initramfs-tools/hook-functions
-
-for d in /usr /usr/local; do
- if [ -d "$d"/lib/mandos ]; then
- prefix="$d"
- break
- fi
-done
-if [ -z "$prefix" ]; then
- # Mandos not found
- exit 1
-fi
-
-for d in /etc/keys/mandos /etc/mandos/keys; do
- if [ -d "$d" ]; then
- keydir="$d"
- break
- fi
-done
-if [ -z "$keydir" ]; then
- # Mandos key directory not found
- exit 1
-fi
-
-set `{ getent passwd _mandos \
- || getent passwd nobody \
- || echo ::65534:65534:::; } \
- | cut --delimiter=: --fields=3,4 --only-delimited \
- --output-delimiter=" "`
-mandos_user="$1"
-mandos_group="$2"
-
-# The Mandos network client uses the network
-auto_add_modules net
-# The Mandos network client uses IPv6
-force_load ipv6
-
-# These are directories inside the initrd
-CONFDIR="/conf/conf.d/mandos"
-MANDOSDIR="/lib/mandos"
-PLUGINDIR="${MANDOSDIR}/plugins.d"
-
-# Make directories
-install --directory --mode=u=rwx,go=rx "${DESTDIR}${CONFDIR}" \
- "${DESTDIR}${MANDOSDIR}"
-install --owner=${mandos_user} --group=${mandos_group} --directory \
- --mode=u=rwx "${DESTDIR}${PLUGINDIR}"
-
-# Copy the Mandos plugin runner
-copy_exec "$prefix"/lib/mandos/plugin-runner "${MANDOSDIR}"
-
-# Copy the plugins
-
-# Copy the packaged plugins
-for file in "$prefix"/lib/mandos/plugins.d/*; do
- base="`basename \"$file\"`"
- # Is this plugin overridden?
- if [ -e "/etc/mandos/plugins.d/$base" ]; then
- continue
- fi
- case "$base" in
- *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert)
- : ;;
- "*") echo "W: Mandos client plugin directory is empty." >&2 ;;
- *) copy_exec "$file" "${PLUGINDIR}" ;;
- esac
-done
-
-# Copy any user-supplied plugins
-for file in /etc/mandos/plugins.d/*; do
- base="`basename \"$file\"`"
- case "$base" in
- *~|.*|\#*\#|*.dpkg-old|*.dpkg-bak|*.dpkg-new|*.dpkg-divert)
- : ;;
- "*") : ;;
- *) copy_exec "$file" "${PLUGINDIR}" ;;
- esac
-done
-
-# GPGME needs /usr/bin/gpg
-if [ ! -e "${DESTDIR}/usr/bin/gpg" \
- -a -n "`ls \"${DESTDIR}\"/usr/lib/libgpgme.so* \
- 2>/dev/null`" ]; then
- copy_exec /usr/bin/gpg
-fi
-
-# Config files
-for file in /etc/mandos/plugin-runner.conf; do
- if [ -d "$file" ]; then
- continue
- fi
- cp --archive --sparse=always "$file" "${DESTDIR}${CONFDIR}"
-done
-
-if [ ${mandos_user} != 65534 ]; then
- sed --in-place --expression="1i--userid=${mandos_user}" \
- "${DESTDIR}${CONFDIR}/plugin-runner.conf"
-fi
-
-if [ ${mandos_group} != 65534 ]; then
- sed --in-place --expression="1i--groupid=${mandos_group}" \
- "${DESTDIR}${CONFDIR}/plugin-runner.conf"
-fi
-
-# Key files
-for file in "$keydir"/*; do
- if [ -d "$file" ]; then
- continue
- fi
- cp --archive --sparse=always "$file" "${DESTDIR}${CONFDIR}"
- chown ${mandos_user}:${mandos_group} \
- "${DESTDIR}${CONFDIR}/`basename \"$file\"`"
-done
-
-# /lib/mandos/plugin-runner will drop priviliges, but needs access to
-# its plugin directory and its config file. However, since almost all
-# files in initrd have been created with umask 027, this opening of
-# permissions is needed.
-#
-# (The umask is not really intended to affect the files inside the
-# initrd; it is intended to affect the initrd.img file itself, since
-# it now contains secret key files. There is, however, no other way
-# to set the permission of the initrd.img file without a race
-# condition. This umask is set by "initramfs-tools-hook-conf",
-# installed as "/usr/share/initramfs-tools/conf-hooks.d/mandos".)
-#
-for full in "${MANDOSDIR}" "${CONFDIR}"; do
- while [ "$full" != "/" ]; do
- chmod a+rX "${DESTDIR}$full"
- full="`dirname \"$full\"`"
- done
-done
-
-# Reset some other things to sane permissions which we have
-# inadvertently affected with our umask setting.
-for dir in / /bin /etc /keyscripts /sbin /scripts /usr /usr/bin; do
- if [ -d "${DESTDIR}$dir" ]; then
- chmod a+rX "${DESTDIR}$dir"
- fi
-done
-for dir in "${DESTDIR}"/lib* "${DESTDIR}"/usr/lib*; do
- if [ -d "$dir" ]; then
- find "$dir" \! -perm -u+rw,g+r -prune -or -print0 \
- | xargs --null --no-run-if-empty chmod a+rX
- fi
-done
=== removed file 'initramfs-tools-hook-conf'
--- initramfs-tools-hook-conf 2009-05-17 00:50:09 +0000
+++ initramfs-tools-hook-conf 1970-01-01 00:00:00 +0000
@@ -1,13 +0,0 @@
-# -*- shell-script -*-
-
-# if mkinitramfs is started by mkinitramfs-kpkg, mkinitramfs-kpkg has
-# already touched the initrd file with umask 022 before we had a
-# chance to affect it. We cannot allow a readable initrd file,
-# therefore we must fix this now.
-if [ -e "${outfile}" ] \
- && [ `stat --format=%s "${outfile}"` -eq 0 ]; then
- rm "${outfile}"
- (umask 027; touch "${outfile}")
-fi
-
-UMASK=027
=== removed file 'initramfs-tools-script'
--- initramfs-tools-script 2009-09-16 23:28:39 +0000
+++ initramfs-tools-script 1970-01-01 00:00:00 +0000
@@ -1,151 +0,0 @@
-#!/bin/sh -e
-#
-# This script will run in the initrd environment at boot and edit
-# /conf/conf.d/cryptroot to set /lib/mandos/plugin-runner as keyscript
-# when no other keyscript is set, before cryptsetup.
-#
-
-# This script should be installed as
-# "/usr/share/initramfs-tools/scripts/init-premount/mandos" which will
-# eventually be "/scripts/init-premount/mandos" in the initrd.img
-# file.
-
-PREREQ="udev"
-prereqs()
-{
- echo "$PREREQ"
-}
-
-case $1 in
-prereqs)
- prereqs
- exit 0
- ;;
-esac
-
-. /scripts/functions
-
-for param in `cat /proc/cmdline`; do
- case "$param" in
- ip=*) IPOPTS="${param#ip=}" ;;
- mandos=*)
- # Split option line on commas
- old_ifs="$IFS"
- IFS="$IFS,"
- for mpar in ${param#mandos=}; do
- IFS="$old_ifs"
- case "$mpar" in
- off) exit 0 ;;
- connect) connect="" ;;
- connect:*) connect="${mpar#connect:}" ;;
- *) log_warning_msg "$0: Bad option ${mpar}" ;;
- esac
- done
- unset mpar
- IFS="$old_ifs"
- unset old_ifs
- ;;
- esac
-done
-unset param
-
-chmod a=rwxt /tmp
-
-test -r /conf/conf.d/cryptroot
-test -w /conf/conf.d
-
-# Get DEVICE from /conf/initramfs.conf and other files
-. /conf/initramfs.conf
-for conf in /conf/conf.d/*; do
- [ -f ${conf} ] && . ${conf}
-done
-if [ -e /conf/param.conf ]; then
- . /conf/param.conf
-fi
-
-# Override DEVICE from sixth field of ip= kernel option, if passed
-case "$IPOPTS" in
- *:*:*:*:*:*) # At least six fields
- # Remove the first five fields
- device="${IPOPTS#*:*:*:*:*:}"
- # Remove all fields except the first one
- DEVICE="${device%%:*}"
- ;;
-esac
-
-# Add device setting (if any) to plugin-runner.conf
-if [ "${DEVICE+set}" = set ]; then
- # Did we get the device from an ip= option?
- if [ "${device+set}" = set ]; then
- # Let ip= option override local config; append:
- cat <<-EOF >>/conf/conf.d/mandos/plugin-runner.conf
-
- --options-for=mandos-client:--interface=${DEVICE}
-EOF
- else
- # Prepend device setting so any later options would override:
- sed -i -e \
- '1i--options-for=mandos-client:--interface='"${DEVICE}" \
- /conf/conf.d/mandos/plugin-runner.conf
- fi
-fi
-unset device
-
-# If we are connecting directly, run "configure_networking" (from
-# /scripts/functions); it needs IPOPTS and DEVICE
-if [ "${connect+set}" = set ]; then
- configure_networking
- if [ -n "$connect" ]; then
- cat <<-EOF >>/conf/conf.d/mandos/plugin-runner.conf
-
- --options-for=mandos-client:--connect=${connect}
-EOF
- fi
-fi
-
-# Do not replace cryptroot file unless we need to.
-replace_cryptroot=no
-
-# Our keyscript
-mandos=/lib/mandos/plugin-runner
-
-# parse /conf/conf.d/cryptroot. Format:
-# target=sda2_crypt,source=/dev/sda2,key=none,keyscript=/foo/bar/baz
-exec 3>/conf/conf.d/cryptroot.mandos
-while read options; do
- newopts=""
- # Split option line on commas
- old_ifs="$IFS"
- IFS="$IFS,"
- for opt in $options; do
- # Find the keyscript option, if any
- case "$opt" in
- keyscript=*)
- keyscript="${opt#keyscript=}"
- newopts="$newopts,$opt"
- ;;
- "") : ;;
- *)
- newopts="$newopts,$opt"
- ;;
- esac
- done
- IFS="$old_ifs"
- unset old_ifs
- # If there was no keyscript option, add one.
- if [ -z "$keyscript" ]; then
- replace_cryptroot=yes
- newopts="$newopts,keyscript=$mandos"
- fi
- newopts="${newopts#,}"
- echo "$newopts" >&3
-done < /conf/conf.d/cryptroot
-exec 3>&-
-
-# If we need to, replace the old cryptroot file with the new file.
-if [ "$replace_cryptroot" = yes ]; then
- mv /conf/conf.d/cryptroot /conf/conf.d/cryptroot.mandos-old
- mv /conf/conf.d/cryptroot.mandos /conf/conf.d/cryptroot
-else
- rm /conf/conf.d/cryptroot.mandos
-fi
=== removed file 'legalnotice.xml'
--- legalnotice.xml 2008-09-06 17:24:58 +0000
+++ legalnotice.xml 1970-01-01 00:00:00 +0000
@@ -1,27 +0,0 @@
-
-
-
-
- This manual page is free software: you can redistribute it and/or
- modify it under the terms of the GNU General
- Public License as published by the Free Software Foundation,
- either version 3 of the License, or (at your option) any later
- version.
-
-
-
- This manual page is distributed in the hope that it will be
- useful, but WITHOUT ANY WARRANTY; without even the implied
- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU General Public License for more
- details.
-
-
-
- You should have received a copy of the GNU
- General Public License along with this program. If not, see
- http://www.gnu.org/licenses/.
-
-
=== modified file 'mandos'
--- mandos 2010-09-28 18:57:31 +0000
+++ mandos 2008-08-08 01:17:17 +0000
@@ -6,13 +6,12 @@
# This program is partly derived from an example program for an Avahi
# service publisher, downloaded from
# . This includes the
-# methods "add", "remove", "server_state_changed",
-# "entry_group_state_changed", "cleanup", and "activate" in the
-# "AvahiService" class, and some lines in "main".
+# methods "add" and "remove" in the "AvahiService" class, the
+# "server_state_changed" and "entry_group_state_changed" functions,
+# and some lines in "main".
#
# Everything else is
-# Copyright © 2008-2010 Teddy Hogeborn
-# Copyright © 2008-2010 Björn Påhlsson
+# Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -25,17 +24,17 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see
-# .
+# along with this program. If not, see .
#
# Contact the authors at .
#
-from __future__ import division, with_statement, absolute_import
+from __future__ import division
-import SocketServer as socketserver
+import SocketServer
import socket
-import optparse
+import select
+from optparse import OptionParser
import datetime
import errno
import gnutls.crypto
@@ -44,67 +43,39 @@
import gnutls.library.functions
import gnutls.library.constants
import gnutls.library.types
-import ConfigParser as configparser
+import ConfigParser
import sys
import re
import os
import signal
+from sets import Set
import subprocess
import atexit
import stat
import logging
import logging.handlers
-import pwd
-import contextlib
-import struct
-import fcntl
-import functools
-import cPickle as pickle
-import multiprocessing
import dbus
-import dbus.service
import gobject
import avahi
from dbus.mainloop.glib import DBusGMainLoop
import ctypes
-import ctypes.util
-import xml.dom.minidom
-import inspect
-
-try:
- SO_BINDTODEVICE = socket.SO_BINDTODEVICE
-except AttributeError:
- try:
- from IN import SO_BINDTODEVICE
- except ImportError:
- SO_BINDTODEVICE = None
-
-
-version = "1.2"
-
-#logger = logging.getLogger(u'mandos')
-logger = logging.Logger(u'mandos')
-syslogger = (logging.handlers.SysLogHandler
- (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
- address = "/dev/log"))
-syslogger.setFormatter(logging.Formatter
- (u'Mandos [%(process)d]: %(levelname)s:'
- u' %(message)s'))
+
+
+logger = logging.Logger('mandos')
+syslogger = logging.handlers.SysLogHandler\
+ (facility = logging.handlers.SysLogHandler.LOG_DAEMON)
+syslogger.setFormatter(logging.Formatter\
+ ('%(levelname)s: %(message)s'))
logger.addHandler(syslogger)
+del syslogger
-console = logging.StreamHandler()
-console.setFormatter(logging.Formatter(u'%(name)s [%(process)d]:'
- u' %(levelname)s:'
- u' %(message)s'))
-logger.addHandler(console)
class AvahiError(Exception):
- def __init__(self, value, *args, **kwargs):
+ def __init__(self, value):
self.value = value
- super(AvahiError, self).__init__(value, *args, **kwargs)
- def __unicode__(self):
- return unicode(repr(self.value))
+ def __str__(self):
+ return repr(self.value)
class AvahiServiceError(AvahiError):
pass
@@ -115,12 +86,11 @@
class AvahiService(object):
"""An Avahi (Zeroconf) service.
-
Attributes:
interface: integer; avahi.IF_UNSPEC or an interface index.
Used to optionally bind to the specified interface.
- name: string; Example: u'Mandos'
- type: string; Example: u'_mandos._tcp'.
+ name: string; Example: 'Mandos'
+ type: string; Example: '_mandos._tcp'.
See
port: integer; what port to announce
TXT: list of strings; TXT record for the service
@@ -129,307 +99,207 @@
max_renames: integer; maximum number of renames
rename_count: integer; counter so we only rename after collisions
a sensible number of times
- group: D-Bus Entry Group
- server: D-Bus Server
- bus: dbus.SystemBus()
"""
def __init__(self, interface = avahi.IF_UNSPEC, name = None,
- servicetype = None, port = None, TXT = None,
- domain = u"", host = u"", max_renames = 32768,
- protocol = avahi.PROTO_UNSPEC, bus = None):
+ type = None, port = None, TXT = None, domain = "",
+ host = "", max_renames = 12):
self.interface = interface
self.name = name
- self.type = servicetype
+ self.type = type
self.port = port
- self.TXT = TXT if TXT is not None else []
+ if TXT is None:
+ self.TXT = []
+ else:
+ self.TXT = TXT
self.domain = domain
self.host = host
self.rename_count = 0
- self.max_renames = max_renames
- self.protocol = protocol
- self.group = None # our entry group
- self.server = None
- self.bus = bus
def rename(self):
"""Derived from the Avahi example code"""
if self.rename_count >= self.max_renames:
- logger.critical(u"No suitable Zeroconf service name found"
- u" after %i retries, exiting.",
- self.rename_count)
- raise AvahiServiceError(u"Too many renames")
- self.name = unicode(self.server.GetAlternativeServiceName(self.name))
- logger.info(u"Changing Zeroconf service name to %r ...",
- self.name)
- syslogger.setFormatter(logging.Formatter
- (u'Mandos (%s) [%%(process)d]:'
- u' %%(levelname)s: %%(message)s'
- % self.name))
+ logger.critical(u"No suitable service name found after %i"
+ u" retries, exiting.", rename_count)
+ raise AvahiServiceError("Too many renames")
+ name = server.GetAlternativeServiceName(name)
+ logger.error(u"Changing name to %r ...", name)
self.remove()
- try:
- self.add()
- except dbus.exceptions.DBusException, error:
- logger.critical(u"DBusException: %s", error)
- self.cleanup()
- os._exit(1)
+ self.add()
self.rename_count += 1
def remove(self):
"""Derived from the Avahi example code"""
- if self.group is not None:
- self.group.Reset()
+ if group is not None:
+ group.Reset()
def add(self):
"""Derived from the Avahi example code"""
- if self.group is None:
- self.group = dbus.Interface(
- self.bus.get_object(avahi.DBUS_NAME,
- self.server.EntryGroupNew()),
- avahi.DBUS_INTERFACE_ENTRY_GROUP)
- self.group.connect_to_signal('StateChanged',
- self
- .entry_group_state_changed)
- logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
- self.name, self.type)
- self.group.AddService(
- self.interface,
- self.protocol,
- dbus.UInt32(0), # flags
- self.name, self.type,
- self.domain, self.host,
- dbus.UInt16(self.port),
- avahi.string_array_to_txt_array(self.TXT))
- self.group.Commit()
- def entry_group_state_changed(self, state, error):
- """Derived from the Avahi example code"""
- logger.debug(u"Avahi entry group state change: %i", state)
-
- if state == avahi.ENTRY_GROUP_ESTABLISHED:
- logger.debug(u"Zeroconf service established.")
- elif state == avahi.ENTRY_GROUP_COLLISION:
- logger.warning(u"Zeroconf service name collision.")
- self.rename()
- elif state == avahi.ENTRY_GROUP_FAILURE:
- logger.critical(u"Avahi: Error in group state changed %s",
- unicode(error))
- raise AvahiGroupError(u"State changed: %s"
- % unicode(error))
- def cleanup(self):
- """Derived from the Avahi example code"""
- if self.group is not None:
- self.group.Free()
- self.group = None
- def server_state_changed(self, state):
- """Derived from the Avahi example code"""
- logger.debug(u"Avahi server state change: %i", state)
- if state == avahi.SERVER_COLLISION:
- logger.error(u"Zeroconf server name collision")
- self.remove()
- elif state == avahi.SERVER_RUNNING:
- self.add()
- def activate(self):
- """Derived from the Avahi example code"""
- if self.server is None:
- self.server = dbus.Interface(
- self.bus.get_object(avahi.DBUS_NAME,
- avahi.DBUS_PATH_SERVER),
- avahi.DBUS_INTERFACE_SERVER)
- self.server.connect_to_signal(u"StateChanged",
- self.server_state_changed)
- self.server_state_changed(self.server.GetState())
+ global group
+ if group is None:
+ group = dbus.Interface\
+ (bus.get_object(avahi.DBUS_NAME,
+ server.EntryGroupNew()),
+ avahi.DBUS_INTERFACE_ENTRY_GROUP)
+ group.connect_to_signal('StateChanged',
+ entry_group_state_changed)
+ logger.debug(u"Adding service '%s' of type '%s' ...",
+ service.name, service.type)
+ group.AddService(
+ self.interface, # interface
+ avahi.PROTO_INET6, # protocol
+ dbus.UInt32(0), # flags
+ self.name, self.type,
+ self.domain, self.host,
+ dbus.UInt16(self.port),
+ avahi.string_array_to_txt_array(self.TXT))
+ group.Commit()
+
+# From the Avahi example code:
+group = None # our entry group
+# End of Avahi example code
class Client(object):
"""A representation of a client host served by this server.
-
Attributes:
- _approved: bool(); 'None' if not yet approved/disapproved
- approval_delay: datetime.timedelta(); Time to wait for approval
- approval_duration: datetime.timedelta(); Duration of one approval
- checker: subprocess.Popen(); a running checker process used
- to see if the client lives.
- 'None' if no process is running.
- checker_callback_tag: a gobject event source tag, or None
- checker_command: string; External command which is run to check
- if client lives. %() expansions are done at
+ name: string; from the config file, used in log messages
+ fingerprint: string (40 or 32 hexadecimal digits); used to
+ uniquely identify the client
+ secret: bytestring; sent verbatim (over TLS) to client
+ fqdn: string (FQDN); available for use by the checker command
+ created: datetime.datetime(); object creation, not client host
+ last_checked_ok: datetime.datetime() or None if not yet checked OK
+ timeout: datetime.timedelta(); How long from last_checked_ok
+ until this client is invalid
+ interval: datetime.timedelta(); How often to start a new checker
+ stop_hook: If set, called by stop() as stop_hook(self)
+ checker: subprocess.Popen(); a running checker process used
+ to see if the client lives.
+ 'None' if no process is running.
+ checker_initiator_tag: a gobject event source tag, or None
+ stop_initiator_tag: - '' -
+ checker_callback_tag: - '' -
+ checker_command: string; External command which is run to check if
+ client lives. %() expansions are done at
runtime with vars(self) as dict, so that for
instance %(name)s can be used in the command.
- checker_initiator_tag: a gobject event source tag, or None
- created: datetime.datetime(); (UTC) object creation
- current_checker_command: string; current running checker_command
- disable_hook: If set, called by disable() as disable_hook(self)
- disable_initiator_tag: a gobject event source tag, or None
- enabled: bool()
- fingerprint: string (40 or 32 hexadecimal digits); used to
- uniquely identify the client
- host: string; available for use by the checker command
- interval: datetime.timedelta(); How often to start a new checker
- last_approval_request: datetime.datetime(); (UTC) or None
- last_checked_ok: datetime.datetime(); (UTC) or None
- last_enabled: datetime.datetime(); (UTC)
- name: string; from the config file, used in log messages and
- D-Bus identifiers
- secret: bytestring; sent verbatim (over TLS) to client
- timeout: datetime.timedelta(); How long from last_checked_ok
- until this client is disabled
- runtime_expansions: Allowed attributes for runtime expansion.
+ Private attibutes:
+ _timeout: Real variable for 'timeout'
+ _interval: Real variable for 'interval'
+ _timeout_milliseconds: Used when calling gobject.timeout_add()
+ _interval_milliseconds: - '' -
"""
-
- runtime_expansions = (u"approval_delay", u"approval_duration",
- u"created", u"enabled", u"fingerprint",
- u"host", u"interval", u"last_checked_ok",
- u"last_enabled", u"name", u"timeout")
-
- @staticmethod
- def _timedelta_to_milliseconds(td):
- "Convert a datetime.timedelta() to milliseconds"
- return ((td.days * 24 * 60 * 60 * 1000)
- + (td.seconds * 1000)
- + (td.microseconds // 1000))
-
- def timeout_milliseconds(self):
- "Return the 'timeout' attribute in milliseconds"
- return self._timedelta_to_milliseconds(self.timeout)
-
- def interval_milliseconds(self):
- "Return the 'interval' attribute in milliseconds"
- return self._timedelta_to_milliseconds(self.interval)
-
- def approval_delay_milliseconds(self):
- return self._timedelta_to_milliseconds(self.approval_delay)
-
- def __init__(self, name = None, disable_hook=None, config=None):
+ def _set_timeout(self, timeout):
+ "Setter function for 'timeout' attribute"
+ self._timeout = timeout
+ self._timeout_milliseconds = ((self.timeout.days
+ * 24 * 60 * 60 * 1000)
+ + (self.timeout.seconds * 1000)
+ + (self.timeout.microseconds
+ // 1000))
+ timeout = property(lambda self: self._timeout,
+ _set_timeout)
+ del _set_timeout
+ def _set_interval(self, interval):
+ "Setter function for 'interval' attribute"
+ self._interval = interval
+ self._interval_milliseconds = ((self.interval.days
+ * 24 * 60 * 60 * 1000)
+ + (self.interval.seconds
+ * 1000)
+ + (self.interval.microseconds
+ // 1000))
+ interval = property(lambda self: self._interval,
+ _set_interval)
+ del _set_interval
+ def __init__(self, name = None, stop_hook=None, config={}):
"""Note: the 'checker' key in 'config' sets the
'checker_command' attribute and *not* the 'checker'
attribute."""
self.name = name
- if config is None:
- config = {}
logger.debug(u"Creating client %r", self.name)
# Uppercase and remove spaces from fingerprint for later
# comparison purposes with return value from the fingerprint()
# function
- self.fingerprint = (config[u"fingerprint"].upper()
- .replace(u" ", u""))
+ self.fingerprint = config["fingerprint"].upper()\
+ .replace(u" ", u"")
logger.debug(u" Fingerprint: %s", self.fingerprint)
- if u"secret" in config:
- self.secret = config[u"secret"].decode(u"base64")
- elif u"secfile" in config:
- with open(os.path.expanduser(os.path.expandvars
- (config[u"secfile"])),
- "rb") as secfile:
- self.secret = secfile.read()
+ if "secret" in config:
+ self.secret = config["secret"].decode(u"base64")
+ elif "secfile" in config:
+ sf = open(config["secfile"])
+ self.secret = sf.read()
+ sf.close()
else:
raise TypeError(u"No secret or secfile for client %s"
% self.name)
- self.host = config.get(u"host", u"")
- self.created = datetime.datetime.utcnow()
- self.enabled = False
- self.last_approval_request = None
- self.last_enabled = None
+ self.fqdn = config.get("fqdn", "")
+ self.created = datetime.datetime.now()
self.last_checked_ok = None
- self.timeout = string_to_delta(config[u"timeout"])
- self.interval = string_to_delta(config[u"interval"])
- self.disable_hook = disable_hook
+ self.timeout = string_to_delta(config["timeout"])
+ self.interval = string_to_delta(config["interval"])
+ self.stop_hook = stop_hook
self.checker = None
self.checker_initiator_tag = None
- self.disable_initiator_tag = None
+ self.stop_initiator_tag = None
self.checker_callback_tag = None
- self.checker_command = config[u"checker"]
- self.current_checker_command = None
- self.last_connect = None
- self._approved = None
- self.approved_by_default = config.get(u"approved_by_default",
- True)
- self.approvals_pending = 0
- self.approval_delay = string_to_delta(
- config[u"approval_delay"])
- self.approval_duration = string_to_delta(
- config[u"approval_duration"])
- self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
-
- def send_changedstate(self):
- self.changedstate.acquire()
- self.changedstate.notify_all()
- self.changedstate.release()
-
- def enable(self):
+ self.check_command = config["checker"]
+ def start(self):
"""Start this client's checker and timeout hooks"""
- if getattr(self, u"enabled", False):
- # Already enabled
- return
- self.send_changedstate()
- self.last_enabled = datetime.datetime.utcnow()
# Schedule a new checker to be started an 'interval' from now,
# and every interval from then on.
- self.checker_initiator_tag = (gobject.timeout_add
- (self.interval_milliseconds(),
- self.start_checker))
- # Schedule a disable() when 'timeout' has passed
- self.disable_initiator_tag = (gobject.timeout_add
- (self.timeout_milliseconds(),
- self.disable))
- self.enabled = True
+ self.checker_initiator_tag = gobject.timeout_add\
+ (self._interval_milliseconds,
+ self.start_checker)
# Also start a new checker *right now*.
self.start_checker()
-
- def disable(self, quiet=True):
- """Disable this client."""
- if not getattr(self, "enabled", False):
+ # Schedule a stop() when 'timeout' has passed
+ self.stop_initiator_tag = gobject.timeout_add\
+ (self._timeout_milliseconds,
+ self.stop)
+ def stop(self):
+ """Stop this client.
+ The possibility that a client might be restarted is left open,
+ but not currently used."""
+ # If this client doesn't have a secret, it is already stopped.
+ if self.secret:
+ logger.info(u"Stopping client %s", self.name)
+ self.secret = None
+ else:
return False
- if not quiet:
- self.send_changedstate()
- if not quiet:
- logger.info(u"Disabling client %s", self.name)
- if getattr(self, u"disable_initiator_tag", False):
- gobject.source_remove(self.disable_initiator_tag)
- self.disable_initiator_tag = None
- if getattr(self, u"checker_initiator_tag", False):
+ if getattr(self, "stop_initiator_tag", False):
+ gobject.source_remove(self.stop_initiator_tag)
+ self.stop_initiator_tag = None
+ if getattr(self, "checker_initiator_tag", False):
gobject.source_remove(self.checker_initiator_tag)
self.checker_initiator_tag = None
self.stop_checker()
- if self.disable_hook:
- self.disable_hook(self)
- self.enabled = False
+ if self.stop_hook:
+ self.stop_hook(self)
# Do not run this again if called by a gobject.timeout_add
return False
-
def __del__(self):
- self.disable_hook = None
- self.disable()
-
- def checker_callback(self, pid, condition, command):
+ self.stop_hook = None
+ self.stop()
+ def checker_callback(self, pid, condition):
"""The checker has completed, so take appropriate actions."""
+ now = datetime.datetime.now()
self.checker_callback_tag = None
self.checker = None
- if os.WIFEXITED(condition):
- exitstatus = os.WEXITSTATUS(condition)
- if exitstatus == 0:
- logger.info(u"Checker for %(name)s succeeded",
- vars(self))
- self.checked_ok()
- else:
- logger.info(u"Checker for %(name)s failed",
- vars(self))
- else:
+ if os.WIFEXITED(condition) \
+ and (os.WEXITSTATUS(condition) == 0):
+ logger.info(u"Checker for %(name)s succeeded",
+ vars(self))
+ self.last_checked_ok = now
+ gobject.source_remove(self.stop_initiator_tag)
+ self.stop_initiator_tag = gobject.timeout_add\
+ (self._timeout_milliseconds,
+ self.stop)
+ elif not os.WIFEXITED(condition):
logger.warning(u"Checker for %(name)s crashed?",
vars(self))
-
- def checked_ok(self):
- """Bump up the timeout for this client.
-
- This should only be called when the client has been seen,
- alive and well.
- """
- self.last_checked_ok = datetime.datetime.utcnow()
- gobject.source_remove(self.disable_initiator_tag)
- self.disable_initiator_tag = (gobject.timeout_add
- (self.timeout_milliseconds(),
- self.disable))
-
- def need_approval(self):
- self.last_approval_request = datetime.datetime.utcnow()
-
+ else:
+ logger.info(u"Checker for %(name)s failed",
+ vars(self))
def start_checker(self):
"""Start a new checker subprocess if one is not running.
-
If a checker already exists, leave it running and do
nothing."""
# The reason for not killing a running checker is that if we
@@ -438,1219 +308,329 @@
# client would inevitably timeout, since no checker would get
# a chance to run to completion. If we instead leave running
# checkers alone, the checker would have to take more time
- # than 'timeout' for the client to be disabled, which is as it
- # should be.
-
- # If a checker exists, make sure it is not a zombie
- try:
- pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
- except (AttributeError, OSError), error:
- if (isinstance(error, OSError)
- and error.errno != errno.ECHILD):
- raise error
- else:
- if pid:
- logger.warning(u"Checker was a zombie")
- gobject.source_remove(self.checker_callback_tag)
- self.checker_callback(pid, status,
- self.current_checker_command)
- # Start a new checker if needed
+ # than 'timeout' for the client to be declared invalid, which
+ # is as it should be.
if self.checker is None:
try:
- # In case checker_command has exactly one % operator
- command = self.checker_command % self.host
+ # In case check_command has exactly one % operator
+ command = self.check_command % self.fqdn
except TypeError:
# Escape attributes for the shell
- escaped_attrs = dict(
- (attr,
- re.escape(unicode(str(getattr(self, attr, u"")),
- errors=
- u'replace')))
- for attr in
- self.runtime_expansions)
-
+ escaped_attrs = dict((key, re.escape(str(val)))
+ for key, val in
+ vars(self).iteritems())
try:
- command = self.checker_command % escaped_attrs
+ command = self.check_command % escaped_attrs
except TypeError, error:
logger.error(u'Could not format string "%s":'
- u' %s', self.checker_command, error)
+ u' %s', self.check_command, error)
return True # Try again later
- self.current_checker_command = command
try:
logger.info(u"Starting checker %r for %s",
command, self.name)
- # We don't need to redirect stdout and stderr, since
- # in normal mode, that is already done by daemon(),
- # and in debug mode we don't want to. (Stdin is
- # always replaced by /dev/null.)
self.checker = subprocess.Popen(command,
close_fds=True,
- shell=True, cwd=u"/")
- self.checker_callback_tag = (gobject.child_watch_add
- (self.checker.pid,
- self.checker_callback,
- data=command))
- # The checker may have completed before the gobject
- # watch was added. Check for this.
- pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
- if pid:
- gobject.source_remove(self.checker_callback_tag)
- self.checker_callback(pid, status, command)
- except OSError, error:
+ shell=True, cwd="/")
+ self.checker_callback_tag = gobject.child_watch_add\
+ (self.checker.pid,
+ self.checker_callback)
+ except subprocess.OSError, error:
logger.error(u"Failed to start subprocess: %s",
error)
# Re-run this periodically if run by gobject.timeout_add
return True
-
def stop_checker(self):
"""Force the checker process, if any, to stop."""
if self.checker_callback_tag:
gobject.source_remove(self.checker_callback_tag)
self.checker_callback_tag = None
- if getattr(self, u"checker", None) is None:
+ if getattr(self, "checker", None) is None:
return
- logger.debug(u"Stopping checker for %(name)s", vars(self))
+ logger.debug("Stopping checker for %(name)s", vars(self))
try:
os.kill(self.checker.pid, signal.SIGTERM)
- #time.sleep(0.5)
+ #os.sleep(0.5)
#if self.checker.poll() is None:
# os.kill(self.checker.pid, signal.SIGKILL)
except OSError, error:
if error.errno != errno.ESRCH: # No such process
raise
self.checker = None
-
-def dbus_service_property(dbus_interface, signature=u"v",
- access=u"readwrite", byte_arrays=False):
- """Decorators for marking methods of a DBusObjectWithProperties to
- become properties on the D-Bus.
-
- The decorated method will be called with no arguments by "Get"
- and with one argument by "Set".
-
- The parameters, where they are supported, are the same as
- dbus.service.method, except there is only "signature", since the
- type from Get() and the type sent to Set() is the same.
- """
- # Encoding deeply encoded byte arrays is not supported yet by the
- # "Set" method, so we fail early here:
- if byte_arrays and signature != u"ay":
- raise ValueError(u"Byte arrays not supported for non-'ay'"
- u" signature %r" % signature)
- def decorator(func):
- func._dbus_is_property = True
- func._dbus_interface = dbus_interface
- func._dbus_signature = signature
- func._dbus_access = access
- func._dbus_name = func.__name__
- if func._dbus_name.endswith(u"_dbus_property"):
- func._dbus_name = func._dbus_name[:-14]
- func._dbus_get_args_options = {u'byte_arrays': byte_arrays }
- return func
- return decorator
-
-
-class DBusPropertyException(dbus.exceptions.DBusException):
- """A base class for D-Bus property-related exceptions
- """
- def __unicode__(self):
- return unicode(str(self))
-
-
-class DBusPropertyAccessException(DBusPropertyException):
- """A property's access permissions disallows an operation.
- """
- pass
-
-
-class DBusPropertyNotFound(DBusPropertyException):
- """An attempt was made to access a non-existing property.
- """
- pass
-
-
-class DBusObjectWithProperties(dbus.service.Object):
- """A D-Bus object with properties.
-
- Classes inheriting from this can use the dbus_service_property
- decorator to expose methods as D-Bus properties. It exposes the
- standard Get(), Set(), and GetAll() methods on the D-Bus.
- """
-
- @staticmethod
- def _is_dbus_property(obj):
- return getattr(obj, u"_dbus_is_property", False)
-
- def _get_all_dbus_properties(self):
- """Returns a generator of (name, attribute) pairs
- """
- return ((prop._dbus_name, prop)
- for name, prop in
- inspect.getmembers(self, self._is_dbus_property))
-
- def _get_dbus_property(self, interface_name, property_name):
- """Returns a bound method if one exists which is a D-Bus
- property with the specified name and interface.
- """
- for name in (property_name,
- property_name + u"_dbus_property"):
- prop = getattr(self, name, None)
- if (prop is None
- or not self._is_dbus_property(prop)
- or prop._dbus_name != property_name
- or (interface_name and prop._dbus_interface
- and interface_name != prop._dbus_interface)):
- continue
- return prop
- # No such property
- raise DBusPropertyNotFound(self.dbus_object_path + u":"
- + interface_name + u"."
- + property_name)
-
- @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ss",
- out_signature=u"v")
- def Get(self, interface_name, property_name):
- """Standard D-Bus property Get() method, see D-Bus standard.
- """
- prop = self._get_dbus_property(interface_name, property_name)
- if prop._dbus_access == u"write":
- raise DBusPropertyAccessException(property_name)
- value = prop()
- if not hasattr(value, u"variant_level"):
- return value
- return type(value)(value, variant_level=value.variant_level+1)
-
- @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"ssv")
- def Set(self, interface_name, property_name, value):
- """Standard D-Bus property Set() method, see D-Bus standard.
- """
- prop = self._get_dbus_property(interface_name, property_name)
- if prop._dbus_access == u"read":
- raise DBusPropertyAccessException(property_name)
- if prop._dbus_get_args_options[u"byte_arrays"]:
- # The byte_arrays option is not supported yet on
- # signatures other than "ay".
- if prop._dbus_signature != u"ay":
- raise ValueError
- value = dbus.ByteArray(''.join(unichr(byte)
- for byte in value))
- prop(value)
-
- @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature=u"s",
- out_signature=u"a{sv}")
- def GetAll(self, interface_name):
- """Standard D-Bus property GetAll() method, see D-Bus
- standard.
-
- Note: Will not include properties with access="write".
- """
- all = {}
- for name, prop in self._get_all_dbus_properties():
- if (interface_name
- and interface_name != prop._dbus_interface):
- # Interface non-empty but did not match
- continue
- # Ignore write-only properties
- if prop._dbus_access == u"write":
- continue
- value = prop()
- if not hasattr(value, u"variant_level"):
- all[name] = value
- continue
- all[name] = type(value)(value, variant_level=
- value.variant_level+1)
- return dbus.Dictionary(all, signature=u"sv")
-
- @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
- out_signature=u"s",
- path_keyword='object_path',
- connection_keyword='connection')
- def Introspect(self, object_path, connection):
- """Standard D-Bus method, overloaded to insert property tags.
- """
- xmlstring = dbus.service.Object.Introspect(self, object_path,
- connection)
- try:
- document = xml.dom.minidom.parseString(xmlstring)
- def make_tag(document, name, prop):
- e = document.createElement(u"property")
- e.setAttribute(u"name", name)
- e.setAttribute(u"type", prop._dbus_signature)
- e.setAttribute(u"access", prop._dbus_access)
- return e
- for if_tag in document.getElementsByTagName(u"interface"):
- for tag in (make_tag(document, name, prop)
- for name, prop
- in self._get_all_dbus_properties()
- if prop._dbus_interface
- == if_tag.getAttribute(u"name")):
- if_tag.appendChild(tag)
- # Add the names to the return values for the
- # "org.freedesktop.DBus.Properties" methods
- if (if_tag.getAttribute(u"name")
- == u"org.freedesktop.DBus.Properties"):
- for cn in if_tag.getElementsByTagName(u"method"):
- if cn.getAttribute(u"name") == u"Get":
- for arg in cn.getElementsByTagName(u"arg"):
- if (arg.getAttribute(u"direction")
- == u"out"):
- arg.setAttribute(u"name", u"value")
- elif cn.getAttribute(u"name") == u"GetAll":
- for arg in cn.getElementsByTagName(u"arg"):
- if (arg.getAttribute(u"direction")
- == u"out"):
- arg.setAttribute(u"name", u"props")
- xmlstring = document.toxml(u"utf-8")
- document.unlink()
- except (AttributeError, xml.dom.DOMException,
- xml.parsers.expat.ExpatError), error:
- logger.error(u"Failed to override Introspection method",
- error)
- return xmlstring
-
-
-class ClientDBus(Client, DBusObjectWithProperties):
- """A Client class using D-Bus
-
- Attributes:
- dbus_object_path: dbus.ObjectPath
- bus: dbus.SystemBus()
- """
-
- runtime_expansions = (Client.runtime_expansions
- + (u"dbus_object_path",))
-
- # dbus.service.Object doesn't use super(), so we can't either.
-
- def __init__(self, bus = None, *args, **kwargs):
- self._approvals_pending = 0
- self.bus = bus
- Client.__init__(self, *args, **kwargs)
- # Only now, when this client is initialized, can it show up on
- # the D-Bus
- client_object_name = unicode(self.name).translate(
- {ord(u"."): ord(u"_"),
- ord(u"-"): ord(u"_")})
- self.dbus_object_path = (dbus.ObjectPath
- (u"/clients/" + client_object_name))
- DBusObjectWithProperties.__init__(self, self.bus,
- self.dbus_object_path)
-
- def _get_approvals_pending(self):
- return self._approvals_pending
- def _set_approvals_pending(self, value):
- old_value = self._approvals_pending
- self._approvals_pending = value
- bval = bool(value)
- if (hasattr(self, "dbus_object_path")
- and bval is not bool(old_value)):
- dbus_bool = dbus.Boolean(bval, variant_level=1)
- self.PropertyChanged(dbus.String(u"ApprovalPending"),
- dbus_bool)
-
- approvals_pending = property(_get_approvals_pending,
- _set_approvals_pending)
- del _get_approvals_pending, _set_approvals_pending
-
- @staticmethod
- def _datetime_to_dbus(dt, variant_level=0):
- """Convert a UTC datetime.datetime() to a D-Bus type."""
- return dbus.String(dt.isoformat(),
- variant_level=variant_level)
-
- def enable(self):
- oldstate = getattr(self, u"enabled", False)
- r = Client.enable(self)
- if oldstate != self.enabled:
- # Emit D-Bus signals
- self.PropertyChanged(dbus.String(u"Enabled"),
- dbus.Boolean(True, variant_level=1))
- self.PropertyChanged(
- dbus.String(u"LastEnabled"),
- self._datetime_to_dbus(self.last_enabled,
- variant_level=1))
- return r
-
- def disable(self, quiet = False):
- oldstate = getattr(self, u"enabled", False)
- r = Client.disable(self, quiet=quiet)
- if not quiet and oldstate != self.enabled:
- # Emit D-Bus signal
- self.PropertyChanged(dbus.String(u"Enabled"),
- dbus.Boolean(False, variant_level=1))
- return r
-
- def __del__(self, *args, **kwargs):
- try:
- self.remove_from_connection()
- except LookupError:
- pass
- if hasattr(DBusObjectWithProperties, u"__del__"):
- DBusObjectWithProperties.__del__(self, *args, **kwargs)
- Client.__del__(self, *args, **kwargs)
-
- def checker_callback(self, pid, condition, command,
- *args, **kwargs):
- self.checker_callback_tag = None
- self.checker = None
- # Emit D-Bus signal
- self.PropertyChanged(dbus.String(u"CheckerRunning"),
- dbus.Boolean(False, variant_level=1))
- if os.WIFEXITED(condition):
- exitstatus = os.WEXITSTATUS(condition)
- # Emit D-Bus signal
- self.CheckerCompleted(dbus.Int16(exitstatus),
- dbus.Int64(condition),
- dbus.String(command))
- else:
- # Emit D-Bus signal
- self.CheckerCompleted(dbus.Int16(-1),
- dbus.Int64(condition),
- dbus.String(command))
-
- return Client.checker_callback(self, pid, condition, command,
- *args, **kwargs)
-
- def checked_ok(self, *args, **kwargs):
- r = Client.checked_ok(self, *args, **kwargs)
- # Emit D-Bus signal
- self.PropertyChanged(
- dbus.String(u"LastCheckedOK"),
- (self._datetime_to_dbus(self.last_checked_ok,
- variant_level=1)))
- return r
-
- def need_approval(self, *args, **kwargs):
- r = Client.need_approval(self, *args, **kwargs)
- # Emit D-Bus signal
- self.PropertyChanged(
- dbus.String(u"LastApprovalRequest"),
- (self._datetime_to_dbus(self.last_approval_request,
- variant_level=1)))
- return r
-
- def start_checker(self, *args, **kwargs):
- old_checker = self.checker
- if self.checker is not None:
- old_checker_pid = self.checker.pid
- else:
- old_checker_pid = None
- r = Client.start_checker(self, *args, **kwargs)
- # Only if new checker process was started
- if (self.checker is not None
- and old_checker_pid != self.checker.pid):
- # Emit D-Bus signal
- self.CheckerStarted(self.current_checker_command)
- self.PropertyChanged(
- dbus.String(u"CheckerRunning"),
- dbus.Boolean(True, variant_level=1))
- return r
-
- def stop_checker(self, *args, **kwargs):
- old_checker = getattr(self, u"checker", None)
- r = Client.stop_checker(self, *args, **kwargs)
- if (old_checker is not None
- and getattr(self, u"checker", None) is None):
- self.PropertyChanged(dbus.String(u"CheckerRunning"),
- dbus.Boolean(False, variant_level=1))
- return r
-
- def _reset_approved(self):
- self._approved = None
- return False
-
- def approve(self, value=True):
- self.send_changedstate()
- self._approved = value
- gobject.timeout_add(self._timedelta_to_milliseconds
- (self.approval_duration),
- self._reset_approved)
-
-
- ## D-Bus methods, signals & properties
- _interface = u"se.bsnet.fukt.Mandos.Client"
-
- ## Signals
-
- # CheckerCompleted - signal
- @dbus.service.signal(_interface, signature=u"nxs")
- def CheckerCompleted(self, exitcode, waitstatus, command):
- "D-Bus signal"
- pass
-
- # CheckerStarted - signal
- @dbus.service.signal(_interface, signature=u"s")
- def CheckerStarted(self, command):
- "D-Bus signal"
- pass
-
- # PropertyChanged - signal
- @dbus.service.signal(_interface, signature=u"sv")
- def PropertyChanged(self, property, value):
- "D-Bus signal"
- pass
-
- # GotSecret - signal
- @dbus.service.signal(_interface)
- def GotSecret(self):
- """D-Bus signal
- Is sent after a successful transfer of secret from the Mandos
- server to mandos-client
- """
- pass
-
- # Rejected - signal
- @dbus.service.signal(_interface, signature=u"s")
- def Rejected(self, reason):
- "D-Bus signal"
- pass
-
- # NeedApproval - signal
- @dbus.service.signal(_interface, signature=u"tb")
- def NeedApproval(self, timeout, default):
- "D-Bus signal"
- return self.need_approval()
-
- ## Methods
-
- # Approve - method
- @dbus.service.method(_interface, in_signature=u"b")
- def Approve(self, value):
- self.approve(value)
-
- # CheckedOK - method
- @dbus.service.method(_interface)
- def CheckedOK(self):
- return self.checked_ok()
-
- # Enable - method
- @dbus.service.method(_interface)
- def Enable(self):
- "D-Bus method"
- self.enable()
-
- # StartChecker - method
- @dbus.service.method(_interface)
- def StartChecker(self):
- "D-Bus method"
- self.start_checker()
-
- # Disable - method
- @dbus.service.method(_interface)
- def Disable(self):
- "D-Bus method"
- self.disable()
-
- # StopChecker - method
- @dbus.service.method(_interface)
- def StopChecker(self):
- self.stop_checker()
-
- ## Properties
-
- # ApprovalPending - property
- @dbus_service_property(_interface, signature=u"b", access=u"read")
- def ApprovalPending_dbus_property(self):
- return dbus.Boolean(bool(self.approvals_pending))
-
- # ApprovedByDefault - property
- @dbus_service_property(_interface, signature=u"b",
- access=u"readwrite")
- def ApprovedByDefault_dbus_property(self, value=None):
- if value is None: # get
- return dbus.Boolean(self.approved_by_default)
- self.approved_by_default = bool(value)
- # Emit D-Bus signal
- self.PropertyChanged(dbus.String(u"ApprovedByDefault"),
- dbus.Boolean(value, variant_level=1))
-
- # ApprovalDelay - property
- @dbus_service_property(_interface, signature=u"t",
- access=u"readwrite")
- def ApprovalDelay_dbus_property(self, value=None):
- if value is None: # get
- return dbus.UInt64(self.approval_delay_milliseconds())
- self.approval_delay = datetime.timedelta(0, 0, 0, value)
- # Emit D-Bus signal
- self.PropertyChanged(dbus.String(u"ApprovalDelay"),
- dbus.UInt64(value, variant_level=1))
-
- # ApprovalDuration - property
- @dbus_service_property(_interface, signature=u"t",
- access=u"readwrite")
- def ApprovalDuration_dbus_property(self, value=None):
- if value is None: # get
- return dbus.UInt64(self._timedelta_to_milliseconds(
- self.approval_duration))
- self.approval_duration = datetime.timedelta(0, 0, 0, value)
- # Emit D-Bus signal
- self.PropertyChanged(dbus.String(u"ApprovalDuration"),
- dbus.UInt64(value, variant_level=1))
-
- # Name - property
- @dbus_service_property(_interface, signature=u"s", access=u"read")
- def Name_dbus_property(self):
- return dbus.String(self.name)
-
- # Fingerprint - property
- @dbus_service_property(_interface, signature=u"s", access=u"read")
- def Fingerprint_dbus_property(self):
- return dbus.String(self.fingerprint)
-
- # Host - property
- @dbus_service_property(_interface, signature=u"s",
- access=u"readwrite")
- def Host_dbus_property(self, value=None):
- if value is None: # get
- return dbus.String(self.host)
- self.host = value
- # Emit D-Bus signal
- self.PropertyChanged(dbus.String(u"Host"),
- dbus.String(value, variant_level=1))
-
- # Created - property
- @dbus_service_property(_interface, signature=u"s", access=u"read")
- def Created_dbus_property(self):
- return dbus.String(self._datetime_to_dbus(self.created))
-
- # LastEnabled - property
- @dbus_service_property(_interface, signature=u"s", access=u"read")
- def LastEnabled_dbus_property(self):
- if self.last_enabled is None:
- return dbus.String(u"")
- return dbus.String(self._datetime_to_dbus(self.last_enabled))
-
- # Enabled - property
- @dbus_service_property(_interface, signature=u"b",
- access=u"readwrite")
- def Enabled_dbus_property(self, value=None):
- if value is None: # get
- return dbus.Boolean(self.enabled)
- if value:
- self.enable()
- else:
- self.disable()
-
- # LastCheckedOK - property
- @dbus_service_property(_interface, signature=u"s",
- access=u"readwrite")
- def LastCheckedOK_dbus_property(self, value=None):
- if value is not None:
- self.checked_ok()
- return
+ def still_valid(self):
+ """Has the timeout not yet passed for this client?"""
+ now = datetime.datetime.now()
if self.last_checked_ok is None:
- return dbus.String(u"")
- return dbus.String(self._datetime_to_dbus(self
- .last_checked_ok))
-
- # LastApprovalRequest - property
- @dbus_service_property(_interface, signature=u"s", access=u"read")
- def LastApprovalRequest_dbus_property(self):
- if self.last_approval_request is None:
- return dbus.String(u"")
- return dbus.String(self.
- _datetime_to_dbus(self
- .last_approval_request))
-
- # Timeout - property
- @dbus_service_property(_interface, signature=u"t",
- access=u"readwrite")
- def Timeout_dbus_property(self, value=None):
- if value is None: # get
- return dbus.UInt64(self.timeout_milliseconds())
- self.timeout = datetime.timedelta(0, 0, 0, value)
- # Emit D-Bus signal
- self.PropertyChanged(dbus.String(u"Timeout"),
- dbus.UInt64(value, variant_level=1))
- if getattr(self, u"disable_initiator_tag", None) is None:
- return
- # Reschedule timeout
- gobject.source_remove(self.disable_initiator_tag)
- self.disable_initiator_tag = None
- time_to_die = (self.
- _timedelta_to_milliseconds((self
- .last_checked_ok
- + self.timeout)
- - datetime.datetime
- .utcnow()))
- if time_to_die <= 0:
- # The timeout has passed
- self.disable()
- else:
- self.disable_initiator_tag = (gobject.timeout_add
- (time_to_die, self.disable))
-
- # Interval - property
- @dbus_service_property(_interface, signature=u"t",
- access=u"readwrite")
- def Interval_dbus_property(self, value=None):
- if value is None: # get
- return dbus.UInt64(self.interval_milliseconds())
- self.interval = datetime.timedelta(0, 0, 0, value)
- # Emit D-Bus signal
- self.PropertyChanged(dbus.String(u"Interval"),
- dbus.UInt64(value, variant_level=1))
- if getattr(self, u"checker_initiator_tag", None) is None:
- return
- # Reschedule checker run
- gobject.source_remove(self.checker_initiator_tag)
- self.checker_initiator_tag = (gobject.timeout_add
- (value, self.start_checker))
- self.start_checker() # Start one now, too
-
- # Checker - property
- @dbus_service_property(_interface, signature=u"s",
- access=u"readwrite")
- def Checker_dbus_property(self, value=None):
- if value is None: # get
- return dbus.String(self.checker_command)
- self.checker_command = value
- # Emit D-Bus signal
- self.PropertyChanged(dbus.String(u"Checker"),
- dbus.String(self.checker_command,
- variant_level=1))
-
- # CheckerRunning - property
- @dbus_service_property(_interface, signature=u"b",
- access=u"readwrite")
- def CheckerRunning_dbus_property(self, value=None):
- if value is None: # get
- return dbus.Boolean(self.checker is not None)
- if value:
- self.start_checker()
- else:
- self.stop_checker()
-
- # ObjectPath - property
- @dbus_service_property(_interface, signature=u"o", access=u"read")
- def ObjectPath_dbus_property(self):
- return self.dbus_object_path # is already a dbus.ObjectPath
-
- # Secret = property
- @dbus_service_property(_interface, signature=u"ay",
- access=u"write", byte_arrays=True)
- def Secret_dbus_property(self, value):
- self.secret = str(value)
-
- del _interface
-
-
-class ProxyClient(object):
- def __init__(self, child_pipe, fpr, address):
- self._pipe = child_pipe
- self._pipe.send(('init', fpr, address))
- if not self._pipe.recv():
- raise KeyError()
-
- def __getattribute__(self, name):
- if(name == '_pipe'):
- return super(ProxyClient, self).__getattribute__(name)
- self._pipe.send(('getattr', name))
- data = self._pipe.recv()
- if data[0] == 'data':
- return data[1]
- if data[0] == 'function':
- def func(*args, **kwargs):
- self._pipe.send(('funcall', name, args, kwargs))
- return self._pipe.recv()[1]
- return func
-
- def __setattr__(self, name, value):
- if(name == '_pipe'):
- return super(ProxyClient, self).__setattr__(name, value)
- self._pipe.send(('setattr', name, value))
-
-
-class ClientHandler(socketserver.BaseRequestHandler, object):
- """A class to handle client connections.
-
- Instantiated once for each connection to handle it.
+ return now < (self.created + self.timeout)
+ else:
+ return now < (self.last_checked_ok + self.timeout)
+
+
+def peer_certificate(session):
+ "Return the peer's OpenPGP certificate as a bytestring"
+ # If not an OpenPGP certificate...
+ if gnutls.library.functions.gnutls_certificate_type_get\
+ (session._c_object) \
+ != gnutls.library.constants.GNUTLS_CRT_OPENPGP:
+ # ...do the normal thing
+ return session.peer_certificate
+ list_size = ctypes.c_uint()
+ cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
+ (session._c_object, ctypes.byref(list_size))
+ if list_size.value == 0:
+ return None
+ cert = cert_list[0]
+ return ctypes.string_at(cert.data, cert.size)
+
+
+def fingerprint(openpgp):
+ "Convert an OpenPGP bytestring to a hexdigit fingerprint string"
+ # New GnuTLS "datum" with the OpenPGP public key
+ datum = gnutls.library.types.gnutls_datum_t\
+ (ctypes.cast(ctypes.c_char_p(openpgp),
+ ctypes.POINTER(ctypes.c_ubyte)),
+ ctypes.c_uint(len(openpgp)))
+ # New empty GnuTLS certificate
+ crt = gnutls.library.types.gnutls_openpgp_crt_t()
+ gnutls.library.functions.gnutls_openpgp_crt_init\
+ (ctypes.byref(crt))
+ # Import the OpenPGP public key into the certificate
+ gnutls.library.functions.gnutls_openpgp_crt_import\
+ (crt, ctypes.byref(datum),
+ gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
+ # New buffer for the fingerprint
+ buffer = ctypes.create_string_buffer(20)
+ buffer_length = ctypes.c_size_t()
+ # Get the fingerprint from the certificate into the buffer
+ gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
+ (crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
+ # Deinit the certificate
+ gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
+ # Convert the buffer to a Python bytestring
+ fpr = ctypes.string_at(buffer, buffer_length.value)
+ # Convert the bytestring to hexadecimal notation
+ hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
+ return hex_fpr
+
+
+class tcp_handler(SocketServer.BaseRequestHandler, object):
+ """A TCP request handler class.
+ Instantiated by IPv6_TCPServer for each request to handle it.
Note: This will run in its own forked process."""
def handle(self):
- with contextlib.closing(self.server.child_pipe) as child_pipe:
- logger.info(u"TCP connection from: %s",
- unicode(self.client_address))
- logger.debug(u"Pipe FD: %d",
- self.server.child_pipe.fileno())
-
- session = (gnutls.connection
- .ClientSession(self.request,
- gnutls.connection
- .X509Credentials()))
-
- # Note: gnutls.connection.X509Credentials is really a
- # generic GnuTLS certificate credentials object so long as
- # no X.509 keys are added to it. Therefore, we can use it
- # here despite using OpenPGP certificates.
-
- #priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
- # u"+AES-256-CBC", u"+SHA1",
- # u"+COMP-NULL", u"+CTYPE-OPENPGP",
- # u"+DHE-DSS"))
- # Use a fallback default, since this MUST be set.
- priority = self.server.gnutls_priority
- if priority is None:
- priority = u"NORMAL"
- (gnutls.library.functions
- .gnutls_priority_set_direct(session._c_object,
- priority, None))
-
- # Start communication using the Mandos protocol
- # Get protocol number
- line = self.request.makefile().readline()
- logger.debug(u"Protocol version: %r", line)
- try:
- if int(line.strip().split()[0]) > 1:
- raise RuntimeError
- except (ValueError, IndexError, RuntimeError), error:
- logger.error(u"Unknown protocol version: %s", error)
- return
-
- # Start GnuTLS connection
- try:
- session.handshake()
- except gnutls.errors.GNUTLSError, error:
- logger.warning(u"Handshake failed: %s", error)
- # Do not run session.bye() here: the session is not
- # established. Just abandon the request.
- return
- logger.debug(u"Handshake succeeded")
-
- approval_required = False
- try:
- try:
- fpr = self.fingerprint(self.peer_certificate
- (session))
- except (TypeError, gnutls.errors.GNUTLSError), error:
- logger.warning(u"Bad certificate: %s", error)
- return
- logger.debug(u"Fingerprint: %s", fpr)
-
- try:
- client = ProxyClient(child_pipe, fpr,
- self.client_address)
- except KeyError:
- return
-
- if client.approval_delay:
- delay = client.approval_delay
- client.approvals_pending += 1
- approval_required = True
-
- while True:
- if not client.enabled:
- logger.warning(u"Client %s is disabled",
- client.name)
- if self.server.use_dbus:
- # Emit D-Bus signal
- client.Rejected("Disabled")
- return
-
- if client._approved or not client.approval_delay:
- #We are approved or approval is disabled
- break
- elif client._approved is None:
- logger.info(u"Client %s needs approval",
- client.name)
- if self.server.use_dbus:
- # Emit D-Bus signal
- client.NeedApproval(
- client.approval_delay_milliseconds(),
- client.approved_by_default)
- else:
- logger.warning(u"Client %s was not approved",
- client.name)
- if self.server.use_dbus:
- # Emit D-Bus signal
- client.Rejected("Denied")
- return
-
- #wait until timeout or approved
- #x = float(client._timedelta_to_milliseconds(delay))
- time = datetime.datetime.now()
- client.changedstate.acquire()
- client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
- client.changedstate.release()
- time2 = datetime.datetime.now()
- if (time2 - time) >= delay:
- if not client.approved_by_default:
- logger.warning("Client %s timed out while"
- " waiting for approval",
- client.name)
- if self.server.use_dbus:
- # Emit D-Bus signal
- client.Rejected("Approval timed out")
- return
- else:
- break
- else:
- delay -= time2 - time
-
- sent_size = 0
- while sent_size < len(client.secret):
- try:
- sent = session.send(client.secret[sent_size:])
- except (gnutls.errors.GNUTLSError), error:
- logger.warning("gnutls send failed")
- return
- logger.debug(u"Sent: %d, remaining: %d",
- sent, len(client.secret)
- - (sent_size + sent))
- sent_size += sent
-
- logger.info(u"Sending secret to %s", client.name)
- # bump the timeout as if seen
- client.checked_ok()
- if self.server.use_dbus:
- # Emit D-Bus signal
- client.GotSecret()
-
- finally:
- if approval_required:
- client.approvals_pending -= 1
- try:
- session.bye()
- except (gnutls.errors.GNUTLSError), error:
- logger.warning("GnuTLS bye failed")
-
- @staticmethod
- def peer_certificate(session):
- "Return the peer's OpenPGP certificate as a bytestring"
- # If not an OpenPGP certificate...
- if (gnutls.library.functions
- .gnutls_certificate_type_get(session._c_object)
- != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
- # ...do the normal thing
- return session.peer_certificate
- list_size = ctypes.c_uint(1)
- cert_list = (gnutls.library.functions
- .gnutls_certificate_get_peers
- (session._c_object, ctypes.byref(list_size)))
- if not bool(cert_list) and list_size.value != 0:
- raise gnutls.errors.GNUTLSError(u"error getting peer"
- u" certificate")
- if list_size.value == 0:
- return None
- cert = cert_list[0]
- return ctypes.string_at(cert.data, cert.size)
-
- @staticmethod
- def fingerprint(openpgp):
- "Convert an OpenPGP bytestring to a hexdigit fingerprint"
- # New GnuTLS "datum" with the OpenPGP public key
- datum = (gnutls.library.types
- .gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
- ctypes.POINTER
- (ctypes.c_ubyte)),
- ctypes.c_uint(len(openpgp))))
- # New empty GnuTLS certificate
- crt = gnutls.library.types.gnutls_openpgp_crt_t()
- (gnutls.library.functions
- .gnutls_openpgp_crt_init(ctypes.byref(crt)))
- # Import the OpenPGP public key into the certificate
- (gnutls.library.functions
- .gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
- gnutls.library.constants
- .GNUTLS_OPENPGP_FMT_RAW))
- # Verify the self signature in the key
- crtverify = ctypes.c_uint()
- (gnutls.library.functions
- .gnutls_openpgp_crt_verify_self(crt, 0,
- ctypes.byref(crtverify)))
- if crtverify.value != 0:
- gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
- raise (gnutls.errors.CertificateSecurityError
- (u"Verify failed"))
- # New buffer for the fingerprint
- buf = ctypes.create_string_buffer(20)
- buf_len = ctypes.c_size_t()
- # Get the fingerprint from the certificate into the buffer
- (gnutls.library.functions
- .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
- ctypes.byref(buf_len)))
- # Deinit the certificate
- gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
- # Convert the buffer to a Python bytestring
- fpr = ctypes.string_at(buf, buf_len.value)
- # Convert the bytestring to hexadecimal notation
- hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
- return hex_fpr
-
-
-class MultiprocessingMixIn(object):
- """Like socketserver.ThreadingMixIn, but with multiprocessing"""
- def sub_process_main(self, request, address):
- try:
- self.finish_request(request, address)
- except:
- self.handle_error(request, address)
- self.close_request(request)
-
- def process_request(self, request, address):
- """Start a new process to process the request."""
- multiprocessing.Process(target = self.sub_process_main,
- args = (request, address)).start()
-
-class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
- """ adds a pipe to the MixIn """
- def process_request(self, request, client_address):
- """Overrides and wraps the original process_request().
-
- This function creates a new pipe in self.pipe
- """
- parent_pipe, self.child_pipe = multiprocessing.Pipe()
-
- super(MultiprocessingMixInWithPipe,
- self).process_request(request, client_address)
- self.child_pipe.close()
- self.add_pipe(parent_pipe)
-
- def add_pipe(self, parent_pipe):
- """Dummy function; override as necessary"""
- pass
-
-class IPv6_TCPServer(MultiprocessingMixInWithPipe,
- socketserver.TCPServer, object):
- """IPv6-capable TCP server. Accepts 'None' as address and/or port
-
+ logger.info(u"TCP connection from: %s",
+ unicode(self.client_address))
+ session = gnutls.connection.ClientSession\
+ (self.request, gnutls.connection.X509Credentials())
+
+ line = self.request.makefile().readline()
+ logger.debug(u"Protocol version: %r", line)
+ try:
+ if int(line.strip().split()[0]) > 1:
+ raise RuntimeError
+ except (ValueError, IndexError, RuntimeError), error:
+ logger.error(u"Unknown protocol version: %s", error)
+ return
+
+ # Note: gnutls.connection.X509Credentials is really a generic
+ # GnuTLS certificate credentials object so long as no X.509
+ # keys are added to it. Therefore, we can use it here despite
+ # using OpenPGP certificates.
+
+ #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
+ # "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
+ # "+DHE-DSS"))
+ priority = "NORMAL" # Fallback default, since this
+ # MUST be set.
+ if self.server.settings["priority"]:
+ priority = self.server.settings["priority"]
+ gnutls.library.functions.gnutls_priority_set_direct\
+ (session._c_object, priority, None);
+
+ try:
+ session.handshake()
+ except gnutls.errors.GNUTLSError, error:
+ logger.warning(u"Handshake failed: %s", error)
+ # Do not run session.bye() here: the session is not
+ # established. Just abandon the request.
+ return
+ try:
+ fpr = fingerprint(peer_certificate(session))
+ except (TypeError, gnutls.errors.GNUTLSError), error:
+ logger.warning(u"Bad certificate: %s", error)
+ session.bye()
+ return
+ logger.debug(u"Fingerprint: %s", fpr)
+ client = None
+ for c in self.server.clients:
+ if c.fingerprint == fpr:
+ client = c
+ break
+ if not client:
+ logger.warning(u"Client not found for fingerprint: %s",
+ fpr)
+ session.bye()
+ return
+ # Have to check if client.still_valid(), since it is possible
+ # that the client timed out while establishing the GnuTLS
+ # session.
+ if not client.still_valid():
+ logger.warning(u"Client %(name)s is invalid",
+ vars(client))
+ session.bye()
+ return
+ sent_size = 0
+ while sent_size < len(client.secret):
+ sent = session.send(client.secret[sent_size:])
+ logger.debug(u"Sent: %d, remaining: %d",
+ sent, len(client.secret)
+ - (sent_size + sent))
+ sent_size += sent
+ session.bye()
+
+
+class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
+ """IPv6 TCP server. Accepts 'None' as address and/or port.
Attributes:
- enabled: Boolean; whether this server is activated yet
- interface: None or a network interface name (string)
- use_ipv6: Boolean; to use IPv6 or not
+ settings: Server settings
+ clients: Set() of Client objects
"""
- def __init__(self, server_address, RequestHandlerClass,
- interface=None, use_ipv6=True):
- self.interface = interface
- if use_ipv6:
- self.address_family = socket.AF_INET6
- socketserver.TCPServer.__init__(self, server_address,
- RequestHandlerClass)
+ address_family = socket.AF_INET6
+ def __init__(self, *args, **kwargs):
+ if "settings" in kwargs:
+ self.settings = kwargs["settings"]
+ del kwargs["settings"]
+ if "clients" in kwargs:
+ self.clients = kwargs["clients"]
+ del kwargs["clients"]
+ return super(type(self), self).__init__(*args, **kwargs)
def server_bind(self):
"""This overrides the normal server_bind() function
to bind to an interface if one was specified, and also NOT to
bind to an address or port if they were not specified."""
- if self.interface is not None:
- if SO_BINDTODEVICE is None:
- logger.error(u"SO_BINDTODEVICE does not exist;"
- u" cannot bind to interface %s",
- self.interface)
- else:
- try:
- self.socket.setsockopt(socket.SOL_SOCKET,
- SO_BINDTODEVICE,
- str(self.interface
- + u'\0'))
- except socket.error, error:
- if error[0] == errno.EPERM:
- logger.error(u"No permission to"
- u" bind to interface %s",
- self.interface)
- elif error[0] == errno.ENOPROTOOPT:
- logger.error(u"SO_BINDTODEVICE not available;"
- u" cannot bind to interface %s",
- self.interface)
- else:
- raise
+ if self.settings["interface"]:
+ # 25 is from /usr/include/asm-i486/socket.h
+ SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
+ try:
+ self.socket.setsockopt(socket.SOL_SOCKET,
+ SO_BINDTODEVICE,
+ self.settings["interface"])
+ except socket.error, error:
+ if error[0] == errno.EPERM:
+ logger.error(u"No permission to"
+ u" bind to interface %s",
+ self.settings["interface"])
+ else:
+ raise error
# Only bind(2) the socket if we really need to.
if self.server_address[0] or self.server_address[1]:
if not self.server_address[0]:
- if self.address_family == socket.AF_INET6:
- any_address = u"::" # in6addr_any
- else:
- any_address = socket.INADDR_ANY
- self.server_address = (any_address,
+ in6addr_any = "::"
+ self.server_address = (in6addr_any,
self.server_address[1])
elif not self.server_address[1]:
self.server_address = (self.server_address[0],
0)
-# if self.interface:
+# if self.settings["interface"]:
# self.server_address = (self.server_address[0],
# 0, # port
# 0, # flowinfo
# if_nametoindex
-# (self.interface))
- return socketserver.TCPServer.server_bind(self)
-
-
-class MandosServer(IPv6_TCPServer):
- """Mandos server.
-
- Attributes:
- clients: set of Client objects
- gnutls_priority GnuTLS priority string
- use_dbus: Boolean; to emit D-Bus signals or not
-
- Assumes a gobject.MainLoop event loop.
- """
- def __init__(self, server_address, RequestHandlerClass,
- interface=None, use_ipv6=True, clients=None,
- gnutls_priority=None, use_dbus=True):
- self.enabled = False
- self.clients = clients
- if self.clients is None:
- self.clients = set()
- self.use_dbus = use_dbus
- self.gnutls_priority = gnutls_priority
- IPv6_TCPServer.__init__(self, server_address,
- RequestHandlerClass,
- interface = interface,
- use_ipv6 = use_ipv6)
- def server_activate(self):
- if self.enabled:
- return socketserver.TCPServer.server_activate(self)
- def enable(self):
- self.enabled = True
- def add_pipe(self, parent_pipe):
- # Call "handle_ipc" for both data and EOF events
- gobject.io_add_watch(parent_pipe.fileno(),
- gobject.IO_IN | gobject.IO_HUP,
- functools.partial(self.handle_ipc,
- parent_pipe = parent_pipe))
-
- def handle_ipc(self, source, condition, parent_pipe=None,
- client_object=None):
- condition_names = {
- gobject.IO_IN: u"IN", # There is data to read.
- gobject.IO_OUT: u"OUT", # Data can be written (without
- # blocking).
- gobject.IO_PRI: u"PRI", # There is urgent data to read.
- gobject.IO_ERR: u"ERR", # Error condition.
- gobject.IO_HUP: u"HUP" # Hung up (the connection has been
- # broken, usually for pipes and
- # sockets).
- }
- conditions_string = ' | '.join(name
- for cond, name in
- condition_names.iteritems()
- if cond & condition)
- # error or the other end of multiprocessing.Pipe has closed
- if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
- return False
-
- # Read a request from the child
- request = parent_pipe.recv()
- command = request[0]
-
- if command == 'init':
- fpr = request[1]
- address = request[2]
-
- for c in self.clients:
- if c.fingerprint == fpr:
- client = c
- break
- else:
- logger.warning(u"Client not found for fingerprint: %s, ad"
- u"dress: %s", fpr, address)
- if self.use_dbus:
- # Emit D-Bus signal
- mandos_dbus_service.ClientNotFound(fpr, address[0])
- parent_pipe.send(False)
- return False
-
- gobject.io_add_watch(parent_pipe.fileno(),
- gobject.IO_IN | gobject.IO_HUP,
- functools.partial(self.handle_ipc,
- parent_pipe = parent_pipe,
- client_object = client))
- parent_pipe.send(True)
- # remove the old hook in favor of the new above hook on same fileno
- return False
- if command == 'funcall':
- funcname = request[1]
- args = request[2]
- kwargs = request[3]
-
- parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
-
- if command == 'getattr':
- attrname = request[1]
- if callable(client_object.__getattribute__(attrname)):
- parent_pipe.send(('function',))
- else:
- parent_pipe.send(('data', client_object.__getattribute__(attrname)))
-
- if command == 'setattr':
- attrname = request[1]
- value = request[2]
- setattr(client_object, attrname, value)
-
- return True
+# (self.settings
+# ["interface"]))
+ return super(type(self), self).server_bind()
def string_to_delta(interval):
"""Parse a string and return a datetime.timedelta
-
- >>> string_to_delta(u'7d')
+
+ >>> string_to_delta('7d')
datetime.timedelta(7)
- >>> string_to_delta(u'60s')
+ >>> string_to_delta('60s')
datetime.timedelta(0, 60)
- >>> string_to_delta(u'60m')
+ >>> string_to_delta('60m')
datetime.timedelta(0, 3600)
- >>> string_to_delta(u'24h')
+ >>> string_to_delta('24h')
datetime.timedelta(1)
>>> string_to_delta(u'1w')
datetime.timedelta(7)
- >>> string_to_delta(u'5m 30s')
- datetime.timedelta(0, 330)
"""
- timevalue = datetime.timedelta(0)
- for s in interval.split():
- try:
- suffix = unicode(s[-1])
- value = int(s[:-1])
- if suffix == u"d":
- delta = datetime.timedelta(value)
- elif suffix == u"s":
- delta = datetime.timedelta(0, value)
- elif suffix == u"m":
- delta = datetime.timedelta(0, 0, 0, 0, value)
- elif suffix == u"h":
- delta = datetime.timedelta(0, 0, 0, 0, 0, value)
- elif suffix == u"w":
- delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
- else:
- raise ValueError(u"Unknown suffix %r" % suffix)
- except (ValueError, IndexError), e:
- raise ValueError(e.message)
- timevalue += delta
- return timevalue
-
+ try:
+ suffix=unicode(interval[-1])
+ value=int(interval[:-1])
+ if suffix == u"d":
+ delta = datetime.timedelta(value)
+ elif suffix == u"s":
+ delta = datetime.timedelta(0, value)
+ elif suffix == u"m":
+ delta = datetime.timedelta(0, 0, 0, 0, value)
+ elif suffix == u"h":
+ delta = datetime.timedelta(0, 0, 0, 0, 0, value)
+ elif suffix == u"w":
+ delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
+ else:
+ raise ValueError
+ except (ValueError, IndexError):
+ raise ValueError
+ return delta
+
+
+def server_state_changed(state):
+ """Derived from the Avahi example code"""
+ if state == avahi.SERVER_COLLISION:
+ logger.error(u"Server name collision")
+ service.remove()
+ elif state == avahi.SERVER_RUNNING:
+ service.add()
+
+
+def entry_group_state_changed(state, error):
+ """Derived from the Avahi example code"""
+ logger.debug(u"state change: %i", state)
+
+ if state == avahi.ENTRY_GROUP_ESTABLISHED:
+ logger.debug(u"Service established.")
+ elif state == avahi.ENTRY_GROUP_COLLISION:
+ logger.warning(u"Service name collision.")
+ service.rename()
+ elif state == avahi.ENTRY_GROUP_FAILURE:
+ logger.critical(u"Error in group state changed %s",
+ unicode(error))
+ raise AvahiGroupError("State changed: %s", str(error))
def if_nametoindex(interface):
- """Call the C function if_nametoindex(), or equivalent
-
- Note: This function cannot accept a unicode string."""
+ """Call the C function if_nametoindex(), or equivalent"""
global if_nametoindex
try:
- if_nametoindex = (ctypes.cdll.LoadLibrary
- (ctypes.util.find_library(u"c"))
- .if_nametoindex)
+ if "ctypes.util" not in sys.modules:
+ import ctypes.util
+ if_nametoindex = ctypes.cdll.LoadLibrary\
+ (ctypes.util.find_library("c")).if_nametoindex
except (OSError, AttributeError):
- logger.warning(u"Doing if_nametoindex the hard way")
+ if "struct" not in sys.modules:
+ import struct
+ if "fcntl" not in sys.modules:
+ import fcntl
def if_nametoindex(interface):
"Get an interface index the hard way, i.e. using fcntl()"
SIOCGIFINDEX = 0x8933 # From /usr/include/linux/sockios.h
- with contextlib.closing(socket.socket()) as s:
- ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
- struct.pack(str(u"16s16x"),
- interface))
- interface_index = struct.unpack(str(u"I"),
- ifreq[16:20])[0]
+ s = socket.socket()
+ ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
+ struct.pack("16s16x", interface))
+ s.close()
+ interface_index = struct.unpack("I", ifreq[16:20])[0]
return interface_index
return if_nametoindex(interface)
def daemon(nochdir = False, noclose = False):
"""See daemon(3). Standard BSD Unix function.
-
This should really exist as os.daemon, but it doesn't (yet)."""
if os.fork():
sys.exit()
os.setsid()
if not nochdir:
- os.chdir(u"/")
+ os.chdir("/")
if os.fork():
sys.exit()
if not noclose:
@@ -1658,8 +638,7 @@
null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
if not stat.S_ISCHR(os.fstat(null).st_mode):
raise OSError(errno.ENODEV,
- u"%s not a character device"
- % os.path.devnull)
+ "/dev/null not a character device")
os.dup2(null, sys.stdin.fileno())
os.dup2(null, sys.stdout.fileno())
os.dup2(null, sys.stderr.fileno())
@@ -1668,38 +647,30 @@
def main():
-
- ##################################################################
- # Parsing of options, both command line and config file
-
- parser = optparse.OptionParser(version = "%%prog %s" % version)
- parser.add_option("-i", u"--interface", type=u"string",
- metavar="IF", help=u"Bind to interface IF")
- parser.add_option("-a", u"--address", type=u"string",
- help=u"Address to listen for requests on")
- parser.add_option("-p", u"--port", type=u"int",
- help=u"Port number to receive requests on")
- parser.add_option("--check", action=u"store_true",
- help=u"Run self-test")
- parser.add_option("--debug", action=u"store_true",
- help=u"Debug mode; run in foreground and log to"
- u" terminal")
- parser.add_option("--debuglevel", type=u"string", metavar="LEVEL",
- help=u"Debug level for stdout output")
- parser.add_option("--priority", type=u"string", help=u"GnuTLS"
- u" priority string (see GnuTLS documentation)")
- parser.add_option("--servicename", type=u"string",
- metavar=u"NAME", help=u"Zeroconf service name")
- parser.add_option("--configdir", type=u"string",
- default=u"/etc/mandos", metavar=u"DIR",
- help=u"Directory to search for configuration"
- u" files")
- parser.add_option("--no-dbus", action=u"store_false",
- dest=u"use_dbus", help=u"Do not provide D-Bus"
- u" system bus interface")
- parser.add_option("--no-ipv6", action=u"store_false",
- dest=u"use_ipv6", help=u"Do not use IPv6")
- options = parser.parse_args()[0]
+ global main_loop_started
+ main_loop_started = False
+
+ parser = OptionParser()
+ parser.add_option("-i", "--interface", type="string",
+ metavar="IF", help="Bind to interface IF")
+ parser.add_option("-a", "--address", type="string",
+ help="Address to listen for requests on")
+ parser.add_option("-p", "--port", type="int",
+ help="Port number to receive requests on")
+ parser.add_option("--check", action="store_true", default=False,
+ help="Run self-test")
+ parser.add_option("--debug", action="store_true",
+ help="Debug mode; run in foreground and log to"
+ " terminal")
+ parser.add_option("--priority", type="string", help="GnuTLS"
+ " priority string (see GnuTLS documentation)")
+ parser.add_option("--servicename", type="string", metavar="NAME",
+ help="Zeroconf service name")
+ parser.add_option("--configdir", type="string",
+ default="/etc/mandos", metavar="DIR",
+ help="Directory to search for configuration"
+ " files")
+ (options, args) = parser.parse_args()
if options.check:
import doctest
@@ -1707,341 +678,150 @@
sys.exit()
# Default values for config file for server-global settings
- server_defaults = { u"interface": u"",
- u"address": u"",
- u"port": u"",
- u"debug": u"False",
- u"priority":
- u"SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
- u"servicename": u"Mandos",
- u"use_dbus": u"True",
- u"use_ipv6": u"True",
- u"debuglevel": u"",
+ server_defaults = { "interface": "",
+ "address": "",
+ "port": "",
+ "debug": "False",
+ "priority":
+ "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
+ "servicename": "Mandos",
}
# Parse config file for server-global settings
- server_config = configparser.SafeConfigParser(server_defaults)
+ server_config = ConfigParser.SafeConfigParser(server_defaults)
del server_defaults
- server_config.read(os.path.join(options.configdir,
- u"mandos.conf"))
+ server_config.read(os.path.join(options.configdir, "mandos.conf"))
+ server_section = "server"
# Convert the SafeConfigParser object to a dict
- server_settings = server_config.defaults()
- # Use the appropriate methods on the non-string config options
- for option in (u"debug", u"use_dbus", u"use_ipv6"):
- server_settings[option] = server_config.getboolean(u"DEFAULT",
- option)
- if server_settings["port"]:
- server_settings["port"] = server_config.getint(u"DEFAULT",
- u"port")
+ server_settings = dict(server_config.items(server_section))
+ # Use getboolean on the boolean config option
+ server_settings["debug"] = server_config.getboolean\
+ (server_section, "debug")
del server_config
# Override the settings from the config file with command line
# options, if set.
- for option in (u"interface", u"address", u"port", u"debug",
- u"priority", u"servicename", u"configdir",
- u"use_dbus", u"use_ipv6", u"debuglevel"):
+ for option in ("interface", "address", "port", "debug",
+ "priority", "servicename", "configdir"):
value = getattr(options, option)
if value is not None:
server_settings[option] = value
del options
- # Force all strings to be unicode
- for option in server_settings.keys():
- if type(server_settings[option]) is str:
- server_settings[option] = unicode(server_settings[option])
# Now we have our good server settings in "server_settings"
- ##################################################################
-
- # For convenience
- debug = server_settings[u"debug"]
- debuglevel = server_settings[u"debuglevel"]
- use_dbus = server_settings[u"use_dbus"]
- use_ipv6 = server_settings[u"use_ipv6"]
-
- if server_settings[u"servicename"] != u"Mandos":
- syslogger.setFormatter(logging.Formatter
- (u'Mandos (%s) [%%(process)d]:'
- u' %%(levelname)s: %%(message)s'
- % server_settings[u"servicename"]))
-
# Parse config file with clients
- client_defaults = { u"timeout": u"1h",
- u"interval": u"5m",
- u"checker": u"fping -q -- %%(host)s",
- u"host": u"",
- u"approval_delay": u"0s",
- u"approval_duration": u"1s",
+ client_defaults = { "timeout": "1h",
+ "interval": "5m",
+ "checker": "fping -q -- %%(fqdn)s",
}
- client_config = configparser.SafeConfigParser(client_defaults)
- client_config.read(os.path.join(server_settings[u"configdir"],
- u"clients.conf"))
-
- global mandos_dbus_service
- mandos_dbus_service = None
-
- tcp_server = MandosServer((server_settings[u"address"],
- server_settings[u"port"]),
- ClientHandler,
- interface=(server_settings[u"interface"]
- or None),
- use_ipv6=use_ipv6,
- gnutls_priority=
- server_settings[u"priority"],
- use_dbus=use_dbus)
- if not debug:
- pidfilename = u"/var/run/mandos.pid"
- try:
- pidfile = open(pidfilename, u"w")
- except IOError:
- logger.error(u"Could not open file %r", pidfilename)
-
- try:
- uid = pwd.getpwnam(u"_mandos").pw_uid
- gid = pwd.getpwnam(u"_mandos").pw_gid
- except KeyError:
- try:
- uid = pwd.getpwnam(u"mandos").pw_uid
- gid = pwd.getpwnam(u"mandos").pw_gid
- except KeyError:
- try:
- uid = pwd.getpwnam(u"nobody").pw_uid
- gid = pwd.getpwnam(u"nobody").pw_gid
- except KeyError:
- uid = 65534
- gid = 65534
- try:
- os.setgid(gid)
- os.setuid(uid)
- except OSError, error:
- if error[0] != errno.EPERM:
- raise error
-
- if not debug and not debuglevel:
- syslogger.setLevel(logging.WARNING)
- console.setLevel(logging.WARNING)
- if debuglevel:
- level = getattr(logging, debuglevel.upper())
- syslogger.setLevel(level)
- console.setLevel(level)
-
- if debug:
- # Enable all possible GnuTLS debugging
-
- # "Use a log level over 10 to enable all debugging options."
- # - GnuTLS manual
- gnutls.library.functions.gnutls_global_set_log_level(11)
-
- @gnutls.library.types.gnutls_log_func
- def debug_gnutls(level, string):
- logger.debug(u"GnuTLS: %s", string[:-1])
-
- (gnutls.library.functions
- .gnutls_global_set_log_function(debug_gnutls))
-
- # Redirect stdin so all checkers get /dev/null
- null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
- os.dup2(null, sys.stdin.fileno())
- if null > 2:
- os.close(null)
- else:
- # No console logging
- logger.removeHandler(console)
-
+ client_config = ConfigParser.SafeConfigParser(client_defaults)
+ client_config.read(os.path.join(server_settings["configdir"],
+ "clients.conf"))
+
+ global service
+ service = AvahiService(name = server_settings["servicename"],
+ type = "_mandos._tcp", );
+ if server_settings["interface"]:
+ service.interface = if_nametoindex(server_settings["interface"])
global main_loop
+ global bus
+ global server
# From the Avahi example code
DBusGMainLoop(set_as_default=True )
main_loop = gobject.MainLoop()
bus = dbus.SystemBus()
+ server = dbus.Interface(
+ bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
+ avahi.DBUS_INTERFACE_SERVER )
# End of Avahi example code
- if use_dbus:
- try:
- bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
- bus, do_not_queue=True)
- except dbus.exceptions.NameExistsException, e:
- logger.error(unicode(e) + u", disabling D-Bus")
- use_dbus = False
- server_settings[u"use_dbus"] = False
- tcp_server.use_dbus = False
- protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
- service = AvahiService(name = server_settings[u"servicename"],
- servicetype = u"_mandos._tcp",
- protocol = protocol, bus = bus)
- if server_settings["interface"]:
- service.interface = (if_nametoindex
- (str(server_settings[u"interface"])))
-
+
+ debug = server_settings["debug"]
+
+ if debug:
+ console = logging.StreamHandler()
+ # console.setLevel(logging.DEBUG)
+ console.setFormatter(logging.Formatter\
+ ('%(levelname)s: %(message)s'))
+ logger.addHandler(console)
+ del console
+
+ clients = Set()
+ def remove_from_clients(client):
+ clients.remove(client)
+ if not clients:
+ logger.critical(u"No clients left, exiting")
+ sys.exit()
+
+ clients.update(Set(Client(name = section,
+ stop_hook = remove_from_clients,
+ config
+ = dict(client_config.items(section)))
+ for section in client_config.sections()))
+
if not debug:
- # Close all input and output, do double fork, etc.
daemon()
-
- global multiprocessing_manager
- multiprocessing_manager = multiprocessing.Manager()
-
- client_class = Client
- if use_dbus:
- client_class = functools.partial(ClientDBus, bus = bus)
- def client_config_items(config, section):
- special_settings = {
- "approved_by_default":
- lambda: config.getboolean(section,
- "approved_by_default"),
- }
- for name, value in config.items(section):
- try:
- yield (name, special_settings[name]())
- except KeyError:
- yield (name, value)
-
- tcp_server.clients.update(set(
- client_class(name = section,
- config= dict(client_config_items(
- client_config, section)))
- for section in client_config.sections()))
- if not tcp_server.clients:
- logger.warning(u"No clients defined")
-
+
+ def cleanup():
+ "Cleanup function; run on exit"
+ global group
+ # From the Avahi example code
+ if not group is None:
+ group.Free()
+ group = None
+ # End of Avahi example code
+
+ while clients:
+ client = clients.pop()
+ client.stop_hook = None
+ client.stop()
+
+ atexit.register(cleanup)
+
if not debug:
- try:
- with pidfile:
- pid = os.getpid()
- pidfile.write(str(pid) + "\n")
- del pidfile
- except IOError:
- logger.error(u"Could not write to file %r with PID %d",
- pidfilename, pid)
- except NameError:
- # "pidfile" was never created
- pass
- del pidfilename
-
signal.signal(signal.SIGINT, signal.SIG_IGN)
-
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
- if use_dbus:
- class MandosDBusService(dbus.service.Object):
- """A D-Bus proxy object"""
- def __init__(self):
- dbus.service.Object.__init__(self, bus, u"/")
- _interface = u"se.bsnet.fukt.Mandos"
-
- @dbus.service.signal(_interface, signature=u"o")
- def ClientAdded(self, objpath):
- "D-Bus signal"
- pass
-
- @dbus.service.signal(_interface, signature=u"ss")
- def ClientNotFound(self, fingerprint, address):
- "D-Bus signal"
- pass
-
- @dbus.service.signal(_interface, signature=u"os")
- def ClientRemoved(self, objpath, name):
- "D-Bus signal"
- pass
-
- @dbus.service.method(_interface, out_signature=u"ao")
- def GetAllClients(self):
- "D-Bus method"
- return dbus.Array(c.dbus_object_path
- for c in tcp_server.clients)
-
- @dbus.service.method(_interface,
- out_signature=u"a{oa{sv}}")
- def GetAllClientsWithProperties(self):
- "D-Bus method"
- return dbus.Dictionary(
- ((c.dbus_object_path, c.GetAll(u""))
- for c in tcp_server.clients),
- signature=u"oa{sv}")
-
- @dbus.service.method(_interface, in_signature=u"o")
- def RemoveClient(self, object_path):
- "D-Bus method"
- for c in tcp_server.clients:
- if c.dbus_object_path == object_path:
- tcp_server.clients.remove(c)
- c.remove_from_connection()
- # Don't signal anything except ClientRemoved
- c.disable(quiet=True)
- # Emit D-Bus signal
- self.ClientRemoved(object_path, c.name)
- return
- raise KeyError(object_path)
-
- del _interface
-
- mandos_dbus_service = MandosDBusService()
-
- def cleanup():
- "Cleanup function; run on exit"
- service.cleanup()
-
- while tcp_server.clients:
- client = tcp_server.clients.pop()
- if use_dbus:
- client.remove_from_connection()
- client.disable_hook = None
- # Don't signal anything except ClientRemoved
- client.disable(quiet=True)
- if use_dbus:
- # Emit D-Bus signal
- mandos_dbus_service.ClientRemoved(client.dbus_object_path,
- client.name)
-
- atexit.register(cleanup)
-
- for client in tcp_server.clients:
- if use_dbus:
- # Emit D-Bus signal
- mandos_dbus_service.ClientAdded(client.dbus_object_path)
- client.enable()
-
- tcp_server.enable()
- tcp_server.server_activate()
-
+ for client in clients:
+ client.start()
+
+ tcp_server = IPv6_TCPServer((server_settings["address"],
+ server_settings["port"]),
+ tcp_handler,
+ settings=server_settings,
+ clients=clients)
# Find out what port we got
service.port = tcp_server.socket.getsockname()[1]
- if use_ipv6:
- logger.info(u"Now listening on address %r, port %d,"
- " flowinfo %d, scope_id %d"
- % tcp_server.socket.getsockname())
- else: # IPv4
- logger.info(u"Now listening on address %r, port %d"
- % tcp_server.socket.getsockname())
+ logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
+ u" scope_id %d" % tcp_server.socket.getsockname())
#service.interface = tcp_server.socket.getsockname()[3]
try:
# From the Avahi example code
+ server.connect_to_signal("StateChanged", server_state_changed)
try:
- service.activate()
+ server_state_changed(server.GetState())
except dbus.exceptions.DBusException, error:
logger.critical(u"DBusException: %s", error)
- cleanup()
sys.exit(1)
# End of Avahi example code
gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
lambda *args, **kwargs:
- (tcp_server.handle_request
- (*args[2:], **kwargs) or True))
+ tcp_server.handle_request\
+ (*args[2:], **kwargs) or True)
- logger.debug(u"Starting main loop")
+ logger.debug("Starting main loop")
+ main_loop_started = True
main_loop.run()
except AvahiError, error:
- logger.critical(u"AvahiError: %s", error)
- cleanup()
+ logger.critical(u"AvahiError: %s" + unicode(error))
sys.exit(1)
except KeyboardInterrupt:
if debug:
- print >> sys.stderr
- logger.debug(u"Server received KeyboardInterrupt")
- logger.debug(u"Server exiting")
- # Must run before the D-Bus bus name gets deregistered
- cleanup()
+ print
if __name__ == '__main__':
main()
=== renamed file 'plugin-runner.c' => 'mandos-client.c'
--- plugin-runner.c 2010-09-26 21:27:28 +0000
+++ mandos-client.c 2008-08-07 21:45:41 +0000
@@ -1,9 +1,8 @@
-/* -*- coding: utf-8; mode: c; mode: orgtbl -*- */
+/* -*- coding: utf-8 -*- */
/*
* Mandos plugin runner - Run Mandos plugins
*
- * Copyright © 2008-2010 Teddy Hogeborn
- * Copyright © 2008-2010 Björn Påhlsson
+ * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -22,329 +21,145 @@
* Contact the authors at .
*/
-#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), getline(),
- asprintf(), O_CLOEXEC */
-#include /* size_t, NULL */
-#include /* malloc(), exit(), EXIT_SUCCESS,
- realloc() */
-#include /* bool, true, false */
-#include /* fileno(), fprintf(),
+#define _GNU_SOURCE /* TEMP_FAILURE_RETRY() */
+
+#include /* popen(), fileno(), fprintf(),
stderr, STDOUT_FILENO */
-#include /* DIR, fdopendir(), stat(), struct
- stat, waitpid(), WIFEXITED(),
- WEXITSTATUS(), wait(), pid_t,
- uid_t, gid_t, getuid(), getgid(),
- dirfd() */
-#include /* fd_set, select(), FD_ZERO(),
- FD_SET(), FD_ISSET(), FD_CLR */
-#include /* wait(), waitpid(), WIFEXITED(),
- WEXITSTATUS(), WTERMSIG(),
- WCOREDUMP() */
+#include /* and, or, not */
+#include /* DIR, opendir(), stat(),
+ struct stat, waitpid(),
+ WIFEXITED(), WEXITSTATUS(),
+ wait() */
+#include /* wait() */
+#include /* DIR, struct dirent, opendir(),
+ readdir(), closedir() */
#include /* struct stat, stat(), S_ISREG() */
-#include /* and, or, not */
-#include /* DIR, struct dirent, fdopendir(),
- readdir(), closedir(), dirfd() */
#include /* struct stat, stat(), S_ISREG(),
- fcntl(), setuid(), setgid(),
- F_GETFD, F_SETFD, FD_CLOEXEC,
- access(), pipe(), fork(), close()
- dup2(), STDOUT_FILENO, _exit(),
- execv(), write(), read(),
- close() */
-#include /* fcntl(), F_GETFD, F_SETFD,
- FD_CLOEXEC */
-#include /* strsep, strlen(), asprintf(),
- strsignal(), strcmp(), strncmp() */
+ fcntl() */
+#include /* fcntl() */
+#include /* NULL */
+#include /* EXIT_FAILURE */
+#include /* fd_set, select(), FD_ZERO(),
+ FD_SET(), FD_ISSET() */
+#include /* strlen(), strcpy(), strcat() */
+#include /* true */
+#include /* waitpid(), WIFEXITED(),
+ WEXITSTATUS() */
#include /* errno */
-#include /* struct argp_option, struct
- argp_state, struct argp,
- argp_parse(), ARGP_ERR_UNKNOWN,
- ARGP_KEY_END, ARGP_KEY_ARG,
- error_t */
-#include /* struct sigaction, sigemptyset(),
- sigaddset(), sigaction(),
- sigprocmask(), SIG_BLOCK, SIGCHLD,
- SIG_UNBLOCK, kill(), sig_atomic_t
- */
-#include /* errno, EBADF */
-#include /* intmax_t, PRIdMAX, strtoimax() */
-#include /* EX_OSERR, EX_USAGE, EX_IOERR,
- EX_CONFIG, EX_UNAVAILABLE, EX_OK */
-#include /* errno */
-#include /* error() */
+#include /* struct argp_option,
+ struct argp_state, struct argp,
+ argp_parse() */
#define BUFFER_SIZE 256
-#define PDIR "/lib/mandos/plugins.d"
-#define AFILE "/conf/conf.d/mandos/plugin-runner.conf"
-
-const char *argp_program_version = "plugin-runner " VERSION;
-const char *argp_program_bug_address = "";
-
-typedef struct plugin{
- char *name; /* can be NULL or any plugin name */
- char **argv;
- int argc;
- char **environ;
- int envc;
- bool disabled;
-
- /* Variables used for running processes*/
+struct process;
+
+typedef struct process{
pid_t pid;
int fd;
char *buffer;
size_t buffer_size;
size_t buffer_length;
bool eof;
- volatile sig_atomic_t completed;
+ bool completed;
int status;
+ struct process *next;
+} process;
+
+typedef struct plugin{
+ char *name; /* can be NULL or any plugin name */
+ char **argv;
+ int argc;
+ bool disabled;
struct plugin *next;
} plugin;
-static plugin *plugin_list = NULL;
-
-/* Gets an existing plugin based on name,
- or if none is found, creates a new one */
-static plugin *getplugin(char *name){
- /* Check for existing plugin with that name */
- for(plugin *p = plugin_list; p != NULL; p = p->next){
- if((p->name == name)
- or (p->name and name and (strcmp(p->name, name) == 0))){
+static plugin *getplugin(char *name, plugin **plugin_list){
+ for (plugin *p = *plugin_list; p != NULL; p = p->next){
+ if ((p->name == name)
+ or (p->name and name and (strcmp(p->name, name) == 0))){
return p;
}
}
/* Create a new plugin */
- plugin *new_plugin = NULL;
- do {
- new_plugin = malloc(sizeof(plugin));
- } while(new_plugin == NULL and errno == EINTR);
- if(new_plugin == NULL){
- return NULL;
- }
- char *copy_name = NULL;
- if(name != NULL){
- do {
- copy_name = strdup(name);
- } while(copy_name == NULL and errno == EINTR);
- if(copy_name == NULL){
- int e = errno;
- free(new_plugin);
- errno = e;
- return NULL;
- }
- }
-
- *new_plugin = (plugin){ .name = copy_name,
- .argc = 1,
- .disabled = false,
- .next = plugin_list };
-
- do {
- new_plugin->argv = malloc(sizeof(char *) * 2);
- } while(new_plugin->argv == NULL and errno == EINTR);
- if(new_plugin->argv == NULL){
- int e = errno;
- free(copy_name);
- free(new_plugin);
- errno = e;
- return NULL;
- }
- new_plugin->argv[0] = copy_name;
+ plugin *new_plugin = malloc(sizeof(plugin));
+ if (new_plugin == NULL){
+ perror("malloc");
+ exit(EXIT_FAILURE);
+ }
+ new_plugin->name = name;
+ new_plugin->argv = malloc(sizeof(char *) * 2);
+ if (new_plugin->argv == NULL){
+ perror("malloc");
+ exit(EXIT_FAILURE);
+ }
+ new_plugin->argv[0] = name;
new_plugin->argv[1] = NULL;
-
- do {
- new_plugin->environ = malloc(sizeof(char *));
- } while(new_plugin->environ == NULL and errno == EINTR);
- if(new_plugin->environ == NULL){
- int e = errno;
- free(copy_name);
- free(new_plugin->argv);
- free(new_plugin);
- errno = e;
- return NULL;
- }
- new_plugin->environ[0] = NULL;
-
+ new_plugin->argc = 1;
+ new_plugin->disabled = false;
+ new_plugin->next = *plugin_list;
/* Append the new plugin to the list */
- plugin_list = new_plugin;
+ *plugin_list = new_plugin;
return new_plugin;
}
-/* Helper function for add_argument and add_environment */
-static bool add_to_char_array(const char *new, char ***array,
- int *len){
- /* Resize the pointed-to array to hold one more pointer */
- do {
- *array = realloc(*array, sizeof(char *)
- * (size_t) ((*len) + 2));
- } while(*array == NULL and errno == EINTR);
- /* Malloc check */
- if(*array == NULL){
- return false;
- }
- /* Make a copy of the new string */
- char *copy;
- do {
- copy = strdup(new);
- } while(copy == NULL and errno == EINTR);
- if(copy == NULL){
- return false;
- }
- /* Insert the copy */
- (*array)[*len] = copy;
- (*len)++;
- /* Add a new terminating NULL pointer to the last element */
- (*array)[*len] = NULL;
- return true;
-}
-
-/* Add to a plugin's argument vector */
-static bool add_argument(plugin *p, const char *arg){
- if(p == NULL){
- return false;
- }
- return add_to_char_array(arg, &(p->argv), &(p->argc));
-}
-
-/* Add to a plugin's environment */
-static bool add_environment(plugin *p, const char *def, bool replace){
- if(p == NULL){
- return false;
- }
- /* namelen = length of name of environment variable */
- size_t namelen = (size_t)(strchrnul(def, '=') - def);
- /* Search for this environment variable */
- for(char **e = p->environ; *e != NULL; e++){
- if(strncmp(*e, def, namelen + 1) == 0){
- /* It already exists */
- if(replace){
- char *new;
- do {
- new = realloc(*e, strlen(def) + 1);
- } while(new == NULL and errno == EINTR);
- if(new == NULL){
- return false;
- }
- *e = new;
- strcpy(*e, def);
- }
- return true;
- }
- }
- return add_to_char_array(def, &(p->environ), &(p->envc));
+static void addargument(plugin *p, char *arg){
+ p->argv[p->argc] = arg;
+ p->argv = realloc(p->argv, sizeof(char *) * (size_t)(p->argc + 2));
+ if (p->argv == NULL){
+ perror("malloc");
+ exit(EXIT_FAILURE);
+ }
+ p->argc++;
+ p->argv[p->argc] = NULL;
}
/*
* Based on the example in the GNU LibC manual chapter 13.13 "File
* Descriptor Flags".
- | [[info:libc:Descriptor%20Flags][File Descriptor Flags]] |
+ * *Note File Descriptor Flags:(libc)Descriptor Flags.
*/
-static int set_cloexec_flag(int fd){
- int ret = (int)TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD, 0));
+static int set_cloexec_flag(int fd)
+{
+ int ret = fcntl(fd, F_GETFD, 0);
/* If reading the flags failed, return error indication now. */
if(ret < 0){
return ret;
}
/* Store modified flag word in the descriptor. */
- return (int)TEMP_FAILURE_RETRY(fcntl(fd, F_SETFD,
- ret | FD_CLOEXEC));
+ return fcntl(fd, F_SETFD, ret | FD_CLOEXEC);
}
-
-/* Mark processes as completed when they exit, and save their exit
+const char *argp_program_version = "plugbasedclient 0.9";
+const char *argp_program_bug_address = "";
+
+process *process_list = NULL;
+
+/* Mark a process as completed when it exits, and save its exit
status. */
-static void handle_sigchld(__attribute__((unused)) int sig){
- int old_errno = errno;
- while(true){
- plugin *proc = plugin_list;
- int status;
- pid_t pid = waitpid(-1, &status, WNOHANG);
- if(pid == 0){
- /* Only still running child processes */
- break;
- }
- if(pid == -1){
- if(errno == ECHILD){
- /* No child processes */
- break;
- }
- error(0, errno, "waitpid");
- }
-
- /* A child exited, find it in process_list */
- while(proc != NULL and proc->pid != pid){
- proc = proc->next;
- }
- if(proc == NULL){
- /* Process not found in process list */
- continue;
- }
- proc->status = status;
- proc->completed = 1;
- }
- errno = old_errno;
-}
-
-/* Prints out a password to stdout */
-static bool print_out_password(const char *buffer, size_t length){
- ssize_t ret;
- for(size_t written = 0; written < length; written += (size_t)ret){
- ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buffer + written,
- length - written));
- if(ret < 0){
- return false;
- }
- }
- return true;
-}
-
-/* Removes and free a plugin from the plugin list */
-static void free_plugin(plugin *plugin_node){
-
- for(char **arg = plugin_node->argv; *arg != NULL; arg++){
- free(*arg);
- }
- free(plugin_node->argv);
- for(char **env = plugin_node->environ; *env != NULL; env++){
- free(*env);
- }
- free(plugin_node->environ);
- free(plugin_node->buffer);
-
- /* Removes the plugin from the singly-linked list */
- if(plugin_node == plugin_list){
- /* First one - simple */
- plugin_list = plugin_list->next;
- } else {
- /* Second one or later */
- for(plugin *p = plugin_list; p != NULL; p = p->next){
- if(p->next == plugin_node){
- p->next = plugin_node->next;
- break;
- }
- }
- }
-
- free(plugin_node);
-}
-
-static void free_plugin_list(void){
- while(plugin_list != NULL){
- free_plugin(plugin_list);
- }
+void handle_sigchld(__attribute__((unused)) int sig){
+ process *proc = process_list;
+ int status;
+ pid_t pid = wait(&status);
+ while(proc != NULL and proc->pid != pid){
+ proc = proc->next;
+ }
+ if(proc == NULL){
+ /* Process not found in process list */
+ return;
+ }
+ proc->status = status;
+ proc->completed = true;
}
int main(int argc, char *argv[]){
- char *plugindir = NULL;
- char *argfile = NULL;
- FILE *conffp;
+ const char *plugindir = "/conf/conf.d/mandos/plugins.d";
size_t d_name_len;
DIR *dir = NULL;
struct dirent *dirst;
struct stat st;
fd_set rfds_all;
int ret, maxfd = 0;
- ssize_t sret;
uid_t uid = 65534;
gid_t gid = 65534;
bool debug = false;
@@ -352,22 +167,20 @@
struct sigaction old_sigchld_action;
struct sigaction sigchld_action = { .sa_handler = handle_sigchld,
.sa_flags = SA_NOCLDSTOP };
- char **custom_argv = NULL;
- int custom_argc = 0;
+ char *plus_options = NULL;
+ char **plus_argv = NULL;
/* Establish a signal handler */
sigemptyset(&sigchld_action.sa_mask);
ret = sigaddset(&sigchld_action.sa_mask, SIGCHLD);
- if(ret == -1){
- error(0, errno, "sigaddset");
- exitstatus = EX_OSERR;
- goto fallback;
+ if(ret < 0){
+ perror("sigaddset");
+ exit(EXIT_FAILURE);
}
ret = sigaction(SIGCHLD, &sigchld_action, &old_sigchld_action);
- if(ret == -1){
- error(0, errno, "sigaction");
- exitstatus = EX_OSERR;
- goto fallback;
+ if(ret < 0){
+ perror("sigaction");
+ exit(EXIT_FAILURE);
}
/* The options we understand. */
@@ -375,357 +188,123 @@
{ .name = "global-options", .key = 'g',
.arg = "OPTION[,OPTION[,...]]",
.doc = "Options passed to all plugins" },
- { .name = "global-env", .key = 'G',
- .arg = "VAR=value",
- .doc = "Environment variable passed to all plugins" },
{ .name = "options-for", .key = 'o',
.arg = "PLUGIN:OPTION[,OPTION[,...]]",
.doc = "Options passed only to specified plugin" },
- { .name = "env-for", .key = 'E',
- .arg = "PLUGIN:ENV=value",
- .doc = "Environment variable passed to specified plugin" },
{ .name = "disable", .key = 'd',
.arg = "PLUGIN",
.doc = "Disable a specific plugin", .group = 1 },
- { .name = "enable", .key = 'e',
- .arg = "PLUGIN",
- .doc = "Enable a specific plugin", .group = 1 },
{ .name = "plugin-dir", .key = 128,
.arg = "DIRECTORY",
.doc = "Specify a different plugin directory", .group = 2 },
- { .name = "config-file", .key = 129,
- .arg = "FILE",
- .doc = "Specify a different configuration file", .group = 2 },
- { .name = "userid", .key = 130,
- .arg = "ID", .flags = 0,
- .doc = "User ID the plugins will run as", .group = 3 },
- { .name = "groupid", .key = 131,
- .arg = "ID", .flags = 0,
- .doc = "Group ID the plugins will run as", .group = 3 },
- { .name = "debug", .key = 132,
- .doc = "Debug mode", .group = 4 },
- /*
- * These reproduce what we would get without ARGP_NO_HELP
- */
- { .name = "help", .key = '?',
- .doc = "Give this help list", .group = -1 },
- { .name = "usage", .key = -3,
- .doc = "Give a short usage message", .group = -1 },
- { .name = "version", .key = 'V',
- .doc = "Print program version", .group = -1 },
+ { .name = "userid", .key = 129,
+ .arg = "ID", .flags = 0,
+ .doc = "User ID the plugins will run as", .group = 2 },
+ { .name = "groupid", .key = 130,
+ .arg = "ID", .flags = 0,
+ .doc = "Group ID the plugins will run as", .group = 2 },
+ { .name = "debug", .key = 131,
+ .doc = "Debug mode", .group = 3 },
{ .name = NULL }
};
- error_t parse_opt(int key, char *arg, struct argp_state *state){
- errno = 0;
- switch(key){
- char *tmp;
- intmax_t tmpmax;
- case 'g': /* --global-options */
- {
- char *plugin_option;
- while((plugin_option = strsep(&arg, ",")) != NULL){
- if(not add_argument(getplugin(NULL), plugin_option)){
- break;
- }
- }
- }
- break;
- case 'G': /* --global-env */
- add_environment(getplugin(NULL), arg, true);
- break;
- case 'o': /* --options-for */
- {
- char *option_list = strchr(arg, ':');
- if(option_list == NULL){
- argp_error(state, "No colon in \"%s\"", arg);
- errno = EINVAL;
- break;
- }
- *option_list = '\0';
- option_list++;
- if(arg[0] == '\0'){
- argp_error(state, "Empty plugin name");
- errno = EINVAL;
- break;
- }
- char *option;
- while((option = strsep(&option_list, ",")) != NULL){
- if(not add_argument(getplugin(arg), option)){
- break;
- }
- }
- }
- break;
- case 'E': /* --env-for */
- {
- char *envdef = strchr(arg, ':');
- if(envdef == NULL){
- argp_error(state, "No colon in \"%s\"", arg);
- errno = EINVAL;
- break;
- }
- *envdef = '\0';
- envdef++;
- if(arg[0] == '\0'){
- argp_error(state, "Empty plugin name");
- errno = EINVAL;
- break;
- }
- add_environment(getplugin(arg), envdef, true);
- }
- break;
- case 'd': /* --disable */
- {
- plugin *p = getplugin(arg);
- if(p != NULL){
- p->disabled = true;
- }
- }
- break;
- case 'e': /* --enable */
- {
- plugin *p = getplugin(arg);
- if(p != NULL){
- p->disabled = false;
- }
- }
- break;
- case 128: /* --plugin-dir */
- free(plugindir);
- plugindir = strdup(arg);
- break;
- case 129: /* --config-file */
- /* This is already done by parse_opt_config_file() */
- break;
- case 130: /* --userid */
- tmpmax = strtoimax(arg, &tmp, 10);
- if(errno != 0 or tmp == arg or *tmp != '\0'
- or tmpmax != (uid_t)tmpmax){
- argp_error(state, "Bad user ID number: \"%s\", using %"
- PRIdMAX, arg, (intmax_t)uid);
- break;
- }
- uid = (uid_t)tmpmax;
- break;
- case 131: /* --groupid */
- tmpmax = strtoimax(arg, &tmp, 10);
- if(errno != 0 or tmp == arg or *tmp != '\0'
- or tmpmax != (gid_t)tmpmax){
- argp_error(state, "Bad group ID number: \"%s\", using %"
- PRIdMAX, arg, (intmax_t)gid);
- break;
- }
- gid = (gid_t)tmpmax;
- break;
- case 132: /* --debug */
+ error_t parse_opt (int key, char *arg, struct argp_state *state) {
+ /* Get the INPUT argument from `argp_parse', which we know is a
+ pointer to our plugin list pointer. */
+ plugin **plugins = state->input;
+ switch (key) {
+ case 'g':
+ if (arg != NULL){
+ char *p = strtok(arg, ",");
+ do{
+ addargument(getplugin(NULL, plugins), p);
+ p = strtok(NULL, ",");
+ } while (p != NULL);
+ }
+ break;
+ case 'o':
+ if (arg != NULL){
+ char *name = strtok(arg, ":");
+ char *p = strtok(NULL, ":");
+ if(p != NULL){
+ p = strtok(p, ",");
+ do{
+ addargument(getplugin(name, plugins), p);
+ p = strtok(NULL, ",");
+ } while (p != NULL);
+ }
+ }
+ break;
+ case 'd':
+ if (arg != NULL){
+ getplugin(arg, plugins)->disabled = true;
+ }
+ break;
+ case 128:
+ plugindir = arg;
+ break;
+ case 129:
+ uid = (uid_t)strtol(arg, NULL, 10);
+ break;
+ case 130:
+ gid = (gid_t)strtol(arg, NULL, 10);
+ break;
+ case 131:
debug = true;
break;
- /*
- * These reproduce what we would get without ARGP_NO_HELP
- */
- case '?': /* --help */
- state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
- argp_state_help(state, state->out_stream, ARGP_HELP_STD_HELP);
- case -3: /* --usage */
- state->flags &= ~(unsigned int)ARGP_NO_EXIT; /* force exit */
- argp_state_help(state, state->out_stream,
- ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
- case 'V': /* --version */
- fprintf(state->out_stream, "%s\n", argp_program_version);
- exit(EXIT_SUCCESS);
- break;
-/*
- * When adding more options before this line, remember to also add a
- * "case" to the "parse_opt_config_file" function below.
- */
case ARGP_KEY_ARG:
- /* Cryptsetup always passes an argument, which is an empty
- string if "none" was specified in /etc/crypttab. So if
- argument was empty, we ignore it silently. */
- if(arg[0] == '\0'){
- break;
+ if(plus_options != NULL or arg == NULL or arg[0] != '+'){
+ argp_usage (state);
}
- default:
- return ARGP_ERR_UNKNOWN;
- }
- return errno; /* Set to 0 at start */
- }
-
- /* This option parser is the same as parse_opt() above, except it
- ignores everything but the --config-file option. */
- error_t parse_opt_config_file(int key, char *arg,
- __attribute__((unused))
- struct argp_state *state){
- errno = 0;
- switch(key){
- case 'g': /* --global-options */
- case 'G': /* --global-env */
- case 'o': /* --options-for */
- case 'E': /* --env-for */
- case 'd': /* --disable */
- case 'e': /* --enable */
- case 128: /* --plugin-dir */
- break;
- case 129: /* --config-file */
- free(argfile);
- argfile = strdup(arg);
- break;
- case 130: /* --userid */
- case 131: /* --groupid */
- case 132: /* --debug */
- case '?': /* --help */
- case -3: /* --usage */
- case 'V': /* --version */
- case ARGP_KEY_ARG:
- break;
- default:
- return ARGP_ERR_UNKNOWN;
- }
- return errno;
- }
-
- struct argp argp = { .options = options,
- .parser = parse_opt_config_file,
- .args_doc = "",
+ plus_options = arg;
+ break;
+ case ARGP_KEY_END:
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+
+ plugin *plugin_list = NULL;
+
+ struct argp argp = { .options = options, .parser = parse_opt,
+ .args_doc = "[+PLUS_SEPARATED_OPTIONS]",
.doc = "Mandos plugin runner -- Run plugins" };
- /* Parse using parse_opt_config_file() in order to get the custom
- config file location, if any. */
- ret = argp_parse(&argp, argc, argv,
- ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
- NULL, NULL);
- switch(ret){
- case 0:
- break;
- case ENOMEM:
- default:
- errno = ret;
- error(0, errno, "argp_parse");
- exitstatus = EX_OSERR;
- goto fallback;
- case EINVAL:
- exitstatus = EX_USAGE;
- goto fallback;
- }
-
- /* Reset to the normal argument parser */
- argp.parser = parse_opt;
-
- /* Open the configfile if available */
- if(argfile == NULL){
- conffp = fopen(AFILE, "r");
- } else {
- conffp = fopen(argfile, "r");
- }
- if(conffp != NULL){
- char *org_line = NULL;
- char *p, *arg, *new_arg, *line;
- size_t size = 0;
- const char whitespace_delims[] = " \r\t\f\v\n";
- const char comment_delim[] = "#";
-
- custom_argc = 1;
- custom_argv = malloc(sizeof(char*) * 2);
- if(custom_argv == NULL){
- error(0, errno, "malloc");
- exitstatus = EX_OSERR;
- goto fallback;
- }
- custom_argv[0] = argv[0];
- custom_argv[1] = NULL;
-
- /* for each line in the config file, strip whitespace and ignore
- commented text */
- while(true){
- sret = getline(&org_line, &size, conffp);
- if(sret == -1){
- break;
- }
-
- line = org_line;
- arg = strsep(&line, comment_delim);
- while((p = strsep(&arg, whitespace_delims)) != NULL){
- if(p[0] == '\0'){
- continue;
- }
- new_arg = strdup(p);
- if(new_arg == NULL){
- error(0, errno, "strdup");
- exitstatus = EX_OSERR;
- free(org_line);
- goto fallback;
- }
-
- custom_argc += 1;
- custom_argv = realloc(custom_argv, sizeof(char *)
- * ((unsigned int) custom_argc + 1));
- if(custom_argv == NULL){
- error(0, errno, "realloc");
- exitstatus = EX_OSERR;
- free(org_line);
- goto fallback;
- }
- custom_argv[custom_argc-1] = new_arg;
- custom_argv[custom_argc] = NULL;
- }
- }
- do {
- ret = fclose(conffp);
- } while(ret == EOF and errno == EINTR);
- if(ret == EOF){
- error(0, errno, "fclose");
- exitstatus = EX_IOERR;
- goto fallback;
- }
- free(org_line);
- } else {
- /* Check for harmful errors and go to fallback. Other errors might
- not affect opening plugins */
- if(errno == EMFILE or errno == ENFILE or errno == ENOMEM){
- error(0, errno, "fopen");
- exitstatus = EX_OSERR;
- goto fallback;
- }
- }
- /* If there were any arguments from the configuration file, pass
- them to parser as command line arguments */
- if(custom_argv != NULL){
- ret = argp_parse(&argp, custom_argc, custom_argv,
- ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
- NULL, NULL);
- switch(ret){
- case 0:
- break;
- case ENOMEM:
- default:
- errno = ret;
- error(0, errno, "argp_parse");
- exitstatus = EX_OSERR;
- goto fallback;
- case EINVAL:
- exitstatus = EX_CONFIG;
- goto fallback;
- }
- }
-
- /* Parse actual command line arguments, to let them override the
- config file */
- ret = argp_parse(&argp, argc, argv,
- ARGP_IN_ORDER | ARGP_NO_EXIT | ARGP_NO_HELP,
- NULL, NULL);
- switch(ret){
- case 0:
- break;
- case ENOMEM:
- default:
- errno = ret;
- error(0, errno, "argp_parse");
- exitstatus = EX_OSERR;
- goto fallback;
- case EINVAL:
- exitstatus = EX_USAGE;
- goto fallback;
+ argp_parse (&argp, argc, argv, 0, 0, &plugin_list);
+
+ if(plus_options){
+ /* This is a mangled argument in the form of
+ "+--option+--other-option=parameter+--yet-another-option", etc */
+ /* Make new argc and argv vars, and call argp_parse() again. */
+ plus_options++; /* skip the first '+' character */
+ const char delims[] = "+";
+ char *arg;
+ int new_argc = 1;
+ plus_argv = malloc(sizeof(char*) * 2);
+ if(plus_argv == NULL){
+ perror("malloc");
+ exitstatus = EXIT_FAILURE;
+ goto end;
+ }
+ plus_argv[0] = argv[0];
+ plus_argv[1] = NULL;
+ arg = strtok(plus_options, delims); /* Get first argument */
+ while(arg != NULL){
+ new_argc++;
+ plus_argv = realloc(plus_argv, sizeof(char *)
+ * ((unsigned int) new_argc + 1));
+ if(plus_argv == NULL){
+ perror("malloc");
+ exitstatus = EXIT_FAILURE;
+ goto end;
+ }
+ plus_argv[new_argc-1] = arg;
+ plus_argv[new_argc] = NULL;
+ arg = strtok(NULL, delims); /* Get next argument */
+ }
+ argp_parse (&argp, new_argc, plus_argv, 0, 0, &plugin_list);
}
if(debug){
@@ -735,90 +314,52 @@
for(char **a = p->argv; *a != NULL; a++){
fprintf(stderr, "\tArg: %s\n", *a);
}
- fprintf(stderr, "...and %d environment variables\n", p->envc);
- for(char **a = p->environ; *a != NULL; a++){
- fprintf(stderr, "\t%s\n", *a);
- }
}
}
- /* Strip permissions down to nobody */
+ ret = setuid(uid);
+ if (ret == -1){
+ perror("setuid");
+ }
+
setgid(gid);
- if(ret == -1){
- error(0, errno, "setgid");
- }
- ret = setuid(uid);
- if(ret == -1){
- error(0, errno, "setuid");
- }
-
- /* Open plugin directory with close_on_exec flag */
+ if (ret == -1){
+ perror("setgid");
+ }
+
+ dir = opendir(plugindir);
+ if(dir == NULL){
+ perror("Could not open plugin dir");
+ exitstatus = EXIT_FAILURE;
+ goto end;
+ }
+
+ /* Set the FD_CLOEXEC flag on the directory, if possible */
{
- int dir_fd = -1;
- if(plugindir == NULL){
- dir_fd = open(PDIR, O_RDONLY |
-#ifdef O_CLOEXEC
- O_CLOEXEC
-#else /* not O_CLOEXEC */
- 0
-#endif /* not O_CLOEXEC */
- );
- } else {
- dir_fd = open(plugindir, O_RDONLY |
-#ifdef O_CLOEXEC
- O_CLOEXEC
-#else /* not O_CLOEXEC */
- 0
-#endif /* not O_CLOEXEC */
- );
- }
- if(dir_fd == -1){
- error(0, errno, "Could not open plugin dir");
- exitstatus = EX_UNAVAILABLE;
- goto fallback;
- }
-
-#ifndef O_CLOEXEC
- /* Set the FD_CLOEXEC flag on the directory */
- ret = set_cloexec_flag(dir_fd);
- if(ret < 0){
- error(0, errno, "set_cloexec_flag");
- TEMP_FAILURE_RETRY(close(dir_fd));
- exitstatus = EX_OSERR;
- goto fallback;
- }
-#endif /* O_CLOEXEC */
-
- dir = fdopendir(dir_fd);
- if(dir == NULL){
- error(0, errno, "Could not open plugin dir");
- TEMP_FAILURE_RETRY(close(dir_fd));
- exitstatus = EX_OSERR;
- goto fallback;
+ int dir_fd = dirfd(dir);
+ if(dir_fd >= 0){
+ ret = set_cloexec_flag(dir_fd);
+ if(ret < 0){
+ perror("set_cloexec_flag");
+ exitstatus = EXIT_FAILURE;
+ goto end;
+ }
}
}
FD_ZERO(&rfds_all);
- /* Read and execute any executable in the plugin directory*/
while(true){
- do {
- dirst = readdir(dir);
- } while(dirst == NULL and errno == EINTR);
+ dirst = readdir(dir);
- /* All directory entries have been processed */
+ // All directory entries have been processed
if(dirst == NULL){
- if(errno == EBADF){
- error(0, errno, "readdir");
- exitstatus = EX_IOERR;
- goto fallback;
- }
break;
}
d_name_len = strlen(dirst->d_name);
- /* Ignore dotfiles, backup files and other junk */
+ // Ignore dotfiles, backup files and other junk
{
bool bad_name = false;
@@ -826,7 +367,6 @@
const char const *bad_suffixes[] = { "~", "#", ".dpkg-new",
".dpkg-old",
- ".dpkg-bak",
".dpkg-divert", NULL };
for(const char **pre = bad_prefixes; *pre != NULL; pre++){
size_t pre_len = strlen(*pre);
@@ -840,13 +380,15 @@
break;
}
}
+
if(bad_name){
continue;
}
+
for(const char **suf = bad_suffixes; *suf != NULL; suf++){
size_t suf_len = strlen(*suf);
if((d_name_len >= suf_len)
- and (strcmp((dirst->d_name) + d_name_len-suf_len, *suf)
+ and (strcmp((dirst->d_name)+d_name_len-suf_len, *suf)
== 0)){
if(debug){
fprintf(stderr, "Ignoring plugin dir entry \"%s\""
@@ -862,30 +404,19 @@
}
}
- char *filename;
- if(plugindir == NULL){
- ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, PDIR "/%s",
- dirst->d_name));
- } else {
- ret = (int)TEMP_FAILURE_RETRY(asprintf(&filename, "%s/%s",
- plugindir,
- dirst->d_name));
- }
- if(ret < 0){
- error(0, errno, "asprintf");
- continue;
- }
-
- ret = (int)TEMP_FAILURE_RETRY(stat(filename, &st));
- if(ret == -1){
- error(0, errno, "stat");
- free(filename);
- continue;
- }
-
- /* Ignore non-executable files */
- if(not S_ISREG(st.st_mode)
- or (TEMP_FAILURE_RETRY(access(filename, X_OK)) != 0)){
+ char *filename = malloc(d_name_len + strlen(plugindir) + 2);
+ if (filename == NULL){
+ perror("malloc");
+ exitstatus = EXIT_FAILURE;
+ goto end;
+ }
+ strcpy(filename, plugindir); /* Spurious warning */
+ strcat(filename, "/"); /* Spurious warning */
+ strcat(filename, dirst->d_name); /* Spurious warning */
+
+ stat(filename, &st);
+
+ if (not S_ISREG(st.st_mode) or (access(filename, X_OK) != 0)){
if(debug){
fprintf(stderr, "Ignoring plugin dir entry \"%s\""
" with bad type or mode\n", filename);
@@ -893,14 +424,7 @@
free(filename);
continue;
}
-
- plugin *p = getplugin(dirst->d_name);
- if(p == NULL){
- error(0, errno, "getplugin");
- free(filename);
- continue;
- }
- if(p->disabled){
+ if(getplugin(dirst->d_name, &plugin_list)->disabled){
if(debug){
fprintf(stderr, "Ignoring disabled plugin \"%s\"\n",
dirst->d_name);
@@ -908,351 +432,265 @@
free(filename);
continue;
}
+ plugin *p = getplugin(dirst->d_name, &plugin_list);
{
/* Add global arguments to argument list for this plugin */
- plugin *g = getplugin(NULL);
- if(g != NULL){
- for(char **a = g->argv + 1; *a != NULL; a++){
- if(not add_argument(p, *a)){
- error(0, errno, "add_argument");
- }
- }
- /* Add global environment variables */
- for(char **e = g->environ; *e != NULL; e++){
- if(not add_environment(p, *e, false)){
- error(0, errno, "add_environment");
- }
- }
- }
- }
- /* If this plugin has any environment variables, we will call
- using execve and need to duplicate the environment from this
- process, too. */
- if(p->environ[0] != NULL){
- for(char **e = environ; *e != NULL; e++){
- if(not add_environment(p, *e, false)){
- error(0, errno, "add_environment");
- }
- }
- }
-
- int pipefd[2];
- ret = (int)TEMP_FAILURE_RETRY(pipe(pipefd));
- if(ret == -1){
- error(0, errno, "pipe");
- exitstatus = EX_OSERR;
- goto fallback;
- }
- /* Ask OS to automatic close the pipe on exec */
+ plugin *g = getplugin(NULL, &plugin_list);
+ for(char **a = g->argv + 1; *a != NULL; a++){
+ addargument(p, *a);
+ }
+ }
+ int pipefd[2];
+ ret = pipe(pipefd);
+ if (ret == -1){
+ perror("pipe");
+ exitstatus = EXIT_FAILURE;
+ goto end;
+ }
ret = set_cloexec_flag(pipefd[0]);
if(ret < 0){
- error(0, errno, "set_cloexec_flag");
- exitstatus = EX_OSERR;
- goto fallback;
+ perror("set_cloexec_flag");
+ exitstatus = EXIT_FAILURE;
+ goto end;
}
ret = set_cloexec_flag(pipefd[1]);
if(ret < 0){
- error(0, errno, "set_cloexec_flag");
- exitstatus = EX_OSERR;
- goto fallback;
+ perror("set_cloexec_flag");
+ exitstatus = EXIT_FAILURE;
+ goto end;
}
/* Block SIGCHLD until process is safely in process list */
- ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_BLOCK,
- &sigchld_action.sa_mask,
- NULL));
+ ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
if(ret < 0){
- error(0, errno, "sigprocmask");
- exitstatus = EX_OSERR;
- goto fallback;
- }
- /* Starting a new process to be watched */
- pid_t pid;
- do {
- pid = fork();
- } while(pid == -1 and errno == EINTR);
- if(pid == -1){
- error(0, errno, "fork");
- exitstatus = EX_OSERR;
- goto fallback;
- }
+ perror("sigprocmask");
+ exitstatus = EXIT_FAILURE;
+ goto end;
+ }
+ // Starting a new process to be watched
+ pid_t pid = fork();
if(pid == 0){
/* this is the child process */
ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
if(ret < 0){
- error(0, errno, "sigaction");
- _exit(EX_OSERR);
+ perror("sigaction");
+ _exit(EXIT_FAILURE);
}
- ret = sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
+ ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
if(ret < 0){
- error(0, errno, "sigprocmask");
- _exit(EX_OSERR);
- }
-
- ret = dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
- if(ret == -1){
- error(0, errno, "dup2");
- _exit(EX_OSERR);
- }
+ perror("sigprocmask");
+ _exit(EXIT_FAILURE);
+ }
+ dup2(pipefd[1], STDOUT_FILENO); /* replace our stdout */
if(dirfd(dir) < 0){
/* If dir has no file descriptor, we could not set FD_CLOEXEC
above and must now close it manually here. */
closedir(dir);
}
- if(p->environ[0] == NULL){
- if(execv(filename, p->argv) < 0){
- error(0, errno, "execv for %s", filename);
- _exit(EX_OSERR);
- }
- } else {
- if(execve(filename, p->argv, p->environ) < 0){
- error(0, errno, "execve for %s", filename);
- _exit(EX_OSERR);
- }
+ if(execv(filename, p->argv) < 0){
+ perror("execv");
+ _exit(EXIT_FAILURE);
}
/* no return */
}
- /* Parent process */
- TEMP_FAILURE_RETRY(close(pipefd[1])); /* Close unused write end of
- pipe */
+ /* parent process */
free(filename);
- plugin *new_plugin = getplugin(dirst->d_name);
- if(new_plugin == NULL){
- error(0, errno, "getplugin");
- ret = (int)(TEMP_FAILURE_RETRY
- (sigprocmask(SIG_UNBLOCK, &sigchld_action.sa_mask,
- NULL)));
+ close(pipefd[1]); /* close unused write end of pipe */
+ process *new_process = malloc(sizeof(process));
+ if (new_process == NULL){
+ perror("malloc");
+ ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
if(ret < 0){
- error(0, errno, "sigprocmask");
+ perror("sigprocmask");
}
- exitstatus = EX_OSERR;
- goto fallback;
+ exitstatus = EXIT_FAILURE;
+ goto end;
}
- new_plugin->pid = pid;
- new_plugin->fd = pipefd[0];
-
+ *new_process = (struct process){ .pid = pid,
+ .fd = pipefd[0],
+ .next = process_list };
+ // List handling
+ process_list = new_process;
/* Unblock SIGCHLD so signal handler can be run if this process
has already completed */
- ret = (int)TEMP_FAILURE_RETRY(sigprocmask(SIG_UNBLOCK,
- &sigchld_action.sa_mask,
- NULL));
+ ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask, NULL);
if(ret < 0){
- error(0, errno, "sigprocmask");
- exitstatus = EX_OSERR;
- goto fallback;
- }
-
- FD_SET(new_plugin->fd, &rfds_all); /* Spurious warning from
- -Wconversion */
-
- if(maxfd < new_plugin->fd){
- maxfd = new_plugin->fd;
- }
- }
-
- TEMP_FAILURE_RETRY(closedir(dir));
+ perror("sigprocmask");
+ exitstatus = EXIT_FAILURE;
+ goto end;
+ }
+
+ FD_SET(new_process->fd, &rfds_all);
+
+ if (maxfd < new_process->fd){
+ maxfd = new_process->fd;
+ }
+
+ }
+
+ /* Free the plugin list */
+ for(plugin *next; plugin_list != NULL; plugin_list = next){
+ next = plugin_list->next;
+ free(plugin_list->argv);
+ free(plugin_list);
+ }
+
+ closedir(dir);
dir = NULL;
- free_plugin(getplugin(NULL));
- for(plugin *p = plugin_list; p != NULL; p = p->next){
- if(p->pid != 0){
- break;
- }
- if(p->next == NULL){
- fprintf(stderr, "No plugin processes started. Incorrect plugin"
- " directory?\n");
- free_plugin_list();
- }
+ if (process_list == NULL){
+ fprintf(stderr, "No plugin processes started, exiting\n");
+ exitstatus = EXIT_FAILURE;
+ goto end;
}
-
- /* Main loop while running plugins exist */
- while(plugin_list){
+ while(process_list){
fd_set rfds = rfds_all;
int select_ret = select(maxfd+1, &rfds, NULL, NULL, NULL);
- if(select_ret == -1 and errno != EINTR){
- error(0, errno, "select");
- exitstatus = EX_OSERR;
- goto fallback;
+ if (select_ret == -1){
+ perror("select");
+ exitstatus = EXIT_FAILURE;
+ goto end;
}
/* OK, now either a process completed, or something can be read
from one of them */
- for(plugin *proc = plugin_list; proc != NULL;){
+ for(process *proc = process_list; proc ; proc = proc->next){
/* Is this process completely done? */
- if(proc->completed and proc->eof){
+ if(proc->eof and proc->completed){
/* Only accept the plugin output if it exited cleanly */
if(not WIFEXITED(proc->status)
or WEXITSTATUS(proc->status) != 0){
/* Bad exit by plugin */
-
if(debug){
if(WIFEXITED(proc->status)){
- fprintf(stderr, "Plugin %s [%" PRIdMAX "] exited with"
- " status %d\n", proc->name,
- (intmax_t) (proc->pid),
- WEXITSTATUS(proc->status));
- } else if(WIFSIGNALED(proc->status)){
- fprintf(stderr, "Plugin %s [%" PRIdMAX "] killed by"
- " signal %d: %s\n", proc->name,
- (intmax_t) (proc->pid),
- WTERMSIG(proc->status),
- strsignal(WTERMSIG(proc->status)));
+ fprintf(stderr, "Plugin %d exited with status %d\n",
+ proc->pid, WEXITSTATUS(proc->status));
+ } else if(WIFSIGNALED(proc->status)) {
+ fprintf(stderr, "Plugin %d killed by signal %d\n",
+ proc->pid, WTERMSIG(proc->status));
} else if(WCOREDUMP(proc->status)){
- fprintf(stderr, "Plugin %s [%" PRIdMAX "] dumped"
- " core\n", proc->name, (intmax_t) (proc->pid));
+ fprintf(stderr, "Plugin %d dumped core\n", proc->pid);
}
}
-
/* Remove the plugin */
- FD_CLR(proc->fd, &rfds_all); /* Spurious warning from
- -Wconversion */
-
+ FD_CLR(proc->fd, &rfds_all);
/* Block signal while modifying process_list */
- ret = (int)TEMP_FAILURE_RETRY(sigprocmask
- (SIG_BLOCK,
- &sigchld_action.sa_mask,
- NULL));
+ ret = sigprocmask (SIG_BLOCK, &sigchld_action.sa_mask, NULL);
if(ret < 0){
- error(0, errno, "sigprocmask");
- exitstatus = EX_OSERR;
- goto fallback;
- }
-
- plugin *next_plugin = proc->next;
- free_plugin(proc);
- proc = next_plugin;
-
+ perror("sigprocmask");
+ exitstatus = EXIT_FAILURE;
+ goto end;
+ }
+ /* Delete this process entry from the list */
+ if(process_list == proc){
+ /* First one - simple */
+ process_list = proc->next;
+ } else {
+ /* Second one or later */
+ for(process *p = process_list; p != NULL; p = p->next){
+ if(p->next == proc){
+ p->next = proc->next;
+ break;
+ }
+ }
+ }
/* We are done modifying process list, so unblock signal */
- ret = (int)(TEMP_FAILURE_RETRY
- (sigprocmask(SIG_UNBLOCK,
- &sigchld_action.sa_mask, NULL)));
+ ret = sigprocmask (SIG_UNBLOCK, &sigchld_action.sa_mask,
+ NULL);
if(ret < 0){
- error(0, errno, "sigprocmask");
- exitstatus = EX_OSERR;
- goto fallback;
- }
-
- if(plugin_list == NULL){
- break;
- }
-
- continue;
+ perror("sigprocmask");
+ }
+ free(proc->buffer);
+ free(proc);
+ /* We deleted this process from the list, so we can't go
+ proc->next. Therefore, start over from the beginning of
+ the process list */
+ break;
}
-
/* This process exited nicely, so print its buffer */
-
- bool bret = print_out_password(proc->buffer,
- proc->buffer_length);
- if(not bret){
- error(0, errno, "print_out_password");
- exitstatus = EX_IOERR;
+ for(size_t written = 0; written < proc->buffer_length;
+ written += (size_t)ret){
+ ret = TEMP_FAILURE_RETRY(write(STDOUT_FILENO,
+ proc->buffer + written,
+ proc->buffer_length
+ - written));
+ if(ret < 0){
+ perror("write");
+ exitstatus = EXIT_FAILURE;
+ goto end;
+ }
}
- goto fallback;
+ goto end;
}
-
/* This process has not completed. Does it have any output? */
- if(proc->eof or not FD_ISSET(proc->fd, &rfds)){ /* Spurious
- warning from
- -Wconversion */
+ if(proc->eof or not FD_ISSET(proc->fd, &rfds)){
/* This process had nothing to say at this time */
- proc = proc->next;
continue;
}
/* Before reading, make the process' data buffer large enough */
if(proc->buffer_length + BUFFER_SIZE > proc->buffer_size){
proc->buffer = realloc(proc->buffer, proc->buffer_size
+ (size_t) BUFFER_SIZE);
- if(proc->buffer == NULL){
- error(0, errno, "malloc");
- exitstatus = EX_OSERR;
- goto fallback;
+ if (proc->buffer == NULL){
+ perror("malloc");
+ exitstatus = EXIT_FAILURE;
+ goto end;
}
proc->buffer_size += BUFFER_SIZE;
}
/* Read from the process */
- sret = TEMP_FAILURE_RETRY(read(proc->fd,
- proc->buffer
- + proc->buffer_length,
- BUFFER_SIZE));
- if(sret < 0){
+ ret = read(proc->fd, proc->buffer + proc->buffer_length,
+ BUFFER_SIZE);
+ if(ret < 0){
/* Read error from this process; ignore the error */
- proc = proc->next;
continue;
}
- if(sret == 0){
+ if(ret == 0){
/* got EOF */
proc->eof = true;
} else {
- proc->buffer_length += (size_t) sret;
+ proc->buffer_length += (size_t) ret;
}
}
}
-
-
- fallback:
-
- if(plugin_list == NULL or (exitstatus != EXIT_SUCCESS
- and exitstatus != EX_OK)){
- /* Fallback if all plugins failed, none are found or an error
- occured */
- bool bret;
- fprintf(stderr, "Going to fallback mode using getpass(3)\n");
- char *passwordbuffer = getpass("Password: ");
- size_t len = strlen(passwordbuffer);
- /* Strip trailing newline */
- if(len > 0 and passwordbuffer[len-1] == '\n'){
- passwordbuffer[len-1] = '\0'; /* not strictly necessary */
- len--;
- }
- bret = print_out_password(passwordbuffer, len);
- if(not bret){
- error(0, errno, "print_out_password");
- exitstatus = EX_IOERR;
- }
+ if(process_list == NULL){
+ fprintf(stderr, "All plugin processes failed, exiting\n");
+ exitstatus = EXIT_FAILURE;
}
+ end:
/* Restore old signal handler */
- ret = sigaction(SIGCHLD, &old_sigchld_action, NULL);
- if(ret == -1){
- error(0, errno, "sigaction");
- exitstatus = EX_OSERR;
- }
-
- if(custom_argv != NULL){
- for(char **arg = custom_argv+1; *arg != NULL; arg++){
- free(*arg);
- }
- free(custom_argv);
+ sigaction(SIGCHLD, &old_sigchld_action, NULL);
+
+ free(plus_argv);
+
+ /* Free the plugin list */
+ for(plugin *next; plugin_list != NULL; plugin_list = next){
+ next = plugin_list->next;
+ free(plugin_list->argv);
+ free(plugin_list);
}
if(dir != NULL){
closedir(dir);
}
- /* Kill the processes */
- for(plugin *p = plugin_list; p != NULL; p = p->next){
- if(p->pid != 0){
- close(p->fd);
- ret = kill(p->pid, SIGTERM);
- if(ret == -1 and errno != ESRCH){
- /* Set-uid proccesses might not get closed */
- error(0, errno, "kill");
- }
- }
+ /* Free the process list and kill the processes */
+ for(process *next; process_list != NULL; process_list = next){
+ next = process_list->next;
+ close(process_list->fd);
+ kill(process_list->pid, SIGTERM);
+ free(process_list->buffer);
+ free(process_list);
}
/* Wait for any remaining child processes to terminate */
- do {
+ do{
ret = wait(NULL);
} while(ret >= 0);
if(errno != ECHILD){
- error(0, errno, "wait");
+ perror("wait");
}
- free_plugin_list();
-
- free(plugindir);
- free(argfile);
-
return exitstatus;
}
=== removed file 'mandos-clients.conf.xml'
--- mandos-clients.conf.xml 2010-09-27 18:57:12 +0000
+++ mandos-clients.conf.xml 1970-01-01 00:00:00 +0000
@@ -1,475 +0,0 @@
-
-
-/etc/mandos/clients.conf">
-
-
-%common;
-]>
-
-
-
- Mandos Manual
-
- Mandos
- &version;
- &TIMESTAMP;
-
-
- Björn
- Påhlsson
-
- belorn@fukt.bsnet.se
-
-
-
- Teddy
- Hogeborn
-
- teddy@fukt.bsnet.se
-
-
-
-
- 2008
- 2009
- 2010
- Teddy Hogeborn
- Björn Påhlsson
-
-
-
-
-
- &CONFNAME;
- 5
-
-
-
- &CONFNAME;
-
- Configuration file for the Mandos server
-
-
-
-
- &CONFPATH;
-
-
-
- DESCRIPTION
-
- The file &CONFPATH; is a configuration file for mandos
- 8, read by it at startup.
- The file needs to list all clients that should be able to use
- the service. All clients listed will be regarded as enabled,
- even if a client was disabled in a previous run of the server.
-
-
- The format starts with a [section
- header] which is either
- [DEFAULT] or [client
- name]. The client
- name can be anything, and is not tied to a host
- name. Following the section header is any number of
- option=value
entries,
- with continuations in the style of RFC 822. option: value
is also accepted. Note that
- leading whitespace is removed from values. Values can contain
- format strings which refer to other values in the same section,
- or values in the DEFAULT
section (see ). Lines beginning with #
- or ;
are ignored and may be used to provide
- comments.
-
-
-
-
- OPTIONS
-
- Note: all option values are subject to
- start time expansion, see .
-
-
- Unknown options are ignored. The used options are as follows:
-
-
-
-
-
-
-
-
- This option is optional.
-
-
- How long to wait for external approval before resorting to
- use the value. The
- default is 0s
, i.e. not to wait.
-
-
- The format of TIME is the same
- as for timeout below.
-
-
-
-
-
-
-
-
- This option is optional.
-
-
- How long an external approval lasts. The default is 1
- second.
-
-
- The format of TIME is the same
- as for timeout below.
-
-
-
-
-
-
-
-
- Whether to approve a client by default after
- the . The default
- is True
.
-
-
-
-
-
-
-
-
- This option is optional.
-
-
- This option allows you to override the default shell
- command that the server will use to check if the client is
- still up. Any output of the command will be ignored, only
- the exit code is checked: If the exit code of the command
- is zero, the client is considered up. The command will be
- run using /bin/sh
-
, so
- PATH will be searched. The default
- value for the checker command is fping %%(host)s
.
-
-
- In addition to normal start time expansion, this option
- will also be subject to runtime expansion; see .
-
-
-
-
-
-
-
-
- This option is required.
-
-
- This option sets the OpenPGP fingerprint that identifies
- the public key that clients authenticate themselves with
- through TLS. The string needs to be in hexidecimal form,
- but spaces or upper/lower case are not significant.
-
-
-
-
-
-
-
-
- This option is optional, but highly
- recommended unless the
- option is modified to a
- non-standard value without %%(host)s
in it.
-
-
- Host name for this client. This is not used by the server
- directly, but can be, and is by default, used by the
- checker. See the option.
-
-
-
-
-
-
-
-
- This option is optional.
-
-
- How often to run the checker to confirm that a client is
- still up. Note: a new checker will
- not be started if an old one is still running. The server
- will wait for a checker to complete until the below
- timeout
occurs, at which
- time the client will be disabled, and any running checker
- killed. The default interval is 5 minutes.
-
-
- The format of TIME is the same
- as for timeout below.
-
-
-
-
-
-
-
-
- This option is only used if is not
- specified, in which case this option is
- required.
-
-
- Similar to the , except the secret
- data is in an external file. The contents of the file
- should not be base64-encoded, but
- will be sent to clients verbatim.
-
-
- File names of the form ~user/foo/bar
- and $ENVVAR/foo/bar
- are supported.
-
-
-
-
-
-
-
-
- If this option is not specified, the option is required
- to be present.
-
-
- If present, this option must be set to a string of
- base64-encoded binary data. It will be decoded and sent
- to the client matching the above
- . This should, of course, be
- OpenPGP encrypted data, decryptable only by the client.
- The program mandos-keygen8 can, using its
- option, be used to generate
- this, if desired.
-
-
- Note: this value of this option will probably be very
- long. A useful feature to avoid having unreadably-long
- lines is that a line beginning with white space adds to
- the value of the previous line, RFC 822-style.
-
-
-
-
-
-
-
-
- This option is optional.
-
-
- The timeout is how long the server will wait (for either a
- successful checker run or a client receiving its secret)
- until a client is disabled and not allowed to get the data
- this server holds. By default Mandos will use 1 hour.
-
-
- The TIME is specified as a
- space-separated number of values, each of which is a
- number and a one-character suffix. The suffix must be one
- of d
, s
, m
,
- h
, and w
for days, seconds,
- minutes, hours, and weeks, respectively. The values are
- added together to give the total time value, so all of
- 330s
,
- 110s 110s 110s
, and
- 5m 30s
will give a value
- of five minutes and thirty seconds.
-
-
-
-
-
-
-
-
- EXPANSION
-
- There are two forms of expansion: Start time expansion and
- runtime expansion.
-
-
- START TIME EXPANSION
-
- Any string in an option value of the form
- %(foo)s
will be replaced by the value of the option
- foo either in the same section, or, if it
- does not exist there, the [DEFAULT]
- section. This is done at start time, when the configuration
- file is read.
-
-
- Note that this means that, in order to include an actual
- percent character (%
) in an option value, two
- percent characters in a row (%%
) must be
- entered.
-
-
-
- RUNTIME EXPANSION
-
- This is currently only done for the checker
- option.
-
-
- Any string in an option value of the form
- %%(foo)s
will be replaced by the value of the attribute
- foo of the internal
- Client
object in the
- Mandos server. The currently allowed values for
- foo are:
- approval_delay
,
- approval_duration
,
- created
,
- enabled
,
- fingerprint
,
- host
,
- interval
,
- last_approval_request
,
- last_checked_ok
,
- last_enabled
,
- name
,
- timeout
, and, if using
- D-Bus, dbus_object_path
.
- See the source code for details. Currently, none of these attributes
- except host
are guaranteed
- to be valid in future versions. Therefore, please
- let the authors know of any attributes that are useful so they
- may be preserved to any new versions of this software.
-
-
- Note that this means that, in order to include an actual
- percent character (%
) in a
- checker option, four
- percent characters in a row (%%%%
) must be
- entered. Also, a bad format here will lead to an immediate
- but silent run-time fatal exit; debug
- mode is needed to expose an error of this kind.
-
-
-
-
-
-
- FILES
-
- The file described here is &CONFPATH;
-
-
-
-
- BUGS
-
- The format for specifying times for timeout
- and interval is not very good.
-
-
- The difference between
- %%(foo)s and
- %(foo)s is
- obscure.
-
-
-
-
- EXAMPLE
-
-
-[DEFAULT]
-timeout = 1h
-interval = 5m
-checker = fping -q -- %%(host)s
-
-# Client "foo"
-[foo]
-fingerprint = 7788 2722 5BA7 DE53 9C5A 7CFA 59CF F7CD BD9A 5920
-secret =
- hQIOA6QdEjBs2L/HEAf/TCyrDe5Xnm9esa+Pb/vWF9CUqfn4srzVgSu234
- REJMVv7lBSrPE2132Lmd2gqF1HeLKDJRSVxJpt6xoWOChGHg+TMyXDxK+N
- Xl89vGvdU1XfhKkVm9MDLOgT5ECDPysDGHFPDhqHOSu3Kaw2DWMV/iH9vz
- 3Z20erVNbdcvyBnuojcoWO/6yfB5EQO0BXp7kcyy00USA3CjD5FGZdoQGI
- Tb8A/ar0tVA5crSQmaSotm6KmNLhrFnZ5BxX+TiE+eTUTqSloWRY6VAvqW
- QHC7OASxK5E6RXPBuFH5IohUA2Qbk5AHt99pYvsIPX88j2rWauOokoiKZo
- t/9leJ8VxO5l3wf/U64IH8bkPIoWmWZfd/nqh4uwGNbCgKMyT+AnvH7kMJ
- 3i7DivfWl2mKLV0PyPHUNva0VQxX6yYjcOhj1R6fCr/at8/NSLe2OhLchz
- dC+Ls9h+kvJXgF8Sisv+Wk/1RadPLFmraRlqvJwt6Ww21LpiXqXHV2mIgq
- WnR98YgSvUi3TJHrUQiNc9YyBzuRo0AjgG2C9qiE3FM+Y28+iQ/sR3+bFs
- zYuZKVTObqiIslwXu7imO0cvvFRgJF/6u3HNFQ4LUTGhiM3FQmC6NNlF3/
- vJM2hwRDMcJqDd54Twx90Wh+tYz0z7QMsK4ANXWHHWHR0JchnLWmenzbtW
- 5MHdW9AYsNJZAQSOpirE4Xi31CSlWAi9KV+cUCmWF5zOFy1x23P6PjdaRm
- 4T2zw4dxS5NswXWU0sVEXxjs6PYxuIiCTL7vdpx8QjBkrPWDrAbcMyBr2O
- QlnHIvPzEArRQLo=
-host = foo.example.org
-interval = 1m
-
-# Client "bar"
-[bar]
-fingerprint = 3e393aeaefb84c7e89e2f547b3a107558fca3a27
-secfile = /etc/mandos/bar-secret
-timeout = 15m
-approved_by_default = False
-approval_delay = 30s
-
-
-
-
-
- SEE ALSO
-
- mandos-keygen
- 8,
- mandos.conf
- 5,
- mandos
- 8
-
-
-
-
-
-
-
-
=== removed file 'mandos-ctl'
--- mandos-ctl 2010-09-28 18:57:31 +0000
+++ mandos-ctl 1970-01-01 00:00:00 +0000
@@ -1,337 +0,0 @@
-#!/usr/bin/python
-# -*- mode: python; coding: utf-8 -*-
-#
-# Mandos Monitor - Control and monitor the Mandos server
-#
-# Copyright © 2008-2010 Teddy Hogeborn
-# Copyright © 2008-2010 Björn Påhlsson
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-# Contact the authors at .
-#
-
-from __future__ import division
-import sys
-import dbus
-from optparse import OptionParser
-import locale
-import datetime
-import re
-import os
-
-locale.setlocale(locale.LC_ALL, u'')
-
-tablewords = {
- 'Name': u'Name',
- 'Enabled': u'Enabled',
- 'Timeout': u'Timeout',
- 'LastCheckedOK': u'Last Successful Check',
- 'LastApprovalRequest': u'Last Approval Request',
- 'Created': u'Created',
- 'Interval': u'Interval',
- 'Host': u'Host',
- 'Fingerprint': u'Fingerprint',
- 'CheckerRunning': u'Check Is Running',
- 'LastEnabled': u'Last Enabled',
- 'ApprovalPending': u'Approval Is Pending',
- 'ApprovedByDefault': u'Approved By Default',
- 'ApprovalDelay': u"Approval Delay",
- 'ApprovalDuration': u"Approval Duration",
- 'Checker': u'Checker',
- }
-defaultkeywords = ('Name', 'Enabled', 'Timeout', 'LastCheckedOK')
-domain = 'se.bsnet.fukt'
-busname = domain + '.Mandos'
-server_path = '/'
-server_interface = domain + '.Mandos'
-client_interface = domain + '.Mandos.Client'
-version = "1.2"
-
-def timedelta_to_milliseconds(td):
- "Convert a datetime.timedelta object to milliseconds"
- return ((td.days * 24 * 60 * 60 * 1000)
- + (td.seconds * 1000)
- + (td.microseconds // 1000))
-
-def milliseconds_to_string(ms):
- td = datetime.timedelta(0, 0, 0, ms)
- return (u"%(days)s%(hours)02d:%(minutes)02d:%(seconds)02d"
- % { "days": "%dT" % td.days if td.days else "",
- "hours": td.seconds // 3600,
- "minutes": (td.seconds % 3600) // 60,
- "seconds": td.seconds % 60,
- })
-
-
-def string_to_delta(interval):
- """Parse a string and return a datetime.timedelta
-
- >>> string_to_delta('7d')
- datetime.timedelta(7)
- >>> string_to_delta('60s')
- datetime.timedelta(0, 60)
- >>> string_to_delta('60m')
- datetime.timedelta(0, 3600)
- >>> string_to_delta('24h')
- datetime.timedelta(1)
- >>> string_to_delta(u'1w')
- datetime.timedelta(7)
- >>> string_to_delta('5m 30s')
- datetime.timedelta(0, 330)
- """
- timevalue = datetime.timedelta(0)
- regexp = re.compile("\d+[dsmhw]")
-
- for s in regexp.findall(interval):
- try:
- suffix = unicode(s[-1])
- value = int(s[:-1])
- if suffix == u"d":
- delta = datetime.timedelta(value)
- elif suffix == u"s":
- delta = datetime.timedelta(0, value)
- elif suffix == u"m":
- delta = datetime.timedelta(0, 0, 0, 0, value)
- elif suffix == u"h":
- delta = datetime.timedelta(0, 0, 0, 0, 0, value)
- elif suffix == u"w":
- delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
- else:
- raise ValueError
- except (ValueError, IndexError):
- raise ValueError
- timevalue += delta
- return timevalue
-
-def print_clients(clients, keywords):
- def valuetostring(value, keyword):
- if type(value) is dbus.Boolean:
- return u"Yes" if value else u"No"
- if keyword in (u"Timeout", u"Interval", u"ApprovalDelay",
- u"ApprovalDuration"):
- return milliseconds_to_string(value)
- return unicode(value)
-
- # Create format string to print table rows
- format_string = u' '.join(u'%%-%ds' %
- max(len(tablewords[key]),
- max(len(valuetostring(client[key],
- key))
- for client in
- clients))
- for key in keywords)
- # Print header line
- print format_string % tuple(tablewords[key] for key in keywords)
- for client in clients:
- print format_string % tuple(valuetostring(client[key], key)
- for key in keywords)
-
-def has_actions(options):
- return any((options.enable,
- options.disable,
- options.bump_timeout,
- options.start_checker,
- options.stop_checker,
- options.is_enabled,
- options.remove,
- options.checker is not None,
- options.timeout is not None,
- options.interval is not None,
- options.approved_by_default is not None,
- options.approval_delay is not None,
- options.approval_duration is not None,
- options.host is not None,
- options.secret is not None,
- options.approve,
- options.deny))
-
-def main():
- parser = OptionParser(version = "%%prog %s" % version)
- parser.add_option("-a", "--all", action="store_true",
- help="Select all clients")
- parser.add_option("-v", "--verbose", action="store_true",
- help="Print all fields")
- parser.add_option("-e", "--enable", action="store_true",
- help="Enable client")
- parser.add_option("-d", "--disable", action="store_true",
- help="disable client")
- parser.add_option("-b", "--bump-timeout", action="store_true",
- help="Bump timeout for client")
- parser.add_option("--start-checker", action="store_true",
- help="Start checker for client")
- parser.add_option("--stop-checker", action="store_true",
- help="Stop checker for client")
- parser.add_option("-V", "--is-enabled", action="store_true",
- help="Check if client is enabled")
- parser.add_option("-r", "--remove", action="store_true",
- help="Remove client")
- parser.add_option("-c", "--checker", type="string",
- help="Set checker command for client")
- parser.add_option("-t", "--timeout", type="string",
- help="Set timeout for client")
- parser.add_option("-i", "--interval", type="string",
- help="Set checker interval for client")
- parser.add_option("--approve-by-default", action="store_true",
- dest=u"approved_by_default",
- help="Set client to be approved by default")
- parser.add_option("--deny-by-default", action="store_false",
- dest=u"approved_by_default",
- help="Set client to be denied by default")
- parser.add_option("--approval-delay", type="string",
- help="Set delay before client approve/deny")
- parser.add_option("--approval-duration", type="string",
- help="Set duration of one client approval")
- parser.add_option("-H", "--host", type="string",
- help="Set host for client")
- parser.add_option("-s", "--secret", type="string",
- help="Set password blob (file) for client")
- parser.add_option("-A", "--approve", action="store_true",
- help="Approve any current client request")
- parser.add_option("-D", "--deny", action="store_true",
- help="Deny any current client request")
- options, client_names = parser.parse_args()
-
- if has_actions(options) and not client_names and not options.all:
- parser.error('Options require clients names or --all.')
- if options.verbose and has_actions(options):
- parser.error('--verbose can only be used alone or with'
- ' --all.')
- if options.all and not has_actions(options):
- parser.error('--all requires an action.')
-
- try:
- bus = dbus.SystemBus()
- mandos_dbus_objc = bus.get_object(busname, server_path)
- except dbus.exceptions.DBusException:
- print >> sys.stderr, "Could not connect to Mandos server"
- sys.exit(1)
-
- mandos_serv = dbus.Interface(mandos_dbus_objc,
- dbus_interface = server_interface)
-
- #block stderr since dbus library prints to stderr
- null = os.open(os.path.devnull, os.O_RDWR)
- stderrcopy = os.dup(sys.stderr.fileno())
- os.dup2(null, sys.stderr.fileno())
- os.close(null)
- try:
- try:
- mandos_clients = mandos_serv.GetAllClientsWithProperties()
- finally:
- #restore stderr
- os.dup2(stderrcopy, sys.stderr.fileno())
- os.close(stderrcopy)
- except dbus.exceptions.DBusException, e:
- print >> sys.stderr, "Access denied: Accessing mandos server through dbus."
- sys.exit(1)
-
- # Compile dict of (clients: properties) to process
- clients={}
-
- if options.all or not client_names:
- clients = dict((bus.get_object(busname, path), properties)
- for path, properties in
- mandos_clients.iteritems())
- else:
- for name in client_names:
- for path, client in mandos_clients.iteritems():
- if client['Name'] == name:
- client_objc = bus.get_object(busname, path)
- clients[client_objc] = client
- break
- else:
- print >> sys.stderr, "Client not found on server: %r" % name
- sys.exit(1)
-
- if not has_actions(options) and clients:
- if options.verbose:
- keywords = ('Name', 'Enabled', 'Timeout',
- 'LastCheckedOK', 'Created', 'Interval',
- 'Host', 'Fingerprint', 'CheckerRunning',
- 'LastEnabled', 'ApprovalPending',
- 'ApprovedByDefault',
- 'LastApprovalRequest', 'ApprovalDelay',
- 'ApprovalDuration', 'Checker')
- else:
- keywords = defaultkeywords
-
- print_clients(clients.values(), keywords)
- else:
- # Process each client in the list by all selected options
- for client in clients:
- if options.remove:
- mandos_serv.RemoveClient(client.__dbus_object_path__)
- if options.enable:
- client.Enable(dbus_interface=client_interface)
- if options.disable:
- client.Disable(dbus_interface=client_interface)
- if options.bump_timeout:
- client.CheckedOK(dbus_interface=client_interface)
- if options.start_checker:
- client.StartChecker(dbus_interface=client_interface)
- if options.stop_checker:
- client.StopChecker(dbus_interface=client_interface)
- if options.is_enabled:
- sys.exit(0 if client.Get(client_interface,
- u"Enabled",
- dbus_interface=dbus.PROPERTIES_IFACE)
- else 1)
- if options.checker:
- client.Set(client_interface, u"Checker", options.checker,
- dbus_interface=dbus.PROPERTIES_IFACE)
- if options.host:
- client.Set(client_interface, u"Host", options.host,
- dbus_interface=dbus.PROPERTIES_IFACE)
- if options.interval:
- client.Set(client_interface, u"Interval",
- timedelta_to_milliseconds
- (string_to_delta(options.interval)),
- dbus_interface=dbus.PROPERTIES_IFACE)
- if options.approval_delay:
- client.Set(client_interface, u"ApprovalDelay",
- timedelta_to_milliseconds
- (string_to_delta(options.
- approval_delay)),
- dbus_interface=dbus.PROPERTIES_IFACE)
- if options.approval_duration:
- client.Set(client_interface, u"ApprovalDuration",
- timedelta_to_milliseconds
- (string_to_delta(options.
- approval_duration)),
- dbus_interface=dbus.PROPERTIES_IFACE)
- if options.timeout:
- client.Set(client_interface, u"Timeout",
- timedelta_to_milliseconds
- (string_to_delta(options.timeout)),
- dbus_interface=dbus.PROPERTIES_IFACE)
- if options.secret:
- client.Set(client_interface, u"Secret",
- dbus.ByteArray(open(options.secret,
- u'rb').read()),
- dbus_interface=dbus.PROPERTIES_IFACE)
- if options.approved_by_default is not None:
- client.Set(client_interface, u"ApprovedByDefault",
- dbus.Boolean(options
- .approved_by_default),
- dbus_interface=dbus.PROPERTIES_IFACE)
- if options.approve:
- client.Approve(dbus.Boolean(True),
- dbus_interface=client_interface)
- elif options.deny:
- client.Approve(dbus.Boolean(False),
- dbus_interface=client_interface)
-
-if __name__ == '__main__':
- main()
=== removed file 'mandos-ctl.xml'
--- mandos-ctl.xml 2010-09-25 23:52:17 +0000
+++ mandos-ctl.xml 1970-01-01 00:00:00 +0000
@@ -1,570 +0,0 @@
-
-
-
-
-%common;
-]>
-
-
-
- Mandos Manual
-
- Mandos
- &version;
- &TIMESTAMP;
-
-
- Björn
- Påhlsson
-
- belorn@fukt.bsnet.se
-
-
-
- Teddy
- Hogeborn
-
- teddy@fukt.bsnet.se
-
-
-
-
- 2010
- Teddy Hogeborn
- Björn Påhlsson
-
-
-
-
-
- &COMMANDNAME;
- 8
-
-
-
- &COMMANDNAME;
-
- Control the operation of the Mandos server
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- CLIENT
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
- CLIENT
-
-
-
-
- &COMMANDNAME;
-
-
-
-
- CLIENT
-
-
- &COMMANDNAME;
-
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
-
-
- DESCRIPTION
-
- &COMMANDNAME; is a program to control the
- operation of the Mandos server mandos8.
-
-
- This program can be used to change client settings, approve or
- deny client requests, and to remove clients from the server.
-
-
-
-
- PURPOSE
-
- The purpose of this is to enable remote and unattended
- rebooting of client host computer with an
- encrypted root file system. See for details.
-
-
-
-
- OPTIONS
-
-
-
-
-
-
-
- Show a help message and exit
-
-
-
-
-
-
-
-
-
- Enable client(s). An enabled client will be eligble to
- receive its secret.
-
-
-
-
-
-
-
-
-
- Disable client(s). A disabled client will not be eligble
- to receive its secret, and no checkers will be started for
- it.
-
-
-
-
-
-
-
-
- Bump the timeout of the specified client(s), just as if a
- checker had completed successfully for it/them.
-
-
-
-
-
-
-
-
- Start a new checker now for the specified client(s).
-
-
-
-
-
-
-
-
- Stop any running checker for the specified client(s).
-
-
-
-
-
-
-
-
-
- Remove the specified client(s) from the server.
-
-
-
-
-
-
-
-
-
- Set the checker option of the specified
- client(s); see mandos-clients.conf5.
-
-
-
-
-
-
-
-
-
- Set the timeout option of the specified
- client(s); see mandos-clients.conf5.
-
-
-
-
-
-
-
-
-
- Set the interval option of the
- specified client(s); see mandos-clients.conf5.
-
-
-
-
-
-
-
-
-
- Set the approved_by_default option of
- the specified client(s) to True or
- False, respectively; see
- mandos-clients.conf5.
-
-
-
-
-
-
-
-
- Set the approval_delay option of the
- specified client(s); see mandos-clients.conf5.
-
-
-
-
-
-
-
-
- Set the approval_duration option of the
- specified client(s); see mandos-clients.conf5.
-
-
-
-
-
-
-
-
-
- Set the host option of the specified
- client(s); see mandos-clients.conf5.
-
-
-
-
-
-
-
-
-
- Set the secfile option of the specified
- client(s); see mandos-clients.conf5.
-
-
-
-
-
-
-
-
-
- Approve client(s) if currently waiting for approval.
-
-
-
-
-
-
-
-
-
- Deny client(s) if currently waiting for approval.
-
-
-
-
-
-
-
-
-
- Make the client-modifying options modify all clients.
-
-
-
-
-
-
-
-
-
- Show all client settings, not just a subset.
-
-
-
-
-
-
-
-
-
- Check if a single client is enabled or not, and exit with
- a successful exit status only if the client is enabled.
-
-
-
-
-
-
-
-
- OVERVIEW
-
-
- This program is a small utility to generate new OpenPGP keys for
- new Mandos clients, and to generate sections for inclusion in
- clients.conf on the server.
-
-
-
-
- EXIT STATUS
-
- If the option is used, the exit
- status will be 0 only if the specified client is enabled.
-
-
-
-
-
-
-
-
-
-
- EXAMPLE
-
-
- To list all clients:
-
-
- &COMMANDNAME;
-
-
-
-
-
- To list all settings for the clients
- named foo1.example.org
and foo2.example.org
:
-
-
-
-
-&COMMANDNAME; --verbose foo1.example.org foo2.example.org
-
-
-
-
-
-
- To enable all clients:
-
-
- &COMMANDNAME; --enable --all
-
-
-
-
-
- To change timeout and interval value for the clients
- named foo1.example.org
and foo2.example.org
:
-
-
-
-
-&COMMANDNAME; --timeout="5m" --interval="1m" foo1.example.org foo2.example.org
-
-
-
-
-
-
- To approve all clients currently waiting for it:
-
-
- &COMMANDNAME; --approve --all
-
-
-
-
-
- SECURITY
-
- This program must be permitted to access the Mandos server via
- the D-Bus interface. This normally requires the root user, but
- could be configured otherwise by reconfiguring the D-Bus server.
-
-
-
-
- SEE ALSO
-
- mandos
- 8,
- mandos-clients.conf
- 5,
- mandos-monitor
- 8
-
-
-
-
-
-
-
-
-
=== removed file 'mandos-keygen'
--- mandos-keygen 2010-10-01 18:40:55 +0000
+++ mandos-keygen 1970-01-01 00:00:00 +0000
@@ -1,344 +0,0 @@
-#!/bin/sh -e
-#
-# Mandos key generator - create a new OpenPGP key for a Mandos client
-#
-# Copyright © 2008-2010 Teddy Hogeborn
-# Copyright © 2008-2010 Björn Påhlsson
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-# Contact the authors at .
-#
-
-VERSION="1.2"
-
-KEYDIR="/etc/keys/mandos"
-KEYTYPE=DSA
-KEYLENGTH=2048
-SUBKEYTYPE=ELG-E
-SUBKEYLENGTH=2048
-KEYNAME="`hostname --fqdn 2>/dev/null || hostname`"
-KEYEMAIL=""
-KEYCOMMENT="Mandos client key"
-KEYEXPIRE=0
-FORCE=no
-KEYCOMMENT_ORIG="$KEYCOMMENT"
-mode=keygen
-
-if [ ! -d "$KEYDIR" ]; then
- KEYDIR="/etc/mandos/keys"
-fi
-
-# Parse options
-TEMP=`getopt --options vhpF:d:t:l:s:L:n:e:c:x:f \
- --longoptions version,help,password,passfile:,dir:,type:,length:,subtype:,sublength:,name:,email:,comment:,expire:,force \
- --name "$0" -- "$@"`
-
-help(){
-basename="`basename $0`"
-cat <&2; exit 1;;
- esac
-done
-if [ "$#" -gt 0 ]; then
- echo "Unknown arguments: '$@'" >&2
- exit 1
-fi
-
-SECKEYFILE="$KEYDIR/seckey.txt"
-PUBKEYFILE="$KEYDIR/pubkey.txt"
-
-# Check for some invalid values
-if [ ! -d "$KEYDIR" ]; then
- echo "$KEYDIR not a directory" >&2
- exit 1
-fi
-if [ ! -r "$KEYDIR" ]; then
- echo "Directory $KEYDIR not readable" >&2
- exit 1
-fi
-
-if [ "$mode" = keygen ]; then
- if [ ! -w "$KEYDIR" ]; then
- echo "Directory $KEYDIR not writeable" >&2
- exit 1
- fi
- if [ -z "$KEYTYPE" ]; then
- echo "Empty key type" >&2
- exit 1
- fi
-
- if [ -z "$KEYNAME" ]; then
- echo "Empty key name" >&2
- exit 1
- fi
-
- if [ -z "$KEYLENGTH" ] || [ "$KEYLENGTH" -lt 512 ]; then
- echo "Invalid key length" >&2
- exit 1
- fi
-
- if [ -z "$KEYEXPIRE" ]; then
- echo "Empty key expiration" >&2
- exit 1
- fi
-
- # Make FORCE be 0 or 1
- case "$FORCE" in
- [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]) FORCE=1;;
- [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|*) FORCE=0;;
- esac
-
- if [ \( -e "$SECKEYFILE" -o -e "$PUBKEYFILE" \) \
- -a "$FORCE" -eq 0 ]; then
- echo "Refusing to overwrite old key files; use --force" >&2
- exit 1
- fi
-
- # Set lines for GnuPG batch file
- if [ -n "$KEYCOMMENT" ]; then
- KEYCOMMENTLINE="Name-Comment: $KEYCOMMENT"
- fi
- if [ -n "$KEYEMAIL" ]; then
- KEYEMAILLINE="Name-Email: $KEYEMAIL"
- fi
-
- # Create temporary gpg batch file
- BATCHFILE="`mktemp -t mandos-keygen-batch.XXXXXXXXXX`"
-fi
-
-if [ "$mode" = password ]; then
- # Create temporary encrypted password file
- SECFILE="`mktemp -t mandos-keygen-secfile.XXXXXXXXXX`"
-fi
-
-# Create temporary key ring directory
-RINGDIR="`mktemp -d -t mandos-keygen-keyrings.XXXXXXXXXX`"
-
-# Remove temporary files on exit
-trap "
-set +e; \
-test -n \"$SECFILE\" && shred --remove \"$SECFILE\"; \
-shred --remove \"$RINGDIR\"/sec*;
-test -n \"$BATCHFILE\" && rm --force \"$BATCHFILE\"; \
-rm --recursive --force \"$RINGDIR\";
-stty echo; \
-" EXIT
-
-set -e
-
-umask 077
-
-if [ "$mode" = keygen ]; then
- # Create batch file for GnuPG
- cat >"$BATCHFILE" <<-EOF
- Key-Type: $KEYTYPE
- Key-Length: $KEYLENGTH
- #Key-Usage: encrypt,sign,auth
- Subkey-Type: $SUBKEYTYPE
- Subkey-Length: $SUBKEYLENGTH
- #Subkey-Usage: encrypt,sign,auth
- Name-Real: $KEYNAME
- $KEYCOMMENTLINE
- $KEYEMAILLINE
- Expire-Date: $KEYEXPIRE
- #Preferences:
- #Handle:
- #%pubring pubring.gpg
- #%secring secring.gpg
- %commit
- EOF
-
- if tty --quiet; then
- cat <<-EOF
- Note: Due to entropy requirements, key generation could take
- anything from a few minutes to SEVERAL HOURS. Please be
- patient and/or supply the system with more entropy if needed.
- EOF
- echo -n "Started: "
- date
- fi
-
- # Generate a new key in the key rings
- gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
- --homedir "$RINGDIR" --trust-model always \
- --gen-key "$BATCHFILE"
- rm --force "$BATCHFILE"
-
- if tty --quiet; then
- echo -n "Finished: "
- date
- fi
-
- # Backup any old key files
- if cp --backup=numbered --force "$SECKEYFILE" "$SECKEYFILE" \
- 2>/dev/null; then
- shred --remove "$SECKEYFILE"
- fi
- if cp --backup=numbered --force "$PUBKEYFILE" "$PUBKEYFILE" \
- 2>/dev/null; then
- rm --force "$PUBKEYFILE"
- fi
-
- FILECOMMENT="Mandos client key for $KEYNAME"
- if [ "$KEYCOMMENT" != "$KEYCOMMENT_ORIG" ]; then
- FILECOMMENT="$FILECOMMENT ($KEYCOMMENT)"
- fi
-
- if [ -n "$KEYEMAIL" ]; then
- FILECOMMENT="$FILECOMMENT <$KEYEMAIL>"
- fi
-
- # Export key from key rings to key files
- gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
- --homedir "$RINGDIR" --armor --export-options export-minimal \
- --comment "$FILECOMMENT" --output "$SECKEYFILE" \
- --export-secret-keys
- gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
- --homedir "$RINGDIR" --armor --export-options export-minimal \
- --comment "$FILECOMMENT" --output "$PUBKEYFILE" --export
-fi
-
-if [ "$mode" = password ]; then
- # Import key into temporary key rings
- gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
- --homedir "$RINGDIR" --trust-model always --armor \
- --import "$SECKEYFILE"
- gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
- --homedir "$RINGDIR" --trust-model always --armor \
- --import "$PUBKEYFILE"
-
- # Get fingerprint of key
- FINGERPRINT="`gpg --quiet --batch --no-tty --no-options \
- --enable-dsa2 --homedir \"$RINGDIR\" --trust-model always \
- --fingerprint --with-colons \
- | sed --quiet \
- --expression='/^fpr:/{s/^fpr:.*:\\([0-9A-Z]*\\):\$/\\1/p;q}'`"
-
- test -n "$FINGERPRINT"
-
- FILECOMMENT="Encrypted password for a Mandos client"
-
- if [ -n "$PASSFILE" ]; then
- cat "$PASSFILE"
- else
- stty -echo
- echo -n "Enter passphrase: " >&2
- first="$(head --lines=1 | tr --delete '\n')"
- echo >&2
- echo -n "Repeat passphrase: " >&2
- second="$(head --lines=1 | tr --delete '\n')"
- echo >&2
- stty echo
- if [ "$first" != "$second" ]; then
- echo "Passphrase mismatch" >&2
- touch "$RINGDIR"/mismatch
- else
- echo -n "$first"
- fi
- fi | gpg --quiet --batch --no-tty --no-options --enable-dsa2 \
- --homedir "$RINGDIR" --trust-model always --armor --encrypt \
- --sign --recipient "$FINGERPRINT" --comment "$FILECOMMENT" \
- > "$SECFILE"
- if [ -e "$RINGDIR"/mismatch ]; then
- rm --force "$RINGDIR"/mismatch
- exit 1
- fi
-
- cat <<-EOF
- [$KEYNAME]
- host = $KEYNAME
- fingerprint = $FINGERPRINT
- secret =
- EOF
- sed --quiet --expression='
- /^-----BEGIN PGP MESSAGE-----$/,/^-----END PGP MESSAGE-----$/{
- /^$/,${
- # Remove 24-bit Radix-64 checksum
- s/=....$//
- # Indent four spaces
- /^[^-]/s/^/ /p
- }
- }' < "$SECFILE"
-fi
-
-trap - EXIT
-
-set +e
-# Remove the password file, if any
-if [ -n "$SECFILE" ]; then
- shred --remove "$SECFILE"
-fi
-# Remove the key rings
-shred --remove "$RINGDIR"/sec*
-rm --recursive --force "$RINGDIR"
=== removed file 'mandos-keygen.xml'
--- mandos-keygen.xml 2009-01-04 21:54:55 +0000
+++ mandos-keygen.xml 1970-01-01 00:00:00 +0000
@@ -1,511 +0,0 @@
-
-
-
-
-%common;
-]>
-
-
-
- Mandos Manual
-
- Mandos
- &version;
- &TIMESTAMP;
-
-
- Björn
- Påhlsson
-
- belorn@fukt.bsnet.se
-
-
-
- Teddy
- Hogeborn
-
- teddy@fukt.bsnet.se
-
-
-
-
- 2008
- 2009
- Teddy Hogeborn
- Björn Påhlsson
-
-
-
-
-
- &COMMANDNAME;
- 8
-
-
-
- &COMMANDNAME;
-
- Generate key and password for Mandos client and server.
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
- FILE
-
-
-
-
-
-
-
-
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
-
-
- DESCRIPTION
-
- &COMMANDNAME; is a program to generate the
- OpenPGP key used by
- mandos-client
- 8mandos. The key is
- normally written to /etc/mandos for later installation into the
- initrd image, but this, and most other things, can be changed
- with command line options.
-
-
- This program can also be used with the
- or
- options to generate a ready-made section for
- clients.conf (see
- mandos-clients.conf
- 5).
-
-
-
-
- PURPOSE
-
- The purpose of this is to enable remote and unattended
- rebooting of client host computer with an
- encrypted root file system. See for details.
-
-
-
-
- OPTIONS
-
-
-
-
-
-
-
- Show a help message and exit
-
-
-
-
-
-
-
-
-
- Target directory for key files. Default is
- /etc/mandos.
-
-
-
-
-
-
-
-
-
- Key type. Default is DSA
.
-
-
-
-
-
-
-
-
-
- Key length in bits. Default is 2048.
-
-
-
-
-
-
-
-
-
- Subkey type. Default is ELG-E
(Elgamal
- encryption-only).
-
-
-
-
-
-
-
-
-
- Subkey length in bits. Default is 2048.
-
-
-
-
-
-
-
-
-
- Email address of key. Default is empty.
-
-
-
-
-
-
-
-
-
- Comment field for key. The default value is
- Mandos client key
.
-
-
-
-
-
-
-
-
-
- Key expire time. Default is no expiration. See
- gpg
- 1 for syntax.
-
-
-
-
-
-
-
-
-
- Force overwriting old key.
-
-
-
-
-
-
-
-
- Prompt for a password and encrypt it with the key already
- present in either /etc/mandos or the
- directory specified with the
- option. Outputs, on standard output, a section suitable
- for inclusion in mandos-clients.conf8. The host name or the name
- specified with the option is used
- for the section header. All other options are ignored,
- and no key is created.
-
-
-
-
-
-
-
-
- The same as , but read from
- FILE, not the terminal.
-
-
-
-
-
-
-
- OVERVIEW
-
-
- This program is a small utility to generate new OpenPGP keys for
- new Mandos clients, and to generate sections for inclusion in
- clients.conf on the server.
-
-
-
-
- EXIT STATUS
-
- The exit status will be 0 if a new key (or password, if the
- option was used) was successfully
- created, otherwise not.
-
-
-
-
- ENVIRONMENT
-
-
- TMPDIR
-
-
- If set, temporary files will be created here. See
- mktemp
- 1.
-
-
-
-
-
-
-
- FILES
-
- Use the option to change where
- &COMMANDNAME; will write the key files. The
- default file names are shown here.
-
-
-
- /etc/mandos/seckey.txt
-
-
- OpenPGP secret key file which will be created or
- overwritten.
-
-
-
-
- /etc/mandos/pubkey.txt
-
-
- OpenPGP public key file which will be created or
- overwritten.
-
-
-
-
- /tmp
-
-
- Temporary files will be written here if
- TMPDIR is not set.
-
-
-
-
-
-
-
-
-
-
-
-
-
- EXAMPLE
-
-
- Normal invocation needs no options:
-
-
- &COMMANDNAME;
-
-
-
-
- Create key in another directory and of another type. Force
- overwriting old key files:
-
-
-
-
-&COMMANDNAME; --dir ~/keydir --type RSA --force
-
-
-
-
-
- Prompt for a password, encrypt it with the key in
- /etc/mandos and output a section suitable
- for clients.conf.
-
-
- &COMMANDNAME; --password
-
-
-
-
- Prompt for a password, encrypt it with the key in the
- client-key directory and output a section
- suitable for clients.conf.
-
-
-
-
-&COMMANDNAME; --password --dir client-key
-
-
-
-
-
-
- SECURITY
-
- The , ,
- , and
- options can be used to create keys of low security. If in
- doubt, leave them to the default values.
-
-
- The key expire time is not guaranteed to be
- honored by mandos
- 8.
-
-
-
-
- SEE ALSO
-
- gpg
- 1,
- mandos-clients.conf
- 5,
- mandos
- 8,
- mandos-client
- 8mandos
-
-
-
-
-
-
-
-
-
=== removed file 'mandos-monitor'
--- mandos-monitor 2010-09-28 18:57:31 +0000
+++ mandos-monitor 1970-01-01 00:00:00 +0000
@@ -1,733 +0,0 @@
-#!/usr/bin/python
-# -*- mode: python; coding: utf-8 -*-
-#
-# Mandos Monitor - Control and monitor the Mandos server
-#
-# Copyright © 2009,2010 Teddy Hogeborn
-# Copyright © 2009,2010 Björn Påhlsson
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-# Contact the authors at .
-#
-
-from __future__ import division, absolute_import, with_statement
-
-import sys
-import os
-import signal
-
-import datetime
-
-import urwid.curses_display
-import urwid
-
-from dbus.mainloop.glib import DBusGMainLoop
-import gobject
-
-import dbus
-
-import UserList
-
-import locale
-
-locale.setlocale(locale.LC_ALL, u'')
-
-import logging
-logging.getLogger('dbus.proxies').setLevel(logging.CRITICAL)
-
-# Some useful constants
-domain = 'se.bsnet.fukt'
-server_interface = domain + '.Mandos'
-client_interface = domain + '.Mandos.Client'
-version = "1.2"
-
-# Always run in monochrome mode
-urwid.curses_display.curses.has_colors = lambda : False
-
-# Urwid doesn't support blinking, but we want it. Since we have no
-# use for underline on its own, we make underline also always blink.
-urwid.curses_display.curses.A_UNDERLINE |= (
- urwid.curses_display.curses.A_BLINK)
-
-def isoformat_to_datetime(iso):
- "Parse an ISO 8601 date string to a datetime.datetime()"
- if not iso:
- return None
- d, t = iso.split(u"T", 1)
- year, month, day = d.split(u"-", 2)
- hour, minute, second = t.split(u":", 2)
- second, fraction = divmod(float(second), 1)
- return datetime.datetime(int(year),
- int(month),
- int(day),
- int(hour),
- int(minute),
- int(second), # Whole seconds
- int(fraction*1000000)) # Microseconds
-
-class MandosClientPropertyCache(object):
- """This wraps a Mandos Client D-Bus proxy object, caches the
- properties and calls a hook function when any of them are
- changed.
- """
- def __init__(self, proxy_object=None, *args, **kwargs):
- self.proxy = proxy_object # Mandos Client proxy object
-
- self.properties = dict()
- self.proxy.connect_to_signal(u"PropertyChanged",
- self.property_changed,
- client_interface,
- byte_arrays=True)
-
- self.properties.update(
- self.proxy.GetAll(client_interface,
- dbus_interface = dbus.PROPERTIES_IFACE))
-
- #XXX This break good super behaviour!
-# super(MandosClientPropertyCache, self).__init__(
-# *args, **kwargs)
-
- def property_changed(self, property=None, value=None):
- """This is called whenever we get a PropertyChanged signal
- It updates the changed property in the "properties" dict.
- """
- # Update properties dict with new value
- self.properties[property] = value
-
-
-class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
- """A Mandos Client which is visible on the screen.
- """
-
- def __init__(self, server_proxy_object=None, update_hook=None,
- delete_hook=None, logger=None, *args, **kwargs):
- # Called on update
- self.update_hook = update_hook
- # Called on delete
- self.delete_hook = delete_hook
- # Mandos Server proxy object
- self.server_proxy_object = server_proxy_object
- # Logger
- self.logger = logger
-
- self._update_timer_callback_tag = None
- self._update_timer_callback_lock = 0
- self.last_checker_failed = False
-
- # The widget shown normally
- self._text_widget = urwid.Text(u"")
- # The widget shown when we have focus
- self._focus_text_widget = urwid.Text(u"")
- super(MandosClientWidget, self).__init__(
- update_hook=update_hook, delete_hook=delete_hook,
- *args, **kwargs)
- self.update()
- self.opened = False
-
- last_checked_ok = isoformat_to_datetime(self.properties
- [u"LastCheckedOK"])
- if last_checked_ok is None:
- self.last_checker_failed = True
- else:
- self.last_checker_failed = ((datetime.datetime.utcnow()
- - last_checked_ok)
- > datetime.timedelta
- (milliseconds=
- self.properties
- [u"Interval"]))
-
- if self.last_checker_failed:
- self.using_timer(True)
-
- if self.need_approval:
- self.using_timer(True)
-
- self.proxy.connect_to_signal(u"CheckerCompleted",
- self.checker_completed,
- client_interface,
- byte_arrays=True)
- self.proxy.connect_to_signal(u"CheckerStarted",
- self.checker_started,
- client_interface,
- byte_arrays=True)
- self.proxy.connect_to_signal(u"GotSecret",
- self.got_secret,
- client_interface,
- byte_arrays=True)
- self.proxy.connect_to_signal(u"NeedApproval",
- self.need_approval,
- client_interface,
- byte_arrays=True)
- self.proxy.connect_to_signal(u"Rejected",
- self.rejected,
- client_interface,
- byte_arrays=True)
-
- def property_changed(self, property=None, value=None):
- super(self, MandosClientWidget).property_changed(property,
- value)
- if property == u"ApprovalPending":
- using_timer(bool(value))
-
- def using_timer(self, flag):
- """Call this method with True or False when timer should be
- activated or deactivated.
- """
- old = self._update_timer_callback_lock
- if flag:
- self._update_timer_callback_lock += 1
- else:
- self._update_timer_callback_lock -= 1
- if old == 0 and self._update_timer_callback_lock:
- self._update_timer_callback_tag = (gobject.timeout_add
- (1000,
- self.update_timer))
- elif old and self._update_timer_callback_lock == 0:
- gobject.source_remove(self._update_timer_callback_tag)
- self._update_timer_callback_tag = None
-
- def checker_completed(self, exitstatus, condition, command):
- if exitstatus == 0:
- if self.last_checker_failed:
- self.last_checker_failed = False
- self.using_timer(False)
- #self.logger(u'Checker for client %s (command "%s")'
- # u' was successful'
- # % (self.properties[u"Name"], command))
- self.update()
- return
- # Checker failed
- if not self.last_checker_failed:
- self.last_checker_failed = True
- self.using_timer(True)
- if os.WIFEXITED(condition):
- self.logger(u'Checker for client %s (command "%s")'
- u' failed with exit code %s'
- % (self.properties[u"Name"], command,
- os.WEXITSTATUS(condition)))
- elif os.WIFSIGNALED(condition):
- self.logger(u'Checker for client %s (command "%s")'
- u' was killed by signal %s'
- % (self.properties[u"Name"], command,
- os.WTERMSIG(condition)))
- elif os.WCOREDUMP(condition):
- self.logger(u'Checker for client %s (command "%s")'
- u' dumped core'
- % (self.properties[u"Name"], command))
- else:
- self.logger(u'Checker for client %s completed'
- u' mysteriously')
- self.update()
-
- def checker_started(self, command):
- #self.logger(u'Client %s started checker "%s"'
- # % (self.properties[u"Name"], unicode(command)))
- pass
-
- def got_secret(self):
- self.last_checker_failed = False
- self.logger(u'Client %s received its secret'
- % self.properties[u"Name"])
-
- def need_approval(self, timeout, default):
- if not default:
- message = u'Client %s needs approval within %s seconds'
- else:
- message = u'Client %s will get its secret in %s seconds'
- self.logger(message
- % (self.properties[u"Name"], timeout/1000))
- self.using_timer(True)
-
- def rejected(self, reason):
- self.logger(u'Client %s was rejected; reason: %s'
- % (self.properties[u"Name"], reason))
-
- def selectable(self):
- """Make this a "selectable" widget.
- This overrides the method from urwid.FlowWidget."""
- return True
-
- def rows(self, (maxcol,), focus=False):
- """How many rows this widget will occupy might depend on
- whether we have focus or not.
- This overrides the method from urwid.FlowWidget"""
- return self.current_widget(focus).rows((maxcol,), focus=focus)
-
- def current_widget(self, focus=False):
- if focus or self.opened:
- return self._focus_widget
- return self._widget
-
- def update(self):
- "Called when what is visible on the screen should be updated."
- # How to add standout mode to a style
- with_standout = { u"normal": u"standout",
- u"bold": u"bold-standout",
- u"underline-blink":
- u"underline-blink-standout",
- u"bold-underline-blink":
- u"bold-underline-blink-standout",
- }
-
- # Rebuild focus and non-focus widgets using current properties
-
- # Base part of a client. Name!
- base = (u'%(name)s: '
- % {u"name": self.properties[u"Name"]})
- if not self.properties[u"Enabled"]:
- message = u"DISABLED"
- elif self.properties[u"ApprovalPending"]:
- timeout = datetime.timedelta(milliseconds
- = self.properties
- [u"ApprovalDelay"])
- last_approval_request = isoformat_to_datetime(
- self.properties[u"LastApprovalRequest"])
- if last_approval_request is not None:
- timer = timeout - (datetime.datetime.utcnow()
- - last_approval_request)
- else:
- timer = datetime.timedelta()
- if self.properties[u"ApprovedByDefault"]:
- message = u"Approval in %s. (d)eny?"
- else:
- message = u"Denial in %s. (a)pprove?"
- message = message % unicode(timer).rsplit(".", 1)[0]
- elif self.last_checker_failed:
- timeout = datetime.timedelta(milliseconds
- = self.properties
- [u"Timeout"])
- last_ok = isoformat_to_datetime(
- max((self.properties[u"LastCheckedOK"]
- or self.properties[u"Created"]),
- self.properties[u"LastEnabled"]))
- timer = timeout - (datetime.datetime.utcnow() - last_ok)
- message = (u'A checker has failed! Time until client'
- u' gets disabled: %s'
- % unicode(timer).rsplit(".", 1)[0])
- else:
- message = u"enabled"
- self._text = "%s%s" % (base, message)
-
- if not urwid.supports_unicode():
- self._text = self._text.encode("ascii", "replace")
- textlist = [(u"normal", self._text)]
- self._text_widget.set_text(textlist)
- self._focus_text_widget.set_text([(with_standout[text[0]],
- text[1])
- if isinstance(text, tuple)
- else text
- for text in textlist])
- self._widget = self._text_widget
- self._focus_widget = urwid.AttrWrap(self._focus_text_widget,
- "standout")
- # Run update hook, if any
- if self.update_hook is not None:
- self.update_hook()
-
- def update_timer(self):
- "called by gobject"
- self.update()
- return True # Keep calling this
-
- def delete(self):
- if self._update_timer_callback_tag is not None:
- gobject.source_remove(self._update_timer_callback_tag)
- self._update_timer_callback_tag = None
- if self.delete_hook is not None:
- self.delete_hook(self)
-
- def render(self, (maxcol,), focus=False):
- """Render differently if we have focus.
- This overrides the method from urwid.FlowWidget"""
- return self.current_widget(focus).render((maxcol,),
- focus=focus)
-
- def keypress(self, (maxcol,), key):
- """Handle keys.
- This overrides the method from urwid.FlowWidget"""
- if key == u"+":
- self.proxy.Enable(dbus_interface = client_interface)
- elif key == u"-":
- self.proxy.Disable(dbus_interface = client_interface)
- elif key == u"a":
- self.proxy.Approve(dbus.Boolean(True, variant_level=1),
- dbus_interface = client_interface)
- elif key == u"d":
- self.proxy.Approve(dbus.Boolean(False, variant_level=1),
- dbus_interface = client_interface)
- elif key == u"R" or key == u"_" or key == u"ctrl k":
- self.server_proxy_object.RemoveClient(self.proxy
- .object_path)
- elif key == u"s":
- self.proxy.StartChecker(dbus_interface = client_interface)
- elif key == u"S":
- self.proxy.StopChecker(dbus_interface = client_interface)
- elif key == u"C":
- self.proxy.CheckedOK(dbus_interface = client_interface)
- # xxx
-# elif key == u"p" or key == "=":
-# self.proxy.pause()
-# elif key == u"u" or key == ":":
-# self.proxy.unpause()
-# elif key == u"RET":
-# self.open()
- else:
- return key
-
- def property_changed(self, property=None, value=None,
- *args, **kwargs):
- """Call self.update() if old value is not new value.
- This overrides the method from MandosClientPropertyCache"""
- property_name = unicode(property)
- old_value = self.properties.get(property_name)
- super(MandosClientWidget, self).property_changed(
- property=property, value=value, *args, **kwargs)
- if self.properties.get(property_name) != old_value:
- self.update()
-
-
-class ConstrainedListBox(urwid.ListBox):
- """Like a normal urwid.ListBox, but will consume all "up" or
- "down" key presses, thus not allowing any containing widgets to
- use them as an excuse to shift focus away from this widget.
- """
- def keypress(self, (maxcol, maxrow), key):
- ret = super(ConstrainedListBox, self).keypress((maxcol,
- maxrow), key)
- if ret in (u"up", u"down"):
- return
- return ret
-
-
-class UserInterface(object):
- """This is the entire user interface - the whole screen
- with boxes, lists of client widgets, etc.
- """
- def __init__(self, max_log_length=1000):
- DBusGMainLoop(set_as_default=True)
-
- self.screen = urwid.curses_display.Screen()
-
- self.screen.register_palette((
- (u"normal",
- u"default", u"default", None),
- (u"bold",
- u"default", u"default", u"bold"),
- (u"underline-blink",
- u"default", u"default", u"underline"),
- (u"standout",
- u"default", u"default", u"standout"),
- (u"bold-underline-blink",
- u"default", u"default", (u"bold", u"underline")),
- (u"bold-standout",
- u"default", u"default", (u"bold", u"standout")),
- (u"underline-blink-standout",
- u"default", u"default", (u"underline", u"standout")),
- (u"bold-underline-blink-standout",
- u"default", u"default", (u"bold", u"underline",
- u"standout")),
- ))
-
- if urwid.supports_unicode():
- self.divider = u"─" # \u2500
- #self.divider = u"━" # \u2501
- else:
- #self.divider = u"-" # \u002d
- self.divider = u"_" # \u005f
-
- self.screen.start()
-
- self.size = self.screen.get_cols_rows()
-
- self.clients = urwid.SimpleListWalker([])
- self.clients_dict = {}
-
- # We will add Text widgets to this list
- self.log = []
- self.max_log_length = max_log_length
-
- # We keep a reference to the log widget so we can remove it
- # from the ListWalker without it getting destroyed
- self.logbox = ConstrainedListBox(self.log)
-
- # This keeps track of whether self.uilist currently has
- # self.logbox in it or not
- self.log_visible = True
- self.log_wrap = u"any"
-
- self.rebuild()
- self.log_message_raw((u"bold",
- u"Mandos Monitor version " + version))
- self.log_message_raw((u"bold",
- u"q: Quit ?: Help"))
-
- self.busname = domain + '.Mandos'
- self.main_loop = gobject.MainLoop()
- self.bus = dbus.SystemBus()
- mandos_dbus_objc = self.bus.get_object(
- self.busname, u"/", follow_name_owner_changes=True)
- self.mandos_serv = dbus.Interface(mandos_dbus_objc,
- dbus_interface
- = server_interface)
- try:
- mandos_clients = (self.mandos_serv
- .GetAllClientsWithProperties())
- except dbus.exceptions.DBusException:
- mandos_clients = dbus.Dictionary()
-
- (self.mandos_serv
- .connect_to_signal(u"ClientRemoved",
- self.find_and_remove_client,
- dbus_interface=server_interface,
- byte_arrays=True))
- (self.mandos_serv
- .connect_to_signal(u"ClientAdded",
- self.add_new_client,
- dbus_interface=server_interface,
- byte_arrays=True))
- (self.mandos_serv
- .connect_to_signal(u"ClientNotFound",
- self.client_not_found,
- dbus_interface=server_interface,
- byte_arrays=True))
- for path, client in mandos_clients.iteritems():
- client_proxy_object = self.bus.get_object(self.busname,
- path)
- self.add_client(MandosClientWidget(server_proxy_object
- =self.mandos_serv,
- proxy_object
- =client_proxy_object,
- properties=client,
- update_hook
- =self.refresh,
- delete_hook
- =self.remove_client,
- logger
- =self.log_message),
- path=path)
-
- def client_not_found(self, fingerprint, address):
- self.log_message((u"Client with address %s and fingerprint %s"
- u" could not be found" % (address,
- fingerprint)))
-
- def rebuild(self):
- """This rebuilds the User Interface.
- Call this when the widget layout needs to change"""
- self.uilist = []
- #self.uilist.append(urwid.ListBox(self.clients))
- self.uilist.append(urwid.Frame(ConstrainedListBox(self.
- clients),
- #header=urwid.Divider(),
- header=None,
- footer=
- urwid.Divider(div_char=
- self.divider)))
- if self.log_visible:
- self.uilist.append(self.logbox)
- pass
- self.topwidget = urwid.Pile(self.uilist)
-
- def log_message(self, message):
- timestamp = datetime.datetime.now().isoformat()
- self.log_message_raw(timestamp + u": " + message)
-
- def log_message_raw(self, markup):
- """Add a log message to the log buffer."""
- self.log.append(urwid.Text(markup, wrap=self.log_wrap))
- if (self.max_log_length
- and len(self.log) > self.max_log_length):
- del self.log[0:len(self.log)-self.max_log_length-1]
- self.logbox.set_focus(len(self.logbox.body.contents),
- coming_from=u"above")
- self.refresh()
-
- def toggle_log_display(self):
- """Toggle visibility of the log buffer."""
- self.log_visible = not self.log_visible
- self.rebuild()
- #self.log_message(u"Log visibility changed to: "
- # + unicode(self.log_visible))
-
- def change_log_display(self):
- """Change type of log display.
- Currently, this toggles wrapping of text lines."""
- if self.log_wrap == u"clip":
- self.log_wrap = u"any"
- else:
- self.log_wrap = u"clip"
- for textwidget in self.log:
- textwidget.set_wrap_mode(self.log_wrap)
- #self.log_message(u"Wrap mode: " + self.log_wrap)
-
- def find_and_remove_client(self, path, name):
- """Find an client from its object path and remove it.
-
- This is connected to the ClientRemoved signal from the
- Mandos server object."""
- try:
- client = self.clients_dict[path]
- except KeyError:
- # not found?
- return
- self.remove_client(client, path)
-
- def add_new_client(self, path):
- client_proxy_object = self.bus.get_object(self.busname, path)
- self.add_client(MandosClientWidget(server_proxy_object
- =self.mandos_serv,
- proxy_object
- =client_proxy_object,
- update_hook
- =self.refresh,
- delete_hook
- =self.remove_client,
- logger
- =self.log_message),
- path=path)
-
- def add_client(self, client, path=None):
- self.clients.append(client)
- if path is None:
- path = client.proxy.object_path
- self.clients_dict[path] = client
- self.clients.sort(None, lambda c: c.properties[u"Name"])
- self.refresh()
-
- def remove_client(self, client, path=None):
- self.clients.remove(client)
- if path is None:
- path = client.proxy.object_path
- del self.clients_dict[path]
- if not self.clients_dict:
- # Work around bug in Urwid 0.9.8.3 - if a SimpleListWalker
- # is completely emptied, we need to recreate it.
- self.clients = urwid.SimpleListWalker([])
- self.rebuild()
- self.refresh()
-
- def refresh(self):
- """Redraw the screen"""
- canvas = self.topwidget.render(self.size, focus=True)
- self.screen.draw_screen(self.size, canvas)
-
- def run(self):
- """Start the main loop and exit when it's done."""
- self.refresh()
- self._input_callback_tag = (gobject.io_add_watch
- (sys.stdin.fileno(),
- gobject.IO_IN,
- self.process_input))
- self.main_loop.run()
- # Main loop has finished, we should close everything now
- gobject.source_remove(self._input_callback_tag)
- self.screen.stop()
-
- def stop(self):
- self.main_loop.quit()
-
- def process_input(self, source, condition):
- keys = self.screen.get_input()
- translations = { u"ctrl n": u"down", # Emacs
- u"ctrl p": u"up", # Emacs
- u"ctrl v": u"page down", # Emacs
- u"meta v": u"page up", # Emacs
- u" ": u"page down", # less
- u"f": u"page down", # less
- u"b": u"page up", # less
- u"j": u"down", # vi
- u"k": u"up", # vi
- }
- for key in keys:
- try:
- key = translations[key]
- except KeyError: # :-)
- pass
-
- if key == u"q" or key == u"Q":
- self.stop()
- break
- elif key == u"window resize":
- self.size = self.screen.get_cols_rows()
- self.refresh()
- elif key == u"\f": # Ctrl-L
- self.refresh()
- elif key == u"l" or key == u"D":
- self.toggle_log_display()
- self.refresh()
- elif key == u"w" or key == u"i":
- self.change_log_display()
- self.refresh()
- elif key == u"?" or key == u"f1" or key == u"esc":
- if not self.log_visible:
- self.log_visible = True
- self.rebuild()
- self.log_message_raw((u"bold",
- u" ".
- join((u"q: Quit",
- u"?: Help",
- u"l: Log window toggle",
- u"TAB: Switch window",
- u"w: Wrap (log)"))))
- self.log_message_raw((u"bold",
- u" "
- .join((u"Clients:",
- u"+: Enable",
- u"-: Disable",
- u"R: Remove",
- u"s: Start new checker",
- u"S: Stop checker",
- u"C: Checker OK",
- u"a: Approve",
- u"d: Deny"))))
- self.refresh()
- elif key == u"tab":
- if self.topwidget.get_focus() is self.logbox:
- self.topwidget.set_focus(0)
- else:
- self.topwidget.set_focus(self.logbox)
- self.refresh()
- #elif (key == u"end" or key == u"meta >" or key == u"G"
- # or key == u">"):
- # pass # xxx end-of-buffer
- #elif (key == u"home" or key == u"meta <" or key == u"g"
- # or key == u"<"):
- # pass # xxx beginning-of-buffer
- #elif key == u"ctrl e" or key == u"$":
- # pass # xxx move-end-of-line
- #elif key == u"ctrl a" or key == u"^":
- # pass # xxx move-beginning-of-line
- #elif key == u"ctrl b" or key == u"meta (" or key == u"h":
- # pass # xxx left
- #elif key == u"ctrl f" or key == u"meta )" or key == u"l":
- # pass # xxx right
- #elif key == u"a":
- # pass # scroll up log
- #elif key == u"z":
- # pass # scroll down log
- elif self.topwidget.selectable():
- self.topwidget.keypress(self.size, key)
- self.refresh()
- return True
-
-ui = UserInterface()
-try:
- ui.run()
-except KeyboardInterrupt:
- ui.screen.stop()
-except Exception, e:
- ui.log_message(unicode(e))
- ui.screen.stop()
- raise
=== removed file 'mandos-monitor.xml'
--- mandos-monitor.xml 2010-09-30 06:24:20 +0000
+++ mandos-monitor.xml 1970-01-01 00:00:00 +0000
@@ -1,233 +0,0 @@
-
-
-
-
-%common;
-]>
-
-
-
- Mandos Manual
-
- Mandos
- &version;
- &TIMESTAMP;
-
-
- Björn
- Påhlsson
-
- belorn@fukt.bsnet.se
-
-
-
- Teddy
- Hogeborn
-
- teddy@fukt.bsnet.se
-
-
-
-
- 2010
- Teddy Hogeborn
- Björn Påhlsson
-
-
-
-
-
- &COMMANDNAME;
- 8
-
-
-
- &COMMANDNAME;
-
- Text-based GUI to control the Mandos server.
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
- DESCRIPTION
-
- &COMMANDNAME; is an interactive program to
- monitor and control the operations of the Mandos server (see
- mandos8).
-
-
-
-
- PURPOSE
-
- The purpose of this is to enable remote and unattended
- rebooting of client host computer with an
- encrypted root file system. See for details.
-
-
-
-
- OVERVIEW
-
-
- This program is used to monitor and control the Mandos server.
- In particular, it can be used to approve Mandos clients which
- have been configured to require approval. It also shows all
- significant events reported by the Mandos server.
-
-
-
-
- KEYS
-
- This program is used to monitor and control the Mandos server.
- In particular, it can be used to approve Mandos clients which
- have been configured to require approval. It also shows all
- significant events reported by the Mandos server.
-
-
- Global Keys
-
- Keys
- Function
-
-
-
- q, Q
- Quit
-
-
- Ctrl-L
- Redraw screen
-
-
- ?, F1
- Show help
-
-
- l, D
- Toggle log window
-
-
- TAB
- Switch window
-
-
- w, i
- Toggle log window line wrap
-
-
- Up, Ctrl-P, k
- Move up a line
-
-
- Down, Ctrl-N, j
- Move down a line
-
-
- PageUp, Meta-V, b
- Move up a page
-
-
- PageDown, Ctrl-V, SPACE, f
- Move down a page
-
-
-
- Client List Keys
-
- Keys
- Function
-
-
-
- +
- Enable client
-
-
- -
- Disable client
-
-
- a
- Approve client
-
-
- d
- Deny client
-
-
- R, _, Ctrl-K
- Remove client
-
-
- s
- Start checker for client
-
-
- S
- Stop checker for client
-
-
- C
- Force a successful check for this client.
-
-
-
-
-
- BUGS
-
- This program can currently only be used to monitor and control a
- Mandos server with the default D-Bus service name of
- Mandos
.
-
-
-
-
- EXAMPLE
-
-
- This program takes no options:
-
-
- &COMMANDNAME;
-
-
-
-
-
- SECURITY
-
- This program must be permitted to access the Mandos server via
- the D-Bus interface. This normally requires the root user, but
- could be configured otherwise by reconfiguring the D-Bus server.
-
-
-
-
- SEE ALSO
-
- mandos
- 8,
- mandos-ctl
- 8
-
-
-
-
-
-
-
-
-
=== removed file 'mandos-options.xml'
--- mandos-options.xml 2009-02-13 05:38:21 +0000
+++ mandos-options.xml 1970-01-01 00:00:00 +0000
@@ -1,89 +0,0 @@
-
-
-
-
-
-
-
-
-
- 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.
-
-
-
=== modified file 'mandos.conf'
--- mandos.conf 2009-02-13 05:38:21 +0000
+++ mandos.conf 2008-08-08 01:17:17 +0000
@@ -1,44 +1,7 @@
-# This file must have exactly one section named "DEFAULT".
-[DEFAULT]
-
-# These are the default values for the server, uncomment and change
-# them if needed.
-
-
-# If "interface" is set, the server will only listen to a specific
-# network interface.
-;interface =
-
-
-# If "address" is set, the server will only listen to a specific
-# address. This must currently be an IPv6 address; an IPv4 address
-# can be specified using the "::FFFF:192.0.2.3" syntax. Also, if this
-# is a link-local address, an interface should be set above.
-;address =
-
-
-# If "port" is set, the server to bind to that port. By default, the
-# server will listen to an arbitrary port.
-;port =
-
-
-# If "debug" is true, the server will run in the foreground and print
-# a lot of debugging information.
-;debug = False
-
-
-# GnuTLS priority for the TLS handshake. See gnutls_priority_init(3).
-;priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP
-
-
-# Zeroconf service name. You need to change this if you for some
-# reason want to run more than one server on the same *host*.
-# If there are name collisions on the same *network*, the server will
-# rename itself to "Mandos #2", etc.
-;servicename = Mandos
-
-# Whether to provide a D-Bus system bus interface or not
-;use_dbus = True
-
-# Whether to use IPv6. (Changing this is NOT recommended.)
-;use_ipv6 = True
+[server]
+#interface =
+#address =
+#port =
+#debug = False
+#priority = SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP
+#servicename = Mandos
=== removed file 'mandos.conf.xml'
--- mandos.conf.xml 2009-02-25 01:14:29 +0000
+++ mandos.conf.xml 1970-01-01 00:00:00 +0000
@@ -1,266 +0,0 @@
-
-
-/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.
-
-
-
-
-
-
-
-
-
-
-
=== removed file 'mandos.lsm'
--- mandos.lsm 2010-09-28 18:57:31 +0000
+++ mandos.lsm 1970-01-01 00:00:00 +0000
@@ -1,22 +0,0 @@
-Begin4
-Title: Mandos
-Version: 1.2
-Entered-date: 2010-09-28
-Description: The Mandos system allows computers to have encrypted
-root file systems and at the same time be capable of remote and/or
-unattended reboots.
-Keywords: boot, encryption, luks, cryptsetup, network, openpgp,
-tls, dm-crypt
-Author: teddy@fukt.bsnet.se (Teddy Hogeborn),
- belorn@fukt.bsnet.se (Björn Påhlsson)
-Maintained-by: teddy@fukt.bsnet.se (Teddy Hogeborn),
- belorn@fukt.bsnet.se (Björn Påhlsson)
-Primary-site: http://www.fukt.bsnet.se/mandos
- 132K mandos_1.2.orig.tar.gz
-Alternate-site: ftp://ftp.fukt.bsnet.se/pub/mandos
- 132K mandos_1.2.orig.tar.gz
-Platforms: Requires GCC, GNU libC, Avahi, GnuPG, Python 2.5, and
-various other libraries. While made for Debian GNU/Linux, it is
-probably portable to other distributions, but not other Unixes.
-Copying-policy: GNU General Public License version 3.0 or later
-End
=== removed file 'mandos.xml'
--- mandos.xml 2010-09-27 17:39:03 +0000
+++ mandos.xml 1970-01-01 00:00:00 +0000
@@ -1,730 +0,0 @@
-
-
-
-
-%common;
-]>
-
-
-
- Mandos Manual
-
- Mandos
- &version;
- &TIMESTAMP;
-
-
- Björn
- Påhlsson
-
- belorn@fukt.bsnet.se
-
-
-
- Teddy
- Hogeborn
-
- teddy@fukt.bsnet.se
-
-
-
-
- 2008
- 2009
- 2010
- Teddy Hogeborn
- Björn Påhlsson
-
-
-
-
-
- &COMMANDNAME;
- 8
-
-
-
- &COMMANDNAME;
-
- Gives encrypted passwords to authenticated Mandos clients
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
- &COMMANDNAME;
-
-
-
- &COMMANDNAME;
-
-
-
-
-
- DESCRIPTION
-
- &COMMANDNAME; is a server daemon which
- handles incoming request for passwords for a pre-defined list of
- client host computers. The Mandos server uses Zeroconf to
- announce itself on the local network, and uses TLS to
- communicate securely with and to authenticate the clients. The
- Mandos server uses IPv6 to allow Mandos clients to use IPv6
- link-local addresses, since the clients will probably not have
- any other addresses configured (see ).
- Any authenticated client is then given the stored pre-encrypted
- password for that specific client.
-
-
-
-
- PURPOSE
-
- The purpose of this is to enable remote and unattended
- rebooting of client host computer with an
- encrypted root file system. See for details.
-
-
-
-
- OPTIONS
-
-
-
-
-
-
- Show a help message and exit
-
-
-
-
-
-
- NAME
-
- NAME
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Run the server’s self-tests. This includes any unit
- tests, etc.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Set the debugging log level.
- LEVEL is a string, one of
- CRITICAL
,
- ERROR
,
- WARNING
,
- INFO
, or
- DEBUG
, in order of
- increasing verbosity. The default level is
- WARNING
.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Directory to search for configuration files. Default is
- /etc/mandos
. See
- mandos.conf
- 5 and
- mandos-clients.conf
- 5.
-
-
-
-
-
-
-
-
- Prints the program version and exit.
-
-
-
-
-
-
-
-
-
- See also .
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- OVERVIEW
-
-
- This program is the server part. It is a normal server program
- and will run in a normal system environment, not in an initial
- RAM disk environment.
-
-
-
-
- NETWORK PROTOCOL
-
- The Mandos server announces itself as a Zeroconf service of type
- _mandos._tcp
. The Mandos
- client connects to the announced address and port, and sends a
- line of text where the first whitespace-separated field is the
- protocol version, which currently is
- 1
. The client and server then
- start a TLS protocol handshake with a slight quirk: the Mandos
- server program acts as a TLS client
while the
- connecting Mandos client acts as a TLS server
.
- The Mandos client must supply an OpenPGP certificate, and the
- fingerprint of this certificate is used by the Mandos server to
- look up (in a list read from clients.conf
- at start time) which binary blob to give the client. No other
- authentication or authorization is done by the server.
-
-
- Mandos Protocol (Version 1)
-
- Mandos Client
- Direction
- Mandos Server
-
-
-
- Connect
- ->
-
-
- 1\r\n
- ->
-
-
- TLS handshake as TLS server
-
- <->
- TLS handshake as TLS client
-
-
-
- OpenPGP public key (part of TLS handshake)
- ->
-
-
-
- <-
- Binary blob (client will assume OpenPGP data)
-
-
-
- <-
- Close
-
-
-
-
-
- CHECKING
-
- The server will, by default, continually check that the clients
- are still up. If a client has not been confirmed as being up
- for some time, the client is assumed to be compromised and is no
- longer eligible to receive the encrypted password. (Manual
- intervention is required to re-enable a client.) The timeout,
- checker program, and interval between checks can be configured
- both globally and per client; see
- mandos-clients.conf
- 5. A client successfully
- receiving its password will also be treated as a successful
- checker run.
-
-
-
-
- APPROVAL
-
- The server can be configured to require manual approval for a
- client before it is sent its secret. The delay to wait for such
- approval and the default action (approve or deny) can be
- configured both globally and per client; see
- mandos-clients.conf
- 5. By default all clients
- will be approved immediately without delay.
-
-
- This can be used to deny a client its secret if not manually
- approved within a specified time. It can also be used to make
- the server delay before giving a client its secret, allowing
- optional manual denying of this specific client.
-
-
-
-
-
- LOGGING
-
- The server will send log message with various severity levels to
- /dev/log. With the
- option, it will log even more messages,
- and also show them on the console.
-
-
-
-
- D-BUS INTERFACE
-
- The server will by default provide a D-Bus system bus interface.
- This interface will only be accessible by the root user or a
- Mandos-specific user, if such a user exists. For documentation
- of the D-Bus API, see the file DBUS-API.
-
-
-
-
- EXIT STATUS
-
- The server will exit with a non-zero exit status only when a
- critical error is encountered.
-
-
-
-
- ENVIRONMENT
-
-
- PATH
-
-
- To start the configured checker (see ), the server uses
- /bin/sh, which in turn uses
- PATH to search for matching commands if
- an absolute path is not given. See
- sh1
- .
-
-
-
-
-
-
-
- FILES
-
- Use the option to change where
- &COMMANDNAME; looks for its configurations
- files. The default file names are listed here.
-
-
-
- /etc/mandos/mandos.conf
-
-
- Server-global settings. See
- mandos.conf
- 5 for details.
-
-
-
-
- /etc/mandos/clients.conf
-
-
- List of clients and client-specific settings. See
- mandos-clients.conf
- 5 for details.
-
-
-
-
- /var/run/mandos.pid
-
-
- The file containing the process id of the
- &COMMANDNAME; process started last.
-
-
-
-
- /dev/log
-
-
- The Unix domain socket to where local syslog messages are
- sent.
-
-
-
-
- /bin/sh
-
-
- This is used to start the configured checker command for
- each client. See
- mandos-clients.conf
- 5 for details.
-
-
-
-
-
-
-
- BUGS
-
- This server might, on especially fatal errors, emit a Python
- backtrace. This could be considered a feature.
-
-
- Currently, if a client is disabled due to having timed out, the
- server does not record this fact onto permanent storage. This
- has some security implications, see .
-
-
- There is no fine-grained control over logging and debug output.
-
-
- Debug mode is conflated with running in the foreground.
-
-
- The console log messages do not show a time stamp.
-
-
- This server does not check the expire time of clients’ OpenPGP
- keys.
-
-
-
-
- EXAMPLE
-
-
- Normal invocation needs no options:
-
-
- &COMMANDNAME;
-
-
-
-
- Run the server in debug mode, read configuration files from
- the ~/mandos directory, and use the
- Zeroconf service name Test
to not collide with
- any other official Mandos server on this host:
-
-
-
-
-&COMMANDNAME; --debug --configdir ~/mandos --servicename Test
-
-
-
-
-
- Run the server normally, but only listen to one interface and
- only on the link-local address on that interface:
-
-
-
-
-&COMMANDNAME; --interface eth7 --address fe80::aede:48ff:fe71:f6f2
-
-
-
-
-
-
- SECURITY
-
- SERVER
-
- Running this &COMMANDNAME; server program
- should not in itself present any security risk to the host
- computer running it. The program switches to a non-root user
- soon after startup.
-
-
-
- CLIENTS
-
- The server only gives out its stored data to clients which
- does have the OpenPGP key of the stored fingerprint. This is
- guaranteed by the fact that the client sends its OpenPGP
- public key in the TLS handshake; this ensures it to be
- genuine. The server computes the fingerprint of the key
- itself and looks up the fingerprint in its list of
- clients. The clients.conf file (see
- mandos-clients.conf
- 5)
- must be made non-readable by anyone
- except the user starting the server (usually root).
-
-
- As detailed in , the status of all
- client computers will continually be checked and be assumed
- compromised if they are gone for too long.
-
-
- If a client is compromised, its downtime should be duly noted
- by the server which would therefore disable the client. But
- if the server was ever restarted, it would re-read its client
- list from its configuration file and again regard all clients
- therein as enabled, and hence eligible to receive their
- passwords. Therefore, be careful when restarting servers if
- it is suspected that a client has, in fact, been compromised
- by parties who may now be running a fake Mandos client with
- the keys from the non-encrypted initial RAM
- image of the client host. What should be done in that case
- (if restarting the server program really is necessary) is to
- stop the server program, edit the configuration file to omit
- any suspect clients, and restart the server program.
-
-
- For more details on client-side security, see
- mandos-client
- 8mandos.
-
-
-
-
-
- SEE ALSO
-
-
- mandos-clients.conf
- 5,
- mandos.conf
- 5,
- mandos-client
- 8mandos,
- sh1
-
-
-
-
-
- Zeroconf
-
-
-
- Zeroconf is the network protocol standard used by clients
- for finding this Mandos server on the local network.
-
-
-
-
-
- Avahi
-
-
-
- Avahi is the library this server calls to implement
- Zeroconf service announcements.
-
-
-
-
-
- GnuTLS
-
-
-
- GnuTLS is the library this server uses to implement TLS for
- communicating securely with the client, and at the same time
- confidently get the client’s public OpenPGP key.
-
-
-
-
-
- RFC 4291: IP Version 6 Addressing
- Architecture
-
-
-
-
- Section 2.2: Text Representation of
- Addresses
-
-
-
- Section 2.5.5.2: IPv4-Mapped IPv6
- Address
-
-
-
- Section 2.5.6, Link-Local IPv6 Unicast
- Addresses
-
-
- The clients use IPv6 link-local addresses, which are
- immediately usable since a link-local addresses is
- automatically assigned to a network interfaces when it
- is brought up.
-
-
-
-
-
-
-
-
- RFC 4346: The Transport Layer Security (TLS)
- Protocol Version 1.1
-
-
-
- TLS 1.1 is the protocol implemented by GnuTLS.
-
-
-
-
-
- RFC 4880: OpenPGP Message Format
-
-
-
- The data sent to clients is binary encrypted OpenPGP data.
-
-
-
-
-
- RFC 5081: Using OpenPGP Keys for Transport Layer
- Security
-
-
-
- This is implemented by GnuTLS and used by this server so
- that OpenPGP keys can be used.
-
-
-
-
-
-
-
-
-
-
-
=== added file 'network-protocol.txt'
--- network-protocol.txt 1970-01-01 00:00:00 +0000
+++ network-protocol.txt 2008-08-07 22:30:45 +0000
@@ -0,0 +1,19 @@
+The Mandos server announces itself as a Zeroconf service of type
+"_mandos._tcp". The Mandos client sends a line of text where the first
+whitespace-separated field is the protocol version, which currently is
+"1". The client and server then start a TLS protocol handshake with a
+slight quirk: the Mandos server program acts as a TLS "client" while
+the connecting Mandos client acts as a TLS "server". The Mandos
+client must supply an OpenPGP certificate, and the fingerprint of this
+certificate is used by the Mandos server to look up (in a list read
+from a file at start time) which binary blob to give the client. No
+other authentication or authorization is done by the server.
+
+| Mandos Client | | Mandos Server |
+|--------------------------------------------+-----+---------------|
+| Connect | | |
+| "1\r\n" | -> | |
+| TLS handshake | <-> | TLS handshake |
+| OpenPGP public key (part of TLS handshake) | -> | |
+| | <- | Binary blob |
+| | | Close |
=== removed file 'overview.xml'
--- overview.xml 2008-09-13 15:36:18 +0000
+++ overview.xml 1970-01-01 00:00:00 +0000
@@ -1,17 +0,0 @@
-
-
-
- This is part of the Mandos system for allowing computers to have
- encrypted root file systems and at the same time be capable of
- remote and/or unattended reboots. The computers run a small client
- program in the initial RAM disk environment which
- will communicate with a server over a network. All network
- communication is encrypted using TLS. The
- clients are identified by the server using an OpenPGP key; each
- client has one unique to it. The server sends the clients an
- encrypted password. The encrypted password is decrypted by the
- clients using the same OpenPGP key, and the password is then used to
- unlock the root file system, whereupon the computers can continue
- booting normally.
-
=== removed file 'plugin-runner.conf'
--- plugin-runner.conf 2009-04-17 08:26:17 +0000
+++ plugin-runner.conf 1970-01-01 00:00:00 +0000
@@ -1,10 +0,0 @@
-## This is the configuration file for plugin-runner(8mandos). This
-## file should be installed as "/etc/mandos/plugin-runner.conf", and
-## will be copied to "/conf/conf.d/mandos/plugin-runner.conf" in the
-## initrd.img file.
-##
-## After editing this file, the initrd image file must be updated for
-## the changes to take effect!
-
-## Example:
-#--options-for=mandos-client:--debug
=== removed file 'plugin-runner.xml'
--- plugin-runner.xml 2009-01-18 06:41:57 +0000
+++ plugin-runner.xml 1970-01-01 00:00:00 +0000
@@ -1,640 +0,0 @@
-
-
-
-
-%common;
-]>
-
-
-
- Mandos Manual
-
- Mandos
- &version;
- &TIMESTAMP;
-
-
- Björn
- Påhlsson
-
- belorn@fukt.bsnet.se
-
-
-
- Teddy
- Hogeborn
-
- teddy@fukt.bsnet.se
-
-
-
-
- 2008
- 2009
- Teddy Hogeborn
- Björn Påhlsson
-
-
-
-
-
- &COMMANDNAME;
- 8mandos
-
-
-
- &COMMANDNAME;
-
- Run Mandos plugins, pass data from first to succeed.
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
- &COMMANDNAME;
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
-
-
- DESCRIPTION
-
- &COMMANDNAME; is a program which is meant to
- be specified as a keyscript
for the root disk in
- crypttab
- 5. The aim of this
- program is therefore to output a password, which then
- cryptsetup
- 8 will use to unlock the
- root disk.
-
-
- This program is not meant to be invoked directly, but can be in
- order to test it. Note that any password obtained will simply
- be output on standard output.
-
-
-
-
- PURPOSE
-
- The purpose of this is to enable remote and unattended
- rebooting of client host computer with an
- encrypted root file system. See for details.
-
-
-
-
- OPTIONS
-
-
-
-
-
-
- This option will add an environment variable setting to
- all plugins. This will override any inherited environment
- variable.
-
-
-
-
-
-
-
-
-
- This option will add an environment variable setting to
- the PLUGIN plugin. This will
- override any inherited environment variables or
- environment variables specified using
- .
-
-
-
-
-
-
-
-
-
- Pass some options to all plugins.
- OPTIONS is a comma separated
- list of options. This is not a very useful option, except
- for specifying the
- option to all plugins.
-
-
-
-
-
-
-
-
-
- Pass some options to a specific plugin. PLUGIN is the name (file basename) of a
- plugin, and OPTIONS is a comma
- separated list of options.
-
-
- Note that since options are not split on whitespace, the
- way to pass, to the plugin
- foo
, the option
- with the option argument
- baz
is either
- --options-for=foo:--bar=baz or
- --options-for=foo:--bar,baz. Using
- --options-for="foo:--bar baz". will
- not work.
-
-
-
-
-
-
-
-
-
- Disable the plugin named
- PLUGIN. The plugin will not be
- started.
-
-
-
-
-
-
-
-
-
- Re-enable the plugin named
- PLUGIN. This is only useful to
- undo a previous option, maybe
- from the configuration file.
-
-
-
-
-
-
-
-
- Change to group ID ID on
- startup. The default is 65534. All plugins will be
- started using this group ID. Note:
- This must be a number, not a name.
-
-
-
-
-
-
-
-
- Change to user ID ID on
- startup. The default is 65534. All plugins will be
- started using this user ID. Note:
- This must be a number, not a name.
-
-
-
-
-
-
-
-
- Specify a different plugin directory. The default is
- /lib/mandos/plugins.d, which will
- exist in the initial RAM disk
- environment.
-
-
-
-
-
-
-
-
- Specify a different file to read additional options from.
- See . Other command line options
- will override options specified in the file.
-
-
-
-
-
-
-
-
- Enable debug mode. This will enable a lot of output to
- standard error about what the program is doing. The
- program will still perform all other functions normally.
- The default is to not run in debug
- mode.
-
-
- The plugins will not be affected by
- this option. Use
-
- if complete debugging eruption is desired.
-
-
-
-
-
-
-
-
-
- Gives a help message about options and their meanings.
-
-
-
-
-
-
-
-
- Gives a short usage message.
-
-
-
-
-
-
-
-
-
- Prints the program version.
-
-
-
-
-
-
-
- OVERVIEW
-
-
- This program will run on the client side in the initial
- RAM disk environment, and is responsible for
- getting a password. It does this by running plugins, one of
- which will normally be the actual client program communicating
- with the server.
-
-
-
- PLUGINS
-
- This program will get a password by running a number of
- plugins, which are simply executable
- programs in a directory in the initial RAM
- disk environment. The default directory is
- /lib/mandos/plugins.d, but this can be
- changed with the option. The
- plugins are started in parallel, and the first plugin to output
- a password and exit with a successful exit
- code will make this plugin-runner output the password from that
- plugin, stop any other plugins, and exit.
-
-
-
- WRITING PLUGINS
-
- A plugin is simply a program which prints a password to its
- standard output and then exits with a successful (zero) exit
- status. If the exit status is not zero, any output on
- standard output will be ignored by the plugin runner. Any
- output on its standard error channel will simply be passed to
- the standard error of the plugin runner, usually the system
- console.
-
-
- If the password is a single-line, manually entered passprase,
- a final trailing newline character should
- not be printed.
-
-
- The plugin will run in the initial RAM disk environment, so
- care must be taken not to depend on any files or running
- services not available there.
-
-
- The plugin must exit cleanly and free all allocated resources
- upon getting the TERM signal, since this is what the plugin
- runner uses to stop all other plugins when one plugin has
- output a password and exited cleanly.
-
-
- The plugin must not use resources, like for instance reading
- from the standard input, without knowing that no other plugin
- is also using it.
-
-
- It is useful, but not required, for the plugin to take the
- option.
-
-
-
-
-
- FALLBACK
-
- If no plugins succeed, this program will, as a fallback, ask for
- a password on the console using getpass3,
- and output it. This is not meant to be the normal mode of
- operation, as there is a separate plugin for getting a password
- from the console.
-
-
-
-
- EXIT STATUS
-
- Exit status of this program is zero if no errors were
- encountered, and otherwise not. The fallback (see ) may or may not have succeeded in either
- case.
-
-
-
-
- ENVIRONMENT
-
- This program does not use any environment variables itself, it
- only passes on its environment to all the plugins. The
- environment passed to plugins can be modified using the
- and
- options.
-
-
-
-
- FILES
-
-
-
- /conf/conf.d/mandos/plugin-runner.conf
-
-
- Since this program will be run as a keyscript, there is
- little to no opportunity to pass command line arguments
- to it. Therefore, it will also
- read this file and use its contents as
- whitespace-separated command line options. Also,
- everything from a #
character to the end
- of a line is ignored.
-
-
- This program is meant to run in the initial RAM disk
- environment, so that is where this file is assumed to
- exist. The file does not need to exist in the normal
- file system.
-
-
- This file will be processed before
- the normal command line options, so the latter can
- override the former, if need be.
-
-
- This file name is the default; the file to read for
- arguments can be changed using the
- option.
-
-
-
-
-
-
-
-
- BUGS
-
- The option is ignored when
- specified from within a configuration file.
-
-
-
-
- EXAMPLE
-
-
- Normal invocation needs no options:
-
-
- &COMMANDNAME;
-
-
-
-
- Run the program, but not the plugins, in debug mode:
-
-
-
-
- &COMMANDNAME; --debug
-
-
-
-
-
- Run all plugins, but run the foo
plugin in
- debug mode:
-
-
-
-
- &COMMANDNAME; --options-for=foo:--debug
-
-
-
-
-
- Run all plugins, but not the program, in debug mode:
-
-
-
-
- &COMMANDNAME; --global-options=--debug
-
-
-
-
-
- Run plugins from a different directory, read a different
- configuration file, and add two options to the
- mandos-client
- 8mandos plugin:
-
-
-
-
-cd /etc/keys/mandos; &COMMANDNAME; --config-file=/etc/mandos/plugin-runner.conf --plugin-dir /usr/lib/mandos/plugins.d --options-for=mandos-client:--pubkey=pubkey.txt,--seckey=seckey.txt
-
-
-
-
-
- SECURITY
-
- This program will, when starting, try to switch to another user.
- If it is started as root, it will succeed, and will by default
- switch to user and group 65534, which are assumed to be
- non-privileged. This user and group is then what all plugins
- will be started as. Therefore, the only way to run a plugin as
- a privileged user is to have the set-user-ID or set-group-ID bit
- set on the plugin executable file (see
- execve2
- ).
-
-
- If this program is used as a keyscript in crypttab5
- , there is a slight risk that if this program
- fails to work, there might be no way to boot the system except
- for booting from another media and editing the initial RAM disk
- image to not run this program. This is, however, unlikely,
- since the password-prompt8mandos
- plugin will read a password from the console in
- case of failure of the other plugins, and this plugin runner
- will also, in case of catastrophic failure, itself fall back to
- asking and outputting a password on the console (see ).
-
-
-
-
- SEE ALSO
-
- cryptsetup
- 8,
- crypttab
- 5,
- execve
- 2,
- mandos
- 8,
- password-prompt
- 8mandos,
- mandos-client
- 8mandos
-
-
-
-
-
-
-
-
-
=== removed file 'plugins.d/askpass-fifo.c'
--- plugins.d/askpass-fifo.c 2010-09-26 18:32:58 +0000
+++ plugins.d/askpass-fifo.c 1970-01-01 00:00:00 +0000
@@ -1,174 +0,0 @@
-/* -*- coding: utf-8 -*- */
-/*
- * Askpass-FIFO - Read a password from a FIFO and output it
- *
- * Copyright © 2008-2010 Teddy Hogeborn
- * Copyright © 2008-2010 Björn Påhlsson
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- * .
- *
- * Contact the authors at .
- */
-
-#define _GNU_SOURCE /* TEMP_FAILURE_RETRY() */
-#include /* ssize_t */
-#include /* mkfifo(), S_IRUSR, S_IWUSR */
-#include /* and */
-#include /* errno, EACCES, ENOTDIR, ELOOP,
- ENAMETOOLONG, ENOSPC, EROFS,
- ENOENT, EEXIST, EFAULT, EMFILE,
- ENFILE, ENOMEM, EBADF, EINVAL, EIO,
- EISDIR, EFBIG */
-#include /* error() */
-#include /* EXIT_FAILURE, NULL, size_t, free(),
- realloc(), EXIT_SUCCESS */
-#include /* open(), O_RDONLY */
-#include /* read(), close(), write(),
- STDOUT_FILENO */
-#include /* EX_OSERR, EX_OSFILE,
- EX_UNAVAILABLE, EX_IOERR */
-
-
-int main(__attribute__((unused))int argc,
- __attribute__((unused))char **argv){
- int ret = 0;
- ssize_t sret;
-
- /* Create FIFO */
- const char passfifo[] = "/lib/cryptsetup/passfifo";
- ret = mkfifo(passfifo, S_IRUSR | S_IWUSR);
- if(ret == -1){
- int e = errno;
- error(0, errno, "mkfifo");
- switch(e){
- case EACCES:
- case ENOTDIR:
- case ELOOP:
- return EX_OSFILE;
- case ENAMETOOLONG:
- case ENOSPC:
- case EROFS:
- default:
- return EX_OSERR;
- case ENOENT:
- return EX_UNAVAILABLE; /* no "/lib/cryptsetup"? */
- case EEXIST:
- break; /* not an error */
- }
- }
-
- /* Open FIFO */
- int fifo_fd = open(passfifo, O_RDONLY);
- if(fifo_fd == -1){
- int e = errno;
- error(0, errno, "open");
- switch(e){
- case EACCES:
- case ENOENT:
- case EFAULT:
- return EX_UNAVAILABLE;
- case ENAMETOOLONG:
- case EMFILE:
- case ENFILE:
- case ENOMEM:
- default:
- return EX_OSERR;
- case ENOTDIR:
- case ELOOP:
- return EX_OSFILE;
- }
- }
-
- /* Read from FIFO */
- char *buf = NULL;
- size_t buf_len = 0;
- {
- size_t buf_allocated = 0;
- const size_t blocksize = 1024;
- do {
- if(buf_len + blocksize > buf_allocated){
- char *tmp = realloc(buf, buf_allocated + blocksize);
- if(tmp == NULL){
- error(0, errno, "realloc");
- free(buf);
- return EX_OSERR;
- }
- buf = tmp;
- buf_allocated += blocksize;
- }
- sret = read(fifo_fd, buf + buf_len, buf_allocated - buf_len);
- if(sret == -1){
- int e = errno;
- free(buf);
- errno = e;
- error(0, errno, "read");
- switch(e){
- case EBADF:
- case EFAULT:
- case EINVAL:
- default:
- return EX_OSERR;
- case EIO:
- return EX_IOERR;
- case EISDIR:
- return EX_UNAVAILABLE;
- }
- }
- buf_len += (size_t)sret;
- } while(sret != 0);
- }
-
- /* Close FIFO */
- close(fifo_fd);
-
- /* Print password to stdout */
- size_t written = 0;
- while(written < buf_len){
- sret = write(STDOUT_FILENO, buf + written, buf_len - written);
- if(sret == -1){
- int e = errno;
- free(buf);
- errno = e;
- error(0, errno, "write");
- switch(e){
- case EBADF:
- case EFAULT:
- case EINVAL:
- return EX_OSFILE;
- case EFBIG:
- case EIO:
- case ENOSPC:
- default:
- return EX_IOERR;
- }
- }
- written += (size_t)sret;
- }
- free(buf);
-
- ret = close(STDOUT_FILENO);
- if(ret == -1){
- int e = errno;
- error(0, errno, "close");
- switch(e){
- case EBADF:
- return EX_OSFILE;
- case EIO:
- default:
- return EX_IOERR;
- }
- }
- return EXIT_SUCCESS;
-}
=== removed file 'plugins.d/askpass-fifo.xml'
--- plugins.d/askpass-fifo.xml 2009-01-04 21:54:55 +0000
+++ plugins.d/askpass-fifo.xml 1970-01-01 00:00:00 +0000
@@ -1,162 +0,0 @@
-
-
-
-
-%common;
-]>
-
-
-
- Mandos Manual
-
- Mandos
- &version;
- &TIMESTAMP;
-
-
- Björn
- Påhlsson
-
- belorn@fukt.bsnet.se
-
-
-
- Teddy
- Hogeborn
-
- teddy@fukt.bsnet.se
-
-
-
-
- 2008
- 2009
- Teddy Hogeborn
- Björn Påhlsson
-
-
-
-
-
- &COMMANDNAME;
- 8mandos
-
-
-
- &COMMANDNAME;
- Mandos plugin to get a password from a
- FIFO.
-
-
-
-
- &COMMANDNAME;
-
-
-
-
- DESCRIPTION
-
- This program reads a password from a FIFO and
- outputs it to standard output.
-
-
- This program is not very useful on its own. This program is
- really meant to run as a plugin in the Mandos client-side system, where it is used as a
- fallback and alternative to retrieving passwords from a
- Mandos server.
-
-
- This program is meant to be imitate a feature of the
- askpass program, so that programs written to
- interface with it can keep working under the
- Mandos system.
-
-
-
-
- OPTIONS
-
- This program takes no options.
-
-
-
-
- EXIT STATUS
-
- If exit status is 0, the output from the program is the password
- as it was read. Otherwise, if exit status is other than 0, the
- program was interrupted or encountered an error, and any output
- so far could be corrupt and/or truncated, and should therefore
- be ignored.
-
-
-
-
- FILES
-
-
- /lib/cryptsetup/passfifo
-
-
- This is the FIFO where this program
- will read the password. If it does not exist, it will be
- created.
-
-
-
-
-
-
-
- EXAMPLE
-
- Note that normally, this program will not be invoked directly,
- but instead started by the Mandos plugin-runner8mandos
- .
-
-
-
- This program takes no options.
-
-
- &COMMANDNAME;
-
-
-
-
-
- SECURITY
-
- The only thing that could be considered worthy of note is
- this: This program is meant to be run by
- plugin-runner8mandos, and will, when run
- standalone, outside, in a normal environment, immediately output
- on its standard output any presumably secret password it just
- received. Therefore, when running this program standalone
- (which should never normally be done), take care not to type in
- any real secret password by force of habit, since it would then
- immediately be shown as output.
-
-
-
-
- SEE ALSO
-
- fifo
- 7,
- plugin-runner
- 8mandos
-
-
-
-
-
-
-
-
=== removed file 'plugins.d/mandos-client.xml'
--- plugins.d/mandos-client.xml 2010-09-26 18:32:58 +0000
+++ plugins.d/mandos-client.xml 1970-01-01 00:00:00 +0000
@@ -1,654 +0,0 @@
-
-
-
-
-%common;
-]>
-
-
-
- Mandos Manual
-
- Mandos
- &version;
- &TIMESTAMP;
-
-
- Björn
- Påhlsson
-
- belorn@fukt.bsnet.se
-
-
-
- Teddy
- Hogeborn
-
- teddy@fukt.bsnet.se
-
-
-
-
- 2008
- 2009
- Teddy Hogeborn
- Björn Påhlsson
-
-
-
-
-
- &COMMANDNAME;
- 8mandos
-
-
-
- &COMMANDNAME;
-
- Client for Mandos
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
- &COMMANDNAME;
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
-
-
- DESCRIPTION
-
- &COMMANDNAME; is a client program that
- communicates with mandos8
- to get a password. In slightly more detail, this client program
- brings up a network interface, uses the interface’s IPv6
- link-local address to get network connectivity, uses Zeroconf to
- find servers on the local network, and communicates with servers
- using TLS with an OpenPGP key to ensure authenticity and
- confidentiality. This client program keeps running, trying all
- servers on the network, until it receives a satisfactory reply
- or a TERM signal is received. If no servers are found, or after
- all servers have been tried, it waits indefinitely for new
- servers to appear.
-
-
- This program is not meant to be run directly; it is really meant
- to run as a plugin of the Mandos
- plugin-runner
- 8mandos, which runs in the
- initial RAM disk environment because it is
- specified as a keyscript
in the
- crypttab5
- file.
-
-
-
-
- PURPOSE
-
- The purpose of this is to enable remote and unattended
- rebooting of client host computer with an
- encrypted root file system. See for details.
-
-
-
-
- OPTIONS
-
- This program is commonly not invoked from the command line; it
- is normally started by the Mandos
- plugin runner, see plugin-runner8mandos
- . Any command line options this program accepts
- are therefore normally provided by the plugin runner, and not
- directly.
-
-
-
-
-
-
-
-
- Do not use Zeroconf to locate servers. Connect directly
- to only one specified Mandos
- server. Note that an IPv6 address has colon characters in
- it, so the last colon character is
- assumed to separate the address from the port number.
-
-
- This option is normally only useful for testing and
- debugging.
-
-
-
-
-
-
-
-
-
- Network interface that will be brought up and scanned for
- Mandos servers to connect to. The default is the empty
- string, which will automatically choose an appropriate
- interface.
-
-
- If the option is used, this
- specifies the interface to use to connect to the address
- given.
-
-
- Note that since this program will normally run in the
- initial RAM disk environment, the interface must be an
- interface which exists at that stage. Thus, the interface
- can not be a pseudo-interface such as br0
- or tun0
; such interfaces will not exist
- until much later in the boot process, and can not be used
- by this program.
-
-
- NAME can be the string
- none
; this will not use
- any specific interface, and will not bring up an interface
- on startup. This is not recommended, and only meant for
- advanced users.
-
-
-
-
-
-
-
-
-
- OpenPGP public key file name. The default name is
- /conf/conf.d/mandos/pubkey.txt
.
-
-
-
-
-
-
-
-
-
- OpenPGP secret key file name. The default name is
- /conf/conf.d/mandos/seckey.txt
.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Sets the number of bits to use for the prime number in the
- TLS Diffie-Hellman key exchange. Default is 1024.
-
-
-
-
-
-
-
-
- After bringing the network interface up, the program waits
- for the interface to arrive in a running
- state before proceeding. During this time, the kernel log
- level will be lowered to reduce clutter on the system
- console, alleviating any other plugins which might be
- using the system console. This option sets the upper
- limit of seconds to wait. The default is 2.5 seconds.
-
-
-
-
-
-
-
-
- Enable debug mode. This will enable a lot of output to
- standard error about what the program is doing. The
- program will still perform all other functions normally.
-
-
- It will also enable debug mode in the Avahi and GnuTLS
- libraries, making them print large amounts of debugging
- output.
-
-
-
-
-
-
-
-
-
- Gives a help message about options and their meanings.
-
-
-
-
-
-
-
-
- Gives a short usage message.
-
-
-
-
-
-
-
-
-
- Prints the program version.
-
-
-
-
-
-
-
- OVERVIEW
-
-
- This program is the client part. It is a plugin started by
- plugin-runner
- 8mandos which will run in
- an initial RAM disk environment.
-
-
- This program could, theoretically, be used as a keyscript in
- /etc/crypttab, but it would then be
- impossible to enter a password for the encrypted root disk at
- the console, since this program does not read from the console
- at all. This is why a separate plugin runner (
- plugin-runner
- 8mandos) is used to run
- both this program and others in in parallel,
- one of which will prompt for passwords on
- the system console.
-
-
-
-
- EXIT STATUS
-
- This program will exit with a successful (zero) exit status if a
- server could be found and the password received from it could be
- successfully decrypted and output on standard output. The
- program will exit with a non-zero exit status only if a critical
- error occurs. Otherwise, it will forever connect to new
- Mandos servers as they appear, trying
- to get a decryptable password and print it.
-
-
-
-
- ENVIRONMENT
-
- This program does not use any environment variables, not even
- the ones provided by cryptsetup8
- .
-
-
-
-
- FILES
-
-
- /conf/conf.d/mandos/pubkey.txt
- /conf/conf.d/mandos/seckey.txt
-
-
- OpenPGP public and private key files, in ASCII
- Armor
format. These are the default file names,
- they can be changed with the and
- options.
-
-
-
-
-
-
-
-
-
-
-
-
-
- EXAMPLE
-
- Note that normally, command line options will not be given
- directly, but via options for the Mandos plugin-runner
- 8mandos.
-
-
-
- Normal invocation needs no options, if the network interface
- is eth0
:
-
-
- &COMMANDNAME;
-
-
-
-
- Search for Mandos servers (and connect to them) using another
- interface:
-
-
-
- &COMMANDNAME; --interface eth1
-
-
-
-
- Run in debug mode, and use a custom key:
-
-
-
-
-&COMMANDNAME; --debug --pubkey keydir/pubkey.txt --seckey keydir/seckey.txt
-
-
-
-
-
- Run in debug mode, with a custom key, and do not use Zeroconf
- to locate a server; connect directly to the IPv6 link-local
- address fe80::aede:48ff:fe71:f6f2
, port 4711,
- using interface eth2:
-
-
-
-
-&COMMANDNAME; --debug --pubkey keydir/pubkey.txt --seckey keydir/seckey.txt --connect fe80::aede:48ff:fe71:f6f2:4711 --interface eth2
-
-
-
-
-
-
- SECURITY
-
- This program is set-uid to root, but will switch back to the
- original (and presumably non-privileged) user and group after
- bringing up the network interface.
-
-
- To use this program for its intended purpose (see ), the password for the root file system will
- have to be given out to be stored in a server computer, after
- having been encrypted using an OpenPGP key. This encrypted data
- which will be stored in a server can only be decrypted by the
- OpenPGP key, and the data will only be given out to those
- clients who can prove they actually have that key. This key,
- however, is stored unencrypted on the client side in its initial
- RAM disk image file system. This is normally
- readable by all, but this is normally fixed during installation
- of this program; file permissions are set so that no-one is able
- to read that file.
-
-
- The only remaining weak point is that someone with physical
- access to the client hard drive might turn off the client
- computer, read the OpenPGP keys directly from the hard drive,
- and communicate with the server. To safeguard against this, the
- server is supposed to notice the client disappearing and stop
- giving out the encrypted data. Therefore, it is important to
- set the timeout and checker interval values tightly on the
- server. See mandos8.
-
-
- It will also help if the checker program on the server is
- configured to request something from the client which can not be
- spoofed by someone else on the network, unlike unencrypted
- ICMP echo (ping
) replies.
-
-
- Note: This makes it completely insecure to
- have Mandos clients which dual-boot
- to another operating system which is not
- trusted to keep the initial RAM disk image
- confidential.
-
-
-
-
- SEE ALSO
-
- cryptsetup
- 8,
- crypttab
- 5,
- mandos
- 8,
- password-prompt
- 8mandos,
- plugin-runner
- 8mandos
-
-
-
-
- Zeroconf
-
-
-
- Zeroconf is the network protocol standard used for finding
- Mandos servers on the local network.
-
-
-
-
-
- Avahi
-
-
-
- Avahi is the library this program calls to find Zeroconf
- services.
-
-
-
-
-
- GnuTLS
-
-
-
- GnuTLS is the library this client uses to implement TLS for
- communicating securely with the server, and at the same time
- send the public OpenPGP key to the server.
-
-
-
-
-
- GPGME
-
-
-
- GPGME is the library used to decrypt the OpenPGP data sent
- by the server.
-
-
-
-
-
- RFC 4291: IP Version 6 Addressing
- Architecture
-
-
-
-
- Section 2.2: Text Representation of
- Addresses
-
-
-
- Section 2.5.5.2: IPv4-Mapped IPv6
- Address
-
-
-
- Section 2.5.6, Link-Local IPv6 Unicast
- Addresses
-
-
- This client uses IPv6 link-local addresses, which are
- immediately usable since a link-local addresses is
- automatically assigned to a network interfaces when it
- is brought up.
-
-
-
-
-
-
-
-
- RFC 4346: The Transport Layer Security (TLS)
- Protocol Version 1.1
-
-
-
- TLS 1.1 is the protocol implemented by GnuTLS.
-
-
-
-
-
- RFC 4880: OpenPGP Message Format
-
-
-
- The data received from the server is binary encrypted
- OpenPGP data.
-
-
-
-
-
- RFC 5081: Using OpenPGP Keys for Transport Layer
- Security
-
-
-
- This is implemented by GnuTLS and used by this program so
- that OpenPGP keys can be used.
-
-
-
-
-
-
-
-
-
-
-
-
=== modified file 'plugins.d/password-prompt.c'
--- plugins.d/password-prompt.c 2010-09-26 18:32:58 +0000
+++ plugins.d/password-prompt.c 2008-08-07 21:45:41 +0000
@@ -1,9 +1,8 @@
-/* -*- coding: utf-8; mode: c; mode: orgtbl -*- */
+/* -*- coding: utf-8 -*- */
/*
- * Password-prompt - Read a password from the terminal and print it
- *
- * Copyright © 2008-2010 Teddy Hogeborn
- * Copyright © 2008-2010 Björn Påhlsson
+ * Passprompt - Read a password from the terminal and print it
+ *
+ * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -19,7 +18,8 @@
* along with this program. If not, see
* .
*
- * Contact the authors at .
+ * Contact the authors at and
+ * .
*/
#define _GNU_SOURCE /* getline() */
@@ -32,47 +32,33 @@
#include /* sig_atomic_t, raise(), struct
sigaction, sigemptyset(),
sigaction(), sigaddset(), SIGINT,
- SIGQUIT, SIGHUP, SIGTERM,
- raise() */
-#include /* NULL, size_t, ssize_t */
+ SIGQUIT, SIGHUP, SIGTERM */
+#include /* NULL, size_t */
#include /* ssize_t */
#include /* EXIT_SUCCESS, EXIT_FAILURE,
- getenv() */
+ getopt_long */
#include /* fprintf(), stderr, getline(),
- stdin, feof(), fputc()
- */
-#include /* errno, EBADF, ENOTTY, EINVAL,
- EFAULT, EFBIG, EIO, ENOSPC, EINTR
- */
-#include /* error() */
+ stdin, feof(), perror(), fputc(),
+ stdout, getopt_long */
+#include /* errno, EINVAL */
#include /* or, not */
#include /* bool, false, true */
-#include /* strlen, rindex */
-#include /* struct argp_option, struct
- argp_state, struct argp,
- argp_parse(), error_t,
- ARGP_KEY_ARG, ARGP_KEY_END,
- ARGP_ERR_UNKNOWN */
-#include /* EX_SOFTWARE, EX_OSERR,
- EX_UNAVAILABLE, EX_IOERR, EX_OK */
+#include /* strlen, rindex, strncmp, strcmp */
+#include /* struct argp_option,
+ struct argp_state, struct argp,
+ argp_parse() */
-volatile sig_atomic_t quit_now = 0;
-int signal_received;
+volatile bool quit_now = false;
bool debug = false;
-const char *argp_program_version = "password-prompt " VERSION;
+const char *argp_program_version = "passprompt 0.9";
const char *argp_program_bug_address = "";
-static void termination_handler(int signum){
- if(quit_now){
- return;
- }
- quit_now = 1;
- signal_received = signum;
+static void termination_handler(__attribute__((unused))int signum){
+ quit_now = true;
}
int main(int argc, char **argv){
- ssize_t sret;
- int ret;
+ ssize_t ret;
size_t n;
struct termios t_new, t_old;
char *buffer = NULL;
@@ -85,310 +71,117 @@
struct argp_option options[] = {
{ .name = "prefix", .key = 'p',
.arg = "PREFIX", .flags = 0,
- .doc = "Prefix shown before the prompt", .group = 2 },
+ .doc = "Prefix used before the passprompt", .group = 2 },
{ .name = "debug", .key = 128,
.doc = "Debug mode", .group = 3 },
- /*
- * These reproduce what we would get without ARGP_NO_HELP
- */
- { .name = "help", .key = '?',
- .doc = "Give this help list", .group = -1 },
- { .name = "usage", .key = -3,
- .doc = "Give a short usage message", .group = -1 },
- { .name = "version", .key = 'V',
- .doc = "Print program version", .group = -1 },
{ .name = NULL }
};
-
- error_t parse_opt (int key, char *arg, struct argp_state *state){
- errno = 0;
- switch (key){
+
+ error_t parse_opt (int key, char *arg, struct argp_state *state) {
+ /* Get the INPUT argument from `argp_parse', which we know is a
+ pointer to our plugin list pointer. */
+ switch (key) {
case 'p':
prefix = arg;
break;
case 128:
debug = true;
break;
- /*
- * These reproduce what we would get without ARGP_NO_HELP
- */
- case '?': /* --help */
- argp_state_help(state, state->out_stream,
- (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
- & ~(unsigned int)ARGP_HELP_EXIT_OK);
- case -3: /* --usage */
- argp_state_help(state, state->out_stream,
- ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
- case 'V': /* --version */
- fprintf(state->out_stream, "%s\n", argp_program_version);
- exit(argp_err_exit_status);
+ case ARGP_KEY_ARG:
+ argp_usage (state);
+ break;
+ case ARGP_KEY_END:
break;
default:
return ARGP_ERR_UNKNOWN;
}
- return errno;
+ return 0;
}
-
+
struct argp argp = { .options = options, .parser = parse_opt,
.args_doc = "",
- .doc = "Mandos password-prompt -- Read and"
- " output a password" };
- ret = argp_parse(&argp, argc, argv,
- ARGP_IN_ORDER | ARGP_NO_HELP, NULL, NULL);
- switch(ret){
- case 0:
- break;
- case ENOMEM:
- default:
- errno = ret;
- error(0, errno, "argp_parse");
- return EX_OSERR;
- case EINVAL:
- return EX_USAGE;
- }
+ .doc = "Mandos Passprompt -- Provides a passprompt" };
+ argp_parse (&argp, argc, argv, 0, 0, NULL);
}
-
- if(debug){
+
+ if (debug){
fprintf(stderr, "Starting %s\n", argv[0]);
}
- if(debug){
+ if (debug){
fprintf(stderr, "Storing current terminal attributes\n");
}
- if(tcgetattr(STDIN_FILENO, &t_old) != 0){
- int e = errno;
- error(0, errno, "tcgetattr");
- switch(e){
- case EBADF:
- case ENOTTY:
- return EX_UNAVAILABLE;
- default:
- return EX_OSERR;
- }
+ if (tcgetattr(STDIN_FILENO, &t_old) != 0){
+ return EXIT_FAILURE;
}
sigemptyset(&new_action.sa_mask);
- ret = sigaddset(&new_action.sa_mask, SIGINT);
- if(ret == -1){
- error(0, errno, "sigaddset");
- return EX_OSERR;
- }
- ret = sigaddset(&new_action.sa_mask, SIGHUP);
- if(ret == -1){
- error(0, errno, "sigaddset");
- return EX_OSERR;
- }
- ret = sigaddset(&new_action.sa_mask, SIGTERM);
- if(ret == -1){
- error(0, errno, "sigaddset");
- return EX_OSERR;
- }
- /* Need to check if the handler is SIG_IGN before handling:
- | [[info:libc:Initial Signal Actions]] |
- | [[info:libc:Basic Signal Handling]] |
- */
- ret = sigaction(SIGINT, NULL, &old_action);
- if(ret == -1){
- error(0, errno, "sigaction");
- return EX_OSERR;
- }
- if(old_action.sa_handler != SIG_IGN){
- ret = sigaction(SIGINT, &new_action, NULL);
- if(ret == -1){
- error(0, errno, "sigaction");
- return EX_OSERR;
- }
- }
- ret = sigaction(SIGHUP, NULL, &old_action);
- if(ret == -1){
- error(0, errno, "sigaction");
- return EX_OSERR;
- }
- if(old_action.sa_handler != SIG_IGN){
- ret = sigaction(SIGHUP, &new_action, NULL);
- if(ret == -1){
- error(0, errno, "sigaction");
- return EX_OSERR;
- }
- }
- ret = sigaction(SIGTERM, NULL, &old_action);
- if(ret == -1){
- error(0, errno, "sigaction");
- return EX_OSERR;
- }
- if(old_action.sa_handler != SIG_IGN){
- ret = sigaction(SIGTERM, &new_action, NULL);
- if(ret == -1){
- error(0, errno, "sigaction");
- return EX_OSERR;
- }
- }
-
-
- if(debug){
+ sigaddset(&new_action.sa_mask, SIGINT);
+ sigaddset(&new_action.sa_mask, SIGHUP);
+ sigaddset(&new_action.sa_mask, SIGTERM);
+ sigaction(SIGINT, NULL, &old_action);
+ if (old_action.sa_handler != SIG_IGN)
+ sigaction(SIGINT, &new_action, NULL);
+ sigaction(SIGHUP, NULL, &old_action);
+ if (old_action.sa_handler != SIG_IGN)
+ sigaction(SIGHUP, &new_action, NULL);
+ sigaction(SIGTERM, NULL, &old_action);
+ if (old_action.sa_handler != SIG_IGN)
+ sigaction(SIGTERM, &new_action, NULL);
+
+
+ if (debug){
fprintf(stderr, "Removing echo flag from terminal attributes\n");
}
t_new = t_old;
- t_new.c_lflag &= ~(tcflag_t)ECHO;
- if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
- int e = errno;
- error(0, errno, "tcsetattr-echo");
- switch(e){
- case EBADF:
- case ENOTTY:
- return EX_UNAVAILABLE;
- case EINVAL:
- default:
- return EX_OSERR;
- }
+ t_new.c_lflag &= ~ECHO;
+ if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_new) != 0){
+ perror("tcsetattr-echo");
+ return EXIT_FAILURE;
}
-
- if(debug){
+
+ if (debug){
fprintf(stderr, "Waiting for input from stdin \n");
}
while(true){
- if(quit_now){
- if(debug){
- fprintf(stderr, "Interrupted by signal, exiting.\n");
- }
+ if (quit_now){
status = EXIT_FAILURE;
break;
}
if(prefix){
- fprintf(stderr, "%s ", prefix);
- }
- {
- const char *cryptsource = getenv("CRYPTTAB_SOURCE");
- const char *crypttarget = getenv("CRYPTTAB_NAME");
- /* Before cryptsetup 1.1.0~rc2 */
- if(cryptsource == NULL){
- cryptsource = getenv("cryptsource");
- }
- if(crypttarget == NULL){
- crypttarget = getenv("crypttarget");
- }
- const char *const prompt1 = "Unlocking the disk";
- const char *const prompt2 = "Enter passphrase";
- if(cryptsource == NULL){
- if(crypttarget == NULL){
- fprintf(stderr, "%s to unlock the disk: ", prompt2);
- } else {
- fprintf(stderr, "%s (%s)\n%s: ", prompt1, crypttarget,
- prompt2);
- }
- } else {
- if(crypttarget == NULL){
- fprintf(stderr, "%s %s\n%s: ", prompt1, cryptsource,
- prompt2);
- } else {
- fprintf(stderr, "%s %s (%s)\n%s: ", prompt1, cryptsource,
- crypttarget, prompt2);
- }
- }
- }
- sret = getline(&buffer, &n, stdin);
- if(sret > 0){
+ fprintf(stderr, "%s Password: ", prefix);
+ } else {
+ fprintf(stderr, "Password: ");
+ }
+ ret = getline(&buffer, &n, stdin);
+ if (ret > 0){
+ fprintf(stdout, "%s", buffer);
status = EXIT_SUCCESS;
- /* Make n = data size instead of allocated buffer size */
- n = (size_t)sret;
- /* Strip final newline */
- if(n > 0 and buffer[n-1] == '\n'){
- buffer[n-1] = '\0'; /* not strictly necessary */
- n--;
- }
- size_t written = 0;
- while(written < n){
- sret = write(STDOUT_FILENO, buffer + written, n - written);
- if(sret < 0){
- int e = errno;
- error(0, errno, "write");
- switch(e){
- case EBADF:
- case EFAULT:
- case EINVAL:
- case EFBIG:
- case EIO:
- case ENOSPC:
- default:
- status = EX_IOERR;
- break;
- case EINTR:
- status = EXIT_FAILURE;
- break;
- }
- break;
- }
- written += (size_t)sret;
- }
- sret = close(STDOUT_FILENO);
- if(sret == -1){
- int e = errno;
- error(0, errno, "close");
- switch(e){
- case EBADF:
- status = EX_OSFILE;
- break;
- case EIO:
- default:
- status = EX_IOERR;
- break;
- }
- }
break;
}
- if(sret < 0){
- int e = errno;
- if(errno != EINTR and not feof(stdin)){
- error(0, errno, "getline");
- switch(e){
- case EBADF:
- status = EX_UNAVAILABLE;
- case EIO:
- case EINVAL:
- default:
- status = EX_IOERR;
- break;
- }
+ if (ret < 0){
+ if (errno != EINTR and not feof(stdin)){
+ perror("getline");
+ status = EXIT_FAILURE;
break;
}
}
- /* if(sret == 0), then the only sensible thing to do is to retry to
+ /* if(ret == 0), then the only sensible thing to do is to retry to
read from stdin */
fputc('\n', stderr);
- if(debug and not quit_now){
- /* If quit_now is nonzero, we were interrupted by a signal, and
- will print that later, so no need to show this too. */
- fprintf(stderr, "getline() returned 0, retrying.\n");
- }
}
- free(buffer);
-
- if(debug){
+ if (debug){
fprintf(stderr, "Restoring terminal attributes\n");
}
- if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
- error(0, errno, "tcsetattr+echo");
- }
-
- if(quit_now){
- sigemptyset(&old_action.sa_mask);
- old_action.sa_handler = SIG_DFL;
- ret = sigaction(signal_received, &old_action, NULL);
- if(ret == -1){
- error(0, errno, "sigaction");
- }
- raise(signal_received);
- }
-
- if(debug){
- fprintf(stderr, "%s is exiting with status %d\n", argv[0],
- status);
- }
- if(status == EXIT_SUCCESS or status == EX_OK){
- fputc('\n', stderr);
+ if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t_old) != 0){
+ perror("tcsetattr+echo");
+ }
+
+ if (debug){
+ fprintf(stderr, "%s is exiting\n", argv[0]);
}
return status;
=== removed file 'plugins.d/password-prompt.xml'
--- plugins.d/password-prompt.xml 2009-10-30 16:23:43 +0000
+++ plugins.d/password-prompt.xml 1970-01-01 00:00:00 +0000
@@ -1,308 +0,0 @@
-
-
-
-
-%common;
-]>
-
-
-
- Mandos Manual
-
- Mandos
- &version;
- &TIMESTAMP;
-
-
- Björn
- Påhlsson
-
- belorn@fukt.bsnet.se
-
-
-
- Teddy
- Hogeborn
-
- teddy@fukt.bsnet.se
-
-
-
-
- 2008
- 2009
- Teddy Hogeborn
- Björn Påhlsson
-
-
-
-
-
- &COMMANDNAME;
- 8mandos
-
-
-
- &COMMANDNAME;
- Prompt for a password and output it.
-
-
-
-
- &COMMANDNAME;
-
-
- PREFIX
-
-
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
- &COMMANDNAME;
-
-
-
- &COMMANDNAME;
-
-
-
-
-
-
-
-
- DESCRIPTION
-
- All &COMMANDNAME; does is prompt for a
- password and output any given password to standard output.
-
-
- This program is not very useful on its own. This program is
- really meant to run as a plugin in the Mandos client-side system, where it is used as a
- fallback and alternative to retrieving passwords from a
- Mandos server.
-
-
- This program is little more than a getpass3
- wrapper, although actual use of that function is not guaranteed
- or implied.
-
-
-
-
- OPTIONS
-
- This program is commonly not invoked from the command line; it
- is normally started by the Mandos
- plugin runner, see plugin-runner8mandos
- . Any command line options this program accepts
- are therefore normally provided by the plugin runner, and not
- directly.
-
-
-
-
-
-
-
-
- Prefix string shown before the password prompt.
-
-
-
-
-
-
-
-
- Enable debug mode. This will enable a lot of output to
- standard error about what the program is doing. The
- program will still perform all other functions normally.
-
-
-
-
-
-
-
-
-
- Gives a help message about options and their meanings.
-
-
-
-
-
-
-
-
- Gives a short usage message.
-
-
-
-
-
-
-
-
-
- Prints the program version.
-
-
-
-
-
-
-
- EXIT STATUS
-
- If exit status is 0, the output from the program is the password
- as it was read. Otherwise, if exit status is other than 0, the
- program has encountered an error, and any output so far could be
- corrupt and/or truncated, and should therefore be ignored.
-
-
-
-
- ENVIRONMENT
-
-
- CRYPTTAB_SOURCE
- CRYPTTAB_NAME
-
-
- If set, these environment variables will be assumed to
- contain the source device name and the target device
- mapper name, respectively, and will be shown as part of
- the prompt.
-
-
- These variables will normally be inherited from
- plugin-runner
- 8mandos, which will
- normally have inherited them from
- /scripts/local-top/cryptroot in the
- initial RAM disk environment, which will
- have set them from parsing kernel arguments and
- /conf/conf.d/cryptroot (also in the
- initial RAM disk environment), which in turn will have been
- created when the initial RAM disk image was created by
- /usr/share/initramfs-tools/hooks/cryptroot, by
- extracting the information of the root file system from
- /etc/crypttab.
-
-
- This behavior is meant to exactly mirror the behavior of
- askpass, the default password prompter.
-
-
-
-
-
-
-
- BUGS
-
- None are known at this time.
-
-
-
-
- EXAMPLE
-
- Note that normally, command line options will not be given
- directly, but via options for the Mandos plugin-runner
- 8mandos.
-
-
-
- Normal invocation needs no options:
-
-
- &COMMANDNAME;
-
-
-
-
- Show a prefix before the prompt; in this case, a host name.
- It might be useful to be reminded of which host needs a
- password, in case of KVM switches, etc.
-
-
-
-
-&COMMANDNAME; --prefix=host.example.org:
-
-
-
-
-
- Run in debug mode.
-
-
-
- &COMMANDNAME; --debug
-
-
-
-
-
- SECURITY
-
- On its own, this program is very simple, and does not exactly
- present any security risks. The one thing that could be
- considered worthy of note is this: This program is meant to be
- run by plugin-runner8mandos
- , and will, when run standalone, outside, in a
- normal environment, immediately output on its standard output
- any presumably secret password it just received. Therefore,
- when running this program standalone (which should never
- normally be done), take care not to type in any real secret
- password by force of habit, since it would then immediately be
- shown as output.
-
-
- To further alleviate any risk of being locked out of a system,
- the plugin-runner
- 8mandos has a fallback
- mode which does the same thing as this program, only with less
- features.
-
-
-
-
- SEE ALSO
-
- crypttab
- 5
- mandos-client
- 8mandos
- plugin-runner
- 8mandos,
-
-
-
-
-
-
-
-
=== renamed file 'plugins.d/mandos-client.c' => 'plugins.d/password-request.c'
--- plugins.d/mandos-client.c 2010-09-26 21:27:28 +0000
+++ plugins.d/password-request.c 2008-08-07 21:45:41 +0000
@@ -1,6 +1,6 @@
/* -*- coding: utf-8 -*- */
/*
- * Mandos-client - get and decrypt data from a Mandos server
+ * Mandos client - get and decrypt data from a Mandos server
*
* This program is partly derived from an example program for an Avahi
* service browser, downloaded from
@@ -9,8 +9,7 @@
* "browse_callback", and parts of "main".
*
* Everything else is
- * Copyright © 2008-2010 Teddy Hogeborn
- * Copyright © 2008-2010 Björn Påhlsson
+ * Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
*
* This program is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -30,69 +29,21 @@
*/
/* Needed by GPGME, specifically gpgme_data_seek() */
-#ifndef _LARGEFILE_SOURCE
#define _LARGEFILE_SOURCE
-#endif
-#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
-#endif
-
-#define _GNU_SOURCE /* TEMP_FAILURE_RETRY(), asprintf() */
-
-#include /* fprintf(), stderr, fwrite(),
- stdout, ferror(), remove() */
-#include /* uint16_t, uint32_t */
-#include /* NULL, size_t, ssize_t */
-#include /* free(), EXIT_SUCCESS, srand(),
- strtof(), abort() */
-#include /* bool, false, true */
-#include /* memset(), strcmp(), strlen(),
- strerror(), asprintf(), strcpy() */
-#include /* ioctl */
-#include /* socket(), inet_pton(), sockaddr,
- sockaddr_in6, PF_INET6,
- SOCK_STREAM, uid_t, gid_t, open(),
- opendir(), DIR */
-#include /* open() */
-#include /* socket(), struct sockaddr_in6,
- inet_pton(), connect() */
-#include /* open() */
-#include /* opendir(), struct dirent, readdir()
- */
-#include /* PRIu16, PRIdMAX, intmax_t,
- strtoimax() */
-#include /* assert() */
-#include /* perror(), errno */
-#include /* nanosleep(), time(), sleep() */
+
+#define _GNU_SOURCE /* TEMP_FAILURE_RETRY() */
+
+#include
+#include
+#include
+#include
+#include /* if_nametoindex */
+#include /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
+ SIOCSIFFLAGS */
#include /* ioctl, ifreq, SIOCGIFFLAGS, IFF_UP,
- SIOCSIFFLAGS, if_indextoname(),
- if_nametoindex(), IF_NAMESIZE */
-#include /* IN6_IS_ADDR_LINKLOCAL,
- INET_ADDRSTRLEN, INET6_ADDRSTRLEN
- */
-#include /* close(), SEEK_SET, off_t, write(),
- getuid(), getgid(), seteuid(),
- setgid(), pause() */
-#include /* inet_pton(), htons */
-#include /* not, or, and */
-#include /* struct argp_option, error_t, struct
- argp_state, struct argp,
- argp_parse(), ARGP_KEY_ARG,
- ARGP_KEY_END, ARGP_ERR_UNKNOWN */
-#include /* sigemptyset(), sigaddset(),
- sigaction(), SIGTERM, sig_atomic_t,
- raise() */
-#include /* EX_OSERR, EX_USAGE, EX_UNAVAILABLE,
- EX_NOHOST, EX_IOERR, EX_PROTOCOL */
-
-#ifdef __linux__
-#include /* klogctl() */
-#endif /* __linux__ */
-
-/* Avahi */
-/* All Avahi types, constants and functions
- Avahi*, avahi_*,
- AVAHI_* */
+ SIOCSIFFLAGS */
+
#include
#include
#include
@@ -100,35 +51,34 @@
#include
#include
-/* GnuTLS */
-#include /* All GnuTLS types, constants and
- functions:
- gnutls_*
- init_gnutls_session(),
- GNUTLS_* */
-#include
- /* gnutls_certificate_set_openpgp_key_file(),
- GNUTLS_OPENPGP_FMT_BASE64 */
+/* Mandos client part */
+#include /* socket(), inet_pton() */
+#include /* socket(), struct sockaddr_in6,
+ struct in6_addr, inet_pton() */
+#include /* All GnuTLS stuff */
+#include /* GnuTLS with openpgp stuff */
+#include /* close() */
+#include
+#include /* true */
+#include /* memset */
+#include /* inet_pton() */
+#include /* not */
+#include /* IF_NAMESIZE */
+#include /* struct argp_option,
+ struct argp_state, struct argp,
+ argp_parse() */
/* GPGME */
-#include /* All GPGME types, constants and
- functions:
- gpgme_*
- GPGME_PROTOCOL_OpenPGP,
- GPG_ERR_NO_* */
+#include /* perror() */
+#include
#define BUFFER_SIZE 256
-#define PATHDIR "/conf/conf.d/mandos"
-#define SECKEY "seckey.txt"
-#define PUBKEY "pubkey.txt"
-
bool debug = false;
+static const char *keydir = "/conf/conf.d/mandos";
static const char mandos_protocol_version[] = "1";
-const char *argp_program_version = "mandos-client " VERSION;
+const char *argp_program_version = "mandosclient 0.9";
const char *argp_program_bug_address = "";
-static const char sys_class_net[] = "/sys/class/net";
-char *connect_to = NULL;
/* Used for passing in values through the Avahi callback functions */
typedef struct {
@@ -138,27 +88,18 @@
unsigned int dh_bits;
gnutls_dh_params_t dh_params;
const char *priority;
- gpgme_ctx_t ctx;
} mandos_context;
-/* global context so signal handler can reach it*/
-mandos_context mc = { .simple_poll = NULL, .server = NULL,
- .dh_bits = 1024, .priority = "SECURE256"
- ":!CTYPE-X.509:+CTYPE-OPENPGP" };
-
-sig_atomic_t quit_now = 0;
-int signal_received = 0;
-
/*
- * Make additional room in "buffer" for at least BUFFER_SIZE more
- * bytes. "buffer_capacity" is how much is currently allocated,
+ * Make room in "buffer" for at least BUFFER_SIZE additional bytes.
+ * "buffer_capacity" is how much is currently allocated,
* "buffer_length" is how much is already used.
*/
-size_t incbuffer(char **buffer, size_t buffer_length,
+size_t adjustbuffer(char **buffer, size_t buffer_length,
size_t buffer_capacity){
- if(buffer_length + BUFFER_SIZE > buffer_capacity){
+ if (buffer_length + BUFFER_SIZE > buffer_capacity){
*buffer = realloc(*buffer, buffer_capacity + BUFFER_SIZE);
- if(buffer == NULL){
+ if (buffer == NULL){
return 0;
}
buffer_capacity += BUFFER_SIZE;
@@ -167,119 +108,58 @@
}
/*
- * Initialize GPGME.
+ * Decrypt OpenPGP data using keyrings in HOMEDIR.
+ * Returns -1 on error
*/
-static bool init_gpgme(const char *seckey,
- const char *pubkey, const char *tempdir){
+static ssize_t pgp_packet_decrypt (const char *cryptotext,
+ size_t crypto_size,
+ char **plaintext,
+ const char *homedir){
+ gpgme_data_t dh_crypto, dh_plain;
+ gpgme_ctx_t ctx;
gpgme_error_t rc;
+ ssize_t ret;
+ size_t plaintext_capacity = 0;
+ ssize_t plaintext_length = 0;
gpgme_engine_info_t engine_info;
-
- /*
- * Helper function to insert pub and seckey to the engine keyring.
- */
- bool import_key(const char *filename){
- int ret;
- int fd;
- gpgme_data_t pgp_data;
-
- fd = (int)TEMP_FAILURE_RETRY(open(filename, O_RDONLY));
- if(fd == -1){
- perror("open");
- return false;
- }
-
- rc = gpgme_data_new_from_fd(&pgp_data, fd);
- if(rc != GPG_ERR_NO_ERROR){
- fprintf(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
- gpgme_strsource(rc), gpgme_strerror(rc));
- return false;
- }
-
- rc = gpgme_op_import(mc.ctx, pgp_data);
- if(rc != GPG_ERR_NO_ERROR){
- fprintf(stderr, "bad gpgme_op_import: %s: %s\n",
- gpgme_strsource(rc), gpgme_strerror(rc));
- return false;
- }
-
- ret = (int)TEMP_FAILURE_RETRY(close(fd));
- if(ret == -1){
- perror("close");
- }
- gpgme_data_release(pgp_data);
- return true;
- }
-
- if(debug){
- fprintf(stderr, "Initializing GPGME\n");
+ if (debug){
+ fprintf(stderr, "Trying to decrypt OpenPGP data\n");
}
/* Init GPGME */
gpgme_check_version(NULL);
rc = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
- if(rc != GPG_ERR_NO_ERROR){
+ if (rc != GPG_ERR_NO_ERROR){
fprintf(stderr, "bad gpgme_engine_check_version: %s: %s\n",
gpgme_strsource(rc), gpgme_strerror(rc));
- return false;
+ return -1;
}
- /* Set GPGME home directory for the OpenPGP engine only */
- rc = gpgme_get_engine_info(&engine_info);
- if(rc != GPG_ERR_NO_ERROR){
+ /* Set GPGME home directory for the OpenPGP engine only */
+ rc = gpgme_get_engine_info (&engine_info);
+ if (rc != GPG_ERR_NO_ERROR){
fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
gpgme_strsource(rc), gpgme_strerror(rc));
- return false;
+ return -1;
}
while(engine_info != NULL){
if(engine_info->protocol == GPGME_PROTOCOL_OpenPGP){
gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP,
- engine_info->file_name, tempdir);
+ engine_info->file_name, homedir);
break;
}
engine_info = engine_info->next;
}
if(engine_info == NULL){
- fprintf(stderr, "Could not set GPGME home dir to %s\n", tempdir);
- return false;
- }
-
- /* Create new GPGME "context" */
- rc = gpgme_new(&(mc.ctx));
- if(rc != GPG_ERR_NO_ERROR){
- fprintf(stderr, "bad gpgme_new: %s: %s\n",
- gpgme_strsource(rc), gpgme_strerror(rc));
- return false;
- }
-
- if(not import_key(pubkey) or not import_key(seckey)){
- return false;
- }
-
- return true;
-}
-
-/*
- * Decrypt OpenPGP data.
- * Returns -1 on error
- */
-static ssize_t pgp_packet_decrypt(const char *cryptotext,
- size_t crypto_size,
- char **plaintext){
- gpgme_data_t dh_crypto, dh_plain;
- gpgme_error_t rc;
- ssize_t ret;
- size_t plaintext_capacity = 0;
- ssize_t plaintext_length = 0;
-
- if(debug){
- fprintf(stderr, "Trying to decrypt OpenPGP data\n");
+ fprintf(stderr, "Could not set GPGME home dir to %s\n", homedir);
+ return -1;
}
/* Create new GPGME data buffer from memory cryptotext */
rc = gpgme_data_new_from_mem(&dh_crypto, cryptotext, crypto_size,
0);
- if(rc != GPG_ERR_NO_ERROR){
+ if (rc != GPG_ERR_NO_ERROR){
fprintf(stderr, "bad gpgme_data_new_from_mem: %s: %s\n",
gpgme_strsource(rc), gpgme_strerror(rc));
return -1;
@@ -287,35 +167,52 @@
/* Create new empty GPGME data buffer for the plaintext */
rc = gpgme_data_new(&dh_plain);
- if(rc != GPG_ERR_NO_ERROR){
+ if (rc != GPG_ERR_NO_ERROR){
fprintf(stderr, "bad gpgme_data_new: %s: %s\n",
gpgme_strsource(rc), gpgme_strerror(rc));
gpgme_data_release(dh_crypto);
return -1;
}
+ /* Create new GPGME "context" */
+ rc = gpgme_new(&ctx);
+ if (rc != GPG_ERR_NO_ERROR){
+ fprintf(stderr, "bad gpgme_new: %s: %s\n",
+ gpgme_strsource(rc), gpgme_strerror(rc));
+ plaintext_length = -1;
+ goto decrypt_end;
+ }
+
/* Decrypt data from the cryptotext data buffer to the plaintext
data buffer */
- rc = gpgme_op_decrypt(mc.ctx, dh_crypto, dh_plain);
- if(rc != GPG_ERR_NO_ERROR){
+ rc = gpgme_op_decrypt(ctx, dh_crypto, dh_plain);
+ if (rc != GPG_ERR_NO_ERROR){
fprintf(stderr, "bad gpgme_op_decrypt: %s: %s\n",
gpgme_strsource(rc), gpgme_strerror(rc));
plaintext_length = -1;
- if(debug){
- gpgme_decrypt_result_t result;
- result = gpgme_op_decrypt_result(mc.ctx);
- if(result == NULL){
- fprintf(stderr, "gpgme_op_decrypt_result failed\n");
- } else {
- fprintf(stderr, "Unsupported algorithm: %s\n",
- result->unsupported_algorithm);
- fprintf(stderr, "Wrong key usage: %u\n",
- result->wrong_key_usage);
- if(result->file_name != NULL){
- fprintf(stderr, "File name: %s\n", result->file_name);
- }
- gpgme_recipient_t recipient;
- recipient = result->recipients;
+ goto decrypt_end;
+ }
+
+ if(debug){
+ fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
+ }
+
+ if (debug){
+ gpgme_decrypt_result_t result;
+ result = gpgme_op_decrypt_result(ctx);
+ if (result == NULL){
+ fprintf(stderr, "gpgme_op_decrypt_result failed\n");
+ } else {
+ fprintf(stderr, "Unsupported algorithm: %s\n",
+ result->unsupported_algorithm);
+ fprintf(stderr, "Wrong key usage: %d\n",
+ result->wrong_key_usage);
+ if(result->file_name != NULL){
+ fprintf(stderr, "File name: %s\n", result->file_name);
+ }
+ gpgme_recipient_t recipient;
+ recipient = result->recipients;
+ if(recipient){
while(recipient != NULL){
fprintf(stderr, "Public key algorithm: %s\n",
gpgme_pubkey_algo_name(recipient->pubkey_algo));
@@ -327,27 +224,22 @@
}
}
}
- goto decrypt_end;
- }
-
- if(debug){
- fprintf(stderr, "Decryption of OpenPGP data succeeded\n");
}
/* Seek back to the beginning of the GPGME plaintext data buffer */
- if(gpgme_data_seek(dh_plain, (off_t)0, SEEK_SET) == -1){
- perror("gpgme_data_seek");
+ if (gpgme_data_seek(dh_plain, (off_t) 0, SEEK_SET) == -1){
+ perror("pgpme_data_seek");
plaintext_length = -1;
goto decrypt_end;
}
*plaintext = NULL;
while(true){
- plaintext_capacity = incbuffer(plaintext,
+ plaintext_capacity = adjustbuffer(plaintext,
(size_t)plaintext_length,
plaintext_capacity);
- if(plaintext_capacity == 0){
- perror("incbuffer");
+ if (plaintext_capacity == 0){
+ perror("adjustbuffer");
plaintext_length = -1;
goto decrypt_end;
}
@@ -355,7 +247,7 @@
ret = gpgme_data_read(dh_plain, *plaintext + plaintext_length,
BUFFER_SIZE);
/* Print the data, if any */
- if(ret == 0){
+ if (ret == 0){
/* EOF */
break;
}
@@ -366,7 +258,7 @@
}
plaintext_length += ret;
}
-
+
if(debug){
fprintf(stderr, "Decrypted password is: ");
for(ssize_t i = 0; i < plaintext_length; i++){
@@ -385,10 +277,9 @@
return plaintext_length;
}
-static const char * safer_gnutls_strerror(int value){
- const char *ret = gnutls_strerror(value); /* Spurious warning from
- -Wunreachable-code */
- if(ret == NULL)
+static const char * safer_gnutls_strerror (int value) {
+ const char *ret = gnutls_strerror (value);
+ if (ret == NULL)
ret = "(unknown)";
return ret;
}
@@ -399,22 +290,23 @@
fprintf(stderr, "GnuTLS: %s", string);
}
-static int init_gnutls_global(const char *pubkeyfilename,
- const char *seckeyfilename){
+static int init_gnutls_global(mandos_context *mc,
+ const char *pubkeyfile,
+ const char *seckeyfile){
int ret;
if(debug){
fprintf(stderr, "Initializing GnuTLS\n");
}
-
- ret = gnutls_global_init();
- if(ret != GNUTLS_E_SUCCESS){
- fprintf(stderr, "GnuTLS global_init: %s\n",
- safer_gnutls_strerror(ret));
+
+ if ((ret = gnutls_global_init ())
+ != GNUTLS_E_SUCCESS) {
+ fprintf (stderr, "GnuTLS global_init: %s\n",
+ safer_gnutls_strerror(ret));
return -1;
}
- if(debug){
+ if (debug){
/* "Use a log level over 10 to enable all debugging options."
* - GnuTLS manual
*/
@@ -423,112 +315,93 @@
}
/* OpenPGP credentials */
- gnutls_certificate_allocate_credentials(&mc.cred);
- if(ret != GNUTLS_E_SUCCESS){
- fprintf(stderr, "GnuTLS memory error: %s\n", /* Spurious warning
- from
- -Wunreachable-code
- */
- safer_gnutls_strerror(ret));
- gnutls_global_deinit();
+ if ((ret = gnutls_certificate_allocate_credentials (&mc->cred))
+ != GNUTLS_E_SUCCESS) {
+ fprintf (stderr, "GnuTLS memory error: %s\n",
+ safer_gnutls_strerror(ret));
+ gnutls_global_deinit ();
return -1;
}
if(debug){
- fprintf(stderr, "Attempting to use OpenPGP public key %s and"
- " secret key %s as GnuTLS credentials\n", pubkeyfilename,
- seckeyfilename);
+ fprintf(stderr, "Attempting to use OpenPGP certificate %s"
+ " and keyfile %s as GnuTLS credentials\n", pubkeyfile,
+ seckeyfile);
}
ret = gnutls_certificate_set_openpgp_key_file
- (mc.cred, pubkeyfilename, seckeyfilename,
- GNUTLS_OPENPGP_FMT_BASE64);
- if(ret != GNUTLS_E_SUCCESS){
+ (mc->cred, pubkeyfile, seckeyfile, GNUTLS_OPENPGP_FMT_BASE64);
+ if (ret != GNUTLS_E_SUCCESS) {
fprintf(stderr,
"Error[%d] while reading the OpenPGP key pair ('%s',"
- " '%s')\n", ret, pubkeyfilename, seckeyfilename);
- fprintf(stderr, "The GnuTLS error is: %s\n",
+ " '%s')\n", ret, pubkeyfile, seckeyfile);
+ fprintf(stdout, "The GnuTLS error is: %s\n",
safer_gnutls_strerror(ret));
goto globalfail;
}
/* GnuTLS server initialization */
- ret = gnutls_dh_params_init(&mc.dh_params);
- if(ret != GNUTLS_E_SUCCESS){
- fprintf(stderr, "Error in GnuTLS DH parameter initialization:"
- " %s\n", safer_gnutls_strerror(ret));
- goto globalfail;
- }
- ret = gnutls_dh_params_generate2(mc.dh_params, mc.dh_bits);
- if(ret != GNUTLS_E_SUCCESS){
- fprintf(stderr, "Error in GnuTLS prime generation: %s\n",
- safer_gnutls_strerror(ret));
- goto globalfail;
- }
-
- gnutls_certificate_set_dh_params(mc.cred, mc.dh_params);
-
+ ret = gnutls_dh_params_init(&mc->dh_params);
+ if (ret != GNUTLS_E_SUCCESS) {
+ fprintf (stderr, "Error in GnuTLS DH parameter initialization:"
+ " %s\n", safer_gnutls_strerror(ret));
+ goto globalfail;
+ }
+ ret = gnutls_dh_params_generate2(mc->dh_params, mc->dh_bits);
+ if (ret != GNUTLS_E_SUCCESS) {
+ fprintf (stderr, "Error in GnuTLS prime generation: %s\n",
+ safer_gnutls_strerror(ret));
+ goto globalfail;
+ }
+
+ gnutls_certificate_set_dh_params(mc->cred, mc->dh_params);
+
return 0;
-
+
globalfail:
-
- gnutls_certificate_free_credentials(mc.cred);
- gnutls_global_deinit();
- gnutls_dh_params_deinit(mc.dh_params);
+
+ gnutls_certificate_free_credentials (mc->cred);
+ gnutls_global_deinit ();
return -1;
+
}
-static int init_gnutls_session(gnutls_session_t *session){
+static int init_gnutls_session(mandos_context *mc,
+ gnutls_session_t *session){
int ret;
/* GnuTLS session creation */
- do {
- ret = gnutls_init(session, GNUTLS_SERVER);
- if(quit_now){
- return -1;
- }
- } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
- if(ret != GNUTLS_E_SUCCESS){
+ ret = gnutls_init(session, GNUTLS_SERVER);
+ if (ret != GNUTLS_E_SUCCESS){
fprintf(stderr, "Error in GnuTLS session initialization: %s\n",
safer_gnutls_strerror(ret));
}
{
const char *err;
- do {
- ret = gnutls_priority_set_direct(*session, mc.priority, &err);
- if(quit_now){
- gnutls_deinit(*session);
- return -1;
- }
- } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
- if(ret != GNUTLS_E_SUCCESS){
+ ret = gnutls_priority_set_direct(*session, mc->priority, &err);
+ if (ret != GNUTLS_E_SUCCESS) {
fprintf(stderr, "Syntax error at: %s\n", err);
fprintf(stderr, "GnuTLS error: %s\n",
safer_gnutls_strerror(ret));
- gnutls_deinit(*session);
+ gnutls_deinit (*session);
return -1;
}
}
- do {
- ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
- mc.cred);
- if(quit_now){
- gnutls_deinit(*session);
- return -1;
- }
- } while(ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN);
- if(ret != GNUTLS_E_SUCCESS){
+ ret = gnutls_credentials_set(*session, GNUTLS_CRD_CERTIFICATE,
+ mc->cred);
+ if (ret != GNUTLS_E_SUCCESS) {
fprintf(stderr, "Error setting GnuTLS credentials: %s\n",
safer_gnutls_strerror(ret));
- gnutls_deinit(*session);
+ gnutls_deinit (*session);
return -1;
}
/* ignore client certificate if any. */
- gnutls_certificate_server_set_request(*session, GNUTLS_CERT_IGNORE);
+ gnutls_certificate_server_set_request (*session,
+ GNUTLS_CERT_IGNORE);
- gnutls_dh_set_prime_bits(*session, mc.dh_bits);
+ gnutls_dh_set_prime_bits (*session, mc->dh_bits);
return 0;
}
@@ -540,137 +413,65 @@
/* Called when a Mandos server is found */
static int start_mandos_communication(const char *ip, uint16_t port,
AvahiIfIndex if_index,
- int af){
- int ret, tcp_sd = -1;
- ssize_t sret;
- union {
- struct sockaddr_in in;
- struct sockaddr_in6 in6;
- } to;
+ mandos_context *mc){
+ int ret, tcp_sd;
+ union { struct sockaddr in; struct sockaddr_in6 in6; } to;
char *buffer = NULL;
- char *decrypted_buffer = NULL;
+ char *decrypted_buffer;
size_t buffer_length = 0;
size_t buffer_capacity = 0;
+ ssize_t decrypted_buffer_size;
size_t written;
- int retval = -1;
+ int retval = 0;
+ char interface[IF_NAMESIZE];
gnutls_session_t session;
- int pf; /* Protocol family */
-
- errno = 0;
-
- if(quit_now){
- errno = EINTR;
- return -1;
- }
-
- switch(af){
- case AF_INET6:
- pf = PF_INET6;
- break;
- case AF_INET:
- pf = PF_INET;
- break;
- default:
- fprintf(stderr, "Bad address family: %d\n", af);
- errno = EINVAL;
- return -1;
- }
-
- ret = init_gnutls_session(&session);
- if(ret != 0){
+
+ ret = init_gnutls_session (mc, &session);
+ if (ret != 0){
return -1;
}
if(debug){
- fprintf(stderr, "Setting up a TCP connection to %s, port %" PRIu16
- "\n", ip, port);
+ fprintf(stderr, "Setting up a tcp connection to %s, port %d\n",
+ ip, port);
}
- tcp_sd = socket(pf, SOCK_STREAM, 0);
- if(tcp_sd < 0){
- int e = errno;
+ tcp_sd = socket(PF_INET6, SOCK_STREAM, 0);
+ if(tcp_sd < 0) {
perror("socket");
- errno = e;
- goto mandos_end;
- }
-
- if(quit_now){
- errno = EINTR;
- goto mandos_end;
- }
-
- memset(&to, 0, sizeof(to));
- if(af == AF_INET6){
- to.in6.sin6_family = (sa_family_t)af;
- ret = inet_pton(af, ip, &to.in6.sin6_addr);
- } else { /* IPv4 */
- to.in.sin_family = (sa_family_t)af;
- ret = inet_pton(af, ip, &to.in.sin_addr);
- }
- if(ret < 0 ){
- int e = errno;
+ return -1;
+ }
+
+ if(debug){
+ if(if_indextoname((unsigned int)if_index, interface) == NULL){
+ perror("if_indextoname");
+ return -1;
+ }
+ fprintf(stderr, "Binding to interface %s\n", interface);
+ }
+
+ memset(&to,0,sizeof(to)); /* Spurious warning */
+ to.in6.sin6_family = AF_INET6;
+ /* It would be nice to have a way to detect if we were passed an
+ IPv4 address here. Now we assume an IPv6 address. */
+ ret = inet_pton(AF_INET6, ip, &to.in6.sin6_addr);
+ if (ret < 0 ){
perror("inet_pton");
- errno = e;
- goto mandos_end;
+ return -1;
}
if(ret == 0){
- int e = errno;
fprintf(stderr, "Bad address: %s\n", ip);
- errno = e;
- goto mandos_end;
- }
- if(af == AF_INET6){
- to.in6.sin6_port = htons(port); /* Spurious warnings from
- -Wconversion and
- -Wunreachable-code */
-
- if(IN6_IS_ADDR_LINKLOCAL /* Spurious warnings from */
- (&to.in6.sin6_addr)){ /* -Wstrict-aliasing=2 or lower and
- -Wunreachable-code*/
- if(if_index == AVAHI_IF_UNSPEC){
- fprintf(stderr, "An IPv6 link-local address is incomplete"
- " without a network interface\n");
- errno = EINVAL;
- goto mandos_end;
- }
- /* Set the network interface number as scope */
- to.in6.sin6_scope_id = (uint32_t)if_index;
- }
- } else {
- to.in.sin_port = htons(port); /* Spurious warnings from
- -Wconversion and
- -Wunreachable-code */
- }
+ return -1;
+ }
+ to.in6.sin6_port = htons(port); /* Spurious warning */
- if(quit_now){
- errno = EINTR;
- goto mandos_end;
- }
+ to.in6.sin6_scope_id = (uint32_t)if_index;
if(debug){
- if(af == AF_INET6 and if_index != AVAHI_IF_UNSPEC){
- char interface[IF_NAMESIZE];
- if(if_indextoname((unsigned int)if_index, interface) == NULL){
- perror("if_indextoname");
- } else {
- fprintf(stderr, "Connection to: %s%%%s, port %" PRIu16 "\n",
- ip, interface, port);
- }
- } else {
- fprintf(stderr, "Connection to: %s, port %" PRIu16 "\n", ip,
- port);
- }
- char addrstr[(INET_ADDRSTRLEN > INET6_ADDRSTRLEN) ?
- INET_ADDRSTRLEN : INET6_ADDRSTRLEN] = "";
- const char *pcret;
- if(af == AF_INET6){
- pcret = inet_ntop(af, &(to.in6.sin6_addr), addrstr,
- sizeof(addrstr));
- } else {
- pcret = inet_ntop(af, &(to.in.sin_addr), addrstr,
- sizeof(addrstr));
- }
- if(pcret == NULL){
+ fprintf(stderr, "Connection to: %s, port %d\n", ip, port);
+ char addrstr[INET6_ADDRSTRLEN] = "";
+ if(inet_ntop(to.in6.sin6_family, &(to.in6.sin6_addr), addrstr,
+ sizeof(addrstr)) == NULL){
perror("inet_ntop");
} else {
if(strcmp(addrstr, ip) != 0){
@@ -679,156 +480,97 @@
}
}
- if(quit_now){
- errno = EINTR;
- goto mandos_end;
- }
-
- if(af == AF_INET6){
- ret = connect(tcp_sd, &to.in6, sizeof(to));
- } else {
- ret = connect(tcp_sd, &to.in, sizeof(to)); /* IPv4 */
- }
- if(ret < 0){
- if ((errno != ECONNREFUSED and errno != ENETUNREACH) or debug){
- int e = errno;
- perror("connect");
- errno = e;
- }
- goto mandos_end;
- }
-
- if(quit_now){
- errno = EINTR;
- goto mandos_end;
- }
-
+ ret = connect(tcp_sd, &to.in, sizeof(to));
+ if (ret < 0){
+ perror("connect");
+ return -1;
+ }
+
const char *out = mandos_protocol_version;
written = 0;
- while(true){
+ while (true){
size_t out_size = strlen(out);
- ret = (int)TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
+ ret = TEMP_FAILURE_RETRY(write(tcp_sd, out + written,
out_size - written));
- if(ret == -1){
- int e = errno;
+ if (ret == -1){
perror("write");
- errno = e;
+ retval = -1;
goto mandos_end;
}
written += (size_t)ret;
if(written < out_size){
continue;
} else {
- if(out == mandos_protocol_version){
+ if (out == mandos_protocol_version){
written = 0;
out = "\r\n";
} else {
break;
}
}
-
- if(quit_now){
- errno = EINTR;
- goto mandos_end;
- }
}
-
+
if(debug){
fprintf(stderr, "Establishing TLS session with %s\n", ip);
}
- if(quit_now){
- errno = EINTR;
- goto mandos_end;
- }
-
- gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t) tcp_sd);
-
- if(quit_now){
- errno = EINTR;
- goto mandos_end;
- }
-
- do {
- ret = gnutls_handshake(session);
- if(quit_now){
- errno = EINTR;
- goto mandos_end;
- }
- } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
-
- if(ret != GNUTLS_E_SUCCESS){
+ gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) tcp_sd);
+
+ ret = gnutls_handshake (session);
+
+ if (ret != GNUTLS_E_SUCCESS){
if(debug){
fprintf(stderr, "*** GnuTLS Handshake failed ***\n");
- gnutls_perror(ret);
+ gnutls_perror (ret);
}
- errno = EPROTO;
+ retval = -1;
goto mandos_end;
}
/* Read OpenPGP packet that contains the wanted password */
if(debug){
- fprintf(stderr, "Retrieving OpenPGP encrypted password from %s\n",
+ fprintf(stderr, "Retrieving pgp encrypted password from %s\n",
ip);
}
-
+
while(true){
-
- if(quit_now){
- errno = EINTR;
- goto mandos_end;
- }
-
- buffer_capacity = incbuffer(&buffer, buffer_length,
+ buffer_capacity = adjustbuffer(&buffer, buffer_length,
buffer_capacity);
- if(buffer_capacity == 0){
- int e = errno;
- perror("incbuffer");
- errno = e;
- goto mandos_end;
- }
-
- if(quit_now){
- errno = EINTR;
- goto mandos_end;
- }
-
- sret = gnutls_record_recv(session, buffer+buffer_length,
- BUFFER_SIZE);
- if(sret == 0){
+ if (buffer_capacity == 0){
+ perror("adjustbuffer");
+ retval = -1;
+ goto mandos_end;
+ }
+
+ ret = gnutls_record_recv(session, buffer+buffer_length,
+ BUFFER_SIZE);
+ if (ret == 0){
break;
}
- if(sret < 0){
- switch(sret){
+ if (ret < 0){
+ switch(ret){
case GNUTLS_E_INTERRUPTED:
case GNUTLS_E_AGAIN:
break;
case GNUTLS_E_REHANDSHAKE:
- do {
- ret = gnutls_handshake(session);
-
- if(quit_now){
- errno = EINTR;
- goto mandos_end;
- }
- } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
- if(ret < 0){
+ ret = gnutls_handshake (session);
+ if (ret < 0){
fprintf(stderr, "*** GnuTLS Re-handshake failed ***\n");
- gnutls_perror(ret);
- errno = EPROTO;
+ gnutls_perror (ret);
+ retval = -1;
goto mandos_end;
}
break;
default:
fprintf(stderr, "Unknown error while reading data from"
" encrypted session with Mandos server\n");
- gnutls_bye(session, GNUTLS_SHUT_RDWR);
- errno = EIO;
+ retval = -1;
+ gnutls_bye (session, GNUTLS_SHUT_RDWR);
goto mandos_end;
}
} else {
- buffer_length += (size_t) sret;
+ buffer_length += (size_t) ret;
}
}
@@ -836,80 +578,47 @@
fprintf(stderr, "Closing TLS session\n");
}
- if(quit_now){
- errno = EINTR;
- goto mandos_end;
- }
-
- do {
- ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
- if(quit_now){
- errno = EINTR;
- goto mandos_end;
- }
- } while(ret == GNUTLS_E_AGAIN or ret == GNUTLS_E_INTERRUPTED);
-
- if(buffer_length > 0){
- ssize_t decrypted_buffer_size;
+ gnutls_bye (session, GNUTLS_SHUT_RDWR);
+
+ if (buffer_length > 0){
decrypted_buffer_size = pgp_packet_decrypt(buffer,
buffer_length,
- &decrypted_buffer);
- if(decrypted_buffer_size >= 0){
-
+ &decrypted_buffer,
+ keydir);
+ if (decrypted_buffer_size >= 0){
written = 0;
while(written < (size_t) decrypted_buffer_size){
- if(quit_now){
- errno = EINTR;
- goto mandos_end;
- }
-
- ret = (int)fwrite(decrypted_buffer + written, 1,
- (size_t)decrypted_buffer_size - written,
- stdout);
+ ret = (int)fwrite (decrypted_buffer + written, 1,
+ (size_t)decrypted_buffer_size - written,
+ stdout);
if(ret == 0 and ferror(stdout)){
- int e = errno;
if(debug){
fprintf(stderr, "Error writing encrypted data: %s\n",
strerror(errno));
}
- errno = e;
- goto mandos_end;
+ retval = -1;
+ break;
}
written += (size_t)ret;
}
- retval = 0;
+ free(decrypted_buffer);
+ } else {
+ retval = -1;
}
}
/* Shutdown procedure */
mandos_end:
- {
- int e = errno;
- free(decrypted_buffer);
- free(buffer);
- if(tcp_sd >= 0){
- ret = (int)TEMP_FAILURE_RETRY(close(tcp_sd));
- }
- if(ret == -1){
- if(e == 0){
- e = errno;
- }
- perror("close");
- }
- gnutls_deinit(session);
- if(quit_now){
- e = EINTR;
- retval = -1;
- }
- errno = e;
- }
+ free(buffer);
+ close(tcp_sd);
+ gnutls_deinit (session);
return retval;
}
static void resolve_callback(AvahiSServiceResolver *r,
AvahiIfIndex interface,
- AvahiProtocol proto,
+ AVAHI_GCC_UNUSED AvahiProtocol protocol,
AvahiResolverEvent event,
const char *name,
const char *type,
@@ -920,22 +629,19 @@
AVAHI_GCC_UNUSED AvahiStringList *txt,
AVAHI_GCC_UNUSED AvahiLookupResultFlags
flags,
- AVAHI_GCC_UNUSED void* userdata){
- assert(r);
+ void* userdata) {
+ mandos_context *mc = userdata;
+ assert(r); /* Spurious warning */
/* Called whenever a service has been resolved successfully or
timed out */
- if(quit_now){
- return;
- }
-
- switch(event){
+ switch (event) {
default:
case AVAHI_RESOLVER_FAILURE:
fprintf(stderr, "(Avahi Resolver) Failed to resolve service '%s'"
" of type '%s' in domain '%s': %s\n", name, type, domain,
- avahi_strerror(avahi_server_errno(mc.server)));
+ avahi_strerror(avahi_server_errno(mc->server)));
break;
case AVAHI_RESOLVER_FOUND:
@@ -943,46 +649,41 @@
char ip[AVAHI_ADDRESS_STR_MAX];
avahi_address_snprint(ip, sizeof(ip), address);
if(debug){
- fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %"
- PRIdMAX ") on port %" PRIu16 "\n", name, host_name,
- ip, (intmax_t)interface, port);
+ fprintf(stderr, "Mandos server \"%s\" found on %s (%s, %d) on"
+ " port %d\n", name, host_name, ip, interface, port);
}
- int ret = start_mandos_communication(ip, port, interface,
- avahi_proto_to_af(proto));
- if(ret == 0){
- avahi_simple_poll_quit(mc.simple_poll);
+ int ret = start_mandos_communication(ip, port, interface, mc);
+ if (ret == 0){
+ exit(EXIT_SUCCESS);
}
}
}
avahi_s_service_resolver_free(r);
}
-static void browse_callback(AvahiSServiceBrowser *b,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiBrowserEvent event,
- const char *name,
- const char *type,
- const char *domain,
- AVAHI_GCC_UNUSED AvahiLookupResultFlags
- flags,
- AVAHI_GCC_UNUSED void* userdata){
- assert(b);
+static void browse_callback( AvahiSServiceBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags
+ flags,
+ void* userdata) {
+ mandos_context *mc = userdata;
+ assert(b); /* Spurious warning */
/* Called whenever a new services becomes available on the LAN or
is removed from the LAN */
- if(quit_now){
- return;
- }
-
- switch(event){
+ switch (event) {
default:
case AVAHI_BROWSER_FAILURE:
fprintf(stderr, "(Avahi browser) %s\n",
- avahi_strerror(avahi_server_errno(mc.server)));
- avahi_simple_poll_quit(mc.simple_poll);
+ avahi_strerror(avahi_server_errno(mc->server)));
+ avahi_simple_poll_quit(mc->simple_poll);
return;
case AVAHI_BROWSER_NEW:
@@ -991,11 +692,12 @@
the callback function is called the Avahi server will free the
resolver for us. */
- if(avahi_s_service_resolver_new(mc.server, interface, protocol,
- name, type, domain, protocol, 0,
- resolve_callback, NULL) == NULL)
+ if (!(avahi_s_service_resolver_new(mc->server, interface,
+ protocol, name, type, domain,
+ AVAHI_PROTO_INET6, 0,
+ resolve_callback, mc)))
fprintf(stderr, "Avahi: Failed to resolve service '%s': %s\n",
- name, avahi_strerror(avahi_server_errno(mc.server)));
+ name, avahi_strerror(avahi_server_errno(mc->server)));
break;
case AVAHI_BROWSER_REMOVE:
@@ -1010,809 +712,310 @@
}
}
-/* stop main loop after sigterm has been called */
-static void handle_sigterm(int sig){
- if(quit_now){
- return;
- }
- quit_now = 1;
- signal_received = sig;
- int old_errno = errno;
- if(mc.simple_poll != NULL){
- avahi_simple_poll_quit(mc.simple_poll);
- }
- errno = old_errno;
+/* Combines file name and path and returns the malloced new
+ string. some sane checks could/should be added */
+static const char *combinepath(const char *first, const char *second){
+ size_t f_len = strlen(first);
+ size_t s_len = strlen(second);
+ char *tmp = malloc(f_len + s_len + 2);
+ if (tmp == NULL){
+ return NULL;
+ }
+ if(f_len > 0){
+ memcpy(tmp, first, f_len); /* Spurious warning */
+ }
+ tmp[f_len] = '/';
+ if(s_len > 0){
+ memcpy(tmp + f_len + 1, second, s_len); /* Spurious warning */
+ }
+ tmp[f_len + 1 + s_len] = '\0';
+ return tmp;
}
-/*
- * This function determines if a directory entry in /sys/class/net
- * corresponds to an acceptable network device.
- * (This function is passed to scandir(3) as a filter function.)
- */
-int good_interface(const struct dirent *if_entry){
- ssize_t ssret;
- char *flagname = NULL;
- int ret = asprintf(&flagname, "%s/%s/flags", sys_class_net,
- if_entry->d_name);
- if(ret < 0){
- perror("asprintf");
- return 0;
- }
- if(if_entry->d_name[0] == '.'){
- return 0;
- }
- int flags_fd = (int)TEMP_FAILURE_RETRY(open(flagname, O_RDONLY));
- if(flags_fd == -1){
- perror("open");
- return 0;
- }
- typedef short ifreq_flags; /* ifreq.ifr_flags in netdevice(7) */
- /* read line from flags_fd */
- ssize_t to_read = (sizeof(ifreq_flags)*2)+3; /* "0x1003\n" */
- char *flagstring = malloc((size_t)to_read+1); /* +1 for final \0 */
- flagstring[(size_t)to_read] = '\0';
- if(flagstring == NULL){
- perror("malloc");
- close(flags_fd);
- return 0;
- }
- while(to_read > 0){
- ssret = (ssize_t)TEMP_FAILURE_RETRY(read(flags_fd, flagstring,
- (size_t)to_read));
- if(ssret == -1){
- perror("read");
- free(flagstring);
- close(flags_fd);
- return 0;
- }
- to_read -= ssret;
- if(ssret == 0){
- break;
- }
- }
- close(flags_fd);
- intmax_t tmpmax;
- char *tmp;
- errno = 0;
- tmpmax = strtoimax(flagstring, &tmp, 0);
- if(errno != 0 or tmp == flagstring or (*tmp != '\0'
- and not (isspace(*tmp)))
- or tmpmax != (ifreq_flags)tmpmax){
- if(debug){
- fprintf(stderr, "Invalid flags \"%s\" for interface \"%s\"\n",
- flagstring, if_entry->d_name);
- }
- free(flagstring);
- return 0;
- }
- free(flagstring);
- ifreq_flags flags = (ifreq_flags)tmpmax;
- /* Reject the loopback device */
- if(flags & IFF_LOOPBACK){
- if(debug){
- fprintf(stderr, "Rejecting loopback interface \"%s\"\n",
- if_entry->d_name);
- }
- return 0;
- }
- /* Accept point-to-point devices only if connect_to is specified */
- if(connect_to != NULL and (flags & IFF_POINTOPOINT)){
- if(debug){
- fprintf(stderr, "Accepting point-to-point interface \"%s\"\n",
- if_entry->d_name);
- }
- return 1;
- }
- /* Otherwise, reject non-broadcast-capable devices */
- if(not (flags & IFF_BROADCAST)){
- if(debug){
- fprintf(stderr, "Rejecting non-broadcast interface \"%s\"\n",
- if_entry->d_name);
- }
- return 0;
- }
- /* Accept this device */
- if(debug){
- fprintf(stderr, "Interface \"%s\" is acceptable\n",
- if_entry->d_name);
- }
- return 1;
-}
int main(int argc, char *argv[]){
- AvahiSServiceBrowser *sb = NULL;
- int error;
- int ret;
- intmax_t tmpmax;
- char *tmp;
- int exitcode = EXIT_SUCCESS;
- const char *interface = "";
- struct ifreq network;
- int sd = -1;
- bool take_down_interface = false;
- uid_t uid;
- gid_t gid;
- char tempdir[] = "/tmp/mandosXXXXXX";
- bool tempdir_created = false;
- AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
- const char *seckey = PATHDIR "/" SECKEY;
- const char *pubkey = PATHDIR "/" PUBKEY;
-
- bool gnutls_initialized = false;
- bool gpgme_initialized = false;
- float delay = 2.5f;
-
- struct sigaction old_sigterm_action = { .sa_handler = SIG_DFL };
- struct sigaction sigterm_action = { .sa_handler = handle_sigterm };
-
- uid = getuid();
- gid = getgid();
-
- /* Lower any group privileges we might have, just to be safe */
- errno = 0;
- ret = setgid(gid);
- if(ret == -1){
- perror("setgid");
- }
-
- /* Lower user privileges (temporarily) */
- errno = 0;
- ret = seteuid(uid);
- if(ret == -1){
- perror("seteuid");
- }
-
- if(quit_now){
- goto end;
- }
-
- {
- struct argp_option options[] = {
- { .name = "debug", .key = 128,
- .doc = "Debug mode", .group = 3 },
- { .name = "connect", .key = 'c',
- .arg = "ADDRESS:PORT",
- .doc = "Connect directly to a specific Mandos server",
- .group = 1 },
- { .name = "interface", .key = 'i',
- .arg = "NAME",
- .doc = "Network interface that will be used to search for"
- " Mandos servers",
- .group = 1 },
- { .name = "seckey", .key = 's',
- .arg = "FILE",
- .doc = "OpenPGP secret key file base name",
- .group = 1 },
- { .name = "pubkey", .key = 'p',
- .arg = "FILE",
- .doc = "OpenPGP public key file base name",
- .group = 2 },
- { .name = "dh-bits", .key = 129,
- .arg = "BITS",
- .doc = "Bit length of the prime number used in the"
- " Diffie-Hellman key exchange",
- .group = 2 },
- { .name = "priority", .key = 130,
- .arg = "STRING",
- .doc = "GnuTLS priority string for the TLS handshake",
- .group = 1 },
- { .name = "delay", .key = 131,
- .arg = "SECONDS",
- .doc = "Maximum delay to wait for interface startup",
- .group = 2 },
- /*
- * These reproduce what we would get without ARGP_NO_HELP
- */
- { .name = "help", .key = '?',
- .doc = "Give this help list", .group = -1 },
- { .name = "usage", .key = -3,
- .doc = "Give a short usage message", .group = -1 },
- { .name = "version", .key = 'V',
- .doc = "Print program version", .group = -1 },
- { .name = NULL }
- };
-
- error_t parse_opt(int key, char *arg,
- struct argp_state *state){
- errno = 0;
- switch(key){
- case 128: /* --debug */
- debug = true;
- break;
- case 'c': /* --connect */
- connect_to = arg;
- break;
- case 'i': /* --interface */
- interface = arg;
- break;
- case 's': /* --seckey */
- seckey = arg;
- break;
- case 'p': /* --pubkey */
- pubkey = arg;
- break;
- case 129: /* --dh-bits */
- errno = 0;
- tmpmax = strtoimax(arg, &tmp, 10);
- if(errno != 0 or tmp == arg or *tmp != '\0'
- or tmpmax != (typeof(mc.dh_bits))tmpmax){
- argp_error(state, "Bad number of DH bits");
- }
- mc.dh_bits = (typeof(mc.dh_bits))tmpmax;
- break;
- case 130: /* --priority */
- mc.priority = arg;
- break;
- case 131: /* --delay */
- errno = 0;
- delay = strtof(arg, &tmp);
- if(errno != 0 or tmp == arg or *tmp != '\0'){
- argp_error(state, "Bad delay");
- }
- break;
- /*
- * These reproduce what we would get without ARGP_NO_HELP
- */
- case '?': /* --help */
- argp_state_help(state, state->out_stream,
- (ARGP_HELP_STD_HELP | ARGP_HELP_EXIT_ERR)
- & ~(unsigned int)ARGP_HELP_EXIT_OK);
- case -3: /* --usage */
- argp_state_help(state, state->out_stream,
- ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR);
- case 'V': /* --version */
- fprintf(state->out_stream, "%s\n", argp_program_version);
- exit(argp_err_exit_status);
- break;
- default:
- return ARGP_ERR_UNKNOWN;
- }
- return errno;
- }
-
- struct argp argp = { .options = options, .parser = parse_opt,
- .args_doc = "",
- .doc = "Mandos client -- Get and decrypt"
- " passwords from a Mandos server" };
- ret = argp_parse(&argp, argc, argv,
- ARGP_IN_ORDER | ARGP_NO_HELP, 0, NULL);
- switch(ret){
- case 0:
- break;
- case ENOMEM:
- default:
- errno = ret;
- perror("argp_parse");
- exitcode = EX_OSERR;
- goto end;
- case EINVAL:
- exitcode = EX_USAGE;
- goto end;
- }
- }
-
- if(not debug){
- avahi_set_log_function(empty_log);
- }
-
- if(interface[0] == '\0'){
- struct dirent **direntries;
- ret = scandir(sys_class_net, &direntries, good_interface,
- alphasort);
- if(ret >= 1){
- /* Pick the first good interface */
- interface = strdup(direntries[0]->d_name);
- if(debug){
- fprintf(stderr, "Using interface \"%s\"\n", interface);
- }
- if(interface == NULL){
- perror("malloc");
- free(direntries);
- exitcode = EXIT_FAILURE;
- goto end;
- }
- free(direntries);
- } else {
- free(direntries);
- fprintf(stderr, "Could not find a network interface\n");
+ AvahiSServiceBrowser *sb = NULL;
+ int error;
+ int ret;
+ int exitcode = EXIT_SUCCESS;
+ const char *interface = "eth0";
+ struct ifreq network;
+ int sd;
+ uid_t uid;
+ gid_t gid;
+ char *connect_to = NULL;
+ AvahiIfIndex if_index = AVAHI_IF_UNSPEC;
+ const char *pubkeyfile = "pubkey.txt";
+ const char *seckeyfile = "seckey.txt";
+ mandos_context mc = { .simple_poll = NULL, .server = NULL,
+ .dh_bits = 1024, .priority = "SECURE256"};
+ bool gnutls_initalized = false;
+
+ {
+ struct argp_option options[] = {
+ { .name = "debug", .key = 128,
+ .doc = "Debug mode", .group = 3 },
+ { .name = "connect", .key = 'c',
+ .arg = "IP",
+ .doc = "Connect directly to a sepcified mandos server",
+ .group = 1 },
+ { .name = "interface", .key = 'i',
+ .arg = "INTERFACE",
+ .doc = "Interface that Avahi will conntect through",
+ .group = 1 },
+ { .name = "keydir", .key = 'd',
+ .arg = "KEYDIR",
+ .doc = "Directory where the openpgp keyring is",
+ .group = 1 },
+ { .name = "seckey", .key = 's',
+ .arg = "SECKEY",
+ .doc = "Secret openpgp key for gnutls authentication",
+ .group = 1 },
+ { .name = "pubkey", .key = 'p',
+ .arg = "PUBKEY",
+ .doc = "Public openpgp key for gnutls authentication",
+ .group = 2 },
+ { .name = "dh-bits", .key = 129,
+ .arg = "BITS",
+ .doc = "dh-bits to use in gnutls communication",
+ .group = 2 },
+ { .name = "priority", .key = 130,
+ .arg = "PRIORITY",
+ .doc = "GNUTLS priority", .group = 1 },
+ { .name = NULL }
+ };
+
+
+ error_t parse_opt (int key, char *arg,
+ struct argp_state *state) {
+ /* Get the INPUT argument from `argp_parse', which we know is
+ a pointer to our plugin list pointer. */
+ switch (key) {
+ case 128:
+ debug = true;
+ break;
+ case 'c':
+ connect_to = arg;
+ break;
+ case 'i':
+ interface = arg;
+ break;
+ case 'd':
+ keydir = arg;
+ break;
+ case 's':
+ seckeyfile = arg;
+ break;
+ case 'p':
+ pubkeyfile = arg;
+ break;
+ case 129:
+ errno = 0;
+ mc.dh_bits = (unsigned int) strtol(arg, NULL, 10);
+ if (errno){
+ perror("strtol");
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 130:
+ mc.priority = arg;
+ break;
+ case ARGP_KEY_ARG:
+ argp_usage (state);
+ break;
+ case ARGP_KEY_END:
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+
+ struct argp argp = { .options = options, .parser = parse_opt,
+ .args_doc = "",
+ .doc = "Mandos client -- Get and decrypt"
+ " passwords from mandos server" };
+ argp_parse (&argp, argc, argv, 0, 0, NULL);
+ }
+
+ pubkeyfile = combinepath(keydir, pubkeyfile);
+ if (pubkeyfile == NULL){
+ perror("combinepath");
exitcode = EXIT_FAILURE;
goto end;
}
- }
-
- /* Initialize Avahi early so avahi_simple_poll_quit() can be called
- from the signal handler */
- /* Initialize the pseudo-RNG for Avahi */
- srand((unsigned int) time(NULL));
- mc.simple_poll = avahi_simple_poll_new();
- if(mc.simple_poll == NULL){
- fprintf(stderr, "Avahi: Failed to create simple poll object.\n");
- exitcode = EX_UNAVAILABLE;
- goto end;
- }
-
- sigemptyset(&sigterm_action.sa_mask);
- ret = sigaddset(&sigterm_action.sa_mask, SIGINT);
- if(ret == -1){
- perror("sigaddset");
- exitcode = EX_OSERR;
- goto end;
- }
- ret = sigaddset(&sigterm_action.sa_mask, SIGHUP);
- if(ret == -1){
- perror("sigaddset");
- exitcode = EX_OSERR;
- goto end;
- }
- ret = sigaddset(&sigterm_action.sa_mask, SIGTERM);
- if(ret == -1){
- perror("sigaddset");
- exitcode = EX_OSERR;
- goto end;
- }
- /* Need to check if the handler is SIG_IGN before handling:
- | [[info:libc:Initial Signal Actions]] |
- | [[info:libc:Basic Signal Handling]] |
- */
- ret = sigaction(SIGINT, NULL, &old_sigterm_action);
- if(ret == -1){
- perror("sigaction");
- return EX_OSERR;
- }
- if(old_sigterm_action.sa_handler != SIG_IGN){
- ret = sigaction(SIGINT, &sigterm_action, NULL);
- if(ret == -1){
- perror("sigaction");
- exitcode = EX_OSERR;
- goto end;
- }
- }
- ret = sigaction(SIGHUP, NULL, &old_sigterm_action);
- if(ret == -1){
- perror("sigaction");
- return EX_OSERR;
- }
- if(old_sigterm_action.sa_handler != SIG_IGN){
- ret = sigaction(SIGHUP, &sigterm_action, NULL);
- if(ret == -1){
- perror("sigaction");
- exitcode = EX_OSERR;
- goto end;
- }
- }
- ret = sigaction(SIGTERM, NULL, &old_sigterm_action);
- if(ret == -1){
- perror("sigaction");
- return EX_OSERR;
- }
- if(old_sigterm_action.sa_handler != SIG_IGN){
- ret = sigaction(SIGTERM, &sigterm_action, NULL);
- if(ret == -1){
- perror("sigaction");
- exitcode = EX_OSERR;
- goto end;
- }
- }
-
- /* If the interface is down, bring it up */
- if(strcmp(interface, "none") != 0){
+
+ seckeyfile = combinepath(keydir, seckeyfile);
+ if (seckeyfile == NULL){
+ perror("combinepath");
+ goto end;
+ }
+
+ ret = init_gnutls_global(&mc, pubkeyfile, seckeyfile);
+ if (ret == -1){
+ fprintf(stderr, "init_gnutls_global\n");
+ goto end;
+ } else {
+ gnutls_initalized = true;
+ }
+
+ uid = getuid();
+ gid = getgid();
+
+ ret = setuid(uid);
+ if (ret == -1){
+ perror("setuid");
+ }
+
+ setgid(gid);
+ if (ret == -1){
+ perror("setgid");
+ }
+
if_index = (AvahiIfIndex) if_nametoindex(interface);
if(if_index == 0){
fprintf(stderr, "No such interface: \"%s\"\n", interface);
- exitcode = EX_UNAVAILABLE;
- goto end;
- }
-
- if(quit_now){
- goto end;
- }
-
- /* Re-raise priviliges */
- errno = 0;
- ret = seteuid(0);
- if(ret == -1){
- perror("seteuid");
- }
-
-#ifdef __linux__
- /* Lower kernel loglevel to KERN_NOTICE to avoid KERN_INFO
- messages about the network interface to mess up the prompt */
- ret = klogctl(8, NULL, 5);
- bool restore_loglevel = true;
- if(ret == -1){
- restore_loglevel = false;
- perror("klogctl");
- }
-#endif /* __linux__ */
-
- sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
- if(sd < 0){
- perror("socket");
- exitcode = EX_OSERR;
-#ifdef __linux__
- if(restore_loglevel){
- ret = klogctl(7, NULL, 0);
- if(ret == -1){
- perror("klogctl");
- }
- }
-#endif /* __linux__ */
- /* Lower privileges */
- errno = 0;
- ret = seteuid(uid);
- if(ret == -1){
- perror("seteuid");
- }
- goto end;
- }
- strcpy(network.ifr_name, interface);
- ret = ioctl(sd, SIOCGIFFLAGS, &network);
- if(ret == -1){
- perror("ioctl SIOCGIFFLAGS");
-#ifdef __linux__
- if(restore_loglevel){
- ret = klogctl(7, NULL, 0);
- if(ret == -1){
- perror("klogctl");
- }
- }
-#endif /* __linux__ */
- exitcode = EX_OSERR;
- /* Lower privileges */
- errno = 0;
- ret = seteuid(uid);
- if(ret == -1){
- perror("seteuid");
- }
- goto end;
- }
- if((network.ifr_flags & IFF_UP) == 0){
- network.ifr_flags |= IFF_UP;
- take_down_interface = true;
- ret = ioctl(sd, SIOCSIFFLAGS, &network);
- if(ret == -1){
- take_down_interface = false;
- perror("ioctl SIOCSIFFLAGS +IFF_UP");
- exitcode = EX_OSERR;
-#ifdef __linux__
- if(restore_loglevel){
- ret = klogctl(7, NULL, 0);
- if(ret == -1){
- perror("klogctl");
- }
- }
-#endif /* __linux__ */
- /* Lower privileges */
- errno = 0;
- ret = seteuid(uid);
- if(ret == -1){
- perror("seteuid");
- }
- goto end;
- }
- }
- /* sleep checking until interface is running */
- for(int i=0; i < delay * 4; i++){
+ exit(EXIT_FAILURE);
+ }
+
+ if(connect_to != NULL){
+ /* Connect directly, do not use Zeroconf */
+ /* (Mainly meant for debugging) */
+ char *address = strrchr(connect_to, ':');
+ if(address == NULL){
+ fprintf(stderr, "No colon in address\n");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+ errno = 0;
+ uint16_t port = (uint16_t) strtol(address+1, NULL, 10);
+ if(errno){
+ perror("Bad port number");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+ *address = '\0';
+ address = connect_to;
+ ret = start_mandos_communication(address, port, if_index, &mc);
+ if(ret < 0){
+ exitcode = EXIT_FAILURE;
+ } else {
+ exitcode = EXIT_SUCCESS;
+ }
+ goto end;
+ }
+
+ /* If the interface is down, bring it up */
+ {
+ sd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
+ if(sd < 0) {
+ perror("socket");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+ strcpy(network.ifr_name, interface); /* Spurious warning */
ret = ioctl(sd, SIOCGIFFLAGS, &network);
if(ret == -1){
perror("ioctl SIOCGIFFLAGS");
- } else if(network.ifr_flags & IFF_RUNNING){
- break;
- }
- struct timespec sleeptime = { .tv_nsec = 250000000 };
- ret = nanosleep(&sleeptime, NULL);
- if(ret == -1 and errno != EINTR){
- perror("nanosleep");
- }
- }
- if(not take_down_interface){
- /* We won't need the socket anymore */
- ret = (int)TEMP_FAILURE_RETRY(close(sd));
- if(ret == -1){
- perror("close");
- }
- }
-#ifdef __linux__
- if(restore_loglevel){
- /* Restores kernel loglevel to default */
- ret = klogctl(7, NULL, 0);
- if(ret == -1){
- perror("klogctl");
- }
- }
-#endif /* __linux__ */
- /* Lower privileges */
- errno = 0;
- if(take_down_interface){
- /* Lower privileges */
- ret = seteuid(uid);
- if(ret == -1){
- perror("seteuid");
- }
- } else {
- /* Lower privileges permanently */
- ret = setuid(uid);
- if(ret == -1){
- perror("setuid");
- }
- }
- }
-
- if(quit_now){
- goto end;
- }
-
- ret = init_gnutls_global(pubkey, seckey);
- if(ret == -1){
- fprintf(stderr, "init_gnutls_global failed\n");
- exitcode = EX_UNAVAILABLE;
- goto end;
- } else {
- gnutls_initialized = true;
- }
-
- if(quit_now){
- goto end;
- }
-
- tempdir_created = true;
- if(mkdtemp(tempdir) == NULL){
- tempdir_created = false;
- perror("mkdtemp");
- goto end;
- }
-
- if(quit_now){
- goto end;
- }
-
- if(not init_gpgme(pubkey, seckey, tempdir)){
- fprintf(stderr, "init_gpgme failed\n");
- exitcode = EX_UNAVAILABLE;
- goto end;
- } else {
- gpgme_initialized = true;
- }
-
- if(quit_now){
- goto end;
- }
-
- if(connect_to != NULL){
- /* Connect directly, do not use Zeroconf */
- /* (Mainly meant for debugging) */
- char *address = strrchr(connect_to, ':');
- if(address == NULL){
- fprintf(stderr, "No colon in address\n");
- exitcode = EX_USAGE;
- goto end;
- }
-
- if(quit_now){
- goto end;
- }
-
- uint16_t port;
- errno = 0;
- tmpmax = strtoimax(address+1, &tmp, 10);
- if(errno != 0 or tmp == address+1 or *tmp != '\0'
- or tmpmax != (uint16_t)tmpmax){
- fprintf(stderr, "Bad port number\n");
- exitcode = EX_USAGE;
- goto end;
- }
-
- if(quit_now){
- goto end;
- }
-
- port = (uint16_t)tmpmax;
- *address = '\0';
- address = connect_to;
- /* Colon in address indicates IPv6 */
- int af;
- if(strchr(address, ':') != NULL){
- af = AF_INET6;
- } else {
- af = AF_INET;
- }
-
- if(quit_now){
- goto end;
- }
-
- while(not quit_now){
- ret = start_mandos_communication(address, port, if_index, af);
- if(quit_now or ret == 0){
- break;
- }
- sleep(15);
- };
-
- if (not quit_now){
- exitcode = EXIT_SUCCESS;
- }
-
- goto end;
- }
-
- if(quit_now){
- goto end;
- }
-
- {
- AvahiServerConfig config;
- /* Do not publish any local Zeroconf records */
- avahi_server_config_init(&config);
- config.publish_hinfo = 0;
- config.publish_addresses = 0;
- config.publish_workstation = 0;
- config.publish_domain = 0;
-
- /* Allocate a new server */
- mc.server = avahi_server_new(avahi_simple_poll_get
- (mc.simple_poll), &config, NULL,
- NULL, &error);
-
- /* Free the Avahi configuration data */
- avahi_server_config_free(&config);
- }
-
- /* Check if creating the Avahi server object succeeded */
- if(mc.server == NULL){
- fprintf(stderr, "Failed to create Avahi server: %s\n",
- avahi_strerror(error));
- exitcode = EX_UNAVAILABLE;
- goto end;
- }
-
- if(quit_now){
- goto end;
- }
-
- /* Create the Avahi service browser */
- sb = avahi_s_service_browser_new(mc.server, if_index,
- AVAHI_PROTO_UNSPEC, "_mandos._tcp",
- NULL, 0, browse_callback, NULL);
- if(sb == NULL){
- fprintf(stderr, "Failed to create service browser: %s\n",
- avahi_strerror(avahi_server_errno(mc.server)));
- exitcode = EX_UNAVAILABLE;
- goto end;
- }
-
- if(quit_now){
- goto end;
- }
-
- /* Run the main loop */
-
- if(debug){
- fprintf(stderr, "Starting Avahi loop search\n");
- }
-
- avahi_simple_poll_loop(mc.simple_poll);
-
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+ if((network.ifr_flags & IFF_UP) == 0){
+ network.ifr_flags |= IFF_UP;
+ ret = ioctl(sd, SIOCSIFFLAGS, &network);
+ if(ret == -1){
+ perror("ioctl SIOCSIFFLAGS");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+ }
+ close(sd);
+ }
+
+ if (not debug){
+ avahi_set_log_function(empty_log);
+ }
+
+ /* Initialize the pseudo-RNG for Avahi */
+ srand((unsigned int) time(NULL));
+
+ /* Allocate main Avahi loop object */
+ mc.simple_poll = avahi_simple_poll_new();
+ if (mc.simple_poll == NULL) {
+ fprintf(stderr, "Avahi: Failed to create simple poll"
+ " object.\n");
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+
+ {
+ AvahiServerConfig config;
+ /* Do not publish any local Zeroconf records */
+ avahi_server_config_init(&config);
+ config.publish_hinfo = 0;
+ config.publish_addresses = 0;
+ config.publish_workstation = 0;
+ config.publish_domain = 0;
+
+ /* Allocate a new server */
+ mc.server = avahi_server_new(avahi_simple_poll_get
+ (mc.simple_poll), &config, NULL,
+ NULL, &error);
+
+ /* Free the Avahi configuration data */
+ avahi_server_config_free(&config);
+ }
+
+ /* Check if creating the Avahi server object succeeded */
+ if (mc.server == NULL) {
+ fprintf(stderr, "Failed to create Avahi server: %s\n",
+ avahi_strerror(error));
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+
+ /* Create the Avahi service browser */
+ sb = avahi_s_service_browser_new(mc.server, if_index,
+ AVAHI_PROTO_INET6,
+ "_mandos._tcp", NULL, 0,
+ browse_callback, &mc);
+ if (sb == NULL) {
+ fprintf(stderr, "Failed to create service browser: %s\n",
+ avahi_strerror(avahi_server_errno(mc.server)));
+ exitcode = EXIT_FAILURE;
+ goto end;
+ }
+
+ /* Run the main loop */
+
+ if (debug){
+ fprintf(stderr, "Starting Avahi loop search\n");
+ }
+
+ avahi_simple_poll_loop(mc.simple_poll);
+
end:
-
- if(debug){
- fprintf(stderr, "%s exiting\n", argv[0]);
- }
-
- /* Cleanup things */
- if(sb != NULL)
- avahi_s_service_browser_free(sb);
-
- if(mc.server != NULL)
- avahi_server_free(mc.server);
-
- if(mc.simple_poll != NULL)
- avahi_simple_poll_free(mc.simple_poll);
-
- if(gnutls_initialized){
- gnutls_certificate_free_credentials(mc.cred);
- gnutls_global_deinit();
- gnutls_dh_params_deinit(mc.dh_params);
- }
-
- if(gpgme_initialized){
- gpgme_release(mc.ctx);
- }
-
- /* Take down the network interface */
- if(take_down_interface){
- /* Re-raise priviliges */
- errno = 0;
- ret = seteuid(0);
- if(ret == -1){
- perror("seteuid");
- }
- if(geteuid() == 0){
- ret = ioctl(sd, SIOCGIFFLAGS, &network);
- if(ret == -1){
- perror("ioctl SIOCGIFFLAGS");
- } else if(network.ifr_flags & IFF_UP) {
- network.ifr_flags &= ~(short)IFF_UP; /* clear flag */
- ret = ioctl(sd, SIOCSIFFLAGS, &network);
- if(ret == -1){
- perror("ioctl SIOCSIFFLAGS -IFF_UP");
- }
- }
- ret = (int)TEMP_FAILURE_RETRY(close(sd));
- if(ret == -1){
- perror("close");
- }
- /* Lower privileges permanently */
- errno = 0;
- ret = setuid(uid);
- if(ret == -1){
- perror("setuid");
- }
- }
- }
-
- /* Removes the temp directory used by GPGME */
- if(tempdir_created){
- DIR *d;
- struct dirent *direntry;
- d = opendir(tempdir);
- if(d == NULL){
- if(errno != ENOENT){
- perror("opendir");
- }
- } else {
- while(true){
- direntry = readdir(d);
- if(direntry == NULL){
- break;
- }
- /* Skip "." and ".." */
- if(direntry->d_name[0] == '.'
- and (direntry->d_name[1] == '\0'
- or (direntry->d_name[1] == '.'
- and direntry->d_name[2] == '\0'))){
- continue;
- }
- char *fullname = NULL;
- ret = asprintf(&fullname, "%s/%s", tempdir,
- direntry->d_name);
- if(ret < 0){
- perror("asprintf");
- continue;
- }
- ret = remove(fullname);
- if(ret == -1){
- fprintf(stderr, "remove(\"%s\"): %s\n", fullname,
- strerror(errno));
- }
- free(fullname);
- }
- closedir(d);
- }
- ret = rmdir(tempdir);
- if(ret == -1 and errno != ENOENT){
- perror("rmdir");
- }
- }
-
- if(quit_now){
- sigemptyset(&old_sigterm_action.sa_mask);
- old_sigterm_action.sa_handler = SIG_DFL;
- ret = (int)TEMP_FAILURE_RETRY(sigaction(signal_received,
- &old_sigterm_action,
- NULL));
- if(ret == -1){
- perror("sigaction");
- }
- do {
- ret = raise(signal_received);
- } while(ret != 0 and errno == EINTR);
- if(ret != 0){
- perror("raise");
- abort();
- }
- TEMP_FAILURE_RETRY(pause());
- }
-
- return exitcode;
+
+ if (debug){
+ fprintf(stderr, "%s exiting\n", argv[0]);
+ }
+
+ /* Cleanup things */
+ if (sb != NULL)
+ avahi_s_service_browser_free(sb);
+
+ if (mc.server != NULL)
+ avahi_server_free(mc.server);
+
+ if (mc.simple_poll != NULL)
+ avahi_simple_poll_free(mc.simple_poll);
+ free(pubkeyfile);
+ free(seckeyfile);
+
+ if (gnutls_initalized){
+ gnutls_certificate_free_credentials (mc.cred);
+ gnutls_global_deinit ();
+ }
+
+ return exitcode;
}
=== removed file 'plugins.d/plymouth.c'
--- plugins.d/plymouth.c 2010-09-26 18:32:58 +0000
+++ plugins.d/plymouth.c 1970-01-01 00:00:00 +0000
@@ -1,431 +0,0 @@
-/* -*- coding: utf-8 -*- */
-/*
- * Usplash - Read a password from usplash and output it
- *
- * Copyright © 2010 Teddy Hogeborn
- * Copyright © 2010 Björn Påhlsson
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- * .
- *
- * Contact the authors at .
- */
-
-#define _GNU_SOURCE /* asprintf(), TEMP_FAILURE_RETRY() */
-#include /* sig_atomic_t, struct sigaction,
- sigemptyset(), sigaddset(), SIGINT,
- SIGHUP, SIGTERM, sigaction(),
- kill(), SIG_IGN */
-#include /* bool, false, true */
-#include /* open(), O_RDONLY */
-#include /* and, or, not*/
-#include /* size_t, ssize_t, pid_t, struct
- dirent, waitpid() */
-#include /* waitpid() */
-#include /* NULL */
-#include /* strchr(), memcmp() */
-#include /* asprintf(), perror(), fopen(),
- fscanf() */
-#include /* close(), readlink(), read(),
- fork(), setsid(), chdir(), dup2(),
- STDERR_FILENO, execv(), access() */
-#include /* free(), EXIT_FAILURE, realloc(),
- EXIT_SUCCESS, malloc(), _exit(),
- getenv() */
-#include /* scandir(), alphasort() */
-#include /* intmax_t, strtoumax(), SCNuMAX */
-#include /* struct stat, lstat() */
-#include /* EX_OSERR, EX_UNAVAILABLE */
-#include /* error() */
-#include /* TEMP_FAILURE_RETRY */
-#include /* argz_count(), argz_extract() */
-
-sig_atomic_t interrupted_by_signal = 0;
-const char plymouth_pid[] = "/dev/.initramfs/plymouth.pid";
-const char plymouth_path[] = "/bin/plymouth";
-const char plymouthd_path[] = "/sbin/plymouthd";
-const char *plymouthd_default_argv[] = {"/sbin/plymouthd",
- "--mode=boot",
- "--attach-to-session",
- "--pid-file="
- "/dev/.initramfs/"
- "plymouth.pid",
- NULL };
-
-static void termination_handler(__attribute__((unused))int signum){
- if(interrupted_by_signal){
- return;
- }
- interrupted_by_signal = 1;
-}
-
-/* Create prompt string */
-char *makeprompt(void){
- int ret = 0;
- char *prompt;
- const char *const cryptsource = getenv("cryptsource");
- const char *const crypttarget = getenv("crypttarget");
- const char prompt_start[] = "Enter passphrase to unlock the disk";
-
- if(cryptsource == NULL){
- if(crypttarget == NULL){
- ret = asprintf(&prompt, "%s: ", prompt_start);
- } else {
- ret = asprintf(&prompt, "%s (%s): ", prompt_start,
- crypttarget);
- }
- } else {
- if(crypttarget == NULL){
- ret = asprintf(&prompt, "%s %s: ", prompt_start, cryptsource);
- } else {
- ret = asprintf(&prompt, "%s %s (%s): ", prompt_start,
- cryptsource, crypttarget);
- }
- }
- if(ret == -1){
- return NULL;
- }
- return prompt;
-}
-
-void kill_and_wait(pid_t pid){
- TEMP_FAILURE_RETRY(kill(pid, SIGTERM));
- TEMP_FAILURE_RETRY(waitpid(pid, NULL, 0));
-}
-
-bool become_a_daemon(void){
- int ret = setuid(geteuid());
- if(ret == -1){
- error(0, errno, "setuid");
- }
-
- setsid();
- ret = chdir("/");
- if(ret == -1){
- error(0, errno, "chdir");
- return false;
- }
- ret = dup2(STDERR_FILENO, STDOUT_FILENO); /* replace our stdout */
- if(ret == -1){
- error(0, errno, "dup2");
- return false;
- }
- return true;
-}
-
-bool exec_and_wait(pid_t *pid_return, const char *path,
- const char **argv, bool interruptable,
- bool daemonize){
- int status;
- int ret;
- pid_t pid;
- pid = fork();
- if(pid == -1){
- error(0, errno, "fork");
- return false;
- }
- if(pid == 0){
- /* Child */
- if(daemonize){
- if(not become_a_daemon()){
- _exit(EX_OSERR);
- }
- }
-
- char **new_argv = NULL;
- char *tmp;
- int i = 0;
- for (; argv[i]!=(char *)NULL; i++){
- tmp = realloc(new_argv, sizeof(const char *) * ((size_t)i + 1));
- if (tmp == NULL){
- error(0, errno, "realloc");
- free(new_argv);
- _exit(EX_OSERR);
- }
- new_argv = (char **)tmp;
- new_argv[i] = strdup(argv[i]);
- }
- new_argv[i] = (char *) NULL;
-
- execv(path, (char *const *)new_argv);
- error(0, errno, "execv");
- _exit(EXIT_FAILURE);
- }
- if(pid_return != NULL){
- *pid_return = pid;
- }
- do {
- ret = waitpid(pid, &status, 0);
- } while(ret == -1 and errno == EINTR
- and ((not interrupted_by_signal)
- or (not interruptable)));
- if(interrupted_by_signal and interruptable){
- return false;
- }
- if(ret == -1){
- error(0, errno, "waitpid");
- return false;
- }
- if(WIFEXITED(status) and (WEXITSTATUS(status) == 0)){
- return true;
- }
- return false;
-}
-
-int is_plymouth(const struct dirent *proc_entry){
- int ret;
- {
- uintmax_t maxvalue;
- char *tmp;
- errno = 0;
- maxvalue = strtoumax(proc_entry->d_name, &tmp, 10);
-
- if(errno != 0 or *tmp != '\0'
- or maxvalue != (uintmax_t)((pid_t)maxvalue)){
- return 0;
- }
- }
- char exe_target[sizeof(plymouth_path)];
- char *exe_link;
- ret = asprintf(&exe_link, "/proc/%s/exe", proc_entry->d_name);
- if(ret == -1){
- error(0, errno, "asprintf");
- return 0;
- }
-
- struct stat exe_stat;
- ret = lstat(exe_link, &exe_stat);
- if(ret == -1){
- free(exe_link);
- if(errno != ENOENT){
- error(0, errno, "lstat");
- }
- return 0;
- }
-
- if(not S_ISLNK(exe_stat.st_mode)
- or exe_stat.st_uid != 0
- or exe_stat.st_gid != 0){
- free(exe_link);
- return 0;
- }
-
- ssize_t sret = readlink(exe_link, exe_target, sizeof(exe_target));
- free(exe_link);
- if((sret != (ssize_t)sizeof(plymouth_path)-1) or
- (memcmp(plymouth_path, exe_target,
- sizeof(plymouth_path)-1) != 0)){
- return 0;
- }
- return 1;
-}
-
-pid_t get_pid(void){
- int ret;
- FILE *pidfile = fopen(plymouth_pid, "r");
- uintmax_t maxvalue = 0;
- if(pidfile != NULL){
- ret = fscanf(pidfile, "%" SCNuMAX, &maxvalue);
- if(ret != 1){
- maxvalue = 0;
- }
- fclose(pidfile);
- }
- if(maxvalue == 0){
- struct dirent **direntries;
- ret = scandir("/proc", &direntries, is_plymouth, alphasort);
- sscanf(direntries[0]->d_name, "%" SCNuMAX, &maxvalue);
- }
- pid_t pid;
- pid = (pid_t)maxvalue;
- if((uintmax_t)pid == maxvalue){
- return pid;
- }
-
- return 0;
-}
-
-const char **getargv(pid_t pid){
- int cl_fd;
- char *cmdline_filename;
- ssize_t sret;
- int ret;
-
- ret = asprintf(&cmdline_filename, "/proc/%" PRIuMAX "/cmdline",
- (uintmax_t)pid);
- if(ret == -1){
- error(0, errno, "asprintf");
- return NULL;
- }
-
- /* Open /proc//cmdline */
- cl_fd = open(cmdline_filename, O_RDONLY);
- free(cmdline_filename);
- if(cl_fd == -1){
- error(0, errno, "open");
- return NULL;
- }
-
- size_t cmdline_allocated = 0;
- size_t cmdline_len = 0;
- char *cmdline = NULL;
- char *tmp;
- const size_t blocksize = 1024;
- do {
- /* Allocate more space? */
- if(cmdline_len + blocksize > cmdline_allocated){
- tmp = realloc(cmdline, cmdline_allocated + blocksize);
- if(tmp == NULL){
- error(0, errno, "realloc");
- free(cmdline);
- close(cl_fd);
- return NULL;
- }
- cmdline = tmp;
- cmdline_allocated += blocksize;
- }
-
- /* Read data */
- sret = read(cl_fd, cmdline + cmdline_len,
- cmdline_allocated - cmdline_len);
- if(sret == -1){
- error(0, errno, "read");
- free(cmdline);
- close(cl_fd);
- return NULL;
- }
- cmdline_len += (size_t)sret;
- } while(sret != 0);
- ret = close(cl_fd);
- if(ret == -1){
- error(0, errno, "close");
- free(cmdline);
- return NULL;
- }
-
- /* we got cmdline and cmdline_len, ignore rest... */
- char **argv = malloc((argz_count(cmdline, cmdline_len) + 1)
- * sizeof(char *)); /* Get number of args */
- if(argv == NULL){
- error(0, errno, "argv = malloc()");
- free(cmdline);
- return NULL;
- }
- argz_extract(cmdline, cmdline_len, argv); /* Create argv */
- return (const char **)argv;
-}
-
-int main(__attribute__((unused))int argc,
- __attribute__((unused))char **argv){
- char *prompt;
- char *prompt_arg;
- pid_t plymouth_command_pid;
- int ret;
- bool bret;
-
- /* test -x /bin/plymouth */
- ret = access(plymouth_path, X_OK);
- if(ret == -1){
- /* Plymouth is probably not installed. Don't print an error
- message, just exit. */
- exit(EX_UNAVAILABLE);
- }
-
- { /* Add signal handlers */
- struct sigaction old_action,
- new_action = { .sa_handler = termination_handler,
- .sa_flags = 0 };
- sigemptyset(&new_action.sa_mask);
- for(int *sig = (int[]){ SIGINT, SIGHUP, SIGTERM, 0 };
- *sig != 0; sig++){
- ret = sigaddset(&new_action.sa_mask, *sig);
- if(ret == -1){
- error(EX_OSERR, errno, "sigaddset");
- }
- ret = sigaction(*sig, NULL, &old_action);
- if(ret == -1){
- error(EX_OSERR, errno, "sigaction");
- }
- if(old_action.sa_handler != SIG_IGN){
- ret = sigaction(*sig, &new_action, NULL);
- if(ret == -1){
- error(EX_OSERR, errno, "sigaction");
- }
- }
- }
- }
-
- /* plymouth --ping */
- bret = exec_and_wait(&plymouth_command_pid, plymouth_path,
- (const char *[])
- { plymouth_path, "--ping", NULL },
- true, false);
- if(not bret){
- if(interrupted_by_signal){
- kill_and_wait(plymouth_command_pid);
- exit(EXIT_FAILURE);
- }
- /* Plymouth is probably not running. Don't print an error
- message, just exit. */
- exit(EX_UNAVAILABLE);
- }
-
- prompt = makeprompt();
- ret = asprintf(&prompt_arg, "--prompt=%s", prompt);
- free(prompt);
- if(ret == -1){
- error(EX_OSERR, errno, "asprintf");
- }
-
- /* plymouth ask-for-password --prompt="$prompt" */
- bret = exec_and_wait(&plymouth_command_pid,
- plymouth_path, (const char *[])
- { plymouth_path, "ask-for-password",
- prompt_arg, NULL },
- true, false);
- free(prompt_arg);
- if(bret){
- exit(EXIT_SUCCESS);
- }
- if(not interrupted_by_signal){
- /* exec_and_wait failed for some other reason */
- exit(EXIT_FAILURE);
- }
- kill_and_wait(plymouth_command_pid);
-
- const char **plymouthd_argv;
- pid_t pid = get_pid();
- if(pid == 0){
- error(0, 0, "plymouthd pid not found");
- plymouthd_argv = plymouthd_default_argv;
- } else {
- plymouthd_argv = getargv(pid);
- }
-
- bret = exec_and_wait(NULL, plymouth_path, (const char *[])
- { plymouth_path, "quit", NULL },
- false, false);
- if(not bret){
- exit(EXIT_FAILURE);
- }
- bret = exec_and_wait(NULL, plymouthd_path, plymouthd_argv,
- false, true);
- if(not bret){
- exit(EXIT_FAILURE);
- }
- exec_and_wait(NULL, plymouth_path, (const char *[])
- { plymouth_path, "show-splash", NULL },
- false, false);
- exit(EXIT_FAILURE);
-}
=== removed file 'plugins.d/plymouth.xml'
--- plugins.d/plymouth.xml 2010-09-26 18:32:58 +0000
+++ plugins.d/plymouth.xml 1970-01-01 00:00:00 +0000
@@ -1,278 +0,0 @@
-
-
-
-
-%common;
-]>
-
-
-
- Mandos Manual
-
- Mandos
- &version;
- &TIMESTAMP;
-
-
- Björn
- Påhlsson
-
- belorn@fukt.bsnet.se
-
-
-
- Teddy
- Hogeborn
-
- teddy@fukt.bsnet.se
-
-
-
-
- 2010
- Teddy Hogeborn
- Björn Påhlsson
-
-
-
-
-
- &COMMANDNAME;
- 8mandos
-
-
-
- &COMMANDNAME;
- Mandos plugin to use plymouth to get a
- password.
-
-
-
-
- &COMMANDNAME;
-
-
-
-
- DESCRIPTION
-
- This program prompts for a password using
- plymouth8
- and outputs any given password to standard
- output. If no plymouth8
- process can be found, this program will immediately exit with an
- exit code indicating failure.
-
-
- This program is not very useful on its own. This program is
- really meant to run as a plugin in the Mandos client-side system, where it is used as a
- fallback and alternative to retrieving passwords from a
- Mandos server.
-
-
- If this program is killed (presumably by
- plugin-runner
- 8mandos because some other
- plugin provided the password), it cannot tell
- plymouth8
- to abort requesting a password, because
- plymouth
- 8 does not support this.
- Therefore, this program will then kill the
- running plymouth
- 8 process and start a
- new one using the same command line
- arguments as the old one was using.
-
-
-
-
- OPTIONS
-
- This program takes no options.
-
-
-
-
- EXIT STATUS
-
- If exit status is 0, the output from the program is the password
- as it was read. Otherwise, if exit status is other than 0, the
- program was interrupted or encountered an error, and any output
- so far could be corrupt and/or truncated, and should therefore
- be ignored.
-
-
-
-
- ENVIRONMENT
-
-
- cryptsource
- crypttarget
-
-
- If set, these environment variables will be assumed to
- contain the source device name and the target device
- mapper name, respectively, and will be shown as part of
- the prompt.
-
-
- These variables will normally be inherited from
-