pax_global_header00006660000000000000000000000064134221667230014520gustar00rootroot0000000000000052 comment=56ce950295b3b3315ee3ff83e52fdea4797d91a3 monkeysphere-0.43/000077500000000000000000000000001342216672300141575ustar00rootroot00000000000000monkeysphere-0.43/.gitignore000066400000000000000000000000571342216672300161510ustar00rootroot00000000000000*~ src/agent-transfer/agent-transfer replaced/ monkeysphere-0.43/COPYING000066400000000000000000001061771342216672300152260ustar00rootroot00000000000000Monkeysphere is a system to use the OpenPGP web-of-trust to authenticate and encrypt ssh connections. It is free software, developed by: Jameson Rollins Daniel Kahn Gillmor Jamie McClelland Micah Anderson Matthew Goins Mike Castleman Elliot Winard Ross Glover Greg Lyle Monkeysphere 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. Monkeysphere Copyright 2007, and are all released under the GPL, version 3 or later. 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 . monkeysphere-0.43/Changelog000066400000000000000000000550041342216672300157750ustar00rootroot00000000000000monkeysphere (0.43) unstable; urgency=medium * Depend on a modern version of GnuPG (>= 2.1.11) for --export-ssh-key * Depend on OpenSSH's ssh-keygen directly for most SSH fingerprints * Depend on OpenSSH >= 6.0 for ed25519 and "sshd -T" * Use runuser instead of su * Support Ed25519 authentication-capable subkeys for users * Use https for all outbound links * Clean up spelling * Use 3072 bits for RSA keys everywhere by default * Provide clearer error message for PEM2OPENPGP_NEWKEY (Closes: #906755) * Avoid locking out users unnecessarily (Closes: #897366) -- Daniel Kahn Gillmor Wed, 23 Jan 2019 17:42:19 -0500 monkeysphere (0.42) unstable; urgency=medium * bugfix release: * use --send-keys instead of --send (closes: #908228) * tests: Ensure that stale sockets don't fail socat (Closes: #899060) * Remove deprecated option from test sshd config (Closes: #902320) * write old-style PEM files to unbreak test suite (Closes: #909700) * yet more colon fixes that escaped previous inspections * fix more gnupg2 colons changes (Closes: #902367) * Remove RSAAuthentication from test ssh config (Closes: #902318) * clean up test suite failures when built against newer GnuPG * use generic compiler (closes: #883015) * make print_date_from_seconds_since_the_epoch deal better with bad input -- Daniel Kahn Gillmor Tue, 16 Oct 2018 11:38:39 -0400 monkeysphere (0.41) unstable; urgency=medium * pem2openpgp now includes issuer fingerprint subpacket in hashed self-sig, more compatible with GnuPG 2.1.16 (Closes: #846554) * avoid blocking for entropy during test suite (Closes: #841208) * augment test suite for id certifier with a subkey, for better realism * ensure that attempts to fetch primary key fingerprint only fetch primary key fingerprint even if subkey fprs are emitted (Closes: #846554) * include $CPPFLAGS in agent-transfer build -- Daniel Kahn Gillmor Wed, 12 Oct 2016 01:12:27 -0400 monkeysphere (0.40) unstable; urgency=medium * bugfix release: * get tests to pass against GnuPG 2.1.15 * build more portably -- Daniel Kahn Gillmor Wed, 12 Oct 2016 01:12:27 -0400 monkeysphere (0.39) unstable; urgency=medium * avoid warning about unused asprintf return value * avoid treating src/share/common as an executable * ensure that this works even if SYSSHAREDIR has whitespace * Include local build of agent-transfer in $PATH (Closes: #835719) * force bash as the shell during su (Closes: #827660) -- Daniel Kahn Gillmor Tue, 30 Aug 2016 03:10:53 -0400 monkeysphere (0.38) unstable; urgency=medium * make tests work with modern gpg (2.1.x and later) as /usr/bin/gpg * this introduces a new binary, agent-transfer, to transfer secret keys from gpg-agent to ssh-agent. -- Daniel Kahn Gillmor Tue, 17 May 2016 00:44:32 -0400 monkeysphere (0.37) unstable; urgency=medium * Bugfix release with minor improvements and dependency accommodations. * Test openpgp2ssh functionality (closes MS #6524) * use new GnuPG with-colons output * accommodate changed behavior of ssh-keygen -F * accommodate multiple AuthorizedKeysFile directives * deal sanely with empty lines in authorized_user_ids (closes MS #6344) * treat non-standard ports properly (closes MS #3402) -- Daniel Kahn Gillmor Wed, 06 Aug 2014 13:23:04 -0400 monkeysphere (0.36) unstable; urgency=low * keytrans no longer confuses user IDs across different keys (closes MS #2682) * fetch all available keys from keyserver instead of first 5 (closes MS #1046) * enable openpgp2pem for keytrans (Closes: #698383) * enable openpgp2spki as well [ Jonas Smedegaard ] * m-a gpg-cmd now takes its arguments as separate parameters, not as a single string. -- Daniel Kahn Gillmor Thu, 11 Jul 2013 13:19:45 -0400 monkeysphere (0.35) upstream; * Remove reference to USE_VALIDATION_AGENT. * Fix ssh_proxycommand for marginal hosts (closes MS #2593) * GnuPG should always behave as --fixed-list-mode (closes MS #2587) -- Jameson Rollins Fri, 29 Oct 2010 20:21:54 -0400 monkeysphere (0.34) upstream; * fix keys-for-user so that it outputs proper authorized_keys lines (close MS #2550) * refactor key processing for key files, greatly reducing redundant code paths * update authorized_keys and known_hosts in temp filess that are atomically moved into place * don't fail if authorized_keys file not already present (Closes: 600644) * document CHECK_KEYSERVER in monkeysphere-authentication man page (close MS #2556) -- Jameson Rollins Tue, 26 Oct 2010 10:27:01 -0400 monkeysphere (0.33) upstream; [ Daniel Kahn Gillmor ] * defaulting MONKEYSPHERE_HASH_KNOWN_HOSTS to false (closes MS #2483) [ Jameson Rollins ] * fix security vulnerability is parsing userids in monkeysphere-authentication keys-for-user (Closes: #600304) * fix failure after first invalid key in monkeysphere-authentication keys-for-user (closes MS #2545) * ignore command options in monkeysphere-authentication keys-for-user -- Jameson Rollins Fri, 15 Oct 2010 18:05:18 -0400 monkeysphere (0.32) upstream; [ Jameson Rollins ] * Fix specification of install paths in all scripts and man pages (closes MS #2491) * Fix need for single argument to gpg_sphere (thanks Clint) (closes MS #442) * specify LC_ALL=C for all gpg calls (closes MS #2496) [ Micah Anderson ] * fix monkeysphere-host revoke-key, which never worked properly :( * add some debug output to monkeysphere-host publish-key (closes MS #2289) [ Clint Adams ] * add support for options to the authorized User IDs file. Options that should apply to keys for a given User ID should be on whitespace-prefixed lines immediately following that User ID. (closes MS #440) -- Jameson Rollins Wed, 06 Oct 2010 17:41:09 -0400 monkeysphere (0.31) upstream; [ Daniel Kahn Gillmor ] * support x509 anchors for monkeysphere-host, allow shared anchor between m-h and m-a (closes MS #2288) * do not bail or fail on m-h publish-key if the admin interactively declines to publish one of the keys key (closes MS #1945) * report updated expiration date upon successful conclusion of m-h set-expire (closes MS #2291) * added some files in examples/ to demonstrate system integration with OpenSSH [ Jameson Rollins ] * add keys-for-user subcommand to monkeysphere-authentication -- Daniel Kahn Gillmor Thu, 15 Jul 2010 19:20:35 -0400 monkeysphere (0.30) upstream; * changing tarball creation and packaging strategies * make non-ssh parts of monkeysphere work well when openssh is not installed; degrade ssh-specific parts gracefully when openssh is not installed. -- Daniel Kahn Gillmor Sat, 17 Apr 2010 16:46:52 -0400 monkeysphere (0.29) upstream; * This is mainly a bugfix release * Fix man page typo about monkeysphere authorized_keys location * Monkeysphere should work properly even if the user has "armor" in their gpg.conf (closes MS #1625) * monkeysphere keys-for-userid now respects MONKEYSPHERE_CHECK_KEYSERVER environment variable (and defaults to true) * introduce monkeysphere sshfprs-for-userid (deprecates sshfpr), closes MS #1436 * respect CHECK_KEYSERVER in more places (closes MS #1997) * warn on keyserver failures for monkeysphere-authentication (closes MS #1750) * avoid checking trustdb for monkeysphere-host (closes MS #1957) * allow monkeysphere-authentication to use hkps with trusted X.509 root certificate authorities in /etc/monkeysphere/monkeysphere-authentication-x509-anchors.crt -- Daniel Kahn Gillmor Sun, 14 Mar 2010 21:00:47 -0400 monkeysphere (0.28) upstream; * Major rework of monkeysphere-host to handle multiple host keys. We also no longer assume ssh service keys. monkeysphere-host is now a general-purpose host service OpenPGP key management UI. * Rename keys-from-userid command to more accurate keys-for-userid * separate upstream and debian changelogs -- Jameson Rollins Tue, 19 Jan 2010 13:50:31 -0500 monkeysphere (0.27) upstream; * fixed monkeysphere gen-subkey subcommand that was erroneously creating DSA subkeys due to unannounced change in gpg edit-key UI. Now tests for gpg version (closes MS #1536) * add new monkeysphere keys-from-userid subcommand to output all acceptable keys for a given user ID literal -- Jameson Rollins Mon, 11 Jan 2010 20:54:21 -0500 monkeysphere (0.26) upstream; * add 'refresh-keys' subcommand to monkeysphere-authentication * improve marginal UI (closes MS #1141) * add MONKEYSPHERE_STRICT_MODES configuration to avoid permission-checking (closes MS #649) * test scripts use STRICT_MODES to avoid failure when built under /tmp * do permissions checks with a perl script instead of non-portable readlink GNUisms * bail on permissions check if we hit the home directory (helpful on Mac OS and other systems with loose /home or /Users (closes MS #675) -- Jameson Graef Rollins Sat, 01 Aug 2009 17:11:05 -0400 monkeysphere (0.25) upstream; * New upstream release: * update/fix the marginal ui output * use msmktempdir everywhere (avoid unwrapped calls to mktemp for portability) * clean out some redundant "cat"s * fix monkeysphere update-known_hosts for sshd running on non-standard ports * add 'sshfpr' subcommand to output the ssh fingerprint of a gpg key * pem2openpgp now generates self-sigs over SHA-256 instead of SHA-1 (changes dependency to libdigest-sha-perl) * some portability improvements * properly handle translation of keys with fingerprints with leading all-zero bytes. * resolve symlinks when checking paths (thanks Silvio Rhatto) (closes MS #917) * explicitly set and use MONKEYSPHERE_GROUP from system "groups" * monkeysphere-host now uses keytrans to add and revoke hostname (closes MS #422) -- Jameson Graef Rollins Thu, 16 Jul 2009 22:09:19 -0400 monkeysphere (0.24) upstream; * fixed how version information is stored/retrieved * now uses perl-based keytrans for both pem2openpgp and openpgp2ssh * no longer needs base64 in PATH * added "test" make target * improved transitions/0.23 script so it no longer fails in common circumstances (Closes: #517779) * RSA only: no longer handles DSA keys * added ability to specify subkeys to add to ssh agent with new MONKEYSPHERE_SUBKEYS_FOR_AGENT environment variable -- Jameson Graef Rollins Tue, 03 Mar 2009 19:38:33 -0500 monkeysphere (0.23) upstream; "The Golden Bezoar Release" * rearchitect UI: - replace monkeysphere-server with monkeysphere-{authentication,host} - fold monkeysphere-ssh-proxycommand into /usr/bin/monkeysphere * new ability to import existing ssh host key into monkeysphere. So now m-a import-key replaces m-s gen-key. * provide pem2openpgp for translating unencrypted PEM-encoded raw key material into OpenPGP keys (introduces new perl dependencies) * get rid of getopts dependency * added version output option * better checks for the existence of a host private key for monkeysphere-host subcommands that need it. * better checks on validity of existing authentication subkeys when doing monkeysphere gen_subkey. * add transition infrastructure for major changes between releases (see transitions/README.txt) * implement and document two new monkeysphere-host subcommands: revoke-key and add-revoker -- Daniel Kahn Gillmor Sat, 21 Feb 2009 17:51:06 -0500 monkeysphere (0.22) upstream; [ Jameson Graef Rollins ] * added info log output when a new key is added to known_hosts file. * added some useful output to the ssh-proxycommand for "marginal" cases where keys are found for host but do not have full validity. * force ssh-keygen to read from stdin to get ssh key fingerprint. [ Daniel Kahn Gillmor ] * automatically output two copies of the host's public key: one standard ssh public key file, and the other a minimal OpenPGP key with just the latest valid self-sig. * debian/control: corrected alternate dependency from procfile to procmail (which provides /usr/bin/lockfile) -- Jameson Graef Rollins Fri, 28 Nov 2008 14:23:31 -0500 monkeysphere (0.21) upstream; * move debian packaging to packaging subdirectory. -- Jameson Graef Rollins Sat, 15 Nov 2008 16:14:27 -0500 monkeysphere (0.20) upstream; [ Daniel Kahn Gillmor ] * ensure that tempdirs are properly created, bail out otherwise instead of stumbling ahead. * minor fussing with the test script to make it cleaner. [ Jameson Graef Rollins ] * clean up Makefile to generate more elegant source tarballs. * make myself the maintainer. -- Jameson Graef Rollins Sat, 15 Nov 2008 13:12:57 -0500 monkeysphere (0.19) experimental; urgency=low [ Daniel Kahn Gillmor ] * simulating an X11 session in the test script. * updated packaging so that symlinks to config files are correct. -- Daniel Kahn Gillmor Wed, 29 Oct 2008 02:47:49 -0400 monkeysphere (0.18) experimental; urgency=low [ Jameson Graef Rollins ] * Fix bugs in authorized_{user_ids,keys} file permission checking. * Add new monkeysphere tmpdir to enable atomic moves of authorized_keys files. * chown authorized_keys files to `whoami`, for compatibility with test suite. * major improvements to test suite, added more tests. [ Daniel Kahn Gillmor ] * update make install to ensure placement of /etc/monkeysphere/gnupg-{host,authentication}.conf * choose either --quick-random or --debug-quick-random depending on which gpg supports for the test suite. -- Daniel Kahn Gillmor Wed, 29 Oct 2008 00:41:38 -0400 monkeysphere (0.17) experimental; urgency=low [ Jameson Graef Rollins ] * Fix some bugs in, and cleanup, authorized_keys file creation in monkeysphere-server update-users. * Move to using the empty string for not adding a user-controlled authorized_keys file in the RAW_AUTHORIZED_KEYS variable. -- Daniel Kahn Gillmor Tue, 28 Oct 2008 02:04:22 -0400 monkeysphere (0.16) experimental; urgency=low [ Daniel Kahn Gillmor ] * replaced "#!/bin/bash" with "#!/usr/bin/env bash" for better portability. * fixed busted lockfile arrangement, where empty file was being locked * portability fixes in the way we use date, mktemp, hostname, su * stop using /usr/bin/stat, since the syntax appears to be totally unportable * require GNU getopt, and test for getopt failures (look for getopt in /usr/local/bin first, since that's where FreeBSD's GNU-compatible getopt lives. * monkeysphere-server diagnostics now counts problems and suggests a re-run after they have been resolved. * completed basic test suite: this can be run from the git sources or the tarball with: cd tests && ./basic [ Jameson Graef Rollins ] * Genericize fs location variables. * break out gpg.conf files into SYSCONFIGDIR, and not auto-generated at install. -- Daniel Kahn Gillmor Sun, 26 Oct 2008 03:06:18 -0400 monkeysphere (0.15) experimental; urgency=low * porting work and packaging simplification: clarifying makefiles, pruning dependencies, etc. * added tests to monkeysphere-server diagnostics * moved monkeysphere(5) to section 7 of the manual * now shipping TODO in /usr/share/doc/monkeysphere -- Daniel Kahn Gillmor Thu, 04 Sep 2008 19:08:40 -0400 monkeysphere (0.14) experimental; urgency=low * changing debian packaging back to format 1.0 so we get automatic tarballs, and easier inclusion in other build networks. * no other source changes. -- Daniel Kahn Gillmor Thu, 04 Sep 2008 13:03:35 -0400 monkeysphere (0.13) experimental; urgency=low [ Daniel Kahn Gillmor ] * tweaks in /usr/bin/monkeysphere to handle odd secret keyrings. * updated makefile to reflect the package building technique we've been using for a month now. [ Jameson Graef Rollins ] * move location of user config directory to ~/.monkeysphere. -- Daniel Kahn Gillmor Wed, 03 Sep 2008 17:26:10 -0400 monkeysphere (0.12) experimental; urgency=low [ Jameson Graef Rollins ] * Improved output handling. New LOG_LEVEL variable. [ Daniel Kahn Gillmor ] * debian/control: switched Homepage: and Vcs-Git: to canonicalized upstream hostnames. * updated documentation for new release. * changed my associated e-mail address for this package. -- Daniel Kahn Gillmor Tue, 02 Sep 2008 18:54:29 -0400 monkeysphere (0.11) experimental; urgency=low [ Jameson Graef Rollins ] * fix bug in trustdb update on add/revoke-hostname. [ Daniel Kahn Gillmor ] * debian/control: added Build-Depends: git-core for the new packaging format * new subcommand: monkeysphere subkey-to-ssh-agent (relies on a patched GnuTLS to deal with GPG's gnu-dummy S2K extension, but fails cleanly if not found). -- Daniel Kahn Gillmor Wed, 20 Aug 2008 11:24:35 -0400 monkeysphere (0.10) experimental; urgency=low [ Jameson Graef Rollins ] * brown paper bag release: invert test on calculated validity of keys. -- Daniel Kahn Gillmor Mon, 18 Aug 2008 16:22:34 -0400 monkeysphere (0.9) experimental; urgency=low [ Daniel Kahn Gillmor ] * implemented "monkeysphere-server extend-key" to adjust expiration date of host key. * removed "monkeysphere-server fingerprint". Use "monkeysphere-server show-key" instead. [ Jameson Graef Rollins ] * fixed bug in user id processing that prevented bad primary keys from being properly removed. -- Daniel Kahn Gillmor Mon, 18 Aug 2008 15:42:12 -0400 monkeysphere (0.8) experimental; urgency=low [ Daniel Kahn Gillmor ] * debian/control: switched Vcs-Git to use "centralized" git repo instead of my own. * More monkeysphere-server diagnostics * monkeysphere --gen-subkey now guesses what KeyID you meant. * added Recommends: ssh-askpass to ensure monkeysphere --gen-subkey works sensibly under X11 [ Jameson Graef Rollins ] * fix another bug when known_hosts files are missing. * sort processed keys so that "good" keys are processed after "bad" keys. This will prevent malicious bad keys from causing good keys to be removed from key files. * enabled host key publication. * added checking of gpg.conf for keyserver * new functions to add/revoke host key user IDs * improved list-certifiers function (now non-privileged) -- Daniel Kahn Gillmor Mon, 18 Aug 2008 12:43:37 -0400 monkeysphere (0.7) experimental; urgency=low [ Daniel Kahn Gillmor ] * Added monkeysphere-server diagnostics subcommand. * rebuilding package using Format: 3.0 (git) [ Jameson Graef Rollins ] * fix how check for file modification is done. * rework out user id processing is done to provide more verbose log output. * fix bug in monkeysphpere update-authorized_keys subcommand where disallowed keys failed to be remove from authorized_keys file. -- Daniel Kahn Gillmor Mon, 04 Aug 2008 10:47:41 -0400 monkeysphere (0.6) experimental; urgency=low [ Jameson Graef Rollins ] * Fix bug in return on error of ssh-proxycommand. [ Daniel Kahn Gillmor ] * try socat if netcat is not available in proxycommand. -- Daniel Kahn Gillmor Tue, 29 Jul 2008 10:27:20 -0400 monkeysphere (0.5) experimental; urgency=low [ Daniel Kahn Gillmor ] * updated READMEs to match current state of code [ Jameson Graef Rollins ] * Tweak how empty authorized_user_ids and known_hosts files are handled. * Do not fail when authorized_user_ids or known_hosts file is not found. -- Daniel Kahn Gillmor Mon, 28 Jul 2008 10:50:02 -0400 monkeysphere (0.4) experimental; urgency=low [ Daniel Kahn Gillmor ] * New version. * Fixed return code error in openpgp2ssh [ Jameson Graef Rollins ] * Privilege separation: use monkeysphere user to handle maintenance of the gnupg authentication keychain for server. * Improved certifier key management. * Fixed variable scoping and config file precedence. * Add options for key generation and add-certifier functions. * Fix return codes for known_host and authorized_keys updating functions. * Add write permission check on authorized_keys, known_hosts, and authorized_user_ids files. -- Daniel Kahn Gillmor Tue, 22 Jul 2008 21:50:17 -0400 monkeysphere (0.3) experimental; urgency=low [ Daniel Kahn Gillmor ] * new version. [ Jameson Graef Rollins ] * Move files in /var/cache/monkeysphere and GNUPGHOME for server to the more appropriate /var/lib/monkeysphere. -- Daniel Kahn Gillmor Tue, 24 Jun 2008 00:55:29 -0400 monkeysphere (0.2) experimental; urgency=low * added lockfile-progs dependency -- Daniel Kahn Gillmor Mon, 23 Jun 2008 19:34:05 -0400 monkeysphere (0.2) experimental; urgency=low [ Daniel Kahn Gillmor ] * openpgp2ssh now supports specifying keys by full fingerprint. [ Jameson Graef Rollins ] * Add AUTHORIZED_USER_IDS config variable for server, which defaults to %h/.config/monkeysphere/authorized_user_ids, instead of /etc/monkeysphere/authorized_user_ids. * Remove {update,remove}-userids functions, since we decided they weren't useful enough to be worth maintaining. * Better handling of unknown users in server update-users * Add file locking when modifying known_hosts or authorized_keys * Better failure/prompting for gen-subkey * Add ability to set any owner trust level for keys in server keychain. -- Daniel Kahn Gillmor Mon, 23 Jun 2008 17:03:19 -0400 monkeysphere (0.1) experimental; urgency=low * First release of debian package for monkeysphere. * This is experimental -- please report bugs! -- Daniel Kahn Gillmor Thu, 19 Jun 2008 00:34:53 -0400 monkeysphere-0.43/Makefile000077500000000000000000000124651342216672300156320ustar00rootroot00000000000000#!/usr/bin/make -f # Makefile for monkeysphere # © 2008-2019 Daniel Kahn Gillmor # Licensed under GPL v3 or later MONKEYSPHERE_VERSION = `head -n1 Changelog | sed 's/.*(\([^-]*\)).*/\1/'` # these defaults are for debian. porters should probably adjust them # before calling make install ETCPREFIX ?= ETCSUFFIX ?= PREFIX ?= /usr MANPREFIX ?= $(PREFIX)/share/man LOCALSTATEDIR ?= /var/lib CFLAGS += $(shell libassuan-config --cflags) CFLAGS += $(shell libgcrypt-config --cflags) CFLAGS += --pedantic -Wall -Werror -std=c99 LIBS += $(shell libassuan-config --libs) LIBS += $(shell libgcrypt-config --libs) REPLACEMENTS = src/monkeysphere src/monkeysphere-host \ src/monkeysphere-authentication src/share/defaultenv $(wildcard \ src/transitions/*) REPLACED_COMPRESSED_MANPAGES = $(addsuffix .gz,$(addprefix replaced/,$(wildcard man/*/*))) all: src/agent-transfer/agent-transfer $(addprefix replaced/,$(REPLACEMENTS)) $(REPLACED_COMPRESSED_MANPAGES) src/agent-transfer/agent-transfer: src/agent-transfer/main.c src/agent-transfer/ssh-agent-proto.h $(CC) -o $@ $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $< $(LIBS) debian-package: git buildpackage -uc -us # don't explicitly depend on the tarball, since our tarball # (re)generation is not idempotent even when no source changes. freebsd-distinfo: ./util/build-freebsd-distinfo macports-portfile: ./util/build-macports-portfile clean: rm -f src/agent-transfer/agent-transfer rm -rf replaced/ # clean up old monkeysphere packages lying around as well. rm -f monkeysphere_* replaced/%: % mkdir -p $(dir $@) sed < $< > $@ \ -e 's:__SYSSHAREDIR_PREFIX__:$(PREFIX):' \ -e 's:__SYSCONFDIR_PREFIX__:$(ETCPREFIX):' \ -e 's:__SYSDATADIR_PREFIX__:$(LOCALSTATEDIR):' replaced/%.gz: replaced/% gzip -f -n $< # this target is to be called from the tarball, not from the git # working dir! install: all installman mkdir -p $(DESTDIR)$(PREFIX)/bin $(DESTDIR)$(PREFIX)/sbin mkdir -p $(DESTDIR)$(PREFIX)/share/monkeysphere/m $(DESTDIR)$(PREFIX)/share/monkeysphere/mh $(DESTDIR)$(PREFIX)/share/monkeysphere/ma $(DESTDIR)$(PREFIX)/share/monkeysphere/transitions mkdir -p $(DESTDIR)$(ETCPREFIX)/etc/monkeysphere mkdir -p $(DESTDIR)$(PREFIX)/share/doc/monkeysphere printf "Monkeysphere %s\n" $(MONKEYSPHERE_VERSION) > $(DESTDIR)$(PREFIX)/share/monkeysphere/VERSION install replaced/src/monkeysphere $(DESTDIR)$(PREFIX)/bin install replaced/src/monkeysphere-host $(DESTDIR)$(PREFIX)/sbin install replaced/src/monkeysphere-authentication $(DESTDIR)$(PREFIX)/sbin install src/monkeysphere-authentication-keys-for-user $(DESTDIR)$(PREFIX)/share/monkeysphere install -m 0644 src/share/common $(DESTDIR)$(PREFIX)/share/monkeysphere install -m 0644 replaced/src/share/defaultenv $(DESTDIR)$(PREFIX)/share/monkeysphere install -m 0755 src/share/checkperms $(DESTDIR)$(PREFIX)/share/monkeysphere install -m 0755 src/share/keytrans $(DESTDIR)$(PREFIX)/share/monkeysphere ln -sf ../share/monkeysphere/keytrans $(DESTDIR)$(PREFIX)/bin/pem2openpgp ln -sf ../share/monkeysphere/keytrans $(DESTDIR)$(PREFIX)/bin/openpgp2ssh ln -sf ../share/monkeysphere/keytrans $(DESTDIR)$(PREFIX)/bin/openpgp2pem ln -sf ../share/monkeysphere/keytrans $(DESTDIR)$(PREFIX)/bin/openpgp2spki install -m 0755 src/agent-transfer/agent-transfer $(DESTDIR)$(PREFIX)/bin install -m 0744 replaced/src/transitions/* $(DESTDIR)$(PREFIX)/share/monkeysphere/transitions install -m 0644 src/transitions/README.txt $(DESTDIR)$(PREFIX)/share/monkeysphere/transitions install -m 0644 src/share/m/* $(DESTDIR)$(PREFIX)/share/monkeysphere/m install -m 0644 src/share/mh/* $(DESTDIR)$(PREFIX)/share/monkeysphere/mh install -m 0644 src/share/ma/* $(DESTDIR)$(PREFIX)/share/monkeysphere/ma install -m 0644 Changelog $(DESTDIR)$(PREFIX)/share/doc/monkeysphere install -d $(DESTDIR)$(PREFIX)/share/doc/monkeysphere/examples install -m 0644 examples/* $(DESTDIR)$(PREFIX)/share/doc/monkeysphere/examples install -m 0644 etc/monkeysphere.conf $(DESTDIR)$(ETCPREFIX)/etc/monkeysphere/monkeysphere.conf$(ETCSUFFIX) install -m 0644 etc/monkeysphere-host.conf $(DESTDIR)$(ETCPREFIX)/etc/monkeysphere/monkeysphere-host.conf$(ETCSUFFIX) install -m 0644 etc/monkeysphere-authentication.conf $(DESTDIR)$(ETCPREFIX)/etc/monkeysphere/monkeysphere-authentication.conf$(ETCSUFFIX) installman: $(REPLACED_COMPRESSED_MANPAGES) mkdir -p $(DESTDIR)$(MANPREFIX)/man1 $(DESTDIR)$(MANPREFIX)/man7 $(DESTDIR)$(MANPREFIX)/man8 install replaced/man/man1/* $(DESTDIR)$(MANPREFIX)/man1 install replaced/man/man7/* $(DESTDIR)$(MANPREFIX)/man7 install replaced/man/man8/* $(DESTDIR)$(MANPREFIX)/man8 ln -sf openpgp2ssh.1.gz $(DESTDIR)$(MANPREFIX)/man1/openpgp2pem.1.gz ln -sf openpgp2ssh.1.gz $(DESTDIR)$(MANPREFIX)/man1/openpgp2spki.1.gz # this target depends on you having the monkeysphere-docs # repo checked out as a peer of your monkeysphere repo. releasenote: ../monkeysphere-docs/utils/build-releasenote test: test-keytrans test-basic test-ed25519 check: test test-basic: src/agent-transfer/agent-transfer MONKEYSPHERE_TEST_NO_EXAMINE=true ./tests/basic test-ed25519: src/agent-transfer/agent-transfer MONKEYSPHERE_TEST_NO_EXAMINE=true MONKEYSPHERE_TEST_USE_ED25519=true ./tests/basic test-keytrans: src/agent-transfer/agent-transfer MONKEYSPHERE_TEST_NO_EXAMINE=true ./tests/keytrans .PHONY: all tarball debian-package freebsd-distinfo clean install installman releasenote test check monkeysphere-0.43/README000066400000000000000000000015761342216672300150500ustar00rootroot00000000000000The Monkeysphere Project ------------------------ The Monkeysphere project's goal is to extend OpenPGP's web of trust to new areas of the Internet to help us securely identify each other while we work online. Specifically, monkeysphere currently offers a framework to leverage the OpenPGP web of trust for OpenSSH authentication. In other words, it allows you to use secure shell as you normally do, but to identify yourself and the servers you administer or connect to with your OpenPGP keys. OpenPGP keys are tracked via GnuPG, and monkeysphere manages the known_hosts and authorized_keys files used by OpenSSH for authentication, checking them for cryptographic validity. Dependencies ------------ Monkeysphere depends on: * GnuPG >= 2.1.11 * Perl * Perl's Crypt::OpenSSL::RSA module * lockfile-progs or procmail's lockfile * Bash * OpenSSH's ssh-keygen utility (ideally >= 6.0) monkeysphere-0.43/etc/000077500000000000000000000000001342216672300147325ustar00rootroot00000000000000monkeysphere-0.43/etc/cron.hourly/000077500000000000000000000000001342216672300172145ustar00rootroot00000000000000monkeysphere-0.43/etc/cron.hourly/monkeysphere000077500000000000000000000010151342216672300216500ustar00rootroot00000000000000#!/bin/sh # monkeysphere-authentication cron.hourly script # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # Micah Anderson # # They are Copyright 2008-2009, and are all released under the GPL, version 3 # or later. # update all keys from the key servers monkeysphere-authentication refresh-keys # update all user authorized_keys files monkeysphere-authentication update-users monkeysphere-0.43/etc/monkeysphere-authentication.conf000066400000000000000000000026331342216672300233330ustar00rootroot00000000000000# Monkeysphere authentication configuration file. # This is an sh-style shell configuration file. Variable names should # be separated from their assignments by a single '=' and no spaces. # Environment variables with the same names as these variables but # prefaced by "MONKEYSPHERE_" will take precedence over the values # specified here. # Log level. Can be SILENT, ERROR, INFO, VERBOSE, DEBUG, in # increasing order of verbosity. #LOG_LEVEL=INFO # OpenPGP keyserver #KEYSERVER=pool.sks-keyservers.net # User who controls the monkeysphere 'sphere' keyring. #MONKEYSPHERE_USER=monkeysphere # Whether or not to query keyservers by default #CHECK_KEYSERVER=true # Path to authorized_user_ids file to process to create # authorized_keys file. '%h' will be replaced by the home directory # of the user, and '%u' will be replaced by the username of the user. # For purely admin-controlled authorized_user_ids, you might put them # in /etc/monkeysphere/authorized_user_ids/%u, for instance. #AUTHORIZED_USER_IDS="%h/.monkeysphere/authorized_user_ids" # Path to a user controlled authorized_keys file to be added to the # monkeysphere-generated authorized_keys file. '%h' will be replaced # by the home directory of the user, and '%u' will by replaced by the # username of the user. Setting this variable to 'none' prevents the # inclusion of user controlled authorized_keys file. #RAW_AUTHORIZED_KEYS="%h/.ssh/authorized_keys" monkeysphere-0.43/etc/monkeysphere-host.conf000066400000000000000000000007641342216672300212740ustar00rootroot00000000000000# Monkeysphere host configuration file. # This is an sh-style shell configuration file. Variable names should # be separated from their assignments by a single '=' and no spaces. # Environment variables with the same names as these variables but # prefaced by "MONKEYSPHERE_" will take precedence over the values # specified here. # Log level. Can be SILENT, ERROR, INFO, VERBOSE, DEBUG, in # increasing order of verbosity. #LOG_LEVEL=INFO # OpenPGP keyserver #KEYSERVER=pool.sks-keyservers.net monkeysphere-0.43/etc/monkeysphere.conf000066400000000000000000000027711342216672300203210ustar00rootroot00000000000000# Monkeysphere system-wide client configuration file. # This is an sh-style shell configuration file. Variable names should # be separated from their assignments by a single '=' and no spaces. # Environment variables with the same names as these variables but # prefaced by "MONKEYSPHERE_" will take precedence over the values # specified here. # Log level. Can be SILENT, ERROR, INFO, VERBOSE, DEBUG, in # increasing order of verbosity. #LOG_LEVEL=INFO # GPG home directory. If not specified either here or in the # MONKEYSPHERE_GNUPGHOME environment variable, then the value of the # GNUPGHOME environment variable will be used. If GNUPGHOME is not # set either, then the default value is listed below. #GNUPGHOME=~/.gnupg # GPG keyserver to search for keys. #KEYSERVER=pool.sks-keyservers.net # Set whether or not to check keyservers at every monkeysphere # interaction, including all ssh connections if you use the # monkeysphere ssh-proxycommand. Leave unset for default behavior # (see KEYSERVER CHECKING in monkeysphere(1)), or set to true or false. # NOTE: setting CHECK_KEYSERVER explicitly to true will leak # information about the timing and frequency of your ssh connections # to the maintainer of the keyserver. #CHECK_KEYSERVER=true # The path to the SSH known_hosts file. #KNOWN_HOSTS=~/.ssh/known_hosts # Whether or not to hash the generated known_hosts lines. # Should be "true" or "false". #HASH_KNOWN_HOSTS=false # The path to the SSH authorized_keys file. #AUTHORIZED_KEYS=~/.ssh/authorized_keys monkeysphere-0.43/examples/000077500000000000000000000000001342216672300157755ustar00rootroot00000000000000monkeysphere-0.43/examples/crontab000066400000000000000000000003131342216672300173450ustar00rootroot00000000000000# example Monkeysphere cron job: # Hourly: update the per-user authorized_keys in /var based on # ~/.monkeysphere/authorized_user_ids 36 * * * * root /usr/sbin/monkeysphere-authentication update-users monkeysphere-0.43/examples/make-x509-certreqs000066400000000000000000000057431342216672300211770ustar00rootroot00000000000000#!/bin/bash # Author: Daniel Kahn Gillmor # Date: 2010-12-20 20:54:55-0500 # On a system with keys for https (or some other X.509-using protocol) # already imported into monkeysphere-host, this script generates X.509 # certificate requests for each key, with appropriate subjectAltNames # and the PGPExtension embedded. # The generated requests get dumped to stdout. redirect to a file or # copy/paste if you want to save them/send them someplace. # This script uses bashisms # It currently needs OpenSSL binaries to work properly # It assumes that the monkeysphere-host keyring is in # /var/lib/monkeysphere/host (which it is on debian) # This should probably eventually be incorporated into # monkeysphere-host directly. get_openssl_config() { # first param is seconds since the epoch: X509_PGP_EXTENSION="$(TZ=UTC date -d "@$1" '+%Y%m%d%H%M%SZ')" # next parameter is SAN names, separated by newlines: SUBJECTALTNAME=$(printf "%s" "$2" | sed 's/^/DNS:/' | tr '\n' ',' | \ sed -e 's/,*$//' -e 's/^,*//') printf "sAN: %s\n" "$SUBJECTALTNAME" >&2 cat < # # every time it detects a change in an authorized_keys or authorized_user_ids # file. The update-users command operates on the username that owns the file # that was updated. # # The list of files to monitor is generated from the AUTHORIZED_USER_IDS and # RAW_AUTHORIZED_KEYS variables found in # /etc/monkeysphere/monkeysphere-authentication.conf and expanded using a list # of users on the system. # # Additionally, the /var/lib/monkeysphere/user-update/lastchange file is # monitored. If a change is made to that file, the list of files to monitor is # re-generated based on a fresh listing of users. If you run a hook on user # creation and deletion that generates a file in this directory, you can ensure # that the list of files to monitor is always up-to-date. # # On debian system you can install required perl modules with: aptitude install # libfile-changenotify-perl libfile-spec-perl libconfig-general-perl # # This script is designed to run at system start and should be run with root # privileges. # # File::ChangeNotify is cross platform - it will choose a sub class for # monitoring file system changes appropriate to your operating system (if you # are running Linux, liblinux-inotify2-perl is recommended). # FIXME: does this handle revocations and re-keying? if a sysadmin # switches over to this arrangement, how will the system check for # revocations? Scheduling a simple gpg --refresh should handle # revocations. I'm not sure how to best handle re-keyings. use strict; use warnings; use File::ChangeNotify; use File::Basename; use File::Spec; use Config::General; my $user_update_file = '/var/lib/monkeysphere/user-update/lastchange'; my %watch_files; my $debug = 0; if (defined($ENV{MONKEYSPHERE_LOG_LEVEL}) && $ENV{MONKEYSPHERE_LOG_LEVEL} =~ /^debug/i) { $debug = 1; } sub debug { printf STDERR @_ if ($debug eq 1); } sub set_watch_files() { my %key_file_locations = get_key_file_locations(); # get list of users on the system while(my ($name, $passwd, $uid, $gid, $gcos, $dir, $shell, $home) = getpwent()) { while (my ($key, $file) = each (%key_file_locations)) { $file =~ s/%h/$home/; $file =~ s/%u/$name/; $watch_files{ $file } = $name; } } endpwent(); $watch_files{ $user_update_file } = ''; } sub get_key_file_locations { # set defaults my %key_file_locations; $key_file_locations{ 'authorized_user_ids' } = '%h/.monkeysphere/authorized_user_ids'; $key_file_locations{ 'authorized_keys' } = '%h/.ssh/authorized_keys'; # check monkeysphere-authentication configuration my $config_file = '/etc/monkeysphere/monkeysphere-authentication.conf'; if (-f $config_file) { if (-r $config_file) { my %config; %config = Config::General::ParseConfig($config_file); if (exists $config{'AUTHORIZED_USER_IDS'}) { $key_file_locations{'authorized_user_ids'} = $config{'AUTHORIZED_USER_IDS'}; } if (exists $config{'RAW_AUTHORIZED_KEYS'}) { $key_file_locations{'authorized_keys'} = $config{'RAW_AUTHORIZED_KEYS'}; } } } return %key_file_locations; } sub get_watcher { my @filters; my @dirs; set_watch_files(); for my $file (%watch_files) { my $dir = dirname($file); if ( -d $dir && !grep $_ eq $dir, @dirs ) { debug("Watching dir: %s\n", $dir); push(@dirs,$dir); my $file = basename($file); if ( !grep $_ eq $file, @filters ) { $file = quotemeta($file); debug("Adding file filter: %s\n", $file); push(@filters,$file); } } } # create combined file filters to limit our monitor my $filter = '^(' . join("|",@filters) . ')$'; # return a watcher object return my $watcher = File::ChangeNotify->instantiate_watcher ( directories => [ @dirs ], filter => qr/$filter/, ); } sub watch { my $watcher = get_watcher(); while ( my @events = $watcher->wait_for_events() ) { my %users; for my $event (@events) { if($event->path eq "$user_update_file") { debug("Reloading user list\n"); $watcher = get_watcher(); } else { # if user deleted, file might not exist # FIXME - m-a u returns an error if the username # doesn't exist. It should silently ensure that # the generated authorized_keys file is deleted. # Once it's fixed, we should execute even if the # file is gone. if( -f $event->path) { my $username = $watch_files { $event->path }; $users{ $username } = 1; } } } for ((my $username) = each(%users)) { debug("Updating user: %s\n", $username); # FIXME: this call blocks until m-a u finishes running, i think. # what happens if other changes occur in the meantime? Can we # rate-limit this? Could we instead spawn child processes that # run this command directly? system('monkeysphere-authentication', 'update-users', $username); } } } watch(); monkeysphere-0.43/examples/ssh_config000066400000000000000000000002601342216672300200400ustar00rootroot00000000000000# Monkeysphere ssh config stanza (for ~/.ssh/config or /etc/ssh_config) # This checks for host keys in the OpenPGP WoT: Host * ProxyCommand monkeysphere ssh-proxycommand %h %p monkeysphere-0.43/examples/sshd_config000066400000000000000000000003751342216672300202130ustar00rootroot00000000000000# Monkeysphere sshd config (for use in /etc/sshd_config) # This checks for user keys in the OpenPGP WoT: AuthorizedKeysFile /var/lib/monkeysphere/authorized_keys/%u # be sure to also add a scheduled job to update these keys # (see the example cronjob) monkeysphere-0.43/man/000077500000000000000000000000001342216672300147325ustar00rootroot00000000000000monkeysphere-0.43/man/man1/000077500000000000000000000000001342216672300155665ustar00rootroot00000000000000monkeysphere-0.43/man/man1/agent-transfer.1000066400000000000000000000043151342216672300205730ustar00rootroot00000000000000.TH AGENT-TRANSFER "1" "Jan 2019" "monkeysphere" "User Commands" .SH NAME agent-transfer - copy a secret key from gpg-agent to ssh-agent .SH SYNOPSIS .B agent-transfer [\fIoptions\fP] \fIKEYGRIP\fP [\fICOMMENT\fP] .SH DESCRIPTION \fBagent-transfer\fP extracts a secret key from a modern version of GnuPG agent and sends it to the running SSH agent. This is useful for people whose keys are managed in the long-term by GnuPG's gpg-agent, but who prefer the semantics of OpenSSH's ssh-agent for regular use. \fBagent-transfer\fP was written as part of the monkeysphere project. The \fBKEYGRIP\fP should be specified as a sequence of 20 hexadecimal characters. If you aren't sure of the keygrip, you can inspect the output of: gpg \-\-with\-keygrip \-\-list\-secret\-keys The \fBCOMMENT\fP is optional, and will be stored alongside the key in ssh-agent. It must not start with a \-, to avoid being mistaken for an option. .SH OPTIONS \fBagent-transfer\fP also accepts options that would normally be passed to \fBssh\-add\fP(1) to constrain the use of the transferred key: .TP \-c Indicates that added identities should be subject to confirmation before being used for authentication. .TP \-t SECONDS Indicates that the key should have a lifetime of SECONDS in the running ssh\-agent. .SH FILES .TP ~/.gnupg/S.gpg\-agent The socket where gpg\-agent is listening. This is the "standard socket" for modern GnuPG. .SH ENVIRONMENT VARIABLES .TP GNUPGHOME The GnuPG home directory, where the standard socket for gpg\-agent lives. If this is not set, it is assumed to be ~/.gnupg. .TP SSH_AUTH_SOCK Specifies the location where the running ssh-agent is present. .P Several other environment variables are also passed in some form to the gpg\-agent to help it figure out how to run a sensible pinentry, including GPG_TTY, TERM, DISPLAY, XAUTHORITY, GTK_IM_MODULE, DBUS_SESSION_BUS_ADDRESS, and LANG. .SH BUGS \fBagent-transfer\fP can only work with RSA and Ed25519 keys. Support for other key types not yet implemented. Patches welcome! .SH AUTHOR Written by: Daniel Kahn Gillmor .SH SEE ALSO .BR monkeysphere (7), .BR ssh (1), .BR ssh\-add (1), .BR ssh\-agent (1), .BR gpg (1) .BR gpg\-agent (1), monkeysphere-0.43/man/man1/monkeysphere.1000066400000000000000000000204571342216672300203710ustar00rootroot00000000000000.TH MONKEYSPHERE "1" "June 2008" "monkeysphere" "User Commands" .SH NAME monkeysphere - Monkeysphere client user interface .SH SYNOPSIS .B monkeysphere \fIsubcommand\fP [\fIargs\fP] .SH DESCRIPTION \fBMonkeysphere\fP is a framework to leverage the OpenPGP web of trust for OpenSSH and TLS key-based authentication. OpenPGP keys are tracked via GnuPG, and added to the authorized_keys and known_hosts files used by OpenSSH for connection authentication. Monkeysphere can also be used by a validation agent to validate TLS connections (e.g. https). \fBmonkeysphere\fP is the Monkeysphere client utility. .SH SUBCOMMANDS \fBmonkeysphere\fP takes various subcommands: .TP .B update\-known_hosts [HOST]... Update the known_hosts file. For each specified host, gpg will be queried for a key associated with the host URI (see HOST IDENTIFICATION in .BR monkeysphere(7)), optionally querying a keyserver. If an acceptable key is found for the host (see KEY ACCEPTABILITY in .BR monkeysphere(7)), the key is added to the user's known_hosts file. If a key is found but is unacceptable for the host, any matching keys are removed from the user's known_hosts file. If no gpg key is found for the host, nothing is done. If no hosts are specified, all hosts listed in the known_hosts file will be processed. This subcommand will exit with a status of 0 if at least one acceptable key was found for a specified host, 1 if no matching keys were found at all, and 2 if matching keys were found but none were acceptable. `k' may be used in place of `update\-known_hosts'. .TP .B update\-authorized_keys Update the authorized_keys file for the user executing the command (see MONKEYSPHERE_AUTHORIZED_KEYS in ENVIRONMENT, below). First all monkeysphere keys are cleared from the authorized_keys file. Then, for each user ID in the user's authorized_user_ids file, gpg will be queried for keys associated with that user ID, optionally querying a keyserver. If an acceptable key is found (see KEY ACCEPTABILITY in .BR monkeysphere (7)), the key is added to the user's authorized_keys file. If a key is found but is unacceptable for the user ID, any matching keys are removed from the user's authorized_keys file. If no gpg key is found for the user ID, nothing is done. This subcommand will exit with a status of 0 if at least one acceptable key was found for a user ID, 1 if no matching keys were found at all, and 2 if matching keys were found but none were acceptable. `a' may be used in place of `update\-authorized_keys'. .TP .B gen\-subkey [KEYID] Generate an authentication subkey for a private key in your GnuPG keyring. KEYID is the key ID for the primary key for which the subkey with "authentication" capability will be generated. If no key ID is specified, but only one key exists in the secret keyring, that key will be used. The length of the generated key can be specified with the `\-\-length' or `\-l' option. `g' may be used in place of `gen\-subkey'. .TP .B ssh\-proxycommand [--no-connect] HOST [PORT] An ssh ProxyCommand that can be used to trigger a monkeysphere update of the ssh known_hosts file for a host that is being connected to with ssh. This works by updating the known_hosts file for the host first, before an attempted connection to the host is made. Once the known_hosts file has been updated, a TCP connection to the host is made by exec'ing netcat(1). Regular ssh communication is then done over this netcat TCP connection (see ProxyCommand in ssh_config(5) for more info). This command is meant to be run as the ssh "ProxyCommand". This can either be done by specifying the proxy command on the command line: .B ssh \-o ProxyCommand="monkeysphere ssh\-proxycommand %h %p" ... or by adding the following line to your ~/.ssh/config script: .B ProxyCommand monkeysphere ssh\-proxycommand %h %p The script can easily be incorporated into other ProxyCommand scripts by calling it with the "\-\-no\-connect" option, i.e.: .B monkeysphere ssh\-proxycommand \-\-no\-connect "$HOST" "$PORT" This will run everything except the final exec of netcat to make the TCP connection to the host. In this way this command can be added to another proxy command that does other stuff, and then makes the connection to the host itself. For example, in ~/.ssh/config: .B ProxyCommand sh -c 'monkeysphere ssh-proxycommand --no-connect "%h" "%p"; ssh -W "%h:%p" jumphost.example.net' KEYSERVER CHECKING: The proxy command has a fairly nuanced policy for when keyservers are queried when processing a host. If the host userID is not found in either the user's keyring or in the known_hosts file, then the keyserver is queried for the host userID. If the host userID is found in the user's keyring, then the keyserver is not checked. This assumes that the keyring is kept up-to-date, in a cronjob or the like, so that revocations are properly handled. If the host userID is not found in the user's keyring, but the host is listed in the known_hosts file, then the keyserver is not checked. This last policy might change in the future, possibly by adding a deferred check, so that hosts that go from non-monkeysphere-enabled to monkeysphere-enabled will be properly checked. Setting the CHECK_KEYSERVER variable in the config file or the MONKEYSPHERE_CHECK_KEYSERVER environment variable to either `true' or `false' will override the keyserver-checking policy defined above and either always or never check the keyserver for host key updates. .TP .B subkey\-to\-ssh\-agent [ssh\-add arguments] Push all authentication-capable subkeys in your GnuPG secret keyring into your running ssh-agent. Additional arguments are passed through to .BR ssh\-add (1). For example, to remove the authentication subkeys, pass an additional `\-d' argument. To require confirmation on each use of the key, pass `\-c'. The MONKEYSPHERE_SUBKEYS_FOR_AGENT environment can be used to specify the full fingerprints of specific keys to add to the agent (space separated), instead of adding them all. `s' may be used in place of `subkey\-to\-ssh\-agent'. .TP .B keys\-for\-userid USERID Output to stdout all acceptable keys for a given user ID. `u' may be used in place of `keys\-for\-userid'. .TP .B sshfprs\-for\-userid USERID Output the ssh fingerprints of acceptable keys for a given user ID. .TP .B version Show the monkeysphere version number. `v' may be used in place of `version'. .TP .B help Output a brief usage summary. `h' or `?' may be used in place of `help'. .SH ENVIRONMENT The following environment variables will override those specified in the monkeysphere.conf configuration file (defaults in parentheses): .TP MONKEYSPHERE_LOG_LEVEL Set the log level. Can be SILENT, ERROR, INFO, VERBOSE, DEBUG, in increasing order of verbosity. (INFO) .TP MONKEYSPHERE_GNUPGHOME, GNUPGHOME GnuPG home directory. (~/.gnupg) .TP MONKEYSPHERE_KEYSERVER OpenPGP keyserver to use. (pool.sks-keyservers.net) .TP MONKEYSPHERE_CHECK_KEYSERVER Whether or not to check keyserver when making gpg queries. (true) .TP MONKEYSPHERE_KNOWN_HOSTS Path to ssh known_hosts file. (~/.ssh/known_hosts) .TP MONKEYSPHERE_HASH_KNOWN_HOSTS Whether or not to hash to the known_hosts file entries. (false) .TP MONKEYSPHERE_AUTHORIZED_KEYS Path to ssh authorized_keys file. (~/.ssh/authorized_keys) .TP MONKEYSPHERE_PROMPT If set to `false', never prompt the user for confirmation. (true) .TP MONKEYSPHERE_STRICT_MODES If set to `false', ignore too-loose permissions on known_hosts, authorized_keys, and authorized_user_ids files. NOTE: setting this to false may expose you to abuse by other users on the system. (true) .TP MONKEYSPHERE_SUBKEYS_FOR_AGENT A space-separated list of authentication-capable subkeys to add to the ssh agent with subkey-to-ssh-agent. .SH FILES .TP ~/.monkeysphere/monkeysphere.conf User monkeysphere config file. .TP __SYSCONFDIR_PREFIX__/etc/monkeysphere/monkeysphere.conf System-wide monkeysphere config file. .TP ~/.monkeysphere/authorized_user_ids A list of OpenPGP user IDs, one per line. OpenPGP keys with an exactly-matching User ID (calculated valid by the designated identity certifiers), will have any valid authorization-capable keys or subkeys added to the given user's authorized_keys file. .SH AUTHOR Written by: Jameson Rollins , Daniel Kahn Gillmor .SH SEE ALSO .BR monkeysphere\-host (8), .BR monkeysphere\-authentication (8), .BR monkeysphere (7), .BR ssh (1), .BR ssh\-add (1), .BR gpg (1) monkeysphere-0.43/man/man1/openpgp2ssh.1000066400000000000000000000062661342216672300201320ustar00rootroot00000000000000.\" -*- nroff -*- .Dd $Mdocdate: January 18, 2013 $ .Dt OPENPGP2SSH 1 .Os .Sh NAME openpgp2ssh .Nd translate OpenPGP keys to SSH keys .Sh SYNOPSIS .Nm openpgp2ssh < mykey.gpg .Pp .Nm gpg \-\-export $KEYID | openpgp2ssh $KEYID .Pp .Nm gpg \-\-export $KEYID | openpgp2pem $KEYID .Pp .Nm gpg \-\-export $KEYID | openpgp2spki $KEYID .Pp .Nm gpg \-\-export\-secret\-key $KEYID | openpgp2ssh $KEYID .Sh DESCRIPTION .Nm takes an OpenPGP-formatted primary key and associated subkeys on standard input, and spits out the requested equivalent SSH-style (or PEM-encoded) key on standard output. .Pp If the data on standard input contains no subkeys, you can invoke .Nm without arguments. If the data on standard input contains multiple keys (e.g. a primary key and associated subkeys), you must specify a specific OpenPGP key identifier as the first argument to indicate which key to export. The key ID is normally the 40 hex digit OpenPGP fingerprint of the key or subkey desired, but .Nm will accept as few as the last 8 digits of the fingerprint as a key ID. .Pp If the input contains an OpenPGP RSA public key, it will be converted to the OpenSSH-style single-line keystring, prefixed with the key type (`ssh\-rsa'). This format is suitable (with minor alterations) for insertion into known_hosts files and authorized_keys files. If invoked as `openpgp2pem', a PEM-encoded public key will be emitted instead. .Pp If invoked as `openpgp2spki', a PEM-encoded subjectPublicKeyInfo (as defined in the X.509 standard) will be emitted instead. .Pp If the input contains an OpenPGP RSA secret key, it will be converted to the equivalent PEM-encoded private key. .Pp .Nm is part of the .Xr monkeysphere 7 framework for providing a PKI for SSH. .Sh CAVEATS The keys produced by this process are stripped of all identifying information, including certifications, self-signatures, etc. This is intentional, since ssh attaches no inherent significance to these features. .Pp .Nm will produce output for any requested RSA key. This means, among other things, that it will happily export revoked keys, unverifiable keys, expired keys, etc. Make sure you do your own key validation before using this tool! .Sh EXAMPLES .Nm gpg \-\-export\-secret\-key $KEYID | openpgp2ssh $KEYID | ssh\-add \-c /dev/stdin .Pp This pushes the secret key into the active .Xr ssh\-agent 1 . Tools such as .Xr ssh 1 which know how to talk to the .Xr ssh\-agent 1 can now rely on the key. .Sh AUTHOR .Nm and this man page were written by Daniel Kahn Gillmor . .Sh BUGS .Nm only works with RSA keys. DSA keys are the only other key type available in both OpenPGP and SSH, but they are currently unsupported by this utility. .Pp .Nm only accepts raw OpenPGP packets on standard input. It does not accept ASCII-armored input. .Nm Currently only exports into formats used by the OpenSSH. It should support other key output formats, such as those used by .Xr lsh 1 and .Xr putty 1 . .Pp Secret key output is currently not passphrase-protected. .Pp .Nm currently cannot handle passphrase-protected secret keys on input. .Sh SEE ALSO .Xr pem2openpgp 1 , .Xr monkeysphere 1 , .Xr monkeysphere 7 , .Xr ssh 1 , .Xr monkeysphere-authentication 8 , .Xr monkeysphere-host 8 monkeysphere-0.43/man/man1/pem2openpgp.1000066400000000000000000000062161342216672300201110ustar00rootroot00000000000000.\" -*- nroff -*- .Dd $Mdocdate: Jan 22, 2019 $ .Dt PEM2OPENPGP 1 .Os .Sh NAME pem2openpgp .Nd translate PEM-encoded RSA keys to OpenPGP certificates .Sh SYNOPSIS .Nm pem2openpgp "$USERID" < mykey.pem | gpg \-\-import .Pp .Nm PEM2OPENPGP_EXPIRATION=$((86400 * $DAYS)) PEM2OPENPGP_USAGE_FLAGS=authenticate,certify pem2openpgp "$USERID" . .Sh BUGS Only handles RSA keys at the moment. It might be nice to handle DSA keys as well. .Pp Currently only creates certificates with a single User ID. Should be able to create certificates with multiple User IDs. .Pp Currently only accepts unencrypted RSA keys. It should be able to deal with passphrase-locked key material. .Pp Currently outputs OpenPGP certificates with cleartext secret key material. It would be good to be able to lock the output with a passphrase. .Pp If you find other bugs, please report them at https://labs.riseup.net/code/projects/show/monkeysphere .Sh SEE ALSO .Xr openpgp2ssh 1 , .Xr monkeysphere 1 , .Xr monkeysphere 7 , .Xr ssh 1 , .Xr monkeysphere\-host 8 , .Xr monkeysphere\-authentication 8 monkeysphere-0.43/man/man7/000077500000000000000000000000001342216672300155745ustar00rootroot00000000000000monkeysphere-0.43/man/man7/monkeysphere.7000066400000000000000000000063131342216672300204000ustar00rootroot00000000000000.TH MONKEYSPHERE "7" "March 2010" "monkeysphere" "System Frameworks" .SH NAME monkeysphere - ssh and TLS authentication framework using OpenPGP Web of Trust .SH DESCRIPTION \fBMonkeysphere\fP is a framework to leverage the OpenPGP web of trust for OpenSSH and TLS key-based authentication. OpenPGP keys are tracked via GnuPG, and added to the authorized_keys and known_hosts files used by OpenSSH for connection authentication. Monkeysphere can also be used by a validation agent to validate TLS connections (e.g. https). .SH IDENTITY CERTIFIERS Each host that uses the \fBMonkeysphere\fP to authenticate its remote users needs some way to determine that those users are who they claim to be. SSH permits key-based authentication, but we want instead to bind authenticators to human-comprehensible user identities. This switch from raw keys to User IDs makes it possible for administrators to see intuitively who has access to an account, and it also enables end users to transition keys (and revoke compromised ones) automatically across all \fBMonkeysphere\fP-enabled hosts. The User IDs and certifications that the \fBMonkeysphere\fP relies on are found in the OpenPGP Web of Trust. However, in order to establish this binding, each host must know whose cerifications to trust. Someone who a host trusts to certify User Identities is called an Identity Certifier. A host must have at least one Identity Certifier in order to bind User IDs to keys. Commonly, every ID Certifier would be trusted by the host to fully identify any User ID, but more nuanced approaches are possible as well. For example, a given host could specify a dozen ID certifiers, but assign them all "marginal" trust. Then any given User ID would need to be certified in the OpenPGP Web of Trust by at least three of those certifiers. It is also possible to limit the scope of trust for a given ID Certifier to a particular domain. That is, a host can be configured to fully (or marginally) trust a particular ID Certifier only when they certify identities within, say, example.org (based on the e-mail address in the User ID). .SH KEY ACCEPTABILITY The monkeysphere commands work from a set of user IDs to determine acceptable keys for ssh and TLS authentication. OpenPGP keys are considered acceptable if the following criteria are met: .TP .B capability The key must have the `authentication' (`a') usage flag set. .TP .B validity The key itself must be valid, i.e. it must be well-formed, not expired, and not revoked. .TP .B certification The relevant user ID must be signed by a trusted identity certifier. .SH HOST IDENTIFICATION The OpenPGP keys for hosts have associated `service names` (OpenPGP user IDs) that are based on URI specifications for the service. Some examples: .TP .B ssh: ssh://host.example.com[:port] .TP .B https: https://host.example.com[:port] .SH AUTHOR Written by: Jameson Rollins , Daniel Kahn Gillmor .SH SEE ALSO .BR monkeysphere (1), .BR monkeysphere\-host (8), .BR monkeysphere\-authentication (8), .BR openpgp2ssh (1), .BR pem2openpgp (1), .BR gpg (1), .BR https://tools.ietf.org/html/rfc4880, .BR ssh (1), .BR https://tools.ietf.org/wg/secsh/draft\-ietf\-secsh\-scp\-sftp\-ssh\-uri/ monkeysphere-0.43/man/man8/000077500000000000000000000000001342216672300155755ustar00rootroot00000000000000monkeysphere-0.43/man/man8/monkeysphere-authentication.8000066400000000000000000000212471342216672300234220ustar00rootroot00000000000000.TH MONKEYSPHERE-AUTHENTICATION "8" "March 13, 2013" "monkeysphere" "System Commands" .SH NAME monkeysphere\-authentication - Monkeysphere authentication admin tool. .SH SYNOPSIS .B monkeysphere\-authentication \fIsubcommand\fP [\fIargs\fP] .SH DESCRIPTION \fBMonkeysphere\fP is a framework to leverage the OpenPGP Web of Trust (WoT) for key-based authentication. OpenPGP keys are tracked via GnuPG, and added to the authorized_keys files used by OpenSSH for connection authentication. \fBmonkeysphere\-authentication\fP is a Monkeysphere server admin utility for configuring and managing SSH user authentication through the WoT. .SH SUBCOMMANDS \fBmonkeysphere\-authentication\fP takes various subcommands: .TP .B update\-users [USER]... Rebuild the monkeysphere-controlled authorized_keys files. For each specified account, the user ID's listed in the account's authorized_user_ids file are processed. For each user ID, gpg will be queried for keys associated with that user ID, optionally querying a keyserver. If an acceptable key is found (see KEY ACCEPTABILITY in monkeysphere(7)), the key is added to the account's monkeysphere-controlled authorized_keys file. If the RAW_AUTHORIZED_KEYS variable is set, then a separate authorized_keys file (usually ~USER/.ssh/authorized_keys) is appended to the monkeysphere-controlled authorized_keys file. If no accounts are specified, then all accounts on the system are processed. `u' may be used in place of `update\-users'. .TP .B keys\-for\-user USER Output to stdout authorized_keys lines for USER. This command behaves exactly like update\-users (above), except that the resulting authorized_keys lines are output to stdout, instead of being written to the monkeysphere-controlled authorized_keys file. .TP .B refresh\-keys Refresh all keys in the monkeysphere-authentication keyring. If no accounts are specified, then all accounts on the system are processed. `r' may be used in place of `refresh\-keys'. .TP .B add\-id\-certifier KEYID|FILE Instruct system to trust user identity certifications made by KEYID. The key ID will be loaded from the keyserver. A file may be loaded instead of pulling the key from the keyserver by specifying the path to the file as the argument, or by specifying `\-' to load from stdin. Using the `\-n' or `\-\-domain' option allows you to indicate that you only trust the given KEYID to make identifications within a specific domain (e.g. "trust KEYID to certify user identities within the @example.org domain"). A certifier trust level can be specified with the `\-t' or `\-\-trust' option (possible values are `marginal' and `full' (default is `full')). A certifier trust depth can be specified with the `\-d' or `\-\-depth' option (default is 1). `c+' may be used in place of `add\-id\-certifier'. .TP .B remove\-id\-certifier KEYID Instruct system to ignore user identity certifications made by KEYID. `c\-' may be used in place of `remove\-id\-certifier'. .TP .B list\-id\-certifiers List key IDs trusted by the system to certify user identities. `c' may be used in place of `list\-id\-certifiers'. .TP .B version Show the monkeysphere version number. `v' may be used in place of `version'. .TP .B help Output a brief usage summary. `h' or `?' may be used in place of `help'. Other commands: .TP .B setup Setup the server in preparation for Monkeysphere user authentication. This command is idempotent and run automatically by the other commands, and should therefore not usually need to be run manually. `s' may be used in place of `setup'. .TP .B diagnostics Review the state of the server with respect to authentication. `d' may be used in place of `diagnostics'. .TP .B gpg\-cmd Execute a gpg command, as the monkeysphere user, on the monkeysphere authentication `sphere' keyring. As of monkeysphere 0.36, this takes its arguments separately, not as a single string. Use this command with caution, as modifying the authentication sphere keyring can affect ssh user authentication. .SH SETUP USER AUTHENTICATION If the server will handle user authentication through monkeysphere-generated authorized_keys files, the server must be told which keys will act as identity certifiers. This is done with the \fBadd\-id\-certifier\fP command: # monkeysphere\-authentication add\-id\-certifier KEYID where KEYID is the key ID of the server admin, or whoever's certifications should be acceptable to the system for the purposes of authenticating remote users. You can run this command multiple times to indicate that multiple certifiers are trusted. You may also specify a filename instead of a key ID, as long as the file contains a single OpenPGP public key. Certifiers can be removed with the \fBremove\-id\-certifier\fP command, and listed with the \fBlist\-id\-certifiers\fP command. A remote user will be granted access to a local account based on the appropriately-signed and valid keys associated with user IDs listed in that account's authorized_user_ids file. By default, the authorized_user_ids file for an account is ~/.monkeysphere/authorized_user_ids. This can be changed in the monkeysphere\-authentication.conf file. The \fBupdate\-users\fP command is used to generate authorized_keys files for a local account based on the user IDs listed in the account's authorized_user_ids file: # monkeysphere\-authentication update\-users USER Not specifying USER will cause all accounts on the system to updated. The ssh server can use these monkeysphere-generated authorized_keys files to grant access to user accounts for remote users. In order for sshd to look at the monkeysphere-generated authorized_keys file for user authentication, the AuthorizedKeysFile parameter must be set in the sshd_config to point to the monkeysphere\-generated authorized_keys files: AuthorizedKeysFile __SYSDATADIR_PREFIX__/monkeysphere/authorized_keys/%u It is recommended to add "monkeysphere\-authentication update\-users" to a system crontab, so that user keys are kept up-to-date, and key revocations and expirations can be processed in a timely manner. .SH ENVIRONMENT The following environment variables will override those specified in the config file (defaults in parentheses): .TP MONKEYSPHERE_MONKEYSPHERE_USER User to control authentication keychain. (monkeysphere) .TP MONKEYSPHERE_LOG_LEVEL Set the log level. Can be SILENT, ERROR, INFO, VERBOSE, DEBUG, in increasing order of verbosity. (INFO) .TP MONKEYSPHERE_KEYSERVER OpenPGP keyserver to use. (pool.sks\-keyservers.net) .TP MONKEYSPHERE_CHECK_KEYSERVER Whether or not to check the keyserver when making gpg queries. (true) .TP MONKEYSPHERE_AUTHORIZED_USER_IDS Path to user's authorized_user_ids file. %h gets replaced with the user's homedir, %u with the username. (%h/.monkeysphere/authorized_user_ids) .TP MONKEYSPHERE_RAW_AUTHORIZED_KEYS Path to regular ssh-style authorized_keys file to append to monkeysphere-generated authorized_keys. `none' means not to add any raw authorized_keys file. %h gets replaced with the user's homedir, %u with the username. (%h/.ssh/authorized_keys) .TP MONKEYSPHERE_PROMPT If set to `false', never prompt the user for confirmation. (true) .TP MONKEYSPHERE_STRICT_MODES If set to `false', ignore too-loose permissions on known_hosts, authorized_keys, and authorized_user_ids files. NOTE: setting this to false may expose users to abuse by other users on the system. (true) .SH FILES .TP __SYSCONFDIR_PREFIX__/etc/monkeysphere/monkeysphere\-authentication.conf System monkeysphere-authentication config file. .TP __SYSCONFDIR_PREFIX__/etc/monkeysphere/monkeysphere\-authentication\-x509\-anchors.crt or\p \ __SYSCONFDIR_PREFIX__/etc/monkeysphere/monkeysphere\-x509\-anchors.crt If monkeysphere-authentication is configured to query an hkps keyserver, it will use the PEM-encoded X.509 Certificate Authority certificates in this file to validate any X.509 certificates used by the keyserver. If the monkeysphere-authentication-x509 file is present, the monkeysphere-x509 file will be ignored. .TP __SYSDATADIR_PREFIX__/monkeysphere/authorized_keys/USER Monkeysphere-controlled user authorized_keys files. .TP ~/.monkeysphere/authorized_user_ids A list of OpenPGP user IDs, one per line. OpenPGP keys with an exactly-matching User ID (calculated valid by the designated identity certifiers), will have any valid authorization-capable keys or subkeys added to the given user's authorized_keys file. Any line with initial whitespace will be interpreted as ssh authorized_keys options applicable to the preceding User ID. .SH AUTHOR This man page was written by: Jameson Rollins , Daniel Kahn Gillmor , Matthew Goins .SH SEE ALSO .BR monkeysphere (1), .BR monkeysphere\-host (8), .BR monkeysphere (7), .BR gpg (1), .BR ssh (1), .BR sshd (8), .BR sshd_config (5) monkeysphere-0.43/man/man8/monkeysphere-host.8000066400000000000000000000246351342216672300213640ustar00rootroot00000000000000.TH MONKEYSPHERE-HOST "8" "January 2010" "monkeysphere" "System Commands" .SH NAME monkeysphere\-host \- Monkeysphere host key administration tool. .SH SYNOPSIS .B monkeysphere\-host \fIsubcommand\fP [\fIargs\fP] .SH DESCRIPTION \fBMonkeysphere\fP is a framework to leverage the OpenPGP web of trust for SSH and TLS key\-based authentication. \fBmonkeysphere\-host\fP stores and manages OpenPGP certificates for various services offered by the host. Most subcommands take a KEYID argument, which identifies (by OpenPGP key ID (e.g. 0xDEADBEEF) or full OpenPGP fingerprint) which certificate is to be operated upon. If only one certificate is currently managed by \fBmonkeysphere\-host\fP, the KEYID argument may be omitted, and \fBmonkeysphere\-host\fP will operate on it. .SH SUBCOMMANDS \fBmonkeysphere\-host\fP takes various subcommands: .TP .B import\-key FILE SCHEME://HOSTNAME[:PORT] Import a PEM\-encoded host secret key from file FILE. If FILE is `\-', then the key will be imported from stdin. Only RSA keys are supported at the moment. SCHEME://HOSTNAME[:PORT] is used to specify the scheme (e.g. ssh or https), fully\-qualified hostname (and port) used in the user ID of the new OpenPGP key (e.g. ssh://example.net or https://www.example.net). If PORT is not specified, then no port is added to the user ID, which means the default port for that service (e.g. 22 for ssh) is assumed. `i' may be used in place of `import\-key'. .TP .B show\-keys [KEYID ...] Output information about the OpenPGP certificate(s) for services offered by the host, including their KEYIDs. If no KEYID is specified (or if the special string `\-\-all' is used), output information about all certificates managed by \fBmonkeysphere\-host\fP. `s' may be used in place of `show\-keys'. .TP .B set\-expire EXPIRE [KEYID] Extend the validity of the OpenPGP certificate specified until EXPIRE from the present. Expiration is specified as with GnuPG (measured from today's date): .nf 0 = key does not expire = key expires in n days w = key expires in n weeks m = key expires in n months y = key expires in n years .fi `e' may be used in place of `set\-expire'. .TP .B add\-servicename SCHEME://HOSTNAME[:PORT] [KEYID] Add a service\-specific user ID to the specified certificate. For example, the operator of `https://example.net' may wish to add an additional servicename of `https://www.example.net' to the certificate corresponding to the secret key used by the TLS\-enabled web server. `add\-name' or `n+' may be used in place of `add\-servicename'. .TP .B revoke\-servicename SCHEME://HOSTNAME[:PORT] [KEYID] Revoke a service\-specific user ID from the specified certificate. `revoke\-name' or `n\-' may be used in place of `revoke\-servicename'. .TP .B add\-revoker REVOKER_KEYID|FILE [KEYID] Add a revoker to the specified OpenPGP certificate. The revoker can be specified by their own REVOKER_KEYID (in which case it will be loaded from an OpenPGP keyserver), or by specifying a path to a file containing the revoker's OpenPGP certificate, or by specifying `\-' to load from stdin. `r+' may be be used in place of `add\-revoker'. .TP .B revoke\-key [KEYID] Generate (with the option to publish) a revocation certificate for given OpenPGP certificate. If such a certificate is published, the given key will be permanently revoked, and will no longer be accepted by monkeysphere\-enabled clients. This subcommand will ask you a series of questions, and then generate a key revocation certificate, sending it to stdout. You might want to store these certificates safely offline, to publish in case of compromise). If you explicitly tell it to publish the revocation certificate immediately, it will send it to the public keyservers. PUBLISH THESE CERTIFICATES ONLY IF YOU ARE SURE THE CORRESPONDING KEY WILL NEVER BE RE\-USED! .TP .B publish\-keys [KEYID ...] Publish the specified OpenPGP certificates to the public keyservers. If the special string `\-\-all' is specified, all of the host's OpenPGP certificates will be published. `p' may be used in place of `publish\-keys'. NOTE: that there is no way to remove a key from the public keyservers once it is published! .TP .B version Show the monkeysphere version number. `v' may be used in place of `version'. .TP .B help Output a brief usage summary. `h' or `?' may be used in place of `help'. .TP .B diagnostics Review the state of the monkeysphere server host key and report on suggested changes. Among other checks, this includes making sure there is a valid host key, that the key is not expired, that the sshd configuration points to the right place, etc. `d' may be used in place of `diagnostics'. .SH SETUP SSH SERVER CERTIFICATES To enable users to verify your SSH host's key via the monkeysphere, an OpenPGP certificate must be made out of the host's RSA ssh key, and the certificate must be published to the Web of Trust. Certificate publication is not done by default. The first step is to import the host's ssh key into a monkeysphere\-style OpenPGP certificate. This is done with the import\-key command. For example: # monkeysphere\-host import\-key __SYSCONFDIR_PREFIX__/etc/ssh/ssh_host_rsa_key ssh://host.example.org On most systems, sshd's RSA secret key is stored at __SYSCONFDIR_PREFIX__/etc/ssh/ssh_host_rsa_key. See PUBLISHING AND CERTIFYING MONKEYSPHERE SERVICE CERTIFICATES for how to make sure your users can verify the ssh service offered by your host once the key is imported into \fBmonkeysphere\-host\fP. .SH SETUP WEB SERVER CERTIFICATES You can set up your HTTPS\-capable web server so that your users can verify it via the monkeysphere, without changing your server's software at all. You just need access to a (PEM\-encoded) version of the server's RSA secret key (most secret keys are already stored PEM\-encoded). The first step is to import the web server's key into a monkeysphere\-style OpenPGP certificate. This is done with the import\-key command. For example: # monkeysphere\-host import\-key __SYSCONFDIR_PREFIX__/etc/ssl/private/host.example.net\-key.pem https://host.example.net If you don't know where the web server's key is stored on your machine, consult the configuration files for your web server. Debian\-based systems using the `ssl\-cert' packages often have a default self\-signed certificate stored in `__SYSCONFDIR_PREFIX__/etc/ssl/private/ssl\-cert\-snakeoil.key' ; if you're using that key, your users are getting browser warnings about it. You can keep using the same key, but help them use the OpenPGP WoT to verify that it does belong to your web server by using something like: # monkeysphere\-host import\-key __SYSCONFDIR_PREFIX__/etc/ssl/private/ssl\-cert\-snakeoil.key https://$(hostname \-\-fqdn) If you offer multiple HTTPS websites using the same secret key, you should add the additional website names with the `add\-servicename' subcommand. See PUBLISHING AND CERTIFYING MONKEYSPHERE SERVICE CERTIFICATES (the next section) for how to make sure your users can verify the https service offered by your host once the key is imported and any extra site names have been added. Note that you can add or remove additional servicenames at any time, but you'll need to certify any new ones separately. .SH PUBLISHING AND CERTIFYING MONKEYSPHERE SERVICE CERTIFICATES Once the host key has been imported, the corresponding certificate must be published to the Web of Trust so that users can retrieve the cert when connecting to the host. The host certificates are published to the keyserver with the publish\-key command: $ monkeysphere\-host publish\-key \-\-all In order for users accessing the system to be able to identify the host's service via the monkeysphere, at least one person (e.g. a server admin) will need to sign the host's certificate. This is done using standard OpenPGP keysigning techniques. Usually: pull the host's OpenPGP certificate from the keyserver, verify and sign it, and then re\-publish your signature. More than one person can certify any certificate. Please see https://web.monkeysphere.info/doc/host\-keys/ for more information and details. Once an admin's signature is published, users accessing the host can use the certificate to validate the host's key without having to manually check the host key's fingerprint (in the case of ssh) or without seeing a nasty "security warning" in their browsers (in the case of https). .SH SECURITY CONSIDERATIONS Note that \fBmonkeysphere\-host\fP currently caches a copy of all imported secret keys (stored in OpenPGP form for future manipulation) in __SYSDATADIR_PREFIX__/monkeysphere/host/. Cleartext backups of files in this directory could expose secret key material if not handled sensitively. .SH ENVIRONMENT The following environment variables will override those specified in the config file (defaults in parentheses): .TP MONKEYSPHERE_LOG_LEVEL Set the log level. Can be SILENT, ERROR, INFO, VERBOSE, DEBUG, in increasing order of verbosity. (INFO) .TP MONKEYSPHERE_KEYSERVER OpenPGP keyserver to use. (pool.sks\-keyservers.net) .TP MONKEYSPHERE_PROMPT If set to `false', never prompt the user for confirmation. (true) .SH FILES .TP __SYSCONFDIR_PREFIX__/etc/monkeysphere/monkeysphere\-host.conf System monkeysphere\-host config file. .TP __SYSDATADIR_PREFIX__/monkeysphere/host_keys.pub.pgp A world\-readable copy of the host's OpenPGP certificates in ASCII armored format. This includes the certificates (including the public keys, servicename\-based User IDs, and most recent relevant self\-signatures) corresponding to every key used by Monkeysphere\-enabled services on the host. .TP __SYSDATADIR_PREFIX__/monkeysphere/host/ A locked directory (readable only by the superuser) containing copies of all imported secret keys (this is the host's GNUPGHOME directory). .TP __SYSCONFDIR_PREFIX__/etc/monkeysphere/monkeysphere\-host\-x509\-anchors.crt or\p \ __SYSCONFDIR_PREFIX__/etc/monkeysphere/monkeysphere\-x509\-anchors.crt If monkeysphere-host is configured to query an hkps keyserver for publish-keys, it will use the PEM-encoded X.509 Certificate Authority certificates in this file to validate any X.509 certificates used by the keyserver. If the monkeysphere-host-x509 file is present, the monkeysphere-x509 file will be ignored. .SH AUTHOR This man page was written by: Jameson Rollins , Daniel Kahn Gillmor , Matthew Goins .SH SEE ALSO .BR monkeysphere (1), .BR monkeysphere (7), .BR gpg (1), .BR monkeysphere\-authentication (8), .BR ssh (1), .BR sshd (8) monkeysphere-0.43/packaging/000077500000000000000000000000001342216672300161035ustar00rootroot00000000000000monkeysphere-0.43/packaging/macports/000077500000000000000000000000001342216672300177335ustar00rootroot00000000000000monkeysphere-0.43/packaging/macports/Portfile000066400000000000000000000061241342216672300214450ustar00rootroot00000000000000# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:ft=tcl:et:sw=4:ts=4:sts=4 # $Id$ PortSystem 1.0 name monkeysphere version 0.26 categories net security maintainers nomaintainer description use the OpenPGP web of trust to verify ssh connections long_description SSH key-based authentication is tried-and-true, \ but it lacks a true Public Key Infrastructure for \ key certification, revocation and expiration. \ Monkeysphere is a framework that uses the OpenPGP \ web of trust for these PKI functions. It can be \ used in both directions: for users to get \ validated host keys, and for hosts to authenticate \ users. homepage https://web.monkeysphere.info/ platforms darwin depends_run bin:ssh:openssh \ port:gnupg \ port:p5-crypt-openssl-rsa \ port:p5-digest-sha \ port:procmail master_sites https://archive.monkeysphere.info/debian/pool/monkeysphere/m/monkeysphere/ distname ${name}_${version} extract.suffix .orig.tar.gz worksrcdir ${name}-${version} checksums md5 f0e5fe66a9affd951e601ea5d6188972 use_configure no post-build { # update paths to SYS*DIRs exec sed -i .tmp -e "s|/etc/monkeysphere|${prefix}/etc/monkeysphere|g" \ ${worksrcpath}/src/share/defaultenv \ ${worksrcpath}/src/transitions/0.23 \ ${worksrcpath}/man/man1/monkeysphere.1 \ ${worksrcpath}/man/man8/monkeysphere-authentication.8 \ ${worksrcpath}/man/man8/monkeysphere-host.8 \ ${worksrcpath}/etc/monkeysphere-authentication.conf exec sed -i .tmp -e "s|/var/lib/monkeysphere|${prefix}/var/db/monkeysphere|g" \ ${worksrcpath}/src/transitions/0.23 \ ${worksrcpath}/man/man1/monkeysphere.1 \ ${worksrcpath}/man/man8/monkeysphere-authentication.8 \ ${worksrcpath}/man/man8/monkeysphere-host.8 \ ${worksrcpath}/src/monkeysphere-host \ ${worksrcpath}/src/monkeysphere-authentication \ ${worksrcpath}/doc/getting-started-admin.mdwn exec sed -i .tmp -e "s|/usr/share/monkeysphere|${prefix}/share/monkeysphere|g" \ ${worksrcpath}/src/monkeysphere-host \ ${worksrcpath}/src/monkeysphere-authentication \ ${worksrcpath}/src/monkeysphere # fix perl shebang line to point to macports perl install exec sed -i .tmp -e "s|^#!/usr/bin/perl -T$|#!/opt/local/bin/perl -T|" \ ${worksrcpath}/src/share/keytrans \ ${worksrcpath}/src/share/checkperms # remove leftover sed cruft exec find ${worksrcpath} -name *.tmp -delete } destroot.destdir DESTDIR=${destroot}${prefix} destroot.args PREFIX= # variant to use the port version of bash, which may be much newer # than the one provided by the system variant port-bash description {use port version of Bash} { depends_run-append port:bash } monkeysphere-0.43/packaging/slackware/000077500000000000000000000000001342216672300200575ustar00rootroot00000000000000monkeysphere-0.43/packaging/slackware/README000066400000000000000000000011301342216672300207320ustar00rootroot00000000000000Monkeysphere on Slackware ========================= Silvio Rhatto wrote a SlackBuild script for the monkeysphere, which was found here: https://slack.sarava.org/slackbuilds/net/misc/monkeysphere/ As of January 2019, it appears to be no longer online, but a copy can be found at: https://github.com/pyllyukko/monkeysphere.SlackBuild History ------- This SlackBuild script was generated from a .mkbuild script, published separately: https://slack.sarava.org/mkbuilds/net/misc/monkeysphere/ You can read more about the mkbuild system here: https://simplepkg.sarava.org Thanks, rhatto! monkeysphere-0.43/src/000077500000000000000000000000001342216672300147465ustar00rootroot00000000000000monkeysphere-0.43/src/agent-transfer/000077500000000000000000000000001342216672300176665ustar00rootroot00000000000000monkeysphere-0.43/src/agent-transfer/main.c000066400000000000000000000547651342216672300207770ustar00rootroot00000000000000#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ssh-agent-proto.h" #define KEYGRIP_LENGTH 40 #define KEYWRAP_ALGO GCRY_CIPHER_AES128 #define KEYWRAP_ALGO_MODE GCRY_CIPHER_MODE_AESWRAP int custom_log (assuan_context_t ctx, void *hook, unsigned int cat, const char *msg) { fprintf (stderr, "assuan (cat %d), %s\n", cat, msg); return 1; } /* Count octets required after trimming whitespace off the end of STRING and unescaping it. Note that this will never be larger than strlen (STRING). This count does not include any trailing null byte. */ static size_t count_trimmed_unescaped (const char *string) { size_t n = 0; size_t last_non_whitespace = 0; while (*string) { n++; if (*string == '%' && string[1] && isxdigit(string[1]) && string[2] && isxdigit(string[2])) { string++; string++; } else if (!isspace(*string)) { last_non_whitespace = n; } string++; } return last_non_whitespace; } #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) /* Trim whitespace off the right of STRING, unescape it, and return a malloc'ed buffer of the correct size. returns NULL on failure */ static char * trim_and_unescape (const char *string) { size_t sz = count_trimmed_unescaped (string); char *p = malloc(sz+1); if (!p) return NULL; p[sz] = '\0'; for (int i = 0; i < sz; i++) { if (*string == '%' && string[1] && isxdigit(string[1]) && string[2] && isxdigit(string[2])) { string++; p[i] = xtoi_2 (string); string++; } else p[i] = *string; string++; } return (p); } #ifdef PATH_MAX #define BUFSIZE PATH_MAX #else #define BUFSIZE 4096 #endif char* gpg_agent_sockname () { FILE *f; size_t bytecount, pos; char buf[BUFSIZE]; f = popen("gpgconf --list-dirs | grep ^agent-socket: | cut -f2 -d:", "r"); if (!f) return NULL; pos = 0; while (!feof(f)) { bytecount = fread(buf + pos, 1, sizeof(buf) - pos, f); if (ferror(f)) return NULL; pos += bytecount; if (pos >= sizeof(buf)) /* too much data! */ return NULL; } buf[pos] = '\0'; return trim_and_unescape(buf); } typedef enum { kt_unknown = 0, kt_rsa, kt_ed25519 } key_type; struct exporter { assuan_context_t ctx; gcry_cipher_hd_t wrap_cipher; unsigned char *wrapped_key; size_t wrapped_len; unsigned char *unwrapped_key; size_t unwrapped_len; key_type ktype; gcry_sexp_t sexp; gcry_mpi_t n; gcry_mpi_t e; gcry_mpi_t d; gcry_mpi_t p; gcry_mpi_t q; gcry_mpi_t iqmp; gcry_mpi_t curve; gcry_mpi_t flags; }; /* percent_plus_escape is copyright Free Software Foundation */ /* taken from common/percent.c in gnupg */ /* Create a newly allocated string from STRING with all spaces and control characters converted to plus signs or %xx sequences. The function returns the new string or NULL in case of a malloc failure. Note that we also escape the quote character to work around a bug in the mingw32 runtime which does not correctly handle command line quoting. We correctly double the quote mark when calling a program (i.e. gpg-protect-tool), but the pre-main code does not notice the double quote as an escaped quote. We do this also on POSIX systems for consistency. */ char * percent_plus_escape (const char *string) { char *buffer, *p; const char *s; size_t length; for (length=1, s=string; *s; s++) { if (*s == '+' || *s == '\"' || *s == '%' || *(const unsigned char *)s < 0x20) length += 3; else length++; } buffer = p = malloc (length); if (!buffer) return NULL; for (s=string; *s; s++) { if (*s == '+' || *s == '\"' || *s == '%' || *(const unsigned char *)s < 0x20) { snprintf (p, 4, "%%%02X", *(unsigned char *)s); p += 3; } else if (*s == ' ') *p++ = '+'; else *p++ = *s; } *p = 0; return buffer; } gpg_error_t extend_wrapped_key (struct exporter *e, const void *data, size_t data_sz) { size_t newsz = e->wrapped_len + data_sz; unsigned char *wknew = realloc (e->wrapped_key, newsz); if (!wknew) return GPG_ERR_ENOMEM; memcpy (wknew + e->wrapped_len, data, data_sz); e->wrapped_key = wknew; e->wrapped_len = newsz; return GPG_ERR_NO_ERROR; } gpg_error_t unwrap_rsa_key (struct exporter *e) { gpg_error_t ret; e->iqmp = gcry_mpi_new(0); ret = gcry_mpi_invm (e->iqmp, e->q, e->p); if (!ret) { fprintf (stderr, "Could not calculate the (inverse of q) mod p\n"); return GPG_ERR_GENERAL; } else { e->ktype = kt_rsa; return GPG_ERR_NO_ERROR; } } gpg_error_t unwrap_ed25519_key (struct exporter *e) { unsigned int sz; const char * data; #define opaque_compare(val, str, err) { \ data = gcry_mpi_get_opaque (val, &sz); \ if ((sz != strlen (str)*8) || !data || \ memcmp (data, str, strlen(str))) \ return gpg_error (err); } /* verify that curve matches "Ed25519" */ opaque_compare (e->curve, "Ed25519", GPG_ERR_UNKNOWN_CURVE); /* verify that flags contains "eddsa" */ /* FIXME: what if there are other flags besides eddsa? */ opaque_compare (e->flags, "eddsa", GPG_ERR_UNKNOWN_FLAG); /* verify that q starts with 0x40 and is 33 octets long */ data = gcry_mpi_get_opaque (e->q, &sz); if (sz != 33*8 || !data || data[0] != 0x40) return gpg_error (GPG_ERR_INV_CURVE); /* verify that d is 32 octets long */ data = gcry_mpi_get_opaque (e->d, &sz); if (sz < 32*8) return gpg_error (GPG_ERR_TOO_SHORT); if (sz > 32*8) return gpg_error (GPG_ERR_TOO_LARGE); if (!data) return gpg_error (GPG_ERR_NO_OBJ); e->ktype = kt_ed25519; return GPG_ERR_NO_ERROR; } gpg_error_t unwrap_key (struct exporter *e) { unsigned char *out = NULL; gpg_error_t ret; const size_t sz_diff = 8; /* need 8 octets less: 'GCRY_CIPHER_MODE_AESWRAP' This mode is used to implement the AES-Wrap algorithm according to RFC-3394. It may be used with any 128 bit block length algorithm, however the specs require one of the 3 AES algorithms. These special conditions apply: If 'gcry_cipher_setiv' has not been used the standard IV is used; if it has been used the lower 64 bit of the IV are used as the Alternative Initial Value. On encryption the provided output buffer must be 64 bit (8 byte) larger than the input buffer; in-place encryption is still allowed. On decryption the output buffer may be specified 64 bit (8 byte) shorter than then input buffer. As per specs the input length must be at least 128 bits and the length must be a multiple of 64 bits. */ if ((e->ctx == NULL) || (e->wrap_cipher == NULL) || (e->wrapped_key == NULL) || (e->wrapped_len < 1)) return GPG_ERR_GENERAL; /* this exporter is not in the right state */ out = realloc (e->unwrapped_key, e->wrapped_len - sz_diff); if (!out) return GPG_ERR_ENOMEM; e->unwrapped_key = out; e->unwrapped_len = e->wrapped_len - sz_diff; ret = gcry_cipher_decrypt (e->wrap_cipher, e->unwrapped_key, e->unwrapped_len, e->wrapped_key, e->wrapped_len); if (ret) return ret; ret = gcry_sexp_new(&e->sexp, e->unwrapped_key, e->unwrapped_len, 0); if (ret) return ret; /* RSA has: n, e, d, p, q */ ret = gcry_sexp_extract_param (e->sexp, "private-key!rsa", "nedpq", &e->n, &e->e, &e->d, &e->p, &e->q, NULL); if (!ret) return unwrap_rsa_key (e); if (gpg_err_code (ret) == GPG_ERR_NOT_FOUND) { /* check whether it's ed25519 */ /* EdDSA has: curve, flags, q, d */ ret = gcry_sexp_extract_param (e->sexp, "private-key!ecc", "/'curve''flags'qd", &e->curve, &e->flags, &e->q, &e->d, NULL); if (!ret) return unwrap_ed25519_key (e); } return ret; } gpg_error_t data_cb (void *arg, const void *data, size_t data_sz) { struct exporter *e = (struct exporter*)arg; gpg_error_t ret; if (e->wrap_cipher == NULL) { size_t cipher_keylen = gcry_cipher_get_algo_keylen(KEYWRAP_ALGO); if (data_sz != cipher_keylen) { fprintf (stderr, "wrong number of bytes in keywrap key (expected %zu, got %zu)\n", cipher_keylen, data_sz); return GPG_ERR_INV_KEYLEN; } ret = gcry_cipher_open (&(e->wrap_cipher), KEYWRAP_ALGO, KEYWRAP_ALGO_MODE, 0); if (ret) return ret; ret = gcry_cipher_setkey (e->wrap_cipher, data, data_sz); if (ret) return ret; } else { return extend_wrapped_key (e, data, data_sz); } return 0; } gpg_error_t inquire_cb (void *arg, const char *prompt) { fprintf (stderr, "inquire: %s\n", prompt); return 0; } gpg_error_t status_cb (void *arg, const char *status) { fprintf (stderr, "status: %s\n", status); return 0; } gpg_error_t transact (struct exporter *e, const char *command) { return assuan_transact (e->ctx, command, data_cb, e, inquire_cb, e, status_cb, e); } gpg_error_t sendenv (struct exporter *e, const char *env, const char *val, const char *option_name) { char *str = NULL; gpg_error_t ret; int r; if (!val) val = getenv(env); /* skip env vars that are unset */ if (!val) return GPG_ERR_NO_ERROR; if (option_name) r = asprintf (&str, "OPTION %s=%s", option_name, val); else r = asprintf (&str, "OPTION putenv=%s=%s", env, val); if (r <= 0) return GPG_ERR_ENOMEM; ret = transact (e, str); free (str); return ret; } size_t get_ssh_sz (gcry_mpi_t mpi) { size_t wid; gcry_mpi_print (GCRYMPI_FMT_SSH, NULL, 0, &wid, mpi); return wid; } int send_to_ssh_agent(struct exporter *e, int fd, unsigned int seconds, int confirm, const char *comment) { const char *key_type; int ret; size_t len, mpilen; off_t offset; unsigned char *msgbuf = NULL; uint32_t tmp; size_t slen; ssize_t written, bytesread; unsigned char resp; if (e->ktype != kt_rsa && e->ktype != kt_ed25519) { fprintf (stderr, "key is neither RSA nor Ed25519, cannot handle it.\n"); return -1; } if (e->ktype == kt_rsa) { key_type = "ssh-rsa"; mpilen = get_ssh_sz (e->n) + get_ssh_sz (e->e) + get_ssh_sz (e->d) + get_ssh_sz (e->iqmp) + get_ssh_sz (e->p) + get_ssh_sz (e->q); } else if (e->ktype == kt_ed25519) { key_type = "ssh-ed25519"; mpilen = 4 + 32 + /* ENC(A) */ 4 + 64; /* k || ENC(A) */ } len = 1 + /* request byte */ 4 + strlen(key_type) + /* type of key */ mpilen + 4 + (comment ? strlen (comment) : 0) + (confirm ? 1 : 0) + (seconds ? 5 : 0); msgbuf = malloc (4 + len); if (msgbuf == NULL) { fprintf (stderr, "could not allocate %zu bytes for the message to ssh-agent\n", 4 + len); return -1; } #define w32(a) { tmp = htonl(a); memcpy(msgbuf + offset, &tmp, sizeof(tmp)); offset += sizeof(tmp); } #define wstr(a) { slen = (a ? strlen (a) : 0); w32 (slen); if (a) memcpy (msgbuf + offset, a, slen); offset += slen; } #define wbyte(x) { msgbuf[offset] = (x); offset += 1; } #define wmpi(n) { ret = gcry_mpi_print (GCRYMPI_FMT_SSH, msgbuf + offset, get_ssh_sz (n), &slen, n); \ if (ret) { fprintf (stderr, "failed writing ssh mpi " #n "\n"); free (msgbuf); return -1; }; offset += slen; } offset = 0; w32 (len); wbyte (seconds || confirm ? SSH2_AGENTC_ADD_ID_CONSTRAINED : SSH2_AGENTC_ADD_IDENTITY); wstr (key_type); if (e->ktype == kt_rsa) { wmpi (e->n); wmpi (e->e); wmpi (e->d); wmpi (e->iqmp); wmpi (e->p); wmpi (e->q); } else if (e->ktype == kt_ed25519) { unsigned int dsz, qsz; const char *ddata, *qdata; qdata = gcry_mpi_get_opaque (e->q, &qsz); ddata = gcry_mpi_get_opaque (e->d, &dsz); if (qsz != 33*8 || dsz != 32*8 || !qdata || !ddata) { fprintf (stderr, "Ed25519 key did not have the expected components (q: %d %p, d: %d %p)\n", qsz, qdata, dsz, ddata); return -1; } /* ENC(A) (aka q)*/ w32 (32); memcpy (msgbuf + offset, qdata+1, 32); offset += 32; /* k || ENC(A) (aka d || q) */ w32 (64); memcpy (msgbuf + offset, ddata, 32); offset += 32; memcpy (msgbuf + offset, qdata+1, 32); offset += 32; } wstr (comment); if (confirm) wbyte (SSH_AGENT_CONSTRAIN_CONFIRM); if (seconds) { wbyte (SSH_AGENT_CONSTRAIN_LIFETIME); w32 (seconds); } written = write (fd, msgbuf, 4+len); if (written != 4 + len) { fprintf (stderr, "failed writing message to ssh agent socket (%zd) (errno: %d)\n", written, errno); free (msgbuf); return -1; } free (msgbuf); /* FIXME: this could actually be done in a select loop if we think the ssh-agent will dribble out its response or not respond immediately.*/ bytesread = read (fd, &tmp, sizeof (tmp)); if (bytesread != sizeof (tmp)) { fprintf (stderr, "failed to get %zu bytes from ssh-agent (got %zd)\n", sizeof (tmp), bytesread); return -1; } slen = ntohl (tmp); if (slen != sizeof(resp)) { fprintf (stderr, "ssh-agent response was wrong size (expected: %zu; got %zu)\n", sizeof(resp), slen); return -1; } bytesread = read (fd, &resp, sizeof (resp)); if (bytesread != sizeof (resp)) { fprintf (stderr, "failed to get %zu bytes from ssh-agent (got %zd)\n", sizeof (resp), bytesread); return -1; } if (resp != SSH_AGENT_SUCCESS) { fprintf (stderr, "ssh-agent did not claim success (expected: %d; got %d)\n", SSH_AGENT_SUCCESS, resp); return -1; } return 0; } void free_exporter (struct exporter *e) { assuan_release (e->ctx); if (e->wrap_cipher) gcry_cipher_close (e->wrap_cipher); free (e->wrapped_key); free (e->unwrapped_key); gcry_mpi_release(e->n); gcry_mpi_release(e->d); gcry_mpi_release(e->e); gcry_mpi_release(e->p); gcry_mpi_release(e->q); gcry_mpi_release(e->iqmp); gcry_mpi_release(e->curve); gcry_mpi_release(e->flags); gcry_sexp_release (e->sexp); } void usage (FILE *f) { fprintf (f, "Usage: agent-transfer [options] KEYGRIP [COMMENT]\n" "\n" "Extracts a secret key from the GnuPG agent (by keygrip),\n" "and sends it to the running SSH agent.\n" "\n" " KEYGRIP should be a GnuPG keygrip\n" " (e.g. try \"gpg --with-keygrip --list-secret-keys\")\n" " COMMENT (optional) can be any string\n" " (must not start with a \"-\")\n" "\n" "Options:\n" " -t SECONDS lifetime (in seconds) for the key to live in ssh-agent\n" " -c require confirmation when using the key in ssh-agent\n" " -h print this help\n" ); } int get_ssh_auth_sock_fd() { char *sock_name = getenv("SSH_AUTH_SOCK"); struct sockaddr_un sockaddr; int ret = -1; if (sock_name == NULL) { fprintf (stderr, "SSH_AUTH_SOCK is not set, cannot talk to agent.\n"); return -1; } if (strlen(sock_name) + 1 > sizeof(sockaddr.sun_path)) { fprintf (stderr, "SSH_AUTH_SOCK (%s) is larger than the maximum allowed socket path (%zu)\n", sock_name, sizeof(sockaddr.sun_path)); return -1; } sockaddr.sun_family = AF_UNIX; strncpy(sockaddr.sun_path, sock_name, sizeof(sockaddr.sun_path) - 1); sockaddr.sun_path[sizeof(sockaddr.sun_path) - 1] = '\0'; ret = socket (AF_UNIX, SOCK_STREAM, 0); if (ret == -1) { fprintf (stderr, "Could not open a socket file descriptor\n"); return ret; } if (-1 == connect (ret, (const struct sockaddr*)(&sockaddr), sizeof(sockaddr))) { fprintf (stderr, "Failed to connect to ssh agent socket %s\n", sock_name); close (ret); return -1; } return ret; } struct args { int seconds; int confirm; const char *comment; const char *keygrip; int help; }; int parse_args (int argc, const char **argv, struct args *args) { int ptr = 1; int idx = 0; while (ptr < argc) { if (argv[ptr][0] == '-') { int looking_for_seconds = 0; const char *x = argv[ptr] + 1; while (*x != '\0') { switch (*x) { case 'c': args->confirm = 1; break; case 't': looking_for_seconds = 1; break; case 'h': args->help = 1; break; default: fprintf (stderr, "flag not recognized: %c\n", *x); return 1; } x++; } if (looking_for_seconds) { if (argc <= ptr + 1) { fprintf (stderr, "lifetime (-t) needs an argument (number of seconds)\n"); return 1; } args->seconds = atoi (argv[ptr + 1]); if (args->seconds <= 0) { fprintf (stderr, "lifetime (seconds) must be > 0\n"); return 1; } ptr += 1; } } else { if (args->keygrip == NULL) { if (strlen (argv[ptr]) != KEYGRIP_LENGTH) { fprintf (stderr, "keygrip must be 40 hexadecimal digits\n"); return 1; } for (idx = 0; idx < KEYGRIP_LENGTH; idx++) { if (!isxdigit(argv[ptr][idx])) { fprintf (stderr, "keygrip must be 40 hexadecimal digits\n"); return 1; } } args->keygrip = argv[ptr]; } else { if (args->comment == NULL) { args->comment = argv[ptr]; } else { fprintf (stderr, "unrecognized argument %s\n", argv[ptr]); return 1; } } } ptr += 1; }; return 0; } int main (int argc, const char* argv[]) { gpg_error_t err; char *gpg_agent_socket = NULL; int ssh_sock_fd = 0; char *get_key = NULL, *desc_prompt = NULL; int idx = 0, ret = 0; struct exporter e = { .wrapped_key = NULL }; /* ssh agent constraints: */ struct args args = { .keygrip = NULL }; char *escaped_comment = NULL; char *alt_comment = NULL; if (!gcry_check_version (GCRYPT_VERSION)) { fprintf (stderr, "libgcrypt version mismatch\n"); return 1; } gcry_control (GCRYCTL_DISABLE_SECMEM, 0); gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); if (parse_args(argc, argv, &args)) { usage (stderr); return 1; } if (args.help) { usage (stdout); return 0; } if (asprintf (&get_key, "EXPORT_KEY %s", args.keygrip) < 0) { fprintf (stderr, "failed to generate key export string\n"); return 1; } if (args.comment && (escaped_comment = percent_plus_escape (args.comment), escaped_comment)) { ret = asprintf (&desc_prompt, "SETKEYDESC Sending+key+for+'%s'+" "from+gpg-agent+to+ssh-agent...%%0a" "(keygrip:+%s)", escaped_comment, args.keygrip); free (escaped_comment); } else { ret = asprintf (&desc_prompt, "SETKEYDESC Sending+key+from+gpg-agent+to+ssh-agent...%%0a" "(keygrip:+%s)", args.keygrip); } if (ret < 0) { fprintf (stderr, "failed to generate prompt description\n"); return 1; } ssh_sock_fd = get_ssh_auth_sock_fd(); if (ssh_sock_fd == -1) return 1; err = assuan_new (&(e.ctx)); if (err) { fprintf (stderr, "failed to create assuan context (%d) (%s)\n", err, gpg_strerror (err)); return 1; } gpg_agent_socket = gpg_agent_sockname(); /* launch gpg-agent if it is not already connected */ err = assuan_socket_connect (e.ctx, gpg_agent_socket, ASSUAN_INVALID_PID, ASSUAN_SOCKET_CONNECT_FDPASSING); if (err) { if (gpg_err_code (err) != GPG_ERR_ASS_CONNECT_FAILED) { fprintf (stderr, "failed to connect to gpg-agent socket (%d) (%s)\n", err, gpg_strerror (err)); } else { fprintf (stderr, "could not find gpg-agent, trying to launch it...\n"); int r = system ("gpgconf --launch gpg-agent"); if (r) { fprintf (stderr, "failed to launch gpg-agent\n"); return 1; } /* try to connect again: */ err = assuan_socket_connect (e.ctx, gpg_agent_socket, ASSUAN_INVALID_PID, ASSUAN_SOCKET_CONNECT_FDPASSING); if (err) { fprintf (stderr, "failed to connect to gpg-agent after launching (%d) (%s)\n", err, gpg_strerror (err)); return 1; } } } /* FIXME: what do we do if "getinfo std_env_names" includes something new? */ struct { const char *env; const char *val; const char *opt; } vars[] = { { .env = "GPG_TTY", .val = ttyname(0), .opt = "ttyname" }, { .env = "TERM", .opt = "ttytype" }, { .env = "DISPLAY", .opt = "display" }, { .env = "XAUTHORITY", .opt = "xauthority" }, { .env = "GTK_IM_MODULE" }, { .env = "DBUS_SESSION_BUS_ADDRESS" }, { .env = "LANG", .opt = "lc-ctype" }, { .env = "LANG", .opt = "lc-messages" } }; for (idx = 0; idx < sizeof(vars)/sizeof(vars[0]); idx++) { if (err = sendenv (&e, vars[idx].env, vars[idx].val, vars[idx].opt), err) { fprintf (stderr, "failed to set %s (%s)\n", vars[idx].opt ? vars[idx].opt : vars[idx].env, gpg_strerror(err)); } } err = transact (&e, "keywrap_key --export"); if (err) { fprintf (stderr, "failed to export keywrap key (%d), %s\n", err, gpg_strerror(err)); return 1; } err = transact (&e, desc_prompt); if (err) { fprintf (stderr, "failed to set the description prompt (%d), %s\n", err, gpg_strerror(err)); return 1; } err = transact (&e, get_key); if (err) { fprintf (stderr, "failed to export secret key %s (%d), %s\n", args.keygrip, err, gpg_strerror(err)); return 1; } err = unwrap_key (&e); if (err) { fprintf (stderr, "failed to unwrap secret key (%d), %s\n", err, gpg_strerror(err)); return 1; } if (!args.comment) { int bytes_printed = asprintf (&alt_comment, "GnuPG keygrip %s", args.keygrip); if (bytes_printed < 0) { fprintf (stderr, "failed to generate key comment\n"); return 1; } } err = send_to_ssh_agent (&e, ssh_sock_fd, args.seconds, args.confirm, args.comment ? args.comment : alt_comment); if (err) return 1; /* fwrite (e.unwrapped_key, e.unwrapped_len, 1, stdout); */ close (ssh_sock_fd); free (gpg_agent_socket); free (get_key); free (desc_prompt); free (alt_comment); free_exporter (&e); return 0; } monkeysphere-0.43/src/agent-transfer/ssh-agent-proto.h000066400000000000000000000041041342216672300230700ustar00rootroot00000000000000/* see https://tools.ietf.org/html/draft-miller-ssh-agent */ /* 3.2 Requests from client to agent for protocol 2 key operations */ #define SSH2_AGENTC_REQUEST_IDENTITIES 11 #define SSH2_AGENTC_SIGN_REQUEST 13 #define SSH2_AGENTC_ADD_IDENTITY 17 #define SSH2_AGENTC_REMOVE_IDENTITY 18 #define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19 #define SSH2_AGENTC_ADD_ID_CONSTRAINED 25 /* 3.3 Key-type independent requests from client to agent */ #define SSH_AGENTC_ADD_SMARTCARD_KEY 20 #define SSH_AGENTC_REMOVE_SMARTCARD_KEY 21 #define SSH_AGENTC_LOCK 22 #define SSH_AGENTC_UNLOCK 23 #define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26 /* 3.4 Generic replies from agent to client */ #define SSH_AGENT_FAILURE 5 #define SSH_AGENT_SUCCESS 6 /* 3.6 Replies from agent to client for protocol 2 key operations */ #define SSH2_AGENT_IDENTITIES_ANSWER 12 #define SSH2_AGENT_SIGN_RESPONSE 14 /* 3.7 Key constraint identifiers */ #define SSH_AGENT_CONSTRAIN_LIFETIME 1 #define SSH_AGENT_CONSTRAIN_CONFIRM 2 /* All protocol messages are prefixed with their length in bytes, encoded as a 32 bit unsigned integer. Specifically: uint32 message_length byte[message_length] message RSA keys may be added with this request: byte SSH2_AGENTC_ADD_IDENTITY or SSH2_AGENTC_ADD_ID_CONSTRAINED string "ssh-rsa" mpint rsa_n mpint rsa_e mpint rsa_d mpint rsa_iqmp mpint rsa_p mpint rsa_q string key_comment constraint[] key_constraints Ed25519 keys may be added with this request: byte SSH_AGENTC_ADD_IDENTITY or SSH_AGENTC_ADD_ID_CONSTRAINED string "ssh-ed25519" string ENC(A) string k || ENC(A) string comment constraint[] constraints The first value is the 32 byte Ed25519 public key "ENC(A)". The second value is a concatenation of the 32 byte private key "k" and 32 byte public "ENC(A)" key. The contents and interpretation of the "ENC(A)" and "k" values are defined by [I-D.irtf-cfrg-eddsa]. */ monkeysphere-0.43/src/monkeysphere000077500000000000000000000211011342216672300174000ustar00rootroot00000000000000#!/usr/bin/env bash # monkeysphere: Monkeysphere client tool # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # Micah Anderson # # They are Copyright 2008-2019, and are all released under the GPL, version 3 # or later. ######################################################################## set -e PGRM=$(basename $0) SYSSHAREDIR=${MONKEYSPHERE_SYSSHAREDIR:-"__SYSSHAREDIR_PREFIX__/share/monkeysphere"} export SYSSHAREDIR . "${SYSSHAREDIR}/defaultenv" . "${SYSSHAREDIR}/common" # sharedir for host functions MSHAREDIR="${SYSSHAREDIR}/m" # UTC date in ISO 8601 format if needed DATE=$(date -u '+%FT%T') # unset some environment variables that could screw things up unset GREP_OPTIONS # set the file creation mask to be only owner rw umask 077 ######################################################################## # FUNCTIONS ######################################################################## usage() { cat <&2 usage: $PGRM [options] [args] Monkeysphere client tool. subcommands: update-known_hosts (k) [HOST]... update known_hosts file update-authorized_keys (a) update authorized_keys file ssh-proxycommand HOST [PORT] monkeysphere ssh ProxyCommand --no-connect do not make TCP connection to host subkey-to-ssh-agent (s) store authentication subkey in ssh-agent keys-for-userid (u) USERID output valid ssh keys for given user id sshfprs-for-userid USERID output ssh fingerprints for given user id gen-subkey (g) [KEYID] generate an authentication subkey --length (-l) BITS RSA key length in bits (3072) version (v) show version number help (h,?) this help EOF } # user gpg command to define common options gpg_user() { LC_ALL=C "${GPG:-gpg}" --fixed-list-mode --no-greeting --quiet --no-tty "$@" } # output the ssh fingerprint of a gpg key gpg_ssh_fingerprint() { keyid="$1" gpg_user --export-ssh-key "0x${keyid}!" | cut -f1,2 -d' ' | sed 's/$/ ./' | ssh-keygen -l -f - } # take a secret key ID and check that only zero or one ID is provided, # and that it corresponds to only a single secret key ID check_gpg_sec_key_id() { local gpgSecOut case "$#" in 0) gpgSecOut=$(gpg_user --list-secret-keys --with-colons 2>/dev/null | egrep '^sec:') ;; 1) gpgSecOut=$(gpg_user --list-secret-keys --with-colons "$1" | egrep '^sec:') || failure ;; *) failure "You must specify only a single primary key ID." ;; esac # check that only a single secret key was found case $(echo "$gpgSecOut" | grep -c '^sec:') in 0) failure "No secret keys found. Create an OpenPGP key with the following command: gpg --gen-key" ;; 1) echo "$gpgSecOut" | cut -d: -f5 ;; *) local seckeys=$(echo "$gpgSecOut" | cut -d: -f5) failure "Multiple primary secret keys found: $seckeys Please specify which primary key to use." ;; esac } # check that a valid authentication subkey does not already exist check_gpg_authentication_subkey() { local keyID local IFS local line local type local validity local usage keyID="$1" # check that a valid authentication key does not already exist IFS=$'\n' for line in $(gpg_user --list-keys --with-colons "$keyID") ; do type=$(echo "$line" | cut -d: -f1) validity=$(echo "$line" | cut -d: -f2) usage=$(echo "$line" | cut -d: -f12) # look at keys only if [ "$type" != 'pub' -a "$type" != 'sub' ] ; then continue fi # check for authentication capability if ! check_capability "$usage" 'a' ; then continue fi # if authentication key is valid, prompt to continue if [ "$validity" = 'u' ] ; then echo "A valid authentication key already exists for primary key '$keyID'." 1>&2 if [ "$PROMPT" != "false" ] ; then printf "Are you sure you would like to generate another one? (y/N) " >&2 read OK; OK=${OK:N} if [ "${OK/y/Y}" != 'Y' ] ; then failure "aborting." fi break else failure "aborting." fi fi done } ######################################################################## # MAIN ######################################################################## # set unset default variables GNUPGHOME=${GNUPGHOME:="${HOME}/.gnupg"} KNOWN_HOSTS="${HOME}/.ssh/known_hosts" HASH_KNOWN_HOSTS="false" AUTHORIZED_KEYS="${HOME}/.ssh/authorized_keys" # unset the check keyserver variable, since that needs to have # different defaults for the different functions unset CHECK_KEYSERVER # load global config [ -r "${SYSCONFIGDIR}/monkeysphere.conf" ] \ && . "${SYSCONFIGDIR}/monkeysphere.conf" # set monkeysphere home directory MONKEYSPHERE_HOME=${MONKEYSPHERE_HOME:="${HOME}/.monkeysphere"} mkdir -p -m 0700 "$MONKEYSPHERE_HOME" # load local config [ -e ${MONKEYSPHERE_CONFIG:="${MONKEYSPHERE_HOME}/monkeysphere.conf"} ] \ && . "$MONKEYSPHERE_CONFIG" # set empty config variables with ones from the environment GNUPGHOME=${MONKEYSPHERE_GNUPGHOME:=$GNUPGHOME} LOG_LEVEL=${MONKEYSPHERE_LOG_LEVEL:=$LOG_LEVEL} KEYSERVER=${MONKEYSPHERE_KEYSERVER:=$KEYSERVER} # if keyserver not specified in env or conf, then look in gpg.conf if [ -z "$KEYSERVER" ] ; then if [ -f "${GNUPGHOME}/gpg.conf" ] ; then KEYSERVER=$(grep -e "^[[:space:]]*keyserver " "${GNUPGHOME}/gpg.conf" | tail -1 | awk '{ print $2 }') fi fi PROMPT=${MONKEYSPHERE_PROMPT:=$PROMPT} KNOWN_HOSTS=${MONKEYSPHERE_KNOWN_HOSTS:=$KNOWN_HOSTS} HASH_KNOWN_HOSTS=${MONKEYSPHERE_HASH_KNOWN_HOSTS:=$HASH_KNOWN_HOSTS} AUTHORIZED_KEYS=${MONKEYSPHERE_AUTHORIZED_KEYS:=$AUTHORIZED_KEYS} STRICT_MODES=${MONKEYSPHERE_STRICT_MODES:=$STRICT_MODES} # other variables not in config file AUTHORIZED_USER_IDS=${MONKEYSPHERE_AUTHORIZED_USER_IDS:="${MONKEYSPHERE_HOME}/authorized_user_ids"} REQUIRED_HOST_KEY_CAPABILITY=${MONKEYSPHERE_REQUIRED_HOST_KEY_CAPABILITY:="a"} REQUIRED_USER_KEY_CAPABILITY=${MONKEYSPHERE_REQUIRED_USER_KEY_CAPABILITY:="a"} # note that only using '=' instead of ':=' tests only if the variable # in unset, not if it's "null" LOG_PREFIX=${MONKEYSPHERE_LOG_PREFIX='ms: '} # export GNUPGHOME and make sure gpg home exists with proper # permissions export GNUPGHOME mkdir -p -m 0700 "$GNUPGHOME" export LOG_LEVEL export LOG_PREFIX if [ "$#" -eq 0 ] ; then usage failure "Please supply a subcommand." fi if ! is_gpg_version_greater_equal 2.1.11 ; then failure "Monkeysphere needs GnuPG >= 2.1.11" fi # get subcommand COMMAND="$1" shift case $COMMAND in 'update-known_hosts'|'update-known-hosts'|'k') # whether or not to check keyservers CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=${CHECK_KEYSERVER:="true"}} source "${MSHAREDIR}/update_known_hosts" # if hosts are specified on the command line, process just # those hosts if [ "$1" ] ; then update_known_hosts "$@" # otherwise, if no hosts are specified, process every host # in the user's known_hosts file else process_known_hosts fi ;; 'update-authorized_keys'|'update-authorized-keys'|'a') # whether or not to check keyservers CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=${CHECK_KEYSERVER:="true"}} source "${MSHAREDIR}/update_authorized_keys" update_authorized_keys ;; 'import-subkey'|'import'|'i') source "${MSHAREDIR}/import_subkey" import_subkey "$@" ;; 'gen-subkey'|'g') source "${MSHAREDIR}/gen_subkey" gen_subkey "$@" ;; 'ssh-proxycommand'|'p') source "${MSHAREDIR}/ssh_proxycommand" ssh_proxycommand "$@" ;; 'subkey-to-ssh-agent'|'s') source "${MSHAREDIR}/subkey_to_ssh_agent" subkey_to_ssh_agent "$@" ;; 'sshfpr') echo "Warning: 'sshfpr' is deprecated. Please use 'sshfprs-for-userid' instead." >&2 gpg_ssh_fingerprint "$@" ;; 'keys-for-userid'|'u') CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=${CHECK_KEYSERVER:="true"}} source "${MSHAREDIR}/keys_for_userid" keys_for_userid "$@" ;; 'sshfprs-for-userid') CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=${CHECK_KEYSERVER:="true"}} source "${MSHAREDIR}/keys_for_userid" keys_for_userid "$@" | "$SYSSHAREDIR/keytrans" sshfpr ;; 'keys-from-userid') echo "Warning: 'keys-from-userid' is deprecated. Please use 'keys-for-userid' instead." >&2 CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=${CHECK_KEYSERVER:="true"}} source "${MSHAREDIR}/keys_for_userid" keys_for_userid "$@" ;; 'version'|'--version'|'v') version ;; 'help'|'--help'|'-h'|'h'|'?') usage ;; *) failure "Unknown command: '$COMMAND' Try '$PGRM help' for usage." ;; esac monkeysphere-0.43/src/monkeysphere-authentication000077500000000000000000000145771342216672300224400ustar00rootroot00000000000000#!/usr/bin/env bash # monkeysphere-authentication: Monkeysphere authentication admin tool # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # Micah Anderson # # They are Copyright 2008-2019, and are all released under the GPL, # version 3 or later. ######################################################################## set -e # set the pipefail option so pipelines fail on first command failure set -o pipefail PGRM=$(basename $0) SYSSHAREDIR=${MONKEYSPHERE_SYSSHAREDIR:-"__SYSSHAREDIR_PREFIX__/share/monkeysphere"} export SYSSHAREDIR . "${SYSSHAREDIR}/defaultenv" . "${SYSSHAREDIR}/common" # sharedir for authentication functions MASHAREDIR="${SYSSHAREDIR}/ma" # datadir for authentication functions MADATADIR="${SYSDATADIR}/authentication" # temp directory to enable atomic moves of authorized_keys files MATMPDIR="${MADATADIR}/tmp" export MATMPDIR # UTC date in ISO 8601 format if needed DATE=$(date -u '+%FT%T') # unset some environment variables that could screw things up unset GREP_OPTIONS ######################################################################## # FUNCTIONS ######################################################################## usage() { cat <&2 usage: $PGRM [options] [args] Monkeysphere authentication admin tool. subcommands: update-users (u) [USER]... update user authorized_keys files keys-for-user (k) USER output user authorized_keys lines to stdout refresh-keys (r) refresh keys in keyring add-id-certifier (c+) KEYID|FILE import and tsign a certification key [--domain (-n) DOMAIN] limit ID certifications to DOMAIN [--trust (-t) TRUST] trust level of certifier (default: full) [--depth (-d) DEPTH] trust depth for certifier (default: 1) remove-id-certifier (c-) KEYID remove a certification key list-id-certifiers (c) list certification keys version (v) show version number help (h,?) this help See ${PGRM}(8) for more info. EOF } # function to interact with the gpg core keyring gpg_core() { GNUPGHOME="$GNUPGHOME_CORE" export GNUPGHOME gpg --fixed-list-mode --no-greeting --quiet --no-tty "$@" } # function to interact with the gpg sphere keyring gpg_sphere() { GNUPGHOME="$GNUPGHOME_SPHERE" export GNUPGHOME run_as_monkeysphere_user gpg --fixed-list-mode --no-greeting --quiet --no-tty "$@" } # output to stdout the core fingerprint from the gpg core secret # keyring core_fingerprint() { log debug "determining core key fingerprint..." gpg_core --list-secret-key --with-colons \ --with-fingerprint \ | awk -F: '/^fpr:/{ if (ok) { print $10 } ; ok=0 } /^sec:/{ ok=1 }' } # export signatures from core to sphere gpg_core_sphere_sig_transfer() { log debug "exporting core local sigs to sphere..." gpg_core --export-options export-local-sigs --export | \ gpg_sphere --import-options import-local-sigs --import 2>&1 | log debug } ######################################################################## # MAIN ######################################################################## # set unset default variables AUTHORIZED_USER_IDS="%h/.monkeysphere/authorized_user_ids" RAW_AUTHORIZED_KEYS="%h/.ssh/authorized_keys" # load configuration file [ -e ${MONKEYSPHERE_AUTHENTICATION_CONFIG:="${SYSCONFIGDIR}/monkeysphere-authentication.conf"} ] \ && . "$MONKEYSPHERE_AUTHENTICATION_CONFIG" # set empty config variable with ones from the environment LOG_LEVEL=${MONKEYSPHERE_LOG_LEVEL:=$LOG_LEVEL} KEYSERVER=${MONKEYSPHERE_KEYSERVER:=$KEYSERVER} CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=$CHECK_KEYSERVER} MONKEYSPHERE_USER=${MONKEYSPHERE_MONKEYSPHERE_USER:=$MONKEYSPHERE_USER} MONKEYSPHERE_GROUP=$(get_primary_group "$MONKEYSPHERE_USER") PROMPT=${MONKEYSPHERE_PROMPT:=$PROMPT} AUTHORIZED_USER_IDS=${MONKEYSPHERE_AUTHORIZED_USER_IDS:=$AUTHORIZED_USER_IDS} RAW_AUTHORIZED_KEYS=${MONKEYSPHERE_RAW_AUTHORIZED_KEYS:=$RAW_AUTHORIZED_KEYS} STRICT_MODES=${MONKEYSPHERE_STRICT_MODES:=$STRICT_MODES} # other variables REQUIRED_USER_KEY_CAPABILITY=${MONKEYSPHERE_REQUIRED_USER_KEY_CAPABILITY:="a"} GNUPGHOME_CORE=${MONKEYSPHERE_GNUPGHOME_CORE:="${MADATADIR}/core"} GNUPGHOME_SPHERE=${MONKEYSPHERE_GNUPGHOME_SPHERE:="${MADATADIR}/sphere"} CORE_KEYLENGTH=${MONKEYSPHERE_CORE_KEYLENGTH:="3072"} LOG_PREFIX=${MONKEYSPHERE_LOG_PREFIX:='ms: '} # export variables needed for invoking command under monkeysphere user export DATE export LOG_LEVEL export KEYSERVER export MONKEYSPHERE_USER export MONKEYSPHERE_GROUP export PROMPT export CHECK_KEYSERVER export REQUIRED_USER_KEY_CAPABILITY export GNUPGHOME_CORE export GNUPGHOME_SPHERE export GNUPGHOME export CORE_KEYLENGTH export LOG_PREFIX if [ "$#" -eq 0 ] ; then usage failure "Please supply a subcommand." fi # get subcommand COMMAND="$1" shift case $COMMAND in 'setup'|'setup'|'s') source "${MASHAREDIR}/setup" setup ;; 'update-users'|'update-user'|'update'|'u') source "${MASHAREDIR}/setup" setup source "${MASHAREDIR}/update_users" OUTPUT_STDOUT= update_users "$@" ;; 'keys-for-user'|'k') (( $# > 0 )) || failure "Must specify user." source "${MASHAREDIR}/setup" setup source "${MASHAREDIR}/update_users" OUTPUT_STDOUT=true update_users "$1" ;; 'refresh-keys'|'refresh'|'r') source "${MASHAREDIR}/setup" setup gpg_sphere --keyserver "$KEYSERVER" --refresh-keys ;; 'add-identity-certifier'|'add-id-certifier'|'add-certifier'|'c+') source "${MASHAREDIR}/setup" setup source "${MASHAREDIR}/add_certifier" add_certifier "$@" ;; 'remove-identity-certifier'|'remove-id-certifier'|'remove-certifier'|'c-') source "${MASHAREDIR}/setup" setup source "${MASHAREDIR}/remove_certifier" remove_certifier "$@" ;; 'list-identity-certifiers'|'list-id-certifiers'|'list-certifiers'|'list-certifier'|'c') source "${MASHAREDIR}/setup" setup source "${MASHAREDIR}/list_certifiers" list_certifiers ;; 'diagnostics'|'d') source "${MASHAREDIR}/setup" setup source "${MASHAREDIR}/diagnostics" diagnostics ;; 'gpg-cmd') source "${MASHAREDIR}/setup" setup gpg_sphere "$@" ;; 'version'|'--version'|'v') version ;; '--help'|'help'|'-h'|'h'|'?') usage ;; *) failure "Unknown command: '$COMMAND' Try '$PGRM help' for usage." ;; esac monkeysphere-0.43/src/monkeysphere-authentication-keys-for-user000077500000000000000000000001061342216672300251300ustar00rootroot00000000000000#!/usr/bin/env sh exec monkeysphere-authentication keys-for-user "$@" monkeysphere-0.43/src/monkeysphere-host000077500000000000000000000323241342216672300203640ustar00rootroot00000000000000#!/usr/bin/env bash # monkeysphere-host: Monkeysphere host admin tool # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # Micah Anderson # # They are Copyright 2008-2019, and are all released under the GPL, # version 3 or later. ######################################################################## set -e # set the pipefail option so pipelines fail on first command failure set -o pipefail PGRM=$(basename $0) SYSSHAREDIR=${MONKEYSPHERE_SYSSHAREDIR:-"__SYSSHAREDIR_PREFIX__/share/monkeysphere"} export SYSSHAREDIR . "${SYSSHAREDIR}/defaultenv" . "${SYSSHAREDIR}/common" # sharedir for host functions MHSHAREDIR="${SYSSHAREDIR}/mh" # datadir for host functions MHDATADIR="${SYSDATADIR}/host" # temp directory to enable sharing a temporary directory between root # and monkeysphere users. This is needed because on systems with # libpam-tmpdir, simply changing ownership/permissions on a directory # is not enough to share the directory. Parent directories such as # /tmp/user/0 will be inaccessible to monkeysphere user. # # XXX: Reusing monkeysphere-authentication's tmp directory. MHTMPDIR="${SYSDATADIR}/authentication/tmp" # host pub key files HOST_KEY_FILE="${SYSDATADIR}/host_keys.pub.pgp" # UTC date in ISO 8601 format if needed DATE=$(date -u '+%FT%T') # unset some environment variables that could screw things up unset GREP_OPTIONS ######################################################################## # FUNCTIONS ######################################################################## usage() { cat <&2 usage: $PGRM [options] [args] Monkeysphere host admin tool. subcommands: import-key (i) FILE SERVICENAME import PEM-encoded key from file show-keys (s) [KEYID ...] output host key information publish-keys (p) [KEYID ...] publish key(s) to keyserver set-expire (e) EXPIRE [KEYID] set key expiration add-servicename (n+) SERVICENAME [KEYID] add a service name to key revoke-servicename (n-) SERVICENAME [KEYID] revoke a service name from key add-revoker (r+) REVOKER_KEYID|FILE [KEYID] add a revoker to key revoke-key [KEYID] generate and/or publish revocation certificate for key version (v) show version number help (h,?) this help See ${PGRM}(8) for more info. EOF } # function to interact with the gpg keyring gpg_host() { GNUPGHOME="$GNUPGHOME_HOST" LC_ALL=C gpg --no-auto-check-trustdb --trust-model=always --no-greeting --quiet --no-tty --fixed-list-mode "$@" } # list the info about the a key, in colon format, to stdout gpg_host_list_keys() { if [ "$1" ] ; then gpg_host --list-keys --with-colons \ --with-fingerprint --with-fingerprint \ "$1" else gpg_host --list-keys --with-colons \ --with-fingerprint --with-fingerprint fi } # edit key scripts, takes scripts on stdin, and keyID as first input gpg_host_edit() { gpg_host --command-fd 0 --edit-key "$@" } # export the monkeysphere OpenPGP pub key file update_pgp_pub_file() { log debug "updating openpgp public key file '$HOST_KEY_FILE'..." gpg_host --export --armor --export-options export-minimal \ $(gpg_host --list-secret-keys --with-colons --fingerprint | awk -F: '/^fpr:/{ if (ok) { print $10 } ; ok=0 } /^sec:/{ ok=1 }') \ > "$HOST_KEY_FILE" } # check that the service name is well formed. we assume that the # service name refers to a host; DNS labels for host names are limited # to a very small range of characters (see RFC 1912, section 2.1). # FIXME: i'm failing to check here for label components that are # all-number (e.g. ssh://666.666), which are technically not allowed # (though some exist on the 'net, apparently) # FIXME: this will probably misbehave if raw IP addresses are provided, # either IPv4 or IPv6 using the bracket notation. # FIXME: this doesn't address the use of hashed User IDs. check_service_name() { local name="$1" local errs="" local scheme local port local assigned_ports [ -n "$name" ] || \ failure "You must supply a service name to check" printf '%s' "$name" | perl -n -e '($str = $_) =~ s/\s//g ; exit !(lc($str) eq $_);' || \ failure "Not a valid service name: '$name' Service names should be canonicalized to all lower-case, with no whitespace" [[ "$name" =~ ^[a-z0-9./:-]+$ ]] || \ failure "Not a valid service name: '$name' Service names should contain only lower-case ASCII letters numbers, dots (.), hyphens (-), slashes (/), and a colon (:). If you are using non-ASCII characters (e.g. IDN), you should use the canonicalized ASCII (NAMEPREP -> Punycode) representation (see RFC 3490)." [[ "$name" =~ \. ]] || \ failure "Not a valid service name: '$name' Service names should use fully-qualified domain names (FQDN), but the domain name you chose appears to only have the local part. For example: don't use 'ssh://foo' ; use 'ssh://foo.example.com' instead." [[ "$name" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?://[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.|((\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+))(:[1-9][0-9]{0,4})?$ ]] || \ failure "Not a valid service name: '$name' Service names look like ://full.example.com[:], where is something like ssh or https, and is a decimal number (supplied only if the service is on a non-standard port)." scheme=$(cut -f1 -d: <<<"$name") port=$(cut -f3 -d: <<<"$name") # check that the scheme name is found in the system services # database available_=$(get_port_for_service "$scheme") || \ log error "Error looking up service scheme named '%s'" "$scheme" # FIXME: if the service isn't found, or does not have a port, what # should we do? at the moment, we're just warning. if [ -n "$port" ]; then # check that the port number is a legitimate port number (> 0, < 65536) [ "$port" -gt 0 ] && [ "$port" -lt 65536 ] || \ failure "The given port number should be greater than 0 and less than 65536. '$port' is not OK" # if the port number is given, and the scheme is in the services # database, check that the port number does *not* match the # default port. if (printf '%s' "$assigned_ports" | grep -q -F -x "$port" ) ; then failure $(printf "The scheme %s uses port number %d by default. You should leave off the port number if it is the default" "$scheme" "$port") fi fi } # fail if host key not present check_no_keys() { [ -s "$HOST_KEY_FILE" ] \ || failure "You don't appear to have a Monkeysphere host key on this server. Please run 'monkeysphere-host import-key' import a key." } # key input to functions, outputs full fingerprint of specified key if # found check_key_input() { local keyID="$1" # array of fingerprints local fprs=($(list_primary_fingerprints <"$HOST_KEY_FILE")) case ${#fprs[@]} in 0) failure "You don't appear to have any Monkeysphere host keys. Please run 'monkeysphere-host import-key' to import a key." ;; 1) : ;; *) if [ -z "$keyID" ] ; then failure "Your host keyring contains multiple keys. Please specify one to act on (see 'monkeysphere-host show-keys')." fi ;; esac printf '%s\n' "${fprs[@]}" | grep "${keyID}$" \ || failure "Host key '$keyID' not found." } # return 0 if user ID was found. # return 1 if user ID not found. check_key_userid() { local keyID="$1" local userID="$2" local tmpuidMatch # match to only "unknown" user IDs (host has no need for ultimate trust) tmpuidMatch="uid:-:$(echo $userID | gpg_escape)" # See whether the requested user ID is present gpg_host_list_keys "$keyID" | cut -f1,2,10 -d: | \ grep -q -x -F "$tmpuidMatch" 2>/dev/null } prompt_userid_exists() { local userID="$1" local gpgOut local fingerprint if gpgOut=$(gpg_host_list_keys "=${userID}" 2>/dev/null) ; then fingerprint=$(echo "$gpgOut" | awk -F: '/^fpr:/{ if (ok) { print $10 } ; ok=0 } /^pub:/{ ok=1 }') if [ "$PROMPT" != "false" ] ; then printf "Service name '%s' is already being used by key '%s'.\nAre you sure you want to use it again? (y/N) " "$userID" "$fingerprint" >&2 read OK; OK=${OK:=N} if [ "${OK/y/Y}" != 'Y' ] ; then failure "Service name not added." fi else log info "Key '%s' is already using the service name '%s'." "$fingerprint" "$userID" >&2 fi fi } # run command looped over keys multi_key() { local cmd="$1" shift local keys=$@ local i=0 local key check_no_keys log debug "listing primary fingerprints from $HOST_KEY_FILE" local fprs=($(list_primary_fingerprints <"$HOST_KEY_FILE")) log debug "obtained the following fingerprints: $fprs" if [[ -z "$1" || "$1" == '--all' ]] ; then log debug "publishing all keys" keys="${fprs[@]}" fi log debug "using keys: $keys" for key in $keys ; do if (( i++ > 0 )) ; then printf "\n" fi log debug "invoking $cmd $key" "$cmd" "$key" done } # show info about the a key show_key() { local id="$1" local GNUPGHOME local fingerprint local revokers # tmp gpghome dir export GNUPGHOME=$(msmktempdir) cleanup() { if type gpgconf &>/dev/null; then gpgconf --kill gpg-agent fi rm -rf "$GNUPGHOME" } # trap to remove tmp dir if break trap cleanup EXIT # import the host key into the tmp dir gpg --quiet --import <"$HOST_KEY_FILE" # get the gpg fingerprint if gpg --quiet --list-keys \ --with-colons --with-fingerprint "$id" \ | awk -F: '/^fpr:/{ if (ok) { print $10 } ; ok=0 } /^pub:/{ ok=1 }' > "$GNUPGHOME"/fingerprint ; then fingerprint=$(cat "$GNUPGHOME"/fingerprint) else failure "ID '$id' not found." fi # list the host key info # FIXME: make no-show-keyring work so we don't have to do the grep'ing # FIXME: can we show uid validity somehow? gpg --list-keys --list-options show-unusable-uids "$fingerprint" 2>/dev/null \ | egrep -v "^${GNUPGHOME}/pubring.(gpg|kbx)$" \ | egrep -v '^-+$' \ | grep -v '^$' # list revokers, if there are any revokers=$(gpg --list-keys --with-colons --fixed-list-mode "$fingerprint" \ | awk -F: '/^rvk:/{ print $10 }' ) if [ "$revokers" ] ; then echo "The following keys are allowed to revoke this host key:" for key in $revokers ; do echo "revoker: $key" done fi # list the pgp fingerprint echo "OpenPGP fingerprint: $fingerprint" # list the ssh fingerprint printf "ssh fingerprint: %s\n" \ "$(gpg --export-ssh-key "0x${fingerprint}!" 2>/dev/null | cut -f1,2 -d' ' | sed 's/$/ ./' | ssh-keygen -l -f -)" # remove the tmp file trap - EXIT cleanup } ######################################################################## # MAIN ######################################################################## # load configuration file [ -e ${MONKEYSPHERE_HOST_CONFIG:="${SYSCONFIGDIR}/monkeysphere-host.conf"} ] \ && . "$MONKEYSPHERE_HOST_CONFIG" # set empty config variable with ones from the environment, or with # defaults LOG_LEVEL=${MONKEYSPHERE_LOG_LEVEL:=$LOG_LEVEL} KEYSERVER=${MONKEYSPHERE_KEYSERVER:=$KEYSERVER} log debug "using keyserver: $KEYSERVER" CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=$CHECK_KEYSERVER} MONKEYSPHERE_USER=${MONKEYSPHERE_MONKEYSPHERE_USER:=$MONKEYSPHERE_USER} MONKEYSPHERE_GROUP=$(get_primary_group "$MONKEYSPHERE_USER") PROMPT=${MONKEYSPHERE_PROMPT:=$PROMPT} # other variables GNUPGHOME_HOST=${MONKEYSPHERE_GNUPGHOME_HOST:="${MHDATADIR}"} LOG_PREFIX=${MONKEYSPHERE_LOG_PREFIX:='ms: '} # export variables needed for invoking command under monkeysphere user export DATE export LOG_LEVEL export KEYSERVER export CHECK_KEYSERVER export MONKEYSPHERE_USER export MONKEYSPHERE_GROUP export PROMPT export GNUPGHOME_HOST export GNUPGHOME export HOST_FINGERPRINT export LOG_PREFIX if [ "$#" -eq 0 ] ; then usage failure "Please supply a subcommand." fi # get subcommand COMMAND="$1" shift case $COMMAND in 'import-key'|'import'|'i') source "${MHSHAREDIR}/import_key" import_key "$@" ;; 'show-keys'|'show-key'|'show'|'s') multi_key show_key "$@" ;; 'set-expire'|'extend-key'|'extend'|'e') source "${MHSHAREDIR}/set_expire" set_expire "$@" ;; 'add-servicename'|'add-hostname'|'add-name'|'n+') source "${MHSHAREDIR}/add_name" add_name "$@" ;; 'revoke-servicename'|'revoke-hostname'|'revoke-name'|'n-') source "${MHSHAREDIR}/revoke_name" revoke_name "$@" ;; 'add-revoker'|'r+') source "${MHSHAREDIR}/add_revoker" add_revoker "$@" ;; 'revoke-key') source "${MHSHAREDIR}/revoke_key" revoke_key "$@" ;; 'publish-keys'|'publish-key'|'publish'|'p') source "${MHSHAREDIR}/publish_key" multi_key publish_key "$@" ;; 'diagnostics'|'d') source "${MHSHAREDIR}/diagnostics" diagnostics ;; 'update-pgp-pub-file') update_pgp_pub_file ;; 'version'|'--version'|'v') version ;; '--help'|'help'|'-h'|'h'|'?') usage ;; *) failure "Unknown command: '$COMMAND' Try '$PGRM help' for usage." ;; esac monkeysphere-0.43/src/openpgp2ssh000077700000000000000000000000001342216672300220212share/keytransustar00rootroot00000000000000monkeysphere-0.43/src/pem2openpgp000077700000000000000000000000001342216672300220052share/keytransustar00rootroot00000000000000monkeysphere-0.43/src/share/000077500000000000000000000000001342216672300160505ustar00rootroot00000000000000monkeysphere-0.43/src/share/checkperms000077500000000000000000000062121342216672300201230ustar00rootroot00000000000000#!/usr/bin/perl -T # checkperms: ensure as best we can that a given file can only be # modified by the given user (or the superuser, naturally). This # means checking file ownership and permissions all the way back to # the root directory. Pass the file by its absolute path. # example invocation: # checkperms dkg /home/dkg/.monkeysphere/authorized_user_ids # return values: zero if we believe the file and path can only be # modified by the user. non-zero otherwise. # see StrictModes in sshd_config(5) (and its implementation in # OpenSSH's secure_filename() in auth.c) for the initial # inspiration/rationale for this code. # Author: # Daniel Kahn Gillmor # Started on: 2009-07-31 11:10:16-0400 # License: GPL v3 or later use strict; use Cwd qw(realpath); # found in debian in perl-base use File::stat; # found in debian in perl-modules use User::pwent; # found in debian in perl-modules use Fcntl qw(:mode); # for S_IS* functions (in perl-base) use File::Basename; # for dirname (in perl-modules) my $username = shift; my $path = shift; defined($username) or die "You must pass a username and an absolute path.\n"; defined($path) or die "You must pass a username and an absolute path.\n"; my $pw = getpwnam($username) or die "no such user $username\n"; $path =~ m#^/# or die "path was not absolute (did not start with /)\n"; sub mslog { my $level = shift; # FIXME: check and compare the log level if ($ENV{LOG_LEVEL} eq 'DEBUG') { my $format = shift; my $out = sprintf($format, @_); $out =~ s/^/$ENV{LOG_PREFIX}/ ; printf STDERR "%s", $out; } } ## return undef if permissions are OK. otherwise return an error string sub permissions_ok { my $user = shift; my $path = shift; # if we can't even stat the path, the permissions are not ok: my $stat = lstat($path) or return "cannot stat '$path'"; while (S_ISLNK($stat->mode)) { my $newpath = realpath($path) or return "cannot trace symlink '$path'"; mslog('DEBUG', "tracing link %s to %s\n", $path, $newpath); $path = $newpath; $stat = lstat($path) or return "cannot stat '$path'"; } mslog('DEBUG', "checking '%s'\n", $path); if (($stat->uid != $user->uid) && ($stat->uid != 0)) { return sprintf("improper ownership on '%s': owner ID %d is neither %s (ID %d) nor the superuser", $path, $stat->uid, $user->name, $user->uid); } if ($stat->mode & S_IWGRP) { return sprintf("improper group writability on '%s'", $path); } if ($stat->mode & S_IWOTH) { return sprintf("improper other writability on '%s'", $path); } # see the rationalization in secure_filename() in auth.c in the # OpenSSH sources for an explanation of this bailout (see also # monkeysphere #675): if ($path eq $user->dir) { mslog('DEBUG', "stopping at %s's home directory '%s'\n", $user->name, $path); return undef; } my $nextlevel = dirname($path); if ($path eq $nextlevel) { # we bottom out at the root (/ in UNIX) return undef; } return permissions_ok($user, $nextlevel); } my $err = permissions_ok($pw, $path); if (defined($err)) { printf(STDERR "%s%s\n", $ENV{LOG_PREFIX}, $err); exit(1); } else { exit(0); } monkeysphere-0.43/src/share/common000066400000000000000000000660361342216672300172760ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Shared sh functions for the monkeysphere # # Written by # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # Copyright 2008-2019, released under the GPL, version 3 or later # all-caps variables are meant to be user supplied (ie. from config # file) and are considered global ######################################################################## ### UTILITY FUNCTIONS # output version info version() { cat "${SYSSHAREDIR}/VERSION" } # failure function. exits with code 255, unless specified otherwise. failure() { [ "$1" ] && echo "$1" >&2 exit ${2:-'255'} } # write output to stderr based on specified LOG_LEVEL the first # parameter is the priority of the output, and everything else is what # is echoed to stderr. If there is nothing else, then output comes # from stdin, and is not prefaced by log prefix. log() { local priority local level local output local alllevels local found= local written= # don't include SILENT in alllevels: it's handled separately # list in decreasing verbosity (all caps). # separate with $IFS explicitly, since we do some fancy footwork # elsewhere. alllevels="DEBUG${IFS}VERBOSE${IFS}INFO${IFS}ERROR" # translate lowers to uppers in global log level LOG_LEVEL=${LOG_LEVEL^^} # just go ahead and return if the log level is silent if [ "$LOG_LEVEL" = 'SILENT' ] ; then if [ ! "$2" ] ; then cat >/dev/null fi return fi for level in $alllevels ; do if [ "$LOG_LEVEL" = "$level" ] ; then found=true fi done if [ -z "$found" ] ; then # default to INFO: LOG_LEVEL=INFO fi # get priority from first parameter, translating all lower to # uppers priority=${1^^} shift # scan over available levels for level in $alllevels ; do # output if the log level matches, set output to true # this will output for all subsequent loops as well. if [ "$LOG_LEVEL" = "$level" ] ; then output=true fi if [ "$priority" = "$level" -a "$output" = 'true' ] ; then if [ "$1" ] ; then echo "$@" else cat fi | sed 's/^/'"${LOG_PREFIX}"'/' >&2 written=true fi done if [ "$written" != 'true' -a ! "$1" ]; then cat >/dev/null fi } # run command as monkeysphere user run_as_monkeysphere_user() { # our main goal here is to run the given command as the the # monkeysphere user, but without prompting for any sort of # authentication. If this is not possible, we should just fail. # # A simple command and its arguments are expected. Shell # expressions are not supported. If they are required, they may # be executed with 'bash -c ""'. case $(id -un) in # if monkeysphere user, run the command as a subshell "$MONKEYSPHERE_USER") ( "$@" ) ;; # if root, run command as monkeysphere user 'root') # requote arguments using bash builtin feature (see "help printf"): runuser -u "$MONKEYSPHERE_USER" -- "$@" ;; # otherwise, fail *) log error "non-privileged user." ;; esac } # cut out all comments(#) and blank lines from standard input meat() { grep -v -e "^[[:space:]]*#" -e '^$' "$1" } # cut a specified line from standard input cutline() { head --line="$1" "$2" | tail -1 } # make a temporary directory msmktempdir() { mktemp -d -- "${TMPDIR:-/tmp}/monkeysphere.XXXXXXXXXX" } # make a temporary file msmktempfile() { mktemp -- "${TMPDIR:-/tmp}/monkeysphere.XXXXXXXXXX" } # this is a wrapper for doing lock functions. # # it lets us depend on either lockfile-progs (preferred) or procmail's # lockfile, and should lock() { local use_lockfileprogs=true local action="$1" local file="$2" if ! ( type lockfile-create &>/dev/null ) ; then if ! ( type lockfile &>/dev/null ); then failure "Neither lockfile-create nor lockfile are in the path!" fi use_lockfileprogs= fi case "$action" in create) if [ -n "$use_lockfileprogs" ] ; then lockfile-create "$file" || failure "unable to lock '$file'" else lockfile -r 20 "${file}.lock" || failure "unable to lock '$file'" fi log debug "lock created on '$file'." ;; touch) if [ -n "$use_lockfileprogs" ] ; then lockfile-touch --oneshot "$file" else : Nothing to do here fi log debug "lock touched on '$file'." ;; remove) if [ -n "$use_lockfileprogs" ] ; then lockfile-remove "$file" else rm -f "${file}.lock" fi log debug "lock removed on '$file'." ;; *) failure "bad argument for lock subfunction '$action'" esac } # for portability, between gnu date and BSD date. # arguments should be: number longunits format # e.g. advance_date 20 seconds +%F advance_date() { local gnutry local number="$1" local longunits="$2" local format="$3" local shortunits # try things the GNU way first if date -d "$number $longunits" "$format" &>/dev/null; then date -d "$number $longunits" "$format" else # otherwise, convert to (a limited version of) BSD date syntax: case "$longunits" in years) shortunits=y ;; months) shortunits=m ;; weeks) shortunits=w ;; days) shortunits=d ;; hours) shortunits=H ;; minutes) shortunits=M ;; seconds) shortunits=S ;; *) # this is a longshot, and will likely fail; oh well. shortunits="$longunits" esac date "-v+${number}${shortunits}" "$format" fi } print_date_from_seconds_since_the_epoch() { local seconds="$1" local gnutry if [ -z "$seconds" ] || [[ "$seconds" =~ [^0-9] ]]; then # not a decimal number, don't bother trying to pass it to date printf "\n" else if ! date '+%F %T' -d @"${seconds}" 2>/dev/null ; then # try it the BSD date way: date -r "${seconds}" '+%F %T' fi fi } # check that characters are in a string (in an AND fashion). # used for checking key capability # check_capability capability a [b...] check_capability() { local usage local capcheck usage="$1" shift 1 for capcheck ; do if echo "$usage" | grep -q -v "$capcheck" ; then return 1 fi done return 0 } # hash of a file file_hash() { if type md5sum &>/dev/null ; then md5sum "$1" elif type md5 &>/dev/null ; then md5 "$1" else failure "Neither md5sum nor md5 are in the path!" fi } # convert escaped characters in pipeline from gpg output back into # original character # FIXME: undo all escape character translation in with-colons gpg # output gpg_unescape() { sed 's/\\x3a/:/g' } # convert nasty chars into gpg-friendly form in pipeline # FIXME: escape everything, not just colons! gpg_escape() { sed 's/:/\\x3a/g' } # prompt for GPG-formatted expiration, and emit result on stdout get_gpg_expiration() { local keyExpire keyExpire="$1" if [ -z "$keyExpire" -a "$PROMPT" != 'false' ]; then cat >&2 < = key expires in n days w = key expires in n weeks m = key expires in n months y = key expires in n years EOF while [ -z "$keyExpire" ] ; do printf "Key is valid for? (0) " >&2 read keyExpire if ! test_gpg_expire ${keyExpire:=0} ; then echo "invalid value" >&2 unset keyExpire fi done elif ! test_gpg_expire "$keyExpire" ; then failure "invalid key expiration value '$keyExpire'." fi echo "$keyExpire" } passphrase_prompt() { local prompt="$1" local fifo="$2" local PASS if [ "$DISPLAY" ] && type "${SSH_ASKPASS:-ssh-askpass}" >/dev/null 2>/dev/null; then printf 'Launching "%s"\n' "${SSH_ASKPASS:-ssh-askpass}" | log info printf '(with prompt "%s")\n' "$prompt" | log debug "${SSH_ASKPASS:-ssh-askpass}" "$prompt" > "$fifo" else read -s -p "$prompt" PASS # Uses the builtin echo, so should not put the passphrase into # the process table. I think. --dkg echo "$PASS" > "$fifo" fi } # remove all lines with specified string from specified file remove_line() { local file local lines local tempfile file="$1" shift if [ ! -e "$file" ] ; then return 1 fi if (($# == 1)) ; then lines=$(grep -F "$1" "$file") || true else lines=$(grep -F "$1" "$file" | grep -F "$2") || true fi # if the string was found, remove it if [ "$lines" ] ; then log debug "removing matching key lines..." tempfile=$(mktemp "${file}.XXXXXXX") || \ failure "Unable to make temp file '${file}.XXXXXXX'" grep -v -x -F "$lines" "$file" >"$tempfile" || : mv -f "$tempfile" "$file" fi } # remove all lines with MonkeySphere strings from stdin remove_monkeysphere_lines() { egrep -v ' MonkeySphere[[:digit:]]{4}(-[[:digit:]]{2}){2}T[[:digit:]]{2}(:[[:digit:]]{2}){2} ' } # translate ssh-style path variables %h and %u translate_ssh_variables() { local uname local home uname="$1" path="$2" # get the user's home directory userHome=$(get_homedir "$uname") # translate '%u' to user name path=${path/\%u/"$uname"} # translate '%h' to user home directory path=${path/\%h/"$userHome"} echo "$path" } # test that a string to conforms to GPG's expiration format test_gpg_expire() { echo "$1" | egrep -q "^[0-9]+[mwy]?$" } # touch a key file if it doesn't exist, including creating needed # directories with correct permissions touch_key_file_or_fail() { local keyFile="$1" local newUmask if [ ! -f "$keyFile" ]; then # make sure to create files and directories with the # appropriate write bits turned off: newUmask=$(printf "%04o" $(( 0$(umask) | 0022 )) ) [ -d $(dirname "$keyFile") ] \ || (umask "$newUmask" && mkdir -p -m 0700 $(dirname "$keyFile") ) \ || failure "Could not create path to $keyFile" # make sure to create this file with the appropriate bits turned off: (umask "$newUmask" && touch "$keyFile") \ || failure "Unable to create $keyFile" fi } # check that a file is properly owned, and that all it's parent # directories are not group/other writable check_key_file_permissions() { local uname local path uname="$1" path="$2" if [ "$STRICT_MODES" = 'false' ] ; then log debug "skipping path permission check for '$path' because STRICT_MODES is false..." return 0 fi log debug "checking path permission '$path'..." "${SYSSHAREDIR}/checkperms" "$uname" "$path" } # return a list of all users on the system list_users() { if type getent &>/dev/null ; then # for linux and FreeBSD systems getent passwd | cut -d: -f1 elif type dscl &>/dev/null ; then # for Darwin systems dscl localhost -list /Search/Users else failure "Neither getent or dscl is in the path! Could not determine list of users." fi } # take one argument, a service name. in response, print a series of # lines, each with a unique numeric port number that might be # associated with that service name. (e.g. in: "https", out: "443") # if nothing is found, print nothing, and return 0. # # return 1 if there was an error in the search somehow get_port_for_service() { [[ "$1" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ ]] || \ failure $(printf "This is not a valid service name: '%s'" "$1") if type getent &>/dev/null ; then # for linux and FreeBSD systems (getent returns 2 if not found, 0 on success, 1 or 3 on various failures) (getent services "$service" || if [ "$?" -eq 2 ] ; then true ; else false; fi) | awk '{ print $2 }' | cut -f1 -d/ | sort -u elif [ -r /etc/services ] ; then # fall back to /etc/services for systems that don't have getent (MacOS?) # FIXME: doesn't handle aliases like "null" (or "http"?), which don't show up at the beginning of the line. awk $(printf '/^%s[[:space:]]/{ print $2 }' "$1") /etc/services | cut -f1 -d/ | sort -u else return 1 fi } # return the path to the home directory of a user get_homedir() { local uname=${1:-`whoami`} eval "echo ~${uname}" } # return the primary group of a user get_primary_group() { local uname=${1:-`whoami`} groups "$uname" | sed 's/^..* : //' | awk '{ print $1 }' } ### CONVERSION UTILITIES # output the ssh key for a given key ID gpg2ssh() { local keyID keyID="$1" gpg --export-ssh-key "0x${keyID}!" 2>/dev/null | cut -f1,2 -d' ' } # output known_hosts line from ssh key ssh2known_hosts() { local host local port local key # FIXME this does not properly deal with IPv6 hosts using the # standard port (because it's unclear whether their final # colon-delimited address section is a port number or an address # string) host=${1%:*} port=${1##*:} key="$2" # specify the host and port properly for new ssh known_hosts # format if [ "$port" != "$host" ] ; then host="[${host}]:${port}" fi # hash if specified if [ "$HASH_KNOWN_HOSTS" = 'true' ] ; then if (type ssh-keygen >/dev/null) ; then log verbose "hashing known_hosts line" # FIXME: this is really hackish cause # ssh-keygen won't hash from stdin to # stdout tmpfile=$(mktemp ${TMPDIR:-/tmp}/tmp.XXXXXXXXXX) printf "%s %s MonkeySphere%s\n" "$host" "$key" "$DATE" \ > "$tmpfile" ssh-keygen -H -f "$tmpfile" 2>/dev/null if [[ "$keyFile" == '-' ]] ; then cat "$tmpfile" else cat "$tmpfile" >> "$keyFile" fi rm -f "$tmpfile" "${tmpfile}.old" # FIXME: we could do this without needing ssh-keygen. # hashed known_hosts looks like: |1|X|Y where 1 means SHA1 # (nothing else is defined in openssh sources), X is the # salt (same length as the digest output), base64-encoded, # and Y is the digested hostname (also base64-encoded). # see hostfile.{c,h} in openssh sources. else log error "Cannot hash known_hosts line as requested." fi else printf "%s %s MonkeySphere%s\n" "$host" "$key" "$DATE" fi } # output authorized_keys line from ssh key ssh2authorized_keys() { local userID="$1" local key="$2" if [[ "$AUTHORIZED_KEYS_OPTIONS" ]]; then printf "%s %s MonkeySphere%s %s\n" "$AUTHORIZED_KEYS_OPTIONS" "$key" "$DATE" "$userID" else printf "%s MonkeySphere%s %s\n" "$key" "$DATE" "$userID" fi } # convert key from gpg to ssh known_hosts format gpg2known_hosts() { local host local keyID local key host="$1" keyID="$2" key=$(gpg2ssh "$keyID") # NOTE: it seems that ssh-keygen -R removes all comment fields from # all lines in the known_hosts file. why? # NOTE: just in case, the COMMENT can be matched with the # following regexp: # '^MonkeySphere[[:digit:]]{4}(-[[:digit:]]{2}){2}T[[:digit:]]{2}(:[[:digit:]]{2}){2}$' printf "%s %s MonkeySphere%s\n" "$host" "$key" "$DATE" } # convert key from gpg to ssh authorized_keys format gpg2authorized_keys() { local userID local keyID local key userID="$1" keyID="$2" key=$(gpg2ssh "$keyID") # NOTE: just in case, the COMMENT can be matched with the # following regexp: # '^MonkeySphere[[:digit:]]{4}(-[[:digit:]]{2}){2}T[[:digit:]]{2}(:[[:digit:]]{2}){2}$' printf "%s MonkeySphere%s %s\n" "$key" "$DATE" "$userID" } ### GPG UTILITIES # script to determine if gpg version is equal to or greater than specified version is_gpg_version_greater_equal() { local gpgVersion=$(gpg --version | head -1 | awk '{ print $3 }') local latest=$(printf '%s\n%s\n' "$1" "$gpgVersion" \ | tr '.' ' ' | sort -g -k1 -k2 -k3 \ | tail -1 | tr ' ' '.') [[ "$gpgVersion" == "$latest" ]] } # retrieve all keys with given user id from keyserver gpg_fetch_userid() { local returnCode=0 local userID local foundkeyids if [ "$CHECK_KEYSERVER" != 'true' ] ; then return 0 fi userID="$1" log verbose " checking keyserver $KEYSERVER... " foundkeyids="$(echo | \ gpg --quiet --batch --with-colons \ --command-fd 0 --keyserver "$KEYSERVER" \ --search ="$userID" 2>/dev/null)" returnCode="$?" if [ "$returnCode" != 0 ] ; then log error "Failure ($returnCode) searching keyserver $KEYSERVER for user id '$userID'" else log debug " keyserver raw output: ----- $foundkeyids -----" foundkeyids="$(printf "%s" "$foundkeyids" | grep '^pub:' | cut -f2 -d: | sed 's/^/0x/')" log verbose " Found keyids on keyserver: $(printf "%s" "$foundkeyids" | tr '\n' ' ')" if [ -n "$foundkeyids" ]; then echo | gpg --quiet --batch --with-colons \ --command-fd 0 --keyserver "$KEYSERVER" \ --recv-keys $foundkeyids &>/dev/null returnCode="$?" if [ "$returnCode" != 0 ] ; then log error "Failure ($returnCode) receiving keyids ($foundkeyids) from keyserver $KEYSERVER" fi fi fi return "$returnCode" } ######################################################################## ### PROCESSING FUNCTIONS # userid and key policy checking # the following checks policy on the returned keys # - checks that full key has appropriate valididy (u|f) # - checks key has specified capability (REQUIRED_KEY_CAPABILITY) # - checks that requested user ID has appropriate validity # (see /usr/share/doc/gnupg/DETAILS.gz) # output is one line for every found key, in the following format: # # flag:sshKey # # "flag" is an acceptability flag, 0 = ok, 1 = bad # "sshKey" is the relevant OpenPGP key, in the form accepted by OpenSSH # # all log output must go to stderr, as stdout is used to pass the # flag:sshKey to the calling function. process_user_id() { local returnCode=0 local userID="$1" local requiredCapability local requiredPubCapability local gpgOut local type local validity local keyid local uidfpr local usage local keyOK local uidOK local lastKey local lastKeyOK local fingerprint # set the required key capability based on the mode requiredCapability=${REQUIRED_KEY_CAPABILITY:="a"} requiredPubCapability=${requiredCapability^^} # fetch the user ID if necessary/requested gpg_fetch_userid "$userID" # output gpg info for (exact) userid and store gpgOut=$(gpg --list-key --fixed-list-mode --with-colons \ --with-fingerprint --with-fingerprint \ ="$userID" 2>/dev/null) || returnCode="$?" # if the gpg query return code is not 0, return 1 if [ "$returnCode" -ne 0 ] ; then log verbose " no primary keys found." return 1 fi # loop over all lines in the gpg output and process. echo "$gpgOut" | cut -d: -f1,2,5,10,12 | \ while IFS=: read -r type validity keyid uidfpr usage ; do # process based on record type case $type in 'pub') # primary keys # new key, wipe the slate keyOK= uidOK= lastKey=pub lastKeyOK= fingerprint= log verbose " primary key found: $keyid" # if overall key is not valid, skip if [ "$validity" != 'u' -a "$validity" != 'f' ] ; then log debug " - unacceptable primary key validity ($validity)." continue fi # if overall key is disabled, skip if check_capability "$usage" 'D' ; then log debug " - key disabled." continue fi # if overall key capability is not ok, skip if ! check_capability "$usage" $requiredPubCapability ; then log debug " - unacceptable primary key capability ($usage)." continue fi # mark overall key as ok keyOK=true # mark primary key as ok if capability is ok if check_capability "$usage" $requiredCapability ; then lastKeyOK=true fi ;; 'uid') # user ids if [ "$lastKey" != pub ] ; then log verbose " ! got a user ID after a sub key?! user IDs should only follow primary keys!" continue fi # if an acceptable user ID was already found, skip if [ "$uidOK" = 'true' ] ; then continue fi # if the user ID does matches... if [ "$(echo "$uidfpr" | gpg_unescape)" = "$userID" ] ; then # and the user ID validity is ok if [ "$validity" = 'u' -o "$validity" = 'f' ] ; then # mark user ID acceptable uidOK=true else log debug " - unacceptable user ID validity ($validity)." fi else continue fi # output a line for the primary key # 0 = ok, 1 = bad if [ "$keyOK" -a "$uidOK" -a "$lastKeyOK" ] ; then log verbose " * acceptable primary key." if [ -z "$sshKey" ] ; then log verbose " ! primary key could not be translated (not RSA?)." else echo "0:${sshKey}" fi else log debug " - unacceptable primary key." if [ -z "$sshKey" ] ; then log debug " ! primary key could not be translated (not RSA?)." else echo "1:${sshKey}" fi fi ;; 'sub') # sub keys # unset acceptability of last key lastKey=sub lastKeyOK= fingerprint= # don't bother with sub keys if the primary key is not valid if [ "$keyOK" != true ] ; then continue fi # don't bother with sub keys if no user ID is acceptable: if [ "$uidOK" != true ] ; then continue fi # if sub key validity is not ok, skip if [ "$validity" != 'u' -a "$validity" != 'f' ] ; then log debug " - unacceptable sub key validity ($validity)." continue fi # if sub key capability is not ok, skip if ! check_capability "$usage" $requiredCapability ; then log debug " - unacceptable sub key capability ($usage)." continue fi # mark sub key as ok lastKeyOK=true ;; 'fpr') # key fingerprint fingerprint="$uidfpr" sshKey=$(gpg2ssh "$fingerprint") # if the last key was the pub key, skip if [ "$lastKey" = pub ] ; then continue fi # output a line for the sub key # 0 = ok, 1 = bad if [ "$keyOK" -a "$uidOK" -a "$lastKeyOK" ] ; then log verbose " * acceptable sub key." if [ -z "$sshKey" ] ; then log error " ! sub key could not be translated (not RSA?)." else echo "0:${sshKey}" fi else log debug " - unacceptable sub key." if [ -z "$sshKey" ] ; then log debug " ! sub key could not be translated (not RSA?)." else echo "1:${sshKey}" fi fi ;; esac done | sort -t: -k1 -n -r # NOTE: this last sort is important so that the "good" keys (key # flag '0') come last. This is so that they take precedence when # being processed in the key files over "bad" keys (key flag '1') } process_keys_for_file() { local keyFile="$1" local userID="$2" local host local ok local sshKey local keyLine log verbose "processing: $userID" log debug "key file: $keyFile" IFS=$'\n' for line in $(process_user_id "$userID") ; do ok=${line%%:*} sshKey=${line#*:} if [ -z "$sshKey" ] ; then continue fi # remove the old key line if [[ "$keyFile" != '-' ]] ; then case "$FILE_TYPE" in ('authorized_keys') remove_line "$keyFile" "$sshKey" ;; ('known_hosts') host=${userID#ssh://} if [[ "${host}" == *:* ]]; then host="[${host%:*}]:${host##*:}" fi remove_line "$keyFile" "$host" "$sshKey" ;; esac fi ((++KEYS_PROCESSED)) # if key OK, add new key line if [ "$ok" -eq '0' ] ; then case "$FILE_TYPE" in ('raw') keyLine="$sshKey" ;; ('authorized_keys') keyLine=$(ssh2authorized_keys "$userID" "$sshKey") ;; ('known_hosts') host=${userID#ssh://} keyLine=$(ssh2known_hosts "$host" "$sshKey") ;; esac echo "key line: $keyLine" | log debug if [[ "$keyFile" == '-' ]] ; then echo "$keyLine" else log debug "adding key line to file..." echo "$keyLine" >>"$keyFile" fi ((++KEYS_VALID)) fi done log debug "KEYS_PROCESSED=$KEYS_PROCESSED" log debug "KEYS_VALID=$KEYS_VALID" } # process an authorized_user_ids file on stdin for authorized_keys process_authorized_user_ids() { local authorizedKeys="$1" declare -i nline=0 local line declare -a userIDs declare -a koptions # extract user IDs from authorized_user_ids file IFS=$'\n' while read line ; do case "$line" in ("#"*|"") continue ;; (" "*|$'\t'*) if [[ -z ${koptions[${nline}]} ]]; then koptions[${nline}]=$(echo $line | sed 's/^[ ]*//;s/[ ]$//;') else koptions[${nline}]="${koptions[${nline}]},$(echo $line | sed 's/^[ ]*//;s/[ ]$//;')" fi ;; (*) ((++nline)) userIDs[${nline}]="$line" unset koptions[${nline}] || true ;; esac done for i in $(seq 1 $nline); do AUTHORIZED_KEYS_OPTIONS="${koptions[$i]}" FILE_TYPE='authorized_keys' process_keys_for_file "$authorizedKeys" "${userIDs[$i]}" || returnCode="$?" done } # takes a gpg key or keys on stdin, and outputs a list of # fingerprints, one per line: list_primary_fingerprints() { local fake=$(msmktempdir) cleanup() { if type gpgconf &>/dev/null; then GNUPGHOME="$fake" gpgconf --kill gpg-agent fi rm -rf "$fake" } trap cleanup EXIT GNUPGHOME="$fake" gpg --no-tty --quiet --import --ignore-time-conflict 2>/dev/null GNUPGHOME="$fake" gpg --with-colons --fingerprint --list-keys | \ awk -F: '/^fpr:/{ if (ok) { print $10 } ; ok=0 } /^pub:/{ ok=1 }' trap - EXIT cleanup } # takes an OpenPGP key or set of keys on stdin, a fingerprint or other # key identifier as $1, and outputs the gpg-formatted information for # the requested keys from the material on stdin get_cert_info() { local fake=$(msmktempdir) cleanup() { if type gpgconf &>/dev/null; then GNUPGHOME="$fake" gpgconf --kill gpg-agent fi rm -rf "$fake" } GNUPGHOME="$fake" gpg --no-tty --quiet --import --ignore-time-conflict 2>/dev/null GNUPGHOME="$fake" gpg --with-colons --fingerprint --fixed-list-mode --list-keys "$1" trap - EXIT cleanup } check_cruft_file() { local loc="$1" local version="$2" if [ -e "$loc" ] ; then printf "! The file '%s' is no longer used by\n monkeysphere (as of version %s), and can be removed.\n\n" "$loc" "$version" | log info fi } check_upgrade_dir() { local loc="$1" local version="$2" if [ -d "$loc" ] ; then printf "The presence of directory '%s' indicates that you have\nnot yet completed a monkeysphere upgrade.\nYou should probably run the following script:\n %s/transitions/%s\n\n" "$loc" "$SYSSHAREDIR" "$version" | log info fi } ## look for cruft from old versions of the monkeysphere, and notice if ## upgrades have not been run: report_cruft() { check_upgrade_dir "${SYSCONFIGDIR}/gnupg-host" 0.23 check_upgrade_dir "${SYSCONFIGDIR}/gnupg-authentication" 0.23 check_cruft_file "${SYSCONFIGDIR}/gnupg-authentication.conf" 0.23 check_cruft_file "${SYSCONFIGDIR}/gnupg-host.conf" 0.23 local found= for foo in "${SYSDATADIR}/backup-from-"*"-transition" ; do if [ -d "$foo" ] ; then printf "! %s\n" "$foo" | log info found=true fi done if [ "$found" ] ; then printf "The directories above are backups left over from a monkeysphere transition.\nThey may contain copies of sensitive data (host keys, certifier lists), but\nthey are no longer needed by monkeysphere.\nYou may remove them at any time.\n\n" | log info fi } monkeysphere-0.43/src/share/defaultenv000066400000000000000000000016511342216672300201330ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Shared sh variables for the monkeysphere # # Written by # Jameson Rollins # # Copyright 2009, released under the GPL, version 3 or later # managed directories SYSCONFIGDIR=${MONKEYSPHERE_SYSCONFIGDIR:-"__SYSCONFDIR_PREFIX__/etc/monkeysphere"} export SYSCONFIGDIR SYSDATADIR=${MONKEYSPHERE_SYSDATADIR:-"__SYSDATADIR_PREFIX__/monkeysphere"} export SYSDATADIR # default log level LOG_LEVEL="INFO" # default keyserver KEYSERVER="pool.sks-keyservers.net" # whether or not to check keyservers by default CHECK_KEYSERVER="true" # whether or not to care about extra write bits on sensitive files # like known_hosts, authorized_keys, and authorized_user_ids STRICT_MODES="true" # default monkeysphere user MONKEYSPHERE_USER="monkeysphere" # default about whether or not to prompt PROMPT="true" monkeysphere-0.43/src/share/keytrans000077500000000000000000001015331342216672300176410ustar00rootroot00000000000000#!/usr/bin/perl -T # keytrans: this is an RSA key translation utility; it is capable of # transforming RSA keys (both public keys and secret keys) between # several popular representations, including OpenPGP, PEM-encoded # PKCS#1 DER, and OpenSSH-style public key lines. # How it behaves depends on the name under which it is invoked. The # two implementations currently are: pem2openpgp and openpgp2ssh. # pem2openpgp: take a PEM-encoded RSA private-key on standard input, a # User ID as the first argument, and generate an OpenPGP secret key # and certificate from it. # WARNING: the secret key material *will* appear on stdout (albeit in # OpenPGP form) -- if you redirect stdout to a file, make sure the # permissions on that file are appropriately locked down! # Usage: # pem2openpgp 'ssh://'$(hostname -f) < /etc/ssh/ssh_host_rsa_key | gpg --import # openpgp2ssh: take a stream of OpenPGP packets containing public or # secret key material on standard input, and a Key ID (or fingerprint) # as the first argument. Find the matching key in the input stream, # and emit it on stdout in an OpenSSH-compatible format. If the input # key is an OpenPGP public key (either primary or subkey), the output # will be an OpenSSH single-line public key. If the input key is an # OpenPGP secret key, the output will be a PEM-encoded RSA key. # Example usage: # gpg --export-secret-subkeys --export-options export-reset-subkey-passwd $KEYID | \ # openpgp2ssh $KEYID | ssh-add /dev/stdin # Authors: # Jameson Rollins # Daniel Kahn Gillmor # Started on: 2009-01-07 02:01:19-0500 # License: GPL v3 or later (we may need to adjust this given that this # connects to OpenSSL via perl) use strict; use warnings; use File::Basename; use Crypt::OpenSSL::RSA; use Crypt::OpenSSL::Bignum; use Crypt::OpenSSL::Bignum::CTX; use Digest::SHA; use MIME::Base64; use POSIX; ## make sure all length() and substr() calls use bytes only: use bytes; my $old_format_packet_lengths = { one => 0, two => 1, four => 2, indeterminate => 3, }; # see RFC 4880 section 9.1 (ignoring deprecated algorithms for now) my $asym_algos = { rsa => 1, elgamal => 16, dsa => 17, }; # see RFC 4880 section 9.2 my $ciphers = { plaintext => 0, idea => 1, tripledes => 2, cast5 => 3, blowfish => 4, aes128 => 7, aes192 => 8, aes256 => 9, twofish => 10, }; # see RFC 4880 section 9.3 my $zips = { uncompressed => 0, zip => 1, zlib => 2, bzip2 => 3, }; # see RFC 4880 section 9.4 my $digests = { md5 => 1, sha1 => 2, ripemd160 => 3, sha256 => 8, sha384 => 9, sha512 => 10, sha224 => 11, }; # see RFC 4880 section 5.2.3.21 my $usage_flags = { certify => 0x01, sign => 0x02, encrypt_comms => 0x04, encrypt_storage => 0x08, encrypt => 0x0c, ## both comms and storage split => 0x10, # the private key is split via secret sharing authenticate => 0x20, shared => 0x80, # more than one person holds the entire private key }; # see RFC 4880 section 4.3 my $packet_types = { pubkey_enc_session => 1, sig => 2, symkey_enc_session => 3, onepass_sig => 4, seckey => 5, pubkey => 6, sec_subkey => 7, compressed_data => 8, symenc_data => 9, marker => 10, literal => 11, trust => 12, uid => 13, pub_subkey => 14, uat => 17, symenc_w_integrity => 18, mdc => 19, }; # see RFC 4880 section 5.2.1 my $sig_types = { binary_doc => 0x00, text_doc => 0x01, standalone => 0x02, generic_certification => 0x10, persona_certification => 0x11, casual_certification => 0x12, positive_certification => 0x13, subkey_binding => 0x18, primary_key_binding => 0x19, key_signature => 0x1f, key_revocation => 0x20, subkey_revocation => 0x28, certification_revocation => 0x30, timestamp => 0x40, thirdparty => 0x50, }; # see RFC 4880 section 5.2.3.23 my $revocation_reasons = { no_reason_specified => 0, key_superseded => 1, key_compromised => 2, key_retired => 3, user_id_no_longer_valid => 32, }; # see RFC 4880 section 5.2.3.1 my $subpacket_types = { sig_creation_time => 2, sig_expiration_time => 3, exportable => 4, trust_sig => 5, regex => 6, revocable => 7, key_expiration_time => 9, preferred_cipher => 11, revocation_key => 12, issuer => 16, notation => 20, preferred_digest => 21, preferred_compression => 22, keyserver_prefs => 23, preferred_keyserver => 24, primary_uid => 25, policy_uri => 26, usage_flags => 27, signers_uid => 28, revocation_reason => 29, features => 30, signature_target => 31, embedded_signature => 32, issuer_fpr => 33, }; # bitstring (see RFC 4880 section 5.2.3.24) my $features = { mdc => 0x01 }; # bitstring (see RFC 4880 5.2.3.17) my $keyserver_prefs = { nomodify => 0x80 }; ###### end lookup tables ###### # FIXME: if we want to be able to interpret openpgp data as well as # produce it, we need to produce key/value-swapped lookup tables as well. ########### Math/Utility Functions ############## # see the bottom of page 44 of RFC 4880 (https://tools.ietf.org/html/rfc4880#page-44) sub simple_checksum { my $bytes = shift; return unpack("%16C*",$bytes); } # calculate/print the fingerprint of an openssh-style keyblob: sub sshfpr { sshfpr_sha256(shift); } sub sshfpr_md5 { my $keyblob = shift; use Digest::MD5; return 'MD5:'.join(':', map({unpack("H*", $_)} split('', Digest::MD5::md5($keyblob)))); } sub sshfpr_sha256 { my $keyblob = shift; use Digest::SHA; return 'SHA256:'.Digest::SHA::sha256_base64($keyblob); } # calculate the multiplicative inverse of a mod b this is euclid's # extended algorithm. For more information see: # https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm the # arguments here should be Crypt::OpenSSL::Bignum objects. $a should # be the larger of the two values, and the two values should be # coprime. sub modular_multi_inverse { my $a = shift; my $b = shift; my $origdivisor = $b->copy(); my $ctx = Crypt::OpenSSL::Bignum::CTX->new(); my $x = Crypt::OpenSSL::Bignum->zero(); my $y = Crypt::OpenSSL::Bignum->one(); my $lastx = Crypt::OpenSSL::Bignum->one(); my $lasty = Crypt::OpenSSL::Bignum->zero(); my $finalquotient; my $finalremainder; while (! $b->is_zero()) { my ($quotient, $remainder) = $a->div($b, $ctx); $a = $b; $b = $remainder; my $temp = $x; $x = $lastx->sub($quotient->mul($x, $ctx)); $lastx = $temp; $temp = $y; $y = $lasty->sub($quotient->mul($y, $ctx)); $lasty = $temp; } if (!$a->is_one()) { die "did this math wrong.\n"; } # let's make sure that we return a positive value because RFC 4880, # section 3.2 only allows unsigned values: ($finalquotient, $finalremainder) = $lastx->add($origdivisor)->div($origdivisor, $ctx); return $finalremainder; } ############ OpenPGP formatting functions ############ # make an old-style packet out of the given packet type and body. # old-style (see RFC 4880 section 4.2) sub make_packet { my $type = shift; my $body = shift; my $options = shift; my $len = length($body); my $pseudolen = $len; # if the caller wants to use at least N octets of packet length, # pretend that we're using that many. if (defined $options && defined $options->{'packet_length'}) { $pseudolen = 2**($options->{'packet_length'} * 8) - 1; } if ($pseudolen < $len) { $pseudolen = $len; } my $lenbytes; my $lencode; if ($pseudolen < 2**8) { $lenbytes = $old_format_packet_lengths->{one}; $lencode = 'C'; } elsif ($pseudolen < 2**16) { $lenbytes = $old_format_packet_lengths->{two}; $lencode = 'n'; } elsif ($pseudolen < 2**31) { ## not testing against full 32 bits because i don't want to deal ## with potential overflow. $lenbytes = $old_format_packet_lengths->{four}; $lencode = 'N'; } else { ## what the hell do we do here? $lenbytes = $old_format_packet_lengths->{indeterminate}; $lencode = ''; } return pack('C'.$lencode, 0x80 + ($type * 4) + $lenbytes, $len). $body; } # takes a Crypt::OpenSSL::Bignum, returns it formatted as OpenPGP MPI # (RFC 4880 section 3.2) sub mpi_pack { my $num = shift; my $val = $num->to_bin(); my $mpilen = length($val)*8; # this is a kludgy way to get the number of significant bits in the # first byte: my $bitsinfirstbyte = length(sprintf("%b", ord($val))); $mpilen -= (8 - $bitsinfirstbyte); return pack('n', $mpilen).$val; } # takes a Crypt::OpenSSL::Bignum, returns an MPI packed in preparation # for an OpenSSH-style public key format. see: # https://marc.info/?l=openssh-unix-dev&m=121866301718839&w=2 sub openssh_mpi_pack { my $num = shift; my $val = $num->to_bin(); my $mpilen = length($val); my $ret = pack('N', $mpilen); # if the first bit of the leading byte is high, we should include a # 0 byte: if (ord($val) & 0x80) { $ret = pack('NC', $mpilen+1, 0); } return $ret.$val; } sub openssh_pubkey_pack { my $key = shift; my ($modulus, $exponent) = $key->get_key_parameters(); return openssh_mpi_pack(Crypt::OpenSSL::Bignum->new_from_bin("ssh-rsa")). openssh_mpi_pack($exponent). openssh_mpi_pack($modulus); } # pull an OpenPGP-specified MPI off of a given stream, returning it as # a Crypt::OpenSSL::Bignum. sub read_mpi { my $instr = shift; my $readtally = shift; my $bitlen; read($instr, $bitlen, 2) or die "could not read MPI length.\n"; $bitlen = unpack('n', $bitlen); $$readtally += 2; my $bytestoread = POSIX::floor(($bitlen + 7)/8); my $ret; read($instr, $ret, $bytestoread) or die "could not read MPI body.\n"; $$readtally += $bytestoread; return Crypt::OpenSSL::Bignum->new_from_bin($ret); } # FIXME: genericize these to accept either RSA or DSA keys: sub make_rsa_pub_key_body { my $key = shift; my $key_timestamp = shift; my ($n, $e) = $key->get_key_parameters(); return pack('CN', 4, $key_timestamp). pack('C', $asym_algos->{rsa}). mpi_pack($n). mpi_pack($e); } sub make_rsa_sec_key_body { my $key = shift; my $key_timestamp = shift; # we're not using $a and $b, but we need them to get to $c. my ($n, $e, $d, $p, $q) = $key->get_key_parameters(); my $c3 = modular_multi_inverse($p, $q); my $secret_material = mpi_pack($d). mpi_pack($p). mpi_pack($q). mpi_pack($c3); # according to Crypt::OpenSSL::RSA, the closest value we can get out # of get_key_parameters is 1/q mod p; but according to sec 5.5.3 of # RFC 4880, we're actually looking for u, the multiplicative inverse # of p, mod q. This is why we're calculating the value directly # with modular_multi_inverse. return pack('CN', 4, $key_timestamp). pack('C', $asym_algos->{rsa}). mpi_pack($n). mpi_pack($e). pack('C', 0). # seckey material is not encrypted -- see RFC 4880 sec 5.5.3 $secret_material. pack('n', simple_checksum($secret_material)); } # expects an RSA key (public or private) and a timestamp sub fingerprint { my $key = shift; my $key_timestamp = shift; my $rsabody = make_rsa_pub_key_body($key, $key_timestamp); return Digest::SHA::sha1(pack('Cn', 0x99, length($rsabody)).$rsabody); } # FIXME: handle DSA keys as well! sub makeselfsig { my $rsa = shift; my $uid = shift; my $args = shift; # strong assertion of identity is the default (for a self-sig): if (! defined $args->{certification_type}) { $args->{certification_type} = $sig_types->{positive_certification}; } if (! defined $args->{sig_timestamp}) { $args->{sig_timestamp} = time(); } my $key_timestamp = $args->{key_timestamp} + 0; # generate and aggregate subpackets: # key usage flags: my $flags = 0; if (! defined $args->{usage_flags}) { $flags = $usage_flags->{certify}; } else { my @ff = split(",", $args->{usage_flags}); foreach my $f (@ff) { if (! defined $usage_flags->{$f}) { die "No such flag $f"; } $flags |= $usage_flags->{$f}; } } my $usage_subpacket = pack('CCC', 2, $subpacket_types->{usage_flags}, $flags); # how should we determine how far off to set the expiration date? # default is no expiration. Specify the timestamp in seconds from the # key creation. my $expiration_subpacket = ''; if (defined $args->{expiration}) { my $expires_in = $args->{expiration} + 0; $expiration_subpacket = pack('CCN', 5, $subpacket_types->{key_expiration_time}, $expires_in); } # prefer AES-256, AES-192, AES-128, CAST5, 3DES: my $pref_sym_algos = pack('CCCCCCC', 6, $subpacket_types->{preferred_cipher}, $ciphers->{aes256}, $ciphers->{aes192}, $ciphers->{aes128}, $ciphers->{cast5}, $ciphers->{tripledes} ); # prefer SHA-512, SHA-384, SHA-256, SHA-224, RIPE-MD/160, SHA-1 my $pref_hash_algos = pack('CCCCCCCC', 7, $subpacket_types->{preferred_digest}, $digests->{sha512}, $digests->{sha384}, $digests->{sha256}, $digests->{sha224}, $digests->{ripemd160}, $digests->{sha1} ); # prefer ZLIB, BZip2, ZIP my $pref_zip_algos = pack('CCCCC', 4, $subpacket_types->{preferred_compression}, $zips->{zlib}, $zips->{bzip2}, $zips->{zip} ); # we support the MDC feature: my $feature_subpacket = pack('CCC', 2, $subpacket_types->{features}, $features->{mdc}); # keyserver preference: only owner modify (???): my $keyserver_pref = pack('CCC', 2, $subpacket_types->{keyserver_prefs}, $keyserver_prefs->{nomodify}); $args->{hashed_subpackets} = $usage_subpacket. $expiration_subpacket. $pref_sym_algos. $pref_hash_algos. $pref_zip_algos. $feature_subpacket. $keyserver_pref; return gensig($rsa, $uid, $args); } # FIXME: handle non-RSA keys # FIXME: this currently only makes self-sigs -- we should parameterize # it to make certifications over keys other than the issuer. sub gensig { my $rsa = shift; my $uid = shift; my $args = shift; # FIXME: allow signature creation using digests other than SHA256 $rsa->use_sha256_hash(); # see page 22 of RFC 4880 for why i think this is the right padding # choice to use: $rsa->use_pkcs1_padding(); if (! $rsa->check_key()) { die "key does not check\n"; } my $certtype = $args->{certification_type} + 0; my $version = pack('C', 4); my $sigtype = pack('C', $certtype); # RSA my $pubkey_algo = pack('C', $asym_algos->{rsa}); # SHA256 FIXME: allow signature creation using digests other than SHA256 my $hash_algo = pack('C', $digests->{sha256}); # FIXME: i'm worried about generating a bazillion new OpenPGP # certificates from the same key, which could easily happen if you run # this script more than once against the same key (because the # timestamps will differ). How can we prevent this? # this argument (if set) overrides the current time, to # be able to create a standard key. If we read the key from a file # instead of stdin, should we use the creation time on the file? my $sig_timestamp = ($args->{sig_timestamp} + 0); my $key_timestamp = ($args->{key_timestamp} + 0); if ($key_timestamp > $sig_timestamp) { die "key timestamp must not be later than signature timestamp\n"; } my $v4_fpr = fingerprint($rsa, $key_timestamp); my $creation_time_packet = pack('CCN', 5, $subpacket_types->{sig_creation_time}, $sig_timestamp); my $issuer_fpr_packet = pack('CCCa20', 22, $subpacket_types->{issuer_fpr}, 4, $v4_fpr); my $hashed_subs = $issuer_fpr_packet.$creation_time_packet.$args->{hashed_subpackets}; my $subpacket_octets = pack('n', length($hashed_subs)); my $sig_data_to_be_hashed = $version. $sigtype. $pubkey_algo. $hash_algo. $subpacket_octets. $hashed_subs; my $pubkey = make_rsa_pub_key_body($rsa, $key_timestamp); # this is for signing. it needs to be an old-style header with a # 2-packet octet count. my $key_data = make_packet($packet_types->{pubkey}, $pubkey, {'packet_length'=>2}); # take the last 8 bytes of the fingerprint as the keyid: my $keyid = substr($v4_fpr, 20 - 8, 8); # the v4 signature trailer is: # version number, literal 0xff, and then a 4-byte count of the # signature data itself. my $trailer = pack('CCN', 4, 0xff, length($sig_data_to_be_hashed)); my $uid_data = pack('CN', 0xb4, length($uid)). $uid; my $datatosign = $key_data. $uid_data. $sig_data_to_be_hashed. $trailer; # FIXME: handle signatures over digests other than SHA256: my $data_hash = Digest::SHA::sha256_hex($datatosign); my $issuer_packet = pack('CCa8', 9, $subpacket_types->{issuer}, $keyid); my $sig = Crypt::OpenSSL::Bignum->new_from_bin($rsa->sign($datatosign)); my $sig_body = $sig_data_to_be_hashed. pack('n', length($issuer_packet)). $issuer_packet. pack('n', hex(substr($data_hash, 0, 4))). mpi_pack($sig); return make_packet($packet_types->{sig}, $sig_body); } # FIXME: switch to passing the whole packet as the arg, instead of the # input stream. # FIXME: think about native perl representation of the packets instead. # Put a user ID into the $data sub finduid { my $data = shift; my $instr = shift; my $tag = shift; my $packetlen = shift; my $dummy; ($tag == $packet_types->{uid}) or die "This should not be called on anything but a User ID packet\n"; read($instr, $dummy, $packetlen); $data->{uid}->{$dummy} = {}; $data->{current}->{uid} = $dummy; } # find signatures associated with the given fingerprint and user ID. sub findsig { my $data = shift; my $instr = shift; my $tag = shift; my $packetlen = shift; ($tag == $packet_types->{sig}) or die "No calling findsig on anything other than a signature packet.\n"; my $dummy; my $readbytes = 0; read($instr, $dummy, $packetlen - $readbytes) or die "Could not read in this packet.\n"; if ((! defined $data->{key}) || (! defined $data->{uid}) || (! defined $data->{uid}->{$data->{target}->{uid}})) { # the user ID we are looking for has not been found yet. return; } if ( (!defined($data->{current_key_match})) || (! $data->{current_key_match})) { # this is not the key in question. return; } # the current ID is not what we're looking for: return if ($data->{current}->{uid} ne $data->{target}->{uid}); # just storing the raw signatures for the moment: push @{$data->{sigs}}, make_packet($packet_types->{sig}, $dummy); return; } # given an input stream and data, store the found key in data and # consume the rest of the stream corresponding to the packet. # data contains: (fpr: fingerprint to find, key: current best guess at key) sub findkey { my $data = shift; my $instr = shift; my $tag = shift; my $packetlen = shift; my $dummy; my $ver; my $readbytes = 0; read($instr, $ver, 1) or die "could not read key version\n"; $readbytes += 1; $ver = ord($ver); if ($ver != 4) { printf(STDERR "We only work with version 4 keys. This key appears to be version %s.\n", $ver); read($instr, $dummy, $packetlen - $readbytes) or die "Could not skip past this packet.\n"; return; } my $key_timestamp; read($instr, $key_timestamp, 4) or die "could not read key timestamp.\n"; $readbytes += 4; $key_timestamp = unpack('N', $key_timestamp); my $algo; read($instr, $algo, 1) or die "could not read key algorithm.\n"; $readbytes += 1; $algo = ord($algo); if ($algo != $asym_algos->{rsa}) { printf(STDERR "We only support RSA keys (this key used algorithm %d).\n", $algo); read($instr, $dummy, $packetlen - $readbytes) or die "Could not skip past this packet.\n"; return; } ## we have an RSA key. my $modulus = read_mpi($instr, \$readbytes); my $exponent = read_mpi($instr, \$readbytes); my $pubkey = Crypt::OpenSSL::RSA->new_key_from_parameters($modulus, $exponent); my $foundfpr = fingerprint($pubkey, $key_timestamp); my $foundfprstr = Crypt::OpenSSL::Bignum->new_from_bin($foundfpr)->to_hex(); # left-pad with 0's to bring up to full 40-char (160-bit) fingerprint: $foundfprstr = sprintf("%040s", $foundfprstr); $data->{current_key_match} = 0; # is this a match? if ((!defined($data->{target}->{fpr})) || (substr($foundfprstr, -1 * length($data->{target}->{fpr})) eq $data->{target}->{fpr})) { if (defined($data->{key})) { die "Found two matching keys.\n"; } $data->{key} = { 'rsa' => $pubkey, 'timestamp' => $key_timestamp }; $data->{current_key_match} = 1; } if ($tag != $packet_types->{seckey} && $tag != $packet_types->{sec_subkey}) { if ($readbytes < $packetlen) { read($instr, $dummy, $packetlen - $readbytes) or die "Could not skip past this packet.\n"; } return; } if (!$data->{current_key_match}) { # we don't think the public part of this key matches if ($readbytes < $packetlen) { read($instr, $dummy, $packetlen - $readbytes) or die "Could not skip past this packet.\n"; } return; } my $s2k; read($instr, $s2k, 1) or die "Could not read S2K octet.\n"; $readbytes += 1; $s2k = ord($s2k); if ($s2k != 0) { printf(STDERR "We cannot handle encrypted secret keys. Skipping!\n") ; read($instr, $dummy, $packetlen - $readbytes) or die "Could not skip past this packet.\n"; return; } # secret material is unencrypted # see https://tools.ietf.org/html/rfc4880#section-5.5.3 my $d = read_mpi($instr, \$readbytes); my $p = read_mpi($instr, \$readbytes); my $q = read_mpi($instr, \$readbytes); my $u = read_mpi($instr, \$readbytes); my $checksum; read($instr, $checksum, 2) or die "Could not read checksum of secret key material.\n"; $readbytes += 2; $checksum = unpack('n', $checksum); # FIXME: compare with the checksum! how? the data is # gone into the Crypt::OpenSSL::Bignum $data->{key}->{rsa} = Crypt::OpenSSL::RSA->new_key_from_parameters($modulus, $exponent, $d, $p, $q); $data->{key}->{rsa}->check_key() or die "Secret key is not a valid RSA key.\n"; if ($readbytes < $packetlen) { read($instr, $dummy, $packetlen - $readbytes) or die "Could not skip past this packet.\n"; } } sub openpgp2rsa { my $instr = shift; my $fpr = shift; if (defined $fpr) { if (length($fpr) < 8) { die "We need at least 8 hex digits of fingerprint.\n"; } $fpr = uc($fpr); } my $data = { target => { fpr => $fpr, }, }; my $subs = { $packet_types->{pubkey} => \&findkey, $packet_types->{pub_subkey} => \&findkey, $packet_types->{seckey} => \&findkey, $packet_types->{sec_subkey} => \&findkey }; packetwalk($instr, $subs, $data); return $data->{key}->{rsa}; } sub findkeyfprs { my $data = shift; my $instr = shift; my $tag = shift; my $packetlen = shift; findkey($data, $instr, $tag, $packetlen); if (defined($data->{key})) { if (defined($data->{key}->{rsa}) && defined($data->{key}->{timestamp})) { $data->{keys}->{fingerprint($data->{key}->{rsa}, $data->{key}->{timestamp})} = $data->{key}; } else { die "should have found some key here"; } undef($data->{key}); } }; sub getallprimarykeys { my $instr = shift; my $subs = { $packet_types->{pubkey} => \&findkeyfprs, $packet_types->{seckey} => \&findkeyfprs, }; my $data = {target => { } }; packetwalk($instr, $subs, $data); if (defined $data->{keys}) { return $data->{keys}; } else { return {}; } } sub adduserid { my $instr = shift; my $fpr = shift; my $uid = shift; my $args = shift; if ((! defined $fpr) || (length($fpr) < 8)) { die "We need at least 8 hex digits of fingerprint.\n"; } $fpr = uc($fpr); if (! defined $uid) { die "No User ID defined.\n"; } my $data = { target => { fpr => $fpr, uid => $uid, }, }; my $subs = { $packet_types->{seckey} => \&findkey, $packet_types->{uid} => \&finduid, $packet_types->{sig} => \&findsig, }; packetwalk($instr, $subs, $data); if ((! defined $data->{key}) || (! defined $data->{key}->{rsa}) || (! defined $data->{key}->{timestamp})) { die "The key requested was not found.\n" } if (defined $data->{uid}->{$uid} && defined $data->{sigs} && scalar(@{$data->{sigs}}) > 0 ) { die "The requested User ID '$uid' is already associated with this key.\n"; } $args->{key_timestamp} = $data->{key}->{timestamp}; return make_packet($packet_types->{pubkey}, make_rsa_pub_key_body($data->{key}->{rsa}, $data->{key}->{timestamp})). make_packet($packet_types->{uid}, $uid). makeselfsig($data->{key}->{rsa}, $uid, $args); } sub revokeuserid { my $instr = shift; my $fpr = shift; my $uid = shift; my $sigtime = shift; if ((! defined $fpr) || (length($fpr) < 8)) { die "We need at least 8 hex digits of fingerprint.\n"; } $fpr = uc($fpr); if (! defined $uid) { die "No User ID defined.\n"; } my $data = { target => { fpr => $fpr, uid => $uid, }, }; my $subs = { $packet_types->{seckey} => \&findkey, $packet_types->{uid} => \&finduid, $packet_types->{sig} => \&findsig, }; packetwalk($instr, $subs, $data); if ((! defined $data->{uid}) || (! defined $data->{uid}->{$uid})) { die "The User ID \"$uid\" is not associated with this key"; } if ((! defined $data->{key}) || (! defined $data->{key}->{rsa}) || (! defined $data->{key}->{timestamp})) { die "The key requested was not found." } my $revocation_reason = 'No longer using this hostname'; if (defined $data->{revocation_reason}) { $revocation_reason = $data->{revocation_reason}; } my $rev_reason_subpkt = prefixsubpacket(pack('CC', $subpacket_types->{revocation_reason}, $revocation_reasons->{user_id_no_longer_valid}). $revocation_reason); if (! defined $sigtime) { $sigtime = time(); } # what does a signature like this look like? my $args = { key_timestamp => $data->{key}->{timestamp}, sig_timestamp => $sigtime, certification_type => $sig_types->{certification_revocation}, hashed_subpackets => $rev_reason_subpkt, }; return make_packet($packet_types->{pubkey}, make_rsa_pub_key_body($data->{key}->{rsa}, $data->{key}->{timestamp})). make_packet($packet_types->{uid}, $uid). join('', @{$data->{sigs}}). gensig($data->{key}->{rsa}, $uid, $args); } # see 5.2.3.1 for tips on how to calculate the length of a subpacket: sub prefixsubpacket { my $subpacket = shift; my $len = length($subpacket); my $prefix; use bytes; if ($len < 192) { # one byte: $prefix = pack('C', $len); } elsif ($len < 16576) { my $in = $len - 192; my $second = $in%256; my $first = ($in - $second)>>8; $prefix = pack('CC', $first + 192, $second) } else { $prefix = pack('CN', 255, $len); } return $prefix.$subpacket; } sub packetwalk { my $instr = shift; my $subs = shift; my $data = shift; my $packettag; my $dummy; my $tag; while (! eof($instr)) { read($instr, $packettag, 1); $packettag = ord($packettag); my $packetlen; if ( ! (0x80 & $packettag)) { die "This is not an OpenPGP packet\n"; } if (0x40 & $packettag) { # this is a new-format packet. $tag = (0x3f & $packettag); my $nextlen = 0; read($instr, $nextlen, 1); $nextlen = ord($nextlen); if ($nextlen < 192) { $packetlen = $nextlen; } elsif ($nextlen < 224) { my $newoct; read($instr, $newoct, 1); $newoct = ord($newoct); $packetlen = (($nextlen - 192) << 8) + ($newoct) + 192; } elsif ($nextlen == 255) { read($instr, $nextlen, 4); $packetlen = unpack('N', $nextlen); } else { # packet length is undefined. } } else { # this is an old-format packet. my $lentype; $lentype = 0x03 & $packettag; $tag = ( 0x3c & $packettag ) >> 2; if ($lentype == 0) { read($instr, $packetlen, 1) or die "could not read packet length\n"; $packetlen = unpack('C', $packetlen); } elsif ($lentype == 1) { read($instr, $packetlen, 2) or die "could not read packet length\n"; $packetlen = unpack('n', $packetlen); } elsif ($lentype == 2) { read($instr, $packetlen, 4) or die "could not read packet length\n"; $packetlen = unpack('N', $packetlen); } else { # packet length is undefined. } } if (! defined($packetlen)) { die "Undefined packet lengths are not supported.\n"; } if (defined $subs->{$tag}) { $subs->{$tag}($data, $instr, $tag, $packetlen); } else { read($instr, $dummy, $packetlen) or die "Could not skip past this packet!\n"; } } return $data->{key}; } for (basename($0)) { if (/^pem2openpgp$/) { my $rsa; my $stdin; my $uid = shift; defined($uid) or die "You must specify a user ID string.\n"; # FIXME: fail if there is no given user ID; or should we default to # hostname_long() from Sys::Hostname::Long ? if (defined $ENV{PEM2OPENPGP_NEWKEY}) { my $rsa_keysize = ($ENV{PEM2OPENPGP_NEWKEY} + 0); $rsa_keysize >= 2048 or die "Generating new RSA key: PEM2OPENPGP_NEWKEY should be at least 2048\n"; $rsa = Crypt::OpenSSL::RSA->generate_key($rsa_keysize); } else { $stdin = do { local $/; # slurp! ; }; $rsa = Crypt::OpenSSL::RSA->new_private_key($stdin); } my $key_timestamp = $ENV{PEM2OPENPGP_KEY_TIMESTAMP}; my $sig_timestamp = $ENV{PEM2OPENPGP_TIMESTAMP}; $sig_timestamp = time() if (!defined $sig_timestamp); $key_timestamp = $sig_timestamp if (!defined $key_timestamp); print make_packet($packet_types->{seckey}, make_rsa_sec_key_body($rsa, $key_timestamp)). make_packet($packet_types->{uid}, $uid). makeselfsig($rsa, $uid, { sig_timestamp => $sig_timestamp, key_timestamp => $key_timestamp, expiration => $ENV{PEM2OPENPGP_EXPIRATION}, usage_flags => $ENV{PEM2OPENPGP_USAGE_FLAGS}, } ); } elsif (/^openpgp2ssh$/) { my $fpr = shift; my $instream; open($instream,'-'); binmode($instream, ":bytes"); my $key = openpgp2rsa($instream, $fpr); if (defined($key)) { if ($key->is_private()) { print $key->get_private_key_string(); } else { print "ssh-rsa ".encode_base64(openssh_pubkey_pack($key), '')."\n"; } } else { die "No matching key found.\n"; } } elsif (/^openpgp2pem$/) { my $fpr = shift; my $instream; open($instream,'-'); binmode($instream, ":bytes"); my $key = openpgp2rsa($instream, $fpr); if (defined($key)) { if ($key->is_private()) { print $key->get_private_key_string(); } else { print $key->get_public_key_string(); } } else { die "No matching key found.\n"; } } elsif (/^openpgp2spki$/) { my $fpr = shift; my $instream; open($instream,'-'); binmode($instream, ":bytes"); my $key = openpgp2rsa($instream, $fpr); if (defined($key)) { print $key->get_public_key_x509_string(); } else { die "No matching key found.\n"; } } elsif (/^keytrans$/) { # subcommands when keytrans is invoked directly are UNSUPPORTED, # UNDOCUMENTED, and WILL NOT BE MAINTAINED. my $subcommand = shift; for ($subcommand) { if (/^revokeuserid$/) { my $fpr = shift; my $uid = shift; my $instream; open($instream,'-'); binmode($instream, ":bytes"); my $revcert = revokeuserid($instream, $fpr, $uid, $ENV{PEM2OPENPGP_TIMESTAMP}); print $revcert; } elsif (/^adduserid$/) { my $fpr = shift; my $uid = shift; my $instream; open($instream,'-'); binmode($instream, ":bytes"); my $newuid = adduserid($instream, $fpr, $uid, { sig_timestamp => $ENV{PEM2OPENPGP_TIMESTAMP}, expiration => $ENV{PEM2OPENPGP_EXPIRATION}, usage_flags => $ENV{PEM2OPENPGP_USAGE_FLAGS}, }); print $newuid; } elsif (/^listfprs$/) { my $instream; open($instream,'-'); binmode($instream, ":bytes"); my $keys = getallprimarykeys($instream); printf("%s\n", join("\n", map { uc(unpack('H*', $_)) } keys(%{$keys}))); } elsif (/^sshfpr$/) { use MIME::Base64; while () { my ($dummy,$b64keyblob) = split(/ /, $_); printf("%s\n", sshfpr(decode_base64($b64keyblob))); } } elsif (/^openpgp2sshfpr$/) { my $fpr = shift; my $instream; open($instream,'-'); binmode($instream, ":bytes"); my $key = openpgp2rsa($instream, $fpr); if (defined($key)) { # openssh uses MD5 for key fingerprints: printf("%d %s %s\n", $key->size() * 8, # size() is in bytes -- we want bits sshfpr(openssh_pubkey_pack($key)), '(RSA)', # FIXME when we support other than RSA. ); } else { die "No matching key found.\n"; } } else { die "Unrecognized subcommand. keytrans subcommands are not a stable interface!\n"; } } } else { die "Unrecognized keytrans call.\n"; } } monkeysphere-0.43/src/share/m/000077500000000000000000000000001342216672300163045ustar00rootroot00000000000000monkeysphere-0.43/src/share/m/gen_subkey000066400000000000000000000037211342216672300203650ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere gen-subkey subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2019, and are all released under the GPL, # version 3 or later. # generate a subkey with the 'a' usage flags set gen_subkey(){ local keyLength local gpgSecOut local keyID local editCommands local fifoDir local keyType # get options while true ; do case "$1" in -l|--length) keyLength="$2" shift 2 ;; *) if [ "$(echo "$1" | cut -c 1)" = '-' ] ; then failure "Unknown option '$1'. Type '$PGRM help' for usage." fi break ;; esac done # check that the keyID is unique keyID=$(check_gpg_sec_key_id "$@") # check that an authentication subkey does not already exist check_gpg_authentication_subkey "$keyID" # since GnuPG 2.0.13, we use keyType = 8. keyType=8 # generate the list of commands that will be passed to edit-key editCommands="addkey $keyType S E A Q $keyLength 0 save" # setup the temp fifo dir for retrieving the key password log debug "creating password fifo..." fifoDir=$(msmktempdir) (umask 077 && mkfifo "$fifoDir/pass") # FIXME: are we adequately cleaning up any trailing gpg process here? trap "rm -rf $fifoDir; kill %% || true" EXIT echo "$editCommands" | gpg_user --batch --passphrase-fd 3 3< "$fifoDir/pass" --expert --command-fd 0 --edit-key "$keyID" & log debug "Prompting for passphrase" # FIXME: this needs to fail more gracefully if the passphrase is incorrect passphrase_prompt "Please enter your passphrase for $keyID: " "$fifoDir/pass" log info "Generating subkey. This may take a long time..." trap - EXIT rm -rf "$fifoDir" wait log verbose "done." } monkeysphere-0.43/src/share/m/import_subkey000066400000000000000000000042171342216672300211270ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere import-subkey subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2009, and are all released under the GPL, # version 3 or later. # import an existing ssh key as a gpg subkey ## 2009-02-20 00:49:11-0500: This is not implemented yet, because we ## don't currently have a good way to manipulate the user's OpenPGP ## secret key such that we could make a proper subkey binding ## signature. import_subkey() { local sshKeyFile local keyID local gpgSecOut local fifoDir # FIXME: implement! failure "import-subkey is not implemented yet. We welcome patches. Sorry!" sshKeyFile="$1" shift # check that key file specified if [ -z "$sshKeyFile" ] ; then failure "Must specify ssh key file to import, or specify '-' for stdin." fi # check that the keyID is unique keyID=$(check_gpg_sec_key_id "$@") # check that an authentication subkey does not already exist check_gpg_authentication_subkey "$keyID" # setup the temp fifo dir for retrieving the key password log debug "creating password fifo..." fifoDir=$(msmktempdir) trap "rm -rf $fifoDir" EXIT (umask 077 && mkfifo "$fifoDir/pass") # import ssh key to as authentication subkey if [ "$sshKeyFile" = '-' ] ; then log verbose "importing ssh key from stdin..." PEM2OPENPGP_USAGE_FLAGS=authenticate pem2openpgp "$userID" \ | gpg_user --batch --passphrase-fd 3 3< "$fifoDir/pass" --expert --command-fd 0 --import & else log verbose "importing ssh key from file '$sshKeyFile'..." PEM2OPENPGP_USAGE_FLAGS=authenticate pem2openpgp "$userID" <"$sshKeyFile" \ | gpg_user --batch --passphrase-fd 3 3< "$fifoDir/pass" --expert --command-fd 0 --import & fi # get the password if needed passphrase_prompt "Please enter your passphrase for $keyID: " "$fifoDir/pass" trap - EXIT rm -rf "$fifoDir" wait log verbose "done." } monkeysphere-0.43/src/share/m/keys_for_userid000066400000000000000000000007511342216672300214260ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere keys-for-userid subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2010, and are all released under the GPL, version # 3 or later. keys_for_userid() { FILE_TYPE='raw' process_keys_for_file - "$@" } monkeysphere-0.43/src/share/m/ssh_proxycommand000066400000000000000000000222351342216672300216300ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere ssh-proxycommand subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Daniel Kahn Gillmor # # They are Copyright 2008-2009, and are all released under the GPL, # version 3 or later. # This is meant to be run as an ssh ProxyCommand to initiate a # monkeysphere known_hosts update before an ssh connection to host is # established. Can be added to ~/.ssh/config as follows: # ProxyCommand monkeysphere ssh-proxycommand %h %p # the ssh proxycommand function itself ssh_proxycommand() { local connect='true' local HOST local PORT local HOSTP local URI if [[ "$1" == '--no-connect' ]] ; then connect='false' shift 1 fi HOST="$1" PORT="$2" if [ -z "$HOST" ] ; then log error "Host not specified." usage exit 255 fi if [ -z "$PORT" ] ; then PORT=22 fi # set the host URI if [ "$PORT" != '22' ] ; then HOSTP="${HOST}:${PORT}" else HOSTP="${HOST}" fi URI="ssh://${HOSTP}" # passed HOST/PORT/HOSTP/URI validate_monkeysphere # exec a netcat passthrough to host for the ssh connection if [[ "$connect" == 'true' ]] ; then if (type nc &>/dev/null); then exec nc "$HOST" "$PORT" elif (type socat &>/dev/null); then exec socat STDIO "TCP:$HOST:$PORT" else echo "Neither netcat nor socat found -- could not complete monkeysphere-ssh-proxycommand connection to $HOST:$PORT" >&2 exit 255 fi fi } validate_monkeysphere() { local hostKey # specify keyserver checking. the behavior of this proxy command # is intentionally different than that of running monkeyesphere # normally, and keyserver checking is intentionally done under # certain circumstances. This can be overridden by setting the # MONKEYSPHERE_CHECK_KEYSERVER environment variable, or by setting # the CHECK_KEYSERVER variable in the monkeysphere.conf file. # if the host is in the gpg keyring... if gpg_user --list-key ="${URI}" &>/dev/null ; then # do not check the keyserver CHECK_KEYSERVER=${CHECK_KEYSERVER:="false"} # if the host is NOT in the keyring... else # FIXME: what about system-wide known_hosts file (/etc/ssh/known_hosts)? if [ -r "$KNOWN_HOSTS" ]; then # look up the host key is found in the known_hosts file... if (type ssh-keygen &>/dev/null) ; then hostKey=$(ssh-keygen -F "$HOST" -f "$KNOWN_HOSTS" 2>/dev/null || true) else # FIXME: we're not dealing with digested known_hosts # if we don't have ssh-keygen # But we could do this without needing ssh-keygen. # hashed known_hosts looks like: |1|X|Y where 1 means # SHA1 (nothing else is defined in openssh sources), X # is the salt (same length as the digest output), # base64-encoded, and Y is the digested hostname (also # base64-encoded). # see hostfile.{c,h} in openssh sources. hostKey=$(cut -f1 -d\ < .ssh/known_hosts | tr ',' '\n' | grep -Fx -e "$HOST" || :) fi fi if [ "$hostKey" ] ; then # do not check the keyserver # FIXME: more nuanced checking should be done here to properly # take into consideration hosts that join monkeysphere by # converting an existing and known ssh key CHECK_KEYSERVER=${CHECK_KEYSERVER:="false"} # if the host key is not found in the known_hosts file... else # check the keyserver CHECK_KEYSERVER=${CHECK_KEYSERVER:="true"} fi fi # finally look in the MONKEYSPHERE_ environment variable for a # CHECK_KEYSERVER setting to override all else CHECK_KEYSERVER=${MONKEYSPHERE_CHECK_KEYSERVER:=$CHECK_KEYSERVER} declare -i KEYS_PROCESSED=0 declare -i KEYS_VALID=0 # update the known_hosts file for the host source "${MSHAREDIR}/update_known_hosts" update_known_hosts "$HOSTP" if ((KEYS_PROCESSED > 0)) && ((KEYS_VALID == 0)) ; then log debug "output ssh marginal ui..." output_no_valid_key fi # FIXME: what about the case where monkeysphere successfully finds # a valid key for the host and adds it to the known_hosts file, # but a different non-monkeysphere key for the host already exists # in the known_hosts, and it is this non-ms key that is offered by # the host? monkeysphere will succeed, and the ssh connection # will succeed, and the user will be left with the impression that # they are dealing with a OpenPGP/PKI host key when in fact they # are not. should we use ssh-keyscan to compare the keys first? } # output the key info, including the RSA fingerprint show_key_info() { local keyid="$1" local sshKeyGPGFile local sshFingerprint local gpgSigOut local otherUids # get the ssh key of the gpg key sshFingerprint=$(gpg2ssh "$keyid" | "$SYSSHAREDIR/keytrans" sshfpr) # get the sigs for the matching key gpgSigOut=$(gpg_user --check-sigs \ --list-options show-uid-validity \ "$keyid") echo | log info # output the sigs, but only those on the user ID # we are looking for echo "$gpgSigOut" | awk ' { if (match($0,"^pub")) { print; } if (match($0,"^uid")) { ok=0; } if (match($0,"^uid.*'$userID'$")) { ok=1; print; } if (ok) { if (match($0,"^sig")) { print; } } } ' # output ssh fingerprint cat </dev/null ) ; then # retrieve the ssh key being offered by the host sshKeyOffered=$(ssh-keyscan -t rsa -p "$PORT" "$HOST" 2>/dev/null \ | awk '{ print $2, $3 }') fi # get the gpg info for userid gpgOut=$(gpg_user --list-key --with-colons \ --with-fingerprint --with-fingerprint \ ="$userID" 2>/dev/null) # output header log info < 0)) ; then log info < # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2019, and are all released under the GPL, # version 3 or later. # try to add all authentication subkeys to the agent # FIXME: what if you only want to add one authentication subkey to the # agent? subkey_to_ssh_agent() { local sshaddresponse=0 local secretkeys local authsubkeys local workingdir local keysuccess=0 local subkey local publine local kname local awk_pgrm # if there's no agent running, don't bother: if [ -z "$SSH_AUTH_SOCK" ] || ! type ssh-add >/dev/null ; then failure "No ssh-agent available." fi # and if it looks like it's running, but we can't actually talk to # it, bail out: ssh-add -l >/dev/null || sshaddresponse="$?" if [ "$sshaddresponse" = "2" ]; then failure "Could not connect to ssh-agent" fi # if the MONKEYSPHERE_SUBKEYS_FOR_AGENT variable is set, use the # keys specified there if [ "$MONKEYSPHERE_SUBKEYS_FOR_AGENT" ] ; then authsubkeys="$MONKEYSPHERE_SUBKEYS_FOR_AGENT" # otherwise find all authentication-capable subkeys and use those else # get list of secret keys # (to work around bug https://bugs.g10code.com/gnupg/issue945): secretkeys=$(gpg_user --list-secret-keys --with-colons \ --fingerprint | \ awk -F: '/^fpr:/{ if (ok) { print "0x" $10 "!" } ; ok=0 } /^sec:/{ ok=1 }') if [ -z "$secretkeys" ]; then failure "You have no secret keys in your keyring! You might want to run 'gpg --gen-key'." fi # $2 regex means "is some kind of valid, or at least not invalid" # $12 ~ /a/ means "authentication-capable" # $4 == 1 means "RSA", $4 == 22 means "EdDSA" awk_pgrm=' /^sub:/{ if (($2 ~ /^[somfuq-]$/) && ($12 ~ /a/) && (($4 == 1) || ($4 == 22))) { ok = 1 }; }; /^fpr:/{ if (ok) { print $10 ; ok = 0; }; };' authsubkeys=$(gpg_user --list-keys --with-colons \ --fingerprint --fingerprint $secretkeys | \ awk -F: "$awk_pgrm" | sort -u) if [ -z "$authsubkeys" ]; then failure "no authentication-capable subkeys available. You might want to run 'monkeysphere gen-subkey'." fi fi workingdir=$(msmktempdir) trap "rm -rf $workingdir" EXIT umask 077 mkfifo "$workingdir/passphrase" # FIXME: we're currently allowing any other options to get passed # through to ssh-add. should we limit it to known ones? For # example: -d or -c and/or -t for subkey in $authsubkeys; do # test that the subkey has proper capability awk_pgrm=' /^[ps]ub:/{ caps = $12 } /^fpr:/{ if ($10 == "'"${subkey}"'") { print caps }; }' capability=$(gpg_user --with-colons --with-fingerprint --with-fingerprint \ --list-keys "0x${subkey}!" \ | awk -F: "$awk_pgrm") if ! check_capability "$capability" 'a' ; then log error "Did not find authentication-capable subkey with key ID '$subkey'." continue fi # choose a label by which this key will be known in the agent: # we are labelling the key by User ID instead of by # fingerprint, but filtering out all / characters to make sure # the filename is legit. # FIXME: this assumes that the first listed uid is the primary # UID. does gpg guarantee that? is there some better way to # get this info? primaryuid=$(gpg_user --with-colons --list-key "0x${subkey}!" | grep '^uid:' | head -n1 | cut -f10 -d: | tr -d /) #kname="[monkeysphere] $primaryuid" kname="${primaryuid:-Monkeysphere Key 0x${subkey}}" if [ "$1" = '-d' ]; then # we're removing the subkey: gpg_user --export-ssh-key "0x${subkey}!" | cut -f1,2 -d' ' > "$workingdir/$kname" (cd "$workingdir" && ssh-add -d "$kname") || keysuccess="$?" else awk_pgrm=' /^fpr:/{ fpr = $10 } /^grp:/{ if (fpr == "'"${subkey}"'") { print $10; } }' keygrip=$(gpg_user --with-colons --with-keygrip --with-fingerprint \ --with-fingerprint --list-keys "0x${subkey}!" \ | awk -F: "$awk_pgrm") agent-transfer "$@" "$keygrip" "$kname" || keysuccess="$?" fi rm -f "$workingdir/$kname" done trap - EXIT rm -rf "$workingdir" # FIXME: sort out the return values: we're just returning the # failure code of the last authentication subkey which fails. # what if more than one authentication subkey fails? return "$keysuccess" } monkeysphere-0.43/src/share/m/update_authorized_keys000066400000000000000000000037121342216672300230050ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere update_authorized_keys subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2010, and are all released under the GPL, version # 3 or later. update_authorized_keys() { local newUmask local tmpFile if [ ! -s "$AUTHORIZED_USER_IDS" ] ; then log error "empty or absent authorized_user_ids file." failure fi check_key_file_permissions $(whoami) "$AUTHORIZED_USER_IDS" \ || failure "Bad permissions governing authorized_user_ids file '$AUTHORIZED_USER_IDS'" # touch the authorized_keys file so that the file permission check # below won't fail upon not finding the file touch_key_file_or_fail "$AUTHORIZED_KEYS" check_key_file_permissions $(whoami) "$AUTHORIZED_KEYS" \ || failure "Bad permissions governing authorized_keys file $AUTHORIZED_KEYS" lock create "$AUTHORIZED_KEYS" # FIXME: we're discarding any pre-existing EXIT trap; is this bad? trap "log debug TRAP; lock remove $AUTHORIZED_KEYS" EXIT tmpFile=$(mktemp "${AUTHORIZED_KEYS}.monkeysphere.XXXXXX") trap "log debug TRAP; lock remove $AUTHORIZED_KEYS; rm -f $tmpFile" EXIT # remove any monkeysphere lines from authorized_keys file this is # to insure that that all old authorized keys that are no longer # authorized are removed log debug "removing old monkeysphere lines..." remove_monkeysphere_lines <"$AUTHORIZED_KEYS" >"$tmpFile" || true process_authorized_user_ids "$tmpFile" \ < "$AUTHORIZED_USER_IDS" if [ "$(file_hash "$AUTHORIZED_KEYS")" != "$(file_hash "$tmpFile")" ] ; then mv -f "$tmpFile" "$AUTHORIZED_KEYS" log verbose "authorized_keys file updated." else rm -f "$tmpFile" fi lock remove "$AUTHORIZED_KEYS" trap - EXIT } monkeysphere-0.43/src/share/m/update_known_hosts000066400000000000000000000041351342216672300221500ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere update_known_hosts subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2010, and are all released under the GPL, version # 3 or later. # update the known_hosts file for a set of hosts listed on command # line update_known_hosts() { local tmpFile local host # touch the known_hosts file so that the file permission check # below won't fail upon not finding the file touch_key_file_or_fail "$KNOWN_HOSTS" check_key_file_permissions $(whoami) "$KNOWN_HOSTS" \ || failure "Bad permissions governing known_hosts file $KNOWN_HOSTS" lock create "$KNOWN_HOSTS" # FIXME: we're discarding any pre-existing EXIT trap; is this bad? trap "log debug TRAP; lock remove $KNOWN_HOSTS" EXIT tmpFile=$(mktemp "${KNOWN_HOSTS}.monkeysphere.XXXXXX") trap "log debug TRAP; lock remove $KNOWN_HOSTS; rm -f $tmpFile" EXIT cat "$KNOWN_HOSTS" >"$tmpFile" for host ; do FILE_TYPE='known_hosts' process_keys_for_file "$tmpFile" "ssh://${host}" lock touch "$KNOWN_HOSTS" done if [ "$(file_hash "$KNOWN_HOSTS")" != "$(file_hash "$tmpFile")" ] ; then mv -f "$tmpFile" "$KNOWN_HOSTS" log debug "known_hosts file updated." else rm -f "$tmpFile" fi lock remove "$KNOWN_HOSTS" trap - EXIT } # process hosts from a known_hosts file process_known_hosts() { local hosts if [ ! -e "$KNOWN_HOSTS" ] ; then failure "known_hosts file '$KNOWN_HOSTS' does not exist." fi log debug "processing known_hosts file:" log debug " $KNOWN_HOSTS" hosts=$(meat "$KNOWN_HOSTS" | cut -d ' ' -f 1 | grep -v '^|.*$' | tr , ' ' | tr '\n' ' ') if [ -z "$hosts" ] ; then log debug "no hosts to process." return fi # take all the hosts from the known_hosts file (first # field), grep out all the hashed hosts (lines starting # with '|')... update_known_hosts $hosts } monkeysphere-0.43/src/share/ma/000077500000000000000000000000001342216672300164455ustar00rootroot00000000000000monkeysphere-0.43/src/share/ma/add_certifier000066400000000000000000000124731342216672300211630ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere authentication add-certifier subcommand # # This function adds a certifier whose signatures will be used to # calculate validity of keys used to connect to user accounts on the # server. The specified certifier key is first retrieved from the Web # of Trust with the monkeysphere-user-controlled gpg_sphere keyring. # Once then new key is retrieved, it is imported into the core # keyring. The gpg_core then ltsigns the key with the desired trust # level, and then the key is exported back to the gpg_sphere keyring. # The gpg_sphere has ultimate owner trust of the core key, so the core # ltsigs on the new certifier key can then be used by gpg_sphere # calculate validity for keys inserted in the authorized_keys file. # # This is all to keep the monkeysphere user that connects to the # keyservers from accessing the core secret key. # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2019, and are all released under the GPL, # version 3 or later. add_certifier() { local domain= local trust=full local depth=1 local keyID local fingerprint local ltsignCommand local trustval # get options while true ; do case "$1" in -n|--domain) domain="$2" shift 2 ;; -t|--trust) trust="$2" shift 2 ;; -d|--depth) depth="$2" shift 2 ;; -) break ;; *) if [ "$(echo "$1" | cut -c 1)" = '-' ] ; then failure "Unknown option '$1'. Type '$PGRM help' for usage." fi break ;; esac done keyID="$1" # check that key ID or file is specified if [ -z "$keyID" ] ; then failure "You must specify the key ID of a key to add, or specify a file to read the key from." fi # check the trust value case "$trust" in 'marginal') trustval=1 ;; 'full') trustval=2 ;; *) failure "Trust value requested ('$trust') was unclear (only 'marginal' or 'full' are supported)." ;; esac # if file is specified if [ -f "$keyID" -o "$keyID" = '-' ] ; then # load the key from stdin if [ "$keyID" = '-' ] ; then # make a temporary file to hold the key from stdin keyID=$(msmktempfile) trap "rm -f $keyID" EXIT log verbose "reading key from stdin..." cat > "$keyID" # load the key from the file elif [ -f "$keyID" ] ; then log verbose "reading key from file '$keyID'..." fi TMPDIR=$MATMPDIR tmpDir=$(msmktempdir) trap "$(printf 'rm -rf -- %q' "$tmpDir")" EXIT # fix permissions and ownership on temporary directory which will # be used by monkeysphere user chmod 0700 "$tmpDir" chown "$MONKEYSPHERE_USER":"$MONKEYSPHERE_GROUP" -- "$tmpDir" # check the key is ok as monkeysphere user before loading log debug "checking keys in file..." fingerprint=$(run_as_monkeysphere_user env "TMPDIR=$tmpDir" \ bash -c "$(printf ". %q && list_primary_fingerprints" "${SYSSHAREDIR}/common")" < "$keyID") if [ $(printf "%s" "$fingerprint" | egrep -c '^[A-F0-9]{40}$') -ne 1 ] ; then failure "There was not exactly one gpg key in the file." fi # remove the temporary directory trap - EXIT rm -rf -- "$tmpDir" # load the key gpg_sphere --import <"$keyID" 2>/dev/null \ || failure "could not read key from '$keyID'" # else, get the key from the keyserver else log verbose "searching keyserver $KEYSERVER for keyID $keyID..." gpg_sphere --keyserver "$KEYSERVER" --recv-key "0x${keyID}!" \ || failure "Could not receive a key with this ID from the '$KEYSERVER' keyserver." # get the full fingerprint of new certifier key log debug "getting fingerprint of certifier key..." fingerprint=$(gpg_sphere --list-key --with-colons --with-fingerprint "0x${keyID}!" \ | awk -F: '/^fpr:/{ if (ok) { print $10 } ; ok=0 } /^pub:/{ ok=1 }') # test that there is only a single fingerprint if (( $(echo "$fingerprint" | wc -l) != 1 )) ; then cat <&2 read OK; OK=${OK:-Y} if [ "${OK/y/Y}" != 'Y' ] ; then failure "Identity certifier not added." fi else log debug "adding key without prompting." fi fi # export the key to the core keyring so that the core can sign the # new certifier key log debug "loading key into core keyring..." gpg_sphere --export "0x${fingerprint}!" | gpg_core --import # edit-key script to ltsign key # NOTE: *all* user IDs will be ltsigned ltsignCommand="ltsign y $trustval $depth $domain y save" # end script # core ltsigns the newly imported certifier key log debug "executing core ltsign script..." if echo "$ltsignCommand" | \ gpg_core --command-fd 0 --edit-key "0x${fingerprint}!" ; then # transfer the new sigs back to the sphere keyring gpg_core_sphere_sig_transfer # update the sphere trustdb log debug "updating sphere trustdb..." gpg_sphere --check-trustdb 2>&1 | log debug log info "Identity certifier added." else failure "Problem adding identify certifier." fi } monkeysphere-0.43/src/share/ma/diagnostics000066400000000000000000000132351342216672300207030ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere authentication diagnostics subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2009, and are all released under the GPL, # version 3 or later. # check on the status and validity of the key and public certificates diagnostics() { local seckey local keysfound local curdate local warnwindow local warndate local create local expire local uid local fingerprint local badhostkeys local sshd_config local problemsfound=0 report_cruft if ! id monkeysphere >/dev/null ; then echo "! No monkeysphere user found! Please create a monkeysphere system user with bash as its shell." problemsfound=$(($problemsfound+1)) fi if ! [ -d "$SYSDATADIR" ] ; then echo "! no $SYSDATADIR directory found. Please create it." problemsfound=$(($problemsfound+1)) fi echo "Checking for authentication directory..." if ! [ -d "$MADATADIR" ] ; then echo "! No authentication data directory found." echo " - Recommendation: run 'monkeysphere-authentication setup'" exit fi # FIXME: what's the correct, cross-platform way to determine where # sshd_config lives? sshd_config=/etc/ssh/sshd_config seckey=$(gpg_core --list-secret-keys --fingerprint --with-colons) keysfound=$(echo "$seckey" | grep -c ^sec:) curdate=$(date +%s) # warn when anything is 2 months away from expiration warnwindow='2 months' warndate=$(advance_date $warnwindow +%s) echo "Checking core GPG key..." if (( "$keysfound" < 1 )); then echo "! No core key found." echo " - Recommendation: run 'monkeysphere-authentication setup'" problemsfound=$(($problemsfound+1)) elif (( "$keysfound" > 1 )); then echo "! More than one core key found?" # FIXME: recommend a way to resolve this problemsfound=$(($problemsfound+1)) else create=$(echo "$seckey" | grep ^sec: | cut -f6 -d:) expire=$(echo "$seckey" | grep ^sec: | cut -f7 -d:) fingerprint=$(echo "$seckey" | awk -F: '/^fpr:/{ if (ok) { print $10 } ; ok=0 } /^pub:/{ ok=1 }') # check for key expiration: if [ "$expire" ]; then if (( "$expire" < "$curdate" )); then echo "! Core key is expired." echo " - Recommendation: ???" problemsfound=$(($problemsfound+1)) elif (( "$expire" < "$warndate" )); then echo "! Core key expires in less than $warnwindow:" $(advance_date $(( $expire - $curdate )) seconds +%F) echo " - Recommendation: ???" problemsfound=$(($problemsfound+1)) fi fi # and weirdnesses: if [ "$create" ] && (( "$create" > "$curdate" )); then echo "! Core key was created in the future(?!). Is your clock correct?" echo " - Recommendation: Check clock ($(date +%F_%T)); use NTP?" problemsfound=$(($problemsfound+1)) fi fi # FIXME: look at the ownership/privileges of the various keyrings, # directories housing them, etc (what should those values be? can # we make them as minimal as possible?) # FIXME: look to see that the ownertrust rules are set properly on the # sphere keyring # make sure that at least one identity certifier exists echo echo "Checking for Identity Certifiers..." if ! ( monkeysphere-authentication list-identity-certifiers | egrep '^[A-F0-9]{40}:' >/dev/null ) ; then echo "! No Identity Certifiers found!" echo " - Recommendation: once you know who should be able to certify the identities of connecting users, you should add their key, with: monkeysphere-authentication add-identity-certifier" problemsfound=$(($problemsfound+1)) fi # FIXME: look at the timestamps on the monkeysphere-generated # authorized_keys files -- warn if they seem out-of-date. # FIXME: check for a cronjob that updates monkeysphere-generated # authorized_keys? echo echo "Checking for Monkeysphere-enabled public-key authentication for users ..." # Ensure that User ID authentication is enabled: if echo "AuthorizedKeysFile foo bar" | /usr/sbin/sshd -t -f /dev/stdin; then # OpenSSH >= 6.0, multiple authorized_keys file supported if ! /usr/sbin/sshd -T | grep "^authorizedkeysfile " | tr ' ' '\n' | tail +2 | grep -q --fixed-strings --line-regexp "${SYSDATADIR}/authorized_keys/%u" ; then echo "! $sshd_config does not point to monkeysphere authorized keys." echo " - Recommendation: add a line to $sshd_config:" echo " 'AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys2 ${SYSDATADIR}/authorized_keys/%u'" problemsfound=$(($problemsfound+1)) fi if [ "$RAW_AUTHORIZED_KEYS" != none ]; then if badauthorizedkeys=$(/usr/sbin/sshd -T | grep "^authorizedkeysfile " | tr ' ' '\n' | tail +2 | grep -v --fixed-strings --line-regexp "${SYSDATADIR}/authorized_keys/%u") ; then echo "! $sshd_config refers to non-monkeysphere authorized_keys files:" echo "$badauthorizedkeys" echo " - Recommendation: disable raw authorized_keys import:" echo " 'echo RAW_AUTHORIZED_KEYS=none >> ${SYSCONFIGDIR}/monkeysphere-authentication.conf'" problemsfound=$(($problemsfound+1)) fi fi else echo "! /usr/bin/sshd does not appear to support multiple entries in AuthorizedKeysFile." echo " - Recommendation: upgrade to OpenSSH 6.0 or later." problemsfound=$(($problemsfound+1)) fi if [ "$problemsfound" -gt 0 ]; then echo "When the above $problemsfound issue"$(if [ "$problemsfound" -eq 1 ] ; then echo " is" ; else echo "s are" ; fi)" resolved, please re-run:" echo " monkeysphere-authentication diagnostics" else echo "Everything seems to be in order!" fi } monkeysphere-0.43/src/share/ma/list_certifiers000066400000000000000000000051021342216672300215600ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere authentication list-certifiers subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2009, and are all released under the GPL, # version 3 or later. # list the host certifiers list_certifiers() { local keys local key local authfpr local keyfpr local uid local printedfpr # find trusted keys in sphere keychain log debug "finding trusted keys..." # FIXME: this assumes that the keygrip (16 hex chars) is unique; we're # only searching by keygrip at the moment. authgrip=$(core_fingerprint | cut -b 25-40) # We're walking the list of known signatures, and extracting all trust # signatures made by the core fingerprint and known to the sphere # keyring. # for each one of these, we're printing (colon-delimited): the # fingerprint, the trust depth, the trust level (60 == marginal, 120 # == full), and the domain regex (if any): gpg_sphere --fingerprint --with-colons --check-sigs | \ cut -f 1,2,5,8,9,10 -d: | \ egrep '^(fpr:::::|uat:|uid:|sig:!:'"$authgrip"':[[:digit:]]+ [[:digit:]]+:)' | \ while IFS=: read -r type validity grip trustparams trustdomain fpr ; do case $type in 'fpr') # this is a new key keyfpr=$fpr uid= printedfpr=no ;; 'uid') # here comes a user id (if we don't have a key, or the # uid has no calculated validity, we will not bother # with it): if [ "$keyfpr" ] && [ "$validity" = 'f' ] ; then uid="$fpr" else uid= fi ;; 'uat') # this is a user attribute. DETAILS.gz states that the # 10th field is the number of user attribute # subpackets, followed by the total number of bytes of # the subpackets: if [ "$keyfpr" ] && [ "$validity" = 'f' ] ; then uid=$(printf "%d JPEG(?) image(s), total %d bytes" \ "${fpr%% *}" "${fpr##* }") else uid= fi ;; 'sig') # print all trust signatures, including regexes if # present, assuming that if [ "$keyfpr" ] && [ "$uid" ] ; then trustdepth=${trustparams%% *} trustlevel=${trustparams##* } if [ "$printedfpr" = no ] ; then printf "%s:\n" "$keyfpr" printedfpr=yes fi # FIXME: this is clumsy and not human-friendly. we should # print out more human-readable information, if possible. printf " :%s:%d:%d:%s\n" "$uid" "$trustdepth" "$trustlevel" "$trustdomain" fi ;; esac done } monkeysphere-0.43/src/share/ma/remove_certifier000066400000000000000000000026631342216672300217300ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere authentication remove-certifier subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2009, and are all released under the GPL, # version 3 or later. # delete a certifiers key from the host keyring remove_certifier() { local keyID local fingerprint keyID="$1" if [ -z "$keyID" ] ; then failure "You must specify the key ID of a key to remove." fi # FIXME: should we be doing a fancier list_certifier output here? gpg_core --list-key --fingerprint "0x${keyID}!" || failure if [ "$PROMPT" != "false" ] ; then printf "Really remove the above listed identity certifier? (Y/n) " >&2 read OK; OK=${OK:-Y} if [ "${OK/y/Y}" != 'Y' ] ; then failure "Identity certifier not removed." fi else log debug "certifier removed without prompting." fi # delete the requested key from the sphere keyring if gpg_sphere --delete-key --batch --yes "0x${keyID}!" ; then # delete key from core keyring as well gpg_core --delete-key --batch --yes "0x${keyID}!" # update the trustdb for the authentication keyring gpg_sphere --check-trustdb log info "Identity certifier removed." else failure "Problem removing identity certifier." fi } monkeysphere-0.43/src/share/ma/setup000066400000000000000000000116601342216672300175340ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere authentication setup subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2009, and are all released under the GPL, # version 3 or later. setup() { # make all needed directories log debug "checking authentication directory structure..." mkdir -p "${MADATADIR}" chmod 0750 "${MADATADIR}" chgrp "$MONKEYSPHERE_GROUP" "${MADATADIR}" mkdir -p "${MATMPDIR}" chmod 0750 "${MATMPDIR}" chgrp "$MONKEYSPHERE_GROUP" "${MATMPDIR}" mkdir -p "${GNUPGHOME_CORE}" chmod 0700 "${GNUPGHOME_CORE}" mkdir -p "${GNUPGHOME_SPHERE}" chmod 0700 "${GNUPGHOME_SPHERE}" mkdir -p "${SYSDATADIR}"/authorized_keys # deliberately replace the config files via truncation # FIXME: should we be dumping to tmp files and then moving atomically? log debug "writing core gpg.conf..." cat >"${GNUPGHOME_CORE}"/gpg.conf <"${GNUPGHOME_SPHERE}"/gpg.conf </dev/null | perl -MMIME::Base64 -ne 'print encode_base64($_)')) printf "generating monkeysphere authentication trust core key:\nsize: %d bits\nuid: '%s'\n" "$CORE_KEYLENGTH" "$CORE_UID" | log debug PEM2OPENPGP_USAGE_FLAGS=certify \ PEM2OPENPGP_NEWKEY=$CORE_KEYLENGTH pem2openpgp "$CORE_UID" \ | gpg_core --import \ || failure "Could not import new key for Monkeysphere authentication trust core" # get fingerprint of core key. should definitely not be empty at this point CORE_FPR=$(core_fingerprint) log debug "core fingerprint: $CORE_FPR" if [ -z "$CORE_FPR" ] ; then failure "Failed to create Monkeysphere authentication trust core!" fi else log verbose "Monkeysphere authentication trust core already exists." fi # export the core key to the sphere keyring log debug "exporting core pub key to sphere keyring..." gpg_core --export | gpg_sphere --import # ensure that the authentication sphere checker has absolute ownertrust on the expected key. log debug "setting ultimate owner trust on core key in gpg_sphere..." printf "%s:6:\n" "$CORE_FPR" | gpg_sphere --import-ownertrust 2>&1 | log verbose gpg_sphere --export-ownertrust 2>&1 | log debug # check the owner trust log debug "checking gpg_sphere owner trust set properly..." local ORIG_TRUST if ORIG_TRUST=$(gpg_sphere --export-ownertrust | grep '^[^#]') ; then if [ "${CORE_FPR}:6:" != "$ORIG_TRUST" ] ; then failure "Monkeysphere authentication trust sphere should explicitly trust the core. It does not have proper ownertrust settings." fi else failure "Could not get monkeysphere-authentication trust guidelines." # FIXME: what does this mean? should we suggest how to fix? fi # ensure that we're using the extended trust model (1), and that # our preferences are reasonable (i.e. 3 marginal OR 1 fully # trusted certifications are sufficient to grant full validity. log debug "checking trust model for authentication ..." local TRUST_MODEL=$(gpg_sphere --with-colons --list-keys 2>/dev/null \ | head -n1 | grep "^tru:" | cut -d: -f3,6,7) log debug "sphere trust model: $TRUST_MODEL" if [ "$TRUST_MODEL" != '1:3:1' ] ; then failure "monkeysphere-authentication does not have the expected trust model settings." # FIXME: what does this mean? should we suggest how to fix? fi } monkeysphere-0.43/src/share/ma/update_users000066400000000000000000000113731342216672300211000ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere authentication update-users subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2019, and are all released under the GPL, # version 3 or later. update_users() { local returnCode=0 local unames local uname local authorizedKeysDir local tmpAuthorizedKeys local authorizedUserIDs if [ "$1" ] ; then # get users from command line unames="$@" else # or just look at all users if none specified unames=$(list_users) fi # set gnupg home GNUPGHOME="$GNUPGHOME_SPHERE" # the authorized_keys directory authorizedKeysDir="${SYSDATADIR}/authorized_keys" # check to see if the gpg trust database has been initialized if [ ! -s "${GNUPGHOME}/trustdb.gpg" ] ; then failure "GNUPG trust database uninitialized. Please see MONKEYSPHERE-SERVER(8)." fi # make sure the authorized_keys directory exists mkdir -p "${authorizedKeysDir}" # loop over users for uname in $unames ; do # check all specified users exist if ! id "$uname" >/dev/null ; then log error "----- unknown user '$uname' -----" continue fi log verbose "----- user: $uname -----" # make temporary directory TMPLOC=$(mktemp -d -- "${MATMPDIR}/tmp.XXXXXXXXXX") || failure "Could not create temporary directory!" # trap to delete temporary directory on exit trap "$(printf 'rm -rf -- %q' "$TMPLOC")" EXIT # create temporary authorized_keys file tmpAuthorizedKeys="${TMPLOC}/authorized_keys" touch -- "$tmpAuthorizedKeys" # set restrictive permissions on the temporary files # FIXME: is there a better way to do this? chmod 0700 -- "$TMPLOC" chmod 0600 -- "$tmpAuthorizedKeys" chown -R "$MONKEYSPHERE_USER" -- "$TMPLOC" # process authorized_user_ids file log debug "checking for authorized_user_ids..." # translating ssh-style path variables authorizedUserIDs=$(translate_ssh_variables "$uname" "$AUTHORIZED_USER_IDS") if [ -s "$authorizedUserIDs" ] ; then # check permissions on the authorized_user_ids file path if check_key_file_permissions "$uname" "$authorizedUserIDs" ; then log verbose "processing authorized_user_ids..." # process authorized_user_ids file, as monkeysphere user run_as_monkeysphere_user \ env STRICT_MODES="$STRICT_MODES" \ bash -c "$(printf ". %q && process_authorized_user_ids -" "${SYSSHAREDIR}/common")"\ < "$authorizedUserIDs" \ > "$tmpAuthorizedKeys" else log debug "not processing authorized_user_ids." fi else log debug "empty or absent authorized_user_ids file." fi # add user-controlled authorized_keys file if specified translate # ssh-style path variables rawAuthorizedKeys=$(translate_ssh_variables "$uname" "$RAW_AUTHORIZED_KEYS") if [ "$rawAuthorizedKeys" != 'none' ] ; then log debug "checking for raw authorized_keys..." if [ -s "$rawAuthorizedKeys" ] ; then # check permissions on the authorized_keys file path if check_key_file_permissions "$uname" "$rawAuthorizedKeys" ; then log verbose "adding raw authorized_keys..." cat "$rawAuthorizedKeys" >> "$tmpAuthorizedKeys" else log debug "not adding raw authorized_keys." fi else log debug "empty or absent authorized_keys file." fi fi # move the new authorized_keys file into place if [ -s "$tmpAuthorizedKeys" ] ; then # openssh appears to check the contents of the authorized_keys # file as the user in question, so the file must be readable # by that user at least. # but in general, we don't want the user tampering with this # file directly, so we'll adopt this approach: Own the file by # the monkeysphere-authentication invoker (usually root, but should be # the same uid that sshd is launched as); change the group of # the file so that members of the user's group can read it. if [ "$OUTPUT_STDOUT" ] ; then log debug "outputting keys to stdout..." cat -- "$tmpAuthorizedKeys" else log debug "moving new file to ${authorizedKeysDir}/${uname}..." # FIXME: is there a better way to do this? chown "$(whoami)" -- "$tmpAuthorizedKeys" && \ chgrp "$(id -g "$uname")" -- "$tmpAuthorizedKeys" && \ chmod g+r -- "$tmpAuthorizedKeys" && \ mv -f -- "$tmpAuthorizedKeys" "${authorizedKeysDir}/${uname}" || \ { log error "Failed to install authorized_keys for '$uname'!" rm -f -- "$tmpAuthorizedKeys" # indicate that there has been a failure: returnCode=1 } fi else rm -f -- "${authorizedKeysDir}/${uname}" fi # unset the trap trap - EXIT # destroy temporary directory rm -rf -- "$TMPLOC" done return $returnCode } monkeysphere-0.43/src/share/mh/000077500000000000000000000000001342216672300164545ustar00rootroot00000000000000monkeysphere-0.43/src/share/mh/add_name000066400000000000000000000035131342216672300201310ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere host add-hostname subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2019, and are all released under the GPL, # version 3 or later. # add servicename user ID to server key add_name() { local serviceName local keyID local fingerprint local tmpuidMatch local line local adduidCommand if [ -z "$1" ] ; then failure "You must specify a service name to add." fi serviceName="$1" shift keyID=$(check_key_input "$@") # test that the desired user ID does not already exist check_key_userid "$keyID" "$serviceName" && \ failure "Service name '$serviceName' already exists on key '$keyID'." # test that a key with that user ID does not already exist prompt_userid_exists "$serviceName" check_service_name "$serviceName" if [ "$PROMPT" != "false" ] ; then printf "The following service name will be added to key '$keyID':\n %s\nAre you sure you would like to add this service name? (Y/n) " "$serviceName" >&2 read OK; OK=${OK:=Y} if [ "${OK/y/Y}" != 'Y' ] ; then failure "Service name not added." fi else log debug "adding service name without prompting." fi # execute edit-key script if gpg_host --export-secret-keys "$keyID" | \ PEM2OPENPGP_USAGE_FLAGS=authenticate \ "$SYSSHAREDIR/keytrans" adduserid "$keyID" "$serviceName" \ | gpg_host --import ; then gpg_host --check-trustdb update_pgp_pub_file show_key "$keyID" echo echo "NOTE: Service name added to key, but key not published." echo "Run '$PGRM publish-key' to publish the new service name." else failure "Problem adding service name." fi } monkeysphere-0.43/src/share/mh/add_revoker000066400000000000000000000100741342216672300206660ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere host add-revoker subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2019, and are all released under the GPL, # version 3 or later. # add a revoker to the host key add_revoker() { local revokerKeyID local keyID local tmpDir local fingerprint local addrevokerCommand # check that key ID or file is specified if [ -z "$1" ] ; then failure "You must specify the key ID of a revoker key, or specify a file to read the key from." fi revokerKeyID="$1" shift keyID=$(check_key_input "$@") # make a temporary directory for storing keys during import, and set # the trap to delete it on exit TMPDIR=$MHTMPDIR tmpDir=$(msmktempdir) trap "$(printf 'rm -rf -- %q' "$tmpDir")" EXIT # fix permissions and ownership on temporary directory which will # be used by monkeysphere user chmod 0700 -- "$tmpDir" chown "$MONKEYSPHERE_USER":"$MONKEYSPHERE_GROUP" -- "$tmpDir" # if file is specified if [ -f "$revokerKeyID" -o "$revokerKeyID" = '-' ] ; then # load the key from stdin if [ "$revokerKeyID" = '-' ] ; then # make a temporary file to hold the key from stdin revokerKeyID="$tmpDir"/importkey log verbose "reading revoker key from stdin..." cat > "$revokerKeyID" # load the key from the file elif [ -f "$revokerKeyID" ] ; then log verbose "reading revoker key from file '$revokerKeyID'..." fi # check the key is ok as monkeysphere user before loading log debug "checking keys in file..." fingerprint=$(run_as_monkeysphere_user env "TMPDIR=$tmpDir" \ bash -c "$(printf ". %q && list_primary_fingerprints" "${SYSSHAREDIR}/common")" < "$revokerKeyID") if [ $(printf "%s" "$fingerprint" | egrep -c '^[A-F0-9]{40}$') -ne 1 ] ; then failure "There was not exactly one gpg key in the file." fi # load the key gpg_host --import <"$revokerKeyID" \ || failure "could not read revoker key from '$revokerKeyID'" # else, get the revoker key from the keyserver else # download the key from the keyserver as the monkeysphere user log verbose "searching keyserver $KEYSERVER for revoker keyID $revokerKeyID..." run_as_monkeysphere_user env GNUPGHOME="$tmpDir" gpg --quiet --keyserver "$KEYSERVER" --recv-key "0x${revokerKeyID}!" \ || failure "Could not receive a key with this ID from keyserver '$KEYSERVER'." # get the full fingerprint of new revoker key log debug "getting fingerprint of revoker key..." fingerprint=$(run_as_monkeysphere_user env GNUPGHOME="$tmpDir" gpg --list-key --with-colons --with-fingerprint "${revokerKeyID}" \ | awk -F: '/^fpr:/{ if (ok) { print $10 }; ok=0 } /^pub:/{ ok=1 }') # test that there is only a single fingerprint if (( $(echo "$fingerprint" | wc -l) != 1 )) ; then cat <&2 read OK; OK=${OK:-Y} if [ "${OK/y/Y}" != 'Y' ] ; then failure "revoker not added." fi else log debug "adding revoker without prompting." fi # export the new key to the host keyring log debug "loading revoker key into host keyring..." run_as_monkeysphere_user env GNUPGHOME="$tmpDir" gpg --quiet --export "0x${fingerprint}!" \ | gpg_host --import fi # edit-key script to add revoker addrevokerCommand="addrevoker $fingerprint y save " # end script # core ltsigns the newly imported revoker key log debug "executing add revoker script..." if echo "$addrevokerCommand" | gpg_host_edit "0x${keyID}!" ; then update_pgp_pub_file log info "Revoker added." else failure "Problem adding revoker." fi # remove the temporary directory trap - EXIT rm -rf -- "$tmpDir" } monkeysphere-0.43/src/share/mh/diagnostics000066400000000000000000000131071342216672300207100ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere host diagnostics subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2010, and are all released under the GPL, # version 3 or later. # check on the status and validity of the host's public certificates (and keys?) # global vars for communicating between functions: MHD_CURDATE=$(date +%s) # warn when anything is 2 months away from expiration MHD_WARNWINDOW='2 months' MHD_WARNDATE=$(advance_date $MHD_WARNWINDOW +%s) MHD_PROBLEMSFOUND=0 diagnose_key() { local fpr="$1" local certinfo local create local expire local uid local keysfound local uiderrs local errcount printf "Checking OpenPGP Certificate for key 0x%s\n" "$fpr" certinfo=$(get_cert_info "0x$fpr" <"$HOST_KEY_FILE") keysfound=$(grep -c ^pub: <<<"$certinfo") if [ "$keysfound" -lt 1 ] ; then printf "! Could not find key with fingerprint 0x%s\n" "$fpr" # FIXME: recommend a way to resolve this! MHD_PROBLEMSFOUND=$(($MHD_PROBLEMSFOUND+1)) fi create=$(echo "$certinfo" | grep ^pub: | cut -f6 -d:) expire=$(echo "$certinfo" | grep ^pub: | cut -f7 -d:) # check for key expiration: if [ "$expire" ]; then if (( "$expire" < "$MHD_CURDATE" )); then printf "! Host key 0x%s is expired.\n" "$fpr" printf " - Recommendation: extend lifetime of key with 'monkeysphere-host set-expire 0x%s'\n" "$fpr" MHD_PROBLEMSFOUND=$(($MHD_PROBLEMSFOUND+1)) elif (( "$expire" < "$MHD_WARNDATE" )); then printf "! Host key 0x%s expires in less than %s: %s\n" "$fpr" "$MHD_WARNWINDOW" $(advance_date $(( $expire - $MHD_CURDATE )) seconds +%F) printf " - Recommendation: extend lifetime of key with 'monkeysphere-host set-expire %s'\n" "$fpr" MHD_PROBLEMSFOUND=$(($MHD_PROBLEMSFOUND+1)) fi fi # and weirdnesses: if [ "$create" ] && (( "$create" > "$MHD_CURDATE" )); then printf "! Host key 0x%s was created in the future(?!): %s. Is your clock correct?\n" "$fpr" $(date -d "1970-01-01 + $create seconds" +%F) printf " - Recommendation: Check your clock (is it really %s?); use NTP?\n" $(date +%F_%T) MHD_PROBLEMSFOUND=$(($MHD_PROBLEMSFOUND+1)) fi # check for UserID expiration: uiderrs=$(printf '%s\n' "$certinfo" | grep ^uid: | cut -d: -f6,7,10 | \ while IFS=: read -r create expire uid ; do uid=$(gpg_unescape <<<"$uid") check_service_name "$uid" if [ "$create" ] && (( "$create" > "$MHD_CURDATE" )); then printf "! The latest self-sig on User ID '%s' was created in the future(?!): %s.\n - Is your clock correct?\n" "$uid" $(date -d "1970-01-01 + $create seconds" +%F) printf " - Recommendation: Check your clock (is it really %s ?); use NTP?\n" $(date +%F_%T) fi if [ "$expire" ] ; then if (( "$expire" < "$MHD_CURDATE" )); then printf "! User ID '%s' is expired.\n" "$uid" # FIXME: recommend a way to resolve this elif (( "$expire" < "$MHD_WARNDATE" )); then printf "! User ID '%s' expires in less than %s: %s\n" "%s" "$MHD_WARNWINDOW" $(advance_date $(( $expire - $MHD_CURDATE )) seconds +%F) # FIXME: recommend a way to resolve this fi fi done) errcount=$(grep -c '^!' <<<"$uiderrs") || \ MHD_PROBLEMSFOUND=$(($MHD_PROBLEMSFOUND+ $errcount )) printf '%s\n' "$uiderrs" # FIXME: verify that the host key is properly published to the # keyservers (do this with the non-privileged user) # FIXME: check that there are valid, non-expired certifying signatures # attached to the host key after fetching from the public keyserver # (do this with the non-privileged user as well) # FIXME: propose adding a revoker to the host key if none exist (do we # have a way to do that after key generation?) # FIXME: test (with ssh-keyscan?) that any running ssh daemon is # actually offering the monkeysphere host key, if such a key is # loaded. # FIXME: scan /proc/net/tcp and /proc/net/tcp6 to see what # known-crypto ports (ssh, https, imaps?, ldaps?, etc) are in use # locally. Propose bringing them into the monkeysphere. # FIXME: ensure that the key is of a reasonable size # FIXME: ensure that the cert has the right key usage flags # FIXME: ensure that the key doesn't match any known blacklist } diagnostics() { MHD_PROBLEMSFOUND=0 if ! [ -d "$SYSDATADIR" ] ; then echo "! no $SYSDATADIR directory found. Please create it." exit fi if ! [ -f "$HOST_KEY_FILE" ] ; then echo "No host OpenPGP certificates file found!" echo " - Recommendation: run 'monkeysphere-host import-key' with a service key" exit fi if ! id monkeysphere >/dev/null ; then echo "! No monkeysphere user found! Please create a monkeysphere system user with bash as its shell." MHD_PROBLEMSFOUND=$(($MHD_PROBLEMSFOUND+1)) fi echo "Checking host OpenPGP certificates..." multi_key diagnose_key # FIXME: look at the ownership/privileges of the various keyrings, # directories housing them, etc (what should those values be? can # we make them as minimal as possible?) # report on any cruft from old monkeysphere version report_cruft if [ "$MHD_PROBLEMSFOUND" -gt 0 ]; then echo "When the above $MHD_PROBLEMSFOUND issue"$(if [ "$MHD_PROBLEMSFOUND" -eq 1 ] ; then echo " is" ; else echo "s are" ; fi)" resolved, please re-run:" echo " monkeysphere-host diagnostics" else echo "Everything seems to be in order!" fi } monkeysphere-0.43/src/share/mh/import_key000066400000000000000000000031051342216672300205600ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere host import-key subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2010 and are all released under the GPL, # version 3 or later. import_key() { local keyFile="$1" local serviceName="$2" # check that key file specified if [ -z "$keyFile" ] ; then failure "Must specify PEM-encoded key file to import, or specify '-' for stdin." fi # fail if hostname not specified if [ -z "$serviceName" ] ; then failure "You must specify a service name for use in the OpenPGP certificate user ID." fi # test that a key with that user ID does not already exist prompt_userid_exists "$serviceName" # check that the service name is well formatted check_service_name "$serviceName" # create host home mkdir -p "${MHDATADIR}" mkdir -p "${GNUPGHOME_HOST}" chmod 700 "${GNUPGHOME_HOST}" # import pem-encoded key to an OpenPGP private key if [ "$keyFile" = '-' ] ; then log verbose "importing key from stdin..." PEM2OPENPGP_USAGE_FLAGS=authenticate pem2openpgp "$serviceName" \ | gpg_host --import else log verbose "importing key from file '$keyFile'..." PEM2OPENPGP_USAGE_FLAGS=authenticate pem2openpgp "$serviceName" \ <"$keyFile" \ | gpg_host --import fi # export to OpenPGP public key to file update_pgp_pub_file log info "host key imported:" # show info about new key show_key "$serviceName" } monkeysphere-0.43/src/share/mh/publish_key000066400000000000000000000036351342216672300207240ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere host publish-key subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2019, and are all released under the GPL, # version 3 or later. # publish keys to keyserver publish_key() { local keyID="$1" local GNUPGHOME if [ "$PROMPT" != "false" ] ; then log debug "Because \$MONKEYSPHERE_PROMPT is set to $PROMPT, interactively confirm publishing key" printf "Really publish key '$keyID' to $KEYSERVER? (Y/n) " >&2 read OK; OK=${OK:=Y} if [ "${OK/y/Y}" != 'Y' ] ; then log error "key not published." return fi else log debug "publishing key '$keyID' without prompting." fi # create a temporary gnupg directory from which to publish the key export TMPDIR=$MHTMPDIR export GNUPGHOME=$(msmktempdir) chmod 0700 "$GNUPGHOME" chown "$MONKEYSPHERE_USER":"$MONKEYSPHERE_GROUP" -- "$GNUPGHOME" cleanup() { if type gpgconf &>/dev/null; then gpgconf --kill gpg-agent fi rm -rf "$GNUPGHOME" } # trap to remove tmp dir if break trap cleanup EXIT # import the key into the tmp dir run_as_monkeysphere_user \ gpg --quiet --import <"$HOST_KEY_FILE" ANCHORFILE="" for anchorfile in "${SYSCONFIGDIR}/monkeysphere-host-x509-anchors.crt" "${SYSCONFIGDIR}/monkeysphere-x509-anchors.crt"; do if [ -z "$ANCHORFILE" ] && [ -r "$anchorfile" ] ; then log debug "using trust anchor file: $anchorfile" ANCHORFILE="$anchorfile" fi done # publish key log debug "publishing key with the following gpg command line and options:" run_as_monkeysphere_user \ gpg --keyserver "$KEYSERVER" ${ANCHORFILE:+--keyserver-options "ca-cert-file=$ANCHORFILE"} --send-keys "0x${keyID}!" # remove the tmp file trap - EXIT cleanup } monkeysphere-0.43/src/share/mh/revoke_key000066400000000000000000000063671342216672300205560ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere host revoke-key subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2010, and are all released under the GPL, # version 3 or later. # revoke host key revoke_key() { local keyID local publish keyID=$(check_key_input "$@") if [ "$PROMPT" = "false" ] ; then publish=N else cat <&2 This will generate a revocation certificate for key $keyID and dump the certificate to standard output. It can also directly publish the new revocation certificate to the public keyservers via $KEYSERVER if you want it to. Publishing this certificate will IMMEDIATELY and PERMANENTLY revoke your host key! EOF printf "Publish the certificate after generation? (y/n/Q) " >&2 read publish if ! [ "${publish/y/Y}" = 'Y' -o "${publish/n/N}" = 'N' ] ; then failure "aborting at user request" fi fi # our current implementation is very simple: we just want to # generate the revocation certificate on stdout. This provides # for the two most likely (but hopefully not common) scenarios: # an admin wants a revocation certificate for the host which they # can store securely offline. In this case, the admin can # redirect stdout to a file, or can simply copy/paste or # transcribe from the terminal. # Alternately, an admin might want to publish the revocation # certificate immediately, which we can help them do as well. if [ "$PROMPT" = 'false' ] ; then # FIXME: allow the end user to choose something other than # "key was compromised" (1) and to supply their own revocation # string. local revoke_commands="y 1 Monkeysphere host key revocation (automated) $(date '+%F_%T%z') y " revcert=$(GNUPGHOME="$GNUPGHOME_HOST" gpg_host --command-fd 0 --armor --gen-revoke "0x${keyID}!" <<<"$revoke_commands" ) \ || failure "Failed to generate revocation certificate!" else # note: we're not using the gpg_host function because we actually # want to use gpg's UI in this case, so we want to omit --no-tty revcert=$(GNUPGHOME="$GNUPGHOME_HOST" gpg --no-greeting --quiet --armor --gen-revoke "0x${keyID}!") \ || failure "Failed to generate revocation certificate!" fi # if you run gpg --gen-revoke but cancel it or quit in the middle, # it returns success, but emits no revocation certificate: if ! [ "$revcert" ] ; then failure "Revocation canceled." fi ## ok, now we have the revocation certificate. Print it, and ## offer to publish if originally requested: printf "%s\n" "$revcert" if [ "${publish/y/Y}" = 'Y' ] ; then printf "\n" >&2 printf "Really publish this cert to $KEYSERVER ? (Y/n) " >&2 read really if [ "${really/n/N}" = 'N' ] ; then printf "Not publishing.\n" >&2 else local newhome=$(msmktempdir) GNUPGHOME="$newhome" gpg --no-tty --quiet --import < "$HOST_KEY_FILE" GNUPGHOME="$newhome" gpg --no-tty --quiet --import <<< "$revcert" GNUPGHOME="$newhome" gpg --keyserver "$KEYSERVER" --send-keys "0x${keyID}!" rm -rf "$newhome" fi fi } monkeysphere-0.43/src/share/mh/revoke_name000066400000000000000000000036531342216672300207010ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere host revoke-hostname subcommand # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2019, and are all released under the GPL, # version 3 or later. # revoke service name user ID from host key revoke_name() { local serviceName local keyID local fingerprint local tmpuidMatch local line local message local revuidCommand if [ -z "$1" ] ; then failure "You must specify a service name to revoke." fi serviceName="$1" shift keyID=$(check_key_input "$@") # make sure the user ID to revoke exists check_key_userid "$keyID" "$serviceName" || \ failure "No non-revoked service name found matching '$serviceName'." if [ "$PROMPT" != "false" ] ; then printf "The following service name on key '$keyID' will be revoked:\n %s\nAre you sure you would like to revoke this service name? (Y/n) " "$serviceName" >&2 read OK; OK=${OK:=Y} if [ "${OK/y/Y}" != 'Y' ] ; then failure "User ID not revoked." fi else log debug "revoking service name without prompting." fi # actually revoke: # the gpg secring might not contain the host key we are trying to # revoke (let alone any selfsig over that host key), but the plain # --export won't contain the secret key. "keytrans revokeuserid" # needs access to both pieces, so we feed it both of them. if gpg_host --export-secret-keys "$keyID" \ | "$SYSSHAREDIR/keytrans" revokeuserid "$keyID" "$serviceName" \ | gpg_host --import ; then gpg_host --check-trustdb update_pgp_pub_file show_key "$keyID" echo echo "NOTE: Service name revoked, but revocation not published." echo "Run '$PGRM publish-key' to publish the revocation." else failure "Problem revoking service name." fi } monkeysphere-0.43/src/share/mh/set_expire000066400000000000000000000037051342216672300205530ustar00rootroot00000000000000# -*-shell-script-*- # This should be sourced by bash (though we welcome changes to make it POSIX sh compliant) # Monkeysphere host set-expire subcommand # # This is a function to set the expiration date of the monkeysphere # host key. # # The monkeysphere scripts are written by: # Jameson Rollins # Jamie McClelland # Daniel Kahn Gillmor # # They are Copyright 2008-2010, and are all released under the GPL, # version 3 or later. set_expire() { local extendBy local keyID local formatMsg=' The possibilities are: 0 = key does not expire = key expires in n days w = key expires in n weeks m = key expires in n months y = key expires in n years' if [ -z "$1" ] ; then failure "Must specify expiration.$formatMsg" fi extendBy="$1" shift if ! <<<"$extendBy" egrep -q '^[[:digit:]]+[wmy]?$' ; then failure "'$extendBy' is not a valid expiration date.$formatMsg" fi keyID=$(check_key_input "$@") if [ "$PROMPT" != "false" ] ; then printf "Are you sure you want to change the expiration on key '$keyID' by '%s'? (Y/n) " "$extendBy" >&2 read OK; OK=${OK:-Y} if [ "${OK/y/Y}" != 'Y' ] ; then failure "expiration not set." fi else log debug "extending without prompting." fi log info "setting key expiration to ${extendBy}." log debug "executing key expire script..." gpg_host_edit "0x${keyID}!" expire <=0.23) setup. # You should be able to run this script after any version >= 0.23 is # installed. This script should be well-behaved, even if it is run # repeatedly. # Written by # Jameson Rollins # Daniel Kahn Gillmor # # Copyright 2009, released under the GPL, version 3 or later # NOTE: the reverse operation (downgrading) is not directly supported, # and MAY LOCK YOU OUT OF YOUR SYSTEM, depending on how you have # configured the monkeysphere! # any unexpected errors should cause this script to bail: set -e SYSSHAREDIR=${MONKEYSPHERE_SYSSHAREDIR:-"__SYSSHAREDIR_PREFIX__/share/monkeysphere"} export SYSSHAREDIR . "${SYSSHAREDIR}/defaultenv" MADATADIR="${SYSDATADIR}/authentication" MHDATADIR="${SYSDATADIR}/host" STASHDIR="${SYSDATADIR}/backup-from-0.23-transition" log() { printf "$@" >&2 } # FIXME: implement this function better. here, we only care about # dots, *and* about reversing the regexification of them. gpg_unescape_and_unregex() { sed 's/\\x5c\././g' } is_domain_name() { printf "%s" "$1" | egrep -q '^[[:alnum:]][[:alnum:]-.]*[[:alnum:]]$' } # move the old server conf file to be the authentication conf file if [ -f "$SYSCONFIGDIR"/monkeysphere-server.conf -a \ ! -f "$SYSCONFIGDIR"/monkeysphere-authentication.conf ] ; then mv "$SYSCONFIGDIR"/monkeysphere-server.conf "$SYSCONFIGDIR"/monkeysphere-authentication.conf fi # run the authentication setup (this is also the first chance to bail # if 0.23 is not fully-installed, because m-a did not exist before # 0.23) monkeysphere-authentication setup # before 0.23, the old gnupg-host data directory used to contain the # trust core and the system's ssh host key. if [ -d "$SYSDATADIR"/gnupg-host ] ; then ### transfer identity certifiers, if they don't already exist in the ### current setup: if monkeysphere-authentication list-identity-certifiers | \ grep -q '^[A-F0-9]{40}:$' ; then log 'There are already certifiers in the new system!\nNot transferring any certifiers.\n' else # get the old host keygrip (don't know why there would be more # than one, but we'll transfer all tsigs made by any key that # had been given ultimate ownertrust): for authgrip in $(GNUPGHOME="$SYSDATADIR"/gnupg-host gpg --quiet --no-tty --no-permission-warning --export-ownertrust | \ grep ':6:$' | \ sed -r 's/^[A-F0-9]{24}([A-F0-9]{16}):6:$/\1/') ; do # we're assuming that old id certifiers were only added by old # versions of m-s c+, which added certifiers by ltsigning # entire keys. # so we'll walk the list of tsigs from the old host key, and # add those keys as certifiers to the new system. # FIXME: if an admin has run "m-s add-id-certifier $foo" # multiple times for the same $foo, we'll only transfer # one of those certifications (even if later # certifications had different parameters). GNUPGHOME="$SYSDATADIR"/gnupg-host gpg --quiet --no-tty --no-permission-warning --fingerprint --with-colons --fixed-list-mode --check-sigs | \ cut -f 1,2,5,8,9,10 -d: | \ egrep '^(fpr:::::|sig:!:'"$authgrip"':[[:digit:]]+ [[:digit:]]+:)' | \ while IFS=: read -r type validity grip trustparams trustdomain fpr ; do case $type in 'fpr') # this is a new key keyfpr=$fpr ;; 'sig') # deal with all trust signatures, including # regexes if present. if [ "$keyfpr" ] ; then trustdepth=${trustparams%% *} trustlevel=${trustparams##* } if [ "$trustlevel" -ge 120 ] ; then truststring=full elif [ "$trustlevel" -ge 60 ] ; then truststring=marginal else # trust levels below marginal are ignored. continue fi finaldomain= if [ "$trustdomain" ] ; then # FIXME: deal with translating # $trustdomain back to a domain. if [ printf "%s" "$trustdomain" | egrep -q '^<\[\^>\]\+\[@\.\][^>]+>\$$' ] ; then dpart=$(printf "%s" "$trustdomain" | sed -r 's/^<\[\^>\]\+\[@\.\]([^>]+)>\$$/\1/' | gpg_unescape_and_unregex) if [ is_domain_name "$dpart" ]; then finaldomain="--domain $dpart" else log "Does not seem to be a domain name (%s), not adding certifier\n" "$dpart" continue fi else log "Does not seem to be a standard gpg domain-based tsig (%s), not adding certifier\n" "$trustdomain" continue fi fi CERTKEY=$(mktemp ${TMPDIR:-/tmp}/mstransition.XXXXXXXX) log "Adding identity certifier with fingerprint %s\n" "$keyfpr" GNUPGHOME="$SYSDATADIR"/gnupg-host gpg --quiet --no-tty --no-permission-warning --export "0x$keyfpr" --export-options export-clean >"$CERTKEY" MONKEYSPHERE_PROMPT=false monkeysphere-authentication add-identity-certifier $finaldomain --trust "$truststring" --depth "$trustdepth" "$CERTKEY" rm -f "$CERTKEY" # clear the fingerprint so that we don't # make additional tsigs on it if more uids # are present: keyfpr= fi ;; esac done done fi ### transfer host key information (if present) into the new spot if [ -d "${MHDATADIR}" ] ; then log "Not transferring host key info because host directory already exists.\n" else if [ -s "$SYSDATADIR"/ssh_host_rsa_key ] || \ GNUPGHOME="$SYSDATADIR"/gnupg-host gpg --quiet --no-tty --no-permission-warning --with-colons --list-secret-keys | grep -q '^sec:' ; then FPR=$(GNUPGHOME="$SYSDATADIR"/gnupg-host gpg --quiet --no-tty --no-permission-warning --with-colons --fixed-list-mode --list-secret-keys --fingerprint | awk -F: '/^fpr:/{ print $10 }' ) # create host home mkdir -p $(dirname "$MHDATADIR") NEWDATADIR=$(mktemp -d "${MHDATADIR}.XXXXXX") chmod 0700 "${NEWDATADIR}" log "importing host key from old monkeysphere installation\n" # export from the pubring as well as the that new (non-expired) # self-sigs are available, otherwise the secret key import may fail # FIXME: turns out the secret key import fails anyway, stupidly :( # FIXME: if all self-sigs are expired, then the secret key import may # fail anyway. How should we deal with that? if (GNUPGHOME="$SYSDATADIR"/gnupg-host gpg --quiet --no-tty --no-permission-warning --export-secret-keys && \ GNUPGHOME="$SYSDATADIR"/gnupg-host gpg --quiet --no-tty --no-permission-warning --export "$FPR") | \ GNUPGHOME="$NEWDATADIR" gpg --quiet --no-tty --import ; then : we are in good shape! else if ! GNUPGHOME="$NEWDATADIR" gpg --quiet --no-tty --list-secret-key >/dev/null ; then log "The old host key (%s) was not imported properly.\n" "$FPR" exit 1 fi fi # if we get here cleanly, then we're OK to move forward: mv "$NEWDATADIR" "$MHDATADIR" monkeysphere-host update-gpg-pub-file else log "No host key found in old monkeysphere install; not importing any host key.\n" fi fi ### get rid of this old stuff, since we've transferred it all: mkdir -p "$STASHDIR" chmod 0700 "$STASHDIR" mv "${SYSDATADIR}/gnupg-host" "$STASHDIR"/gnupg-host.$(date '+%F_%T%z') fi # There is nothing in the old authentication directory that we should # need to keep around, but it is not unreasonable to transfer keys to # the new authentication keyring. if [ -d "${SYSDATADIR}/gnupg-authentication" ] ; then GNUPGHOME="${SYSDATADIR}/gnupg-authentication" \ gpg --quiet --no-tty --no-permission-warning --export 2>/dev/null | \ monkeysphere-authentication gpg-cmd --import 2>/dev/null || \ log "No OpenPGP certificates imported into monkeysphere-authentication trust sphere.\n" mkdir -p "$STASHDIR" chmod 0700 "$STASHDIR" mv "${SYSDATADIR}/gnupg-authentication" "$STASHDIR"/gnupg-authentication.$(date '+%F_%T%z') fi monkeysphere-0.43/src/transitions/0.28000077500000000000000000000014671342216672300176500ustar00rootroot00000000000000#!/bin/bash # This is a post-install script for monkeysphere, to transition an old # (<0.28) setup to the new (>=0.28) setup. # You should be able to run this script after any version >= 0.23 is # installed. This script should be well-behaved, even if it is run # repeatedly. # Written by # Jameson Rollins # Daniel Kahn Gillmor # # Copyright 2010, released under the GPL, version 3 or later # any unexpected errors should cause this script to bail: set -e SYSSHAREDIR=${MONKEYSPHERE_SYSSHAREDIR:-"__SYSSHAREDIR_PREFIX__/share/monkeysphere"} export SYSSHAREDIR . "${SYSSHAREDIR}/defaultenv" OLD_HOST_KEY_FILE="$SYSDATADIR"/ssh_host_rsa_key.pub.gpg if [ -f "$OLD_HOST_KEY_FILE" ] ; then monkeysphere-host update-pgp-pub-file rm -f "$OLD_HOST_KEY_FILE" fi monkeysphere-0.43/src/transitions/README.txt000066400000000000000000000011241342216672300210170ustar00rootroot00000000000000This directory contains transition scripts for major changes to monkeysphere infrastructure. They are expected to be run immediately after upgrading to the named version or later. For example: you upgrade to from version 0.8 to version 0.15, and the directory contains 0.6, 0.12 and 0.15, you should run 0.12 followed by 0.15. The scripts are supposed to be cleverly-written enough that you can run them repeatedly, and they should only make their intended changes once. If they do not behave that way, this is a bug. Please report it! https://labs.riseup.net/code/projects/monkeysphere/ monkeysphere-0.43/tests/000077500000000000000000000000001342216672300153215ustar00rootroot00000000000000monkeysphere-0.43/tests/README000066400000000000000000000023011342216672300161750ustar00rootroot00000000000000Monkeysphere test infrastructure ================================ These are scripts to test various aspects of the Monkeysphere system. Some notes about getting started working on the monkeysphere test infrastructure: - the tests can and should be run as a non-privileged user. since the tests do potentially destructive things (like wiping out and recreating gpg keyrings) they should definitely *not* be run as root. it may even be advisable to run them as a different, dedicated user, so that any goofs you make while updating the test infrastructure don't compromise your main account. - you do not need the monkeysphere package installed locally, though you will need the monkeysphere dependencies installed locally. - the idea with this script is to do the following: - set up test server infrastructure - test the server setup - set up test user - test an ssh connection between test user and test server - modify server/user setup and rerun ssh_test to make sure it succeeds/fails as expected - there are various FIXMEs in the script that outline some of the further testing that should be undertaken. - good documentation in the code in the form of comments are needed. monkeysphere-0.43/tests/basic000077500000000000000000000560031342216672300163340ustar00rootroot00000000000000#!/usr/bin/env bash # Tests to ensure that the monkeysphere is working # # unset MONKEYSPHERE_TEST_NO_EXAMINE to get a prompt to examine the # test state after failure. # Authors: # Daniel Kahn Gillmor # Jameson Rollins # Micah Anderson # # Copyright: © 2008-2019 # License: GPL v3 or later # these tests should all be able to run as a non-privileged user. # put all the test output to stdout exec 2>&1 # all subcommands in this script should complete without failure: set -e # piped commands should return the code of the first non-zero return set -o pipefail # make sure the TESTDIR is an absolute path, not a relative one. export TESTDIR=$(cd $(dirname "$0") && pwd) source "$TESTDIR"/common ## make sure that the right tools are installed to run the test. the ## test has *more* requirements than plain ol' monkeysphere: [ -f /usr/sbin/sshd ] || { echo "You must have sshd installed to run this test." ; exit 1; } which socat >/dev/null || { echo "You must have socat installed to run this test." ; exit 1; } perl -MCrypt::OpenSSL::RSA -e 1 2>/dev/null || { echo "You must have the perl module Crypt::OpenSSL::RSA installed to run this test. On debian-derived systems, you can set this up with: apt-get install libcrypt-openssl-rsa-perl" ; exit 1; } perl -MDigest::SHA -e 1 2>/dev/null || { echo "You must have the perl module Digest::SHA installed to run this test. On debian-derived systems, you can set this up with: apt-get install libdigest-sha-perl" ; exit 1; } ## FIXME: other checks? ###################################################################### ### FUNCTIONS # gpg command for test admin user gpgadmin() { chmod 0700 "$TEMPDIR"/admin "$TEMPDIR"/admin/.gnupg GNUPGHOME="$TEMPDIR"/admin/.gnupg gpg --no-tty "$@" } # test ssh connection # first argument is expected return code from ssh connection ssh_test() { local RETURN=0 local remote_command=${1:-true} umask 0077 CODE=${2:-0} # start the ssh daemon on the socket echo "##### starting ssh server..." socat EXEC:"/usr/sbin/sshd -f ${SSHD_CONFIG} -i -D -e" "UNIX-LISTEN:${SOCKET},unlink-early" 2> "$TEMPDIR"/sshd.log & SSHD_PID="$!" # wait until the socket is created before continuing while [ ! -S "$SOCKET" ] ; do sleep 1 done # make a client connection to the socket echo "##### starting ssh client..." ssh-agent bash -c \ "monkeysphere subkey-to-ssh-agent && ssh -F $TEMPDIR/testuser/.ssh/config ${target_hostname:-testhost.example} $remote_command" \ || RETURN="$?" # kill the sshd process if it's still running kill "$SSHD_PID" || true wait SSHD_PID= if [ "$RETURN" = "$CODE" ] ; then echo "##### ssh connection test PASSED. returned: $RETURN" return 0 else echo "##### ssh connection test FAILED. returned: $RETURN, expected: $CODE" return 1 fi } # invoke this instead of ssh_test() if you want this test to be # skipped when the working directory has bad permissions. ssh_good_perm_test() { if [ "$TEMPDIR_PERMISSIONS_SAFE" = no ] ; then echo "WARNING!!! Test SKIPPED because we are running in an unsafe working directory." else ssh_test "$@" fi } SSHD_PID= ## setup trap trap failed_cleanup EXIT ###################################################################### ### SETUP VARIABLES ## set up some variables to ensure that we're operating strictly in ## the tests, not system-wide: # set up temp dir # NOTE: /tmp can not be used as the temp dir here, since the # permissions on /tmp are usually such that they will not pass the # monkeysphere/ssh path permission checking. If you need to use a # different location than the current source, please set $TMPDIR # somewhere with tighter permissions. mkdir -p "$TESTDIR"/tmp TEMPDIR=$(mktemp -d "${TMPDIR:-$TESTDIR/tmp}/ms.XXX") if [ -z "$MONKEYSPHERE_TEST_USE_SYSTEM" ] ; then # Use the local copy of executables first, instead of system ones. # This should help us test without installing. export PATH="$TESTDIR/../src:$TESTDIR/../src/agent-transfer:$PATH" export MONKEYSPHERE_SYSSHAREDIR="$TESTDIR"/../src/share else if ! which monkeysphere-host; then PATH=/usr/sbin:"$PATH" fi fi export MONKEYSPHERE_SYSCONFIGDIR="$TEMPDIR" export MONKEYSPHERE_SYSDATADIR="$TEMPDIR" export MONKEYSPHERE_MONKEYSPHERE_USER=$(whoami) HOST_KEY_FILE="$MONKEYSPHERE_SYSCONFIGDIR"/host_keys.pub.pgp export MONKEYSPHERE_CHECK_KEYSERVER=false # example.org does not respond to the HKP port, so this should cause # any keyserver connection attempts that do happen (they shouldn't!) # to hang, so we'll notice them: export MONKEYSPHERE_KEYSERVER=example.org export MONKEYSPHERE_LOG_LEVEL=DEBUG export MONKEYSPHERE_CORE_KEYLENGTH=3072 export MONKEYSPHERE_PROMPT=false # unset SUBKEYS_FOR_AGENT variable which, if set, would confuse the # into trying to use the user's key, instead of the testuser's key unset MONKEYSPHERE_SUBKEYS_FOR_AGENT # unset MONKEYSPHERE_VALIDATION_AGENT_SOCKET variable which, if set, # would confuse the test into trying to talk the the user's agent unset MONKEYSPHERE_VALIDATION_AGENT_SOCKET export SSHD_CONFIG="$TEMPDIR"/sshd_config export SOCKET="$TEMPDIR"/ssh-socket # Make sure $DISPLAY is set to convince ssh and monkeysphere to fall # back on $SSH_ASKPASS. Make sure it's not set to the current actual # $DISPLAY (if one exists) because this test suite should not be doing # *anything* with any running X11 session. export DISPLAY=monkeys ## we cannot do proper directory permissions checking if the current ## working directory has unsatisfactory permissions: if "$MONKEYSPHERE_SYSSHAREDIR"/checkperms $(whoami) "$TEMPDIR"; then echo "Permissions on temporary directory '$TEMPDIR' are OK for permissions checks." TEMPDIR_PERMISSIONS_SAFE=yes else cat <> "$TESTHOME"/.ssh/config UserKnownHostsFile $TESTHOME/.ssh/known_hosts IdentityFile $TESTHOME/.ssh/no-such-identity ProxyCommand $TESTHOME/.ssh/proxy-command %h %p $SOCKET EOF cat <> "$MONKEYSPHERE_HOME"/monkeysphere.conf KNOWN_HOSTS=$TESTHOME/.ssh/known_hosts EOF cat < "$GNUPGHOME"/gpg.conf verify-options show-uid-validity list-options show-uid-validity EOF get_gpg_prng_arg >> "$GNUPGHOME"/gpg.conf echo pinentry-program "$TESTDIR"/phony-pinentry >> "$GNUPGHOME/gpg-agent.conf" gpg --import < "$TESTDIR"/home-setup/testuser/secret_keyring.keys gpg --import-options import-local --import < "$TESTDIR"/home-setup/testuser/public_keyring.keys gpg --import-ownertrust < "$TESTDIR"/home-setup/testuser/ownertrustdb.txt echo echo "##################################################" echo "### configuring admin home..." mkdir -m 0700 -p "$TEMPDIR"/admin/.gnupg gpgadmin --import < "$TESTDIR"/home-setup/admin/secret_keyring.keys gpgadmin --import-options import-local --import < "$TESTDIR"/home-setup/admin/public_keyring.keys gpgadmin --import-ownertrust < "$TESTDIR"/home-setup/admin/ownertrustdb.txt # set up sshd echo echo "##################################################" echo "### configuring sshd..." cp "$TESTDIR"/etc/ssh/sshd_config "$SSHD_CONFIG" # write the sshd_config cat <> "$SSHD_CONFIG" HostKey ${MONKEYSPHERE_SYSDATADIR}/ssh_host_rsa_key AuthorizedKeysFile ${MONKEYSPHERE_SYSDATADIR}/authorized_keys/%u EOF # disable sshd's strict permissions settings so that some tests can # complete when running under a dubious path: if [ "$TEMPDIR_PERMISSIONS_SAFE" != yes ] ; then cat <> "$SSHD_CONFIG" StrictModes no EOF fi ###################################################################### ### SERVER HOST SETUP # import host key echo echo "##################################################" echo "### import host key..." ssh-keygen -m PEM -b 3072 -t rsa -N '' -f "$TEMPDIR"/ssh_host_rsa_key monkeysphere-host import-key "$TEMPDIR"/ssh_host_rsa_key ssh://testhost.example echo echo "##################################################" echo "### getting host key fingerprint..." SSHHOSTKEYID=$( monkeysphere-host show-keys | grep '^OpenPGP fingerprint: ' | cut -f3 -d\ ) echo "$SSHHOSTKEYID" # change host key expiration echo echo "##################################################" echo "### setting host key expiration..." monkeysphere-host set-expire 1 # FIXME: how do we check that the expiration has really been set? # certify host key with the "Admin's Key". # (this would normally be done via keyservers) echo echo "##################################################" echo "### certifying server host key..." < "$HOST_KEY_FILE" gpgadmin --import echo y | gpgadmin --command-fd 0 --sign-key "$SSHHOSTKEYID" # FIXME: add revoker? # FIXME: how can we test publish-key without flooding junk into the # keyservers? # FIXME: should we run "diagnostics" here to test setup? ###################################################################### ### SERVER AUTHENTICATION SETUP # set up monkeysphere authentication echo echo "##################################################" echo "### setup monkeysphere authentication..." cp "$TESTDIR"/etc/monkeysphere/monkeysphere-authentication.conf "$TEMPDIR"/ cat <> "$TEMPDIR"/monkeysphere-authentication.conf AUTHORIZED_USER_IDS="$MONKEYSPHERE_HOME/authorized_user_ids" EOF monkeysphere-authentication setup get_gpg_prng_arg >> "$MONKEYSPHERE_SYSDATADIR"/authentication/sphere/gpg.conf # add admin as identity certifier for testhost.example echo echo "##################################################" echo "### adding admin as certifier..." gpgadmin --export '' | monkeysphere-authentication add-id-certifier - echo echo "##################################################" echo "### list certifiers..." monkeysphere-authentication list-certifiers # FIXME: should we run "diagnostics" here to test setup? ###################################################################### ### TESTUSER SETUP # generate an auth subkey for the test user that expires in 2 days echo echo "##################################################" if [ "$MONKEYSPHERE_TEST_USE_ED25519" = true ]; then echo "### generating ed25519 key for testuser..." # from the imported secret key USER_FPR=8A4B353B4CBA6F30625498BAE00B5EEEBA79B482 gpg --quick-add-key "$USER_FPR" ed25519 auth 2d else echo "### generating standard monkeysphere key for testuser..." monkeysphere gen-subkey fi # add server key to testuser keychain echo echo "##################################################" echo "### export server key to testuser..." gpgadmin --armor --export "$SSHHOSTKEYID" | gpg --import # teach the "server" about the testuser's key echo echo "##################################################" echo "### export testuser key to server..." gpg --export testuser | monkeysphere-authentication gpg-cmd --import # update authorized_keys for user echo echo "##################################################" echo "### update server authorized_keys file for this testuser..." monkeysphere-authentication update-users $(whoami) # FIXME: this is maybe not failing properly for: # ms: improper group or other writability on path '/tmp'. ###################################################################### ### TESTS ## see whether keys-for-userid works from the client's perspective: echo echo "##################################################" echo "### testing monkeysphere keys-for-userid ..." diff <( monkeysphere keys-for-userid ssh://testhost.example ) <( cut -f1,2 -d' ' < "$TEMPDIR"/ssh_host_rsa_key.pub ) # connect to test sshd, using monkeysphere ssh-proxycommand to verify # the identity before connection. This should work in both directions! echo echo "##################################################" echo "### ssh connection test for success..." ssh_test true # Make sure it works if there is "armor" written in gpg.conf # add other weirdnesses here as they come up. echo echo "##################################################" echo "### testing functionality in the face of unusual gpg.conf settings..." echo 'armor' >> "$GNUPGHOME"/gpg.conf ssh_test true # remove the testuser's authorized_user_ids file, update, and make # sure that the ssh authentication FAILS echo echo "##################################################" echo "### removing testuser authorized_user_ids and updating..." mv "$TESTHOME"/.monkeysphere/authorized_user_ids{,.bak} monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for failure..." ssh_test true 255 mv "$TESTHOME"/.monkeysphere/authorized_user_ids{.bak,} # put improper permissions on authorized_user_ids file, update, and # make sure ssh authentication FAILS echo echo "##################################################" echo "### setting group writability on authorized_user_ids and updating..." chmod g+w "$TESTHOME"/.monkeysphere/authorized_user_ids monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for failure..." ssh_good_perm_test true 255 chmod g-w "$TESTHOME"/.monkeysphere/authorized_user_ids echo echo "##################################################" echo "### setting other writability on authorized_user_ids and updating..." chmod o+w "$TESTHOME"/.monkeysphere/authorized_user_ids monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for failure..." ssh_good_perm_test true 255 chmod o-w "$TESTHOME"/.monkeysphere/authorized_user_ids monkeysphere-authentication update-users $(whoami) # test symlinks echo echo "##################################################" echo "### setup for symlink tests..." cp -a "$TESTHOME"/.monkeysphere{,.linktest} echo echo "##################################################" echo "### make authorized_user_ids an absolute symlink and updating..." mv "$TESTHOME"/.monkeysphere/authorized_user_ids{,.bak} ln -s "$TESTHOME"/.monkeysphere{.linktest,}/authorized_user_ids monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for success..." ssh_test true echo echo "##################################################" echo "### create bad permissions on link dir and updating..." chmod o+w "$TESTHOME"/.monkeysphere.linktest monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for failure..." ssh_good_perm_test true 255 chmod o-w "$TESTHOME"/.monkeysphere.linktest echo echo "##################################################" echo "### make authorized_user_ids a relative symlink and updating..." ln -sf ../.monkeysphere.linktest/authorized_user_ids "$TESTHOME"/.monkeysphere/authorized_user_ids monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for success..." ssh_test true echo echo "##################################################" echo "### create bad permissions on link dir updating..." chmod o+w "$TESTHOME"/.monkeysphere.linktest monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for failure..." ssh_good_perm_test true 255 chmod o-w "$TESTHOME"/.monkeysphere.linktest # FIXME: implement check of link path, and uncomment this test # echo # echo "##################################################" # echo "### create bad permissions on link dir and updating..." # chmod o+w "$TESTHOME"/.monkeysphere # monkeysphere-authentication update-users $(whoami) # echo # echo "##################################################" # echo "### ssh connection test for failure..." # ssh_good_perm_test true 255 # chmod o-w "$TESTHOME"/.monkeysphere rm "$TESTHOME"/.monkeysphere/authorized_user_ids mv "$TESTHOME"/.monkeysphere/authorized_user_ids{.bak,} echo echo "##################################################" echo "### make .monkeysphere directory an absolute symlink and updating..." mv "$TESTHOME"/.monkeysphere{,.bak} ln -s "$TESTHOME"/.monkeysphere{.linktest,} monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for success..." ssh_test true echo echo "##################################################" echo "### create bad permissions on link dir and updating..." chmod o+w "$TESTHOME"/.monkeysphere.linktest monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for failure..." ssh_good_perm_test true 255 chmod o-w "$TESTHOME"/.monkeysphere.linktest echo echo "##################################################" echo "### make .monkeysphere directory a relative symlink and updating..." ln -sfn .monkeysphere.linktest "$TESTHOME"/.monkeysphere monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for success..." ssh_test true echo echo "##################################################" echo "### create bad permissions on link dir updating..." chmod o+w "$TESTHOME"/.monkeysphere.linktest monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for failure..." ssh_good_perm_test true 255 chmod o-w "$TESTHOME"/.monkeysphere.linktest rm "$TESTHOME"/.monkeysphere mv "$TESTHOME"/.monkeysphere{.bak,} # ensure we're back to normal: echo echo "##################################################" echo "### making sure we are back to normal..." monkeysphere-authentication update-users $(whoami) ssh_test true # check ssh authorized_key options echo echo "##################################################" echo "### checking ssh authorized_key option support..." cp "$TESTHOME"/.monkeysphere/authorized_user_ids{,.bak} echo ' no-X11-forwarding' >>"$TESTHOME"/.monkeysphere/authorized_user_ids echo ' no-port-forwarding' >>"$TESTHOME"/.monkeysphere/authorized_user_ids echo ' command="/bin/false"' >>"$TESTHOME"/.monkeysphere/authorized_user_ids monkeysphere-authentication update-users $(whoami) ssh_test /bin/true 1 ssh_test /bin/false 1 mv "$TESTHOME"/.monkeysphere/authorized_user_ids{.bak,} # ensure we're back to normal: echo echo "##################################################" echo "### making sure we are back to normal..." monkeysphere-authentication update-users $(whoami) ssh_test true echo echo "##################################################" echo "### ssh connection test directly to 'testhost2.example' without new name..." target_hostname=testhost2.example ssh_test true 255 echo echo "##################################################" echo "### add servicename, certify by admin, import by user..." monkeysphere-host add-servicename ssh://testhost2.example <"$HOST_KEY_FILE" gpgadmin --import printf "y\ny\n" | gpgadmin --command-fd 0 --sign-key "$SSHHOSTKEYID" echo echo "##################################################" echo "### ssh connection test with hostname 'testhost2.example' added..." gpgadmin --export "$SSHHOSTKEYID" | gpg --import gpg --check-trustdb ssh_test true echo echo "##################################################" echo "### ssh connection test directly to 'testhost2.example' ..." gpg --import <"$HOST_KEY_FILE" gpg --check-trustdb target_hostname=testhost2.example ssh_test true echo echo "##################################################" echo "### ssh connection test for failure with 'testhost2.example' revoked..." monkeysphere-host revoke-servicename ssh://testhost2.example gpg --import <"$HOST_KEY_FILE" gpg --check-trustdb target_hostname=testhost2.example ssh_test true 255 # FIXME: addtest: remove admin as id-certifier and check ssh failure # FIXME: addtest: how do we test that set-expire makes sense after new # servicenames have been added? echo echo "##################################################" echo "### testing monkeysphere authentication keys-for-user" diff <(monkeysphere-authentication keys-for-user $(whoami) | cut -d' ' -f1,2) <(cut -d' ' -f1,2 ${MONKEYSPHERE_SYSDATADIR}/authorized_keys/${MONKEYSPHERE_MONKEYSPHERE_USER}) # test to make sure things are OK after the previous tests: echo echo "##################################################" echo "### settings reset, updating..." monkeysphere-authentication update-users $(whoami) echo echo "##################################################" echo "### ssh connection test for success..." ssh_test true echo echo "##################################################" echo "### Testing TLS setup..." openssl req -config "$TESTDIR"/openssl.cnf -x509 -newkey rsa:3072 -subj '/DC=example/DC=testhost/CN=testhost.example/' -days 3 -keyout "$TEMPDIR"/tls_key.pem -nodes >"$TEMPDIR"/tls_cert.pem monkeysphere-host import-key "$TEMPDIR"/tls_key.pem https://testhost.example # FIXME: how can we test this via an https client? # We don't currently provide one. # FIXME: should we test other monkeysphere-host operations somehow now # that we have more than one key in the host keyring? echo echo "##################################################" echo "### revoking ssh host key..." # generate the revocation certificate and feed it directly to the test # user's keyring (we're not publishing to the keyservers) monkeysphere-host revoke-key "$SSHHOSTKEYID" | gpg --import echo echo "##################################################" echo "### ssh connection test for failure..." ssh_test true 255 ###################################################################### trap - EXIT echo echo "##################################################" echo " Monkeysphere basic tests completed successfully!" echo "##################################################" cleanup monkeysphere-0.43/tests/common000066400000000000000000000013731342216672300165400ustar00rootroot00000000000000# -*-shell-script-*- failed_cleanup() { # FIXME: can we be more verbose here? echo 'FAILED!' if [ -z "$MONKEYSPHERE_TEST_NO_EXAMINE" ] ; then printf "press enter to cleanup and remove tmp (or type 'bash' for a subshell to examine): " >&2 read XX if [ "$XX" = bash ] ; then echo "Entering subshell..." cd "$TEMPDIR" bash fi fi cleanup } get_gpg_prng_arg() { if (gpg --quick-random --version >/dev/null 2>&1) ; then echo quick-random elif (gpg --debug-quick-random --version >/dev/null 2>&1) ; then echo debug-quick-random fi } cleanup() { echo "### removing temp dir..." rm -rf "$TEMPDIR" if [ "$SSHD_PID" ] ; then echo "### killing off lingering sshd..." kill "$SSHD_PID" fi wait } monkeysphere-0.43/tests/etc/000077500000000000000000000000001342216672300160745ustar00rootroot00000000000000monkeysphere-0.43/tests/etc/monkeysphere/000077500000000000000000000000001342216672300206055ustar00rootroot00000000000000monkeysphere-0.43/tests/etc/monkeysphere/monkeysphere-authentication.conf000066400000000000000000000002401342216672300271760ustar00rootroot00000000000000# Base monkeysphere-authentication.conf for monkeysphere tests # AUTHORIZED_USER_IDS variable will be added dynamically during test. RAW_AUTHORIZED_KEYS=none monkeysphere-0.43/tests/etc/ssh/000077500000000000000000000000001342216672300166715ustar00rootroot00000000000000monkeysphere-0.43/tests/etc/ssh/sshd_config000066400000000000000000000007601342216672300211050ustar00rootroot00000000000000# Base sshd_config for monkeysphere test # HostKey and AuthorizedKeysFile lines will be added dynamically # during test. # goal: minimal ssh configuration to do public key authentication. Protocol 2 PubkeyAuthentication yes HostbasedAuthentication no PermitEmptyPasswords no ChallengeResponseAuthentication no PasswordAuthentication no KerberosAuthentication no GSSAPIAuthentication no X11Forwarding no PrintMotd no PrintLastLog no TCPKeepAlive no AcceptEnv LANG LC_* UsePAM no LogLevel DEBUG monkeysphere-0.43/tests/home-setup/000077500000000000000000000000001342216672300174075ustar00rootroot00000000000000monkeysphere-0.43/tests/home-setup/admin/000077500000000000000000000000001342216672300204775ustar00rootroot00000000000000monkeysphere-0.43/tests/home-setup/admin/ownertrustdb.txt000066400000000000000000000002461342216672300240040ustar00rootroot00000000000000# List of assigned trustvalues, created Fri 17 Jun 2016 02:45:59 AM EDT # (Use "gpg --import-ownertrust" to restore them) 4275279C9512E14BDD14098A36FF78B37005D3BE:6: monkeysphere-0.43/tests/home-setup/admin/public_keyring.keys000066400000000000000000000012261342216672300244030ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v2 mI0ESPEWYQEEANNbGiaPPvsSCA1OULPF7L3Nid70hLMmDTpKxLg3AZv5D3abhS1L IeNPE149O49FVAhsKEMZStfH0CYS1EBllgFbw/qQ19u3nExysvNh+wXRv06dji5u J2eviZJ2RMcz3tnlgb+3rE8YL6WeB1AEWzZKtUFK7ntxIRjWXlDwoDo1ABEBAAG0 WE1vbmtleXNwaGVyZSBUZXN0IFN1aXRlIEZha2UgQWRtaW5pc3RyYXRpdmUgVXNl ciAoRE8gTk9UIFVTRSEhISkgPGZha2VhZG1pbkBleGFtcGxlLm5ldD6ItgQTAQIA IAUCSPEWYQIbAwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEDb/eLNwBdO+maEE AJR0+/ZQot/amz1uBJn+HTemGKrCO6U7iPKN2ZpsvDNGK+W3qOg1zT71PudPxoRy WKJiOvJWn1VtQFqmZmgq89eeXC76FqU4kIForxqKnBaOIkRbVHAxc4PNFO5ve5qI Y4g/ZxKpQBWFegSZ+KVE9hj05UeYiudgQ1Q7wuPPRg3Q =I7pC -----END PGP PUBLIC KEY BLOCK----- monkeysphere-0.43/tests/home-setup/admin/secret_keyring.keys000066400000000000000000000037071342216672300244200ustar00rootroot00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- lQHYBEjxFmEBBADTWxomjz77EggNTlCzxey9zYne9ISzJg06SsS4NwGb+Q92m4Ut SyHjTxNePTuPRVQIbChDGUrXx9AmEtRAZZYBW8P6kNfbt5xMcrLzYfsF0b9OnY4u bidnr4mSdkTHM97Z5YG/t6xPGC+lngdQBFs2SrVBSu57cSEY1l5Q8KA6NQARAQAB AAP9EqvKIBzobeqWXoKMG42BD/Vf2BvWuzLB2WPwg+uMD1jNtmcfuTQZbsi2B821 /L6doU7YsbDCzVQf/bUNHhE7UoNMu/jcDWXdHq9UqXnb/b12rs4S6Z+I2pMPudFE PUldxb6YbZL4H1aVj9eDpcQxw4PK3auAX0obtAPSxRY75HMCAOZNQbwEQOFOE8iA l1ad87MpN5WpqXvCOUAUoyXbpk5xcAqwfcSohCs4tRa2W8t5/pi9VDfxX2adMBcr 1xDgW8sCAOrwpBxvv86cX93C+6XzeU3b1PrcX99Wm1NucKyUnVIoJCD71QAu3pfw Fsu+eDKfH+GeXT0ruXrlRY8p8is7Af8B/0Up4cvgs4p0KBHVaoxNfwusMBWv+lAH qRwiHLLMJNUWfQShfbadLqounhTalWRORiEa7ucKBfBl3tNDH0Xd/6+l07RYTW9u a2V5c3BoZXJlIFRlc3QgU3VpdGUgRmFrZSBBZG1pbmlzdHJhdGl2ZSBVc2VyIChE TyBOT1QgVVNFISEhKSA8ZmFrZWFkbWluQGV4YW1wbGUubmV0Poi2BBMBAgAgBQJI 8RZhAhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AACgkQNv94s3AF076ZoQQAlHT7 9lCi39qbPW4Emf4dN6YYqsI7pTuI8o3Zmmy8M0Yr5beo6DXNPvU+50/GhHJYomI6 8lafVW1AWqZmaCrz155cLvoWpTiQgWivGoqcFo4iRFtUcDFzg80U7m97mohjiD9n EqlAFYV6BJn4pUT2GPTlR5iK52BDVDvC489GDdCdAdgEWEElpAEEAMv4gZ3ioPBv TV7iJAlvf2Bl6EMvnGXoxyvnoayGxgXj2cmCr2PTmtl4p0XYd61wnQl+XQGX3r54 GsZfpjc4OMsQfvt/E+XyThJ//JkSV/uqU0dRr6cxjhvZNNyptusIEddur+ZeYkvI Xyz2xQ2z29B7+qnYNSdPKoiYBM6aPy9JABEBAAEAA/0STOGUQNkfuVy/vpptHkvX Yk1MeeEWY6Q7FDU1PYJW1PKdWdRSqJGkIWcgpSNXtFrilyvREMczX6NHWR2Yprqx lEWo7yYb2Us3zOShn1GNmCpL8vtn5ft/b8vE4GQauajv6Ug9+ATE4CvBTkR/K1C7 nIqhC0UOwFCbLD4Qy3MzYQIA0Nl2Ly5qR+T8BjrXiKJSbxf+G2Ety+TddknBLAxw Ul5tMzbCsbEXXX5Lie3NIyLoy7RGDBvb3BHvs3HggEmt7wIA+gUTjqHIUUlYTKIg GnbA10ZAn4VhTdQyZpwwNS1N4GHYMsJQmAX6CVPBSybGqzKCGpU16clyOaeYroic L2EuRwH+MenAFJdzhtQfQbJJ9ymtHjAzK2lgzDqObxPV8kZdFYoEFW7GpZYA9x4m 7mMcfcvp4Fsgt4BswJJ9lgWS+dCqY5rViLYEGAEIACAWIQRCdSeclRLhS90UCYo2 /3izcAXTvgUCWEElpAIbDAAKCRA2/3izcAXTvtuWA/9uGptsbfv0bPn6J90IvEMe cU/UopxijmympU/G4089RmfprjhEkXWI20/xi7Y/iKp6NpLcdImIIsZouQG3CQxO kAc81qeUJPo5eF4uAO3JBCoZkQA9hh7dAISkD35M9aip0SxHLTUd99TUl96fM1XK k5zWszPZPe1Oz7SebbUgDg== =fOOz -----END PGP PRIVATE KEY BLOCK----- monkeysphere-0.43/tests/home-setup/testuser/000077500000000000000000000000001342216672300212655ustar00rootroot00000000000000monkeysphere-0.43/tests/home-setup/testuser/ownertrustdb.txt000066400000000000000000000003221342216672300245650ustar00rootroot00000000000000# List of assigned trustvalues, created Fri 17 Jun 2016 02:44:59 AM EDT # (Use "gpg --import-ownertrust" to restore them) 8A4B353B4CBA6F30625498BAE00B5EEEBA79B482:6: 4275279C9512E14BDD14098A36FF78B37005D3BE:5: monkeysphere-0.43/tests/home-setup/testuser/public_keyring.keys000066400000000000000000000031231342216672300251670ustar00rootroot00000000000000-----BEGIN PGP PUBLIC KEY BLOCK----- mI0ESPPkSQEEALmNpNQ5hko9iOy16JTV3ATS7DuYzRwBWprMlBbBh3rRofQ3Dv1U 3c2rQH+KMl2vvXhjZHl/AkFsZoE9rJW2+ituHGq1ZpqXGwZ4RChpLkfC6cj91rOq PS7lSNlxvGuS89NkocBxlfA499YjISbWtU7S2LeZ3azHHbe22r0hd0gHABEBAAG0 SE1vbmtleXNwaGVyZSBUZXN0IFN1aXRlIFRlc3QgVXNlciAoRE8gTk9UIFVTRSEh ISkgPHRlc3R1c2VyQGV4YW1wbGUubmV0Poi2BBMBAgAgBQJI8+RJAhsDBgsJCAcD AgQVAggDBBYCAwECHgECF4AACgkQ4Ate7rp5tII5mgP/V2baETuBTab/CU4btM+1 UM/zYS6hbBRoZSZnG2Bgysv3f88+JbGRBQoDNzagQ39HsPAjdg24STkjdDxvmmt0 wot9gMaqmt91EjeaZ22jMnKoUvVQkTApgtPl6o+902NrW2Qb24X4safM4r28Reax LVkBRE+6u3AF5GZUs696qgyInAQQAQIABgUCSQN+kAAKCRA2/3izcAXTvh48A/9V o50pU5psfXgFrAx4xLpWgLIjDkNZMnbBZ83hZejNbR9ZzW/YTdkUXfDOcvEFKa5S qiaEVjdS8kDtAlauLnyKDMY6Uxdl3vEGnm/6PpDEunfvS6TGplDAGTpZT6OEFUv2 0/B02ehiryYQ6NZxnSFyoTbH+SBef/raH3WPsYHEk5iNBEjxFmEBBADTWxomjz77 EggNTlCzxey9zYne9ISzJg06SsS4NwGb+Q92m4UtSyHjTxNePTuPRVQIbChDGUrX x9AmEtRAZZYBW8P6kNfbt5xMcrLzYfsF0b9OnY4ubidnr4mSdkTHM97Z5YG/t6xP GC+lngdQBFs2SrVBSu57cSEY1l5Q8KA6NQARAQABtFhNb25rZXlzcGhlcmUgVGVz dCBTdWl0ZSBGYWtlIEFkbWluaXN0cmF0aXZlIFVzZXIgKERPIE5PVCBVU0UhISEp IDxmYWtlYWRtaW5AZXhhbXBsZS5uZXQ+iLYEEwECACAFAkjxFmECGwMGCwkIBwMC BBUCCAMEFgIDAQIeAQIXgAAKCRA2/3izcAXTvpmhBACUdPv2UKLf2ps9bgSZ/h03 phiqwjulO4jyjdmabLwzRivlt6joNc0+9T7nT8aEcliiYjryVp9VbUBapmZoKvPX nlwu+halOJCBaK8aipwWjiJEW1RwMXODzRTub3uaiGOIP2cSqUAVhXoEmfilRPYY 9OVHmIrnYENUO8Ljz0YN0IifBBABAgAJBQJJA34PAgQAAAoJEOALXu66ebSCEsgE ALU6f27lL4q8sU+Z1EzJNswvg5nGYkHYqRxWwWISieECD4L8qgSBwDEefJ7r8YBU CWAhYW/CHscM62vYWxmBYrpEfZTY8ZinPU5MKOXJfsHHwFYACtN+6VIi90V74e7V gEYiOWHVDFOXt+jm92olQ8tSP1N+G6J0LXs3yLDOJYTK =mJRL -----END PGP PUBLIC KEY BLOCK----- monkeysphere-0.43/tests/home-setup/testuser/secret_keyring.keys000066400000000000000000000025111342216672300251760ustar00rootroot00000000000000-----BEGIN PGP PRIVATE KEY BLOCK----- lQIGBEjz5EkBBAC5jaTUOYZKPYjsteiU1dwE0uw7mM0cAVqazJQWwYd60aH0Nw79 VN3Nq0B/ijJdr714Y2R5fwJBbGaBPayVtvorbhxqtWaalxsGeEQoaS5HwunI/daz qj0u5UjZcbxrkvPTZKHAcZXwOPfWIyEm1rVO0ti3md2sxx23ttq9IXdIBwARAQAB /gcDAuXaHu9dBA2w5lXTBsz2c0YjWRO9RfEJlEMKwKko9jSerctbeSgqbw1h/Qu4 6rMU0ybGuzQbfbQlgFRvYlLjM3ZPe3zCSXBKD8/H5PLaYBlyg6KSY0LaYR7MVZbZ jYruRz2WBN+3IEiDmO2BCliE60t4Eh19Qf2gXE8PxnE+rKT+eiLjQrRPqyxtbDQw eK7SbTLMXKFvPFy2b9oTnLuKNZGlw3briapKxUlr4sWoRgZE/166g4T41srhkMzw SRWoy63cN+MsB24hbEGLrmQSmh8Oie6XoXre1PVOOZXpKE8KNyqMgbsQqD46Ao/U kmkHfInqWKqpF3Q6FTeJu/MeG3oGSuoPWkuh4O6DHbQ4GOYEN81jhUqOF5nR7vIa CK9t66RuA5DG20V3OQsYt0K+L2E74ixgoGfRpyBb51ibF4TpfvGtFK87OgTaTnIE 5OspSR+L6U9bKL6U0biqVU6gPqcDSjRHFt7depGQTtxov232pD1jmq+0SE1vbmtl eXNwaGVyZSBUZXN0IFN1aXRlIFRlc3QgVXNlciAoRE8gTk9UIFVTRSEhISkgPHRl c3R1c2VyQGV4YW1wbGUubmV0Poi2BBMBAgAgBQJI8+RJAhsDBgsJCAcDAgQVAggD BBYCAwECHgECF4AACgkQ4Ate7rp5tII5mgP/V2baETuBTab/CU4btM+1UM/zYS6h bBRoZSZnG2Bgysv3f88+JbGRBQoDNzagQ39HsPAjdg24STkjdDxvmmt0wot9gMaq mt91EjeaZ22jMnKoUvVQkTApgtPl6o+902NrW2Qb24X4safM4r28ReaxLVkBRE+6 u3AF5GZUs696qgyInAQQAQIABgUCSQN+kAAKCRA2/3izcAXTvh48A/9Vo50pU5ps fXgFrAx4xLpWgLIjDkNZMnbBZ83hZejNbR9ZzW/YTdkUXfDOcvEFKa5SqiaEVjdS 8kDtAlauLnyKDMY6Uxdl3vEGnm/6PpDEunfvS6TGplDAGTpZT6OEFUv20/B02ehi ryYQ6NZxnSFyoTbH+SBef/raH3WPsYHEkw== =UVPj -----END PGP PRIVATE KEY BLOCK----- monkeysphere-0.43/tests/home/000077500000000000000000000000001342216672300162515ustar00rootroot00000000000000monkeysphere-0.43/tests/home/testuser/000077500000000000000000000000001342216672300201275ustar00rootroot00000000000000monkeysphere-0.43/tests/home/testuser/.monkeysphere/000077500000000000000000000000001342216672300227165ustar00rootroot00000000000000monkeysphere-0.43/tests/home/testuser/.monkeysphere/authorized_user_ids000066400000000000000000000001111342216672300267050ustar00rootroot00000000000000Monkeysphere Test Suite Test User (DO NOT USE!!!) monkeysphere-0.43/tests/home/testuser/.monkeysphere/monkeysphere.conf000066400000000000000000000002121342216672300262710ustar00rootroot00000000000000# monkeysphere config for testuser in monkeysphere test suite LOG_LEVEL=DEBUG # KNOWN_HOSTS will be dynamically defined after creation. monkeysphere-0.43/tests/home/testuser/.ssh/000077500000000000000000000000001342216672300210025ustar00rootroot00000000000000monkeysphere-0.43/tests/home/testuser/.ssh/askpass000077500000000000000000000001651342216672300223770ustar00rootroot00000000000000#!/usr/bin/env bash # phony/automatic askpass, to provide the passphrase for the # testuser's GPG key. echo abc123 monkeysphere-0.43/tests/home/testuser/.ssh/config000066400000000000000000000004471342216672300221770ustar00rootroot00000000000000# ssh config file for testuser for monkeysphere test suite. Host * ChallengeResponseAuthentication no PasswordAuthentication no KbdInteractiveAuthentication no GSSAPIAuthentication no StrictHostKeyChecking yes LogLevel DEBUG # UserKnownHostsFile and ProxyCommand will be filled in dynamically. monkeysphere-0.43/tests/home/testuser/.ssh/proxy-command000077500000000000000000000003771342216672300235340ustar00rootroot00000000000000#!/usr/bin/env bash # simple socket-based proxy-command wrapper for testing monkeysphere. # pass this thing the host, the port, and the socket. which monkeysphere >&2 monkeysphere ssh-proxycommand --no-connect "$1" "$2" && \ exec socat STDIO UNIX:"$3" monkeysphere-0.43/tests/keytrans000077500000000000000000000167351342216672300171230ustar00rootroot00000000000000#!/usr/bin/env bash # Tests to ensure that the monkeysphere is working # Authors: # Daniel Kahn Gillmor # Jameson Rollins # Micah Anderson # # Copyright: 2008-2009 # License: GPL v3 or later # these tests should all be able to run as a non-privileged user. # all subcommands in this script should complete without failure: set -e # piped commands should return the code of the first non-zero return set -o pipefail # make sure the TESTDIR is an absolute path, not a relative one. export TESTDIR=$(cd $(dirname "$0") && pwd) source "$TESTDIR"/common perl -MCrypt::OpenSSL::RSA -e 1 2>/dev/null || { echo "You must have the perl module Crypt::OpenSSL::RSA installed to run this test. On debian-derived systems, you can set this up with: apt-get install libcrypt-openssl-rsa-perl" ; exit 1; } perl -MDigest::SHA -e 1 2>/dev/null || { echo "You must have the perl module Digest::SHA installed to run this test. On debian-derived systems, you can set this up with: apt-get install libdigest-sha1-perl" ; exit 1; } ###################################################################### ### SETUP VARIABLES ## set up some variables to ensure that we're operating strictly in ## the tests, not system-wide: mkdir -p "$TESTDIR"/tmp TEMPDIR=$(mktemp -d "${TMPDIR:-$TESTDIR/tmp}/ms.XXX") if [ -z "$MONKEYSPHERE_TEST_USE_SYSTEM" ] ; then mkdir "$TEMPDIR"/bin ln -s "$TESTDIR"/../src/share/keytrans "$TEMPDIR"/bin/openpgp2ssh ln -s "$TESTDIR"/../src/share/keytrans "$TEMPDIR"/bin/pem2openpgp ln -s "$TESTDIR"/../src/share/keytrans "$TEMPDIR"/bin/keytrans # Use the local copy of executables first, instead of system ones. # This should help us test without installing. export PATH="$TEMPDIR"/bin:"$PATH" else export PATH=/usr/share/monkeysphere:"$PATH" fi ## setup trap trap failed_cleanup EXIT ###################################################################### ### TEST KEYTRANS echo "##################################################" echo "### generating openpgp key..." export GNUPGHOME="$TEMPDIR" chmod 700 "$TEMPDIR" # create the key with the same preferences that monkeysphere uses. cat > "$TEMPDIR"/gpg.conf < "$TEMPDIR"/gpg-agent.conf < \ "$TEMPDIR"/test.pem gpg --export-secret-keys > "$TEMPDIR"/secret.key PEM2OPENPGP_USAGE_FLAGS=sign,certify \ PEM2OPENPGP_TIMESTAMP="$timestamp" pem2openpgp testtest \ < "$TEMPDIR"/test.pem > "$TEMPDIR"/converted.secret.key echo "##################################################" echo "### reconvert key, and compare to key in gpg keyring..." diff -u \ <(gpg --list-packets < "$TEMPDIR"/secret.key) \ <(gpg --list-packets < "$TEMPDIR"/converted.secret.key) diff -u \ <(hd "$TEMPDIR"/secret.key) \ <(hd "$TEMPDIR"/converted.secret.key) KEYFPR=$(gpg --fingerprint --with-colons --list-keys | awk -F: '/^fpr:/{ if (ok) { print $10 } ; ok=0 } /^pub:/{ ok=1 }') KEYID=$(printf "%s" "$KEYFPR" | cut -b25-40) echo "conversions look good!" echo "Now working with key $KEYID at time $timestamp" gpg --check-trustdb gpg --list-keys echo "##################################################" echo "### test User ID addition..." gpg --export-secret-keys | \ PEM2OPENPGP_TIMESTAMP="$timestamp" \ PEM2OPENPGP_USAGE_FLAGS=sign,certify \ keytrans adduserid "$KEYID" "monkeymonkey" | gpg --import gpg --check-trustdb gpg --list-keys cat >"$TEMPDIR"/expectedout <"$TEMPDIR"/expectedout < "$TEMPDIR"/newkey.gpg NEWKEYFPR=$(< "$TEMPDIR"/newkey.gpg keytrans listfprs) NEWKEYID=$( printf "%s" "$NEWKEYFPR" | cut -b25-40) < "$TEMPDIR"/newkey.gpg gpg --import gpg --export-secret-keys | \ PEM2OPENPGP_TIMESTAMP="$timestamp" \ keytrans adduserid "$KEYID" "baz" | gpg --import cat >"$TEMPDIR"/expectedout <"$TEMPDIR"/expectedout <&2 "received %s %s\n" "$command" "$rest" printf "OK\n" esac done monkeysphere-0.43/tests/phony-pinentry-nopass000077500000000000000000000004771342216672300215630ustar00rootroot00000000000000#!/bin/bash printf "OK Pleased to meet you\n" while read command rest; do case "${command,,}" in bye) exit 0 ;; getpin) printf "OK\n" ;; *) printf >&2 "received %s %s\n" "$command" "$rest" printf "OK\n" esac done monkeysphere-0.43/utils/000077500000000000000000000000001342216672300153175ustar00rootroot00000000000000monkeysphere-0.43/utils/build-freebsd-distinfo000077500000000000000000000011171342216672300215710ustar00rootroot00000000000000#!/bin/bash VERSION=`head -n1 packaging/debian/changelog | sed 's/.*(\([^-]*\)-.*/\1/'` { echo "MD5 (monkeysphere_${VERSION}.orig.tar.gz) =" $(md5sum "monkeysphere_${VERSION}.orig.tar.gz" | cut -f1 -d\ ) echo "SHA256 (monkeysphere_${VERSION}.orig.tar.gz) =" $(sha256sum "monkeysphere_${VERSION}.orig.tar.gz" | cut -f1 -d\ ) echo "SIZE (monkeysphere_${VERSION}.orig.tar.gz) =" $(stat -c %s "monkeysphere_${VERSION}.orig.tar.gz") } > packaging/freebsd/security/monkeysphere/distinfo sed -i~ 's/^PORTVERSION=.*$/PORTVERSION= '"${VERSION}"/ packaging/freebsd/security/monkeysphere/Makefile monkeysphere-0.43/utils/build-macports-portfile000077500000000000000000000005251342216672300220160ustar00rootroot00000000000000#!/bin/bash -e PORTFILE="packaging/macports/Portfile" VERSION=`head -n1 packaging/debian/changelog | sed 's/.*(\([^-]*\)-.*/\1/'` MD5=`md5sum monkeysphere_${VERSION}.orig.tar.gz | awk '{ print $1 }'` sed -i~ 's/^version.*$/version '"$VERSION"/ "$PORTFILE" sed -i~ 's/^checksums.*$/checksums md5 '"$MD5"/ "$PORTFILE" monkeysphere-0.43/utils/preparing-release000066400000000000000000000015231342216672300206500ustar00rootroot00000000000000### Notes about preparing a release for the monkeysphere ### * make sure that ./Changelog and debian/changelog both have reasonable version numbers. * have the monkeysphere archive signing key handy! * create upstream version tag: git tag -s -m "Tagging Monkeysphere $version" monkeysphere_$version master * create debian-specific version tag: git tag -s -m "Tagging Monkeysphere $version-1" monkeysphere_$version-1 debian * make releasenote * upload to monkeysphere repo: (cd ../monkeysphere-docs/repo && reprepro --ignore=wrongdistribution include experimental ../../monkeysphere_$version-1_*.changes) * upload to debian: (cd .. && dupload monkeysphere_$version-1_*.changes) * git push monkeysphere.info --follow-tags master debian/master * cd ../monkeysphere-docs, inspect, and then push out those changes.