summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikhail Bautin <mbautin@users.noreply.github.com>2022-05-19 12:08:12 -0700
committerMikhail Bautin <mbautin@users.noreply.github.com>2022-05-19 12:08:12 -0700
commit4b0753269f45d2aa254827bb720e96056a074539 (patch)
treedb284d87c3eac170a4b7bbe23dd6019cd1efec0e
Imoprt from https://people.redhat.com/~dhowells/keyutils/keyutils-1.6.1.tar.bz2v1.6.1
-rw-r--r--.gitignore11
-rw-r--r--LICENCE.GPL339
-rw-r--r--LICENCE.LGPL504
-rw-r--r--Makefile313
-rw-r--r--README15
-rw-r--r--SUBMITTING_PATCHES45
-rw-r--r--dns.afsdb.c335
-rw-r--r--key.dns.h70
-rw-r--r--key.dns_resolver.c565
-rw-r--r--keyctl.c2375
-rw-r--r--keyutils.c880
-rw-r--r--keyutils.h269
-rw-r--r--keyutils.spec302
-rw-r--r--libkeyutils.pc.in8
-rw-r--r--man/asymmetric-key.7232
-rw-r--r--man/find_key_by_type_and_name.365
-rw-r--r--man/key.dns_resolver.834
-rw-r--r--man/keyctl.1908
-rw-r--r--man/keyctl.3123
-rw-r--r--man/keyctl_capabilities.3109
-rw-r--r--man/keyctl_chown.388
-rw-r--r--man/keyctl_clear.374
-rw-r--r--man/keyctl_describe.3112
-rw-r--r--man/keyctl_dh_compute.3168
-rw-r--r--man/keyctl_get_keyring_ID.396
-rw-r--r--man/keyctl_get_persistent.3114
-rw-r--r--man/keyctl_get_security.3102
-rw-r--r--man/keyctl_instantiate.3191
-rw-r--r--man/keyctl_invalidate.376
-rw-r--r--man/keyctl_join_session_keyring.384
-rw-r--r--man/keyctl_link.3108
-rw-r--r--man/keyctl_move.3109
-rw-r--r--man/keyctl_pkey_decrypt.31
-rw-r--r--man/keyctl_pkey_encrypt.3113
-rw-r--r--man/keyctl_pkey_query.3137
-rw-r--r--man/keyctl_pkey_sign.3133
-rw-r--r--man/keyctl_pkey_verify.31
-rw-r--r--man/keyctl_read.3112
-rw-r--r--man/keyctl_restrict_keyring.382
-rw-r--r--man/keyctl_revoke.373
-rw-r--r--man/keyctl_search.3138
-rw-r--r--man/keyctl_session_to_parent.377
-rw-r--r--man/keyctl_set_reqkey_keyring.3101
-rw-r--r--man/keyctl_set_timeout.381
-rw-r--r--man/keyctl_setperm.3130
-rw-r--r--man/keyctl_update.396
-rw-r--r--man/keyutils.7105
-rw-r--r--man/recursive_key_scan.390
-rw-r--r--man/request-key.861
-rw-r--r--man/request-key.conf.5145
-rwxr-xr-xrequest-key-debug.sh33
-rw-r--r--request-key.c955
-rw-r--r--request-key.conf41
-rw-r--r--tests/Makefile76
-rw-r--r--tests/PURPOSE82
-rw-r--r--tests/bugzillas/bz1031154/runtest.sh104
-rw-r--r--tests/bugzillas/bz1033467/runtest.sh68
-rw-r--r--tests/bugzillas/bz1071346/runtest.sh79
-rw-r--r--tests/keyctl/add/bad-args/runtest.sh74
-rw-r--r--tests/keyctl/add/noargs/runtest.sh35
-rw-r--r--tests/keyctl/add/useradd/runtest.sh53
-rw-r--r--tests/keyctl/clear/bad-args/runtest.sh39
-rw-r--r--tests/keyctl/clear/noargs/runtest.sh23
-rw-r--r--tests/keyctl/clear/valid/runtest.sh96
-rw-r--r--tests/keyctl/describing/bad-args/runtest.sh38
-rw-r--r--tests/keyctl/describing/noargs/runtest.sh25
-rw-r--r--tests/keyctl/describing/valid/runtest.sh71
-rw-r--r--tests/keyctl/dh_compute/bad-args/runtest.sh89
-rw-r--r--tests/keyctl/dh_compute/noargs/runtest.sh30
-rw-r--r--tests/keyctl/dh_compute/valid/runtest.sh266
-rw-r--r--tests/keyctl/instantiating/bad-args/runtest.sh55
-rw-r--r--tests/keyctl/instantiating/noargs/runtest.sh36
-rw-r--r--tests/keyctl/invalidate/bad-args/runtest.sh40
-rw-r--r--tests/keyctl/invalidate/noargs/runtest.sh29
-rw-r--r--tests/keyctl/invalidate/valid/runtest.sh80
-rw-r--r--tests/keyctl/link/bad-args/runtest.sh47
-rw-r--r--tests/keyctl/link/noargs/runtest.sh27
-rw-r--r--tests/keyctl/link/recursion/runtest.sh185
-rw-r--r--tests/keyctl/link/valid/runtest.sh139
-rw-r--r--tests/keyctl/listing/bad-args/runtest.sh38
-rw-r--r--tests/keyctl/listing/noargs/runtest.sh23
-rw-r--r--tests/keyctl/listing/valid/runtest.sh109
-rw-r--r--tests/keyctl/move/bad-args/runtest.sh64
-rw-r--r--tests/keyctl/move/noargs/runtest.sh39
-rw-r--r--tests/keyctl/move/recursion/runtest.sh213
-rw-r--r--tests/keyctl/move/valid/runtest.sh230
-rw-r--r--tests/keyctl/newring/bad-args/runtest.sh49
-rw-r--r--tests/keyctl/newring/noargs/runtest.sh27
-rw-r--r--tests/keyctl/newring/valid/runtest.sh61
-rw-r--r--tests/keyctl/noargs/runtest.sh24
-rw-r--r--tests/keyctl/padd/bad-args/runtest.sh74
-rw-r--r--tests/keyctl/padd/noargs/runtest.sh31
-rw-r--r--tests/keyctl/padd/useradd/runtest.sh91
-rw-r--r--tests/keyctl/permitting/bad-args/runtest.sh49
-rw-r--r--tests/keyctl/permitting/noargs/runtest.sh30
-rw-r--r--tests/keyctl/permitting/valid/runtest.sh99
-rw-r--r--tests/keyctl/pupdate/bad-args/runtest.sh39
-rw-r--r--tests/keyctl/pupdate/noargs/runtest.sh23
-rw-r--r--tests/keyctl/pupdate/userupdate/runtest.sh38
-rw-r--r--tests/keyctl/reading/bad-args/runtest.sh42
-rw-r--r--tests/keyctl/reading/noargs/runtest.sh25
-rw-r--r--tests/keyctl/reading/valid/runtest.sh96
-rw-r--r--tests/keyctl/requesting/bad-args/runtest.sh128
-rw-r--r--tests/keyctl/requesting/noargs/runtest.sh43
-rw-r--r--tests/keyctl/requesting/piped/runtest.sh106
-rw-r--r--tests/keyctl/requesting/valid/runtest.sh106
-rw-r--r--tests/keyctl/restrict/bad-args/runtest.sh50
-rw-r--r--tests/keyctl/restrict/valid/runtest.sh512
-rw-r--r--tests/keyctl/revoke/bad-args/runtest.sh25
-rw-r--r--tests/keyctl/revoke/noargs/runtest.sh22
-rw-r--r--tests/keyctl/revoke/valid/runtest.sh75
-rw-r--r--tests/keyctl/search/bad-args/runtest.sh87
-rw-r--r--tests/keyctl/search/noargs/runtest.sh31
-rw-r--r--tests/keyctl/search/valid/runtest.sh186
-rw-r--r--tests/keyctl/session/bad-args/runtest.sh32
-rw-r--r--tests/keyctl/session/valid/runtest.sh42
-rw-r--r--tests/keyctl/show/noargs/runtest.sh51
-rw-r--r--tests/keyctl/show/valid/runtest.sh84
-rw-r--r--tests/keyctl/supports/bad-args/runtest.sh23
-rw-r--r--tests/keyctl/supports/valid/runtest.sh23
-rw-r--r--tests/keyctl/timeout/bad-args/runtest.sh34
-rw-r--r--tests/keyctl/timeout/noargs/runtest.sh26
-rw-r--r--tests/keyctl/timeout/valid/runtest.sh130
-rw-r--r--tests/keyctl/unlink/all/runtest.sh103
-rw-r--r--tests/keyctl/unlink/bad-args/runtest.sh47
-rw-r--r--tests/keyctl/unlink/noargs/runtest.sh30
-rw-r--r--tests/keyctl/unlink/valid/runtest.sh99
-rw-r--r--tests/keyctl/update/bad-args/runtest.sh39
-rw-r--r--tests/keyctl/update/noargs/runtest.sh27
-rw-r--r--tests/keyctl/update/userupdate/runtest.sh38
-rw-r--r--tests/prepare.inc.sh138
-rw-r--r--tests/runtest.sh35
-rw-r--r--tests/toolbox.inc.sh1261
-rw-r--r--tests/vercmp.sh31
-rw-r--r--tests/version.inc.sh199
-rw-r--r--version.lds100
136 files changed, 18892 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0ac39ed
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+*~
+*.o
+*.os
+*.so
+*.a
+*.so.*
+/keyctl
+/request-key
+/key.dns_resolver
+/rpmbuild
+test.out
diff --git a/LICENCE.GPL b/LICENCE.GPL
new file mode 100644
index 0000000..4505352
--- /dev/null
+++ b/LICENCE.GPL
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc. <http://fsf.org/>
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) 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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. 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.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: 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
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.
diff --git a/LICENCE.LGPL b/LICENCE.LGPL
new file mode 100644
index 0000000..519334a
--- /dev/null
+++ b/LICENCE.LGPL
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc. <http://fsf.org/>
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+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 this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser 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 Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5f8b163
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,313 @@
+CPPFLAGS := -I.
+CFLAGS := -g -Wall -Werror
+INSTALL := install
+DESTDIR :=
+SPECFILE := keyutils.spec
+NO_GLIBC_KEYERR := 0
+NO_ARLIB := 0
+NO_SOLIB := 0
+ETCDIR := /etc
+BINDIR := /bin
+SBINDIR := /sbin
+SHAREDIR := /usr/share/keyutils
+MANDIR := /usr/share/man
+MAN1 := $(MANDIR)/man1
+MAN3 := $(MANDIR)/man3
+MAN5 := $(MANDIR)/man5
+MAN7 := $(MANDIR)/man7
+MAN8 := $(MANDIR)/man8
+INCLUDEDIR := /usr/include
+LN := ln
+LNS := $(LN) -sf
+PREFIX := /usr
+
+###############################################################################
+#
+# Determine the current package version from the specfile
+#
+###############################################################################
+vermajor := $(shell grep "%define vermajor" $(SPECFILE))
+verminor := $(shell grep "%define verminor" $(SPECFILE))
+MAJOR := $(word 3,$(vermajor))
+MINOR := $(word 3,$(verminor))
+VERSION := $(MAJOR).$(MINOR)
+
+TARBALL := keyutils-$(VERSION).tar
+ZTARBALL := $(TARBALL).bz2
+
+###############################################################################
+#
+# Determine the current library version from the version script
+#
+###############################################################################
+libversion := $(filter KEYUTILS_%,$(shell grep ^KEYUTILS_ version.lds))
+libversion := $(lastword $(libversion))
+libversion := $(lastword $(libversion))
+APIVERSION := $(subst KEYUTILS_,,$(libversion))
+vernumbers := $(subst ., ,$(APIVERSION))
+APIMAJOR := $(firstword $(vernumbers))
+
+ARLIB := libkeyutils.a
+DEVELLIB := libkeyutils.so
+SONAME := libkeyutils.so.$(APIMAJOR)
+LIBNAME := libkeyutils.so.$(APIVERSION)
+
+###############################################################################
+#
+# Guess at the appropriate lib directory and word size
+#
+###############################################################################
+ifeq ($(origin LIBDIR),undefined)
+LIBDIR := $(shell ldd /usr/bin/make | grep '\(/libc\)' | sed -e 's!.*\(/.*\)/libc[.].*!\1!')
+endif
+ifeq ($(origin USRLIBDIR),undefined)
+USRLIBDIR := $(patsubst /lib/%,/usr/lib/%,$(LIBDIR))
+endif
+BUILDFOR := $(shell file /usr/bin/make | sed -e 's!.*ELF \(32\|64\)-bit.*!\1!')-bit
+
+ifeq ($(origin CFLAGS),undefined)
+ifeq ($(BUILDFOR),32-bit)
+CFLAGS += -m32
+LIBDIR := /lib
+USRLIBDIR := /usr/lib
+else
+ifeq ($(BUILDFOR),64-bit)
+CFLAGS += -m64
+LIBDIR := /lib64
+USRLIBDIR := /usr/lib64
+endif
+endif
+endif
+
+PKGCONFIG := libkeyutils.pc
+PKGCONFIG_DIR := pkgconfig
+
+###############################################################################
+#
+# This is necessary if glibc doesn't know about the key management error codes
+#
+###############################################################################
+ifeq ($(NO_GLIBC_KEYERR),1)
+CFLAGS += -DNO_GLIBC_KEYERR
+LIBLIBS := -ldl -lc
+else
+LIBLIBS :=
+endif
+
+###############################################################################
+#
+# Normal build rule
+#
+###############################################################################
+all: keyctl request-key key.dns_resolver
+
+###############################################################################
+#
+# Build the libraries
+#
+###############################################################################
+#RPATH = -Wl,-rpath,$(LIBDIR)
+
+VCPPFLAGS := -DPKGBUILD="\"$(shell date -u +%F)\""
+VCPPFLAGS += -DPKGVERSION="\"keyutils-$(VERSION)\""
+VCPPFLAGS += -DAPIVERSION="\"libkeyutils-$(APIVERSION)\""
+
+ifeq ($(NO_ARLIB),0)
+all: $(ARLIB)
+$(ARLIB): keyutils.o
+ $(AR) rcs $@ $<
+
+keyutils.o: keyutils.c keyutils.h Makefile
+ $(CC) $(CPPFLAGS) $(VCPPFLAGS) $(CFLAGS) -UNO_GLIBC_KEYERR -o $@ -c $<
+LIB_DEPENDENCY := libkeyutils.a
+endif
+
+
+ifeq ($(NO_SOLIB),0)
+all: $(DEVELLIB)
+$(DEVELLIB): $(SONAME)
+ $(LNS) $< $@
+
+$(SONAME): $(LIBNAME)
+ $(LNS) $< $@
+
+LIBVERS := -shared -Wl,-soname,$(SONAME) -Wl,--version-script,version.lds
+
+$(LIBNAME): keyutils.os version.lds Makefile
+ $(CC) $(CFLAGS) -fPIC $(LDFLAGS) $(LIBVERS) -o $@ keyutils.os $(LIBLIBS)
+
+keyutils.os: keyutils.c keyutils.h Makefile
+ $(CC) $(CPPFLAGS) $(VCPPFLAGS) $(CFLAGS) -fPIC -o $@ -c $<
+LIB_DEPENDENCY := $(DEVELLIB)
+endif
+
+###############################################################################
+#
+# Build the programs
+#
+###############################################################################
+%.o: %.c keyutils.h Makefile
+ $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<
+
+keyctl: keyctl.o $(LIB_DEPENDENCY)
+ $(CC) -L. $(CFLAGS) $(LDFLAGS) $(RPATH) -o $@ $< -lkeyutils
+
+request-key: request-key.o $(LIB_DEPENDENCY)
+ $(CC) -L. $(CFLAGS) $(LDFLAGS) $(RPATH) -o $@ $< -lkeyutils
+
+key.dns_resolver: key.dns_resolver.o dns.afsdb.o $(LIB_DEPENDENCY)
+ $(CC) -L. $(CFLAGS) $(LDFLAGS) $(RPATH) -o $@ \
+ key.dns_resolver.o dns.afsdb.o -lkeyutils -lresolv
+
+key.dns_resolver.o: key.dns_resolver.c key.dns.h
+dns.afsdb.o: dns.afsdb.c key.dns.h
+
+###############################################################################
+#
+# Install everything
+#
+###############################################################################
+pkgconfig:
+ sed \
+ -e 's,@VERSION\@,$(VERSION),g' \
+ -e 's,@prefix\@,$(PREFIX),g' \
+ -e 's,@exec_prefix\@,$(PREFIX),g' \
+ -e 's,@libdir\@,$(USRLIBDIR),g' \
+ -e 's,@includedir\@,$(INCLUDEDIR),g' \
+ < $(PKGCONFIG).in > $(PKGCONFIG) || rm $(PKGCONFIG)
+
+install: all
+ifeq ($(NO_ARLIB),0)
+ $(INSTALL) -D -m 0644 $(ARLIB) $(DESTDIR)$(USRLIBDIR)/$(ARLIB)
+endif
+ifeq ($(NO_SOLIB),0)
+ $(INSTALL) -D $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(LIBNAME)
+ $(LNS) $(LIBNAME) $(DESTDIR)$(LIBDIR)/$(SONAME)
+ mkdir -p $(DESTDIR)$(USRLIBDIR)
+ $(LNS) $(LIBDIR)/$(SONAME) $(DESTDIR)$(USRLIBDIR)/$(DEVELLIB)
+ sed \
+ -e 's,@VERSION\@,$(VERSION),g' \
+ -e 's,@prefix\@,$(PREFIX),g' \
+ -e 's,@exec_prefix\@,$(PREFIX),g' \
+ -e 's,@libdir\@,$(USRLIBDIR),g' \
+ -e 's,@includedir\@,$(INCLUDEDIR),g' \
+ < $(PKGCONFIG).in > $(PKGCONFIG) || rm $(PKGCONFIG)
+ $(INSTALL) -D $(PKGCONFIG) $(DESTDIR)$(LIBDIR)/$(PKGCONFIG_DIR)/$(PKGCONFIG)
+ rm $(PKGCONFIG)
+endif
+ $(INSTALL) -D keyctl $(DESTDIR)$(BINDIR)/keyctl
+ $(INSTALL) -D request-key $(DESTDIR)$(SBINDIR)/request-key
+ $(INSTALL) -D request-key-debug.sh $(DESTDIR)$(SHAREDIR)/request-key-debug.sh
+ $(INSTALL) -D key.dns_resolver $(DESTDIR)$(SBINDIR)/key.dns_resolver
+ $(INSTALL) -D -m 0644 request-key.conf $(DESTDIR)$(ETCDIR)/request-key.conf
+ mkdir -p $(DESTDIR)$(ETCDIR)/request-key.d
+ mkdir -p $(DESTDIR)$(MAN1)
+ $(INSTALL) -m 0644 $(wildcard man/*.1) $(DESTDIR)$(MAN1)
+ mkdir -p $(DESTDIR)$(MAN3)
+ $(INSTALL) -m 0644 $(wildcard man/*.3) $(DESTDIR)$(MAN3)
+ mkdir -p $(DESTDIR)$(MAN5)
+ $(INSTALL) -m 0644 $(wildcard man/*.5) $(DESTDIR)$(MAN5)
+ mkdir -p $(DESTDIR)$(MAN7)
+ $(INSTALL) -m 0644 $(wildcard man/*.7) $(DESTDIR)$(MAN7)
+ mkdir -p $(DESTDIR)$(MAN8)
+ $(INSTALL) -m 0644 $(wildcard man/*.8) $(DESTDIR)$(MAN8)
+ $(LNS) keyctl_describe.3 $(DESTDIR)$(MAN3)/keyctl_describe_alloc.3
+ $(LNS) keyctl_get_security.3 $(DESTDIR)$(MAN3)/keyctl_get_security_alloc.3
+ $(LNS) keyctl_instantiate.3 $(DESTDIR)$(MAN3)/keyctl_instantiate_iov.3
+ $(LNS) keyctl_instantiate.3 $(DESTDIR)$(MAN3)/keyctl_reject.3
+ $(LNS) keyctl_instantiate.3 $(DESTDIR)$(MAN3)/keyctl_negate.3
+ $(LNS) keyctl_instantiate.3 $(DESTDIR)$(MAN3)/keyctl_assume_authority.3
+ $(LNS) keyctl_link.3 $(DESTDIR)$(MAN3)/keyctl_unlink.3
+ $(LNS) keyctl_read.3 $(DESTDIR)$(MAN3)/keyctl_read_alloc.3
+ $(LNS) recursive_key_scan.3 $(DESTDIR)$(MAN3)/recursive_session_key_scan.3
+ $(LNS) keyctl_dh_compute.3 $(DESTDIR)$(MAN3)/keyctl_dh_compute_alloc.3
+ $(LNS) keyctl_dh_compute.3 $(DESTDIR)$(MAN3)/keyctl_dh_compute_kdf.3
+ $(INSTALL) -D -m 0644 keyutils.h $(DESTDIR)$(INCLUDEDIR)/keyutils.h
+
+###############################################################################
+#
+# Run tests
+#
+###############################################################################
+test:
+ $(MAKE) -C tests run
+
+###############################################################################
+#
+# Clean up
+#
+###############################################################################
+clean:
+ $(MAKE) -C tests clean
+ $(RM) libkeyutils.so* libkeyutils.a
+ $(RM) keyctl request-key key.dns_resolver
+ $(RM) *.o *.os *~
+ $(RM) debugfiles.list debugsources.list
+
+distclean: clean
+ $(RM) -r rpmbuild $(TARBALL)
+
+###############################################################################
+#
+# Generate a tarball
+#
+###############################################################################
+$(ZTARBALL):
+ git archive --prefix=keyutils-$(VERSION)/ --format tar -o $(TARBALL) HEAD
+ bzip2 -9 <$(TARBALL) >$(ZTARBALL)
+
+tarball: $(ZTARBALL)
+
+###############################################################################
+#
+# Generate an RPM
+#
+###############################################################################
+SRCBALL := rpmbuild/SOURCES/$(TARBALL)
+ZSRCBALL := rpmbuild/SOURCES/$(ZTARBALL)
+
+BUILDID := .local
+dist := $(word 2,$(shell grep -r "^%dist" /etc/rpm /usr/lib/rpm))
+release3:= $(word 2,$(shell grep ^Release: $(SPECFILE)))
+release2:= $(subst %{?dist},$(dist),$(release3))
+release1:= $(subst %{?buildid},$(BUILDID),$(release2))
+release := $(subst %{?distprefix},,$(release1))
+rpmver := $(VERSION)-$(release)
+SRPM := rpmbuild/SRPMS/keyutils-$(rpmver).src.rpm
+
+RPMBUILDDIRS := \
+ --define "_srcrpmdir $(CURDIR)/rpmbuild/SRPMS" \
+ --define "_rpmdir $(CURDIR)/rpmbuild/RPMS" \
+ --define "_sourcedir $(CURDIR)/rpmbuild/SOURCES" \
+ --define "_specdir $(CURDIR)/rpmbuild/SPECS" \
+ --define "_builddir $(CURDIR)/rpmbuild/BUILD" \
+ --define "_buildrootdir $(CURDIR)/rpmbuild/BUILDROOT"
+
+RPMFLAGS := \
+ --define "buildid $(BUILDID)"
+
+rpm:
+ mkdir -p rpmbuild
+ chmod ug-s rpmbuild
+ mkdir -p rpmbuild/{SPECS,SOURCES,BUILD,BUILDROOT,RPMS,SRPMS}
+ git archive --prefix=keyutils-$(VERSION)/ --format tar -o $(SRCBALL) HEAD
+ bzip2 -9 <$(SRCBALL) >$(ZSRCBALL)
+ rpmbuild -ts $(ZSRCBALL) --define "_srcrpmdir rpmbuild/SRPMS" $(RPMFLAGS)
+ rpmbuild --rebuild $(SRPM) $(RPMBUILDDIRS) $(RPMFLAGS)
+
+rpmlint: rpm
+ rpmlint $(SRPM) $(CURDIR)/rpmbuild/RPMS/*/keyutils-{,libs-,libs-devel-,debuginfo-}$(rpmver).*.rpm
+
+###############################################################################
+#
+# Build debugging
+#
+###############################################################################
+show_vars:
+ @echo VERSION=$(VERSION)
+ @echo APIVERSION=$(APIVERSION)
+ @echo LIBDIR=$(LIBDIR)
+ @echo USRLIBDIR=$(USRLIBDIR)
+ @echo BUILDFOR=$(BUILDFOR)
+ @echo SONAME=$(SONAME)
+ @echo LIBNAME=$(LIBNAME)
diff --git a/README b/README
new file mode 100644
index 0000000..5c3d94d
--- /dev/null
+++ b/README
@@ -0,0 +1,15 @@
+These tools are used to control the key management system built into the Linux
+kernel.
+
+To build and install the tools and manual pages, run:
+
+ make
+ make install
+
+If your glibc does not contain definitions for the new error codes and system
+calls, then try:
+
+ make NO_GLIBC_KEYERR=1
+
+The tools are licensed under the GPL and the utility library under the LGPL.
+Copies of these are included in this tarball.
diff --git a/SUBMITTING_PATCHES b/SUBMITTING_PATCHES
new file mode 100644
index 0000000..c5ba909
--- /dev/null
+++ b/SUBMITTING_PATCHES
@@ -0,0 +1,45 @@
+ ==================
+ SUBMITTING PATCHES
+ ==================
+
+Patches can be sent to the keyrings@vger.kernel.org mailing list or directly to
+David Howells <dhowells@redhat.com>, the author, in a private email.
+
+When sending patches to be included in keyutils, please certify that your patch
+meets the criteria below by including include a sign-off line in your email
+which looks like this:
+
+ Signed-off-by: Random J Developer <random@developer.example.org>
+
+This confirms that you are permitted to submit the patch for inclusion in
+keyutils under the GPLv2 licence (utilities and documentation) or the LGPLv2.1
+licence (library). The full text of the certificate is as follows:
+
+ Developer's Certificate of Origin v1.1
+ ======================================
+
+ By making a contribution to this project, I certify that:
+
+ (a) The contribution was created in whole or in part by me and I
+ have the right to submit it under the open source licences
+ indicated in the file; or
+
+ (b) The contribution is based upon previous work that, to the best
+ of my knowledge, is covered under an appropriate open source
+ licence and I have the right under that licence to submit that
+ work with modifications, whether created in whole or in part
+ by me, under the same open source licence (unless I am
+ permitted to submit under a different licence), as indicated
+ in the file; or
+
+ (c) The contribution was provided directly to me by some other
+ person who certified (a), (b) or (c) and I have not modified
+ it.
+
+ and also that:
+
+ (d) I understand and agree that this project and the contribution are
+ public and that a record of the contribution (including all
+ personal information I submit with it, including my sign-off) is
+ maintained indefinitely and may be redistributed consistent with
+ this project or the open source licence(s) involved.
diff --git a/dns.afsdb.c b/dns.afsdb.c
new file mode 100644
index 0000000..fa60e04
--- /dev/null
+++ b/dns.afsdb.c
@@ -0,0 +1,335 @@
+/*
+ * DNS Resolver Module User-space Helper for AFSDB records
+ *
+ * Copyright (C) Wang Lei (wang840925@gmail.com) 2010
+ * Authors: Wang Lei (wang840925@gmail.com)
+ *
+ * Copyright (C) David Howells (dhowells@redhat.com) 2018
+ *
+ * This is a userspace tool for querying AFSDB RR records in the DNS on behalf
+ * of the kernel, and converting the VL server addresses to IPv4 format so that
+ * they can be used by the kAFS filesystem.
+ *
+ * As some function like res_init() should use the static library, which is a
+ * bug of libresolv, that is the reason for cifs.upcall to reimplement.
+ *
+ * To use this program, you must tell /sbin/request-key how to invoke it. You
+ * need to have the keyutils package installed and something like the following
+ * lines added to your /etc/request-key.conf file:
+ *
+ * #OP TYPE DESCRIPTION CALLOUT INFO PROGRAM ARG1 ARG2 ARG3 ...
+ * ====== ============ =========== ============ ==========================
+ * create dns_resolver afsdb:* * /sbin/key.dns_resolver %k
+ *
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "key.dns.h"
+
+static unsigned long afs_ttl = ULONG_MAX;
+
+/*
+ *
+ */
+static void afsdb_hosts_to_addrs(ns_msg handle, ns_sect section)
+{
+ char *vllist[MAX_VLS]; /* list of name servers */
+ int vlsnum = 0; /* number of name servers in list */
+ int rrnum;
+ ns_rr rr;
+ int subtype, i, ret;
+ unsigned int ttl = UINT_MAX, rr_ttl;
+
+ debug("AFSDB RR count is %d", ns_msg_count(handle, section));
+
+ /* Look at all the resource records in this section. */
+ for (rrnum = 0; rrnum < ns_msg_count(handle, section); rrnum++) {
+ /* Expand the resource record number rrnum into rr. */
+ if (ns_parserr(&handle, section, rrnum, &rr)) {
+ _error("ns_parserr failed : %m");
+ continue;
+ }
+
+ /* We're only interested in AFSDB records */
+ if (ns_rr_type(rr) == ns_t_afsdb) {
+ vllist[vlsnum] = malloc(MAXDNAME);
+ if (!vllist[vlsnum])
+ error("Out of memory");
+
+ subtype = ns_get16(ns_rr_rdata(rr));
+
+ /* Expand the name server's domain name */
+ if (ns_name_uncompress(ns_msg_base(handle),
+ ns_msg_end(handle),
+ ns_rr_rdata(rr) + 2,
+ vllist[vlsnum],
+ MAXDNAME) < 0)
+ error("ns_name_uncompress failed");
+
+ rr_ttl = ns_rr_ttl(rr);
+ if (ttl > rr_ttl)
+ ttl = rr_ttl;
+
+ /* Check the domain name we've just unpacked and add it to
+ * the list of VL servers if it is not a duplicate.
+ * If it is a duplicate, just ignore it.
+ */
+ for (i = 0; i < vlsnum; i++)
+ if (strcasecmp(vllist[i], vllist[vlsnum]) == 0)
+ goto next_one;
+
+ /* Turn the hostname into IP addresses */
+ ret = dns_resolver(vllist[vlsnum], NULL);
+ if (ret) {
+ debug("AFSDB RR can't resolve."
+ "subtype:%d, server name:%s, netmask:%u",
+ subtype, vllist[vlsnum], mask);
+ goto next_one;
+ }
+
+ info("AFSDB RR subtype:%d, server name:%s, ip:%*.*s, ttl:%u",
+ subtype, vllist[vlsnum],
+ (int)payload[payload_index - 1].iov_len,
+ (int)payload[payload_index - 1].iov_len,
+ (char *)payload[payload_index - 1].iov_base,
+ ttl);
+
+ /* prepare for the next record */
+ vlsnum++;
+ continue;
+
+ next_one:
+ free(vllist[vlsnum]);
+ }
+ }
+
+ afs_ttl = ttl;
+ info("ttl: %u", ttl);
+}
+
+/*
+ *
+ */
+static void srv_hosts_to_addrs(ns_msg handle, ns_sect section)
+{
+ char *vllist[MAX_VLS]; /* list of name servers */
+ int vlsnum = 0; /* number of name servers in list */
+ int rrnum;
+ ns_rr rr;
+ int subtype, i, ret;
+ unsigned short pref, weight, port;
+ unsigned int ttl = UINT_MAX, rr_ttl;
+ char sport[8];
+
+ debug("SRV RR count is %d", ns_msg_count(handle, section));
+
+ /* Look at all the resource records in this section. */
+ for (rrnum = 0; rrnum < ns_msg_count(handle, section); rrnum++) {
+ /* Expand the resource record number rrnum into rr. */
+ if (ns_parserr(&handle, section, rrnum, &rr)) {
+ _error("ns_parserr failed : %m");
+ continue;
+ }
+
+ if (ns_rr_type(rr) == ns_t_srv) {
+ vllist[vlsnum] = malloc(MAXDNAME);
+ if (!vllist[vlsnum])
+ error("Out of memory");
+
+ subtype = ns_get16(ns_rr_rdata(rr));
+
+ /* Expand the name server's domain name */
+ if (ns_name_uncompress(ns_msg_base(handle),
+ ns_msg_end(handle),
+ ns_rr_rdata(rr) + 6,
+ vllist[vlsnum],
+ MAXDNAME) < 0) {
+ _error("ns_name_uncompress failed");
+ continue;
+ }
+
+ rr_ttl = ns_rr_ttl(rr);
+ if (ttl > rr_ttl)
+ ttl = rr_ttl;
+
+ pref = ns_get16(ns_rr_rdata(rr));
+ weight = ns_get16(ns_rr_rdata(rr) + 2);
+ port = ns_get16(ns_rr_rdata(rr) + 4);
+ info("rdata %u %u %u", pref, weight, port);
+
+ sprintf(sport, "+%hu", port);
+
+ /* Check the domain name we've just unpacked and add it to
+ * the list of VL servers if it is not a duplicate.
+ * If it is a duplicate, just ignore it.
+ */
+ for (i = 0; i < vlsnum; i++)
+ if (strcasecmp(vllist[i], vllist[vlsnum]) == 0)
+ goto next_one;
+
+ /* Turn the hostname into IP addresses */
+ ret = dns_resolver(vllist[vlsnum], sport);
+ if (ret) {
+ debug("SRV RR can't resolve."
+ "subtype:%d, server name:%s, netmask:%u",
+ subtype, vllist[vlsnum], mask);
+ goto next_one;
+ }
+
+ info("SRV RR subtype:%d, server name:%s, ip:%*.*s, ttl:%u",
+ subtype, vllist[vlsnum],
+ (int)payload[payload_index - 1].iov_len,
+ (int)payload[payload_index - 1].iov_len,
+ (char *)payload[payload_index - 1].iov_base,
+ ttl);
+
+ /* prepare for the next record */
+ vlsnum++;
+ continue;
+
+ next_one:
+ free(vllist[vlsnum]);
+ }
+ }
+
+ afs_ttl = ttl;
+ info("ttl: %u", ttl);
+}
+
+/*
+ * Look up an AFSDB record to get the VL server addresses.
+ */
+static int dns_query_AFSDB(const char *cell)
+{
+ int response_len; /* buffer length */
+ ns_msg handle; /* handle for response message */
+ union {
+ HEADER hdr;
+ u_char buf[NS_PACKETSZ];
+ } response; /* response buffers */
+
+ debug("Get AFSDB RR for cell name:'%s'", cell);
+
+ /* query the dns for an AFSDB resource record */
+ response_len = res_query(cell,
+ ns_c_in,
+ ns_t_afsdb,
+ response.buf,
+ sizeof(response));
+
+ if (response_len < 0) {
+ /* negative result */
+ _nsError(h_errno, cell);
+ return -1;
+ }
+
+ if (ns_initparse(response.buf, response_len, &handle) < 0)
+ error("ns_initparse: %m");
+
+ /* look up the hostnames we've obtained to get the actual addresses */
+ afsdb_hosts_to_addrs(handle, ns_s_an);
+
+ info("DNS query AFSDB RR results:%u ttl:%lu", payload_index, afs_ttl);
+ return 0;
+}
+
+/*
+ * Look up an SRV record to get the VL server addresses [RFC 5864].
+ */
+static int dns_query_VL_SRV(const char *cell)
+{
+ int response_len; /* buffer length */
+ ns_msg handle; /* handle for response message */
+ union {
+ HEADER hdr;
+ u_char buf[NS_PACKETSZ];
+ } response;
+ char name[1024];
+
+ snprintf(name, sizeof(name), "_afs3-vlserver._udp.%s", cell);
+
+ debug("Get VL SRV RR for name:'%s'", name);
+
+ response_len = res_query(name,
+ ns_c_in,
+ ns_t_srv,
+ response.buf,
+ sizeof(response));
+
+ if (response_len < 0) {
+ /* negative result */
+ _nsError(h_errno, cell);
+ return -1;
+ }
+
+ if (ns_initparse(response.buf, response_len, &handle) < 0)
+ error("ns_initparse: %m");
+
+ /* look up the hostnames we've obtained to get the actual addresses */
+ srv_hosts_to_addrs(handle, ns_s_an);
+
+ info("DNS query VL SRV RR results:%u ttl:%lu", payload_index, afs_ttl);
+ return 0;
+}
+
+/*
+ * Instantiate the key.
+ */
+static __attribute__((noreturn))
+void afs_instantiate(const char *cell)
+{
+ int ret;
+
+ /* set the key's expiry time from the minimum TTL encountered */
+ if (!debug_mode) {
+ ret = keyctl_set_timeout(key, afs_ttl);
+ if (ret == -1)
+ error("%s: keyctl_set_timeout: %m", __func__);
+ }
+
+ /* handle a lack of results */
+ if (payload_index == 0)
+ nsError(NO_DATA, cell);
+
+ /* must include a NUL char at the end of the payload */
+ payload[payload_index].iov_base = "";
+ payload[payload_index++].iov_len = 1;
+ dump_payload();
+
+ /* load the key with data key */
+ if (!debug_mode) {
+ ret = keyctl_instantiate_iov(key, payload, payload_index, 0);
+ if (ret == -1)
+ error("%s: keyctl_instantiate: %m", __func__);
+ }
+
+ exit(0);
+}
+
+/*
+ * Look up VL servers for AFS.
+ */
+void afs_look_up_VL_servers(const char *cell, char *options)
+{
+ /* Is the IP address family limited? */
+ if (strcmp(options, "ipv4") == 0)
+ mask = INET_IP4_ONLY;
+ else if (strcmp(options, "ipv6") == 0)
+ mask = INET_IP6_ONLY;
+
+ if (dns_query_VL_SRV(cell) != 0)
+ dns_query_AFSDB(cell);
+
+ afs_instantiate(cell);
+}
diff --git a/key.dns.h b/key.dns.h
new file mode 100644
index 0000000..b143f4a
--- /dev/null
+++ b/key.dns.h
@@ -0,0 +1,70 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public Licence as published by
+ * the Free Software Foundation; either version 2 of the Licence, 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 Licence for more details.
+ */
+#define _GNU_SOURCE
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <arpa/inet.h>
+#include <limits.h>
+#include <resolv.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <keyutils.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+#define MAX_VLS 15 /* Max Volume Location Servers Per-Cell */
+#define INET_IP4_ONLY 0x1
+#define INET_IP6_ONLY 0x2
+#define INET_ALL 0xFF
+#define ONE_ADDR_ONLY 0x100
+
+/*
+ * key.dns_resolver.c
+ */
+extern key_serial_t key;
+extern int debug_mode;
+extern unsigned mask;
+
+#define N_PAYLOAD 256
+extern struct iovec payload[N_PAYLOAD];
+extern int payload_index;
+
+extern __attribute__((format(printf, 1, 2), noreturn))
+void error(const char *fmt, ...);
+extern __attribute__((format(printf, 1, 2)))
+void _error(const char *fmt, ...);
+extern __attribute__((format(printf, 1, 2)))
+void info(const char *fmt, ...);
+extern __attribute__((noreturn))
+void nsError(int err, const char *domain);
+extern void _nsError(int err, const char *domain);
+extern __attribute__((format(printf, 1, 2)))
+void debug(const char *fmt, ...);
+
+extern void append_address_to_payload(const char *addr);
+extern void dump_payload(void);
+extern int dns_resolver(const char *server_name, const char *port);
+
+/*
+ * dns.afsdb.c
+ */
+extern __attribute__((noreturn))
+void afs_look_up_VL_servers(const char *cell, char *options);
diff --git a/key.dns_resolver.c b/key.dns_resolver.c
new file mode 100644
index 0000000..4ac27d3
--- /dev/null
+++ b/key.dns_resolver.c
@@ -0,0 +1,565 @@
+/*
+ * DNS Resolver Module User-space Helper for AFSDB records
+ *
+ * Copyright (C) Wang Lei (wang840925@gmail.com) 2010
+ * Authors: Wang Lei (wang840925@gmail.com)
+ * David Howells (dhowells@redhat.com)
+ *
+ * This is a userspace tool for querying AFSDB RR records in the DNS on behalf
+ * of the kernel, and converting the VL server addresses to IPv4 format so that
+ * they can be used by the kAFS filesystem.
+ *
+ * Compile with:
+ *
+ * cc -o key.dns_resolver key.dns_resolver.c -lresolv -lkeyutils
+ *
+ * As some function like res_init() should use the static library, which is a
+ * bug of libresolv, that is the reason for cifs.upcall to reimplement.
+ *
+ * To use this program, you must tell /sbin/request-key how to invoke it. You
+ * need to have the keyutils package installed and something like the following
+ * lines added to your /etc/request-key.conf file:
+ *
+ * #OP TYPE DESCRIPTION CALLOUT INFO PROGRAM ARG1 ARG2 ARG3 ...
+ * ====== ============ =========== ============ ==========================
+ * create dns_resolver afsdb:* * /sbin/key.dns_resolver %k
+ *
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "key.dns.h"
+
+static const char *DNS_PARSE_VERSION = "1.0";
+static const char prog[] = "key.dns_resolver";
+static const char key_type[] = "dns_resolver";
+static const char a_query_type[] = "a";
+static const char aaaa_query_type[] = "aaaa";
+static const char afsdb_query_type[] = "afsdb";
+key_serial_t key;
+static int verbose;
+int debug_mode;
+unsigned mask = INET_ALL;
+
+
+/*
+ * segmental payload
+ */
+struct iovec payload[N_PAYLOAD];
+int payload_index;
+
+/*
+ * Print an error to stderr or the syslog, negate the key being created and
+ * exit
+ */
+void error(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ if (isatty(2)) {
+ vfprintf(stderr, fmt, va);
+ fputc('\n', stderr);
+ } else {
+ vsyslog(LOG_ERR, fmt, va);
+ }
+ va_end(va);
+
+ /*
+ * on error, negatively instantiate the key ourselves so that we can
+ * make sure the kernel doesn't hang it off of a searchable keyring
+ * and interfere with the next attempt to instantiate the key.
+ */
+ if (!debug_mode)
+ keyctl_negate(key, 1, KEY_REQKEY_DEFL_DEFAULT);
+
+ exit(1);
+}
+
+#define error(FMT, ...) error("Error: " FMT, ##__VA_ARGS__);
+
+/*
+ * Just print an error to stderr or the syslog
+ */
+void _error(const char *fmt, ...)
+{
+ va_list va;
+
+ va_start(va, fmt);
+ if (isatty(2)) {
+ vfprintf(stderr, fmt, va);
+ fputc('\n', stderr);
+ } else {
+ vsyslog(LOG_ERR, fmt, va);
+ }
+ va_end(va);
+}
+
+/*
+ * Print status information
+ */
+void info(const char *fmt, ...)
+{
+ va_list va;
+
+ if (verbose < 1)
+ return;
+
+ va_start(va, fmt);
+ if (isatty(1)) {
+ fputs("I: ", stdout);
+ vfprintf(stdout, fmt, va);
+ fputc('\n', stdout);
+ } else {
+ vsyslog(LOG_INFO, fmt, va);
+ }
+ va_end(va);
+}
+
+/*
+ * Print a nameserver error and exit
+ */
+static const int ns_errno_map[] = {
+ [0] = ECONNREFUSED,
+ [HOST_NOT_FOUND] = ENODATA,
+ [TRY_AGAIN] = EAGAIN,
+ [NO_RECOVERY] = ECONNREFUSED,
+ [NO_DATA] = ENODATA,
+};
+
+void _nsError(int err, const char *domain)
+{
+ if (isatty(2))
+ fprintf(stderr, "NS:%s: %s.\n", domain, hstrerror(err));
+ else
+ syslog(LOG_INFO, "%s: %s", domain, hstrerror(err));
+
+ if (err >= sizeof(ns_errno_map) / sizeof(ns_errno_map[0]))
+ err = ECONNREFUSED;
+ else
+ err = ns_errno_map[err];
+
+ info("Reject the key with error %d", err);
+}
+
+void nsError(int err, const char *domain)
+{
+ unsigned timeout;
+ int ret;
+
+ _nsError(err, domain);
+
+ switch (err) {
+ case TRY_AGAIN:
+ timeout = 1;
+ break;
+ case 0:
+ case NO_RECOVERY:
+ timeout = 10;
+ break;
+ default:
+ timeout = 1 * 60;
+ break;
+ }
+
+ if (!debug_mode) {
+ ret = keyctl_reject(key, timeout, err, KEY_REQKEY_DEFL_DEFAULT);
+ if (ret == -1)
+ error("%s: keyctl_reject: %m", __func__);
+ }
+ exit(0);
+}
+
+/*
+ * Print debugging information
+ */
+void debug(const char *fmt, ...)
+{
+ va_list va;
+
+ if (verbose < 2)
+ return;
+
+ va_start(va, fmt);
+ if (isatty(1)) {
+ fputs("D: ", stdout);
+ vfprintf(stdout, fmt, va);
+ fputc('\n', stdout);
+ } else {
+ vsyslog(LOG_DEBUG, fmt, va);
+ }
+ va_end(va);
+}
+
+/*
+ * Append an address to the payload segment list
+ */
+void append_address_to_payload(const char *addr)
+{
+ size_t sz = strlen(addr);
+ char *copy;
+ int loop;
+
+ debug("append '%s'", addr);
+
+ if (payload_index + 2 > N_PAYLOAD - 1)
+ return;
+
+ /* discard duplicates */
+ for (loop = 0; loop < payload_index; loop++)
+ if (payload[loop].iov_len == sz &&
+ memcmp(payload[loop].iov_base, addr, sz) == 0)
+ return;
+
+ copy = malloc(sz);
+ if (!copy)
+ error("%s: malloc: %m", __func__);
+ memcpy(copy, addr, sz);
+
+ if (payload_index != 0) {
+ payload[payload_index ].iov_base = ",";
+ payload[payload_index++].iov_len = 1;
+ }
+ payload[payload_index ].iov_base = copy;
+ payload[payload_index++].iov_len = sz;
+}
+
+/*
+ * Dump the payload when debugging
+ */
+void dump_payload(void)
+{
+ size_t plen, n;
+ char *buf, *p;
+ int loop;
+
+ if (debug_mode)
+ verbose = 1;
+ if (verbose < 1)
+ return;
+
+ plen = 0;
+ for (loop = 0; loop < payload_index; loop++) {
+ n = payload[loop].iov_len;
+ debug("seg[%d]: %zu", loop, n);
+ plen += n;
+ }
+ if (plen == 0) {
+ info("The key instantiation data is empty");
+ return;
+ }
+
+ debug("total: %zu", plen);
+ buf = malloc(plen + 1);
+ if (!buf)
+ return;
+
+ p = buf;
+ for (loop = 0; loop < payload_index; loop++) {
+ n = payload[loop].iov_len;
+ memcpy(p, payload[loop].iov_base, n);
+ p += n;
+ }
+
+ info("The key instantiation data is '%s'", buf);
+ free(buf);
+}
+
+/*
+ * Perform address resolution on a hostname and add the resulting address as a
+ * string to the list of payload segments.
+ */
+int dns_resolver(const char *server_name, const char *port)
+{
+ struct addrinfo hints, *addr, *ai;
+ char buf[INET6_ADDRSTRLEN + 8 + 1];
+ int ret, len;
+ void *sa;
+
+ debug("Resolve '%s' with %x", server_name, mask);
+
+ memset(&hints, 0, sizeof(hints));
+ switch (mask & INET_ALL) {
+ case INET_IP4_ONLY: hints.ai_family = AF_INET; debug("IPv4"); break;
+ case INET_IP6_ONLY: hints.ai_family = AF_INET6; debug("IPv6"); break;
+ default: break;
+ }
+
+ /* resolve name to ip */
+ ret = getaddrinfo(server_name, NULL, &hints, &addr);
+ if (ret) {
+ info("unable to resolve hostname: %s [%s]",
+ server_name, gai_strerror(ret));
+ return -1;
+ }
+
+ for (ai = addr; ai; ai = ai->ai_next) {
+ debug("RR: %x,%x,%x,%x,%x,%s",
+ ai->ai_flags, ai->ai_family,
+ ai->ai_socktype, ai->ai_protocol,
+ ai->ai_addrlen, ai->ai_canonname);
+
+ /* convert address to string */
+ switch (ai->ai_family) {
+ case AF_INET:
+ if (!(mask & INET_IP4_ONLY))
+ continue;
+ sa = &(((struct sockaddr_in *)ai->ai_addr)->sin_addr);
+ len = INET_ADDRSTRLEN;
+ break;
+ case AF_INET6:
+ if (!(mask & INET_IP6_ONLY))
+ continue;
+ sa = &(((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr);
+ len = INET6_ADDRSTRLEN;
+ break;
+ default:
+ debug("Address of unknown family %u", addr->ai_family);
+ continue;
+ }
+
+ if (!inet_ntop(ai->ai_family, sa, buf, len))
+ error("%s: inet_ntop: %m", __func__);
+
+ if (port)
+ strcat(buf, port);
+ append_address_to_payload(buf);
+ if (mask & ONE_ADDR_ONLY)
+ break;
+ }
+
+ freeaddrinfo(addr);
+ return 0;
+}
+
+/*
+ * Look up a A and/or AAAA records to get host addresses
+ *
+ * The callout_info is parsed for request options. For instance, "ipv4" to
+ * request only IPv4 addresses, "ipv6" to request only IPv6 addresses and
+ * "list" to get multiple addresses.
+ */
+static __attribute__((noreturn))
+int dns_query_a_or_aaaa(const char *hostname, char *options)
+{
+ int ret;
+
+ debug("Get A/AAAA RR for hostname:'%s', options:'%s'",
+ hostname, options);
+
+ if (!options[0]) {
+ /* legacy mode */
+ mask = INET_IP4_ONLY | ONE_ADDR_ONLY;
+ } else {
+ char *k, *val;
+
+ mask = INET_ALL | ONE_ADDR_ONLY;
+
+ do {
+ k = options;
+ options = strchr(options, ' ');
+ if (!options)
+ options = k + strlen(k);
+ else
+ *options++ = '\0';
+ if (!*k)
+ continue;
+ if (strchr(k, ','))
+ error("Option name '%s' contains a comma", k);
+
+ val = strchr(k, '=');
+ if (val)
+ *val++ = '\0';
+
+ debug("Opt %s", k);
+
+ if (strcmp(k, "ipv4") == 0) {
+ mask &= ~INET_ALL;
+ mask |= INET_IP4_ONLY;
+ } else if (strcmp(k, "ipv6") == 0) {
+ mask &= ~INET_ALL;
+ mask |= INET_IP6_ONLY;
+ } else if (strcmp(k, "list") == 0) {
+ mask &= ~ONE_ADDR_ONLY;
+ }
+
+ } while (*options);
+ }
+
+ /* Turn the hostname into IP addresses */
+ ret = dns_resolver(hostname, NULL);
+ if (ret)
+ nsError(NO_DATA, hostname);
+
+ /* handle a lack of results */
+ if (payload_index == 0)
+ nsError(NO_DATA, hostname);
+
+ /* must include a NUL char at the end of the payload */
+ payload[payload_index].iov_base = "";
+ payload[payload_index++].iov_len = 1;
+ dump_payload();
+
+ /* load the key with data key */
+ if (!debug_mode) {
+ ret = keyctl_instantiate_iov(key, payload, payload_index, 0);
+ if (ret == -1)
+ error("%s: keyctl_instantiate: %m", __func__);
+ }
+
+ exit(0);
+}
+
+/*
+ * Print usage details,
+ */
+static __attribute__((noreturn))
+void usage(void)
+{
+ if (isatty(2)) {
+ fprintf(stderr,
+ "Usage: %s [-vv] key_serial\n",
+ prog);
+ fprintf(stderr,
+ "Usage: %s -D [-vv] <desc> <calloutinfo>\n",
+ prog);
+ } else {
+ info("Usage: %s [-vv] key_serial", prog);
+ }
+ exit(2);
+}
+
+const struct option long_options[] = {
+ { "debug", 0, NULL, 'D' },
+ { "verbose", 0, NULL, 'v' },
+ { "version", 0, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+};
+
+/*
+ *
+ */
+int main(int argc, char *argv[])
+{
+ int ktlen, qtlen, ret;
+ char *keyend, *p;
+ char *callout_info = NULL;
+ char *buf = NULL, *name;
+
+ openlog(prog, 0, LOG_DAEMON);
+
+ while ((ret = getopt_long(argc, argv, "vDV", long_options, NULL)) != -1) {
+ switch (ret) {
+ case 'D':
+ debug_mode = 1;
+ continue;
+ case 'V':
+ printf("version: %s from %s (%s)\n",
+ DNS_PARSE_VERSION,
+ keyutils_version_string,
+ keyutils_build_string);
+ exit(0);
+ case 'v':
+ verbose++;
+ continue;
+ default:
+ if (!isatty(2))
+ syslog(LOG_ERR, "unknown option: %c", ret);
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (!debug_mode) {
+ if (argc != 1)
+ usage();
+
+ /* get the key ID */
+ if (!**argv)
+ error("Invalid blank key ID");
+ key = strtol(*argv, &p, 10);
+ if (*p)
+ error("Invalid key ID format");
+
+ /* get the key description (of the form "x;x;x;x;<query_type>:<name>") */
+ ret = keyctl_describe_alloc(key, &buf);
+ if (ret == -1)
+ error("keyctl_describe_alloc failed: %m");
+
+ /* get the callout_info (which can supply options) */
+ ret = keyctl_read_alloc(KEY_SPEC_REQKEY_AUTH_KEY, (void **)&callout_info);
+ if (ret == -1)
+ error("Invalid key callout_info read: %m");
+ } else {
+ if (argc != 2)
+ usage();
+
+ ret = asprintf(&buf, "%s;-1;-1;0;%s", key_type, argv[0]);
+ if (ret < 0)
+ error("Error %m");
+ callout_info = argv[1];
+ }
+
+ ret = 1;
+ info("Key description: '%s'", buf);
+ info("Callout info: '%s'", callout_info);
+
+ p = strchr(buf, ';');
+ if (!p)
+ error("Badly formatted key description '%s'", buf);
+ ktlen = p - buf;
+
+ /* make sure it's the type we are expecting */
+ if (ktlen != sizeof(key_type) - 1 ||
+ memcmp(buf, key_type, ktlen) != 0)
+ error("Key type is not supported: '%*.*s'", ktlen, ktlen, buf);
+
+ keyend = buf + ktlen + 1;
+
+ /* the actual key description follows the last semicolon */
+ keyend = rindex(keyend, ';');
+ if (!keyend)
+ error("Invalid key description: %s", buf);
+ keyend++;
+
+ name = index(keyend, ':');
+ if (!name)
+ dns_query_a_or_aaaa(keyend, callout_info);
+
+ qtlen = name - keyend;
+ name++;
+
+ info("Query type: '%*.*s'", qtlen, qtlen, keyend);
+
+ if ((qtlen == sizeof(a_query_type) - 1 &&
+ memcmp(keyend, a_query_type, sizeof(a_query_type) - 1) == 0) ||
+ (qtlen == sizeof(aaaa_query_type) - 1 &&
+ memcmp(keyend, aaaa_query_type, sizeof(aaaa_query_type) - 1) == 0)
+ ) {
+ info("Do DNS query of A/AAAA type for:'%s' mask:'%s'",
+ name, callout_info);
+ dns_query_a_or_aaaa(name, callout_info);
+ }
+
+ if (qtlen == sizeof(afsdb_query_type) - 1 &&
+ memcmp(keyend, afsdb_query_type, sizeof(afsdb_query_type) - 1) == 0
+ ) {
+ info("Do AFS VL server query for:'%s' mask:'%s'",
+ name, callout_info);
+ afs_look_up_VL_servers(name, callout_info);
+ }
+
+ error("Query type: \"%*.*s\" is not supported", qtlen, qtlen, keyend);
+}
diff --git a/keyctl.c b/keyctl.c
new file mode 100644
index 0000000..4c5d91c
--- /dev/null
+++ b/keyctl.c
@@ -0,0 +1,2375 @@
+/* keyctl.c: key control program
+ *
+ * Copyright (C) 2005, 2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * 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
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <asm/unistd.h>
+#include "keyutils.h"
+#include <limits.h>
+
+struct command {
+ void (*action)(int argc, char *argv[]) __attribute__((noreturn));
+ const char *name;
+ const char *format;
+};
+
+#define nr __attribute__((noreturn))
+
+static nr void act_keyctl___version(int argc, char *argv[]);
+static nr void act_keyctl_show(int argc, char *argv[]);
+static nr void act_keyctl_add(int argc, char *argv[]);
+static nr void act_keyctl_padd(int argc, char *argv[]);
+static nr void act_keyctl_request(int argc, char *argv[]);
+static nr void act_keyctl_request2(int argc, char *argv[]);
+static nr void act_keyctl_prequest2(int argc, char *argv[]);
+static nr void act_keyctl_update(int argc, char *argv[]);
+static nr void act_keyctl_pupdate(int argc, char *argv[]);
+static nr void act_keyctl_newring(int argc, char *argv[]);
+static nr void act_keyctl_revoke(int argc, char *argv[]);
+static nr void act_keyctl_clear(int argc, char *argv[]);
+static nr void act_keyctl_link(int argc, char *argv[]);
+static nr void act_keyctl_unlink(int argc, char *argv[]);
+static nr void act_keyctl_search(int argc, char *argv[]);
+static nr void act_keyctl_read(int argc, char *argv[]);
+static nr void act_keyctl_pipe(int argc, char *argv[]);
+static nr void act_keyctl_print(int argc, char *argv[]);
+static nr void act_keyctl_list(int argc, char *argv[]);
+static nr void act_keyctl_rlist(int argc, char *argv[]);
+static nr void act_keyctl_describe(int argc, char *argv[]);
+static nr void act_keyctl_rdescribe(int argc, char *argv[]);
+static nr void act_keyctl_chown(int argc, char *argv[]);
+static nr void act_keyctl_chgrp(int argc, char *argv[]);
+static nr void act_keyctl_setperm(int argc, char *argv[]);
+static nr void act_keyctl_session(int argc, char *argv[]);
+static nr void act_keyctl_instantiate(int argc, char *argv[]);
+static nr void act_keyctl_pinstantiate(int argc, char *argv[]);
+static nr void act_keyctl_negate(int argc, char *argv[]);
+static nr void act_keyctl_timeout(int argc, char *argv[]);
+static nr void act_keyctl_security(int argc, char *argv[]);
+static nr void act_keyctl_new_session(int argc, char *argv[]);
+static nr void act_keyctl_reject(int argc, char *argv[]);
+static nr void act_keyctl_reap(int argc, char *argv[]);
+static nr void act_keyctl_purge(int argc, char *argv[]);
+static nr void act_keyctl_invalidate(int argc, char *argv[]);
+static nr void act_keyctl_get_persistent(int argc, char *argv[]);
+static nr void act_keyctl_dh_compute(int argc, char *argv[]);
+static nr void act_keyctl_dh_compute_kdf(int argc, char *argv[]);
+static nr void act_keyctl_dh_compute_kdf_oi(int argc, char *argv[]);
+static nr void act_keyctl_restrict_keyring(int argc, char *argv[]);
+static nr void act_keyctl_pkey_query(int argc, char *argv[]);
+static nr void act_keyctl_pkey_encrypt(int argc, char *argv[]);
+static nr void act_keyctl_pkey_decrypt(int argc, char *argv[]);
+static nr void act_keyctl_pkey_sign(int argc, char *argv[]);
+static nr void act_keyctl_pkey_verify(int argc, char *argv[]);
+static nr void act_keyctl_move(int argc, char *argv[]);
+static nr void act_keyctl_supports(int argc, char *argv[]);
+
+const struct command commands[] = {
+ { act_keyctl___version, "--version", "" },
+ { act_keyctl_add, "add", "<type> <desc> <data> <keyring>" },
+ { act_keyctl_chgrp, "chgrp", "<key> <gid>" },
+ { act_keyctl_chown, "chown", "<key> <uid>" },
+ { act_keyctl_clear, "clear", "<keyring>" },
+ { act_keyctl_describe, "describe", "<keyring>" },
+ { act_keyctl_dh_compute, "dh_compute", "<private> <prime> <base>" },
+ { act_keyctl_dh_compute_kdf, "dh_compute_kdf", "<private> <prime> <base> <len> <hash_name>" },
+ { act_keyctl_dh_compute_kdf_oi, "dh_compute_kdf_oi", "<private> <prime> <base> <len> <hash_name>" },
+ { act_keyctl_instantiate, "instantiate","<key> <data> <keyring>" },
+ { act_keyctl_invalidate,"invalidate", "<key>" },
+ { act_keyctl_get_persistent, "get_persistent", "<keyring> [<uid>]" },
+ { act_keyctl_link, "link", "<key> <keyring>" },
+ { act_keyctl_list, "list", "<keyring>" },
+ { act_keyctl_move, "move", "[-f] <key> <from_keyring> <to_keyring>" },
+ { act_keyctl_negate, "negate", "<key> <timeout> <keyring>" },
+ { act_keyctl_new_session, "new_session", "" },
+ { act_keyctl_newring, "newring", "<name> <keyring>" },
+ { act_keyctl_padd, "padd", "<type> <desc> <keyring>" },
+ { act_keyctl_pinstantiate, "pinstantiate","<key> <keyring>" },
+ { act_keyctl_pipe, "pipe", "<key>" },
+ { act_keyctl_pkey_query, "pkey_query", "<key> <pass> [k=v]*" },
+ { act_keyctl_pkey_encrypt, "pkey_encrypt", "<key> <pass> <datafile> [k=v]*" },
+ { act_keyctl_pkey_decrypt, "pkey_decrypt", "<key> <pass> <datafile> [k=v]*" },
+ { act_keyctl_pkey_sign, "pkey_sign", "<key> <pass> <datafile> [k=v]*" },
+ { act_keyctl_pkey_verify, "pkey_verify", "<key> <pass> <datafile> <sigfile> [k=v]*" },
+ { act_keyctl_prequest2, "prequest2", "<type> <desc> [<dest_keyring>]" },
+ { act_keyctl_print, "print", "<key>" },
+ { act_keyctl_pupdate, "pupdate", "<key>" },
+ { act_keyctl_purge, "purge", "<type>" },
+ { NULL, "purge", "[-p] [-i] <type> <desc>" },
+ { NULL, "purge", "-s <type> <desc>" },
+ { act_keyctl_rdescribe, "rdescribe", "<keyring> [sep]" },
+ { act_keyctl_read, "read", "<key>" },
+ { act_keyctl_reap, "reap", "[-v]" },
+ { act_keyctl_reject, "reject", "<key> <timeout> <error> <keyring>" },
+ { act_keyctl_request, "request", "<type> <desc> [<dest_keyring>]" },
+ { act_keyctl_request2, "request2", "<type> <desc> <info> [<dest_keyring>]" },
+ { act_keyctl_restrict_keyring, "restrict_keyring", "<keyring> [<type> [<restriction>]]" },
+ { act_keyctl_revoke, "revoke", "<key>" },
+ { act_keyctl_rlist, "rlist", "<keyring>" },
+ { act_keyctl_search, "search", "<keyring> <type> <desc> [<dest_keyring>]" },
+ { act_keyctl_security, "security", "<key>" },
+ { act_keyctl_session, "session", "" },
+ { NULL, "session", "- [<prog> <arg1> <arg2> ...]" },
+ { NULL, "session", "<name> [<prog> <arg1> <arg2> ...]" },
+ { act_keyctl_setperm, "setperm", "<key> <mask>" },
+ { act_keyctl_show, "show", "[-x] [<keyring>]" },
+ { act_keyctl_supports, "supports", "[<cap>]" },
+ { act_keyctl_timeout, "timeout", "<key> <timeout>" },
+ { act_keyctl_unlink, "unlink", "<key> [<keyring>]" },
+ { act_keyctl_update, "update", "<key> <data>" },
+ { NULL, NULL, NULL }
+};
+
+static int dump_key_tree(key_serial_t keyring, const char *name, int hex_key_IDs);
+static void format(void) __attribute__((noreturn));
+static void error(const char *msg) __attribute__((noreturn));
+static key_serial_t get_key_id(char *arg);
+static void *read_file(const char *name, size_t *_size);
+
+static uid_t myuid;
+static gid_t mygid, *mygroups;
+static int myngroups;
+static int verbose;
+
+/*****************************************************************************/
+/*
+ * handle an error
+ */
+static inline void error(const char *msg)
+{
+ perror(msg);
+ exit(1);
+
+} /* end error() */
+
+/*****************************************************************************/
+/*
+ * execute the appropriate subcommand
+ */
+int main(int argc, char *argv[])
+{
+ const struct command *cmd, *best;
+ int n;
+
+ argv++;
+ argc--;
+
+ if (argc == 0)
+ format();
+
+ /* find the best fit command */
+ best = NULL;
+ n = strlen(*argv);
+
+ for (cmd = commands; cmd->name; cmd++) {
+ if (!cmd->action)
+ continue;
+ if (strlen(cmd->name) > n)
+ continue;
+ if (memcmp(cmd->name, *argv, n) != 0)
+ continue;
+
+ if (cmd->name[n] == 0) {
+ /* exact match */
+ best = cmd;
+ break;
+ }
+
+ /* partial match */
+ if (best) {
+ fprintf(stderr, "Ambiguous command\n");
+ exit(2);
+ }
+
+ best = cmd;
+ }
+
+ if (!best) {
+ fprintf(stderr, "Unknown command\n");
+ exit(2);
+ }
+
+ best->action(argc, argv);
+
+} /* end main() */
+
+/*****************************************************************************/
+/*
+ * display command format information
+ */
+static void format(void)
+{
+ const struct command *cmd;
+
+ fprintf(stderr, "Format:\n");
+
+ for (cmd = commands; cmd->name; cmd++)
+ fprintf(stderr, " keyctl %s %s\n", cmd->name, cmd->format);
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Key/keyring ID:\n");
+ fprintf(stderr, " <nnn> numeric keyring ID\n");
+ fprintf(stderr, " @t thread keyring\n");
+ fprintf(stderr, " @p process keyring\n");
+ fprintf(stderr, " @s session keyring\n");
+ fprintf(stderr, " @u user keyring\n");
+ fprintf(stderr, " @us user default session keyring\n");
+ fprintf(stderr, " @g group keyring\n");
+ fprintf(stderr, " @a assumed request_key authorisation key\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, "<type> can be \"user\" for a user-defined keyring\n");
+ fprintf(stderr, "If you do this, prefix the description with \"<subtype>:\"\n");
+
+ exit(2);
+
+} /* end format() */
+
+/*****************************************************************************/
+/*
+ * Display version information
+ */
+static void act_keyctl___version(int argc, char *argv[])
+{
+ printf("keyctl from %s (Built %s)\n",
+ keyutils_version_string, keyutils_build_string);
+ exit(0);
+}
+
+/*****************************************************************************/
+/*
+ * grab data from stdin
+ */
+static char *grab_stdin(size_t *_size)
+{
+ static char input[1024 * 1024 + 1];
+ int n, tmp;
+
+ n = 0;
+ do {
+ tmp = read(0, input + n, sizeof(input) - 1 - n);
+ if (tmp < 0)
+ error("stdin");
+
+ if (tmp == 0)
+ break;
+
+ n += tmp;
+
+ } while (n < sizeof(input));
+
+ if (n >= sizeof(input)) {
+ fprintf(stderr, "Too much data read on stdin\n");
+ exit(1);
+ }
+
+ input[n] = '\0';
+ *_size = n;
+
+ return input;
+
+} /* end grab_stdin() */
+
+/*
+ * Load the groups list and grab the process's UID and GID.
+ */
+static void grab_creds(void)
+{
+ static int inited;
+
+ if (inited)
+ return;
+ inited = 1;
+
+ /* grab my UID, GID and groups */
+ myuid = geteuid();
+ mygid = getegid();
+ myngroups = getgroups(0, NULL);
+
+ if (myuid == -1 || mygid == -1 || myngroups == -1)
+ error("Unable to get UID/GID/#Groups\n");
+
+ mygroups = calloc(myngroups, sizeof(gid_t));
+ if (!mygroups)
+ error("calloc");
+
+ myngroups = getgroups(myngroups, mygroups);
+ if (myngroups < 0)
+ error("Unable to get Groups\n");
+}
+
+/*****************************************************************************/
+/*
+ * convert the permissions mask to a string representing the permissions we
+ * have actually been granted
+ */
+static void calc_perms(char *pretty, key_perm_t perm, uid_t uid, gid_t gid)
+{
+ unsigned perms;
+ gid_t *pg;
+ int loop;
+
+ grab_creds();
+
+ perms = (perm & KEY_POS_ALL) >> 24;
+
+ if (uid == myuid) {
+ perms |= (perm & KEY_USR_ALL) >> 16;
+ goto write_mask;
+ }
+
+ if (gid != -1) {
+ if (gid == mygid) {
+ perms |= (perm & KEY_GRP_ALL) >> 8;
+ goto write_mask;
+ }
+
+ pg = mygroups;
+ for (loop = myngroups; loop > 0; loop--, pg++) {
+ if (gid == *pg) {
+ perms |= (perm & KEY_GRP_ALL) >> 8;
+ goto write_mask;
+ }
+ }
+ }
+
+ perms |= (perm & KEY_OTH_ALL);
+
+write_mask:
+ sprintf(pretty, "--%c%c%c%c%c%c",
+ perms & KEY_OTH_SETATTR ? 'a' : '-',
+ perms & KEY_OTH_LINK ? 'l' : '-',
+ perms & KEY_OTH_SEARCH ? 's' : '-',
+ perms & KEY_OTH_WRITE ? 'w' : '-',
+ perms & KEY_OTH_READ ? 'r' : '-',
+ perms & KEY_OTH_VIEW ? 'v' : '-');
+
+} /* end calc_perms() */
+
+/*****************************************************************************/
+/*
+ * show the parent process's session keyring
+ */
+static void act_keyctl_show(int argc, char *argv[])
+{
+ key_serial_t keyring = KEY_SPEC_SESSION_KEYRING;
+ int hex_key_IDs = 0;
+
+ if (argc >= 2 && strcmp(argv[1], "-x") == 0) {
+ hex_key_IDs = 1;
+ argc--;
+ argv++;
+ }
+
+ if (argc > 2)
+ format();
+
+ if (argc == 2)
+ keyring = get_key_id(argv[1]);
+
+ dump_key_tree(keyring, argc == 2 ? "Keyring" : "Session Keyring", hex_key_IDs);
+ exit(0);
+
+} /* end act_keyctl_show() */
+
+/*****************************************************************************/
+/*
+ * add a key
+ */
+static void act_keyctl_add(int argc, char *argv[])
+{
+ key_serial_t dest;
+ int ret;
+
+ if (argc != 5)
+ format();
+
+ dest = get_key_id(argv[4]);
+
+ ret = add_key(argv[1], argv[2], argv[3], strlen(argv[3]), dest);
+ if (ret < 0)
+ error("add_key");
+
+ /* print the resulting key ID */
+ printf("%d\n", ret);
+ exit(0);
+
+} /* end act_keyctl_add() */
+
+/*****************************************************************************/
+/*
+ * add a key, reading from a pipe
+ */
+static void act_keyctl_padd(int argc, char *argv[])
+{
+ key_serial_t dest;
+ size_t datalen;
+ void *data;
+ int ret;
+
+
+ if (argc != 4)
+ format();
+
+ dest = get_key_id(argv[3]);
+
+ data = grab_stdin(&datalen);
+
+ ret = add_key(argv[1], argv[2], data, datalen, dest);
+ if (ret < 0)
+ error("add_key");
+
+ /* print the resulting key ID */
+ printf("%d\n", ret);
+ exit(0);
+
+} /* end act_keyctl_padd() */
+
+/*****************************************************************************/
+/*
+ * request a key
+ */
+static void act_keyctl_request(int argc, char *argv[])
+{
+ key_serial_t dest;
+ int ret;
+
+ if (argc != 3 && argc != 4)
+ format();
+
+ dest = 0;
+ if (argc == 4)
+ dest = get_key_id(argv[3]);
+
+ ret = request_key(argv[1], argv[2], NULL, dest);
+ if (ret < 0)
+ error("request_key");
+
+ /* print the resulting key ID */
+ printf("%d\n", ret);
+ exit(0);
+
+} /* end act_keyctl_request() */
+
+/*****************************************************************************/
+/*
+ * request a key, with recourse to /sbin/request-key
+ */
+static void act_keyctl_request2(int argc, char *argv[])
+{
+ key_serial_t dest;
+ int ret;
+
+ if (argc != 4 && argc != 5)
+ format();
+
+ dest = 0;
+ if (argc == 5)
+ dest = get_key_id(argv[4]);
+
+ ret = request_key(argv[1], argv[2], argv[3], dest);
+ if (ret < 0)
+ error("request_key");
+
+ /* print the resulting key ID */
+ printf("%d\n", ret);
+ exit(0);
+
+} /* end act_keyctl_request2() */
+
+/*****************************************************************************/
+/*
+ * request a key, with recourse to /sbin/request-key, reading the callout info
+ * from a pipe
+ */
+static void act_keyctl_prequest2(int argc, char *argv[])
+{
+ char *args[6];
+ size_t datalen;
+
+ if (argc != 3 && argc != 4)
+ format();
+
+ args[0] = argv[0];
+ args[1] = argv[1];
+ args[2] = argv[2];
+ args[3] = grab_stdin(&datalen);
+ args[4] = argv[3];
+ args[5] = NULL;
+
+ act_keyctl_request2(argc + 1, args);
+
+} /* end act_keyctl_prequest2() */
+
+/*****************************************************************************/
+/*
+ * update a key
+ */
+static void act_keyctl_update(int argc, char *argv[])
+{
+ key_serial_t key;
+
+ if (argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ if (keyctl_update(key, argv[2], strlen(argv[2])) < 0)
+ error("keyctl_update");
+
+ exit(0);
+
+} /* end act_keyctl_update() */
+
+/*****************************************************************************/
+/*
+ * update a key, reading from a pipe
+ */
+static void act_keyctl_pupdate(int argc, char *argv[])
+{
+ key_serial_t key;
+ size_t datalen;
+ void *data;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+ data = grab_stdin(&datalen);
+
+ if (keyctl_update(key, data, datalen) < 0)
+ error("keyctl_update");
+
+ exit(0);
+
+} /* end act_keyctl_pupdate() */
+
+/*****************************************************************************/
+/*
+ * create a new keyring
+ */
+static void act_keyctl_newring(int argc, char *argv[])
+{
+ key_serial_t dest;
+ int ret;
+
+ if (argc != 3)
+ format();
+
+ dest = get_key_id(argv[2]);
+
+ ret = add_key("keyring", argv[1], NULL, 0, dest);
+ if (ret < 0)
+ error("add_key");
+
+ printf("%d\n", ret);
+ exit(0);
+
+} /* end act_keyctl_newring() */
+
+/*****************************************************************************/
+/*
+ * revoke a key
+ */
+static void act_keyctl_revoke(int argc, char *argv[])
+{
+ key_serial_t key;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ if (keyctl_revoke(key) < 0)
+ error("keyctl_revoke");
+
+ exit(0);
+
+} /* end act_keyctl_revoke() */
+
+/*****************************************************************************/
+/*
+ * clear a keyring
+ */
+static void act_keyctl_clear(int argc, char *argv[])
+{
+ key_serial_t keyring;
+
+ if (argc != 2)
+ format();
+
+ keyring = get_key_id(argv[1]);
+
+ if (keyctl_clear(keyring) < 0)
+ error("keyctl_clear");
+
+ exit(0);
+
+} /* end act_keyctl_clear() */
+
+/*****************************************************************************/
+/*
+ * link a key to a keyring
+ */
+static void act_keyctl_link(int argc, char *argv[])
+{
+ key_serial_t keyring, key;
+
+ if (argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+ keyring = get_key_id(argv[2]);
+
+ if (keyctl_link(key, keyring) < 0)
+ error("keyctl_link");
+
+ exit(0);
+
+} /* end act_keyctl_link() */
+
+/*
+ * Attempt to unlink a key matching the ID
+ */
+static int act_keyctl_unlink_func(key_serial_t parent, key_serial_t key,
+ char *desc, int desc_len, void *data)
+{
+ key_serial_t *target = data;
+
+ if (key == *target)
+ return keyctl_unlink(key, parent) < 0 ? 0 : 1;
+ return 0;
+}
+
+/*
+ * Unlink a key from a keyring or from the session keyring tree.
+ */
+static void act_keyctl_unlink(int argc, char *argv[])
+{
+ key_serial_t keyring, key;
+ int n;
+
+ if (argc != 2 && argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ if (argc == 3) {
+ keyring = get_key_id(argv[2]);
+ if (keyctl_unlink(key, keyring) < 0)
+ error("keyctl_unlink");
+ } else {
+ n = recursive_session_key_scan(act_keyctl_unlink_func, &key);
+ printf("%d links removed\n", n);
+ }
+
+ exit(0);
+}
+
+/*****************************************************************************/
+/*
+ * search a keyring for a key
+ */
+static void act_keyctl_search(int argc, char *argv[])
+{
+ key_serial_t keyring, dest;
+ int ret;
+
+ if (argc != 4 && argc != 5)
+ format();
+
+ keyring = get_key_id(argv[1]);
+
+ dest = 0;
+ if (argc == 5)
+ dest = get_key_id(argv[4]);
+
+ ret = keyctl_search(keyring, argv[2], argv[3], dest);
+ if (ret < 0)
+ error("keyctl_search");
+
+ /* print the ID of the key we found */
+ printf("%d\n", ret);
+ exit(0);
+
+} /* end act_keyctl_search() */
+
+/*****************************************************************************/
+/*
+ * read a key
+ */
+static void act_keyctl_read(int argc, char *argv[])
+{
+ key_serial_t key;
+ void *buffer;
+ char *p;
+ int ret, sep, col;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ /* read the key payload data */
+ ret = keyctl_read_alloc(key, &buffer);
+ if (ret < 0)
+ error("keyctl_read_alloc");
+
+ if (ret == 0) {
+ printf("No data in key\n");
+ exit(0);
+ }
+
+ /* hexdump the contents */
+ printf("%u bytes of data in key:\n", ret);
+
+ sep = 0;
+ col = 0;
+ p = buffer;
+
+ do {
+ if (sep) {
+ putchar(sep);
+ sep = 0;
+ }
+
+ printf("%02hhx", *p);
+ p++;
+
+ col++;
+ if (col % 32 == 0)
+ sep = '\n';
+ else if (col % 4 == 0)
+ sep = ' ';
+
+ } while (--ret > 0);
+
+ printf("\n");
+ exit(0);
+
+} /* end act_keyctl_read() */
+
+/*****************************************************************************/
+/*
+ * read a key and dump raw to stdout
+ */
+static void act_keyctl_pipe(int argc, char *argv[])
+{
+ key_serial_t key;
+ void *buffer;
+ int ret;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ /* read the key payload data */
+ ret = keyctl_read_alloc(key, &buffer);
+ if (ret < 0)
+ error("keyctl_read_alloc");
+
+ if (ret > 0 && write(1, buffer, ret) < 0)
+ error("write");
+ exit(0);
+
+} /* end act_keyctl_pipe() */
+
+/*****************************************************************************/
+/*
+ * read a key and dump to stdout in printable form
+ */
+static void act_keyctl_print(int argc, char *argv[])
+{
+ key_serial_t key;
+ void *buffer;
+ char *p;
+ int loop, ret;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ /* read the key payload data */
+ ret = keyctl_read_alloc(key, &buffer);
+ if (ret < 0)
+ error("keyctl_read_alloc");
+
+ /* see if it's printable */
+ p = buffer;
+ for (loop = ret; loop > 0; loop--, p++)
+ if (!isprint(*p))
+ goto not_printable;
+
+ /* it is */
+ printf("%s\n", (char *) buffer);
+ exit(0);
+
+not_printable:
+ /* it isn't */
+ printf(":hex:");
+ p = buffer;
+ for (loop = ret; loop > 0; loop--, p++)
+ printf("%02hhx", *p);
+ printf("\n");
+ exit(0);
+
+} /* end act_keyctl_print() */
+
+/*****************************************************************************/
+/*
+ * list a keyring
+ */
+static void act_keyctl_list(int argc, char *argv[])
+{
+ key_serial_t keyring, key, *pk;
+ key_perm_t perm;
+ void *keylist;
+ char *buffer, pretty_mask[9];
+ uid_t uid;
+ gid_t gid;
+ int count, tlen, dpos, n, ret;
+
+ if (argc != 2)
+ format();
+
+ keyring = get_key_id(argv[1]);
+
+ /* read the key payload data */
+ count = keyctl_read_alloc(keyring, &keylist);
+ if (count < 0)
+ error("keyctl_read_alloc");
+
+ count /= sizeof(key_serial_t);
+
+ if (count == 0) {
+ printf("keyring is empty\n");
+ exit(0);
+ }
+
+ /* list the keys in the keyring */
+ if (count == 1)
+ printf("1 key in keyring:\n");
+ else
+ printf("%u keys in keyring:\n", count);
+
+ pk = keylist;
+ do {
+ key = *pk++;
+
+ ret = keyctl_describe_alloc(key, &buffer);
+ if (ret < 0) {
+ printf("%9d: key inaccessible (%m)\n", key);
+ continue;
+ }
+
+ uid = 0;
+ gid = 0;
+ perm = 0;
+
+ tlen = -1;
+ dpos = -1;
+
+ n = sscanf((char *) buffer, "%*[^;]%n;%d;%d;%x;%n",
+ &tlen, &uid, &gid, &perm, &dpos);
+ if (n != 3) {
+ fprintf(stderr, "Unparseable description obtained for key %d\n", key);
+ exit(3);
+ }
+
+ calc_perms(pretty_mask, perm, uid, gid);
+
+ printf("%9d: %s %5d %5d %*.*s: %s\n",
+ key,
+ pretty_mask,
+ uid, gid,
+ tlen, tlen, buffer,
+ buffer + dpos);
+
+ free(buffer);
+
+ } while (--count);
+
+ exit(0);
+
+} /* end act_keyctl_list() */
+
+/*****************************************************************************/
+/*
+ * produce a raw list of a keyring
+ */
+static void act_keyctl_rlist(int argc, char *argv[])
+{
+ key_serial_t keyring, key, *pk;
+ void *keylist;
+ int count;
+
+ if (argc != 2)
+ format();
+
+ keyring = get_key_id(argv[1]);
+
+ /* read the key payload data */
+ count = keyctl_read_alloc(keyring, &keylist);
+ if (count < 0)
+ error("keyctl_read_alloc");
+
+ count /= sizeof(key_serial_t);
+
+ /* list the keys in the keyring */
+ if (count <= 0) {
+ printf("\n");
+ }
+ else {
+ pk = keylist;
+ for (; count > 0; count--) {
+ key = *pk++;
+ printf("%d%c", key, count == 1 ? '\n' : ' ');
+ }
+ }
+
+ exit(0);
+
+} /* end act_keyctl_rlist() */
+
+/*****************************************************************************/
+/*
+ * describe a key
+ */
+static void act_keyctl_describe(int argc, char *argv[])
+{
+ key_serial_t key;
+ key_perm_t perm;
+ char *buffer;
+ uid_t uid;
+ gid_t gid;
+ int tlen, dpos, n, ret;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ /* get key description */
+ ret = keyctl_describe_alloc(key, &buffer);
+ if (ret < 0)
+ error("keyctl_describe_alloc");
+
+ /* parse it */
+ uid = 0;
+ gid = 0;
+ perm = 0;
+
+ tlen = -1;
+ dpos = -1;
+
+ n = sscanf(buffer, "%*[^;]%n;%d;%d;%x;%n",
+ &tlen, &uid, &gid, &perm, &dpos);
+ if (n != 3) {
+ fprintf(stderr, "Unparseable description obtained for key %d\n", key);
+ exit(3);
+ }
+
+ /* display it */
+ printf("%9d:"
+ " %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c"
+ " %5d %5d %*.*s: %s\n",
+ key,
+ perm & KEY_POS_SETATTR ? 'a' : '-',
+ perm & KEY_POS_LINK ? 'l' : '-',
+ perm & KEY_POS_SEARCH ? 's' : '-',
+ perm & KEY_POS_WRITE ? 'w' : '-',
+ perm & KEY_POS_READ ? 'r' : '-',
+ perm & KEY_POS_VIEW ? 'v' : '-',
+
+ perm & KEY_USR_SETATTR ? 'a' : '-',
+ perm & KEY_USR_LINK ? 'l' : '-',
+ perm & KEY_USR_SEARCH ? 's' : '-',
+ perm & KEY_USR_WRITE ? 'w' : '-',
+ perm & KEY_USR_READ ? 'r' : '-',
+ perm & KEY_USR_VIEW ? 'v' : '-',
+
+ perm & KEY_GRP_SETATTR ? 'a' : '-',
+ perm & KEY_GRP_LINK ? 'l' : '-',
+ perm & KEY_GRP_SEARCH ? 's' : '-',
+ perm & KEY_GRP_WRITE ? 'w' : '-',
+ perm & KEY_GRP_READ ? 'r' : '-',
+ perm & KEY_GRP_VIEW ? 'v' : '-',
+
+ perm & KEY_OTH_SETATTR ? 'a' : '-',
+ perm & KEY_OTH_LINK ? 'l' : '-',
+ perm & KEY_OTH_SEARCH ? 's' : '-',
+ perm & KEY_OTH_WRITE ? 'w' : '-',
+ perm & KEY_OTH_READ ? 'r' : '-',
+ perm & KEY_OTH_VIEW ? 'v' : '-',
+ uid, gid,
+ tlen, tlen, buffer,
+ buffer + dpos);
+
+ exit(0);
+
+} /* end act_keyctl_describe() */
+
+/*****************************************************************************/
+/*
+ * get raw key description
+ */
+static void act_keyctl_rdescribe(int argc, char *argv[])
+{
+ key_serial_t key;
+ char *buffer, *q;
+ int ret;
+
+ if (argc != 2 && argc != 3)
+ format();
+ if (argc == 3 && !argv[2][0])
+ format();
+
+ key = get_key_id(argv[1]);
+
+ /* get key description */
+ ret = keyctl_describe_alloc(key, &buffer);
+ if (ret < 0)
+ error("keyctl_describe");
+
+ /* replace semicolon separators with requested alternative */
+ if (argc == 3) {
+ for (q = buffer; *q; q++)
+ if (*q == ';')
+ *q = argv[2][0];
+ }
+
+ /* display raw description */
+ printf("%s\n", buffer);
+ exit(0);
+
+} /* end act_keyctl_rdescribe() */
+
+/*****************************************************************************/
+/*
+ * change a key's ownership
+ */
+static void act_keyctl_chown(int argc, char *argv[])
+{
+ key_serial_t key;
+ uid_t uid;
+ char *q;
+
+ if (argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ uid = strtoul(argv[2], &q, 0);
+ if (*q) {
+ fprintf(stderr, "Unparsable uid: '%s'\n", argv[2]);
+ exit(2);
+ }
+
+ if (keyctl_chown(key, uid, -1) < 0)
+ error("keyctl_chown");
+
+ exit(0);
+
+} /* end act_keyctl_chown() */
+
+/*****************************************************************************/
+/*
+ * change a key's group ownership
+ */
+static void act_keyctl_chgrp(int argc, char *argv[])
+{
+ key_serial_t key;
+ gid_t gid;
+ char *q;
+
+ if (argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ gid = strtoul(argv[2], &q, 0);
+ if (*q) {
+ fprintf(stderr, "Unparsable gid: '%s'\n", argv[2]);
+ exit(2);
+ }
+
+ if (keyctl_chown(key, -1, gid) < 0)
+ error("keyctl_chown");
+
+ exit(0);
+
+} /* end act_keyctl_chgrp() */
+
+/*****************************************************************************/
+/*
+ * set the permissions on a key
+ */
+static void act_keyctl_setperm(int argc, char *argv[])
+{
+ key_serial_t key;
+ key_perm_t perm;
+ char *q;
+
+ if (argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+ perm = strtoul(argv[2], &q, 0);
+ if (*q) {
+ fprintf(stderr, "Unparsable permissions: '%s'\n", argv[2]);
+ exit(2);
+ }
+
+ if (keyctl_setperm(key, perm) < 0)
+ error("keyctl_setperm");
+
+ exit(0);
+
+} /* end act_keyctl_setperm() */
+
+/*****************************************************************************/
+/*
+ * start a process in a new session
+ */
+static void act_keyctl_session(int argc, char *argv[])
+{
+ char *p, *q;
+ int ret;
+
+ argv++;
+ argc--;
+
+ /* no extra arguments signifies a standard shell in an anonymous
+ * session */
+ p = NULL;
+ if (argc != 0) {
+ /* a dash signifies an anonymous session */
+ p = *argv;
+ if (strcmp(p, "-") == 0)
+ p = NULL;
+
+ argv++;
+ argc--;
+ }
+
+ /* create a new session keyring */
+ ret = keyctl_join_session_keyring(p);
+ if (ret < 0)
+ error("keyctl_join_session_keyring");
+
+ fprintf(stderr, "Joined session keyring: %d\n", ret);
+
+ /* run the standard shell if no arguments */
+ if (argc == 0) {
+ q = getenv("SHELL");
+ if (!q)
+ q = "/bin/sh";
+ execl(q, q, NULL);
+ error(q);
+ }
+
+ /* run the command specified */
+ execvp(argv[0], argv);
+ error(argv[0]);
+
+} /* end act_keyctl_session() */
+
+/*****************************************************************************/
+/*
+ * instantiate a key that's under construction
+ */
+static void act_keyctl_instantiate(int argc, char *argv[])
+{
+ key_serial_t key, dest;
+
+ if (argc != 4)
+ format();
+
+ key = get_key_id(argv[1]);
+ dest = get_key_id(argv[3]);
+
+ if (keyctl_instantiate(key, argv[2], strlen(argv[2]), dest) < 0)
+ error("keyctl_instantiate");
+
+ exit(0);
+
+} /* end act_keyctl_instantiate() */
+
+/*****************************************************************************/
+/*
+ * instantiate a key, reading from a pipe
+ */
+static void act_keyctl_pinstantiate(int argc, char *argv[])
+{
+ key_serial_t key, dest;
+ size_t datalen;
+ void *data;
+
+ if (argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+ dest = get_key_id(argv[2]);
+ data = grab_stdin(&datalen);
+
+ if (keyctl_instantiate(key, data, datalen, dest) < 0)
+ error("keyctl_instantiate");
+
+ exit(0);
+
+} /* end act_keyctl_pinstantiate() */
+
+/*****************************************************************************/
+/*
+ * negate a key that's under construction
+ */
+static void act_keyctl_negate(int argc, char *argv[])
+{
+ unsigned long timeout;
+ key_serial_t key, dest;
+ char *q;
+
+ if (argc != 4)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ timeout = strtoul(argv[2], &q, 10);
+ if (*q) {
+ fprintf(stderr, "Unparsable timeout: '%s'\n", argv[2]);
+ exit(2);
+ }
+
+ dest = get_key_id(argv[3]);
+
+ if (keyctl_negate(key, timeout, dest) < 0)
+ error("keyctl_negate");
+
+ exit(0);
+
+} /* end act_keyctl_negate() */
+
+/*****************************************************************************/
+/*
+ * set a key's timeout
+ */
+static void act_keyctl_timeout(int argc, char *argv[])
+{
+ unsigned long timeout;
+ key_serial_t key;
+ char *q;
+
+ if (argc != 3)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ timeout = strtoul(argv[2], &q, 10);
+ if (*q) {
+ fprintf(stderr, "Unparsable timeout: '%s'\n", argv[2]);
+ exit(2);
+ }
+
+ if (keyctl_set_timeout(key, timeout) < 0)
+ error("keyctl_set_timeout");
+
+ exit(0);
+
+} /* end act_keyctl_timeout() */
+
+/*****************************************************************************/
+/*
+ * get a key's security label
+ */
+static void act_keyctl_security(int argc, char *argv[])
+{
+ key_serial_t key;
+ char *buffer;
+ int ret;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ /* get key description */
+ ret = keyctl_get_security_alloc(key, &buffer);
+ if (ret < 0)
+ error("keyctl_getsecurity");
+
+ printf("%s\n", buffer);
+ exit(0);
+}
+
+/*****************************************************************************/
+/*
+ * install a new session keyring on the parent process
+ */
+static void act_keyctl_new_session(int argc, char *argv[])
+{
+ key_serial_t keyring;
+
+ if (argc != 1)
+ format();
+
+ if (keyctl_join_session_keyring(NULL) < 0)
+ error("keyctl_join_session_keyring");
+
+ if (keyctl_session_to_parent() < 0)
+ error("keyctl_session_to_parent");
+
+ keyring = keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 0);
+ if (keyring < 0)
+ error("keyctl_get_keyring_ID");
+
+ /* print the resulting key ID */
+ printf("%d\n", keyring);
+ exit(0);
+}
+
+/*****************************************************************************/
+/*
+ * reject a key that's under construction
+ */
+static void act_keyctl_reject(int argc, char *argv[])
+{
+ unsigned long timeout;
+ key_serial_t key, dest;
+ unsigned long rejerr;
+ char *q;
+
+ if (argc != 5)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ timeout = strtoul(argv[2], &q, 10);
+ if (*q) {
+ fprintf(stderr, "Unparsable timeout: '%s'\n", argv[2]);
+ exit(2);
+ }
+
+ if (strcmp(argv[3], "rejected") == 0) {
+ rejerr = EKEYREJECTED;
+ } else if (strcmp(argv[3], "revoked") == 0) {
+ rejerr = EKEYREVOKED;
+ } else if (strcmp(argv[3], "expired") == 0) {
+ rejerr = EKEYEXPIRED;
+ } else {
+ rejerr = strtoul(argv[3], &q, 10);
+ if (*q) {
+ fprintf(stderr, "Unparsable error: '%s'\n", argv[3]);
+ exit(2);
+ }
+ }
+
+ dest = get_key_id(argv[4]);
+
+ if (keyctl_reject(key, timeout, rejerr, dest) < 0)
+ error("keyctl_negate");
+
+ exit(0);
+}
+
+/*
+ * Attempt to unlink a key if we can't read it for reasons other than we don't
+ * have permission
+ */
+static int act_keyctl_reap_func(key_serial_t parent, key_serial_t key,
+ char *desc, int desc_len, void *data)
+{
+ if (desc_len < 0 && errno != EACCES) {
+ if (verbose)
+ printf("Reap %d", key);
+ if (keyctl_unlink(key, parent) < 0) {
+ if (verbose)
+ printf("... failed %m\n");
+ return 0;
+ } else {
+ if (verbose)
+ printf("\n");
+ return 1;
+ };
+ }
+ return 0;
+}
+
+/*
+ * Reap the dead keys from the session keyring tree
+ */
+static void act_keyctl_reap(int argc, char *argv[])
+{
+ int n;
+
+ if (argc > 1 && strcmp(argv[1], "-v") == 0) {
+ verbose = 1;
+ argc--;
+ argv++;
+ }
+
+ if (argc != 1)
+ format();
+
+ n = recursive_session_key_scan(act_keyctl_reap_func, NULL);
+ printf("%d keys reaped\n", n);
+ exit(0);
+}
+
+struct purge_data {
+ const char *type;
+ const char *desc;
+ size_t desc_len;
+ size_t type_len;
+ char prefix_match;
+ char case_indep;
+};
+
+/*
+ * Attempt to unlink a key matching the type
+ */
+static int act_keyctl_purge_type_func(key_serial_t parent, key_serial_t key,
+ char *raw, int raw_len, void *data)
+{
+ const struct purge_data *purge = data;
+ char *p, *type;
+
+ if (parent == 0 || !raw)
+ return 0;
+
+ /* type is everything before the first semicolon */
+ type = raw;
+ p = memchr(raw, ';', raw_len);
+ if (!p)
+ return 0;
+ *p = 0;
+ if (strcmp(type, purge->type) != 0)
+ return 0;
+
+ return keyctl_unlink(key, parent) < 0 ? 0 : 1;
+}
+
+/*
+ * Attempt to unlink a key matching the type and description literally
+ */
+static int act_keyctl_purge_literal_func(key_serial_t parent, key_serial_t key,
+ char *raw, int raw_len, void *data)
+{
+ const struct purge_data *purge = data;
+ size_t tlen;
+ char *p, *type, *desc;
+
+ if (parent == 0 || !raw)
+ return 0;
+
+ /* type is everything before the first semicolon */
+ type = raw;
+ p = memchr(type, ';', raw_len);
+ if (!p)
+ return 0;
+
+ tlen = p - type;
+ if (tlen != purge->type_len)
+ return 0;
+ if (memcmp(type, purge->type, tlen) != 0)
+ return 0;
+
+ /* description is everything after the last semicolon */
+ p++;
+ desc = memrchr(p, ';', raw + raw_len - p);
+ if (!desc)
+ return 0;
+ desc++;
+
+ if (purge->prefix_match) {
+ if (raw_len - (desc - raw) < purge->desc_len)
+ return 0;
+ } else {
+ if (raw_len - (desc - raw) != purge->desc_len)
+ return 0;
+ }
+
+ if (purge->case_indep) {
+ if (strncasecmp(purge->desc, desc, purge->desc_len) != 0)
+ return 0;
+ } else {
+ if (memcmp(purge->desc, desc, purge->desc_len) != 0)
+ return 0;
+ }
+
+ printf("%*.*s '%s'\n", (int)tlen, (int)tlen, type, desc);
+
+ return keyctl_unlink(key, parent) < 0 ? 0 : 1;
+}
+
+/*
+ * Attempt to unlink a key matching the type and description literally
+ */
+static int act_keyctl_purge_search_func(key_serial_t parent, key_serial_t keyring,
+ char *raw, int raw_len, void *data)
+{
+ const struct purge_data *purge = data;
+ key_serial_t key;
+ int kcount = 0;
+
+ if (!raw || memcmp(raw, "keyring;", 8) != 0)
+ return 0;
+
+ for (;;) {
+ key = keyctl_search(keyring, purge->type, purge->desc, 0);
+ if (keyctl_unlink(key, keyring) < 0)
+ return kcount;
+ kcount++;
+ }
+ return kcount;
+}
+
+/*
+ * Purge matching keys from a keyring
+ */
+static void act_keyctl_purge(int argc, char *argv[])
+{
+ recursive_key_scanner_t func;
+ struct purge_data purge = {
+ .prefix_match = 0,
+ .case_indep = 0,
+ };
+ int n = 0, search_mode = 0;
+
+ argc--;
+ argv++;
+ while (argc > 0 && argv[0][0] == '-') {
+ if (argv[0][1] == 's')
+ search_mode = 1;
+ else if (argv[0][1] == 'p')
+ purge.prefix_match = 1;
+ else if (argv[0][1] == 'i')
+ purge.case_indep = 1;
+ else
+ format();
+ argc--;
+ argv++;
+ }
+
+ if (argc < 1)
+ format();
+
+ purge.type = argv[0];
+ purge.desc = argv[1];
+ purge.type_len = strlen(purge.type);
+ purge.desc_len = purge.desc ? strlen(purge.desc) : 0;
+
+ if (search_mode == 1) {
+ if (argc != 2 || purge.prefix_match || purge.case_indep)
+ format();
+ /* purge all keys of a specific type and description, according
+ * to the kernel's comparator */
+ func = act_keyctl_purge_search_func;
+ } else if (argc == 1) {
+ if (purge.prefix_match || purge.case_indep)
+ format();
+ /* purge all keys of a specific type */
+ func = act_keyctl_purge_type_func;
+ } else if (argc == 2) {
+ /* purge all keys of a specific type with literally matching
+ * description */
+ func = act_keyctl_purge_literal_func;
+ } else {
+ format();
+ }
+
+ n = recursive_session_key_scan(func, &purge);
+ printf("purged %d keys\n", n);
+ exit(0);
+}
+
+/*****************************************************************************/
+/*
+ * Invalidate a key
+ */
+static void act_keyctl_invalidate(int argc, char *argv[])
+{
+ key_serial_t key;
+
+ if (argc != 2)
+ format();
+
+ key = get_key_id(argv[1]);
+
+ if (keyctl_invalidate(key) < 0)
+ error("keyctl_invalidate");
+
+ exit(0);
+}
+
+/*****************************************************************************/
+/*
+ * Get the per-UID persistent keyring
+ */
+static void act_keyctl_get_persistent(int argc, char *argv[])
+{
+ key_serial_t dest, ret;
+ uid_t uid = -1;
+ char *q;
+
+ if (argc != 2 && argc != 3)
+ format();
+
+ dest = get_key_id(argv[1]);
+
+ if (argc > 2) {
+ uid = strtoul(argv[2], &q, 0);
+ if (*q) {
+ fprintf(stderr, "Unparsable uid: '%s'\n", argv[2]);
+ exit(2);
+ }
+ }
+
+ ret = keyctl_get_persistent(uid, dest);
+ if (ret < 0)
+ error("keyctl_get_persistent");
+
+ /* print the resulting key ID */
+ printf("%d\n", ret);
+ exit(0);
+}
+
+/*****************************************************************************/
+/*
+ * Perform Diffie-Hellman computation
+ */
+static void act_keyctl_dh_compute(int argc, char *argv[])
+{
+ key_serial_t priv, prime, base;
+ void *buffer;
+ char *p;
+ int ret, sep, col;
+
+ if (argc != 4)
+ format();
+
+ priv = get_key_id(argv[1]);
+ prime = get_key_id(argv[2]);
+ base = get_key_id(argv[3]);
+
+ ret = keyctl_dh_compute_alloc(priv, prime, base, &buffer);
+ if (ret < 0)
+ error("keyctl_dh_compute_alloc");
+
+ /* hexdump the contents */
+ printf("%u bytes of data in result:\n", ret);
+
+ sep = 0;
+ col = 0;
+ p = buffer;
+
+ do {
+ if (sep) {
+ putchar(sep);
+ sep = 0;
+ }
+
+ printf("%02hhx", *p);
+ *p = 0x00; /* zeroize buffer */
+ p++;
+
+ col++;
+ if (col % 32 == 0)
+ sep = '\n';
+ else if (col % 4 == 0)
+ sep = ' ';
+
+ } while (--ret > 0);
+
+ printf("\n");
+
+ free(buffer);
+
+ exit(0);
+}
+
+static void act_keyctl_dh_compute_kdf(int argc, char *argv[])
+{
+ key_serial_t private, prime, base;
+ char *buffer;
+ char *p;
+ int ret, sep, col;
+ unsigned long buflen = 0;
+
+ if (argc != 6)
+ format();
+
+ private = get_key_id(argv[1]);
+ prime = get_key_id(argv[2]);
+ base = get_key_id(argv[3]);
+
+ buflen = strtoul(argv[4], NULL, 10);
+ if (buflen == ULONG_MAX)
+ error("dh_compute: cannot convert generated length value");
+
+ buffer = malloc(buflen);
+ if (!buffer)
+ error("dh_compute: cannot allocate memory");
+
+ ret = keyctl_dh_compute_kdf(private, prime, base, argv[5], NULL, 0,
+ buffer, buflen);
+ if (ret < 0)
+ error("keyctl_dh_compute_kdf");
+
+ /* hexdump the contents */
+ printf("%u bytes of data in result:\n", ret);
+
+ sep = 0;
+ col = 0;
+ p = buffer;
+
+ do {
+ if (sep) {
+ putchar(sep);
+ sep = 0;
+ }
+
+ printf("%02hhx", *p);
+ *p = 0x00; /* zeroize buffer */
+ p++;
+
+ col++;
+ if (col % 32 == 0)
+ sep = '\n';
+ else if (col % 4 == 0)
+ sep = ' ';
+
+ } while (--ret > 0);
+
+ printf("\n");
+
+ free(buffer);
+
+ exit(0);
+}
+
+static void act_keyctl_dh_compute_kdf_oi(int argc, char *argv[])
+{
+ key_serial_t private, prime, base;
+ char *buffer;
+ char *p;
+ int ret, sep, col;
+ unsigned long buflen = 0;
+ size_t oilen;
+ void *oi;
+
+ if (argc != 6)
+ format();
+
+ private = get_key_id(argv[1]);
+ prime = get_key_id(argv[2]);
+ base = get_key_id(argv[3]);
+
+ buflen = strtoul(argv[4], NULL, 10);
+ if (buflen == ULONG_MAX)
+ error("dh_compute: cannot convert generated length value");
+
+ buffer = malloc(buflen);
+ if (!buffer)
+ error("dh_compute: cannot allocate memory");
+
+ oi = grab_stdin(&oilen);
+
+ ret = keyctl_dh_compute_kdf(private, prime, base, argv[5], oi, oilen,
+ buffer, buflen);
+ if (ret < 0)
+ error("keyctl_dh_compute_kdf");
+
+ /* hexdump the contents */
+ printf("%u bytes of data in result:\n", ret);
+
+ sep = 0;
+ col = 0;
+ p = buffer;
+
+ do {
+ if (sep) {
+ putchar(sep);
+ sep = 0;
+ }
+
+ printf("%02hhx", *p);
+ *p = 0x00; /* zeroize buffer */
+ p++;
+
+ col++;
+ if (col % 32 == 0)
+ sep = '\n';
+ else if (col % 4 == 0)
+ sep = ' ';
+
+ } while (--ret > 0);
+
+ printf("\n");
+
+ free(buffer);
+
+ exit(0);
+}
+
+/*****************************************************************************/
+/*
+ * Restrict the keys that may be added to a keyring
+ */
+static void act_keyctl_restrict_keyring(int argc, char *argv[])
+{
+ key_serial_t keyring;
+ char *type = NULL;
+ char *restriction = NULL;
+ long ret;
+
+ if (argc < 2 || argc > 4)
+ format();
+
+ keyring = get_key_id(argv[1]);
+
+ if (argc > 2)
+ type = argv[2];
+
+ if (argc == 4)
+ restriction = argv[3];
+
+ ret = keyctl_restrict_keyring(keyring, type, restriction);
+ if (ret < 0)
+ error("keyctl_restrict_keyring");
+
+ exit(0);
+}
+
+/*
+ * Parse public key operation info arguments.
+ */
+static void pkey_parse_info(char **argv, char info[4096])
+{
+ int i_len = 0;
+
+ /* A number of key=val pairs can be provided after the main arguments
+ * to inform the kernel of things like encoding type and hash function.
+ */
+ for (; *argv; argv++) {
+ int n = strlen(argv[0]);
+
+ if (!memchr(argv[0], '=', n)) {
+ fprintf(stderr, "Option not in key=val form\n");
+ exit(2);
+ }
+ if (n + 1 > 4096 - 1 - i_len) {
+ fprintf(stderr, "Too many info options\n");
+ exit(2);
+ }
+
+ if (i_len > 0)
+ info[i_len++] = ' ';
+ memcpy(info + i_len, argv[0], n);
+ i_len += n;
+ }
+
+ info[i_len] = 0;
+}
+
+/*
+ * Query a public key.
+ */
+static void act_keyctl_pkey_query(int argc, char *argv[])
+{
+ struct keyctl_pkey_query result;
+ key_serial_t key;
+ char info[4096];
+
+ if (argc < 3)
+ format();
+ pkey_parse_info(argv + 3, info);
+
+ key = get_key_id(argv[1]);
+ if (strcmp(argv[2], "0") != 0) {
+ fprintf(stderr, "Password passing is not yet supported\n");
+ exit(2);
+ }
+
+ if (keyctl_pkey_query(key, info, &result) < 0)
+ error("keyctl_pkey_query");
+
+ printf("key_size=%u\n", result.key_size);
+ printf("max_data_size=%u\n", result.max_data_size);
+ printf("max_sig_size=%u\n", result.max_sig_size);
+ printf("max_enc_size=%u\n", result.max_enc_size);
+ printf("max_dec_size=%u\n", result.max_dec_size);
+ printf("encrypt=%c\n", result.supported_ops & KEYCTL_SUPPORTS_ENCRYPT ? 'y' : 'n');
+ printf("decrypt=%c\n", result.supported_ops & KEYCTL_SUPPORTS_DECRYPT ? 'y' : 'n');
+ printf("sign=%c\n", result.supported_ops & KEYCTL_SUPPORTS_SIGN ? 'y' : 'n');
+ printf("verify=%c\n", result.supported_ops & KEYCTL_SUPPORTS_VERIFY ? 'y' : 'n');
+ exit(0);
+}
+
+/*
+ * Encrypt a blob.
+ */
+static void act_keyctl_pkey_encrypt(int argc, char *argv[])
+{
+ struct keyctl_pkey_query result;
+ key_serial_t key;
+ size_t in_len;
+ long out_len;
+ void *in, *out;
+ char info[4096];
+
+ if (argc < 4)
+ format();
+ pkey_parse_info(argv + 4, info);
+
+ key = get_key_id(argv[1]);
+ if (strcmp(argv[2], "0") != 0) {
+ fprintf(stderr, "Password passing is not yet supported\n");
+ exit(2);
+ }
+ in = read_file(argv[3], &in_len);
+
+ if (keyctl_pkey_query(key, info, &result) < 0)
+ error("keyctl_pkey_query");
+
+ out = malloc(result.max_dec_size);
+ if (!out)
+ error("malloc");
+
+ out_len = keyctl_pkey_encrypt(key, info,
+ in, in_len, out, result.max_dec_size);
+ if (out_len < 0)
+ error("keyctl_pkey_encrypt");
+
+ if (fwrite(out, out_len, 1, stdout) != 1)
+ error("stdout");
+ exit(0);
+}
+
+/*
+ * Decrypt a blob.
+ */
+static void act_keyctl_pkey_decrypt(int argc, char *argv[])
+{
+ struct keyctl_pkey_query result;
+ key_serial_t key;
+ size_t in_len;
+ long out_len;
+ void *in, *out;
+ char info[4096];
+
+ if (argc < 4)
+ format();
+ pkey_parse_info(argv + 4, info);
+
+ key = get_key_id(argv[1]);
+ if (strcmp(argv[2], "0") != 0) {
+ fprintf(stderr, "Password passing is not yet supported\n");
+ exit(2);
+ }
+ in = read_file(argv[3], &in_len);
+
+ if (keyctl_pkey_query(key, info, &result) < 0)
+ error("keyctl_pkey_query");
+
+ out = malloc(result.max_enc_size);
+ if (!out)
+ error("malloc");
+
+ out_len = keyctl_pkey_decrypt(key, info,
+ in, in_len, out, result.max_enc_size);
+ if (out_len < 0)
+ error("keyctl_pkey_decrypt");
+
+ if (fwrite(out, out_len, 1, stdout) != 1)
+ error("stdout");
+ exit(0);
+}
+
+/*
+ * Create a signature
+ */
+static void act_keyctl_pkey_sign(int argc, char *argv[])
+{
+ struct keyctl_pkey_query result;
+ key_serial_t key;
+ size_t in_len;
+ long out_len;
+ void *in, *out;
+ char info[4096];
+
+ if (argc < 4)
+ format();
+ pkey_parse_info(argv + 4, info);
+
+ key = get_key_id(argv[1]);
+ if (strcmp(argv[2], "0") != 0) {
+ fprintf(stderr, "Password passing is not yet supported\n");
+ exit(2);
+ }
+ in = read_file(argv[3], &in_len);
+
+ if (keyctl_pkey_query(key, info, &result) < 0)
+ error("keyctl_pkey_query");
+
+ out = malloc(result.max_sig_size);
+ if (!out)
+ error("malloc");
+
+ out_len = keyctl_pkey_sign(key, info,
+ in, in_len, out, result.max_sig_size);
+ if (out_len < 0)
+ error("keyctl_pkey_sign");
+
+ if (fwrite(out, out_len, 1, stdout) != 1)
+ error("stdout");
+ exit(0);
+}
+
+/*
+ * Verify a signature.
+ */
+static void act_keyctl_pkey_verify(int argc, char *argv[])
+{
+ key_serial_t key;
+ size_t data_len, sig_len;
+ void *data, *sig;
+ char info[4096];
+
+ if (argc < 5)
+ format();
+ pkey_parse_info(argv + 5, info);
+
+ key = get_key_id(argv[1]);
+ if (strcmp(argv[2], "0") != 0) {
+ fprintf(stderr, "Password passing is not yet supported\n");
+ exit(2);
+ }
+ data = read_file(argv[3], &data_len);
+ sig = read_file(argv[4], &sig_len);
+
+ if (keyctl_pkey_verify(key, info,
+ data, data_len, sig, sig_len) < 0)
+ error("keyctl_pkey_verify");
+ exit(0);
+}
+
+/*
+ * Move a key between keyrings.
+ */
+static void act_keyctl_move(int argc, char *argv[])
+{
+ key_serial_t key, from_keyring, to_keyring;
+ unsigned int flags = KEYCTL_MOVE_EXCL;
+
+ if (argc > 4) {
+ if (strcmp("-f", argv[1]) == 0) {
+ flags &= ~KEYCTL_MOVE_EXCL;
+ argc--;
+ argv++;
+ }
+ }
+
+ if (argc != 4)
+ format();
+
+ key = get_key_id(argv[1]);
+ from_keyring = get_key_id(argv[2]);
+ to_keyring = get_key_id(argv[3]);
+
+ if (keyctl_move(key, from_keyring, to_keyring, flags) < 0)
+ error("keyctl_move");
+
+ exit(0);
+}
+
+struct capability_def {
+ const char *name; /* Textual name of capability */
+ unsigned int index; /* Index in capabilities array */
+ unsigned char mask; /* Mask on capabilities array element */
+};
+
+static const struct capability_def capabilities[] = {
+ { "capabilities", 0, KEYCTL_CAPS0_CAPABILITIES },
+ { "persistent_keyrings", 0, KEYCTL_CAPS0_PERSISTENT_KEYRINGS },
+ { "dh_compute", 0, KEYCTL_CAPS0_DIFFIE_HELLMAN },
+ { "public_key", 0, KEYCTL_CAPS0_PUBLIC_KEY },
+ { "big_key_type", 0, KEYCTL_CAPS0_BIG_KEY },
+ { "key_invalidate", 0, KEYCTL_CAPS0_INVALIDATE },
+ { "restrict_keyring", 0, KEYCTL_CAPS0_RESTRICT_KEYRING },
+ { "move_key", 0, KEYCTL_CAPS0_MOVE },
+ {}
+};
+
+/*
+ * Detect/list capabilities.
+ */
+static void act_keyctl_supports(int argc, char *argv[])
+{
+ const struct capability_def *p;
+ unsigned char caps[256];
+
+ if (argc < 1 || argc > 2)
+ format();
+
+ if (keyctl_capabilities(caps, sizeof(caps)) < 0)
+ error("keyctl_capabilities");
+
+ if (argc == 1) {
+ for (p = capabilities; p->name; p++)
+ printf("have_%s=%c\n",
+ p->name,
+ (caps[p->index] & p->mask) ? '1' : '0');
+ exit(0);
+ } else {
+ for (p = capabilities; p->name; p++)
+ if (strcmp(argv[1], p->name) == 0)
+ exit((caps[p->index] & p->mask) ? 0 : 1);
+ exit(3);
+ }
+}
+
+/*****************************************************************************/
+/*
+ * parse a key identifier
+ */
+static key_serial_t get_key_id(char *arg)
+{
+ key_serial_t id;
+ char *end;
+
+ /* handle a special keyring name */
+ if (arg[0] == '@') {
+ if (strcmp(arg, "@t" ) == 0) return KEY_SPEC_THREAD_KEYRING;
+ if (strcmp(arg, "@p" ) == 0) return KEY_SPEC_PROCESS_KEYRING;
+ if (strcmp(arg, "@s" ) == 0) return KEY_SPEC_SESSION_KEYRING;
+ if (strcmp(arg, "@u" ) == 0) return KEY_SPEC_USER_KEYRING;
+ if (strcmp(arg, "@us") == 0) return KEY_SPEC_USER_SESSION_KEYRING;
+ if (strcmp(arg, "@g" ) == 0) return KEY_SPEC_GROUP_KEYRING;
+ if (strcmp(arg, "@a" ) == 0) return KEY_SPEC_REQKEY_AUTH_KEY;
+
+ fprintf(stderr, "Unknown special key: '%s'\n", arg);
+ exit(2);
+ }
+
+ /* handle a lookup-by-name request "%<type>:<desc>", eg: "%keyring:_ses" */
+ if (arg[0] == '%') {
+ char *type;
+
+ arg++;
+ if (!*arg)
+ goto incorrect_key_by_name_spec;
+
+ if (*arg == ':') {
+ type = "keyring";
+ arg++;
+ } else {
+ type = arg;
+ arg = strchr(arg, ':');
+ if (!arg)
+ goto incorrect_key_by_name_spec;
+ *(arg++) = '\0';
+ }
+
+ if (!*arg)
+ goto incorrect_key_by_name_spec;
+
+ id = find_key_by_type_and_desc(type, arg, 0);
+ if (id == -1) {
+ fprintf(stderr, "Can't find '%s:%s'\n", type, arg);
+ exit(1);
+ }
+ return id;
+ }
+
+ /* handle a numeric key ID */
+ id = strtoul(arg, &end, 0);
+ if (*end) {
+ fprintf(stderr, "Unparsable key: '%s'\n", arg);
+ exit(2);
+ }
+
+ return id;
+
+incorrect_key_by_name_spec:
+ fprintf(stderr, "Incorrect key-by-name spec\n");
+ exit(2);
+
+} /* end get_key_id() */
+
+/*
+ * Read the contents of a file into a buffer and return it.
+ */
+static void *read_file(const char *name, size_t *_size)
+{
+ struct stat st;
+ ssize_t r;
+ void *p;
+ int fd;
+
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ error(name);
+ if (fstat(fd, &st) < 0)
+ error(name);
+
+ p = malloc(st.st_size);
+ if (!p)
+ error("malloc");
+ r = read(fd, p, st.st_size);
+ if (r == -1)
+ error(name);
+ if (r != st.st_size) {
+ fprintf(stderr, "%s: Short read\n", name);
+ exit(1);
+ }
+ if (close(fd) < 0)
+ error(name);
+
+ *_size = st.st_size;
+ return p;
+}
+
+/*****************************************************************************/
+/*
+ * recursively display a key/keyring tree
+ */
+static int dump_key_tree_aux(key_serial_t key, int depth, int more, int hex_key_IDs)
+{
+ static char dumpindent[64];
+ key_serial_t *pk;
+ key_perm_t perm;
+ size_t ringlen;
+ void *payload;
+ char *desc, type[255], pretty_mask[9];
+ int uid, gid, ret, n, dpos, rdepth, kcount = 0;
+
+ if (depth > 8 * 4)
+ return 0;
+
+ /* read the description */
+ ret = keyctl_describe_alloc(key, &desc);
+ if (ret < 0) {
+ printf("%d: key inaccessible (%m)\n", key);
+ return 0;
+ }
+
+ /* parse */
+ type[0] = 0;
+ uid = 0;
+ gid = 0;
+ perm = 0;
+
+ n = sscanf(desc, "%[^;];%d;%d;%x;%n",
+ type, &uid, &gid, &perm, &dpos);
+
+ if (n != 4) {
+ fprintf(stderr, "Unparseable description obtained for key %d\n", key);
+ exit(3);
+ }
+
+ /* and print */
+ calc_perms(pretty_mask, perm, uid, gid);
+
+ if (hex_key_IDs)
+ printf("0x%08x %s %5d %5d %s%s%s: %s\n",
+ key,
+ pretty_mask,
+ uid, gid,
+ dumpindent,
+ depth > 0 ? "\\_ " : "",
+ type, desc + dpos);
+ else
+ printf("%10d %s %5d %5d %s%s%s: %s\n",
+ key,
+ pretty_mask,
+ uid, gid,
+ dumpindent,
+ depth > 0 ? "\\_ " : "",
+ type, desc + dpos);
+
+ free(desc);
+
+ /* if it's a keyring then we're going to want to recursively
+ * display it if we can */
+ if (strcmp(type, "keyring") == 0) {
+ /* read its contents */
+ ret = keyctl_read_alloc(key, &payload);
+ if (ret < 0)
+ error("keyctl_read_alloc");
+
+ ringlen = ret;
+ kcount = ringlen / sizeof(key_serial_t);
+
+ /* walk the keyring */
+ pk = payload;
+ while (ringlen >= sizeof(key_serial_t)) {
+ key = *pk++;
+
+ /* recurse into next keyrings */
+ if (strcmp(type, "keyring") == 0) {
+ if (depth == 0) {
+ rdepth = depth;
+ dumpindent[rdepth++] = ' ';
+ dumpindent[rdepth] = 0;
+ }
+ else {
+ rdepth = depth;
+ dumpindent[rdepth++] = ' ';
+ dumpindent[rdepth++] = ' ';
+ dumpindent[rdepth++] = ' ';
+ dumpindent[rdepth++] = ' ';
+ dumpindent[rdepth] = 0;
+ }
+
+ if (more)
+ dumpindent[depth + 0] = '|';
+
+ kcount += dump_key_tree_aux(key,
+ rdepth,
+ ringlen - 4 >= sizeof(key_serial_t),
+ hex_key_IDs);
+ }
+
+ ringlen -= sizeof(key_serial_t);
+ }
+
+ free(payload);
+ }
+
+ return kcount;
+
+} /* end dump_key_tree_aux() */
+
+/*****************************************************************************/
+/*
+ * recursively list a keyring's contents
+ */
+static int dump_key_tree(key_serial_t keyring, const char *name, int hex_key_IDs)
+{
+ printf("%s\n", name);
+
+ keyring = keyctl_get_keyring_ID(keyring, 0);
+ if (keyring == -1)
+ error("Unable to dump key");
+
+ return dump_key_tree_aux(keyring, 0, 0, hex_key_IDs);
+
+} /* end dump_key_tree() */
diff --git a/keyutils.c b/keyutils.c
new file mode 100644
index 0000000..9c37256
--- /dev/null
+++ b/keyutils.c
@@ -0,0 +1,880 @@
+/* keyutils.c: key utility library
+ *
+ * Copyright (C) 2005,2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <asm/unistd.h>
+#include "keyutils.h"
+
+const char keyutils_version_string[] = PKGVERSION;
+const char keyutils_build_string[] = PKGBUILD;
+
+#ifdef NO_GLIBC_KEYERR
+static int error_inited;
+static void (*libc_perror)(const char *msg);
+static char *(*libc_strerror_r)(int errnum, char *buf, size_t n);
+//static int (*libc_xpg_strerror_r)(int errnum, char *buf, size_t n);
+#define RTLD_NEXT ((void *) -1L)
+#endif
+
+#define __weak __attribute__((weak))
+
+key_serial_t __weak add_key(const char *type,
+ const char *description,
+ const void *payload,
+ size_t plen,
+ key_serial_t ringid)
+{
+ return syscall(__NR_add_key,
+ type, description, payload, plen, ringid);
+}
+
+key_serial_t __weak request_key(const char *type,
+ const char *description,
+ const char * callout_info,
+ key_serial_t destringid)
+{
+ return syscall(__NR_request_key,
+ type, description, callout_info, destringid);
+}
+
+static inline long __keyctl(int cmd,
+ unsigned long arg2,
+ unsigned long arg3,
+ unsigned long arg4,
+ unsigned long arg5)
+{
+ return syscall(__NR_keyctl,
+ cmd, arg2, arg3, arg4, arg5);
+}
+
+long __weak keyctl(int cmd, ...)
+{
+ va_list va;
+ unsigned long arg2, arg3, arg4, arg5;
+
+ va_start(va, cmd);
+ arg2 = va_arg(va, unsigned long);
+ arg3 = va_arg(va, unsigned long);
+ arg4 = va_arg(va, unsigned long);
+ arg5 = va_arg(va, unsigned long);
+ va_end(va);
+
+ return __keyctl(cmd, arg2, arg3, arg4, arg5);
+}
+
+key_serial_t keyctl_get_keyring_ID(key_serial_t id, int create)
+{
+ return keyctl(KEYCTL_GET_KEYRING_ID, id, create);
+}
+
+key_serial_t keyctl_join_session_keyring(const char *name)
+{
+ return keyctl(KEYCTL_JOIN_SESSION_KEYRING, name);
+}
+
+long keyctl_update(key_serial_t id, const void *payload, size_t plen)
+{
+ return keyctl(KEYCTL_UPDATE, id, payload, plen);
+}
+
+long keyctl_revoke(key_serial_t id)
+{
+ return keyctl(KEYCTL_REVOKE, id);
+}
+
+long keyctl_chown(key_serial_t id, uid_t uid, gid_t gid)
+{
+ return keyctl(KEYCTL_CHOWN, id, uid, gid);
+}
+
+long keyctl_setperm(key_serial_t id, key_perm_t perm)
+{
+ return keyctl(KEYCTL_SETPERM, id, perm);
+}
+
+long keyctl_describe(key_serial_t id, char *buffer, size_t buflen)
+{
+ return keyctl(KEYCTL_DESCRIBE, id, buffer, buflen);
+}
+
+long keyctl_clear(key_serial_t ringid)
+{
+ return keyctl(KEYCTL_CLEAR, ringid);
+}
+
+long keyctl_link(key_serial_t id, key_serial_t ringid)
+{
+ return keyctl(KEYCTL_LINK, id, ringid);
+}
+
+long keyctl_unlink(key_serial_t id, key_serial_t ringid)
+{
+ return keyctl(KEYCTL_UNLINK, id, ringid);
+}
+
+long keyctl_search(key_serial_t ringid,
+ const char *type,
+ const char *description,
+ key_serial_t destringid)
+{
+ return keyctl(KEYCTL_SEARCH, ringid, type, description, destringid);
+}
+
+long keyctl_read(key_serial_t id, char *buffer, size_t buflen)
+{
+ return keyctl(KEYCTL_READ, id, buffer, buflen);
+}
+
+long keyctl_instantiate(key_serial_t id,
+ const void *payload,
+ size_t plen,
+ key_serial_t ringid)
+{
+ return keyctl(KEYCTL_INSTANTIATE, id, payload, plen, ringid);
+}
+
+long keyctl_negate(key_serial_t id, unsigned timeout, key_serial_t ringid)
+{
+ return keyctl(KEYCTL_NEGATE, id, timeout, ringid);
+}
+
+long keyctl_set_reqkey_keyring(int reqkey_defl)
+{
+ return keyctl(KEYCTL_SET_REQKEY_KEYRING, reqkey_defl);
+}
+
+long keyctl_set_timeout(key_serial_t id, unsigned timeout)
+{
+ return keyctl(KEYCTL_SET_TIMEOUT, id, timeout);
+}
+
+long keyctl_assume_authority(key_serial_t id)
+{
+ return keyctl(KEYCTL_ASSUME_AUTHORITY, id);
+}
+
+long keyctl_get_security(key_serial_t id, char *buffer, size_t buflen)
+{
+ return keyctl(KEYCTL_GET_SECURITY, id, buffer, buflen);
+}
+
+long keyctl_session_to_parent(void)
+{
+ return keyctl(KEYCTL_SESSION_TO_PARENT);
+}
+
+long keyctl_reject(key_serial_t id, unsigned timeout, unsigned error,
+ key_serial_t ringid)
+{
+ long ret = keyctl(KEYCTL_REJECT, id, timeout, error, ringid);
+
+ /* fall back to keyctl_negate() if this op is not supported by this
+ * kernel version */
+ if (ret == -1 && errno == EOPNOTSUPP)
+ return keyctl_negate(id, timeout, ringid);
+ return ret;
+}
+
+long keyctl_instantiate_iov(key_serial_t id,
+ const struct iovec *payload_iov,
+ unsigned ioc,
+ key_serial_t ringid)
+{
+ long ret = keyctl(KEYCTL_INSTANTIATE_IOV, id, payload_iov, ioc, ringid);
+
+ /* fall back to keyctl_instantiate() if this op is not supported by
+ * this kernel version */
+ if (ret == -1 && errno == EOPNOTSUPP) {
+ unsigned loop;
+ size_t bsize = 0, seg;
+ void *buf, *p;
+
+ if (!payload_iov || !ioc)
+ return keyctl_instantiate(id, NULL, 0, ringid);
+ for (loop = 0; loop < ioc; loop++)
+ bsize += payload_iov[loop].iov_len;
+ if (bsize == 0)
+ return keyctl_instantiate(id, NULL, 0, ringid);
+ p = buf = malloc(bsize);
+ if (!buf)
+ return -1;
+ for (loop = 0; loop < ioc; loop++) {
+ seg = payload_iov[loop].iov_len;
+ p = memcpy(p, payload_iov[loop].iov_base, seg) + seg;
+ }
+ ret = keyctl_instantiate(id, buf, bsize, ringid);
+ free(buf);
+ }
+ return ret;
+}
+
+long keyctl_invalidate(key_serial_t id)
+{
+ return keyctl(KEYCTL_INVALIDATE, id);
+}
+
+long keyctl_get_persistent(uid_t uid, key_serial_t id)
+{
+ return keyctl(KEYCTL_GET_PERSISTENT, uid, id);
+}
+
+long keyctl_dh_compute(key_serial_t priv, key_serial_t prime,
+ key_serial_t base, char *buffer, size_t buflen)
+{
+ struct keyctl_dh_params params = { .priv = priv,
+ .prime = prime,
+ .base = base };
+
+ return keyctl(KEYCTL_DH_COMPUTE, &params, buffer, buflen, 0);
+}
+
+long keyctl_dh_compute_kdf(key_serial_t private, key_serial_t prime,
+ key_serial_t base, char *hashname, char *otherinfo,
+ size_t otherinfolen, char *buffer, size_t buflen)
+{
+ struct keyctl_dh_params params = { .priv = private,
+ .prime = prime,
+ .base = base };
+ struct keyctl_kdf_params kdfparams = { .hashname = hashname,
+ .otherinfo = otherinfo,
+ .otherinfolen = otherinfolen };
+
+ return keyctl(KEYCTL_DH_COMPUTE, &params, buffer, buflen, &kdfparams);
+}
+
+long keyctl_restrict_keyring(key_serial_t keyring, const char *type,
+ const char *restriction)
+{
+ return keyctl(KEYCTL_RESTRICT_KEYRING, keyring, type, restriction);
+}
+
+long keyctl_pkey_query(key_serial_t key_id,
+ const char *info,
+ struct keyctl_pkey_query *result)
+{
+ return keyctl(KEYCTL_PKEY_QUERY, key_id, NULL, info, result);
+}
+
+long keyctl_pkey_encrypt(key_serial_t key_id,
+ const char *info,
+ const void *data, size_t data_len,
+ void *enc, size_t enc_len)
+{
+ struct keyctl_pkey_params params = {
+ .key_id = key_id,
+ .in_len = data_len,
+ .out_len = enc_len,
+ };
+
+ return keyctl(KEYCTL_PKEY_ENCRYPT, &params, info, data, enc);
+}
+
+long keyctl_pkey_decrypt(key_serial_t key_id,
+ const char *info,
+ const void *enc, size_t enc_len,
+ void *data, size_t data_len)
+{
+ struct keyctl_pkey_params params = {
+ .key_id = key_id,
+ .in_len = enc_len,
+ .out_len = data_len,
+ };
+
+ return keyctl(KEYCTL_PKEY_DECRYPT, &params, info, enc, data);
+}
+
+long keyctl_pkey_sign(key_serial_t key_id,
+ const char *info,
+ const void *data, size_t data_len,
+ void *sig, size_t sig_len)
+{
+ struct keyctl_pkey_params params = {
+ .key_id = key_id,
+ .in_len = data_len,
+ .out_len = sig_len,
+ };
+
+ return keyctl(KEYCTL_PKEY_SIGN, &params, info, data, sig);
+}
+
+long keyctl_pkey_verify(key_serial_t key_id,
+ const char *info,
+ const void *data, size_t data_len,
+ const void *sig, size_t sig_len)
+{
+ struct keyctl_pkey_params params = {
+ .key_id = key_id,
+ .in_len = data_len,
+ .in2_len = sig_len,
+ };
+
+ return keyctl(KEYCTL_PKEY_VERIFY, &params, info, data, sig);
+}
+
+long keyctl_move(key_serial_t id,
+ key_serial_t from_ringid,
+ key_serial_t to_ringid,
+ unsigned int flags)
+{
+ return keyctl(KEYCTL_MOVE, id, from_ringid, to_ringid, flags);
+}
+
+long keyctl_capabilities(unsigned char *buffer, size_t buflen)
+{
+ long n;
+
+ n = keyctl(KEYCTL_CAPABILITIES, buffer, buflen);
+ if (n != -1 || errno != EOPNOTSUPP)
+ return n;
+
+ /* Emulate the operation */
+ if (buflen > 0) {
+ memset(buffer, 0, buflen);
+
+ errno = 0;
+ keyctl_get_persistent(-1, 0);
+ if (errno != EOPNOTSUPP)
+ buffer[0] |= KEYCTL_CAPS0_PERSISTENT_KEYRINGS;
+
+ errno = 0;
+ keyctl_dh_compute(0, 0, 0, NULL, 0);
+ if (errno != EOPNOTSUPP)
+ buffer[0] |= KEYCTL_CAPS0_DIFFIE_HELLMAN;
+
+ errno = 0;
+ keyctl_pkey_query(0, NULL, NULL);
+ if (errno != EOPNOTSUPP)
+ buffer[0] |= KEYCTL_CAPS0_PUBLIC_KEY;
+
+ /* Can't emulate KEYCTL_CAPS0_BIG_KEY without a valid
+ * destination keyring.
+ */
+
+ errno = 0;
+ keyctl_invalidate(0);
+ if (errno != EOPNOTSUPP)
+ buffer[0] |= KEYCTL_CAPS0_INVALIDATE;
+
+ errno = 0;
+ keyctl_restrict_keyring(0, NULL, NULL);
+ if (errno != EOPNOTSUPP)
+ buffer[0] |= KEYCTL_CAPS0_RESTRICT_KEYRING;
+
+ errno = 0;
+ keyctl_move(0, 0, 0, 0);
+ if (errno != EOPNOTSUPP)
+ buffer[0] |= KEYCTL_CAPS0_MOVE;
+ }
+
+ return sizeof(unsigned char);
+}
+
+/*****************************************************************************/
+/*
+ * fetch key description into an allocated buffer
+ * - resulting string is NUL terminated
+ * - returns count not including NUL
+ */
+int keyctl_describe_alloc(key_serial_t id, char **_buffer)
+{
+ char *buf;
+ long buflen, ret;
+
+ ret = keyctl_describe(id, NULL, 0);
+ if (ret < 0)
+ return -1;
+
+ for (;;) {
+ buflen = ret;
+ buf = malloc(buflen);
+ if (!buf)
+ return -1;
+
+ ret = keyctl_describe(id, buf, buflen);
+ if (ret < 0) {
+ free(buf);
+ return -1;
+ }
+
+ if (buflen >= ret)
+ break;
+ free(buf);
+ }
+
+ *_buffer = buf;
+ return ret - 1;
+}
+
+/*****************************************************************************/
+/*
+ * fetch key contents into an allocated buffer
+ * - resulting buffer has an extra NUL added to the end
+ * - returns count (not including extraneous NUL)
+ */
+int keyctl_read_alloc(key_serial_t id, void **_buffer)
+{
+ char *buf;
+ long buflen, ret;
+
+ ret = keyctl_read(id, NULL, 0);
+ if (ret < 0)
+ return -1;
+
+ for (;;) {
+ buflen = ret;
+ buf = malloc(buflen + 1);
+ if (!buf)
+ return -1;
+
+ ret = keyctl_read(id, buf, buflen);
+ if (ret < 0) {
+ free(buf);
+ return -1;
+ }
+
+ if (buflen >= ret)
+ break;
+ free(buf);
+ }
+
+ buf[ret] = 0;
+ *_buffer = buf;
+ return ret;
+}
+
+/*****************************************************************************/
+/*
+ * fetch key security label into an allocated buffer
+ * - resulting string is NUL terminated
+ * - returns count not including NUL
+ */
+int keyctl_get_security_alloc(key_serial_t id, char **_buffer)
+{
+ char *buf;
+ long buflen, ret;
+
+ ret = keyctl_get_security(id, NULL, 0);
+ if (ret < 0)
+ return -1;
+
+ for (;;) {
+ buflen = ret;
+ buf = malloc(buflen);
+ if (!buf)
+ return -1;
+
+ ret = keyctl_get_security(id, buf, buflen);
+ if (ret < 0) {
+ free(buf);
+ return -1;
+ }
+
+ if (buflen >= ret)
+ break;
+ free(buf);
+ }
+
+ *_buffer = buf;
+ return ret - 1;
+}
+
+/*****************************************************************************/
+/*
+ * fetch DH computation results into an allocated buffer
+ * - resulting buffer has an extra NUL added to the end
+ * - returns count (not including extraneous NUL)
+ */
+int keyctl_dh_compute_alloc(key_serial_t priv, key_serial_t prime,
+ key_serial_t base, void **_buffer)
+{
+ char *buf;
+ long buflen, ret;
+
+ ret = keyctl_dh_compute(priv, prime, base, NULL, 0);
+ if (ret < 0)
+ return -1;
+
+ buflen = ret;
+ buf = malloc(buflen + 1);
+ if (!buf)
+ return -1;
+
+ ret = keyctl_dh_compute(priv, prime, base, buf, buflen);
+ if (ret < 0) {
+ free(buf);
+ return -1;
+ }
+
+ buf[ret] = 0;
+ *_buffer = buf;
+ return ret;
+}
+
+/*
+ * Depth-first recursively apply a function over a keyring tree
+ */
+static int recursive_key_scan_aux(key_serial_t parent, key_serial_t key,
+ int depth, recursive_key_scanner_t func,
+ void *data)
+{
+ key_serial_t *pk;
+ key_perm_t perm;
+ size_t ringlen;
+ void *ring;
+ char *desc, type[255];
+ int desc_len, uid, gid, ret, n, kcount = 0;
+
+ if (depth > 800)
+ return 0;
+
+ /* read the key description */
+ desc = NULL;
+ desc_len = keyctl_describe_alloc(key, &desc);
+ if (desc_len < 0)
+ goto do_this_key;
+
+ /* parse */
+ type[0] = 0;
+
+ n = sscanf(desc, "%[^;];%d;%d;%x;", type, &uid, &gid, &perm);
+ if (n != 4) {
+ free(desc);
+ desc = NULL;
+ errno = -EINVAL;
+ desc_len = -1;
+ goto do_this_key;
+ }
+
+ /* if it's a keyring then we're going to want to recursively search it
+ * if we can */
+ if (strcmp(type, "keyring") == 0) {
+ /* read the keyring's contents */
+ ret = keyctl_read_alloc(key, &ring);
+ if (ret < 0)
+ goto do_this_key;
+
+ ringlen = ret;
+
+ /* walk the keyring */
+ pk = ring;
+ for (ringlen = ret;
+ ringlen >= sizeof(key_serial_t);
+ ringlen -= sizeof(key_serial_t)
+ )
+ kcount += recursive_key_scan_aux(key, *pk++, depth + 1,
+ func, data);
+
+ free(ring);
+ }
+
+do_this_key:
+ kcount += func(parent, key, desc, desc_len, data);
+ free(desc);
+ return kcount;
+}
+
+/*
+ * Depth-first apply a function over a keyring tree
+ */
+int recursive_key_scan(key_serial_t key, recursive_key_scanner_t func, void *data)
+{
+ return recursive_key_scan_aux(0, key, 0, func, data);
+}
+
+/*
+ * Depth-first apply a function over session keyring tree
+ */
+int recursive_session_key_scan(recursive_key_scanner_t func, void *data)
+{
+ key_serial_t session =
+ keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 0);
+ if (session > 0)
+ return recursive_key_scan(session, func, data);
+ return 0;
+}
+
+/*
+ * Find a key by type and description
+ */
+key_serial_t find_key_by_type_and_desc(const char *type, const char *desc,
+ key_serial_t destringid)
+{
+ key_serial_t id, error;
+ FILE *f;
+ char buf[1024], typebuf[40], rdesc[1024], *kdesc, *cp;
+ int n, ndesc, dlen;
+
+ error = ENOKEY;
+
+ id = request_key(type, desc, NULL, destringid);
+ if (id >= 0 || errno == ENOMEM)
+ return id;
+ if (errno != ENOKEY)
+ error = errno;
+
+ dlen = strlen(desc);
+
+ f = fopen("/proc/keys", "r");
+ if (!f) {
+ fprintf(stderr, "libkeyutils: Can't open /proc/keys: %m\n");
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ cp = strchr(buf, '\n');
+ if (*cp)
+ *cp = '\0';
+
+ ndesc = 0;
+ n = sscanf(buf, "%x %*s %*u %*s %*x %*d %*d %s %n",
+ &id, typebuf, &ndesc);
+ if (n == 2 && ndesc > 0 && ndesc <= cp - buf) {
+ if (strcmp(typebuf, type) != 0)
+ continue;
+ kdesc = buf + ndesc;
+ if (memcmp(kdesc, desc, dlen) != 0)
+ continue;
+ if (kdesc[dlen] != ':' &&
+ kdesc[dlen] != '\0' &&
+ kdesc[dlen] != ' ')
+ continue;
+ kdesc[dlen] = '\0';
+
+ /* The key type appends extra stuff to the end of the
+ * description after a colon in /proc/keys. Colons,
+ * however, are allowed in descriptions, so we need to
+ * make a further check. */
+ n = keyctl_describe(id, rdesc, sizeof(rdesc) - 1);
+ if (n == -1) {
+ if (errno != ENOKEY)
+ error = errno;
+ if (errno == ENOMEM)
+ break;
+ }
+ if (n >= sizeof(rdesc) - 1)
+ continue;
+ rdesc[n] = '\0';
+
+ cp = strrchr(rdesc, ';');
+ if (!cp)
+ continue;
+ cp++;
+ if (strcmp(cp, desc) != 0)
+ continue;
+
+ fclose(f);
+
+ if (destringid &&
+ keyctl_link(id, destringid) == -1)
+ return -1;
+
+ return id;
+ }
+ }
+
+ fclose(f);
+ errno = error;
+ return -1;
+}
+
+#ifdef NO_GLIBC_KEYERR
+/*****************************************************************************/
+/*
+ * initialise error handling
+ */
+static void error_init(void)
+{
+ char *err;
+
+ error_inited = 1;
+
+ dlerror();
+
+ libc_perror = dlsym(RTLD_NEXT,"perror");
+ if (!libc_perror) {
+ fprintf(stderr, "Failed to look up next perror\n");
+ err = dlerror();
+ if (err)
+ fprintf(stderr, "%s\n", err);
+ abort();
+ }
+
+ //fprintf(stderr, "next perror at %p\n", libc_perror);
+
+ libc_strerror_r = dlsym(RTLD_NEXT,"strerror_r");
+ if (!libc_strerror_r) {
+ fprintf(stderr, "Failed to look up next strerror_r\n");
+ err = dlerror();
+ if (err)
+ fprintf(stderr, "%s\n", err);
+ abort();
+ }
+
+ //fprintf(stderr, "next strerror_r at %p\n", libc_strerror_r);
+
+#if 0
+ libc_xpg_strerror_r = dlsym(RTLD_NEXT,"xpg_strerror_r");
+ if (!libc_xpg_strerror_r) {
+ fprintf(stderr, "Failed to look up next xpg_strerror_r\n");
+ err = dlerror();
+ if (err)
+ fprintf(stderr, "%s\n", err);
+ abort();
+ }
+
+ //fprintf(stderr, "next xpg_strerror_r at %p\n", libc_xpg_strerror_r);
+#endif
+
+} /* end error_init() */
+
+/*****************************************************************************/
+/*
+ * overload glibc's strerror_r() with a version that knows about key errors
+ */
+char *strerror_r(int errnum, char *buf, size_t n)
+{
+ const char *errstr;
+ int len;
+
+ printf("hello\n");
+
+ if (!error_inited)
+ error_init();
+
+ switch (errnum) {
+ case ENOKEY:
+ errstr = "Requested key not available";
+ break;
+
+ case EKEYEXPIRED:
+ errstr = "Key has expired";
+ break;
+
+ case EKEYREVOKED:
+ errstr = "Key has been revoked";
+ break;
+
+ case EKEYREJECTED:
+ errstr = "Key was rejected by service";
+ break;
+
+ default:
+ return libc_strerror_r(errnum, buf, n);
+ }
+
+ len = strlen(errstr) + 1;
+ if (n > len) {
+ errno = ERANGE;
+ if (n > 0) {
+ memcpy(buf, errstr, n - 1);
+ buf[n - 1] = 0;
+ }
+ return NULL;
+ }
+ else {
+ memcpy(buf, errstr, len);
+ return buf;
+ }
+
+} /* end strerror_r() */
+
+#if 0
+/*****************************************************************************/
+/*
+ * overload glibc's strerror_r() with a version that knows about key errors
+ */
+int xpg_strerror_r(int errnum, char *buf, size_t n)
+{
+ const char *errstr;
+ int len;
+
+ if (!error_inited)
+ error_init();
+
+ switch (errnum) {
+ case ENOKEY:
+ errstr = "Requested key not available";
+ break;
+
+ case EKEYEXPIRED:
+ errstr = "Key has expired";
+ break;
+
+ case EKEYREVOKED:
+ errstr = "Key has been revoked";
+ break;
+
+ case EKEYREJECTED:
+ errstr = "Key was rejected by service";
+ break;
+
+ default:
+ return libc_xpg_strerror_r(errnum, buf, n);
+ }
+
+ len = strlen(errstr) + 1;
+ if (n > len) {
+ errno = ERANGE;
+ if (n > 0) {
+ memcpy(buf, errstr, n - 1);
+ buf[n - 1] = 0;
+ }
+ return -1;
+ }
+ else {
+ memcpy(buf, errstr, len);
+ return 0;
+ }
+
+} /* end xpg_strerror_r() */
+#endif
+
+/*****************************************************************************/
+/*
+ *
+ */
+void perror(const char *msg)
+{
+ if (!error_inited)
+ error_init();
+
+ switch (errno) {
+ case ENOKEY:
+ fprintf(stderr, "%s: Requested key not available\n", msg);
+ return;
+
+ case EKEYEXPIRED:
+ fprintf(stderr, "%s: Key has expired\n", msg);
+ return;
+
+ case EKEYREVOKED:
+ fprintf(stderr, "%s: Key has been revoked\n", msg);
+ return;
+
+ case EKEYREJECTED:
+ fprintf(stderr, "%s: Key was rejected by service\n", msg);
+ return;
+
+ default:
+ libc_perror(msg);
+ return;
+ }
+
+} /* end perror() */
+#endif
diff --git a/keyutils.h b/keyutils.h
new file mode 100644
index 0000000..887cbf2
--- /dev/null
+++ b/keyutils.h
@@ -0,0 +1,269 @@
+/* keyutils.h: key utility library interface
+ *
+ * Copyright (C) 2005,2011 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef KEYUTILS_H
+#define KEYUTILS_H
+
+#include <sys/types.h>
+#include <stdint.h>
+
+extern const char keyutils_version_string[];
+extern const char keyutils_build_string[];
+
+/* key serial number */
+typedef int32_t key_serial_t;
+
+/* special process keyring shortcut IDs */
+#define KEY_SPEC_THREAD_KEYRING -1 /* - key ID for thread-specific keyring */
+#define KEY_SPEC_PROCESS_KEYRING -2 /* - key ID for process-specific keyring */
+#define KEY_SPEC_SESSION_KEYRING -3 /* - key ID for session-specific keyring */
+#define KEY_SPEC_USER_KEYRING -4 /* - key ID for UID-specific keyring */
+#define KEY_SPEC_USER_SESSION_KEYRING -5 /* - key ID for UID-session keyring */
+#define KEY_SPEC_GROUP_KEYRING -6 /* - key ID for GID-specific keyring */
+#define KEY_SPEC_REQKEY_AUTH_KEY -7 /* - key ID for assumed request_key auth key */
+
+/* request-key default keyrings */
+#define KEY_REQKEY_DEFL_NO_CHANGE -1
+#define KEY_REQKEY_DEFL_DEFAULT 0
+#define KEY_REQKEY_DEFL_THREAD_KEYRING 1
+#define KEY_REQKEY_DEFL_PROCESS_KEYRING 2
+#define KEY_REQKEY_DEFL_SESSION_KEYRING 3
+#define KEY_REQKEY_DEFL_USER_KEYRING 4
+#define KEY_REQKEY_DEFL_USER_SESSION_KEYRING 5
+#define KEY_REQKEY_DEFL_GROUP_KEYRING 6
+
+/* key handle permissions mask */
+typedef uint32_t key_perm_t;
+
+#define KEY_POS_VIEW 0x01000000 /* possessor can view a key's attributes */
+#define KEY_POS_READ 0x02000000 /* possessor can read key payload / view keyring */
+#define KEY_POS_WRITE 0x04000000 /* possessor can update key payload / add link to keyring */
+#define KEY_POS_SEARCH 0x08000000 /* possessor can find a key in search / search a keyring */
+#define KEY_POS_LINK 0x10000000 /* possessor can create a link to a key/keyring */
+#define KEY_POS_SETATTR 0x20000000 /* possessor can set key attributes */
+#define KEY_POS_ALL 0x3f000000
+
+#define KEY_USR_VIEW 0x00010000 /* user permissions... */
+#define KEY_USR_READ 0x00020000
+#define KEY_USR_WRITE 0x00040000
+#define KEY_USR_SEARCH 0x00080000
+#define KEY_USR_LINK 0x00100000
+#define KEY_USR_SETATTR 0x00200000
+#define KEY_USR_ALL 0x003f0000
+
+#define KEY_GRP_VIEW 0x00000100 /* group permissions... */
+#define KEY_GRP_READ 0x00000200
+#define KEY_GRP_WRITE 0x00000400
+#define KEY_GRP_SEARCH 0x00000800
+#define KEY_GRP_LINK 0x00001000
+#define KEY_GRP_SETATTR 0x00002000
+#define KEY_GRP_ALL 0x00003f00
+
+#define KEY_OTH_VIEW 0x00000001 /* third party permissions... */
+#define KEY_OTH_READ 0x00000002
+#define KEY_OTH_WRITE 0x00000004
+#define KEY_OTH_SEARCH 0x00000008
+#define KEY_OTH_LINK 0x00000010
+#define KEY_OTH_SETATTR 0x00000020
+#define KEY_OTH_ALL 0x0000003f
+
+/* keyctl commands */
+#define KEYCTL_GET_KEYRING_ID 0 /* ask for a keyring's ID */
+#define KEYCTL_JOIN_SESSION_KEYRING 1 /* join or start named session keyring */
+#define KEYCTL_UPDATE 2 /* update a key */
+#define KEYCTL_REVOKE 3 /* revoke a key */
+#define KEYCTL_CHOWN 4 /* set ownership of a key */
+#define KEYCTL_SETPERM 5 /* set perms on a key */
+#define KEYCTL_DESCRIBE 6 /* describe a key */
+#define KEYCTL_CLEAR 7 /* clear contents of a keyring */
+#define KEYCTL_LINK 8 /* link a key into a keyring */
+#define KEYCTL_UNLINK 9 /* unlink a key from a keyring */
+#define KEYCTL_SEARCH 10 /* search for a key in a keyring */
+#define KEYCTL_READ 11 /* read a key or keyring's contents */
+#define KEYCTL_INSTANTIATE 12 /* instantiate a partially constructed key */
+#define KEYCTL_NEGATE 13 /* negate a partially constructed key */
+#define KEYCTL_SET_REQKEY_KEYRING 14 /* set default request-key keyring */
+#define KEYCTL_SET_TIMEOUT 15 /* set timeout on a key */
+#define KEYCTL_ASSUME_AUTHORITY 16 /* assume authority to instantiate key */
+#define KEYCTL_GET_SECURITY 17 /* get key security label */
+#define KEYCTL_SESSION_TO_PARENT 18 /* set my session keyring on my parent process */
+#define KEYCTL_REJECT 19 /* reject a partially constructed key */
+#define KEYCTL_INSTANTIATE_IOV 20 /* instantiate a partially constructed key */
+#define KEYCTL_INVALIDATE 21 /* invalidate a key */
+#define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */
+#define KEYCTL_DH_COMPUTE 23 /* Compute Diffie-Hellman values */
+#define KEYCTL_PKEY_QUERY 24 /* Query public key parameters */
+#define KEYCTL_PKEY_ENCRYPT 25 /* Encrypt a blob using a public key */
+#define KEYCTL_PKEY_DECRYPT 26 /* Decrypt a blob using a public key */
+#define KEYCTL_PKEY_SIGN 27 /* Create a public key signature */
+#define KEYCTL_PKEY_VERIFY 28 /* Verify a public key signature */
+#define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */
+#define KEYCTL_MOVE 30 /* Move keys between keyrings */
+#define KEYCTL_CAPABILITIES 31 /* Find capabilities of keyrings subsystem */
+
+/* keyctl structures */
+struct keyctl_dh_params {
+ key_serial_t priv;
+ key_serial_t prime;
+ key_serial_t base;
+};
+
+struct keyctl_kdf_params {
+ char *hashname;
+ char *otherinfo;
+ uint32_t otherinfolen;
+ uint32_t __spare[8];
+};
+
+#define KEYCTL_SUPPORTS_ENCRYPT 0x01
+#define KEYCTL_SUPPORTS_DECRYPT 0x02
+#define KEYCTL_SUPPORTS_SIGN 0x04
+#define KEYCTL_SUPPORTS_VERIFY 0x08
+
+struct keyctl_pkey_query {
+ unsigned int supported_ops; /* Which ops are supported */
+ unsigned int key_size; /* Size of the key in bits */
+ unsigned short max_data_size; /* Maximum size of raw data to sign in bytes */
+ unsigned short max_sig_size; /* Maximum size of signature in bytes */
+ unsigned short max_enc_size; /* Maximum size of encrypted blob in bytes */
+ unsigned short max_dec_size; /* Maximum size of decrypted blob in bytes */
+ unsigned int __spare[10];
+};
+
+struct keyctl_pkey_params {
+ key_serial_t key_id; /* Serial no. of public key to use */
+ unsigned int in_len; /* Input data size */
+ union {
+ unsigned int out_len; /* Output buffer size (encrypt/decrypt/sign) */
+ unsigned int in2_len; /* Second input data size (verify) */
+ };
+ unsigned int __spare[7];
+};
+
+#define KEYCTL_MOVE_EXCL 0x00000001 /* Do not displace from the to-keyring */
+
+/*
+ * Capabilities flags. The capabilities list is an array of 8-bit integers;
+ * each integer can carry up to 8 flags.
+ */
+#define KEYCTL_CAPS0_CAPABILITIES 0x01 /* KEYCTL_CAPABILITIES supported */
+#define KEYCTL_CAPS0_PERSISTENT_KEYRINGS 0x02 /* Persistent keyrings enabled */
+#define KEYCTL_CAPS0_DIFFIE_HELLMAN 0x04 /* Diffie-Hellman ops enabled */
+#define KEYCTL_CAPS0_PUBLIC_KEY 0x08 /* Public key ops enabled */
+#define KEYCTL_CAPS0_BIG_KEY 0x10 /* big_key-type enabled */
+#define KEYCTL_CAPS0_INVALIDATE 0x20 /* KEYCTL_INVALIDATE supported */
+#define KEYCTL_CAPS0_RESTRICT_KEYRING 0x40 /* KEYCTL_RESTRICT_KEYRING supported */
+#define KEYCTL_CAPS0_MOVE 0x80 /* KEYCTL_MOVE supported */
+
+/*
+ * syscall wrappers
+ */
+extern key_serial_t add_key(const char *type,
+ const char *description,
+ const void *payload,
+ size_t plen,
+ key_serial_t ringid);
+
+extern key_serial_t request_key(const char *type,
+ const char *description,
+ const char *callout_info,
+ key_serial_t destringid);
+
+extern long keyctl(int cmd, ...);
+
+/*
+ * keyctl function wrappers
+ */
+extern key_serial_t keyctl_get_keyring_ID(key_serial_t id, int create);
+extern key_serial_t keyctl_join_session_keyring(const char *name);
+extern long keyctl_update(key_serial_t id, const void *payload, size_t plen);
+extern long keyctl_revoke(key_serial_t id);
+extern long keyctl_chown(key_serial_t id, uid_t uid, gid_t gid);
+extern long keyctl_setperm(key_serial_t id, key_perm_t perm);
+extern long keyctl_describe(key_serial_t id, char *buffer, size_t buflen);
+extern long keyctl_clear(key_serial_t ringid);
+extern long keyctl_link(key_serial_t id, key_serial_t ringid);
+extern long keyctl_unlink(key_serial_t id, key_serial_t ringid);
+extern long keyctl_search(key_serial_t ringid,
+ const char *type,
+ const char *description,
+ key_serial_t destringid);
+extern long keyctl_read(key_serial_t id, char *buffer, size_t buflen);
+extern long keyctl_instantiate(key_serial_t id,
+ const void *payload,
+ size_t plen,
+ key_serial_t ringid);
+extern long keyctl_negate(key_serial_t id, unsigned timeout, key_serial_t ringid);
+extern long keyctl_set_reqkey_keyring(int reqkey_defl);
+extern long keyctl_set_timeout(key_serial_t key, unsigned timeout);
+extern long keyctl_assume_authority(key_serial_t key);
+extern long keyctl_get_security(key_serial_t key, char *buffer, size_t buflen);
+extern long keyctl_session_to_parent(void);
+extern long keyctl_reject(key_serial_t id, unsigned timeout, unsigned error,
+ key_serial_t ringid);
+struct iovec;
+extern long keyctl_instantiate_iov(key_serial_t id,
+ const struct iovec *payload_iov,
+ unsigned ioc,
+ key_serial_t ringid);
+extern long keyctl_invalidate(key_serial_t id);
+extern long keyctl_get_persistent(uid_t uid, key_serial_t id);
+extern long keyctl_dh_compute(key_serial_t priv, key_serial_t prime,
+ key_serial_t base, char *buffer, size_t buflen);
+extern long keyctl_dh_compute_kdf(key_serial_t private, key_serial_t prime,
+ key_serial_t base, char *hashname,
+ char *otherinfo, size_t otherinfolen,
+ char *buffer, size_t buflen);
+extern long keyctl_restrict_keyring(key_serial_t keyring, const char *type,
+ const char *restriction);
+
+extern long keyctl_pkey_query(key_serial_t key_id,
+ const char *info,
+ struct keyctl_pkey_query *result);
+extern long keyctl_pkey_encrypt(key_serial_t key_id,
+ const char *info,
+ const void *data, size_t data_len,
+ void *enc, size_t enc_len);
+extern long keyctl_pkey_decrypt(key_serial_t key_id,
+ const char *info,
+ const void *enc, size_t enc_len,
+ void *data, size_t data_len);
+extern long keyctl_pkey_sign(key_serial_t key_id,
+ const char *info,
+ const void *data, size_t data_len,
+ void *sig, size_t sig_len);
+extern long keyctl_pkey_verify(key_serial_t key_id,
+ const char *info,
+ const void *data, size_t data_len,
+ const void *sig, size_t sig_len);
+extern long keyctl_move(key_serial_t id,
+ key_serial_t from_ringid,
+ key_serial_t to_ringid,
+ unsigned int flags);
+extern long keyctl_capabilities(unsigned char *buffer, size_t buflen);
+
+/*
+ * utilities
+ */
+extern int keyctl_describe_alloc(key_serial_t id, char **_buffer);
+extern int keyctl_read_alloc(key_serial_t id, void **_buffer);
+extern int keyctl_get_security_alloc(key_serial_t id, char **_buffer);
+extern int keyctl_dh_compute_alloc(key_serial_t priv, key_serial_t prime,
+ key_serial_t base, void **_buffer);
+
+typedef int (*recursive_key_scanner_t)(key_serial_t parent, key_serial_t key,
+ char *desc, int desc_len, void *data);
+extern int recursive_key_scan(key_serial_t key, recursive_key_scanner_t func, void *data);
+extern int recursive_session_key_scan(recursive_key_scanner_t func, void *data);
+extern key_serial_t find_key_by_type_and_desc(const char *type, const char *desc,
+ key_serial_t destringid);
+
+#endif /* KEYUTILS_H */
diff --git a/keyutils.spec b/keyutils.spec
new file mode 100644
index 0000000..6bf7df3
--- /dev/null
+++ b/keyutils.spec
@@ -0,0 +1,302 @@
+%define vermajor 1
+%define verminor 6.1
+%define version %{vermajor}.%{verminor}
+%define libapivermajor 1
+%define libapiversion %{libapivermajor}.9
+
+# % define buildid .local
+
+Name: keyutils
+Version: %{version}
+Release: 1%{?buildid}%{?dist}
+Summary: Linux Key Management Utilities
+License: GPLv2+ and LGPLv2+
+Url: http://people.redhat.com/~dhowells/keyutils/
+
+Source0: http://people.redhat.com/~dhowells/keyutils/keyutils-%{version}.tar.bz2
+
+BuildRequires: gcc
+BuildRequires: glibc-kernheaders >= 2.4-9.1.92
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+
+%description
+Utilities to control the kernel key management facility and to provide
+a mechanism by which the kernel call back to user space to get a key
+instantiated.
+
+%package libs
+Summary: Key utilities library
+
+%description libs
+This package provides a wrapper library for the key management facility system
+calls.
+
+%package libs-devel
+Summary: Development package for building Linux key management utilities
+Requires: %{name}-libs%{?_isa} = %{version}-%{release}
+
+%description libs-devel
+This package provides headers and libraries for building key utilities.
+
+%prep
+%setup -q
+
+%define datadir %{_datarootdir}/keyutils
+
+%build
+make \
+ NO_ARLIB=1 \
+ ETCDIR=%{_sysconfdir} \
+ LIBDIR=%{_libdir} \
+ USRLIBDIR=%{_libdir} \
+ BINDIR=%{_bindir} \
+ SBINDIR=%{_sbindir} \
+ MANDIR=%{_mandir} \
+ INCLUDEDIR=%{_includedir} \
+ SHAREDIR=%{datadir} \
+ RELEASE=.%{release} \
+ NO_GLIBC_KEYERR=1 \
+ CFLAGS="-Wall $RPM_OPT_FLAGS -Werror" \
+ LDFLAGS="%{?__global_ldflags}"
+
+%install
+make \
+ NO_ARLIB=1 \
+ DESTDIR=$RPM_BUILD_ROOT \
+ ETCDIR=%{_sysconfdir} \
+ LIBDIR=%{_libdir} \
+ USRLIBDIR=%{_libdir} \
+ BINDIR=%{_bindir} \
+ SBINDIR=%{_sbindir} \
+ MANDIR=%{_mandir} \
+ INCLUDEDIR=%{_includedir} \
+ SHAREDIR=%{datadir} \
+ install
+
+%ldconfig_scriptlets libs
+
+%files
+%doc README
+%license LICENCE.GPL
+%{_sbindir}/*
+%{_bindir}/*
+%{datadir}
+%{_mandir}/man1/*
+%{_mandir}/man5/*
+%{_mandir}/man8/*
+%config(noreplace) %{_sysconfdir}/*
+
+%files libs
+%license LICENCE.LGPL
+%{_mandir}/man7/*
+%{_libdir}/libkeyutils.so.%{libapiversion}
+%{_libdir}/libkeyutils.so.%{libapivermajor}
+
+%files libs-devel
+%{_libdir}/libkeyutils.so
+%{_includedir}/*
+%{_mandir}/man3/*
+%{_libdir}/pkgconfig/libkeyutils.pc
+
+%changelog
+* Fri Aug 2 2019 David Howells <dhowells@redhat.com> - 1.6.1-1
+- Add support for keyctl_move().
+- Add support for keyctl_capabilities().
+- Make key=val list optional for various public-key ops.
+- Fix system call signature for KEYCTL_PKEY_QUERY.
+- Fix 'keyctl pkey_query' argument passing.
+- Use keyctl_read_alloc() in dump_key_tree_aux().
+- Various manual page fixes.
+
+* Tue Nov 13 2018 David Howells <dhowells@redhat.com> - 1.6-1
+- Apply various specfile cleanups from Fedora.
+- request-key: Provide a command line option to suppress helper execution.
+- request-key: Find least-wildcard match rather than first match.
+- Remove the dependency on MIT Kerberos.
+- Fix some error messages
+- keyctl_dh_compute.3: Suggest /proc/crypto for list of available hashes.
+- Fix doc and comment typos.
+- Add public key ops for encrypt, decrypt, sign and verify (needs linux-4.20).
+- Add pkg-config support for finding libkeyutils.
+
+* Wed May 9 2018 David Howells <dhowells@redhat.com> - 1.5.11-1
+- Add keyring restriction support.
+- Add KDF support to the Diffie-Helman function.
+- DNS: Add support for AFS config files and SRV records
+
+* Wed Mar 15 2017 David Howells <dhowells@redhat.com> - 1.5.10-1
+- Include sys/types.h in keyutils.h.
+- The dns resolver needs limits.h.
+- Overhaul of all manual pages.
+- Some manual pages moved to Linux man-pages project.
+- Add Diffie-Helman keyctl function.
+
+* Fri Feb 21 2014 David Howells <dhowells@redhat.com> - 1.5.9-1
+- Add manpages for get_persistent.
+- Fix memory leaks in keyctl_describe/read/get_security_alloc().
+- Use keyctl_describe_alloc in dump_key_tree_aux rather than open coding it.
+- Exit rather than returning from act_xxx() functions.
+- Fix memory leak in dump_key_tree_aux.
+- Only get the groups list if we need it.
+- Don't trust sscanf's %%n argument.
+- Use the correct path macros in the specfile.
+- Avoid use realloc when the memory has no content.
+- Fix a bunch of issues in key.dns_resolver.
+- Fix command table searching in keyctl utility.
+- Fix a typo in the permissions mask constants.
+- Improve the keyctl_read manpage.
+- Add man7 pages describing various keyrings concepts.
+
+* Fri Oct 4 2013 David Howells <dhowells@redhat.com> - 1.5.8-1
+- New lib symbols should go in a new library minor version.
+
+* Wed Oct 2 2013 David Howells <dhowells@redhat.com> - 1.5.7-1
+- Provide a utility function to find a key by type and name.
+- Allow keyctl commands to take a type+name arg instead of a key-id arg.
+- Add per-UID get_persistent keyring function.
+
+* Thu Aug 29 2013 David Howells <dhowells@redhat.com> - 1.5.6-1
+- Fix the request-key.conf.5 manpage.
+- Fix the max depth of key tree dump (keyctl show).
+- The input buffer size for keyctl padd and pinstantiate should be larger.
+- Add keyctl_invalidate.3 manpage.
+
+* Wed Nov 30 2011 David Howells <dhowells@redhat.com> - 1.5.5-1
+- Fix a Makefile error.
+
+* Wed Nov 30 2011 David Howells <dhowells@redhat.com> - 1.5.4-1
+- Fix the keyctl padd command and similar to handle binary input.
+- Make keyctl show able to take a keyring to dump.
+- Make keyctl show able to take a flag to request hex key IDs.
+- Make keyctl show print the real ID of the root keyring.
+
+* Tue Nov 15 2011 David Howells <dhowells@redhat.com>
+- Allow /sbin/request-key to have multiple config files.
+
+* Wed Aug 31 2011 David Howells <dhowells@redhat.com>
+- Adjust the manual page for 'keyctl unlink' to show keyring is optional.
+- Add --version support for the keyutils version and build date.
+
+* Thu Aug 11 2011 David Howells <dhowells@redhat.com> - 1.5.3-1
+- Make the keyutils rpm depend on the same keyutils-libs rpm version.
+
+* Tue Jul 26 2011 David Howells <dhowells@redhat.com> - 1.5.2-1
+- Use correct format spec for printing pointer subtraction results.
+
+* Tue Jul 19 2011 David Howells <dhowells@redhat.com> - 1.5.1-1
+- Fix unread variables.
+- Licence file update.
+
+* Thu Mar 10 2011 David Howells <dhowells@redhat.com> - 1.5-1
+- Disable RPATH setting in Makefile.
+- Add -I. to build to get this keyutils.h.
+- Make CFLAGS override on make command line work right.
+- Make specfile UTF-8.
+- Support KEYCTL_REJECT.
+- Support KEYCTL_INSTANTIATE_IOV.
+- Add AFSDB DNS lookup program from Wang Lei.
+- Generalise DNS lookup program.
+- Add recursive scan utility function.
+- Add bad key reap command to keyctl.
+- Add multi-unlink variant to keyctl unlink command.
+- Add multi key purge command to keyctl.
+- Handle multi-line commands in keyctl command table.
+- Move the package to version to 1.5.
+
+* Tue Mar 1 2011 David Howells <dhowells@redhat.com> - 1.4-4
+- Make build guess at default libdirs and word size.
+- Make program build depend on library in Makefile.
+- Don't include $(DESTDIR) in MAN* macros.
+- Remove NO_GLIBC_KEYSYS as it is obsolete.
+- Have Makefile extract version info from specfile and version script.
+- Provide RPM build rule in Makefile.
+- Provide distclean rule in Makefile.
+
+* Fri Dec 17 2010 Diego Elio Pettenò <flameeyes@hosting.flameeyes.eu> - 1.4-3
+- Fix local linking and RPATH.
+
+* Thu Jun 10 2010 David Howells <dhowells@redhat.com> - 1.4-2
+- Fix prototypes in manual pages (some char* should be void*).
+- Rename the keyctl_security.3 manpage to keyctl_get_security.3.
+
+* Fri Mar 19 2010 David Howells <dhowells@redhat.com> - 1.4-1
+- Fix the library naming wrt the version.
+- Move the package to version to 1.4.
+
+* Fri Mar 19 2010 David Howells <dhowells@redhat.com> - 1.3-3
+- Fix spelling mistakes in manpages.
+- Add an index manpage for all the keyctl functions.
+
+* Thu Mar 11 2010 David Howells <dhowells@redhat.com> - 1.3-2
+- Fix rpmlint warnings.
+
+* Fri Feb 26 2010 David Howells <dhowells@redhat.com> - 1.3-1
+- Fix compiler warnings in request-key.
+- Expose the kernel function to get a key's security context.
+- Expose the kernel function to set a processes keyring onto its parent.
+- Move libkeyutils library version to 1.3.
+
+* Tue Aug 22 2006 David Howells <dhowells@redhat.com> - 1.2-1
+- Remove syscall manual pages (section 2) to man-pages package [BZ 203582]
+- Don't write to serial port in debugging script
+
+* Mon Jun 5 2006 David Howells <dhowells@redhat.com> - 1.1-4
+- Call ldconfig during (un)installation.
+
+* Fri May 5 2006 David Howells <dhowells@redhat.com> - 1.1-3
+- Don't include the release number in the shared library filename
+- Don't build static library
+
+* Fri May 5 2006 David Howells <dhowells@redhat.com> - 1.1-2
+- More bug fixes from Fedora reviewer.
+
+* Thu May 4 2006 David Howells <dhowells@redhat.com> - 1.1-1
+- Fix rpmlint errors
+
+* Mon Dec 5 2005 David Howells <dhowells@redhat.com> - 1.0-2
+- Add build dependency on glibc-kernheaders with key management syscall numbers
+
+* Tue Nov 29 2005 David Howells <dhowells@redhat.com> - 1.0-1
+- Add data pipe-in facility for keyctl request2
+
+* Mon Nov 28 2005 David Howells <dhowells@redhat.com> - 1.0-1
+- Rename library and header file "keyutil" -> "keyutils" for consistency
+- Fix shared library version naming to same way as glibc.
+- Add versioning for shared library symbols
+- Create new keyutils-libs package and install library and main symlink there
+- Install base library symlink in /usr/lib and place in devel package
+- Added a keyutils archive library
+- Shorten displayed key permissions list to just those we actually have
+
+* Thu Nov 24 2005 David Howells <dhowells@redhat.com> - 0.3-4
+- Add data pipe-in facilities for keyctl add, update and instantiate
+
+* Fri Nov 18 2005 David Howells <dhowells@redhat.com> - 0.3-3
+- Added stdint.h inclusion in keyutils.h
+- Made request-key.c use request_key() rather than keyctl_search()
+- Added piping facility to request-key
+
+* Thu Nov 17 2005 David Howells <dhowells@redhat.com> - 0.3-2
+- Added timeout keyctl option
+- request_key auth keys must now be assumed
+- Fix keyctl argument ordering for debug negate line in request-key.conf
+
+* Thu Jul 28 2005 David Howells <dhowells@redhat.com> - 0.3-1
+- Must invoke initialisation from perror() override in libkeyutils
+- Minor UI changes
+
+* Wed Jul 20 2005 David Howells <dhowells@redhat.com> - 0.2-2
+- Bump version to permit building in main repositories.
+
+* Tue Jul 12 2005 David Howells <dhowells@redhat.com> - 0.2-1
+- Don't attempt to define the error codes in the header file.
+- Pass the release ID through to the makefile to affect the shared library name.
+
+* Tue Jul 12 2005 David Howells <dhowells@redhat.com> - 0.1-3
+- Build in the perror() override to get the key error strings displayed.
+
+* Tue Jul 12 2005 David Howells <dhowells@redhat.com> - 0.1-2
+- Need a defattr directive after each files directive.
+
+* Tue Jul 12 2005 David Howells <dhowells@redhat.com> - 0.1-1
+- Package creation.
diff --git a/libkeyutils.pc.in b/libkeyutils.pc.in
new file mode 100644
index 0000000..4c06267
--- /dev/null
+++ b/libkeyutils.pc.in
@@ -0,0 +1,8 @@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libkeyutils
+Description: keyutils library
+Version: @VERSION@
+Cflags: -I${includedir}
+Libs: -L${libdir} -lkeyutils
diff --git a/man/asymmetric-key.7 b/man/asymmetric-key.7
new file mode 100644
index 0000000..5fc78cb
--- /dev/null
+++ b/man/asymmetric-key.7
@@ -0,0 +1,232 @@
+.\"
+.\" Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" This program is free software; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public Licence
+.\" as published by the Free Software Foundation; either version
+.\" 2 of the Licence, or (at your option) any later version.
+.\"
+.TH ASYMMETRIC-KEY 7 "8 Nov 2018" Linux "Asymmetric Kernel Key Type"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+asymmetric \- Kernel key type for holding asymmetric keys
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH OVERVIEW
+.PP
+A kernel key of
+.B asymmetric
+type acts as a handle to an asymmetric key as used for public-key
+cryptography. The key material itself may be held inside the kernel or it may
+be held in hardware with operations being offloaded. This prevents direct
+user access to the cryptographic material.
+.PP
+Keys may be any asymmetric type (RSA, ECDSA, ...) and may have both private and
+public components present or just the public component.
+.PP
+Asymmetric keys can be made use of by both the kernel and userspace. The
+kernel can make use of them for module signature verification and kexec image
+verification for example. Userspace is provided with a set of
+.BR keyctl ( KEYCTL_PKEY_* )
+calls for querying and using the key. These are wrapped by
+.B libkeyutils
+as functions named
+.BR keyctl_pkey_*() .
+.PP
+An asymmetric-type key can be loaded by the keyctl utility using a command
+line like:
+.PP
+.in +4n
+.EX
+openssl x509 -in key.x509 -outform DER |
+keyctl padd asymmetric foo @s
+.EE
+.in
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.PP
+The asymmetric-type key can be viewed as a container that comprises of a
+number of components:
+.TP
+Parsers
+The asymmetric key parsers attempt to identify the content of the payload blob
+and extract useful data from it with which to instantiate the key. The parser
+is only used when adding, instantiating or updating a key and isn't thereafter
+associated with the key.
+.IP
+Available parsers include ones that can deal with
+.RB "DER-encoded " X.509 ", DER-encoded " PKCS#8 " and DER-encoded " TPM "-wrapped blobs."
+.TP
+Public and private keys
+These are the cryptographic components of the key pair. The public half
+should always be available, but the private half might not be. What
+operations are available can be queried, as can the size of the key. The key
+material may or may not actually reside in the kernel.
+.TP
+Identifiers
+In addition to the normal key description (which can be generated by the
+parser), a number of supplementary identifiers may be available that can be
+searched for. These may be obtained, for example, by hashing the public key
+material or from the subjectKeyIdentifier in an X.509 certificate.
+.IP
+Identifier-based searches are selected by passing as the description to
+.BR keyctl_search ()
+a string constructed of hex characters prefixed with either "id:" or "ex:".
+The "id:" prefix indicates that a partial tail match is permissible whereas
+"ex:" requires an exact match on the full string. The hex characters indicate
+the data to match.
+.TP
+Subtype
+This is the driver inside the kernel that accesses the key material and
+performs operations on it. It might be entirely software-based or it may
+offload the operations to a hardware key store, such as a
+.BR TPM .
+.PP
+Note that expiry times from the payload are ignored as these patches may be
+used during boot before the system clock is set.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH PARSERS
+.PP
+The asymmetric key parsers can handle keys in a number of forms:
+.TP
+\fBX.509\fP
+DER-encoded X.509 certificates can be accepted. Two identifiers are
+constructed: one from from the certificate issuer and serial number and the
+other from the subjectKeyIdentifier, if present. If left blank, the key
+description will be filled in from the subject field plus either the
+subjectKeyIdentifier or the serialNumber. Only the public key is filled in
+and only the encrypt and verify operations are supported.
+.IP
+The signature on the X.509 certificate may be checked by the keyring it is
+being added to and it may also be rejected if the key is blacklisted.
+.TP
+\fBPKCS#8\fP
+Unencrypted DER-encoded PKCS#8 key data containers can be accepted. Currently
+no identifiers are constructed. The private key and the public key are loaded
+from the PKCS#8 blobs. Encrypted PKCS#8 is not currently supported.
+.TP
+\fBTPM-Wrapped keys\fP
+DER-encoded TPM-wrapped TSS key blobs can be accepted. Currently no
+identifiers are constructed. The public key is extracted from the blob but
+the private key is expected to be resident in the TPM. Encryption and
+signature verification is done in software, but decryption and signing are
+offloaded to the TPM so as not to expose the private key.
+.IP
+This parser only supports TPM-1.2 wrappings and enc=pkcs1 encoding type. It
+also uses a hard-coded null SRK password; password-protected SRKs are not yet
+supported.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH USERSPACE API
+.PP
+In addition to the standard keyutils library functions, such as
+.IR keyctl_update (),
+there are five calls specific to the asymmetric key type (though they are open
+to being used by other key types also):
+.PP
+.RS
+\fIkeyctl_pkey_query\fP()
+.br
+\fIkeyctl_pkey_encrypt\fP()
+.br
+\fIkeyctl_pkey_decrypt\fP()
+.br
+\fIkeyctl_pkey_sign\fP()
+.br
+\fIkeyctl_pkey_verify\fP()
+.RE
+.PP
+The query function can be used to retrieve information about an asymmetric key,
+such as the key size, the amount of space required by buffers for the other
+operations and which operations are actually supported.
+.PP
+The other operations form two pairs: encrypt/decrypt and create/verify
+signature. Not all of these operations will necessarily be available;
+typically, encrypt and verify only require the public key to be available
+whereas decrypt and sign require the private key as well.
+.PP
+All of these operations take an information string parameter that supplies
+additional information such as encoding type/form and the password(s) needed to
+unlock/unwrap the key. This takes the form of a comma-separated list of
+"key[=value]" pairs, the exact set of which depends on the subtype driver used
+by a particular key.
+.PP
+Available parameters include:
+.TP
+enc=<type>
+The encoding type for use in an encrypted blob or a signature. An example
+might be "enc=pkcs1".
+.TP
+hash=<name>
+The name of the hash algorithm that was used to digest the data to be signed.
+Note that this is only used to construct any encoding that is used in a
+signature. The data to be signed or verified must have been parsed by the
+caller and the hash passed to \fIkeyctl_pkey_sign\fP() or
+\fIkeyctl_pkey_verify\fP() beforehand. An example might be "hash=sha256".
+.PP
+Note that not all parameters are used by all subtypes.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RESTRICTED KEYRINGS
+.PP
+An additional keyutils function,
+.IR keyctl_restrict_keyring (),
+can be used to gate a keyring so that a new key can only be added to the
+affected keyring if (a) it's an asymmetric key, (b) it's validly signed by a
+key in some appropriate keyring and (c) it's not blacklisted.
+.PP
+.in +4n
+.EX
+ keyctl_restrict_keyring(keyring, "asymmetric",
+ "key_or_keyring:<signing-key>[:chain]");
+.EE
+.in
+.PP
+Where \fI<signing-key>\fP is the ID of a key or a ring of keys that act as the
+authority to permit a new key to be added to the keyring. The \fIchain\fP flag
+indicates that keys that have been added to the keyring may also be used to
+verify new keys. Authorising keys must themselves be asymmetric-type keys that
+can be used to do a signature verification on the key being added.
+.PP
+Note that there are various system keyrings visible to the root user that may
+permit additional keys to be added. These are typically gated by keys that
+already exist, preventing unauthorised keys from being used for such things as
+module verification.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH BLACKLISTING
+.PP
+When the attempt is made to add a key to the kernel, a hash of the public key
+is checked against the
+.BR blacklist.
+This is a system keyring named
+.B .blacklist
+and contains keys of type
+.BR blacklist .
+If the blacklist contains a key whose description matches the hash of the new
+key, that new key will be rejected with error
+.BR EKEYREJECTED .
+.PP
+The blacklist keyring may be loaded from multiple sources, including a list
+compiled into the kernel and the UEFI
+.B dbx
+variable. Further hashes may also be blacklisted by the administrator. Note
+that blacklisting is not retroactive, so an asymmetric key that is already on
+the system cannot be blacklisted by adding a matching blacklist entry later.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH VERSIONS
+The
+.B asymmetric
+key type first appeared in v3.7 of the Linux kernel, the
+.B restriction
+function in v4.11 and the
+.B public key operations
+in v4.20.
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (3),
+.BR keyctl_pkey_encrypt (3),
+.BR keyctl_pkey_query (3),
+.BR keyctl_pkey_sign (3),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/find_key_by_type_and_name.3 b/man/find_key_by_type_and_name.3
new file mode 100644
index 0000000..2fbd21e
--- /dev/null
+++ b/man/find_key_by_type_and_name.3
@@ -0,0 +1,65 @@
+.\"
+.\" Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" This program is free software; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public Licence
+.\" as published by the Free Software Foundation; either version
+.\" 2 of the Licence, or (at your option) any later version.
+.\"
+.TH FIND_KEY_BY_TYPE_AND_NAME 3 "10 Sep 2013" Linux "Linux Key Utility Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+find_key_by_type_and_name \- find a key by type and name
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "key_serial_t find_key_by_type_and_name(const char *" type ,
+.BI " const char *" description ", key_serial_t " destringid ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR find_key_by_type_and_name ()
+searches for a key with the given
+.I type
+and exact
+.IR description ,
+firstly in the thread, process and session keyrings to which a process is
+subscribed and secondly in
+.IR /proc/keys .
+.P
+If a key is found, and
+.I destringid
+is not 0 and specifies a keyring, then the found key will be linked into it.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+The function returns the ID of the key if a key was found or \-1 otherwise.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+No key was found or the keyring specified is invalid.
+.TP
+.B EKEYEXPIRED
+The key or keyring have expired.
+.TP
+.B EKEYREVOKED
+The key or keyring have been revoked.
+.TP
+.B EACCES
+The key is not accessible or keyring exists, but is not
+.B writable
+by the calling process.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7)
diff --git a/man/key.dns_resolver.8 b/man/key.dns_resolver.8
new file mode 100644
index 0000000..e1882e0
--- /dev/null
+++ b/man/key.dns_resolver.8
@@ -0,0 +1,34 @@
+.\"
+.\" Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEY.DNS_RESOLVER 8 "04 Mar 2011" Linux "Linux Key Management Utilities"
+.SH NAME
+key.dns_resolver \- upcall for request\-key to handle dns_resolver keys
+.SH SYNOPSIS
+\fB/sbin/key.dns_resolver \fR<key>
+.br
+\fB/sbin/key.dns_resolver \fR\-D [\-v] [\-v] <keydesc> <calloutinfo>
+.SH DESCRIPTION
+This program is invoked by request\-key on behalf of the kernel when kernel
+services (such as NFS, CIFS and AFS) want to perform a hostname lookup and the
+kernel does not have the key cached. It is not ordinarily intended to be
+called directly.
+.P
+It can be called in debugging mode to test its functionality by passing a
+\fB\-D\fR flag on the command line. For this to work, the key description and
+the callout information must be supplied. Verbosity can be increased by
+supplying one or more \fB\-v\fR flags.
+.SH ERRORS
+All errors will be logged to the syslog.
+.SH SEE ALSO
+.ad l
+.nh
+.BR request\-key.conf (5),
+.BR keyrings (7),
+.BR request\-key (8)
diff --git a/man/keyctl.1 b/man/keyctl.1
new file mode 100644
index 0000000..1c8f569
--- /dev/null
+++ b/man/keyctl.1
@@ -0,0 +1,908 @@
+.\"
+.\" Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL 1 "20 Feb 2014" Linux "Linux Key Management Utilities"
+.SH NAME
+keyctl \- key management facility control
+.SH SYNOPSIS
+\fBkeyctl\fR \-\-version
+.br
+\fBkeyctl\fR supports [<cap>]
+.br
+\fBkeyctl\fR show [\-x] [<keyring>]
+.br
+\fBkeyctl\fR add <type> <desc> <data> <keyring>
+.br
+\fBkeyctl\fR padd <type> <desc> <keyring>
+.br
+\fBkeyctl\fR request <type> <desc> [<dest_keyring>]
+.br
+\fBkeyctl\fR request2 <type> <desc> <info> [<dest_keyring>]
+.br
+\fBkeyctl\fR prequest2 <type> <desc> [<dest_keyring>]
+.br
+\fBkeyctl\fR update <key> <data>
+.br
+\fBkeyctl\fR pupdate <key>
+.br
+\fBkeyctl\fR newring <name> <keyring>
+.br
+\fBkeyctl\fR revoke <key>
+.br
+\fBkeyctl\fR clear <keyring>
+.br
+\fBkeyctl\fR link <key> <keyring>
+.br
+\fBkeyctl\fR unlink <key> [<keyring>]
+.br
+\fBkeyctl\fR move [-f] <key> <from_keyring> <to_keyring>
+.br
+\fBkeyctl\fR search <keyring> <type> <desc> [<dest_keyring>]
+.br
+\fBkeyctl\fR restrict_keyring <keyring> [<type> [<restriction>]]
+.br
+\fBkeyctl\fR read <key>
+.br
+\fBkeyctl\fR pipe <key>
+.br
+\fBkeyctl\fR print <key>
+.br
+\fBkeyctl\fR list <keyring>
+.br
+\fBkeyctl\fR rlist <keyring>
+.br
+\fBkeyctl\fR describe <keyring>
+.br
+\fBkeyctl\fR rdescribe <keyring> [sep]
+.br
+\fBkeyctl\fR chown <key> <uid>
+.br
+\fBkeyctl\fR chgrp <key> <gid>
+.br
+\fBkeyctl\fR setperm <key> <mask>
+.br
+\fBkeyctl\fR new_session
+.br
+\fBkeyctl\fR session
+.br
+\fBkeyctl\fR session \- [<prog> <arg1> <arg2> ...]
+.br
+\fBkeyctl\fR session <name> [<prog> <arg1> <arg2> ...]
+.br
+\fBkeyctl\fR instantiate <key> <data> <keyring>
+.br
+\fBkeyctl\fR pinstantiate <key> <keyring>
+.br
+\fBkeyctl\fR negate <key> <timeout> <keyring>
+.br
+\fBkeyctl\fR reject <key> <timeout> <error> <keyring>
+.br
+\fBkeyctl\fR timeout <key> <timeout>
+.br
+\fBkeyctl\fR security <key>
+.br
+\fBkeyctl\fR reap [\-v]
+.br
+\fBkeyctl\fR purge <type>
+.br
+\fBkeyctl\fR purge [\-i] [\-p] <type> <desc>
+.br
+\fBkeyctl\fR purge \-s <type> <desc>
+.br
+\fBkeyctl\fR get_persistent <keyring> [<uid>]
+.br
+\fBkeyctl\fR dh_compute <private> <prime> <base>
+.br
+\fBkeyctl\fR dh_compute_kdf <private> <prime> <base> <output_length> <hash_type>
+.br
+\fBkeyctl\fR dh_compute_kdf_oi <private> <prime> <base> <output_length> <hash_type>
+.br
+\fBkeyctl\fR pkey_query <key> <pass> [k=v]*
+.br
+\fBkeyctl\fR pkey_encrypt <key> <pass> <datafile> [k=v]* ><encfile>
+.br
+\fBkeyctl\fR pkey_decrypt <key> <pass> <encfile> [k=v]* ><datafile>
+.br
+\fBkeyctl\fR pkey_sign <key> <pass> <datafile> [k=v]* ><sigfile>
+.br
+\fBkeyctl\fR pkey_decrypt <key> <pass> <datafile> <sigfile> [k=v]*
+.SH DESCRIPTION
+This program is used to control the key management facility in various ways
+using a variety of subcommands.
+.SH KEY IDENTIFIERS
+The key identifiers passed to or returned from keyctl are, in general, positive
+integers. There are, however, some special values with special meanings that
+can be passed as arguments:
+.TP
+No key: \fB0\fR
+.TP
+Thread keyring: \fB@t\fR or \fB\-1\fR
+Each thread may have its own keyring. This is searched first, before all
+others. The thread keyring is replaced by (v)fork, exec and clone.
+.TP
+Process keyring: \fB@p\fR or \fB\-2\fR
+Each process (thread group) may have its own keyring. This is shared between
+all members of a group and will be searched after the thread keyring. The
+process keyring is replaced by (v)fork and exec.
+.TP
+Session keyring: \fB@s\fR or \fB\-3\fR
+Each process subscribes to a session keyring that is inherited across (v)fork,
+exec and clone. This is searched after the process keyring. Session keyrings
+can be named and an extant keyring can be joined in place of a process's
+current session keyring.
+.TP
+User specific keyring: \fB@u\fR or \fB\-4\fR
+This keyring is shared between all the processes owned by a particular user. It
+isn't searched directly, but is normally linked to from the session keyring.
+.TP
+User default session keyring: \fB@us\fR or \fB\-5\fR
+This is the default session keyring for a particular user. Login processes that
+change to a particular user will bind to this session until another session is
+set.
+.TP
+Group specific keyring: \fB@g\fR or \fB\-6\fR
+This is a place holder for a group specific keyring, but is not actually
+implemented yet in the kernel.
+.TP
+Assumed request_key authorisation key: \fB@a\fR or \fB\-7\fR
+This selects the authorisation key provided to the
+.BR request_key ()
+helper to
+permit it to access the callers keyrings and instantiate the target key.
+.TP
+Keyring by name: \fB%:<name>\fR
+A named keyring. This will be searched for in the process's keyrings and in
+.IR /proc/keys .
+.TP
+Key by name: \fB%<type>:<name>\fR
+A named key of the given type. This will be searched for in the process's
+keyrings and in
+.IR /proc/keys .
+.SH COMMAND SYNTAX
+Any non-ambiguous shortening of a command name may be used in lieu of the full
+command name. This facility should not be used in scripting as new commands may
+be added in future that then cause ambiguity.
+.SS Display the package version number
+\fBkeyctl \-\-version\fR
+
+This command prints the package version number and build date and exits:
+
+.RS
+.nf
+$ keyctl \-\-version
+keyctl from keyutils\-1.5.3 (Built 2011\-08\-24)
+.fi
+.RE
+.SS Query subsystem capabilities
+\fBkeyctl\fR supports [<cap>]
+
+This command can list the available capabilities:
+
+.RS
+.nf
+$ keyctl supports
+have_capabilities=0
+have_persistent_keyrings=1
+have_dh_compute=1
+have_public_key=1
+...
+.fi
+.RE
+.P
+And it can query a capability:
+
+.RS
+.nf
+$ keyctl supports pkey
+echo $?
+0
+.fi
+.RE
+
+which returns 0 if the capability is supported, 1 if it isn't and 3 if the
+name is not recognised. The capabilities supported are:
+.TP
+.B capabilities
+The kernel supports capability querying. If not, the other capabilities will
+be queried as best libkeyutils can manage.
+.TP
+.B persistent_keyrings
+The kernel supports persistent keyrings.
+.TP
+.B dh_compute
+The kernel supports Diffie-Hellman computation operations.
+.TP
+.B public_key
+The kernel supports public key operations.
+.TP
+.B big_key_type
+The kernel supports the big_key key type.
+.TP
+.B key_invalidate
+The kernel supports the invalidate key operaiton.
+.TP
+.B restrict_keyring
+The kernel supports the restrict_keyring operation.
+.TP
+.B move_key
+The kernel supports the move key operation.
+
+.SS Show process keyrings
+\fBkeyctl show [\-x] [<keyring>]\fR
+
+By default this command recursively shows what keyrings a process is subscribed
+to and what keys and keyrings they contain. If a keyring is specified then
+that keyring will be dumped instead. If \fB\-x\fR is specified then the keyring
+IDs will be dumped in hex instead of decimal.
+.SS Add a key to a keyring
+\fBkeyctl add\fR <type> <desc> <data> <keyring>
+.br
+\fBkeyctl padd\fR <type> <desc> <keyring>
+
+This command creates a key of the specified type and description; instantiates
+it with the given data and attaches it to the specified keyring. It then prints
+the new key's ID on stdout:
+
+.RS
+.nf
+$ keyctl add user mykey stuff @u
+26
+.fi
+.RE
+
+The \fBpadd\fR variant of the command reads the data from stdin rather than
+taking it from the command line:
+
+.RS
+.fi
+$ echo \-n stuff | keyctl padd user mykey @u
+26
+.fi
+.RE
+.SS Request a key
+\fBkeyctl request\fR <type> <desc> [<dest_keyring>]
+.br
+\fBkeyctl request2\fR <type> <desc> <info> [<dest_keyring>]
+.br
+\fBkeyctl prequest2\fR <type> <desc> [<dest_keyring>]
+
+These three commands request the lookup of a key of the given type and
+description. The process's keyrings will be searched, and if a match is found
+the matching key's ID will be printed to stdout; and if a destination keyring
+is given, the key will be added to that keyring also.
+
+If there is no key, the first command will simply return the error ENOKEY and
+fail. The second and third commands will create a partial key with the type and
+description, and call out to
+.IR /sbin/request\-key
+with that key and the
+extra information supplied. This will then attempt to instantiate the key in
+some manner, such that a valid key is obtained.
+
+The third command is like the second, except that the callout information is
+read from stdin rather than being passed on the command line.
+
+If a valid key is obtained, the ID will be printed and the key attached as if
+the original search had succeeded.
+
+If there wasn't a valid key obtained, a temporary negative key will be attached
+to the destination keyring if given and the error "Requested key not available"
+will be given.
+
+.RS
+.nf
+$ keyctl request2 user debug:hello wibble
+23
+$ echo \-n wibble | keyctl prequest2 user debug:hello
+23
+$ keyctl request user debug:hello
+23
+.fi
+.RE
+.SS Update a key
+\fBkeyctl update\fR <key> <data>
+.br
+\fBkeyctl pupdate\fR <key>
+
+This command replaces the data attached to a key with a new set of data. If the
+type of the key doesn't support update then error "Operation not supported"
+will be returned.
+
+.RS
+.nf
+$ keyctl update 23 zebra
+.fi
+.RE
+
+The \fBpupdate\fR variant of the command reads the data from stdin rather than
+taking it from the command line:
+
+.RS
+.nf
+$ echo \-n zebra | keyctl pupdate 23
+.fi
+.RE
+.SS Create a keyring
+\fBkeyctl newring\fR <name> <keyring>
+
+This command creates a new keyring of the specified name and attaches it to the
+specified keyring. The ID of the new keyring will be printed to stdout if
+successful.
+
+.RS
+.nf
+$ keyctl newring squelch @us
+27
+.fi
+.RE
+.SS Revoke a key
+\fBkeyctl revoke\fR <key>
+
+This command marks a key as being revoked. Any further operations on that key
+(apart from unlinking it) will return error "Key has been revoked".
+
+.RS
+.nf
+$ keyctl revoke 26
+$ keyctl describe 26
+keyctl_describe: Key has been revoked
+.fi
+.RE
+.SS Clear a keyring
+\fBkeyctl clear\fR <keyring>
+
+This command unlinks all the keys attached to the specified keyring. Error
+"Not a directory" will be returned if the key specified is not a keyring.
+
+.RS
+.nf
+$ keyctl clear 27
+.fi
+.RE
+.SS Link a key to a keyring
+\fBkeyctl link\fR <key> <keyring>
+
+This command makes a link from the key to the keyring if there's enough
+capacity to do so. Error "Not a directory" will be returned if the destination
+is not a keyring. Error "Permission denied" will be returned if the key doesn't
+have link permission or the keyring doesn't have write permission. Error "File
+table overflow" will be returned if the keyring is full. Error "Resource
+deadlock avoided" will be returned if an attempt was made to introduce a
+recursive link.
+
+.RS
+.nf
+$ keyctl link 23 27
+$ keyctl link 27 27
+keyctl_link: Resource deadlock avoided
+.fi
+.RE
+.SS Unlink a key from a keyring or the session keyring tree
+\fBkeyctl unlink\fR <key> [<keyring>]
+
+If the keyring is specified, this command removes a link to the key from the
+keyring. Error "Not a directory" will be returned if the destination is not a
+keyring. Error "Permission denied" will be returned if the keyring doesn't have
+write permission. Error "No such file or directory" will be returned if the key
+is not linked to by the keyring.
+
+If the keyring is not specified, this command performs a depth-first search of
+the session keyring tree and removes all the links to the nominated key that it
+finds (and that it is permitted to remove). It prints the number of successful
+unlinks before exiting.
+
+.RS
+.nf
+$ keyctl unlink 23 27
+.fi
+.RE
+.SS Move a key between keyrings.
+\fBkeyctl move\fR [-f] <key> <from_keyring> <to_keyring>
+
+This command moves a key from one keyring to another, atomically combining
+"keyctl unlink <key> <from_keyring>" and "keyctl link <key> <to_keyring>".
+
+If the "-f" flag is present, any matching key will be displaced from
+"to_keyring"; if not present, the command will fail with the error message
+"File exists" if the key would otherwise displace another key from
+"to_keyring".
+
+.RS
+.nf
+$ keyctl move 23 27 29
+$ keyctl move -f 71 @u @s
+.fi
+.RE
+.SS Search a keyring
+\fBkeyctl search\fR <keyring> <type> <desc> [<dest_keyring>]
+
+This command non-recursively searches a keyring for a key of a particular type
+and description. If found, the ID of the key will be printed on stdout and the
+key will be attached to the destination keyring if present. Error "Requested
+key not available" will be returned if the key is not found.
+
+.RS
+.nf
+$ keyctl search @us user debug:hello
+23
+$ keyctl search @us user debug:bye
+keyctl_search: Requested key not available
+.fi
+.RE
+.SS Restrict a keyring
+\fBkeyctl restrict_keyring\fR <keyring> [<type> [<restriction>]]
+
+This command limits the linkage of keys to the given keyring using a provided
+restriction scheme. The scheme is associated with a given key type, with
+further details provided in the restriction option string. Options typically
+contain a restriction name possibly followed by key ids or other data relevant
+to the restriction. If no restriction scheme is provided, the keyring will
+reject all links.
+
+.RS
+.nf
+$ keyctl restrict_keyring $1 asymmetric builtin_trusted
+.RE
+.SS Read a key
+\fBkeyctl read\fR <key>
+.br
+\fBkeyctl pipe\fR <key>
+.br
+\fBkeyctl print\fR <key>
+
+These commands read the payload of a key. "read" prints it on stdout as a hex
+dump, "pipe" dumps the raw data to stdout and "print" dumps it to stdout
+directly if it's entirely printable or as a hexdump preceded by ":hex:" if not.
+
+If the key type does not support reading of the payload, then error "Operation
+not supported" will be returned.
+
+.RS
+.nf
+$ keyctl read 26
+1 bytes of data in key:
+62
+$ keyctl print 26
+b
+$ keyctl pipe 26
+$
+.fi
+.RE
+.SS List a keyring
+\fBkeyctl list\fR <keyring>
+.br
+\fBkeyctl rlist\fR <keyring>
+
+These commands list the contents of a key as a keyring. "list" pretty prints
+the contents and "rlist" just produces a space-separated list of key IDs.
+
+No attempt is made to check that the specified keyring is a keyring.
+
+.RS
+.nf
+$ keyctl list @us
+2 keys in keyring:
+ 22: vrwsl\-\-\-\-\-\-\-\-\-\- 4043 \-1 keyring: _uid.4043
+ 23: vrwsl\-\-\-\-\-\-\-\-\-\- 4043 4043 user: debug:hello
+$ keyctl rlist @us
+22 23
+.fi
+.RE
+.SS Describe a key
+\fBkeyctl describe\fR <keyring>
+.br
+\fBkeyctl rdescribe\fR <keyring> [sep]
+
+These commands fetch a description of a keyring. "describe" pretty prints the
+description in the same fashion as the "list" command; "rdescribe" prints the
+raw data returned from the kernel.
+
+.RS
+.nf
+$ keyctl describe @us
+ \-5: vrwsl\-\-\-\-\-\-\-\-\-\- 4043 \-1 keyring: _uid_ses.4043
+$ keyctl rdescribe @us
+keyring;4043;\-1;3f1f0000;_uid_ses.4043
+.fi
+.RE
+
+The raw string is "<type>;<uid>;<gid>;<perms>;<description>", where \fIuid\fR
+and \fIgid\fR are the decimal user and group IDs, \fIperms\fR is the
+permissions mask in hex, \fItype\fR and \fIdescription\fR are the type name and
+description strings (neither of which will contain semicolons).
+.SS Change the access controls on a key
+\fBkeyctl chown\fR <key> <uid>
+.br
+\fBkeyctl chgrp\fR <key> <gid>
+
+These two commands change the UID and GID associated with evaluating a key's
+permissions mask. The UID also governs which quota a key is taken out of.
+
+The chown command is not currently supported; attempting it will earn the error
+"Operation not supported" at best.
+
+For non-superuser users, the GID may only be set to the process's GID or a GID
+in the process's groups list. The superuser may set any GID it likes.
+
+.RS
+.nf
+$ sudo keyctl chown 27 0
+keyctl_chown: Operation not supported
+$ sudo keyctl chgrp 27 0
+.fi
+.RE
+.SS Set the permissions mask on a key
+\fBkeyctl setperm\fR <key> <mask>
+
+This command changes the permission control mask on a key. The mask may be
+specified as a hex number if it begins "0x", an octal number if it begins "0"
+or a decimal number otherwise.
+
+The hex numbers are a combination of:
+
+.RS
+.nf
+Possessor UID GID Other Permission Granted
+======== ======== ======== ======== ==================
+01000000 00010000 00000100 00000001 View
+02000000 00020000 00000200 00000002 Read
+04000000 00040000 00000400 00000004 Write
+08000000 00080000 00000800 00000008 Search
+10000000 00100000 00001000 00000010 Link
+20000000 00200000 00002000 00000020 Set Attribute
+3f000000 003f0000 00003f00 0000003f All
+.fi
+.RE
+
+\fIView\fR permits the type, description and other parameters of a key to be
+viewed.
+
+\fIRead\fR permits the payload (or keyring list) to be read if supported by the
+type.
+
+\fIWrite\fR permits the payload (or keyring list) to be modified or updated.
+
+\fISearch\fR on a key permits it to be found when a keyring to which it is
+linked is searched.
+
+\fILink\fR permits a key to be linked to a keyring.
+
+\fISet Attribute\fR permits a key to have its owner, group membership,
+permissions mask and timeout changed.
+
+.RS
+.nf
+$ keyctl setperm 27 0x1f1f1f00
+.fi
+.RE
+.SS Start a new session with fresh keyrings
+\fBkeyctl session\fR
+.br
+\fBkeyctl session\fR \- [<prog> <arg1> <arg2> ...]
+.br
+\fBkeyctl session\fR <name> [<prog> <arg1> <arg2> ...]
+
+These commands join or create a new keyring and then run a shell or other
+program with that keyring as the session key.
+
+The variation with no arguments just creates an anonymous session keyring and
+attaches that as the session keyring; it then exec's $SHELL.
+
+The variation with a dash in place of a name creates an anonymous session
+keyring and attaches that as the session keyring; it then exec's the supplied
+command, or $SHELL if one isn't supplied.
+
+The variation with a name supplied creates or joins the named keyring and
+attaches that as the session keyring; it then exec's the supplied command, or
+$SHELL if one isn't supplied.
+
+.RS
+.nf
+$ keyctl rdescribe @s
+keyring;4043;\-1;3f1f0000;_uid_ses.4043
+
+$ keyctl session
+Joined session keyring: 28
+
+$ keyctl rdescribe @s
+keyring;4043;4043;3f1f0000;_ses.24082
+
+$ keyctl session \-
+Joined session keyring: 29
+$ keyctl rdescribe @s
+keyring;4043;4043;3f1f0000;_ses.24139
+
+$ keyctl session \- keyctl rdescribe @s
+Joined session keyring: 30
+keyring;4043;4043;3f1f0000;_ses.24185
+
+$ keyctl session fish
+Joined session keyring: 34
+$ keyctl rdescribe @s
+keyring;4043;4043;3f1f0000;fish
+
+$ keyctl session fish keyctl rdesc @s
+Joined session keyring: 35
+keyring;4043;4043;3f1f0000;fish
+.fi
+.RE
+.SS Instantiate a key
+\fBkeyctl instantiate\fR <key> <data> <keyring>
+.br
+\fBkeyctl pinstantiate\fR <key> <keyring>
+.br
+\fBkeyctl negate\fR <key> <timeout> <keyring>
+.br
+\fBkeyctl reject\fR <key> <timeout> <error> <keyring>
+
+These commands are used to attach data to a partially set up key (as created by
+the kernel and passed to
+.IR /sbin/request\-key ).
+"instantiate" marks a key as
+being valid and attaches the data as the payload. "negate" and "reject" mark a
+key as invalid and sets a timeout on it so that it'll go away after a while.
+This prevents a lot of quickly sequential requests from slowing the system down
+overmuch when they all fail, as all subsequent requests will then fail with
+error "Requested key not found" (if negated) or the specified error (if
+rejected) until the negative key has expired.
+
+Reject's error argument can either be a UNIX error number or one of
+.BR "" "'" rejected "', '" expired "' or '" revoked "'."
+
+The newly instantiated key will be attached to the specified keyring.
+
+These commands may only be run from the program run by request\-key - a special
+authorisation key is set up by the kernel and attached to the request\-key's
+session keyring. This special key is revoked once the key to which it refers
+has been instantiated one way or another.
+
+.RS
+.nf
+$ keyctl instantiate $1 "Debug $3" $4
+$ keyctl negate $1 30 $4
+$ keyctl reject $1 30 64 $4
+.fi
+.RE
+
+The \fBpinstantiate\fR variant of the command reads the data from stdin rather
+than taking it from the command line:
+
+.RS
+.nf
+$ echo \-n "Debug $3" | keyctl pinstantiate $1 $4
+.fi
+.RE
+.SS Set the expiry time on a key
+\fBkeyctl timeout\fR <key> <timeout>
+
+This command is used to set the timeout on a key, or clear an existing timeout
+if the value specified is zero. The timeout is given as a number of seconds
+into the future.
+
+.RS
+.nf
+$ keyctl timeout $1 45
+.fi
+.RE
+.SS Retrieve a key's security context
+\fBkeyctl security\fR <key>
+
+This command is used to retrieve a key's LSM security context. The label is
+printed on stdout.
+
+.RS
+.nf
+$ keyctl security @s
+unconfined_u:unconfined_r:unconfined_t:s0\-s0:c0.c1023
+.fi
+.RE
+.SS Give the parent process a new session keyring
+\fBkeyctl new_session\fR
+
+This command is used to give the invoking process (typically a shell) a new
+session keyring, discarding its old session keyring.
+
+.RS
+.nf
+$ keyctl session foo
+Joined session keyring: 723488146
+$ keyctl show
+Session Keyring
+ \-3 \-\-alswrv 0 0 keyring: foo
+$ keyctl new_session
+490511412
+$ keyctl show
+Session Keyring
+ \-3 \-\-alswrv 0 0 keyring: _ses
+.fi
+.RE
+
+Note that this affects the \fIparent\fP of the process that invokes the system
+call, and so may only affect processes with matching credentials.
+Furthermore, the change does not take effect till the parent process next
+transitions from kernel space to user space - typically when the \fBwait\fP()
+system call returns.
+.SS Remove dead keys from the session keyring tree
+\fBkeyctl reap\fR
+
+This command performs a depth-first search of the caller's session keyring tree
+and attempts to unlink any key that it finds that is inaccessible due to
+expiry, revocation, rejection or negation. It does not attempt to remove live
+keys that are unavailable simply due to a lack of granted permission.
+
+A key that is designated reapable will only be removed from a keyring if the
+caller has Write permission on that keyring, and only keyrings that grant
+Search permission to the caller will be searched.
+
+The command prints the number of keys reaped before it exits. If the \fB\-v\fR
+flag is passed then the reaped keys are listed as they're being reaped,
+together with the success or failure of the unlink.
+.SS Remove matching keys from the session keyring tree
+\fBkeyctl\fR purge <type>
+.br
+\fBkeyctl\fR purge [\-i] [\-p] <type> <desc>
+.br
+\fBkeyctl\fR purge \-s <type> <desc>
+
+These commands perform a depth-first search to find matching keys in the
+caller's session keyring tree and attempts to unlink them. The number of
+keys successfully unlinked is printed at the end.
+
+The keyrings must grant Read and View permission to the caller to be searched,
+and the keys to be removed must also grant View permission. Keys can only be
+removed from keyrings that grant Write permission.
+
+The first variant purges all keys of the specified type.
+
+The second variant purges all keys of the specified type that also match the
+given description literally. The \-i flag allows a case-independent match and
+the \-p flag allows a prefix match.
+
+The third variant purges all keys of the specified type and matching
+description using the key type's comparator in the kernel to match the
+description. This permits the key type to match a key with a variety of
+descriptions.
+.SS Get persistent keyring
+\fBkeyctl\fR get_persistent <keyring> [<uid>]
+
+This command gets the persistent keyring for either the current UID or the
+specified UID and attaches it to the nominated keyring. The persistent
+keyring's ID will be printed on stdout.
+
+The kernel will create the keyring if it doesn't exist and every time this
+command is called, will reset the expiration timeout on the keyring to the
+value in:
+.IP
+/proc/sys/kernel/keys/persistent_keyring_expiry
+.P
+(by default three days). Should the timeout be reached, the persistent keyring
+will be removed and everything it pins can then be garbage collected.
+
+If a UID other than the process's real or effective UIDs is specified, then an
+error will be given if the process does not have the CAP_SETUID capability.
+.SS Compute a Diffie-Hellman shared secret or public key
+\fBkeyctl\fR dh_compute <private> <prime> <base>
+
+This command computes either a Diffie-Hellman shared secret or the
+public key corresponding to the provided private key using the
+payloads of three keys. The computation is:
+.IP
+base ^ private (mod prime)
+.P
+The three inputs must be user keys with read permission. If the
+provided base key contains the shared generator value, the public key
+will be computed. If the provided base key contains the remote public
+key value, the shared secret will be computed.
+
+The result is printed to stdout as a hex dump.
+
+.RS
+.nf
+$ keyctl dh_compute $1 $2 $3
+8 bytes of data in result:
+00010203 04050607
+.SS Compute a Diffie-Hellman shared secret and derive key material
+\fBkeyctl\fR dh_compute_kdf <private> <prime> <base> <output_length> <hash_type>
+
+This command computes a Diffie-Hellman shared secret and derives
+key material from the shared secret using a key derivation function (KDF).
+The shared secret is derived as outlined above and is input to the KDF
+using the specified hash type. The hash type must point to a hash name
+known to the kernel crypto API.
+
+The operation derives key material of the length specified by the caller.
+
+The operation is compliant to the specification of SP800-56A.
+
+The result is printed to stdout as hex dump.
+.SS Compute a Diffie-Hellman shared secret and apply KDF with other input
+\fBkeyctl\fR dh_compute_kdf_oi <private> <prime> <base> <output_length> <hash_type>
+
+This command is identical to the command
+.IR dh_compute_kdf
+to generate a Diffie-Hellman shared secret followed by a key derivation
+operation. This command allows the caller to provide the other input data
+(OI data) compliant to SP800-56A via stdin.
+.fi
+.RE
+.SS Perform public-key operations with an asymmetric key
+\fBkeyctl\fR pkey_query <key> <pass> [k=v]*
+.br
+\fBkeyctl\fR pkey_encrypt <key> <pass> <datafile> [k=v]* > <encfile>
+.br
+\fBkeyctl\fR pkey_decrypt <key> <pass> <encfile> [k=v]* > <datafile>
+.br
+\fBkeyctl\fR pkey_sign <key> <pass> <datafile> [k=v]* > <sigfile>
+.br
+\fBkeyctl\fR pkey_verify <key> <pass> <datafile> <sigfile> [k=v]*
+.PP
+These commands query an asymmetric key, encrypt data with it, decrypt the
+encrypted data, generate a signature over some data and verify that signature.
+For encrypt, decrypt and sign, the resulting data is written to stdout; verify
+reads the data and the signature files and compares them.
+.PP
+[\fB!\fP] NOTE that the data is of very limited capacity, with no more bits
+than the size of the key. For signatures, the caller is expected to digest
+the actual data and pass in the result of the digest as the datafile. The
+name of the digest should be specified on the end of the command line as
+"hash=<name>".
+.PP
+The
+.I key
+ID indicates the key to use;
+.I pass
+is a placeholder for future password provision and should be "0" for the
+moment;
+.I datafile
+is the unencrypted data to be encrypted, signed or to have its signature
+checked;
+.I encfile
+is a file containing encrypted data; and
+.I sigfile
+is a file containing a signature.
+.PP
+A list of parameters in "key[=val]" form can be included on the end of the
+command line. These specify things like the digest algorithm used
+("hash=<name>") or the encoding form ("enc=<type>").
+.PP
+.RS
+.nf
+k=`keyctl padd asymmetric "" @s <key.pkcs8.der`
+keyctl pkey_query $k 0 enc=pkcs1 hash=sha256
+keyctl pkey_encrypt $k 0 foo.hash enc=pkcs1 >foo.enc
+keyctl pkey_decrypt $k 0 foo.enc enc=pkcs1 >foo.hash
+keyctl pkey_sign $k 0 foo.hash enc=pkcs1 hash=sha256 >foo.sig
+keyctl pkey_verify $k 0 foo.hash foo.sig enc=pkcs1 hash=sha256
+.fi
+.RE
+.PP
+See asymmetric-key(7) for more information.
+
+.SH ERRORS
+There are a number of common errors returned by this program:
+
+"Not a directory" - a key wasn't a keyring.
+
+"Requested key not found" - the looked for key isn't available.
+
+"Key has been revoked" - a revoked key was accessed.
+
+"Key has expired" - an expired key was accessed.
+
+"Permission denied" - permission was denied by a UID/GID/mask combination.
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR request\-key.conf (5),
+.BR keyrings (7),
+.BR request\-key (8)
diff --git a/man/keyctl.3 b/man/keyctl.3
new file mode 100644
index 0000000..b0f5fdc
--- /dev/null
+++ b/man/keyctl.3
@@ -0,0 +1,123 @@
+.\"
+.\" Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" This program is free software; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public Licence
+.\" as published by the Free Software Foundation; either version
+.\" 2 of the Licence, or (at your option) any later version.
+.\"
+.TH KEYCTL 3 "21 Feb 2014" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_*() \- key management function wrappers
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+The
+.BR keyctl ()
+system call is a multiplexor for a number of key management functions. These
+should be called via the wrappers in the libkeyutils library.
+.P
+The functions can be compiled in by including the \fBkeyutils\fR header file:
+.sp
+.RS
+.nf
+.B #include <keyutils.h>
+.RE
+.P
+and then telling the linker it should link in the library:
+.sp
+.RS
+.nf
+.B \-lkeyutils
+.RE
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH KEYCTL FUNCTIONS
+.BR keyctl_assume_authority (3)
+.br
+.BR keyctl_chown (3)
+.br
+.BR keyctl_capabilities (3)
+.br
+.BR keyctl_clear (3)
+.br
+.BR keyctl_describe (3)
+.br
+.BR keyctl_describe_alloc (3)
+.br
+.BR keyctl_dh_compute (3)
+.br
+.BR keyctl_dh_compute_alloc (3)
+.br
+.BR keyctl_get_keyring_ID (3)
+.br
+.BR keyctl_get_persistent (3)
+.br
+.BR keyctl_get_security (3)
+.br
+.BR keyctl_get_security_alloc (3)
+.br
+.BR keyctl_instantiate (3)
+.br
+.BR keyctl_instantiate_iov (3)
+.br
+.BR keyctl_invalidate (3)
+.br
+.BR keyctl_join_session_keyring (3)
+.br
+.BR keyctl_link (3)
+.br
+.BR keyctl_move (3)
+.br
+.BR keyctl_negate (3)
+.br
+.BR keyctl_pkey_dec (3)
+.br
+.BR keyctl_pkey_enc (3)
+.br
+.BR keyctl_pkey_query (3)
+.br
+.BR keyctl_pkey_sign (3)
+.br
+.BR keyctl_pkey_verify (3)
+.br
+.BR keyctl_read (3)
+.br
+.BR keyctl_read_alloc (3)
+.br
+.BR keyctl_reject (3)
+.br
+.BR keyctl_restrict_keyring (3)
+.br
+.BR keyctl_revoke (3)
+.br
+.BR keyctl_search (3)
+.br
+.BR keyctl_session_to_parent (3)
+.br
+.BR keyctl_set_reqkey_keyring (3)
+.br
+.BR keyctl_set_timeout (3)
+.br
+.BR keyctl_setperm (3)
+.br
+.BR keyctl_unlink (3)
+.br
+.BR keyctl_update (3)
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH UTILITY FUNCTIONS
+.BR find_key_by_type_and_name (3)
+.br
+.BR recursive_key_scan (3)
+.br
+.BR recursive_session_key_scan (3)
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyctl_capabilities.3 b/man/keyctl_capabilities.3
new file mode 100644
index 0000000..cc4d86c
--- /dev/null
+++ b/man/keyctl_capabilities.3
@@ -0,0 +1,109 @@
+.\"
+.\" Copyright (C) 2019 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_CAPABILITIES 3 "30 May 2019" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_capabilities \- Query subsystem capabilities
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_capabilities(unsigned char *" buffer ", size_t " buflen ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_capabilities ()
+queries the keyrings subsystem in the kernel to ask about its capabilities and
+fills in the array in the buffer with bits that indicate the presence or
+absence of specific features in the keyrings subsystem.
+.P
+The function returns the amount of data the kernel has available, irrespective
+of the amount of buffer space available. If the buffer is shorter than the
+data, a short copy will be made; if the buffer is larger than the data, the
+excess space will be cleared.
+.P
+If this operation is not available in the kernel, the keyutils library will be
+emulate it as best it can and the capability bit that indicates if the kernel
+operation is available will be cleared.
+.P
+In
+.IR buffer[0] ,
+the following capabilities exist:
+.TP
+.B KEYCTL_CAPS0_CAPABILITIES
+This is set if the kernel supports this operation and cleared otherwise. If
+it is cleared, the rest of the flags are emulated.
+.TP
+.B KEYCTL_CAPS0_PERSISTENT_KEYRINGS
+This is set if the kernel supports persistent keyrings and cleared otherwise.
+See
+.BR keyctl_get_persistent ( 3 ).
+.TP
+.B KEYCTL_CAPS0_DIFFIE_HELLMAN
+This is set if the kernel supports Diffie-Hellman calculation and cleared
+otherwise. See
+.BR keyctl_dh_compute ( 3 ).
+.TP
+.B KEYCTL_CAPS0_PUBLIC_KEY
+This is set if the kernel supports public-key operations and cleared
+otherwise. See
+.BR keyctl_pkey_query ( 3 ).
+.TP
+.B KEYCTL_CAPS0_BIG_KEY
+This is set if the kernel supports the big_key key type and cleared otherwise.
+.TP
+.B KEYCTL_CAPS0_INVALIDATE
+This is set if the kernel supports key invalidation and cleared otherwise.
+See
+.BR keyctl_invalidate ( 3 ).
+.TP
+.B KEYCTL_CAPS0_RESTRICT_KEYRING
+This is set if the kernel supports restrictions on keyrings and cleared
+otherwise. See
+.BR keyctl_restrict_keyring ( 3 ).
+.TP
+.B KEYCTL_CAPS0_MOVE
+This is set if the kernel supports the move key operation and cleared
+otherwise. See
+.BR keyctl_move ( 3 ).
+.P
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_capabilities ()
+returns the size of the data it has available, irrespective of the size of the
+buffer. On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B EFAULT
+The buffer cannot be written to.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyctl_chown.3 b/man/keyctl_chown.3
new file mode 100644
index 0000000..286bded
--- /dev/null
+++ b/man/keyctl_chown.3
@@ -0,0 +1,88 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_CHOWN 3 "4 May 2006" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_chown \- change the ownership of a key
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_chown(key_serial_t " key ", uid_t " uid ", gid_t " gid ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_chown ()
+changes the user and group ownership details of a key.
+.P
+A setting of
+.B -1
+on either
+.I uid
+or
+.I gid
+will cause that setting to be ignored.
+.P
+A process that does not have the
+.B SysAdmin
+capability may not change a key's UID or set the key's GID to a value that
+does not match the process's GID or one of its group list.
+.P
+The caller must have
+.B setattr
+permission on a key to be able change its ownership.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_chown ()
+returns
+.B 0 .
+On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The specified key does not exist.
+.TP
+.B EKEYEXPIRED
+The specified key has expired.
+.TP
+.B EKEYREVOKED
+The specified key has been revoked.
+.TP
+.B EDQUOT
+Changing the UID to the one specified would run that UID out of quota.
+.TP
+.B EACCES
+The key exists, but does not grant
+.B setattr
+permission to the calling process.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyctl_clear.3 b/man/keyctl_clear.3
new file mode 100644
index 0000000..21e0c26
--- /dev/null
+++ b/man/keyctl_clear.3
@@ -0,0 +1,74 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_CLEAR 3 "4 May 2006" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_clear \- clear a keyring
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_clear(key_serial_t " keyring ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_clear ()
+clears the contents of a
+.IR keyring .
+.P
+The caller must have
+.B write
+permission on a keyring to be able clear it.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_clear ()
+returns
+.BR 0 .
+On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The keyring specified is invalid.
+.TP
+.B EKEYEXPIRED
+The keyring specified has expired.
+.TP
+.B EKEYREVOKED
+The keyring specified had been revoked.
+.TP
+.B EACCES
+The keyring exists, but is not
+.B writable
+by the calling process.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7)
+
diff --git a/man/keyctl_describe.3 b/man/keyctl_describe.3
new file mode 100644
index 0000000..b4f100e
--- /dev/null
+++ b/man/keyctl_describe.3
@@ -0,0 +1,112 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_DESCRIBE 3 "4 May 2006" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_describe \- describe a key
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_describe(key_serial_t " key ", char *" buffer ,
+.BI "size_t" buflen ");"
+.sp
+.BI "long keyctl_describe_alloc(key_serial_t " key ", char **" _buffer ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_describe ()
+describes the attributes of a key as a NUL-terminated string.
+.P
+The caller must have
+.B view
+permission on a key to be able to get a description of it.
+.P
+.I buffer
+and
+.I buflen
+specify the buffer into which the key description will be placed. If the
+buffer is too small, the full size of the description will be returned, and no
+copy will take place.
+.P
+.BR keyctl_describe_alloc ()
+is similar to
+.BR keyctl_describe ()
+except that it allocates a buffer big enough to hold the description and
+places the description in it. If successful, A pointer to the buffer is
+placed in
+.IR *_buffer .
+The caller must free the buffer.
+.P
+The description will be a string of format:
+.IP
+.B "\*(lq%s;%d;%d;%08x;%s\*(rq"
+.P
+where the arguments are: key type name, key UID, key GID, key permissions mask
+and key description.
+.P
+.B NOTE!
+The key description will not contain any semicolons, so that should be
+separated out by working backwards from the end of the string. This permits
+extra information to be inserted before it by later versions of the kernel
+simply by inserting more semicolon-terminated substrings.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_describe ()
+returns the amount of data placed into the buffer. If the buffer was too
+small, then the size of buffer required will be returned, but no data will be
+transferred. On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.P
+On success
+.BR keyctl_describe_alloc ()
+returns the amount of data in the buffer, less the NUL terminator. On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The key specified is invalid.
+.TP
+.B EKEYEXPIRED
+The key specified has expired.
+.TP
+.B EKEYREVOKED
+The key specified had been revoked.
+.TP
+.B EACCES
+The key exists, but is not
+.B viewable
+by the calling process.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyctl_dh_compute.3 b/man/keyctl_dh_compute.3
new file mode 100644
index 0000000..5b79378
--- /dev/null
+++ b/man/keyctl_dh_compute.3
@@ -0,0 +1,168 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Copyright (C) 2016 Intel Corporation. All rights reserved.
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_DH_COMPUTE 3 "07 Apr 2016" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_dh_compute \- Compute a Diffie-Hellman shared secret or public key
+.br
+keyctl_dh_compute_kdf \- Derive key from a Diffie-Hellman shared secret
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_dh_compute(key_serial_t " private ", key_serial_t " prime ,
+.BI "key_serial_t " base ", char *" buffer ", size_t " buflen ");"
+.sp
+.BI "long keyctl_dh_compute_alloc(key_serial_t " private,
+.BI "key_serial_t " prime ", key_serial_t " base ", void **" _buffer ");"
+.sp
+.BI "long keyctl_dh_compute_kdf(key_serial_t " private ", key_serial_t " prime ,
+.BI "key_serial_t " base ", char *" hashname ", char *" otherinfo ",
+.BI "size_t " otherinfolen ", char *" buffer ", size_t " buflen ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_dh_compute ()
+computes a Diffie-Hellman public key or shared secret. That computation is:
+.IP
+.I base
+^
+.I private
+( mod
+.I prime
+)
+.P
+When
+.I base
+is a key containing the shared generator value, the remote public key is
+computed. When
+.I base
+is a key containing the remote public key, the shared secret is computed.
+.P
+.IR base ", " private ", and " prime
+must all refer to
+.BR user -type
+keys containing the parameters for the computation. Each of these keys must
+grant the caller
+.B read
+permission in order for them to be used.
+.P
+.I buffer
+and
+.I buflen
+specify the buffer into which the computed result will be placed.
+.I buflen
+may be zero, in which case the buffer is not used and the minimum buffer length
+is fetched.
+.P
+.BR keyctl_dh_compute_alloc ()
+is similar to
+.BR keyctl_dh_compute ()
+except that it allocates a buffer big enough to hold the payload data and
+places the data in it. If successful, a pointer to the buffer is placed in
+.IR *_buffer .
+The caller must free the buffer.
+.P
+.BR keyctl_dh_compute_kdf ()
+derives a key from a Diffie-Hellman shared secret according to the protocol
+specified in SP800-56A. The Diffie-Hellman computation is based on the same
+primitives as discussed
+for
+.BR keyctl_dh_compute ().
+.P
+To implement the protocol of SP800-56A
+.I base
+is a key containing the remote public key to compute the Diffie-Hellman
+shared secret. That shared secret is post-processed with a key derivation
+function.
+.P
+The
+.I hashname
+specifies the Linux kernel crypto API name for a hash that shall be used
+for the key derivation function, such as sha256.
+The
+.I hashname
+must be a NULL terminated string. See
+.I /proc/crypto
+for available hashes on the system.
+.P
+Following the specification of SP800-56A section 5.8.1.2 the
+.I otherinfo
+parameter may be provided. The format of the OtherInfo field is defined
+by the caller. The caller may also specify NULL as a valid argument when
+no OtherInfo data shall be processed. The length of the
+.I otherinfo
+parameter is specified with
+.I otherinfolen
+and is restricted to a maximum length by the kernel.
+.P
+The KDF returns the requested number of bytes specified with the
+.I genlen
+or the
+.I buflen
+parameter depending on the invoked function.
+.P
+.I buffer
+and
+.I buflen
+specify the buffer into which the computed result will be placed.
+.P
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_dh_compute ()
+returns the amount of data placed into the buffer when
+.I buflen
+is non-zero. When
+.I buflen
+is zero, the minimum buffer length to hold the data is returned.
+.P
+On success
+.BR keyctl_dh_compute_alloc ()
+returns the amount of data in the buffer.
+.P
+On error, both functions set errno to an appropriate code and return the value
+.BR -1 .
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+One of the keys specified is invalid or not readable.
+.TP
+.B EINVAL
+The buffer pointer is invalid or buflen is too small.
+.TP
+.B EOPNOTSUPP
+One of the keys was not a valid user key.
+.TP
+.B EMSGSIZE
+When using
+.BR keyctl_dh_compute_kdf (),
+the size of either
+.I otherinfolen
+or
+.I buflen
+is too big.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.BR keyctl (1),
+.br
+.BR keyctl (2),
+.br
+.BR keyctl (3),
+.br
+.BR keyutils (7)
diff --git a/man/keyctl_get_keyring_ID.3 b/man/keyctl_get_keyring_ID.3
new file mode 100644
index 0000000..6525292
--- /dev/null
+++ b/man/keyctl_get_keyring_ID.3
@@ -0,0 +1,96 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_GET_KEYRING_ID 3 "4 May 2006" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_get_keyring_ID \- get the ID of a special keyring
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "key_serial_t keyctl_get_keyring_ID(key_serial_t " key ","
+.BI " int " create ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_get_keyring_ID ()
+maps a special
+.I key
+or keyring ID to the serial number of the key actually representing that
+feature. The serial number will be returned if that key exists.
+.P
+If the key or keyring does not yet exist, then if
+.I create
+is non-zero, the key or keyring will be created if it is appropriate to do so.
+.P
+The following special key IDs may be specified as
+.IR key :
+.TP
+.B KEY_SPEC_THREAD_KEYRING
+This specifies the caller's thread-specific keyring.
+.TP
+.B KEY_SPEC_PROCESS_KEYRING
+This specifies the caller's process-specific keyring.
+.TP
+.B KEY_SPEC_SESSION_KEYRING
+This specifies the caller's session-specific keyring.
+.TP
+.B KEY_SPEC_USER_KEYRING
+This specifies the caller's UID-specific keyring.
+.TP
+.B KEY_SPEC_USER_SESSION_KEYRING
+This specifies the caller's UID-session keyring.
+.TP
+.B KEY_SPEC_REQKEY_AUTH_KEY
+This specifies the authorisation key created by
+.BR request_key ()
+and passed to the process it spawns to generate a key.
+.P
+If a valid keyring ID is passed in, then this will simply be returned if the
+key exists; an error will be issued if it doesn't exist.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_get_keyring_ID ()
+returns the serial number of the key it found. On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+No matching key was found.
+.TP
+.B ENOMEM
+Insufficient memory to create a key.
+.TP
+.B EDQUOT
+The key quota for this user would be exceeded by creating this key or linking
+it to the keyring.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyctl_get_persistent.3 b/man/keyctl_get_persistent.3
new file mode 100644
index 0000000..8d79957
--- /dev/null
+++ b/man/keyctl_get_persistent.3
@@ -0,0 +1,114 @@
+.\"
+.\" Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_GET_PERSISTENT 3 "20 Feb 2014" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_get_persistent \- get the persistent keyring for a user
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_get_persistent(uid_t " uid ", key_serial_t " keyring ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_get_persistent ()
+gets the persistent keyring for the specified user ID. Unlike the session and
+user keyrings, this keyring will persist once all login sessions have been
+deleted and can thus be used to carry authentication tokens for processes that
+run without user interaction, such as programs started by cron.
+.P
+The persistent keyring will be created by the kernel if it does not yet exist.
+Each time this function is called, the persistent keyring will have its
+expiration timeout reset to the value in:
+.IP
+/proc/sys/kernel/keys/persistent_keyring_expiry
+.P
+(by default three days). Should the timeout be reached, the persistent keyring
+will be removed and everything it pins can then be garbage collected.
+.P
+If
+.I uid
+is
+.B -1
+then the calling process's real user ID will be used. If
+.I uid
+is not
+.B -1
+then error
+.B EPERM
+will be given if the user ID requested does not match either the caller's real
+or effective user IDs or if the calling process does not have
+.B SetUid
+capability.
+.P
+If successful, a link to the persistent keyring will be added into
+.IR keyring .
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_get_persistent ()
+returns the serial number of the persistent keyring. On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B EPERM
+Not permitted to access the persistent keyring for the requested
+.IR uid .
+.TP
+.B ENOMEM
+Insufficient memory to create the persistent keyring or to extend
+.IR keyring .
+.TP
+.B ENOKEY
+.I keyring
+does not exist.
+.TP
+.B EKEYEXPIRED
+.I keyring
+has expired.
+.TP
+.B EKEYREVOKED
+.I keyring
+has been revoked.
+.TP
+.B EDQUOT
+The user does not have sufficient quota to extend
+.IR keyring .
+.TP
+.B EACCES
+.I keyring
+exists, but does not grant
+.B write
+permission to the calling process.
+
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7),
+.BR persistent\-keyring (7),
diff --git a/man/keyctl_get_security.3 b/man/keyctl_get_security.3
new file mode 100644
index 0000000..a2c5b41
--- /dev/null
+++ b/man/keyctl_get_security.3
@@ -0,0 +1,102 @@
+.\"
+.\" Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_GET_SECURITY 3 "26 Feb 2010" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_get_security \- retrieve a key's security context
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_get_security(key_serial_t " key ", char *" buffer ,
+.BI "size_t " buflen ");"
+.sp
+.BI "long keyctl_get_security_alloc(key_serial_t " key ", char **" _buffer ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_get_security ()
+retrieves the security context of a key as a NUL-terminated string. This will
+be rendered in a form appropriate to the LSM in force - for instance, with
+SELinux, it may look like
+.IP
+.B "unconfined_u:unconfined_r:unconfined_t:s0\-s0:c0.c1023"
+.P
+The caller must have
+.B view
+permission on a key to be able to get its security context.
+.P
+.I buffer
+and
+.I buflen
+specify the buffer into which the string will be placed. If the buffer is too
+small, the full size of the string will be returned, and no copy will take
+place.
+.P
+.BR keyctl_get_security_alloc ()
+is similar to
+.BR keyctl_get_security ()
+except that it allocates a buffer big enough to hold the string and copies the
+string into it. If successful, A pointer to the buffer is placed in
+.IR *_buffer .
+The caller must free the buffer.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_get_security ()
+returns the amount of data placed into the buffer. If the buffer was too
+small, then the size of buffer required will be returned, but no data will be
+transferred. On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.P
+On success
+.BR keyctl_get_security_alloc ()
+returns the amount of data in the buffer, less the NUL terminator. On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The key specified is invalid.
+.TP
+.B EKEYEXPIRED
+The key specified has expired.
+.TP
+.B EKEYREVOKED
+The key specified had been revoked.
+.TP
+.B EACCES
+The key exists, but is not
+.B viewable
+by the calling process.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyctl_instantiate.3 b/man/keyctl_instantiate.3
new file mode 100644
index 0000000..d436bf2
--- /dev/null
+++ b/man/keyctl_instantiate.3
@@ -0,0 +1,191 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_INSTANTIATE 3 "4 May 2006" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_assume_authority, keyctl_instantiate, keyctl_instantiate_iov, keyctl_reject, keyctl_negate \- key instantiation functions
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_assume_authority(key_serial_t " key ");"
+.sp
+.BI "long keyctl_instantiate(key_serial_t " key ", const void *" payload ,
+.BI "size_t " plen ", key_serial_t " keyring ");"
+.sp
+.BI "long keyctl_instantiate_iov(key_serial_t " key ,
+.BI "const struct iovec *" payload_iov ", unsigned " ioc ,
+.BI "key_serial_t " keyring ");"
+.sp
+.BI "long keyctl_negate(key_serial_t " key ", unsigned " timeout ,
+.BI "key_serial_t " keyring ");"
+.sp
+.BI "long keyctl_reject(key_serial_t " key ", unsigned " timeout ,
+.BI "unsigned " error ", key_serial_t " keyring ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_assume_authority ()
+assumes the authority for the calling thread to deal with and instantiate the
+specified uninstantiated
+.IR key .
+.P
+The calling thread must have the appropriate authorisation key resident in one
+of its keyrings for this to succeed, and that authority must not have been
+revoked.
+.P
+The authorising key is allocated by
+.BR request_key()
+when it needs to invoke
+userspace to generate a key for the requesting process. This is then attached
+to one of the keyrings of the userspace process to which the task of
+instantiating the key is given:
+.IP
+requester -> request_key() -> instantiator
+.P
+Calling this function modifies the way
+.BR request_key ()
+works when called thereafter by the calling (instantiator) thread; once the
+authority is assumed, the keyrings of the initial process are added to the
+search path, using the initial process's UID, GID, groups and security
+context.
+.P
+If a thread has multiple instantiations to deal with, it may call this
+function to change the authorisation key currently in effect. Supplying a
+.B zero
+.I key
+de-assumes the currently assumed authority.
+.P
+.B NOTE!
+This is a per-thread setting and not a per-process setting so that a
+multithreaded process can be used to instantiate several keys at once.
+.P
+.BR keyctl_instantiate ()
+instantiates the payload of an uninstantiated key from the data specified.
+.I payload
+and
+.I plen
+specify the data for the new payload.
+.I payload
+may be NULL and
+.I plen
+may be zero if the key type permits that. The key type may reject the data if
+it's in the wrong format or in some other way invalid.
+.P
+.BR keyctl_instantiate_iov ()
+is similar, but the data is passed in an array of iovec structs instead of in
+a flat buffer.
+.I payload_iov
+points to the base of the array and
+.I ioc
+indicates how many elements there are.
+.I payload_iov
+may be NULL or
+.I ioc
+may be zero to indicate that no data is being supplied.
+.P
+.BR keyctl_reject ()
+marks a key as negatively instantiated and sets the expiration timer on it.
+.I timeout
+specifies the lifetime of the key in seconds.
+.I error
+specifies the error to be returned when a search hits the key (this is
+typically
+.BR EKEYREJECTED ", " EKEYREVOKED " or " EKEYEXPIRED ")."
+Note that
+.BR keyctl_reject ()
+falls back to
+.BR keyctl_negate ()
+if the kernel does not
+support it.
+.P
+.BR keyctl_negate ()
+as
+.BR keyctl_reject ()
+with an error code of
+.IB ENOKEY .
+.P
+Only a key for which authority has been assumed may be instantiated or
+negatively instantiated, and once instantiated, the authorisation key will be
+revoked and the requesting process will be able to resume.
+.P
+The destination
+.IR keyring ,
+if given, is assumed to belong to the initial requester, and not the
+instantiating process. Therefore, the special keyring IDs refer to the
+requesting process's keyrings, not the caller's, and the requester's UID,
+etc. will be used to access them.
+.P
+The destination keyring can be
+.B zero
+if no extra link is desired.
+.P
+The requester, not the caller, must have
+.B write
+permission on the destination for a link to be made there.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_instantiate ()
+returns
+.BR 0 .
+On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The key or keyring specified is invalid.
+.TP
+.B EKEYEXPIRED
+The keyring specified has expired.
+.TP
+.B EKEYREVOKED
+The key or keyring specified had been revoked, or the authorisation has been
+revoked.
+.TP
+.B EINVAL
+The payload data was invalid.
+.TP
+.B ENOMEM
+Insufficient memory to store the new payload or to expand the destination
+keyring.
+.TP
+.B EDQUOT
+The key quota for the key's user would be exceeded by increasing the size of
+the key to accommodate the new payload or the key quota for the keyring's user
+would be exceeded by expanding the destination keyring.
+.TP
+.B EACCES
+The key exists, but is not
+.B writable
+by the requester.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7),
+.BR request\-key (8)
diff --git a/man/keyctl_invalidate.3 b/man/keyctl_invalidate.3
new file mode 100644
index 0000000..120da45
--- /dev/null
+++ b/man/keyctl_invalidate.3
@@ -0,0 +1,76 @@
+.\"
+.\" Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_INVALIDATE 3 "29 Aug 2013" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_invalidate \- invalidate a key
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_invalidate(key_serial_t " key ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_invalidate ()
+invalidates a
+.IR key .
+The key is scheduled for immediate removal from all the keyrings that point to
+it, after which it will be deleted. The key will be ignored by all searches
+once this function is called even if it is not yet fully dealt with.
+.P
+The caller must have
+.B search
+permission on a key to be able to invalidate it.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_invalidate ()
+returns
+.BR 0 .
+On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The key specified is invalid.
+.TP
+.B EKEYEXPIRED
+The key specified has expired.
+.TP
+.B EKEYREVOKED
+The key specified had been revoked.
+.TP
+.B EACCES
+The key exists, but is not
+.B searchable
+by the calling process.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyctl_join_session_keyring.3 b/man/keyctl_join_session_keyring.3
new file mode 100644
index 0000000..4e27717
--- /dev/null
+++ b/man/keyctl_join_session_keyring.3
@@ -0,0 +1,84 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_JOIN_SESSION_KEYRING 3 "20 Feb 2014" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_join_session_keyring \- join a different session keyring
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "key_serial_t keyctl_join_session_keyring(const char *" name ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_join_session_keyring ()
+changes the session keyring to which a process is subscribed.
+.P
+If
+.I name
+is
+.B NULL
+then a new anonymous keyring will be created, and the process will be
+subscribed to that.
+.P
+If
+.I name
+points to a string, then if a keyring of that name is available, the process
+will attempt to subscribe to that keyring, giving an error if that is not
+permitted; otherwise a new keyring of that name is created and attached as the
+session keyring.
+.P
+To attach to an extant named keyring, the keyring must have
+.B search
+permission available to the calling process.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_join_session_keyring ()
+returns the serial number of the key it found or created. On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOMEM
+Insufficient memory to create a key.
+.TP
+.B EDQUOT
+The key quota for this user would be exceeded by creating this key or linking
+it to the keyring.
+.TP
+.B EACCES
+The named keyring exists, but is not
+.B searchable
+by the calling process.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7),
+.BR session\-keyring (7),
+.BR user\-session\-keyring (7)
diff --git a/man/keyctl_link.3 b/man/keyctl_link.3
new file mode 100644
index 0000000..1185874
--- /dev/null
+++ b/man/keyctl_link.3
@@ -0,0 +1,108 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_LINK 3 "4 May 2006" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_link, keyctl_unlink \- link/unlink a key to/from a keyring
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_link(key_serial_t " key ", key_serial_t " keyring ");"
+.sp
+.BI "long keyctl_unlink(key_serial_t " key ", key_serial_t " keyring ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_link ()
+creates a link from
+.I keyring
+to
+.IR key ,
+displacing any link to another key of the same type and description in that
+keyring if one exists.
+.P
+.BR keyctl_unlink ()
+removes the link from
+.I keyring
+to
+.I key
+if it exists.
+.P
+The caller must have
+.B write
+permission on a keyring to be able create or remove links in it.
+.P
+The caller must have
+.B link
+permission on a key to be able to create a link to it.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_link ()
+and
+.BR keyctl_unlink ()
+return
+.BR 0 .
+On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The key or the keyring specified are invalid.
+.TP
+.B EKEYEXPIRED
+The key or the keyring specified have expired.
+.TP
+.B EKEYREVOKED
+The key or the keyring specified have been revoked.
+.TP
+.B EACCES
+The keyring exists, but is not
+.B writable
+by the calling process.
+.P
+For
+.BR keyctl_link ()
+only:
+.TP
+.B ENOMEM
+Insufficient memory to expand the keyring
+.TP
+.B EDQUOT
+Expanding the keyring would exceed the keyring owner's quota.
+.TP
+.B EACCES
+The key exists, but is not
+.B linkable
+by the calling process.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7)
+
diff --git a/man/keyctl_move.3 b/man/keyctl_move.3
new file mode 100644
index 0000000..b241f70
--- /dev/null
+++ b/man/keyctl_move.3
@@ -0,0 +1,109 @@
+.\"
+.\" Copyright (C) 2019 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_MOVE 3 "29 May 2019" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_move \- Move a key between keyrings
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_move(key_serial_t " key ", key_serial_t " from_keyring ","
+.br
+.BI " key_serial_t " to_keyring ", unsigned int " flags ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_move ()
+atomically unlinks
+.I key
+from
+.I from_keyring
+and links it into
+.I to_keyring
+in a single operation. Depending on the flags set, a link to any matching key
+in to_keyring may get displaced.
+.P
+.I flags
+is a bitwise-OR of zero or more of the following flags:
+.P
+.TP
+.B KEYCTL_MOVE_EXCL
+If there's a matching key in to_keyring, don't displace it but rather return
+an error.
+.P
+The caller must have
+.B write
+permission on both keyring to be able create or remove links in them.
+.P
+The caller must have
+.B link
+permission on a key to be able to create a new link to it.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_move ()
+return
+.BR 0 .
+On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The key or one of the keyrings specified are invalid.
+.TP
+.B ENOKEY
+A key with the same type and description is present in to_keyring and
+KEYCTL_MOVE_EXCL is set.
+.TP
+.B EKEYEXPIRED
+The key or one of the keyrings specified have expired.
+.TP
+.B EKEYREVOKED
+The key or one of the keyrings specified have been revoked.
+.TP
+.B EACCES
+The key exists, but is not
+.B linkable
+by the calling process.
+.TP
+.B EACCES
+The keyrings exist, but are not
+.B writable
+by the calling process.
+.TP
+.B ENOMEM
+Insufficient memory to effect the changes.
+.TP
+.B EDQUOT
+Expanding to_keyring would exceed the keyring owner's quota.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7)
+
diff --git a/man/keyctl_pkey_decrypt.3 b/man/keyctl_pkey_decrypt.3
new file mode 100644
index 0000000..47b1937
--- /dev/null
+++ b/man/keyctl_pkey_decrypt.3
@@ -0,0 +1 @@
+.so keyctl_pkey_encrypt.3
diff --git a/man/keyctl_pkey_encrypt.3 b/man/keyctl_pkey_encrypt.3
new file mode 100644
index 0000000..44c640e
--- /dev/null
+++ b/man/keyctl_pkey_encrypt.3
@@ -0,0 +1,113 @@
+.\"
+.\" Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" This program is free software; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public Licence
+.\" as published by the Free Software Foundation; either version
+.\" 2 of the Licence, or (at your option) any later version.
+.\"
+.TH KEYCTL_PKEY_ENCRYPT 3 "8 Nov 2018" Linux "Linux Public-Key Encryption"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_pkey_encrypt, keyctl_pkey_decrypt \- Encrypt and decrypt data
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_pkey_encrypt(key_serial_t " key ", const char *" info ,
+.BI " const void *" data ", size_t " data_len ,
+.BI " void *" enc ", size_t " enc_len ");"
+.sp
+.BI "long keyctl_pkey_decrypt(key_serial_t " key ", const char *" info ,
+.BI " const void *" enc ", size_t " enc_len ,
+.BI " void *" data ", size_t " data_len ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_pkey_encrypt ()
+asks the kernel to use the crypto material attached to a key to encrypt a blob
+of data and
+.BR keyctl_pkey_decrypt ()
+asks the kernel to use the key to reverse the operation and recover the
+original data. Note that these operations may involve the kernel calling out
+to cryptographic hardware. The caller must have
+.B search
+permission on a key to be able to use them in this manner.
+.PP
+.PP
+When invoking the function,
+.I key
+indicates the key that will provide the cryptographic material and
+.I info
+points to a comma-separated string of "key[=value]" parameters that indicate
+things like encoding forms and passwords to unlock the key; see
+asymmetric-key(7) for more information.
+.PP
+.IR data " and " datalen
+indicate the address and size of the decrypted data buffer and
+.IR enc " and " enclen
+indicate the address and size of the encrypted data buffer. The encrypt
+function draws data from the decrypted data buffer and places the output into
+the encryption buffer. The decrypt function does the reverse, drawing from
+the encryption buffer and writing into the data buffer.
+.PP
+.BR keyctl_pkey_query (2)
+can be called to find out how large the buffers need to be.
+.PP
+Note that not all asymmetric-type keys will support these operations; further,
+the operations available may depend on which components of the key material are
+available: typically encryption only requires the public key, but decryption
+requires the private key as well. Which operations are supported on a
+particular key can also be determined using the query function.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_pkey_encrypt "() and " keyctl_pkey_decrypt ()
+return the amount of data written into the output buffer. On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The key specified is invalid.
+.TP
+.B EKEYEXPIRED
+The key specified has expired.
+.TP
+.B EKEYREVOKED
+The key specified has been revoked.
+.TP
+.B EACCES
+The key exists, but is not
+.B searchable
+by the calling process.
+.TP
+.B ENOPKG
+Some facility needed to complete the requested operation is not available.
+This is most probably a requested or required digest or encryption algorithm.
+.TP
+.B EFAULT
+Bad address.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR keyctl (3),
+.BR keyctl_pkey_query (3),
+.BR keyctl_pkey_sign (3),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyctl_pkey_query.3 b/man/keyctl_pkey_query.3
new file mode 100644
index 0000000..dbeb0fd
--- /dev/null
+++ b/man/keyctl_pkey_query.3
@@ -0,0 +1,137 @@
+.\"
+.\" Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" This program is free software; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public Licence
+.\" as published by the Free Software Foundation; either version
+.\" 2 of the Licence, or (at your option) any later version.
+.\"
+.TH KEYCTL_PKEY_QUERY 3 "8 Nov 2018" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_pkey_query \- Query public key parameters
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_pkey_query(key_serial_t " key ", const char *" info ,
+.BI " struct keyctl_pkey_query *" result ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_pkey_query ()
+queries the public key parameters associated with a kernel
+key that supports these operations (typically
+.BR asymmetric -type).
+The caller must have
+.B search
+permission on the target key to be able to query its parameters.
+.PP
+When invoking the function,
+.I key
+indicates the key to be queried,
+.I info
+points to a comma-separated string of "key[=value]" parameters and
+.I result
+points to a buffer in which the result will be placed.
+.PP
+The parameters that can be used in the
+.I info
+parameter string are dependent on the type of key. Parameters can specify such
+things as encoding types (such as "enc=pkcs1"); see asymmetric-key(7) for more
+information.
+.PP
+If successful, the result is written into the following struct:
+.PP
+.in +4n
+.EX
+struct keyctl_pkey_query {
+ unsigned int supported_ops;
+ unsigned int key_size;
+ unsigned short max_data_size;
+ unsigned short max_sig_size;
+ unsigned short max_enc_size;
+ unsigned short max_dec_size;
+};
+.EE
+.in
+.PP
+The
+.I supported_ops
+field contains a bitmask of the following constants:
+.PP
+.in +4n
+.TS
+lB l.
+KEYCTL_SUPPORTS_ENCRYPT
+KEYCTL_SUPPORTS_DECRYPT
+KEYCTL_SUPPORTS_SIGN
+KEYCTL_SUPPORTS_VERIFY
+.TE
+.in
+.PP
+indicating what operations are supported and thus which of the other
+.BR keyctl_pkey_* ()
+operations can be used with this key.
+.PP
+The
+.I key_size
+field indicates the number of bits in the key size and the
+.IR max_data_size ", " max_sig_size ", " max_enc_size " and " max_dec_size
+fields indicate the maximum sizes in bytes of a blob of data to be signed, a
+signature blob, a blob to be encrypted and a blob to be decrypted respectively.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_pkey_query ()
+returns
+.BR 0 .
+On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The key specified is invalid.
+.TP
+.B EKEYEXPIRED
+The key specified has expired.
+.TP
+.B EKEYREVOKED
+The key specified has been revoked.
+.TP
+.B EACCES
+The key exists, but is not
+.B searchable
+by the calling process.
+.TP
+.B ENOPKG
+Some facility needed to complete the requested operation is not available.
+This is most probably a requested or required digest or encryption algorithm.
+.TP
+.B EFAULT
+Bad address.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR keyctl (3),
+.BR keyctl_pkey_encrypt (3),
+.BR keyctl_pkey_sign (3),
+.BR asymmetric-key (7),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyctl_pkey_sign.3 b/man/keyctl_pkey_sign.3
new file mode 100644
index 0000000..2f17e73
--- /dev/null
+++ b/man/keyctl_pkey_sign.3
@@ -0,0 +1,133 @@
+.\"
+.\" Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" This program is free software; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public Licence
+.\" as published by the Free Software Foundation; either version
+.\" 2 of the Licence, or (at your option) any later version.
+.\"
+.TH KEYCTL_PKEY_SIGN 3 "8 Nov 2018" Linux "Linux Public-Key Signatures"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_pkey_sign, keyctl_pkey_verify \- Generate and verify signatures
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_pkey_sign(key_serial_t " key ", const char *" info ,
+.BI " const void *" data ", size_t " data_len ,
+.BI " void *" sig ", size_t " sig_len ");"
+.sp
+.BI "long keyctl_pkey_verify(key_serial_t " key ", const char *" info ,
+.BI " const void *" data ", size_t " data_len ,
+.BI " const void *" sig ", size_t " sig_len ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_pkey_sign ()
+asks the kernel to use the crypto data attached to a key to generate a
+detached signature for a blob of data. Note that this may involve calling out
+to cryptographic hardware.
+.PP
+.BR keyctl_pkey_verify ()
+asks the kernel to use the key to generate a verify the signature against the
+same blob of data. This may also involve calling out to cryptographic
+hardware.
+.PP
+The caller must have
+.B search
+permission on a key to be able to perform either operation.
+.PP
+.PP
+When invoking the function,
+.I key
+indicates the key that will provide the cryptographic material and
+.I info
+points to a comma-separated string of "key[=value]" parameters that indicate
+things like encoding forms and passwords to unlock the key; see
+asymmetric-key(7) for more information.
+.PP
+.IR data " and " datalen
+indicate the address and size of the buffer of data to be signed and
+.IR sig " and " siglen
+indicate the address and size of the signature buffer. The sign function
+draws data from the data buffer, generates a signature from it and places the
+output into the signature buffer. The verify function also draws data from
+the data buffer, then decrypts the signature and compares the result.
+.PP
+Note that the data buffer is strictly limited in capacity, typically unable to
+hold more bits than the size of the key. The caller is expected to have
+pre-digested the actual data and will thus pass the digest output to this
+function. The name of the digest used should be passed as part of the info
+string as \fPhash=<name>\fR for use in constructing the signature metadata.
+.PP
+.BR keyctl_pkey_query (2)
+can be called to find out how large the buffers need to be and what the
+maximum size of the data can be for a specific signature encoding.
+.PP
+Note that not all asymmetric-type keys will support these operations; further,
+the operations available may depend on which components of the key material are
+available: typically encryption only requires the public key, but decryption
+requires the private key as well. Which operations are supported on a
+particular key can also be determined using the query function.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_pkey_sign ()
+returns the amount of data written into the signature buffer.
+.BR keyctl_pkey_verify ()
+will return 0 in this case as it doesn't write to userspace.
+.PP
+On error the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The key specified is invalid.
+.TP
+.B EKEYEXPIRED
+The key specified has expired.
+.TP
+.B EKEYREVOKED
+The key specified has been revoked.
+.TP
+.B EACCES
+The key exists, but is not
+.B searchable
+by the calling process.
+.TP
+.BR EINVAL ", " EBADMSG ", " EOVERFLOW
+Some part of the key material or signature data is bad.
+.TP
+.BR EKEYREJECTED
+Signature verification failed.
+.TP
+.B ENOPKG
+Some facility needed to complete the requested operation is not available.
+This is most probably a requested or required digest or encryption algorithm.
+.TP
+.B EFAULT
+Bad address.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR keyctl (3),
+.BR keyctl_pkey_query (3),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyctl_pkey_verify.3 b/man/keyctl_pkey_verify.3
new file mode 100644
index 0000000..0535113
--- /dev/null
+++ b/man/keyctl_pkey_verify.3
@@ -0,0 +1 @@
+.so keyctl_pkey_sign.3
diff --git a/man/keyctl_read.3 b/man/keyctl_read.3
new file mode 100644
index 0000000..852bc05
--- /dev/null
+++ b/man/keyctl_read.3
@@ -0,0 +1,112 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_READ 3 "21 Feb 2014" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_read \- read a key
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_read(key_serial_t " key ", char *" buffer ,
+.BI "size_t" buflen ");"
+.sp
+.BI "long keyctl_read_alloc(key_serial_t " key ", void **" _buffer ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_read ()
+reads the payload of a key if the key type supports it.
+.P
+The caller must have
+.B read
+permission on a key to be able to read it.
+.P
+.I buffer
+and
+.I buflen
+specify the buffer into which the payload data will be placed. If the buffer
+is too small, then the full size of the payload will be returned, and the
+contents of the buffer may be overwritten in some undefined way.
+.P
+.BR keyctl_read_alloc ()
+is similar to
+.BR keyctl_read ()
+except that it allocates a buffer big enough to hold the payload data and
+places the data in it. If successful, a pointer to the buffer is placed in
+.IR *_buffer .
+The caller must free the buffer.
+.P
+.BR keyctl_read_alloc ()
+adds a NUL character after the data it retrieves, though this is not counted
+in the size value it returns.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH READING KEYRINGS
+This call can be used to list the contents of a keyring. The data is
+presented to the user as an array of
+.B key_serial_t
+values, each of which corresponds to a key to which the keyring holds a link.
+.P
+The size of the keyring will be sizeof(key_serial_t) multiplied by the number
+of keys. The size of key_serial_t is invariant across different word sizes,
+though the byte-ordering is as appropriate for the kernel.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_read ()
+returns the amount of data placed into the buffer. If the buffer was too
+small, then the size of buffer required will be returned, and the contents of
+the buffer may have been overwritten in some undefined way.
+.P
+On success
+.BR keyctl_read_alloc ()
+returns the amount of data in the buffer.
+.P
+On error, both functions set
+.I errno
+to an appropriate code and return the value
+.BR -1 .
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The key specified is invalid.
+.TP
+.B EKEYEXPIRED
+The key specified has expired.
+.TP
+.B EKEYREVOKED
+The key specified had been revoked.
+.TP
+.B EACCES
+The key exists, but is not
+.B readable
+by the calling process.
+.TP
+.B EOPNOTSUPP
+The key type does not support reading of the payload data.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyctl_restrict_keyring.3 b/man/keyctl_restrict_keyring.3
new file mode 100644
index 0000000..468d5b4
--- /dev/null
+++ b/man/keyctl_restrict_keyring.3
@@ -0,0 +1,82 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Copyright (C) 2017 Intel Corporation. All rights reserved.
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_RESTRICT_KEYRING 3 "28 Feb 2017" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_restrict_keyring \- restrict keys that may be linked to a keyring
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_restrict_keyring(key_serial_t " keyring ,
+.BI "const char *" type ", const char *" restriction ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_restrict_keyring ()
+limits the linkage of keys to the given
+.I keyring
+using a provided key
+.I type
+and
+.I restriction
+scheme. The available options vary depending on the key type, and
+typically contain a restriction name possibly followed by key ids or
+other data relevant to the restriction. If the type and restriction are
+both
+.B NULL,
+the keyring will reject all links.
+.P
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_restrict_keyring ()
+returns
+.BR 0 .
+On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B EDEADLK
+A restriction cycle was avoided. Two keyrings cannot restrict each other.
+.TP
+.B EEXIST
+The keyring is already restricted.
+.TP
+.B EINVAL
+The restriction string is invalid or too large.
+.TP
+.B ENOKEY
+The key type in the restriction is invalid or not available.
+.TP
+.B ENOTDIR
+The provided key id references an item that is not a keyring.
+.TP
+.B ENOENT
+The key type exists but does not support restrictions.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR keyctl (2),
+.BR keyctl (3),
+.BR keyutils (7)
diff --git a/man/keyctl_revoke.3 b/man/keyctl_revoke.3
new file mode 100644
index 0000000..914a253
--- /dev/null
+++ b/man/keyctl_revoke.3
@@ -0,0 +1,73 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_REVOKE 3 "4 May 2006" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_revoke \- revoke a key
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_revoke(key_serial_t " key ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_revoke ()
+marks a key as being revoked.
+.P
+After this operation has been performed on a key, attempts to access it will
+meet with error
+.BR EKEYREVOKED .
+.P
+The caller must have
+.B write
+permission on a key to be able revoke it.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_revoke ()
+returns
+.BR 0 .
+On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The specified key does not exist.
+.TP
+.B EKEYREVOKED
+The key has already been revoked.
+.TP
+.B EACCES
+The named key exists, but is not
+.B writable
+by the calling process.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyctl_search.3 b/man/keyctl_search.3
new file mode 100644
index 0000000..f3e7bc5
--- /dev/null
+++ b/man/keyctl_search.3
@@ -0,0 +1,138 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_SEARCH 3 "4 May 2006" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_search \- search a keyring for a key
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_search(key_serial_t " keyring ", const char *" type ,
+.BI "const char *" description ", key_serial_t " destination ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_search ()
+recursively searches the
+.I keyring
+for a key of the specified
+.I type
+and
+.IR description .
+.P
+If found, the key will be attached to the
+.I destination
+keyring (if given), and its serial number will be returned.
+.P
+The source keyring must grant
+.B search
+permission to the caller, and for a key to be found, it must also grant
+.B search
+permission to the caller. Child keyrings will be only be recursively searched
+if they grant
+.B search
+permission to the caller as well.
+.P
+If the destination keyring is
+.BR zero ,
+no attempt will be made to forge a link to the key, and just the serial number
+will be returned.
+.P
+If the destination keyring is given, then the link may only be formed if the
+found key grants the caller
+.B link
+permission and the destination keyring grants the caller
+.B write
+permission.
+.P
+If the search is successful, and if the destination keyring already contains a
+link to a key that matches the specified
+.IR type " and " description ,
+then that link will be replaced by a link to the found key.
+.P
+The source keyring and destination keyring serial numbers may be those of
+valid keyrings to which the caller has appropriate permission, or they may be
+special keyring IDs:
+.TP
+.B KEY_SPEC_THREAD_KEYRING
+This specifies the caller's thread-specific keyring.
+.TP
+.B KEY_SPEC_PROCESS_KEYRING
+This specifies the caller's process-specific keyring.
+.TP
+.B KEY_SPEC_SESSION_KEYRING
+This specifies the caller's session-specific keyring.
+.TP
+.B KEY_SPEC_USER_KEYRING
+This specifies the caller's UID-specific keyring.
+.TP
+.B KEY_SPEC_USER_SESSION_KEYRING
+This specifies the caller's UID-session keyring.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_search ()
+returns the serial number of the key it found. On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+One of the keyrings doesn't exist, no key was found by the search, or the only
+key found by the search was a negative key.
+.TP
+.B ENOTDIR
+One of the keyrings is a valid key that isn't a keyring.
+.TP
+.B EKEYEXPIRED
+One of the keyrings has expired, or the only key found was expired.
+.TP
+.B EKEYREVOKED
+One of the keyrings has been revoked, or the only key found was revoked.
+.TP
+.B ENOMEM
+Insufficient memory to expand the destination keyring.
+.TP
+.B EDQUOT
+The key quota for this user would be exceeded by creating a link to the found
+key in the destination keyring.
+.TP
+.B EACCES
+The source keyring didn't grant
+.B search
+permission, the destination keyring didn't grant
+.B write
+permission or the found key didn't grant
+.B link
+permission to the caller.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+Although this is a Linux system call, it is not present in
+.I libc
+but can be found rather in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyctl_session_to_parent.3 b/man/keyctl_session_to_parent.3
new file mode 100644
index 0000000..829b3be
--- /dev/null
+++ b/man/keyctl_session_to_parent.3
@@ -0,0 +1,77 @@
+.\"
+.\" Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_SESSION_TO_PARENT 3 "20 Feb 2014" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_session_to_parent \- set the parent process's session keyring
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_session_to_parent();"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_session_to_parent ()
+changes the session keyring to which the calling process's parent subscribes
+to be the that of the calling process.
+.P
+The keyring must have
+.B link
+permission available to the calling process, the parent process must have the
+same UIDs/GIDs as the calling process, and the LSM must not reject the
+replacement. Furthermore, this may not be used to affect init or a kernel
+thread.
+.P
+Note that the replacement will not take immediate effect upon the parent
+process, but will rather be deferred to the next time it returns to userspace
+from kernel space.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_session_to_parent ()
+returns 0. On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOMEM
+Insufficient memory to create a key.
+.TP
+.B EPERM
+The credentials of the parent don't match those of the caller.
+.TP
+.B EACCES
+The named keyring exists, but is not
+.B linkable
+by the calling process.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7),
+.BR session\-keyring (7),
+.BR user\-session-keyring (7)
diff --git a/man/keyctl_set_reqkey_keyring.3 b/man/keyctl_set_reqkey_keyring.3
new file mode 100644
index 0000000..00da586
--- /dev/null
+++ b/man/keyctl_set_reqkey_keyring.3
@@ -0,0 +1,101 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_SET_REQKEY_KEYRING 3 "4 May 2006" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_set_reqkey_keyring \- set the implicit destination keyring
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_set_reqkey_keyring(int " reqkey_defl ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_set_reqkey_keyring ()
+sets the default destination for implicit key requests for the current thread
+and returns the old setting.
+.P
+After this operation has been issued, keys acquired by implicit key requests,
+such as might be performed by
+.BR open ()
+on an AFS or NFS filesystem, will be
+linked by default to the specified keyring by this function.
+.P
+The valid values of
+.I reqkey_defl
+are:
+.TP
+.B KEY_REQKEY_DEFL_NO_CHANGE
+This makes no change to the current setting.
+.TP
+.B KEY_REQKEY_DEFL_THREAD_KEYRING
+This makes the thread-specific keyring the default destination.
+.TP
+.B KEY_REQKEY_DEFL_PROCESS_KEYRING
+This makes the process-specific keyring the default destination.
+.TP
+.B KEY_REQKEY_DEFL_SESSION_KEYRING
+This makes the session keyring the default destination.
+.TP
+.B KEY_REQKEY_DEFL_USER_KEYRING
+This makes the UID-specific keyring the default destination.
+.TP
+.B KEY_REQKEY_DEFL_USER_SESSION_KEYRING
+This makes the UID-specific session keyring the default destination.
+.TP
+.B KEY_REQKEY_DEFL_DEFAULT
+This selects the default behaviour which is to use the thread-specific keyring
+if there is one, otherwise the process-specific keyring if there is one,
+otherwise the session keyring if there is one, otherwise the UID-specific
+session keyring.
+.P
+This setting is inherited across
+.BR fork ()
+and
+.BR exec ().
+
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_set_reqkey_keyring ()
+returns
+.BR 0 .
+On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B EINVAL
+The value of
+.I reqkey_defl
+is invalid.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyutils (7),
+.BR keyrings (7),
+.BR request\-key (8)
diff --git a/man/keyctl_set_timeout.3 b/man/keyctl_set_timeout.3
new file mode 100644
index 0000000..1b7ec94
--- /dev/null
+++ b/man/keyctl_set_timeout.3
@@ -0,0 +1,81 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_SET_TIMEOUT 3 "4 May 2006" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_set_timeout \- set the expiration timer on a key
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_set_timeout(key_serial_t " key ", unsigned " timeout ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_set_timeout ()
+sets the expiration timer on a key to
+.I timeout
+seconds into the future. Setting
+.I timeout
+to
+.B zero
+cancels the expiration, assuming the key hasn't already expired.
+.P
+When the key expires, further attempts to access it will be met with error
+.BR EKEYEXPIRED .
+.P
+The caller must have
+.B setattr
+permission on a key to be able change its permissions mask.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_set_timeout ()
+returns
+.B 0 .
+On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The specified key does not exist.
+.TP
+.B EKEYEXPIRED
+The specified key has already expired.
+.TP
+.B EKEYREVOKED
+The specified key has been revoked.
+.TP
+.B EACCES
+The named key exists, but does not grant
+.B setattr
+permission to the calling process.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyctl_setperm.3 b/man/keyctl_setperm.3
new file mode 100644
index 0000000..0a4426d
--- /dev/null
+++ b/man/keyctl_setperm.3
@@ -0,0 +1,130 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_SETPERM 3 "4 May 2006" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_setperm \- change the permissions mask on a key
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_setperm(key_serial_t " key ", key_perm_t " perm ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_setperm ()
+changes the permissions mask on a key.
+.P
+A process that does not have the
+.B SysAdmin
+capability may not change the permissions mask on a key that doesn't have the
+same UID as the caller.
+.P
+The caller must have
+.B setattr
+permission on a key to be able change its permissions mask.
+.P
+The permissions mask is a bitwise-OR of the following flags:
+.TP
+.B KEY_xxx_VIEW
+Grant permission to view the attributes of a key.
+.TP
+.B KEY_xxx_READ
+Grant permission to read the payload of a key or to list a keyring.
+.TP
+.B KEY_xxx_WRITE
+Grant permission to modify the payload of a key or to add or remove links
+to/from a keyring.
+.TP
+.B KEY_xxx_SEARCH
+Grant permission to find a key or to search a keyring.
+.TP
+.B KEY_xxx_LINK
+Grant permission to make links to a key.
+.TP
+.B KEY_xxx_SETATTR
+Grant permission to change the ownership and permissions attributes of a key.
+.TP
+.B KEY_xxx_ALL
+Grant all the above.
+.P
+The
+.RB ' xxx '
+in the above should be replaced by one of:
+.TP
+.B POS
+Grant the permission to a process that possesses the key (has it attached
+searchably to one of the process's keyrings).
+.TP
+.B USR
+Grant the permission to a process with the same UID as the key.
+.TP
+.B GRP
+Grant the permission to a process with the same GID as the key, or with a
+match for the key's GID amongst that process's Groups list.
+.TP
+.B OTH
+Grant the permission to any other process.
+.P
+Examples include:
+.BR KEY_POS_VIEW ", " KEY_USR_READ ", " KEY_GRP_SEARCH " and " KEY_OTH_ALL .
+.P
+User, group and other grants are exclusive: if a process qualifies in
+the 'user' category, it will not qualify in the 'groups' category; and if a
+process qualifies in either 'user' or 'groups' then it will not qualify in
+the 'other' category.
+.P
+Possessor grants are cumulative with the grants from the 'user', 'groups'
+and 'other' categories.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_setperm ()
+returns
+.B 0 .
+On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The specified key does not exist.
+.TP
+.B EKEYEXPIRED
+The specified key has expired.
+.TP
+.B EKEYREVOKED
+The specified key has been revoked.
+.TP
+.B EACCES
+The named key exists, but does not grant
+.B setattr
+permission to the calling process.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyctl_update.3 b/man/keyctl_update.3
new file mode 100644
index 0000000..17fbdb4
--- /dev/null
+++ b/man/keyctl_update.3
@@ -0,0 +1,96 @@
+.\"
+.\" Copyright (C) 2006 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH KEYCTL_UPDATE 3 "4 May 2006" Linux "Linux Key Management Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyctl_update \- update a key
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "long keyctl_update(key_serial_t " key ", const void *" payload ,
+.BI "size_t " plen ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR keyctl_update ()
+updates the payload of a key if the key type permits it.
+.P
+The caller must have
+.B write
+permission on a key to be able update it.
+.P
+.I payload
+and
+.I plen
+specify the data for the new payload.
+.I payload
+may be NULL and
+.I plen
+may be zero if the key type permits that. The key type may reject the data if
+it's in the wrong format or in some other way invalid.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+On success
+.BR keyctl_update ()
+returns
+.BR 0 .
+On error, the value
+.B -1
+will be returned and
+.I errno
+will have been set to an appropriate error.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+.TP
+.B ENOKEY
+The key specified is invalid.
+.TP
+.B EKEYEXPIRED
+The key specified has expired.
+.TP
+.B EKEYREVOKED
+The key specified had been revoked.
+.TP
+.B EINVAL
+The payload data was invalid.
+.TP
+.B ENOMEM
+Insufficient memory to store the new payload.
+.TP
+.B EDQUOT
+The key quota for this user would be exceeded by increasing the size of the
+key to accommodate the new payload.
+.TP
+.B EACCES
+The key exists, but is not
+.B writable
+by the calling process.
+.TP
+.B EOPNOTSUPP
+The key type does not support the update operation on its keys.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+This is a library function that can be found in
+.IR libkeyutils .
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR add_key (2),
+.BR keyctl (2),
+.BR request_key (2),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR keyutils (7)
diff --git a/man/keyutils.7 b/man/keyutils.7
new file mode 100644
index 0000000..e17253b
--- /dev/null
+++ b/man/keyutils.7
@@ -0,0 +1,105 @@
+.\"
+.\" Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" This program is free software; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public Licence
+.\" as published by the Free Software Foundation; either version
+.\" 2 of the Licence, or (at your option) any later version.
+.\"
+.TH KEYUTILS 7 "21 Feb 2014" Linux "Kernel key management"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+keyutils \- in-kernel key management utilities
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+The
+.B keyutils
+package is a library and a set of utilities for accessing the kernel
+\fBkeyrings\fP facility.
+.P
+A header file is supplied to provide the definitions and declarations required
+to access the library:
+.P
+.RS
+.B #include <keyutils.h>
+.RE
+.P
+To link with the library, the following:
+.P
+.RS
+.B \-lkeyutils
+.RE
+.P
+should be specified to the linker.
+.P
+Three system calls are provided:
+.TP
+.BR add_key (2)
+Supply a new key to the kernel.
+.TP
+.BR request_key (2)
+Find an existing key for use, or, optionally, create one if one does not exist.
+.TP
+.BR keyctl (2)
+Control a key in various ways. The library provides a variety of wrappers
+around this system call and those should be used rather than calling it
+directly.
+.P
+See the
+.BR add_key (2),
+.BR request_key (2),
+and
+.BR keyctl (2)
+manual pages for more information.
+.P
+The \fBkeyctl\fP() wrappers are listed on the
+.BR keyctl (3)
+manual page.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH UTILITIES
+.P
+A program is provided to interact with the kernel facility by a number of
+subcommands, e.g.:
+.P
+.RS
+.B keyctl add user foo bar @s
+.RE
+.P
+See the
+.BR keyctl (1)
+manual page for information on that.
+.P
+The kernel has the ability to upcall to userspace to fabricate new keys. This
+can be triggered by \fBrequest_key\fP(), but userspace is better off using
+\fBadd_key\fP() instead if it possibly can.
+.P
+The upcalling mechanism is usually routed via the
+.BR request\-key (8)
+program. What this does with any particular key is configurable in:
+.P
+.RS
+.I /etc/request\-key.conf
+.br
+.I /etc/request\-key.d/
+.RE
+.P
+See the
+.BR request\-key.conf (5)
+and the
+.BR request\-key (8)
+manual pages for more information.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR keyctl (3),
+.BR keyrings (7),
+.BR persistent\-keyring (7),
+.BR process\-keyring (7),
+.BR session\-keyring (7),
+.BR thread\-keyring (7),
+.BR user\-keyring (7),
+.BR user\-session\-keyring (7),
+.BR pam_keyinit (8)
diff --git a/man/recursive_key_scan.3 b/man/recursive_key_scan.3
new file mode 100644
index 0000000..8658002
--- /dev/null
+++ b/man/recursive_key_scan.3
@@ -0,0 +1,90 @@
+.\"
+.\" Copyright (C) 2011 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" This program is free software; you can redistribute it and/or
+.\" modify it under the terms of the GNU General Public Licence
+.\" as published by the Free Software Foundation; either version
+.\" 2 of the Licence, or (at your option) any later version.
+.\"
+.TH RECURSIVE_KEY_SCAN 3 "10 Mar 2011" Linux "Linux Key Utility Calls"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH NAME
+recursive_key_scan, recursive_session_key_scan \- apply a function to all keys in a keyring tree
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SYNOPSIS
+.nf
+.B #include <keyutils.h>
+.sp
+.BI "typedef int (*" recursive_key_scanner_t ")(key_serial_t " parent ,
+.BI " key_serial_t " key ", char *" desc ", int " desc_len ", void *" data ");"
+.sp
+.BI "long recursive_key_scan(key_serial_t " keyring ,
+.BI " recursive_key_scanner_t " func ", void *" data ");"
+.br
+.BI "long recursive_session_key_scan(recursive_key_scanner_t " func ,
+.BI " void *" data ");"
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH DESCRIPTION
+.BR recursive_key_scan ()
+performs a depth-first recursive scan of the specified
+.I keyring
+tree and applies
+.I func
+to every link found in the accessible keyrings in that tree.
+.I data
+is passed to each invocation of func.
+.P
+The return values of
+.I func
+are summed and returned as the overall return value. Errors are ignored.
+Inaccessible keyrings are not scanned, but links to them are still passed to
+func.
+.P
+.BR recursive_session_key_scan ()
+works exactly like
+.BR recursive_key_scan ()
+with the caller's session keyring specified as the starting keyring.
+.P
+The callback function is called for each link found in all the keyrings in the
+nominated tree and so may be called multiple times for a particular key if that
+key has multiple links to it.
+.P
+The callback function is passed the following parameters:
+.TP
+.B parent
+The keyring containing the link or 0 for the initial key.
+.TP
+.BR key
+The key to which the link points.
+.TP
+.BR desc " and " desc_len
+A pointer to the raw description and its length as retrieved with
+.BR keyctl_describe_alloc ().
+These will be NULL and \-1 respectively if the description couldn't be
+retrieved and
+.I errno
+will retain the error from
+.BR keyctl_describe_alloc ().
+.TP
+.B data
+The data passed to the scanner function.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH RETURN VALUE
+These functions return the sum of the results of the callback functions they
+invoke.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH ERRORS
+Ignored.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH LINKING
+When linking,
+.B \-lkeyutils
+should be specified to the linker.
+.\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (3),
+.BR keyctl_describe_alloc (3),
+.BR keyrings (7)
diff --git a/man/request-key.8 b/man/request-key.8
new file mode 100644
index 0000000..50a7506
--- /dev/null
+++ b/man/request-key.8
@@ -0,0 +1,61 @@
+.\"
+.\" Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH REQUEST-KEY 8 "15 Nov 2011" Linux "Linux Key Management Utilities"
+.SH NAME
+request\-key \- handle key instantiation callback requests from the kernel
+.SH SYNOPSIS
+\fB/sbin/request\-key \fR<op> <key> <uid> <gid> <threadring> <processring>
+ <sessionring> [<info>]
+.SH DESCRIPTION
+This program is invoked by the kernel when the kernel is asked for a key that
+it doesn't have immediately available. The kernel creates a partially set up
+key and then calls out to this program to instantiate it. It is not intended
+to be called directly.
+.PP
+However, for debugging purposes, it can be given some options on the command
+line:
+.IP \fB-d\fP
+Turn on debugging mode. In this mode, no attempts are made to access any keys
+and, if a handler program is selected, it won't be executed; instead, this
+program will print a message and exit 0.
+.IP \fB-D <description>\fP
+In debugging mode, use the proposed key description specified with this rather
+than the sample ("user;0;0;1f0000;debug:1234") built into the program.
+.IP \fB-l\fP
+Use configuration from the current directory. The program will use
+.IR request-key.d/* " and " request-key.conf
+from the current directory rather than from
+.IR /etc .
+.IP \fB-n\fP
+Don't log to the system log. Ordinarily, error messages and debugging messages
+will be copied to the system log - this will prevent that.
+.IP \fB-v\fP
+Turn on debugging output. This may be specified multiple times to produce
+increasing levels of verbosity.
+.IP \fB--version\fP
+Print the program version and exit.
+.SH ERRORS
+All errors will be logged to the syslog.
+.SH FILES
+.ul
+/etc/request\-key.d/*.conf
+.ul 0
+Individual configuration files.
+.P
+.ul
+/etc/request\-key.conf
+.ul 0
+Fallback configuration file.
+.SH SEE ALSO
+.ad l
+.nh
+.BR keyctl (1),
+.BR request\-key.conf (5),
+.BR keyrings (7)
diff --git a/man/request-key.conf.5 b/man/request-key.conf.5
new file mode 100644
index 0000000..276c771
--- /dev/null
+++ b/man/request-key.conf.5
@@ -0,0 +1,145 @@
+.\" -*- nroff -*-
+.\" Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
+.\" Written by David Howells (dhowells@redhat.com)
+.\"
+.\" 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
+.\" 2 of the License, or (at your option) any later version.
+.\"
+.TH REQUEST-KEY.CONF 5 "15 November 2011" Linux "Linux Key Management Utilities"
+.SH NAME
+request\-key.conf \- Instantiation handler configuration file
+.SH DESCRIPTION
+.P
+These files are used by the /sbin/request\-key program to determine which
+program it should run to instantiate a key.
+.P
+request\-key looks for the best match, reading all the following files:
+.IP
+ /etc/request\-key.d/*.conf
+.br
+ /etc/request\-key.conf
+.P
+If it doesn't find a match, it will return an error
+and the kernel will automatically negate the key.
+.P
+The best match is defined as the line with the shortest wildcard skips, ranking
+the columns in order left to right. If two lines have the same length skips,
+then the first read is the one taken.
+.P
+In the files, any blank line or line beginning with a hash mark '#' is
+considered to be a comment and ignored.
+.P
+All other lines are assumed to be command lines with a number of white space
+separated fields:
+.P
+<op> <type> <description> <callout\-info> <prog> <arg1> <arg2> ...
+.P
+The first four fields are used to match the parameters passed to request\-key by
+the kernel. \fIop\fR is the operation type; currently the only supported
+operation is "create".
+.P
+\fItype\fR, \fIdescription\fR and \fIcallout\-info\fR match the three
+parameters passed to \fBkeyctl request2\fR or the \fBrequest_key()\fR system
+call. Each of these may contain one asterisk '*' character as a wildcard
+anywhere within the string.
+.P
+Should a match be made, the program specified by <prog> will be exec'd. This
+must have a fully qualified path name. argv[0] will be set from the part of the
+program name that follows the last slash '/' character.
+.P
+If the program name is prefixed with a pipe bar character '|', then the program
+will be forked and exec'd attached to three pipes. The callout information will
+be piped to it on it's stdin and the intended payload data will be retrieved
+from its stdout. Anything sent to stderr will be posted in syslog. If the
+program exits 0, then /sbin/request\-key will attempt to instantiate the key
+with the data read from stdout. If it fails in any other way, then request\-key
+will attempt to execute the appropriate 'negate' operation command.
+.P
+The program arguments can be substituted with various macros. Only complete
+argument substitution is supported - macro substitutions can't be embedded. All
+macros begin with a percent character '%'. An argument beginning with two
+percent characters will have one of them discarded.
+.P
+The following macros are supported:
+.P
+.RS
+%o Operation type
+.br
+%k Key ID
+.br
+%t Key type
+.br
+%d Key description
+.br
+%c Callout information
+.br
+%u Key UID
+.br
+%g Key GID
+.br
+%T Requestor's thread keyring
+.br
+%P Requestor's process keyring
+.br
+%S Requestor's session keyring
+.RE
+.P
+There's another macro substitution too that permits the interpolation of the
+contents of a key:
+.P
+.RS
+%{<type>:<description>}
+.RE
+.P
+This performs a lookup for a key of the given type and description on the
+requestor's keyrings, and if found, substitutes the contents for the macro. If
+not found an error will be logged and the key under construction will be
+negated.
+.SH EXAMPLE
+.P
+A basic file will be installed in the /etc. This will contain two debugging
+lines that can be used to test the installation:
+.P
+.RS
+create user debug:* negate /bin/keyctl negate %k 30 %S
+.br
+create user debug:loop:* * |/bin/cat
+.br
+create user debug:* * /usr/share/keyutils/request\-key\-debug.sh %k %d %c %S
+.br
+negate * * * /bin/keyctl negate %k 30 %S
+.RE
+.P
+This is set up so that something like:
+.P
+.RS
+keyctl request2 user debug:xxxx negate
+.RE
+.P
+will create a negative user-defined key, something like:
+.P
+.RS
+keyctl request2 user debug:yyyy spoon
+.RE
+.P
+will create an instantiated user-defined key with "Debug spoon" as the payload,
+and something like:
+.P
+.RS
+keyctl request2 user debug:loop:zzzz abcdefghijkl
+.RE
+.P
+will create an instantiated user-defined key with the callout information as
+the payload.
+.SH FILES
+.ul
+/etc/request\-key.conf
+.ul 0
+.br
+.ul
+/etc/request\-key.d/*.conf
+.ul 0
+.SH SEE ALSO
+\fBkeyctl\fR(1), \fBrequest\-key.conf\fR(5)
diff --git a/request-key-debug.sh b/request-key-debug.sh
new file mode 100755
index 0000000..83af01d
--- /dev/null
+++ b/request-key-debug.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+###############################################################################
+#
+# Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+# 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
+# 2 of the License, or (at your option) any later version.
+#
+###############################################################################
+#
+# Request key debugging
+#
+# Call: request-key-debug.sh <keyid> <desc> <callout> <session-keyring>
+#
+
+echo RQDebug keyid: $1
+echo RQDebug desc: $2
+echo RQDebug callout: $3
+echo RQDebug session keyring: $4
+
+if [ "$3" != "neg" ]
+then
+ keyctl instantiate $1 "Debug $3" $4 || exit 1
+else
+ cat /proc/keys
+ echo keyctl negate $1 30 $4
+ keyctl negate $1 30 $4
+fi
+
+exit 0
diff --git a/request-key.c b/request-key.c
new file mode 100644
index 0000000..bf47c0a
--- /dev/null
+++ b/request-key.c
@@ -0,0 +1,955 @@
+/* request-key.c: hand a key request off to the appropriate process
+ *
+ * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * 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
+ * 2 of the License, or (at your option) any later version.
+ *
+ * /sbin/request-key <op> <key> <uid> <gid> <threadring> <processring> <sessionring> [<info>]
+ *
+ * Searches the specified session ring for a key indicating the command to run:
+ * type: "user"
+ * desc: "request-key:<op>"
+ * data: command name, e.g.: "/home/dhowells/request-key-create.sh"
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/select.h>
+#include <sys/wait.h>
+#include "keyutils.h"
+
+
+struct parameters {
+ key_serial_t key_id;
+ char *op;
+ char *key_type;
+ char *key_desc;
+ char *callout_info;
+ char *key;
+ char *uid;
+ char *gid;
+ char *thread_keyring;
+ char *process_keyring;
+ char *session_keyring;
+ int len;
+ int oplen;
+ int ktlen;
+ int kdlen;
+ int cilen;
+
+};
+
+static int verbosity;
+static int xlocaldirs;
+static int xnolog;
+static int debug_mode;
+static char conffile[PATH_MAX + 1];
+static int confline;
+static int norecurse;
+
+static char cmd[4096 + 2], cmd_conffile[PATH_MAX + 1];
+static unsigned int cmd_wildness[4] = { UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX };
+static int cmd_len, cmd_confline;
+
+static void lookup_action(struct parameters *params)
+ __attribute__((noreturn));
+static void scan_conf_dir(struct parameters *params, const char *confdir);
+static void scan_conf_file(struct parameters *params, int dirfd, const char *conffile);
+
+static void execute_program(struct parameters *params,
+ char *cmdline)
+ __attribute__((noreturn));
+
+static void pipe_to_program(struct parameters *params,
+ char *prog,
+ char **argv)
+ __attribute__((noreturn));
+
+static int match(const char *pattern, int plen, const char *datum, int dlen,
+ unsigned int *wildness);
+
+static void debug(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+static void debug(const char *fmt, ...)
+{
+ va_list va;
+
+ if (verbosity) {
+ va_start(va, fmt);
+ vfprintf(stderr, fmt, va);
+ va_end(va);
+
+ if (!xnolog) {
+ openlog("request-key", 0, LOG_AUTHPRIV);
+
+ va_start(va, fmt);
+ vsyslog(LOG_DEBUG, fmt, va);
+ va_end(va);
+
+ closelog();
+ }
+ }
+}
+
+static void error(const char *fmt, ...) __attribute__((noreturn, format(printf, 1, 2)));
+static void error(const char *fmt, ...)
+{
+ va_list va;
+
+ if (verbosity) {
+ va_start(va, fmt);
+ vfprintf(stderr, fmt, va);
+ va_end(va);
+ }
+
+ if (!xnolog) {
+ openlog("request-key", 0, LOG_AUTHPRIV);
+
+ va_start(va, fmt);
+ vsyslog(LOG_ERR, fmt, va);
+ va_end(va);
+
+ closelog();
+ }
+
+ exit(1);
+}
+
+#define file_error(FMT, ...) error("%s: "FMT, conffile, ## __VA_ARGS__)
+#define line_error(FMT, ...) error("%s:%d: "FMT, conffile, confline, ## __VA_ARGS__)
+
+static void oops(int x)
+{
+ error("Died on signal %d", x);
+}
+
+/*****************************************************************************/
+/*
+ *
+ */
+int main(int argc, char *argv[])
+{
+ struct parameters params;
+ char *test_desc = "user;0;0;1f0000;debug:1234";
+ char *buf;
+ int ret, ntype, dpos, n, fd, opt;
+
+ if (argc == 2 && strcmp(argv[1], "--version") == 0) {
+ printf("request-key from %s (Built %s)\n",
+ keyutils_version_string, keyutils_build_string);
+ return 0;
+ }
+
+ signal(SIGSEGV, oops);
+ signal(SIGBUS, oops);
+ signal(SIGPIPE, SIG_IGN);
+
+ while (opt = getopt(argc, argv, "D:dlnv"),
+ opt != -1) {
+ switch (opt) {
+ case 'D':
+ test_desc = optarg;
+ break;
+ case 'd':
+ debug_mode = 1;
+ break;
+ case 'l':
+ xlocaldirs = 1;
+ break;
+ case 'n':
+ xnolog = 1;
+ break;
+ case 'v':
+ verbosity++;
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 7 && argc != 8)
+ error("Unexpected argument count: %d\n", argc);
+
+ fd = open("/dev/null", O_RDWR);
+ if (fd < 0)
+ error("open");
+ if (fd > 2) {
+ close(fd);
+ }
+ else if (fd < 2) {
+ ret = dup(fd);
+ if (ret < 0)
+ error("dup failed: %m\n");
+
+ if (ret < 2 && dup(fd) < 0)
+ error("dup failed: %m\n");
+ }
+
+ params.op = argv[0];
+ params.key = argv[1];
+ params.uid = argv[2];
+ params.gid = argv[3];
+ params.thread_keyring = argv[4];
+ params.process_keyring = argv[5];
+ params.session_keyring = argv[6];
+ params.callout_info = argv[7];
+
+ params.key_id = atoi(params.key);
+
+ /* assume authority over the key
+ * - older kernel doesn't support this function
+ */
+ if (!debug_mode) {
+ ret = keyctl_assume_authority(params.key_id);
+ if (ret < 0 && !(argc == 8 || errno == EOPNOTSUPP))
+ error("Failed to assume authority over key %d (%m)\n",
+ params.key_id);
+ }
+
+ /* ask the kernel to describe the key to us */
+ if (!debug_mode) {
+ ret = keyctl_describe_alloc(params.key_id, &buf);
+ if (ret < 0)
+ goto inaccessible;
+ } else {
+ buf = strdup(test_desc);
+ }
+
+ /* extract the type and description from the key */
+ debug("Key descriptor: \"%s\"\n", buf);
+ ntype = -1;
+ dpos = -1;
+
+ n = sscanf(buf, "%*[^;]%n;%*d;%*d;%x;%n", &ntype, &n, &dpos);
+ if (n != 1)
+ error("Failed to parse key description\n");
+
+ params.key_type = buf;
+ params.key_type[ntype] = 0;
+ params.key_desc = buf + dpos;
+
+ debug("Key type: %s\n", params.key_type);
+ debug("Key desc: %s\n", params.key_desc);
+
+ /* get hold of the callout info */
+ if (!params.callout_info) {
+ void *tmp;
+
+ if (keyctl_read_alloc(KEY_SPEC_REQKEY_AUTH_KEY, &tmp) < 0)
+ error("Failed to retrieve callout info (%m)\n");
+
+ params.callout_info = tmp;
+ }
+
+ debug("CALLOUT: '%s'\n", params.callout_info);
+
+ /* determine the action to perform */
+ params.oplen = strlen(params.op);
+ params.ktlen = strlen(params.key_type);
+ params.kdlen = strlen(params.key_desc);
+ params.cilen = strlen(params.callout_info);
+ lookup_action(&params);
+
+inaccessible:
+ error("Key %d is inaccessible (%m)\n", params.key_id);
+
+} /* end main() */
+
+/*****************************************************************************/
+/*
+ * determine the action to perform
+ */
+static void lookup_action(struct parameters *params)
+{
+ if (!xlocaldirs) {
+ scan_conf_dir(params, "/etc/request-key.d");
+ scan_conf_file(params, AT_FDCWD, "/etc/request-key.conf");
+ } else {
+ scan_conf_dir(params, "request-key.d");
+ scan_conf_file(params, AT_FDCWD, "request-key.conf");
+ }
+
+ if (cmd_len > 0)
+ execute_program(params, cmd);
+
+ file_error("No matching action\n");
+}
+
+/*****************************************************************************/
+/*
+ * Scan the files in a configuration directory.
+ */
+static void scan_conf_dir(struct parameters *params, const char *confdir)
+{
+ struct dirent *d;
+ DIR *dir;
+ int l;
+
+ debug("__ SCAN %s __\n", confdir);
+
+ dir = opendir(confdir);
+ if (!dir) {
+ if (errno == ENOENT)
+ return;
+ error("Cannot open %s: %m\n", confdir);
+ }
+
+ while ((d = readdir(dir))) {
+ if (d->d_name[0] == '.')
+ continue;
+ if (d->d_type != DT_UNKNOWN && d->d_type != DT_REG)
+ continue;
+ l = strlen(d->d_name);
+ if (l < 5)
+ continue;
+ if (memcmp(d->d_name + l - 5, ".conf", 5) != 0)
+ continue;
+ scan_conf_file(params, dirfd(dir), d->d_name);
+ }
+
+ closedir(dir);
+}
+
+/*****************************************************************************/
+/*
+ * Scan the contents of a configuration file.
+ */
+static void scan_conf_file(struct parameters *params, int dirfd, const char *conffile)
+{
+ char buf[4096 + 2], *p, *q;
+ FILE *conf;
+ int fd;
+
+ debug("__ read %s __\n", conffile);
+
+ fd = openat(dirfd, conffile, O_RDONLY);
+ if (fd < 0) {
+ if (errno == ENOENT)
+ return;
+ error("Cannot open %s: %m\n", conffile);
+ }
+
+ conf = fdopen(fd, "r");
+ if (!conf)
+ error("Cannot open %s: %m\n", conffile);
+
+ for (confline = 1;; confline++) {
+ unsigned int wildness[4] = {};
+ unsigned int len;
+
+ /* read the file line-by-line */
+ if (!fgets(buf, sizeof(buf), conf)) {
+ if (feof(conf))
+ break;
+ file_error("error %m\n");
+ }
+
+ len = strlen(buf);
+ if (len >= sizeof(buf) - 2)
+ line_error("Line too long\n");
+
+ /* ignore blank lines and comments */
+ if (len == 1 || buf[0] == '#' || isspace(buf[0]))
+ continue;
+
+ buf[--len] = 0;
+ p = buf;
+
+ /* attempt to match the op */
+ q = p;
+ while (*p && !isspace(*p)) p++;
+ if (!*p)
+ goto syntax_error;
+ *p = 0;
+
+ if (!match(q, p - q, params->op, params->oplen, &wildness[0]))
+ continue;
+
+ p++;
+
+ /* attempt to match the type */
+ while (isspace(*p)) p++;
+ if (!*p)
+ goto syntax_error;
+
+ q = p;
+ while (*p && !isspace(*p)) p++;
+ if (!*p)
+ goto syntax_error;
+ *p = 0;
+
+ if (!match(q, p - q, params->key_type, params->ktlen, &wildness[1]))
+ continue;
+
+ p++;
+
+ /* attempt to match the description */
+ while (isspace(*p)) p++;
+ if (!*p)
+ goto syntax_error;
+
+ q = p;
+ while (*p && !isspace(*p)) p++;
+ if (!*p)
+ goto syntax_error;
+ *p = 0;
+
+ if (!match(q, p - q, params->key_desc, params->kdlen, &wildness[2]))
+ continue;
+
+ p++;
+
+ /* attempt to match the callout info */
+ while (isspace(*p)) p++;
+ if (!*p)
+ goto syntax_error;
+
+ q = p;
+ while (*p && !isspace(*p)) p++;
+ if (!*p)
+ goto syntax_error;
+ *p = 0;
+
+ if (!match(q, p - q, params->callout_info, params->cilen, &wildness[3]))
+ continue;
+
+ p++;
+
+ /* we've got a match */
+ while (isspace(*p)) p++;
+ if (!*p)
+ goto syntax_error;
+
+ debug("%s:%d: Line matches '%s' (%u,%u,%u,%u)\n",
+ conffile, confline, p,
+ wildness[0], wildness[1], wildness[2], wildness[3]);
+
+ if (wildness[0] < cmd_wildness[0] ||
+ (wildness[0] == cmd_wildness[0] &&
+ wildness[1] < cmd_wildness[1]) ||
+ (wildness[0] == cmd_wildness[0] &&
+ wildness[1] == cmd_wildness[1] &&
+ wildness[2] < cmd_wildness[2]) ||
+ (wildness[0] == cmd_wildness[0] &&
+ wildness[1] == cmd_wildness[1] &&
+ wildness[2] == cmd_wildness[2] &&
+ wildness[3] < cmd_wildness[3])
+ ) {
+ memcpy(cmd_wildness, wildness, sizeof(cmd_wildness));
+ cmd_len = len - (p - buf);
+ cmd_confline = confline;
+ debug("%s:%d: Prefer command (%u,%u,%u,%u)\n",
+ conffile, confline,
+ wildness[0], wildness[1], wildness[2], wildness[3]);
+ memcpy(cmd, p, cmd_len + 1);
+ strcpy(cmd_conffile, conffile);
+ }
+ }
+
+ fclose(conf);
+ return;
+
+syntax_error:
+ line_error("Syntax error\n");
+}
+
+/*****************************************************************************/
+/*
+ * attempt to match a datum to a pattern
+ * - one asterisk is allowed anywhere in the pattern to indicate a wildcard
+ * - returns true if matched, false if not
+ * - adds the total number of chars skipped by wildcard to *_wildness
+ */
+static int match(const char *pattern, int plen, const char *datum, int dlen,
+ unsigned int *_wildness)
+{
+ const char *asterisk;
+ int n;
+
+ if (verbosity >= 2)
+ debug("match(%*.*s,%*.*s)", plen, plen, pattern, dlen, dlen, datum);
+
+ asterisk = memchr(pattern, '*', plen);
+ if (!asterisk) {
+ /* exact match only if no wildcard */
+ if (plen == dlen && memcmp(pattern, datum, dlen) == 0)
+ goto yes;
+ goto no;
+ }
+
+ /* the datum mustn't be shorter than the pattern without the asterisk */
+ if (dlen < plen - 1)
+ goto no;
+
+ n = asterisk - pattern;
+ if (n == 0) {
+ /* wildcard at beginning of pattern */
+ pattern++;
+ if (!*pattern)
+ goto yes_wildcard; /* "*" matches everything */
+
+ /* match the end of the datum */
+ if (memcmp(pattern, datum + (dlen - (plen - 1)), plen - 1) == 0)
+ goto yes_wildcard;
+ goto no;
+ }
+
+ /* need to match beginning of datum for "abc*" and "abc*def" */
+ if (memcmp(pattern, datum, n) != 0)
+ goto no;
+
+ if (!asterisk[1])
+ goto yes_wildcard; /* "abc*" matches */
+
+ /* match the end of the datum */
+ asterisk++;
+ n = plen - n - 1;
+ if (memcmp(pattern, datum + (dlen - n), n) == 0)
+ goto yes_wildcard;
+
+no:
+ if (verbosity >= 2)
+ debug(" = no\n");
+ return 0;
+
+yes:
+ if (verbosity >= 2)
+ debug(" = yes (w=0)\n");
+ return 1;
+
+yes_wildcard:
+ *_wildness += dlen - (plen - 1);
+ if (verbosity >= 2)
+ debug(" = yes (w=%u)\n", dlen - (plen - 1));
+ return 1;
+
+} /* end match() */
+
+/*****************************************************************************/
+/*
+ * execute a program to deal with a key
+ */
+static void execute_program(struct parameters *params, char *cmdline)
+{
+ char *argv[256];
+ char *prog, *p, *q;
+ int argc, pipeit;
+
+ debug("execute_program('%s','%s')\n", params->callout_info, cmdline);
+
+ /* if the commandline begins with a bar, then we pipe the callout data into it and read
+ * back the payload data
+ */
+ pipeit = 0;
+
+ if (cmdline[0] == '|') {
+ pipeit = 1;
+ cmdline++;
+ }
+
+ /* extract the path to the program to run */
+ prog = p = cmdline;
+ while (*p && !isspace(*p)) p++;
+// if (!*p)
+// line_error("No command path\n");
+// *p++ = 0;
+ if (*p)
+ *p++ = 0;
+
+ argv[0] = strrchr(prog, '/') + 1;
+
+ /* extract the arguments */
+ for (argc = 1; p; argc++) {
+ while (isspace(*p)) p++;
+ if (!*p)
+ break;
+
+ if (argc >= 254)
+ line_error("Too many arguments\n");
+ argv[argc] = q = p;
+
+ while (*p && !isspace(*p)) p++;
+
+ if (*p)
+ *p++ = 0;
+ else
+ p = NULL;
+
+ debug("argv[%d]: '%s'\n", argc, argv[argc]);
+
+ if (*q != '%')
+ continue;
+
+ /* it's a macro */
+ q++;
+ if (!*q)
+ line_error("Missing macro name\n");
+
+ if (*q == '%') {
+ /* it's actually an anti-macro escape "%%..." -> "%..." */
+ argv[argc]++;
+ continue;
+ }
+
+ /* single character macros */
+ if (!q[1]) {
+ switch (*q) {
+ case 'o': argv[argc] = params->op; continue;
+ case 'k': argv[argc] = params->key; continue;
+ case 't': argv[argc] = params->key_type; continue;
+ case 'd': argv[argc] = params->key_desc; continue;
+ case 'c': argv[argc] = params->callout_info; continue;
+ case 'u': argv[argc] = params->uid; continue;
+ case 'g': argv[argc] = params->gid; continue;
+ case 'T': argv[argc] = params->thread_keyring; continue;
+ case 'P': argv[argc] = params->process_keyring; continue;
+ case 'S': argv[argc] = params->session_keyring; continue;
+ default:
+ line_error("Unsupported macro\n");
+ }
+ }
+
+ /* keysub macro */
+ if (*q == '{') {
+ key_serial_t keysub;
+ void *tmp;
+ char *ksdesc, *end, *subdata;
+ int ret, loop;
+
+ /* extract type and description */
+ q++;
+ ksdesc = strchr(q, ':');
+ if (!ksdesc)
+ line_error("Keysub macro lacks ':'\n");
+ *ksdesc++ = 0;
+ end = strchr(ksdesc, '}');
+ if (!end)
+ line_error("Unterminated keysub macro\n");
+
+ *end++ = 0;
+ if (*end)
+ line_error("Keysub macro has trailing rubbish\n");
+
+ debug("Keysub: %s key \"%s\"\n", q, ksdesc);
+
+ if (!q[0])
+ line_error("Keysub type empty\n");
+
+ if (!ksdesc[0])
+ line_error("Keysub description empty\n");
+
+ /* look up the key in the requestor's keyrings, but fail immediately if the
+ * key is not found rather than invoking /sbin/request-key again
+ */
+ keysub = request_key(q, ksdesc, NULL, 0);
+ if (keysub < 0)
+ line_error("Keysub key not found: %m\n");
+
+ ret = keyctl_read_alloc(keysub, &tmp);
+ if (ret < 0)
+ line_error("Can't read keysub %d data: %m\n", keysub);
+ subdata = tmp;
+
+ for (loop = 0; loop < ret; loop++)
+ if (!isprint(subdata[loop]))
+ error("keysub %d data not printable ('%02hhx')\n",
+ keysub, subdata[loop]);
+
+ argv[argc] = subdata;
+ continue;
+ }
+ }
+
+ if (argc == 0)
+ line_error("No arguments\n");
+
+ argv[argc] = NULL;
+
+ if (verbosity) {
+ char **ap;
+
+ debug("%s %s\n", pipeit ? "PipeThru" : "Run", prog);
+ for (ap = argv; *ap; ap++)
+ debug("- argv[%td] = \"%s\"\n", ap - argv, *ap);
+ }
+
+ /* become the same UID/GID as the key requesting process */
+ //setgid(atoi(xuid));
+ //setuid(atoi(xgid));
+
+ /* if the last argument is a single bar, we spawn off the program dangling on the end of
+ * three pipes and read the key material from the program, otherwise we just exec
+ */
+ if (debug_mode) {
+ printf("-- exec disabled --\n");
+ exit(0);
+ }
+
+ if (pipeit)
+ pipe_to_program(params, prog, argv);
+
+ /* attempt to execute the command */
+ execv(prog, argv);
+
+ line_error("Failed to execute '%s': %m\n", prog);
+
+} /* end execute_program() */
+
+/*****************************************************************************/
+/*
+ * pipe the callout information to the specified program and retrieve the
+ * payload data over another pipe
+ */
+static void pipe_to_program(struct parameters *params, char *prog, char **argv)
+{
+ char errbuf[512], payload[32768 + 1], *pp, *pc, *pe;
+ int ipi[2], opi[2], epi[2], childpid;
+ int ifl, ofl, efl, npay, ninfo, espace, tmp;
+
+ debug("pipe_to_program(%s -> %s)", params->callout_info, prog);
+
+ if (pipe(ipi) < 0 || pipe(opi) < 0 || pipe(epi) < 0)
+ error("pipe failed: %m");
+
+ childpid = fork();
+ if (childpid == -1)
+ error("fork failed: %m");
+
+ if (childpid == 0) {
+ /* child process */
+ if (dup2(ipi[0], 0) < 0 ||
+ dup2(opi[1], 1) < 0 ||
+ dup2(epi[1], 2) < 0)
+ error("dup2 failed: %m");
+ close(ipi[0]);
+ close(ipi[1]);
+ close(opi[0]);
+ close(opi[1]);
+ close(epi[0]);
+ close(epi[1]);
+
+ execv(prog, argv);
+ line_error("Failed to execute '%s': %m\n", prog);
+ }
+
+ /* parent process */
+ close(ipi[0]);
+ close(opi[1]);
+ close(epi[1]);
+
+#define TOSTDIN ipi[1]
+#define FROMSTDOUT opi[0]
+#define FROMSTDERR epi[0]
+
+ ifl = fcntl(TOSTDIN, F_GETFL);
+ ofl = fcntl(FROMSTDOUT, F_GETFL);
+ efl = fcntl(FROMSTDERR, F_GETFL);
+ if (ifl < 0 || ofl < 0 || efl < 0)
+ error("fcntl/F_GETFL failed: %m");
+
+ ifl |= O_NONBLOCK;
+ ofl |= O_NONBLOCK;
+ efl |= O_NONBLOCK;
+
+ if (fcntl(TOSTDIN, F_SETFL, ifl) < 0 ||
+ fcntl(FROMSTDOUT, F_SETFL, ofl) < 0 ||
+ fcntl(FROMSTDERR, F_SETFL, efl) < 0)
+ error("fcntl/F_SETFL failed: %m");
+
+ ninfo = params->cilen;
+ pc = params->callout_info;
+
+ npay = sizeof(payload);
+ pp = payload;
+
+ espace = sizeof(errbuf);
+ pe = errbuf;
+
+ do {
+ fd_set rfds, wfds;
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+
+ if (TOSTDIN != -1) {
+ if (ninfo > 0) {
+ FD_SET(TOSTDIN, &wfds);
+ }
+ else {
+ close(TOSTDIN);
+ TOSTDIN = -1;
+ continue;
+ }
+ }
+
+ if (FROMSTDOUT != -1)
+ FD_SET(FROMSTDOUT, &rfds);
+
+ if (FROMSTDERR != -1)
+ FD_SET(FROMSTDERR, &rfds);
+
+ tmp = TOSTDIN > FROMSTDOUT ? TOSTDIN : FROMSTDOUT;
+ tmp = tmp > FROMSTDERR ? tmp : FROMSTDERR;
+ tmp++;
+
+ debug("select r=%d,%d w=%d m=%d\n", FROMSTDOUT, FROMSTDERR, TOSTDIN, tmp);
+
+ tmp = select(tmp, &rfds, &wfds, NULL, NULL);
+ if (tmp < 0)
+ error("select failed: %m\n");
+
+ if (TOSTDIN != -1 && FD_ISSET(TOSTDIN, &wfds)) {
+ tmp = write(TOSTDIN, pc, ninfo);
+ if (tmp < 0) {
+ if (errno != EPIPE)
+ error("write failed: %m\n");
+
+ debug("EPIPE");
+ ninfo = 0;
+ }
+ else {
+ debug("wrote %d\n", tmp);
+
+ pc += tmp;
+ ninfo -= tmp;
+ }
+ }
+
+ if (FROMSTDOUT != -1 && FD_ISSET(FROMSTDOUT, &rfds)) {
+ tmp = read(FROMSTDOUT, pp, npay);
+ if (tmp < 0)
+ error("read failed: %m\n");
+
+ debug("read %d\n", tmp);
+
+ if (tmp == 0) {
+ close(FROMSTDOUT);
+ FROMSTDOUT = -1;
+ }
+ else {
+ pp += tmp;
+ npay -= tmp;
+
+ if (npay == 0)
+ error("Too much data read from query program\n");
+ }
+ }
+
+ if (FROMSTDERR != -1 && FD_ISSET(FROMSTDERR, &rfds)) {
+ char *nl;
+
+ tmp = read(FROMSTDERR, pe, espace);
+ if (tmp < 0)
+ error("read failed: %m\n");
+
+ debug("read err %d\n", tmp);
+
+ if (tmp == 0) {
+ close(FROMSTDERR);
+ FROMSTDERR = -1;
+ continue;
+ }
+
+ pe += tmp;
+ espace -= tmp;
+
+ while ((nl = memchr(errbuf, '\n', pe - errbuf))) {
+ int n, rest;
+
+ nl++;
+ n = nl - errbuf;
+
+ if (verbosity)
+ fprintf(stderr, "Child: %*.*s", n, n, errbuf);
+
+ if (!xnolog) {
+ openlog("request-key", 0, LOG_AUTHPRIV);
+ syslog(LOG_ERR, "Child: %*.*s", n, n, errbuf);
+ closelog();
+ }
+
+ rest = pe - nl;
+ if (rest > 0) {
+ memmove(errbuf, nl, rest);
+ pe -= n;
+ espace += n;
+ }
+ else {
+ pe = errbuf;
+ espace = sizeof(errbuf);
+ }
+ }
+
+ if (espace == 0) {
+ int n = sizeof(errbuf);
+
+ if (verbosity)
+ fprintf(stderr, "Child: %*.*s", n, n, errbuf);
+
+ if (!xnolog) {
+ openlog("request-key", 0, LOG_AUTHPRIV);
+ syslog(LOG_ERR, "Child: %*.*s", n, n, errbuf);
+ closelog();
+ }
+
+ pe = errbuf;
+ espace = sizeof(errbuf);
+ }
+ }
+
+ } while (TOSTDIN != -1 || FROMSTDOUT != -1 || FROMSTDERR != -1);
+
+ /* wait for the program to exit */
+ if (waitpid(childpid, &tmp, 0) != childpid)
+ error("wait for child failed: %m\n");
+
+ /* if the process exited non-zero or died on a signal, then we call back in to ourself to
+ * decide on negation
+ * - this is not exactly beautiful but the quickest way of having configurable negation
+ * settings
+ */
+ if (WIFEXITED(tmp) && WEXITSTATUS(tmp) != 0) {
+ if (norecurse)
+ error("child exited %d\n", WEXITSTATUS(tmp));
+
+ norecurse = 1;
+ debug("child exited %d\n", WEXITSTATUS(tmp));
+ params->op = "negate";
+ lookup_action(params);
+ }
+
+ if (WIFSIGNALED(tmp)) {
+ if (norecurse)
+ error("child died on signal %d\n", WTERMSIG(tmp));
+
+ norecurse = 1;
+ params->op = "negate";
+ lookup_action(params);
+ }
+
+ /* attempt to instantiate the key */
+ debug("instantiate with %td bytes\n", pp - payload);
+
+ if (keyctl_instantiate(params->key_id, payload, pp - payload, 0) < 0)
+ error("instantiate key failed: %m\n");
+
+ debug("instantiation successful\n");
+ exit(0);
+
+} /* end pipe_to_program() */
diff --git a/request-key.conf b/request-key.conf
new file mode 100644
index 0000000..ff16a95
--- /dev/null
+++ b/request-key.conf
@@ -0,0 +1,41 @@
+###############################################################################
+#
+# Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+# 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
+# 2 of the License, or (at your option) any later version.
+#
+###############################################################################
+
+
+###############################################################################
+#
+# We can run programs or scripts
+# - Macro substitutions in arguments:
+# %%... %...
+# %o operation name
+# %k ID of key being operated upon
+# %t type of key being operated upon
+# %d description of key being operated upon
+# %c callout info
+# %u UID of requestor
+# %g GID of requestor
+# %T thread keyring of requestor (may be 0)
+# %P process keyring of requestor (may be 0)
+# %S session keyring of requestor (may be the user's default session)
+#
+################################################################################
+
+#OP TYPE DESCRIPTION CALLOUT INFO PROGRAM ARG1 ARG2 ARG3 ...
+#====== ======= =============== =============== ===============================
+create dns_resolver * * /sbin/key.dns_resolver %k
+create user debug:* negate /bin/keyctl negate %k 30 %S
+create user debug:* rejected /bin/keyctl reject %k 30 %c %S
+create user debug:* expired /bin/keyctl reject %k 30 %c %S
+create user debug:* revoked /bin/keyctl reject %k 30 %c %S
+create user debug:loop:* * |/bin/cat
+create user debug:* * /usr/share/keyutils/request-key-debug.sh %k %d %c %S
+negate * * * /bin/keyctl negate %k 30 %S
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 0000000..838aea2
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,76 @@
+# Copyright (c) 2015 Red Hat, Inc. All rights reserved. This copyrighted material
+# is made available to anyone wishing to use, modify, copy, or
+# redistribute it subject to the terms and conditions of the GNU General
+# Public License v.2.
+#
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# Author: David Howells <dhowells@redhat.com>
+# Updated to new Make model by Bill Peck <bpeck@redhat.com>
+
+# The toplevel namespace within which the test lives.
+TOPLEVEL_NAMESPACE=/kernel
+
+# The name of the package under test:
+PACKAGE_NAME=keyutils
+
+# The path of the test below the package:
+RELATIVE_PATH=testsuite
+
+# Version of the Test. Used with make tag.
+export TESTVERSION=1.3
+
+# The combined namespace of the test.
+export TEST=$(TOPLEVEL_NAMESPACE)/$(PACKAGE_NAME)/$(RELATIVE_PATH)
+
+.PHONY: all install download clean
+
+TESTS = $(shell /usr/bin/find . -name runtest.sh | \
+ /bin/sed -e 's/runtest.sh//g;s/^\.\///;s/\/$$//')
+
+BUILT_FILES=
+FILES=$(METADATA) *.sh Makefile PURPOSE bugzillas keyctl
+
+run: $(FILES) build
+ bash runtest.sh $(TESTS)
+
+build: $(BUILT_FILES)
+
+clean:
+ rm -f *~ *.rpm $(BUILT_FILES)
+ find * -name test.out -delete
+
+# You may need to add other targets e.g. to build executables from source code
+# Add them here:
+
+
+# Include Common Makefile
+ifneq ($(wildcard /usr/share/rhts/lib/rhts-make.include),)
+include /usr/share/rhts/lib/rhts-make.include
+
+# Generate the testinfo.desc here:
+$(METADATA): Makefile
+ @touch $(METADATA)
+ @echo "Owner: David Howells <dhowells@redhat.com>" > $(METADATA)
+ @echo "Name: $(TEST)" >> $(METADATA)
+ @echo "Path: $(TEST_DIR)" >> $(METADATA)
+ @echo "License: Unknown" >> $(METADATA)
+ @echo "TestVersion: $(TESTVERSION)" >> $(METADATA)
+ @echo "Description: testsuite to verify keyutils ">> $(METADATA)
+ @echo "TestTime: 30m" >> $(METADATA)
+ @echo "RunFor: $(PACKAGE_NAME)" >> $(METADATA)
+ @echo "Requires: $(PACKAGE_NAME)" >> $(METADATA)
+ @echo "Requires: redhat-lsb-core" >> $(METADATA)
+ @echo "Releases: -RHEL2.1 -RHEL3" >> $(METADATA)
+ @echo "Type: Tier1" >> $(METADATA)
+ @echo "Type: KernelTier1" >> $(METADATA)
+ @echo "Destructive: no" >> $(METADATA)
+ rhts-lint $(METADATA)
+
+endif
diff --git a/tests/PURPOSE b/tests/PURPOSE
new file mode 100644
index 0000000..064e775
--- /dev/null
+++ b/tests/PURPOSE
@@ -0,0 +1,82 @@
+The purpose of this testsuite is to do negative and positive testing against
+the keyutils package.
+Sub Test Description
+------------------ -----------------------------------------------------
+/listing/noargs Check list/rlist subcommands fail with the wrong
+ number of arguments
+/listing/bad-args Check list/rlist subcommands fail with bad arguments
+/listing/valid Check list/rlist subcommands work
+/show/noargs Check show subcommand works with no arguments
+/reading/noargs Check read/pipe/print subcommands fail with the wrong
+ number of arguments
+/reading/bad-args Check read/pipe/print subcommands fail with bad
+ arguments
+/reading/valid Check read/pipe/print subcommands work
+/pupdate/noargs Check pupdate subcommand fails with the wrong number
+ of arguments
+/pupdate/bad-args Check pupdate subcommand fails with bad arguments
+/pupdate/userupdate Check pupdate subcommand works for user defined keys
+/newring/noargs Check newring subcommand fails with the wrong number
+ of arguments
+/newring/bad-args Check newring subcommand fails with a bad arguments
+/newring/valid Check newring subcommand works
+/session/bad-args Check session subcommand fails with bad arguments
+/session/valid Check session subcommand works
+/clear/noargs Check clear subcommand fails with the wrong number of
+ arguments
+/clear/bad-args Check clear subcommand fails with a bad arguments
+/clear/valid Check clear subcommand works
+/instantiating/noargs Check instantiate/negate subcommands fail with the
+ wrong number of arguments
+/instantiating/bad-args Check instantiate/negate subcommands fail with bad
+ arguments
+/permitting/noargs Check chown/chgrp/setperm subcommands fail with the
+ wrong number of arguments
+/permitting/bad-args Check chown/chgrp/setperm subcommands fail with bad
+ arguments
+/permitting/valid Check chown/chgrp/setperm subcommands work
+/describing/noargs Check describe/rdescribe subcommands fail with the
+ wrong number of arguments
+/describing/bad-args Check describe/rdescribe subcommands fail with bad
+ arguments
+/describing/valid Check describe/rdescribe subcommands work
+/noargs Check keyutils with no args gives format list
+/revoke/noargs Check revoke subcommand fails with the wrong number of
+ arguments
+/revoke/bad-args Check revoke subcommand fails with a bad arguments
+/revoke/valid Check revoke subcommand works
+/padd/noargs Check padd subcommand fails with the wrong number of
+ arguments
+/padd/bad-args Check padd subcommand fails with bad arguments
+/padd/useradd Check padd subcommand works
+/timeout/noargs Check timeout subcommand fails with the wrong number
+ of arguments
+/timeout/bad-args Check timeout subcommand fails with a bad arguments
+/timeout/valid Check timeout subcommand works
+/update/noargs Check update subcommand fails with the wrong number of
+ arguments
+/update/bad-args Check update subcommand fails with bad arguments
+/update/userupdate Check update subcommand works for user defined keys
+/search/noargs Check search subcommand fails with the wrong number of
+ arguments
+/search/bad-args Check search subcommand fails with a bad arguments
+/search/valid Check search subcommand works
+/link/recursion Check link subcommand handles recursive links correctly
+/link/noargs Check link subcommand fails with the wrong number of
+ arguments
+/link/bad-args Check link subcommand fails with bad arguments
+/link/valid Check link subcommand works
+/add/noargs Check add subcommand fails with the wrong number of
+ arguments
+/add/bad-args Check add subcommand fails with a bad arguments
+/add/useradd Check add subcommand works
+/requesting/piped Check request/prequest2 subcommands work
+/requesting/noargs Check request/request2 subcommands fail with the
+ wrong number of arguments
+/requesting/bad-args Check request/request2 subcommands fail with bad
+ arguments
+/requesting/valid Check request/request2 subcommands work
+/unlink/noargs Check unlink subcommand fails with the wrong number of
+ arguments
+/unlink/bad-args Check unlink subcommand fails with a bad arguments
+/unlink/valid Check unlink subcommand works
diff --git a/tests/bugzillas/bz1031154/runtest.sh b/tests/bugzillas/bz1031154/runtest.sh
new file mode 100644
index 0000000..d90afb4
--- /dev/null
+++ b/tests/bugzillas/bz1031154/runtest.sh
@@ -0,0 +1,104 @@
+#!/bin/bash
+
+# Test for https://bugzilla.redhat.com/show_bug.cgi?id=1031154
+
+. ../../prepare.inc.sh
+. ../../toolbox.inc.sh
+
+# We intentionally generate AVCs so the test system shouldn't fail us
+# because the AVCs were generated.
+export AVC_ERROR=+no_avc_check
+export RHTS_OPTION_STRONGER_AVC=
+
+# ---- do the actual testing ----
+
+result=PASS
+
+if [ $have_big_key_type = 0 ]
+then
+ toolbox_skip_test $TEST "SKIPPING TEST DUE TO LACK OF BIG_KEY TYPE"
+ exit 0
+fi
+
+require_selinux
+require_command getenforce
+require_command setenforce
+require_command runcon
+require_command ausearch
+
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# we need a reference time to scan the audit log from so as not to pick up old
+# results from this test.
+base_date=`date +"%x@%X"`
+base_time=${base_date#*@}
+base_date=${base_date%@*}
+sleep 1
+
+# reset the permissive audit log autocancel thing
+load_policy
+
+# we need to be in permissive mode
+marker "ENTER SELINUX PERMISSIVE MODE"
+
+mode=`getenforce`
+
+if [ "$mode" != "Permissive" ]
+then
+ echo setenforce Permissive >>$OUTPUTFILE
+ if ! setenforce Permissive
+ then
+ failed
+ fi
+fi
+
+# create a big key to probe
+marker "CREATE BIG KEY"
+pcreate_key_by_size 8192 big_key test-key @s
+expect_keyid id
+
+# check the big key is file backed and the right size
+marker "CHECK BIG KEY"
+xid=`printf %08x $id`
+
+pk=`cat /proc/keys | grep "^$xid.*test-key: 8192 \\[file\\]"`
+echo $pk >>$OUTPUTFILE
+if [ -z "$pk" ]
+then
+ echo "+++ Incorrectly created key" >>$OUTPUTFILE
+ cat /proc/keys | grep "^$xid" >>$OUTPUTFILE
+ failed
+fi
+
+# use a separate context to access the key
+marker "ACCESS INTERCONTEXT"
+
+echo runcon system_u:system_r:httpd_t:s0-s0:c0.c1023 keyctl print $id >>$OUTPUTFILE
+if ! runcon system_u:system_r:httpd_t:s0-s0:c0.c1023 keyctl print $id >/dev/null 2>>$OUTPUTFILE
+then
+ failed
+fi
+
+# examine the audit logs
+marker "EXAMINE AUDIT LOGS"
+
+echo ausearch -m AVC -i --subject httpd_t -ts $base_date $base_time \| audit2allow \| grep '-P "allow httpd_t user_tmpfs_t:file [{] (open |read )+[}];"' >>$OUTPUTFILE
+if ausearch -m AVC -i --subject httpd_t -ts $base_date $base_time 2>>$OUTPUTFILE | audit2allow 2>>$OUTPUTFILE | grep -P "allow httpd_t user_tmpfs_t:file [{] (open |read )+[}];"
+then
+ failed
+fi
+
+marker "RESTORE SELINUX MODE"
+if [ "$mode" != "Permissive" ]
+then
+ echo setenforce $mode >>$OUTPUTFILE
+ if ! setenforce $mode
+ then
+ failed
+ fi
+fi
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/bugzillas/bz1033467/runtest.sh b/tests/bugzillas/bz1033467/runtest.sh
new file mode 100644
index 0000000..54ec293
--- /dev/null
+++ b/tests/bugzillas/bz1033467/runtest.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+
+# Test for https://bugzilla.redhat.com/show_bug.cgi?id=1033467
+
+. ../../prepare.inc.sh
+. ../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "ADD SANDBOX KEYRING"
+create_keyring sandbox @s
+expect_keyid sandbox
+
+# create a bunch of nested keyrings in the sandbox
+marker "ADD NESTED KEYRINGS"
+declare -a ring
+for ((i=0; i<=16; i++))
+do
+ create_keyring ring$i $sandbox
+ expect_keyid "ring[$i]"
+done
+
+# create a key in each of those keyrings
+marker "ADD KEYS"
+keys=""
+for ((i=0; i<=16; i++))
+do
+ create_key user a$i a ${ring[$i]}
+ expect_keyid id
+ keys="$keys $id"
+done
+
+# search for the added keys, beginning at sandbox and exercising the nesting
+marker "SEARCH KEYS"
+keys2=""
+for ((i=0; i<=16; i++))
+do
+ search_for_key $sandbox user a$i
+ expect_keyid id
+ keys2="$keys2 $id"
+done
+
+marker "COMPARE KEY LISTS"
+if [ "$keys" != "$keys2" ]
+then
+ echo "Key lists differ" >>$OUTPUTFILE
+ echo List 1: "\"$keys\"" >>$OUTPUTFILE
+ echo List 2: "\"$keys2\"" >>$OUTPUTFILE
+ failed
+fi
+
+# search for some unadded keys and make sure we get an error
+marker "SEARCH MISSES"
+for ((i=17; i<=20; i++))
+do
+ search_for_key --fail $sandbox user a$i
+ expect_error ENOKEY
+done
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/bugzillas/bz1071346/runtest.sh b/tests/bugzillas/bz1071346/runtest.sh
new file mode 100644
index 0000000..45f0a02
--- /dev/null
+++ b/tests/bugzillas/bz1071346/runtest.sh
@@ -0,0 +1,79 @@
+#!/bin/bash
+
+# Test for https://bugzilla.redhat.com/show_bug.cgi?id=1071346
+
+. ../../prepare.inc.sh
+. ../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "ADD SANDBOX KEYRING"
+create_keyring sandbox @s
+expect_keyid sandbox
+
+# Add a second keyring of the same name into the sandbox
+marker "ADD SECOND SANDBOX KEYRING"
+create_keyring sandbox $sandbox
+expect_keyid second
+
+# Now try and link keyrings together in ways that should fail
+marker "CHECK NO LINK SESSION TO SECOND"
+link_key --fail @s $second
+expect_error EDEADLK
+marker "CHECK NO LINK SANDBOX TO SECOND"
+link_key --fail $sandbox $second
+expect_error EDEADLK
+marker "CHECK NO LINK SECOND TO SECOND"
+link_key --fail $second $second
+expect_error EDEADLK
+
+# Add another keyring into sandbox and stick a third sandbox keyring in that
+marker "ADD SIDE KEYRING"
+create_keyring side $sandbox
+expect_keyid side
+marker "ADD THIRD SANDBOX KEYRING"
+create_keyring sandbox $side
+expect_keyid third
+
+# Make sure we can't link the session keyring, the sandbox, the side
+# keyring or the third keyring itself into the third keyring.
+marker "CHECK NO LINK SESSION TO THIRD"
+link_key --fail @s $third
+expect_error EDEADLK
+marker "CHECK NO LINK SANDBOX TO THIRD"
+link_key --fail $sandbox $third
+expect_error EDEADLK
+marker "CHECK NO LINK SIDE TO THIRD"
+link_key --fail $side $third
+expect_error EDEADLK
+marker "CHECK NO LINK THIRD TO THIRD"
+link_key --fail $sandbox $third
+expect_error EDEADLK
+
+# We should, however, be able to link second to third but not then
+# third to second
+marker "CHECK LINK SECOND TO THIRD"
+link_key $second $third
+marker "CHECK NO LINK THIRD TO SECOND"
+link_key --fail $third $second
+expect_error EDEADLK
+
+# We can then detach the link we just made and check the reverse
+# linkage.
+marker "UNLINK SECOND FROM THIRD"
+unlink_key $second $third
+marker "CHECK LINK THIRD TO SECOND"
+link_key $third $second
+marker "CHECK NO LINK SECOND TO THIRD"
+link_key --fail $second $third
+expect_error EDEADLK
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/add/bad-args/runtest.sh b/tests/keyctl/add/bad-args/runtest.sh
new file mode 100644
index 0000000..38795fa
--- /dev/null
+++ b/tests/keyctl/add/bad-args/runtest.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that an empty key type fails correctly
+marker "CHECK EMPTY KEY TYPE"
+create_key --fail "" wibble stuff @p
+expect_error EINVAL
+
+# check that an unsupported key type fails correctly
+marker "CHECK UNSUPPORTED KEY TYPE"
+create_key --fail lizardsgizzards wibble stuff @p
+expect_error ENODEV
+
+# check that an invalid key type fails correctly
+marker "CHECK INVALID KEY TYPE"
+create_key --fail .user wibble stuff @p
+expect_error EPERM
+
+# check that an maximum length invalid key type fails correctly
+marker "CHECK MAXLEN KEY TYPE"
+create_key --fail $maxtype wibble stuff @p
+expect_error ENODEV
+
+# check that an overlong key type fails correctly
+marker "CHECK OVERLONG KEY TYPE"
+create_key --fail a$maxtype wibble stuff @p
+expect_error EINVAL
+
+# check that creation of a keyring with non-empty payload fails correctly
+marker "CHECK ADD KEYRING WITH PAYLOAD"
+create_key --fail keyring wibble a @p
+expect_error EINVAL
+
+# check that an max length key description works correctly (PAGE_SIZE inc NUL)
+if [ $PAGE_SIZE -lt $maxsquota ]
+then
+ marker "CHECK MAXLEN DESC"
+ create_key user $maxdesc stuff @p
+ expect_keyid keyid
+else
+ marker "CHECK MAXLEN DESC FAILS WITH EDQUOT"
+ create_key --fail user $maxdesc stuff @p
+ expect_error EDQUOT
+fi
+
+# This doesn't work on MIPS earler than 3.19 because of a kernel bug
+kver=`uname -r`
+kmch=`uname -m`
+if kernel_at_or_later_than 3.19 ||
+ [ "$kmch" != "mips" -a "$kmch" != "mips64" ]
+then
+ # check that an overlong key description fails correctly (>4095 inc NUL)
+ marker "CHECK OVERLONG DESC"
+ create_key --fail user a$maxdesc stuff @p
+ expect_error EINVAL
+fi
+
+# check that a bad key ID fails correctly
+marker "CHECK BAD KEY ID"
+create_key --fail user wibble stuff 0
+expect_error EINVAL
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/add/noargs/runtest.sh b/tests/keyctl/add/noargs/runtest.sh
new file mode 100644
index 0000000..d5abb7d
--- /dev/null
+++ b/tests/keyctl/add/noargs/runtest.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that no arguments fails correctly
+marker "ADD NO ARGS"
+expect_args_error keyctl add
+
+# check that one argument fails correctly
+marker "ADD ONE ARG"
+expect_args_error keyctl add user
+
+# check that two arguments fail correctly
+marker "ADD TWO ARGS"
+expect_args_error keyctl add user wibble
+
+# check that three arguments fail correctly
+marker "ADD THREE ARGS"
+expect_args_error keyctl add user wibble stuff
+
+# check that five arguments fail correctly
+marker "ADD FIVE ARGS"
+expect_args_error keyctl add user wibble stuff @s x
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/add/useradd/runtest.sh b/tests/keyctl/add/useradd/runtest.sh
new file mode 100644
index 0000000..031241c
--- /dev/null
+++ b/tests/keyctl/add/useradd/runtest.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that we can add a user key to the session keyring
+marker "ADD USER KEY"
+create_key user wibble stuff @s
+expect_keyid keyid
+
+# read back what we put in it
+marker "PRINT PAYLOAD"
+print_key $keyid
+expect_payload payload "stuff"
+
+# check that we can update a user key
+marker "UPDATE USER KEY"
+create_key user wibble lizard @s
+
+# check we get the same key ID back
+expect_keyid keyid2
+
+if [ "x$keyid" != "x$keyid2" ]
+then
+ failed
+fi
+
+# read back what we changed it to
+marker "PRINT UPDATED PAYLOAD"
+print_key $keyid
+expect_payload payload "lizard"
+
+# attempt to add a key to that non-keyring key
+marker "ADD KEY TO NON-KEYRING"
+create_key --fail user lizard gizzards $keyid
+expect_error ENOTDIR
+
+# remove the key we added
+marker "UNLINK KEY"
+unlink_key $keyid @s
+
+keyctl show
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/clear/bad-args/runtest.sh b/tests/keyctl/clear/bad-args/runtest.sh
new file mode 100644
index 0000000..feb89f6
--- /dev/null
+++ b/tests/keyctl/clear/bad-args/runtest.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that a bad key ID fails correctly
+marker "CHECK CLEAR BAD KEY ID"
+clear_keyring --fail 0
+expect_error EINVAL
+
+# create a non-keyring
+marker "CREATE KEY"
+create_key user lizard gizzard @s
+expect_keyid keyid
+
+# check that a non-keyring ID fails correctly
+marker "CHECK CLEAR NON-KEYRING KEY"
+clear_keyring --fail $keyid
+expect_error ENOTDIR
+
+# dispose of the key we were using
+marker "UNLINK KEY"
+unlink_key --wait $keyid @s
+
+# check that a non-existent key ID fails correctly
+marker "CHECK CLEAR NON-EXISTENT KEY ID"
+clear_keyring --fail $keyid
+expect_error ENOKEY
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/clear/noargs/runtest.sh b/tests/keyctl/clear/noargs/runtest.sh
new file mode 100644
index 0000000..7bcfa89
--- /dev/null
+++ b/tests/keyctl/clear/noargs/runtest.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that no arguments fails correctly
+marker "NO ARGS"
+expect_args_error keyctl clear
+
+# check that one argument fails correctly
+marker "TWO ARGS"
+expect_args_error keyctl clear 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/clear/valid/runtest.sh b/tests/keyctl/clear/valid/runtest.sh
new file mode 100644
index 0000000..9cb35ef
--- /dev/null
+++ b/tests/keyctl/clear/valid/runtest.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "ADD KEYRING"
+create_keyring wibble @s
+expect_keyid keyringid
+
+# validate the new keyring's name and type
+marker "VALIDATE KEYRING"
+describe_key $keyringid
+expect_key_rdesc rdesc 'keyring@.*@wibble'
+
+# check that we have an empty keyring
+marker "LIST KEYRING"
+list_keyring $keyringid
+expect_keyring_rlist rlist empty
+
+# clear the empty keyring
+marker "CLEAR EMPTY KEYRING"
+clear_keyring $keyringid
+
+# check that it's empty again
+marker "LIST KEYRING 2"
+list_keyring $keyringid
+expect_keyring_rlist rlist empty
+
+# stick a key in the keyring
+marker "ADD KEY"
+create_key user lizard gizzard $keyringid
+expect_keyid keyid
+
+# check that we can list it
+marker "LIST KEYRING WITH ONE"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid
+
+# clear the keyring
+marker "CLEAR KEYRING WITH ONE"
+clear_keyring $keyringid
+
+# check that it's now empty again
+marker "LIST KEYRING 3"
+list_keyring $keyringid
+expect_keyring_rlist rlist empty
+
+# stick forty keys in the keyring
+marker "ADD FORTY KEYS"
+keys=""
+for ((i=0; i<40; i++))
+ do
+ create_key user lizard$i gizzard$i $keyringid
+ expect_keyid x
+ keys="$keys $x"
+ list_keyring $keyringid
+ expect_keyring_rlist rlist $x
+done
+
+marker "CHECK KEYRING CONTENTS"
+list_keyring $keyringid
+for i in $keys
+do
+ expect_keyring_rlist rlist $i
+done
+
+marker "SHOW KEYRING"
+if ! keyctl show >>$OUTPUTFILE 2>&1
+then
+ failed
+fi
+
+# clear the keyring
+marker "CLEAR KEYRING WITH ONE"
+clear_keyring $keyringid
+
+# check that it's now empty yet again
+marker "LIST KEYRING 4"
+list_keyring $keyringid
+expect_keyring_rlist rlist empty
+
+# remove the keyring we added
+marker "UNLINK KEY"
+unlink_key $keyringid @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/describing/bad-args/runtest.sh b/tests/keyctl/describing/bad-args/runtest.sh
new file mode 100644
index 0000000..a49ee52
--- /dev/null
+++ b/tests/keyctl/describing/bad-args/runtest.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that a bad key ID fails correctly
+marker "CHECK BAD KEY ID"
+describe_key --fail 0
+expect_error EINVAL
+pretty_describe_key --fail 0
+expect_error EINVAL
+
+# create a key
+marker "CREATE KEY"
+create_key user lizard gizzard @s
+expect_keyid keyid
+
+# dispose of the key
+marker "UNLINK KEY"
+unlink_key --wait $keyid @s
+
+# check that a non-existent key ID fails correctly
+marker "CHECK NON-EXISTENT KEY ID"
+describe_key --fail $keyid
+expect_error ENOKEY
+pretty_describe_key --fail $keyid
+expect_error ENOKEY
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/describing/noargs/runtest.sh b/tests/keyctl/describing/noargs/runtest.sh
new file mode 100644
index 0000000..fac4c49
--- /dev/null
+++ b/tests/keyctl/describing/noargs/runtest.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+marker "NO ARGS"
+expect_args_error keyctl describe
+expect_args_error keyctl rdescribe
+
+marker "TWO ARGS"
+expect_args_error keyctl describe 0 0
+
+marker "THREE ARGS"
+expect_args_error keyctl rdescribe 0 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/describing/valid/runtest.sh b/tests/keyctl/describing/valid/runtest.sh
new file mode 100644
index 0000000..e327e4b
--- /dev/null
+++ b/tests/keyctl/describing/valid/runtest.sh
@@ -0,0 +1,71 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "ADD KEYRING"
+create_keyring wibble @s
+expect_keyid keyringid
+
+# validate the new keyring's name and type
+marker "VALIDATE KEYRING"
+describe_key $keyringid
+expect_key_rdesc rdesc 'keyring@.*@wibble'
+
+# validate a pretty description of the keyring
+marker "VALIDATE PRETTY KEYRING"
+pretty_describe_key $keyringid
+expect_key_rdesc pdesc " *$keyringid: [-avrwsl]* *[-0-9]* *[-0-9]* keyring: wibble"
+
+# check that we have an empty keyring
+marker "LIST KEYRING"
+list_keyring $keyringid
+expect_keyring_rlist rlist empty
+
+# stick a key in the keyring
+marker "ADD KEY"
+create_key user lizard gizzard $keyringid
+expect_keyid keyid
+
+# validate the new key's name and type
+marker "VALIDATE KEY"
+describe_key $keyid
+expect_key_rdesc rdesc 'user@.*@lizard'
+
+# validate a pretty description of the key
+marker "VALIDATE PRETTY KEY"
+pretty_describe_key $keyid
+expect_key_rdesc pdesc " *$keyid: [-avrwsl]* *[0-9]* *[-0-9]* user: lizard"
+
+# turn off view permission on the key
+marker "DISABLE VIEW PERM"
+set_key_perm $keyid 0x3e0000
+describe_key --fail $keyid
+expect_error EACCES
+
+# turn on view permission on the key
+marker "REINSTATE VIEW PERM"
+set_key_perm $keyid 0x3f0000
+describe_key $keyid
+
+# revoke the key
+marker "REVOKE KEY"
+revoke_key $keyid
+describe_key --fail $keyid
+expect_error EKEYREVOKED
+
+# remove the keyring we added
+marker "UNLINK KEY"
+unlink_key $keyringid @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/dh_compute/bad-args/runtest.sh b/tests/keyctl/dh_compute/bad-args/runtest.sh
new file mode 100644
index 0000000..7e8828b
--- /dev/null
+++ b/tests/keyctl/dh_compute/bad-args/runtest.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+if [ $have_dh_compute = 0 ]
+then
+ toolbox_skip_test $TEST "SKIPPING DUE TO LACK OF DIFFIE-HELLMAN"
+ exit 0
+fi
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# Prime, generator, and key values created with:
+# openssl dhparam 2048 -check -out dh.pem
+# openssl genpkey -paramfile dh.pem -text
+prime="\x00\xb6\x89\x4d\x8f\xf7\xaf\x56\xd4\x46\xc9\x50\xc7\xe9\x1d"
+prime+="\x29\x8a\x07\x8d\xae\xa7\x79\x66\xf1\x1f\xc1\x6f\x22\x92\x11"
+prime+="\x73\x7f\x1f\x39\xce\xf3\xda\xd7\x87\x2a\x53\x8f\x6c\x8f\x9a"
+prime+="\x27\x6a\x7c\xf7\x7b\xb2\xd6\x3a\x87\x2a\x4d\xb9\xed\x12\xae"
+prime+="\x0f\x1c\x69\xaf\x9b\xf2\xf2\xe3\x69\x1d\x36\x15\xa1\xd7\xd5"
+prime+="\x77\xa8\x7d\x1c\xb4\xab\xa0\x49\x4b\xae\x0c\x24\x9b\x0c\xce"
+prime+="\xef\x6b\x7a\xb9\xa7\xbe\x70\xb5\xb4\x5e\x4c\xf7\xcb\x71\xad"
+prime+="\x8f\xeb\x7a\x4d\x6c\x7c\xcb\x96\xd5\x29\x8f\x0f\xee\xb4\x78"
+prime+="\x77\x60\x5e\x80\xa0\x33\x86\x91\xe3\x58\x62\xf0\xf4\xcb\xb2"
+prime+="\x09\xe1\x7d\xd9\xfe\xbc\xce\x4c\x21\x57\x70\x06\xce\xb1\x15"
+prime+="\x7b\x18\x15\x92\xd2\xf7\x84\xba\x44\xe0\x06\xc3\x14\xdf\x53"
+prime+="\x06\xbd\xbb\x17\xa0\x10\xb3\x66\x0d\x47\x93\x56\xd8\xd5\x2c"
+prime+="\x5a\xf0\x14\x53\x6c\x20\x89\x7e\x76\x53\x21\x8e\x2c\x7a\x65"
+prime+="\x0a\x73\xdc\x27\x58\x45\x98\xde\x92\xde\x5c\x62\x70\x67\x71"
+prime+="\xfa\x2d\x67\xf6\x25\x44\x1d\x91\x1c\xa0\x3f\x21\x49\xb6\xd4"
+prime+="\xc7\x6b\x5e\xcd\x98\x96\xe9\xd7\x99\xa3\xa5\x00\xec\xec\xc5"
+prime+="\x19\xe3\x1b\x71\x15\x4d\x7b\x36\x1b\xd0\xdd\x15\xf7\xce\x8d"
+prime+="\xfc\x63"
+
+generator="\x02"
+
+private="\x40\x94\x9d\xa2\xca\x2b\x7c\x35\x3d\xe3\x8f\xef\xb0\x6d\xdd"
+private+="\x0d\x67\x47\x9a\x63\x61\xc8\x9e\x77\xb9\x80\xd2\x48\x6c\x4d"
+private+="\x31\x97\x1e\xb8\x8f\x65\x72\x06\x99\x73\xe3\xae\x5a\x43\xce"
+private+="\x76\xbc\xcb\x35\xea\x05\xac\x65\x38\xeb\x0e\xa6\xad\xee\x49"
+private+="\x37\x60\x04\x35\xdd\x79\x40\x88\x5d\x2e\x3f\x78\xc7\x28\x08"
+private+="\x34\xf8\x78\xd3\xd5\x50\xcc\x93\x05\x33\x0b\xb8\xf0\x20\x85"
+private+="\xee\x6c\x23\x0d\x42\xd8\x4e\xb6\x7a\x24\x5b\x92\x81\x7f\xd1"
+private+="\x54\xbc\xb1\x39\x4a\x28\x9d\x11\xaf\xb5\xa1\xe5\x0e\x13\x95"
+private+="\x09\x08\xaf\x70\x75\x67\x04\xe9\xbf\x03\xdf\xf0\xe5\xd4\x90"
+private+="\x74\x38\x41\xc5\x34\xcb\x7e\x2c\xf4\xb9\xf0\x49\x3a\x73\x0b"
+private+="\x0d\x71\x09\x6a\x16\xbd\xc0\xe8\x52\xf1\x75\x75\x51\x34\xb2"
+private+="\xb4\x11\x12\x28\x0a\x88\x21\x27\x28\xaf\xbe\x16\xd4\x17\xf3"
+private+="\x18\x93\xcb\xe4\x42\xe0\x6d\x21\x2d\x8e\xfe\x22\x7a\xa0\x03"
+private+="\x9a\x65\xce\x99\x81\x07\xfa\xe2\x78\x51\x1c\x6b\xf4\xd5\x99"
+private+="\x32\x53\x4a\xe9\xfc\x39\xdb\x80\x63\x51\x63\xc0\x54\x66\x57"
+private+="\x50\x08\x66\xd5\x46\x1c\x5f\xa3\x54\x02\x38\x32\x4a\x29\xf3"
+private+="\x16\xe0\x68\xf3\xba\x17\x37\xd0\x42\xcb\x51\xa8\x97\x1b\xc7"
+private+="\xa2"
+
+pcreate_key "-e $prime" user dh:prime @s
+expect_keyid primeid
+
+pcreate_key "-e $generator" user dh:generator @s
+expect_keyid generatorid
+
+pcreate_key "-e $private" user dh:private @s
+expect_keyid privateid
+
+pcreate_key "-e \x00" logon dh:logon @s
+expect_keyid logonid
+
+marker "CHECK WRONG KEY TYPE"
+dh_compute --fail $privateid $primeid $logonid
+expect_error ENOKEY
+dh_compute --fail $privateid $primeid @s
+expect_error EOPNOTSUPP
+
+marker "CHECK MISSING KEY"
+dh_compute --fail $privateid $primeid 0
+expect_error ENOKEY
+unlink_key --wait $generatorid @s
+dh_compute --fail $privateid $primeid $generatorid
+expect_error ENOKEY
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/dh_compute/noargs/runtest.sh b/tests/keyctl/dh_compute/noargs/runtest.sh
new file mode 100644
index 0000000..e761f6b
--- /dev/null
+++ b/tests/keyctl/dh_compute/noargs/runtest.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+if [ $have_dh_compute = 0 ]
+then
+ toolbox_skip_test $TEST "SKIPPING DUE TO LACK OF DIFFIE-HELLMAN"
+ exit 0
+fi
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+marker "NO ARGS"
+expect_args_error keyctl dh_compute
+
+marker "TWO ARGS"
+expect_args_error keyctl dh_compute 0 0
+
+marker "FOUR ARGS"
+expect_args_error keyctl dh_compute 0 0 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/dh_compute/valid/runtest.sh b/tests/keyctl/dh_compute/valid/runtest.sh
new file mode 100644
index 0000000..5ad1d14
--- /dev/null
+++ b/tests/keyctl/dh_compute/valid/runtest.sh
@@ -0,0 +1,266 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+if [ $have_dh_compute = 0 ]
+then
+ toolbox_skip_test $TEST "SKIPPING DUE TO LACK OF DIFFIE-HELLMAN"
+ exit 0
+fi
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# Prime, generator, and key values created with:
+# openssl dhparam 2048 -check -out dh.pem
+# openssl genpkey -paramfile dh.pem -text
+prime="\x00\xb6\x89\x4d\x8f\xf7\xaf\x56\xd4\x46\xc9\x50\xc7\xe9\x1d"
+prime+="\x29\x8a\x07\x8d\xae\xa7\x79\x66\xf1\x1f\xc1\x6f\x22\x92\x11"
+prime+="\x73\x7f\x1f\x39\xce\xf3\xda\xd7\x87\x2a\x53\x8f\x6c\x8f\x9a"
+prime+="\x27\x6a\x7c\xf7\x7b\xb2\xd6\x3a\x87\x2a\x4d\xb9\xed\x12\xae"
+prime+="\x0f\x1c\x69\xaf\x9b\xf2\xf2\xe3\x69\x1d\x36\x15\xa1\xd7\xd5"
+prime+="\x77\xa8\x7d\x1c\xb4\xab\xa0\x49\x4b\xae\x0c\x24\x9b\x0c\xce"
+prime+="\xef\x6b\x7a\xb9\xa7\xbe\x70\xb5\xb4\x5e\x4c\xf7\xcb\x71\xad"
+prime+="\x8f\xeb\x7a\x4d\x6c\x7c\xcb\x96\xd5\x29\x8f\x0f\xee\xb4\x78"
+prime+="\x77\x60\x5e\x80\xa0\x33\x86\x91\xe3\x58\x62\xf0\xf4\xcb\xb2"
+prime+="\x09\xe1\x7d\xd9\xfe\xbc\xce\x4c\x21\x57\x70\x06\xce\xb1\x15"
+prime+="\x7b\x18\x15\x92\xd2\xf7\x84\xba\x44\xe0\x06\xc3\x14\xdf\x53"
+prime+="\x06\xbd\xbb\x17\xa0\x10\xb3\x66\x0d\x47\x93\x56\xd8\xd5\x2c"
+prime+="\x5a\xf0\x14\x53\x6c\x20\x89\x7e\x76\x53\x21\x8e\x2c\x7a\x65"
+prime+="\x0a\x73\xdc\x27\x58\x45\x98\xde\x92\xde\x5c\x62\x70\x67\x71"
+prime+="\xfa\x2d\x67\xf6\x25\x44\x1d\x91\x1c\xa0\x3f\x21\x49\xb6\xd4"
+prime+="\xc7\x6b\x5e\xcd\x98\x96\xe9\xd7\x99\xa3\xa5\x00\xec\xec\xc5"
+prime+="\x19\xe3\x1b\x71\x15\x4d\x7b\x36\x1b\xd0\xdd\x15\xf7\xce\x8d"
+prime+="\xfc\x63"
+
+generator="\x02"
+
+private="\x40\x94\x9d\xa2\xca\x2b\x7c\x35\x3d\xe3\x8f\xef\xb0\x6d\xdd"
+private+="\x0d\x67\x47\x9a\x63\x61\xc8\x9e\x77\xb9\x80\xd2\x48\x6c\x4d"
+private+="\x31\x97\x1e\xb8\x8f\x65\x72\x06\x99\x73\xe3\xae\x5a\x43\xce"
+private+="\x76\xbc\xcb\x35\xea\x05\xac\x65\x38\xeb\x0e\xa6\xad\xee\x49"
+private+="\x37\x60\x04\x35\xdd\x79\x40\x88\x5d\x2e\x3f\x78\xc7\x28\x08"
+private+="\x34\xf8\x78\xd3\xd5\x50\xcc\x93\x05\x33\x0b\xb8\xf0\x20\x85"
+private+="\xee\x6c\x23\x0d\x42\xd8\x4e\xb6\x7a\x24\x5b\x92\x81\x7f\xd1"
+private+="\x54\xbc\xb1\x39\x4a\x28\x9d\x11\xaf\xb5\xa1\xe5\x0e\x13\x95"
+private+="\x09\x08\xaf\x70\x75\x67\x04\xe9\xbf\x03\xdf\xf0\xe5\xd4\x90"
+private+="\x74\x38\x41\xc5\x34\xcb\x7e\x2c\xf4\xb9\xf0\x49\x3a\x73\x0b"
+private+="\x0d\x71\x09\x6a\x16\xbd\xc0\xe8\x52\xf1\x75\x75\x51\x34\xb2"
+private+="\xb4\x11\x12\x28\x0a\x88\x21\x27\x28\xaf\xbe\x16\xd4\x17\xf3"
+private+="\x18\x93\xcb\xe4\x42\xe0\x6d\x21\x2d\x8e\xfe\x22\x7a\xa0\x03"
+private+="\x9a\x65\xce\x99\x81\x07\xfa\xe2\x78\x51\x1c\x6b\xf4\xd5\x99"
+private+="\x32\x53\x4a\xe9\xfc\x39\xdb\x80\x63\x51\x63\xc0\x54\x66\x57"
+private+="\x50\x08\x66\xd5\x46\x1c\x5f\xa3\x54\x02\x38\x32\x4a\x29\xf3"
+private+="\x16\xe0\x68\xf3\xba\x17\x37\xd0\x42\xcb\x51\xa8\x97\x1b\xc7"
+private+="\xa2"
+
+read -d '' public <<"EOF"
+a4cf1f93 95fce03f d02aaece da1f86bd d8d77b69 29039fcc bd138c98 2483bf9c
+7e4406c1 4f3cea24 6cafb29e 95095d0c 6768f13b 31babb24 6c590d92 6c343e69
+59dbd47f 65982a3b b1baa7a3 05a72054 89b6cd0d 78397962 fc834fc9 3ec0517e
+c218396f 9cff860e 29078aee 6b8598b6 79325014 bb84597d f031e149 edbe1c5a
+2a55fe4e bbc64a52 6da59e71 1c7ae5e0 954ba23b 9d58c423 17d84841 815708c8
+b9059987 48773eac 2244b286 cd118277 48b7ed3a 5af5cc0f 8f254190 5e16f998
+a328e894 acc343f4 66a95281 86cea6a3 93eb4fee f83c0e2e f4a00ce6 fcc9ef81
+cc4624d5 ba659411 d1ba7b5f 14a3e286 d42e6ac8 afa9f846 41cb7cb5 66965725
+EOF
+
+pcreate_key "-e $prime" user dh:prime @s
+expect_keyid primeid
+
+pcreate_key "-e $generator" user dh:generator @s
+expect_keyid generatorid
+
+pcreate_key "-e $private" user dh:private @s
+expect_keyid privateid
+
+marker "COMPUTE DH PUBLIC KEY"
+dh_compute $privateid $primeid $generatorid
+expect_multiline payload "$public"
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+
+################################################################
+# Testing DH compute with KDF according to SP800-56A
+#
+# test vectors from http://csrc.nist.gov/groups/STM/cavp/documents/keymgmt/KASTestVectorsFFC2014.zip
+################################################################
+
+# SHA-256
+
+# XephemCAVS
+private="\x81\xb2\xc6\x5f\x5c\xba\xc0\x0b\x13\x53\xac\x38\xbd\x77\xa2\x5a"
+private+="\x86\x50\xed\x48\x5e\x41\x3e\xac\x1d\x6c\x48\x85"
+
+# P
+prime="\xa3\xcc\x62\x23\xe5\x0c\x6e\x3f\x7b\xb0\x58\x1d\xcb\x9e\x9f\xf0"
+prime+="\x2c\x58\x07\x68\x32\x8a\x15\x20\x7b\x1c\x32\x31\x7f\xb7\x84\x96"
+prime+="\x81\x5e\x3c\xf7\xf9\xd0\x9c\xcb\x9f\xa8\x40\xff\x47\x98\x51\x1a"
+prime+="\x17\xb5\x59\x28\x72\x1e\x5d\xfb\xcc\xc5\x41\x47\xe0\xf0\x5f\x85"
+prime+="\xb3\xac\x41\x0b\x6a\xe3\xf5\x9b\x79\x6f\x3f\xea\xc7\xfc\x52\x49"
+prime+="\x21\x7e\xb2\xa0\x45\x88\x29\x3a\x5a\xde\x22\x78\x79\xf4\x6c\xeb"
+prime+="\x56\x45\x7b\x5c\x43\x12\x93\xe5\xe1\x04\xd1\xb9\x64\xbd\x2c\xdf"
+prime+="\xde\xff\xa0\x40\x49\xa9\x1e\x67\xee\x8c\x86\xe9\x44\xf0\x4f\x94"
+prime+="\x4a\x30\xe3\x61\xf8\xd1\x5d\x17\xe5\x01\x0c\xab\xb4\xef\x40\xc0"
+prime+="\xeb\xa5\xf4\xa2\x52\xd4\xfd\x6c\xf9\xda\xe6\x0e\x86\xe4\xb3\x00"
+prime+="\x9b\x1d\xfc\x92\x66\x70\x35\x72\x61\x58\x7a\xd0\x5c\x00\xa6\xc6"
+prime+="\xf0\x10\x6c\xec\x8f\xc5\x91\x31\x51\x50\x84\xa8\x70\x59\x41\x65"
+prime+="\xb4\x93\x90\xdb\x2d\x00\xe7\x53\x8f\x23\x0d\x53\x2f\x4a\x4e\xca"
+prime+="\x83\x09\xd7\x07\xc0\xb3\x83\x5c\xee\x04\xf3\xca\x55\x8a\x22\xc6"
+prime+="\xb5\x20\xfe\x25\xde\x6f\xfa\x90\xef\xda\x49\x27\xd0\x18\x59\x4c"
+prime+="\x0c\x0b\x77\x06\x73\x93\xb7\xf1\xe0\xfc\x7c\xf2\x16\xaf\xf3\x9f"
+
+# YephemIUT
+xa="\x9a\x70\x82\x2d\x3f\x06\x12\x3d\x0e\x51\x8e\xe1\x16\x51\xe5\xf6"
+xa+="\xb1\x19\xdc\x3b\x97\xd5\xb1\xc0\xa2\xa6\xf6\xde\x94\x25\x64\xba"
+xa+="\x10\x06\x1e\xec\xde\xb7\x36\x9c\xa5\x37\x49\x9e\x04\xb0\x36\xe9"
+xa+="\x7f\x44\x5a\x95\x6f\x63\x69\xae\x6e\x63\xfd\x27\xea\xe3\xe3\x47"
+xa+="\x85\x54\x47\xd3\xba\xc1\xc6\x0c\x10\xe7\x35\x07\x72\xc6\xc0\xc6"
+xa+="\xfb\xf9\xca\x3e\x38\xf0\xe8\x65\x88\x25\xd3\xb2\x0f\x1f\x02\x8f"
+xa+="\x35\xe3\x4d\x12\x35\x10\x3d\xf2\x33\x9b\x5b\x09\x9d\x3f\xe3\xe5"
+xa+="\x34\x6a\x69\x16\x42\xba\xc5\xb0\xbb\x03\xcd\x5d\x04\xd7\x56\x26"
+xa+="\x21\x49\x3f\xf1\xc4\x27\x3b\x6a\x45\xc5\xec\xb0\xb5\xe9\x08\xa0"
+xa+="\xf9\xf5\x62\x28\x2e\x85\x3e\xfc\x9a\x7e\xa1\x12\xe9\x47\x4f\xf6"
+xa+="\x94\x18\xf7\xc4\x7a\xe9\x66\xd4\x52\x4c\xa1\x70\x1b\x60\xa4\xbe"
+xa+="\x15\xc7\x5e\x27\xb4\x05\x80\x64\x68\x15\x6e\x02\xcb\xc5\x8f\xf4"
+xa+="\x66\x3c\x96\xac\x0c\x87\x36\x81\x35\xfa\x9b\x0b\xb6\x33\x7a\xe2"
+xa+="\x58\x52\x1d\x7d\x60\xc2\xa9\x1b\x4e\xd7\x72\xad\x65\x03\x40\x49"
+xa+="\x97\xf6\x79\x9d\xf6\x63\xa8\x99\x9c\xfd\x74\x7f\xa0\x67\xb9\x05"
+xa+="\x8a\xb3\x3b\xc1\x45\x94\x36\x6f\x28\xf5\xa2\xd9\x00\xb6\x46\x7a"
+
+# Z
+read -d '' shared <<"EOF"
+0fdbd9a2 ebf50cba 489b4e4d 7cd6924a 42ee6324 a26988b2 22bc38e6 9cc445f1
+eb47c1a4 62eca39f 39bcd7b8 19dede51 30bc38da ec99c16f 40a4e5c1 9c97b796
+8b41823d a0650e37 13c73e6f 5f2a9dff 2e67dbf5 40ee66f4 e694c28f ba1d604b
+71b57b8a eeb67a35 ba425a38 490b6fb9 f713db22 6f893b7a 8962f426 ba3046fb
+cff8538c 16f583e8 ae947672 0ba55ff9 75b440d0 c4565cc7 5837d23a fea61a39
+e0b7f6c4 e24c2154 7eb19fce f8dbed10 b06a9cce 971c0f0f ba7c1d5c b5035eaa
+4fddd3ba fe757339 e3321e3e 4ebfe9e7 9c6c0401 4df63cf9 28d0a2c0 5b2d5521
+030c35f1 c84c97fe 64cad509 8012a003 d52d24c4 1a1f9348 b7575251 3facb02f
+EOF
+
+# OI
+otherinfo="\xa1\xb2\xc3\xd4\xe5\x43\x41\x56\x53\x69\x64\x0d\x64\xc1\xb2"
+otherinfo+="\x33\x61\xb2\x61\xde\x78\x68\x8e\xa8\x65\xfc\xff\x11\x3c\x84"
+
+# DKM
+read -d '' derived <<"EOF"
+8284e313 02c8a26b 393ec52d 9f9e0882
+EOF
+
+pcreate_key "-e $prime" user dh:prime @s
+expect_keyid primeid
+
+pcreate_key "-e $xa" user dh:xa @s
+expect_keyid xaid
+
+pcreate_key "-e $private" user dh:private @s
+expect_keyid privateid
+
+marker "COMPUTE DH SHARED SECRET"
+dh_compute $privateid $primeid $xaid
+expect_multiline payload "$shared"
+
+marker "COMPUTE DERIVED KEY FROM DH SHARED SECRET (SHA-256)"
+echo -e -n $otherinfo | dh_compute_kdf_oi $privateid $primeid $xaid 16 "sha256"
+expect_multiline payload "$derived"
+
+pcreate_key "-e \x01" user dh:leadingzero @s
+expect_keyid lzid
+
+read -d '' derived2 <<"EOF"
+0066207b cdab1d64 bbf489b3 d6a0dadc
+EOF
+
+marker "COMPUTE DERIVED KEY WITH LEADING ZEROS"
+echo -e -n $otherinfo | dh_compute_kdf_oi $privateid $primeid $lzid 16 "sha256"
+expect_multiline payload "$derived2"
+
+# SHA-224
+
+# XephemCAVS
+private="\x86\x1b\xa2\x59\xab\xa6\xaa\x57\x7d\xe2\x2f\x50\x8e\xcb\xbc\x26"
+private+="\xc5\xac\xfc\xcb\x9e\xa2\x3b\x43\x4d\x6d\x2b\x79"
+
+# P
+prime="\xa5\xb1\x76\x4e\x13\xc8\x16\x99\xab\xa3\x8f\x0d\xc0\xd1\x5e\x15"
+prime+="\xf5\x0f\xcd\x5c\xf7\xc2\x23\x72\xca\xfc\x5e\xd7\x62\x94\x1b\xd9"
+prime+="\xe0\xfb\x9a\xab\xee\x74\x66\xd2\xc8\x29\xaa\xb0\x31\xdb\x7b\x1b"
+prime+="\x5a\x64\xe6\x8e\xd5\x3b\xaf\xb2\x83\xba\x0f\x01\x8b\xeb\x3e\xdc"
+prime+="\x95\x7f\xe4\x53\xbe\x0d\xaa\xb6\x1b\x32\x28\x76\x3e\x80\x75\x8c"
+prime+="\x6d\x8c\x28\x3c\xf6\x30\xed\xd9\xd7\x0a\x8a\xf3\x30\xdd\x0a\xf6"
+prime+="\xa8\xd5\x94\xc2\x3c\xdd\x24\xc8\xad\x3f\xcf\xea\x41\x75\x77\x72"
+prime+="\xce\xed\x92\x1e\x63\x86\x2f\x24\x6e\x6f\x49\xd8\x74\x7e\x44\xae"
+prime+="\xf0\x1e\x30\x9b\x6d\xcc\x80\xd4\x50\x38\x3b\xb1\xf9\x4d\xd5\x90"
+prime+="\x84\xf8\xe9\x6f\x85\x6e\xc7\xc8\x33\x5e\xdb\x05\x5f\x8e\xc6\xc4"
+prime+="\x81\x52\x0b\x3f\x28\xe8\x0b\x62\x09\xb8\xae\x61\xcc\x86\x0e\x24"
+prime+="\xc8\x22\xb6\x6c\x4f\x97\x80\x49\x93\xbc\xd0\xa9\x72\xb3\x53\x54"
+prime+="\x01\x33\x0e\xbe\x4b\x2e\x92\x3f\x18\x9b\x63\x35\x62\xe4\x68\xeb"
+prime+="\x99\xa4\xbc\x88\xcc\xbf\xf8\xdf\x0f\xd5\xaf\xcf\xe6\xae\x19\x18"
+prime+="\x42\x14\xab\x3f\xef\xb7\xf0\x66\x8b\x8b\x26\x83\xbe\xbd\x56\x51"
+prime+="\xa4\xc6\x38\x43\xb9\xb1\x4b\xc7\x38\xd5\x20\xb1\xb7\x21\x2c\x69"
+
+# YephemIUT
+xa="\x17\xd7\x1a\xf4\x35\x3c\x22\x12\x2a\xeb\x2a\x06\x19\xcc\x2c\xf7"
+xa+="\x35\x53\xf2\x8e\x9f\xb1\x91\xfd\xb2\x86\xb1\x15\xb9\xfd\xa8\x66"
+xa+="\x2d\xe5\x17\x3b\x1a\xff\x70\x48\x8d\x9b\xc8\x48\xe5\x37\xd7\xe5"
+xa+="\x02\x16\x49\xd3\x7d\xc7\x8c\x94\x36\x9d\xb9\x0c\x27\x84\xc9\x4d"
+xa+="\x97\x0a\xc9\xb5\xe3\x5e\xfd\x22\xd4\x18\xd3\x1b\x68\xd9\x55\x0b"
+xa+="\xaa\x77\x16\xe9\x8e\xa6\x78\x3b\xb3\xa8\x45\x05\x9f\xba\xa4\xa6"
+xa+="\x72\x0a\x6a\x23\xc5\x6b\xa5\x2b\x4d\x9b\x72\x6e\x00\x68\xe9\xeb"
+xa+="\x4d\x17\x5b\xff\x43\x69\xf3\xd2\xa4\xaf\x66\xee\xcd\x62\xef\x7b"
+xa+="\x23\xc3\x37\xd4\x70\x95\x2b\x17\x67\xc8\xbf\x78\x2f\x0b\x58\xb4"
+xa+="\xfc\x82\x45\xf8\x40\x78\x71\x70\xf4\xb0\xa5\x1b\x5e\xb4\x60\x75"
+xa+="\x8a\xdd\xc9\xf4\x4a\x73\xa3\xf6\x07\x60\x3b\xd3\x50\x73\xd1\xa6"
+xa+="\x9a\x20\x3a\x04\x94\xa8\xc2\x02\x1b\xa0\xda\x1f\x04\x95\xf5\x60"
+xa+="\xc0\xba\x81\x79\x4e\xee\xeb\x82\x5d\x1b\xd3\x43\x16\xa5\x2a\xe1"
+xa+="\xc9\x00\x10\x0c\x0d\x6f\xa0\x25\x46\xed\x7a\x9c\x38\xa6\xa3\x43"
+xa+="\xd6\x86\x59\xee\xb5\x9c\xf3\x81\x04\xa9\x6b\xb2\x5a\x6d\xbb\xf0"
+xa+="\xcb\xc0\xed\xe7\x3a\x7b\xba\x67\x51\x81\xe0\xcd\x2e\x7b\x9f\x89"
+
+# Z
+read -d '' shared <<"EOF"
+057c22b8 c5872fef 08ebe852 fafab4b7 c2c2ffbb 376d71bd a941b16e 32614adf
+ebb82aeb d50f29d3 cec63d10 77f50e21 cf381b87 a818c614 52c5cce2 af85f40c
+06615b97 fe8c3a80 68990ac5 83957b52 8dd6d52d a3b51e84 aec355fd 4a3fe5ce
+faa3b17c 9e71cb4d 28ecab6d 21297280 e52397b7 ccb1b62d 8d5d3ce4 1d26b2a3
+bdbf880b b39e8b02 8a745ff2 9f0984da efe97084 5d850884 525403ca d2a52956
+f55b9a89 b2d801f1 710333c0 479c5955 b54c8163 83c65ad9 c78b8c67 cc1b211b
+208b9fab b9c99a68 18293e6a 8da069e6 75eb4317 668a7d4b 6f235533 f3ff4ed0
+4f8ad579 f9ad14e7 f68ae183 41d603d9 d6297123 00716c98 bbbf16eb 2a2cc92f
+EOF
+
+# OI
+otherinfo="\xa1\xb2\xc3\xd4\xe5\x43\x41\x56\x53\x69\x64\xaa\x27\xe2\x49"
+otherinfo+="\xbf\x0a\x12\x76\x46\x8d\x80\x82\x59\xf3\xb8\xe2\x68\x78\x51"
+
+# DKM
+read -d '' derived <<"EOF"
+88bf39c0 08eec33a dc3b4430 054ba262
+EOF
+
+pcreate_key "-e $prime" user dh:prime @s
+expect_keyid primeid
+
+pcreate_key "-e $xa" user dh:xa @s
+expect_keyid xaid
+
+pcreate_key "-e $private" user dh:private @s
+expect_keyid privateid
+
+marker "COMPUTE DH SHARED SECRET"
+dh_compute $privateid $primeid $xaid
+expect_multiline payload "$shared"
+
+marker "COMPUTE DERIVED KEY FROM DH SHARED SECRET (SHA-224)"
+echo -e -n $otherinfo | dh_compute_kdf_oi $privateid $primeid $xaid 16 "sha224"
+expect_multiline payload "$derived"
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/instantiating/bad-args/runtest.sh b/tests/keyctl/instantiating/bad-args/runtest.sh
new file mode 100644
index 0000000..1922282
--- /dev/null
+++ b/tests/keyctl/instantiating/bad-args/runtest.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that a bad key ID fails correctly
+marker "CHECK BAD KEY ID"
+instantiate_key --fail 0 a @p
+expect_error EPERM
+pinstantiate_key --fail a 0 @p
+expect_error EPERM
+negate_key --fail 0 10 @p
+expect_error EPERM
+
+# create a non-keyring
+marker "CREATE KEY"
+create_key user lizard gizzard @s
+expect_keyid keyid
+
+# check that instantiation of an instantiated key fails
+marker "CHECK ALREADY INSTANTIATED KEY"
+instantiate_key --fail $keyid a @p
+expect_error EPERM
+pinstantiate_key --fail a $keyid @p
+expect_error EPERM
+negate_key --fail $keyid 10 @p
+expect_error EPERM
+
+# check negative key timeout must be a number
+marker "CHECK NEGATE TIMEOUT"
+expect_args_error keyctl negate $keyid aa @p
+
+# dispose of the key we were using
+marker "UNLINK KEY"
+unlink_key --wait $keyid @s
+
+# check that a non-existent key ID fails correctly
+marker "CHECK NON-EXISTENT KEY ID"
+instantiate_key --fail 0 a @p
+expect_error EPERM
+pinstantiate_key --fail a 0 @p
+expect_error EPERM
+negate_key --fail 0 10 @p
+expect_error EPERM
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/instantiating/noargs/runtest.sh b/tests/keyctl/instantiating/noargs/runtest.sh
new file mode 100644
index 0000000..1b5150f
--- /dev/null
+++ b/tests/keyctl/instantiating/noargs/runtest.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+marker "NO ARGS"
+expect_args_error keyctl instantiate
+expect_args_error keyctl pinstantiate
+expect_args_error keyctl negate
+
+marker "ONE ARG"
+expect_args_error keyctl instantiate 0
+expect_args_error keyctl pinstantiate 0
+expect_args_error keyctl negate 0
+
+marker "TWO ARGS"
+expect_args_error keyctl instantiate 0 0
+expect_args_error keyctl negate 0 0
+
+marker "THREE ARGS"
+expect_args_error keyctl pinstantiate 0 0 0
+
+marker "FOUR ARGS"
+expect_args_error keyctl instantiate 0 0 0 0
+expect_args_error keyctl negate 0 0 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/invalidate/bad-args/runtest.sh b/tests/keyctl/invalidate/bad-args/runtest.sh
new file mode 100644
index 0000000..2d9ab11
--- /dev/null
+++ b/tests/keyctl/invalidate/bad-args/runtest.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+if [ $have_key_invalidate = 0 ]
+then
+ toolbox_skip_test $TEST "SKIPPING DUE TO LACK OF KEY INVALIDATION"
+ exit 0
+fi
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that a bad key ID fails correctly
+marker "CHECK INVALIDATE BAD KEY ID"
+invalidate_key --fail 0
+expect_error EINVAL
+
+# create a key
+marker "CREATE KEY"
+create_key user lizard gizzard @s
+expect_keyid keyid
+
+# and dispose of it
+marker "UNLINK KEY"
+unlink_key --wait $keyid @s
+
+# check that a non-existent key ID fails correctly
+marker "CHECK INVALIDATE NON-EXISTENT KEY ID"
+invalidate_key --fail $keyid
+expect_error ENOKEY
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/invalidate/noargs/runtest.sh b/tests/keyctl/invalidate/noargs/runtest.sh
new file mode 100644
index 0000000..25b19ba
--- /dev/null
+++ b/tests/keyctl/invalidate/noargs/runtest.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+if [ $have_key_invalidate = 0 ]
+then
+ toolbox_skip_test $TEST "SKIPPING DUE TO LACK OF KEY INVALIDATION"
+ exit 0
+fi
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that no arguments fails correctly
+marker "NO ARGS"
+expect_args_error keyctl invalidate
+
+# check that two arguments fail correctly
+marker "TWO ARGS"
+expect_args_error keyctl invalidate 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/invalidate/valid/runtest.sh b/tests/keyctl/invalidate/valid/runtest.sh
new file mode 100644
index 0000000..afc0d3e
--- /dev/null
+++ b/tests/keyctl/invalidate/valid/runtest.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+if [ $have_key_invalidate = 0 ]
+then
+ toolbox_skip_test $TEST "SKIPPING DUE TO LACK OF KEY INVALIDATION"
+ exit 0
+fi
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "ADD KEYRING"
+create_keyring wibble @s
+expect_keyid keyringid
+
+# check that we have an empty keyring
+marker "LIST KEYRING"
+list_keyring $keyringid
+expect_keyring_rlist rlist empty
+
+# stick a key in the keyring
+marker "ADD KEY"
+create_key user lizard gizzard $keyringid
+expect_keyid keyid
+
+# check that we can list it
+marker "LIST KEYRING 2"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid
+
+# invalidate the key
+marker "INVALIDATE KEY"
+invalidate_key $keyid
+
+# need to wait for the gc
+sleep 1
+
+# check that it's now empty again
+marker "LIST KEYRING 3"
+list_keyring $keyringid
+expect_keyring_rlist rlist empty
+
+# stick another key in the keyring
+marker "ADD KEY"
+create_key user lizard2 gizzard $keyringid
+expect_keyid keyid
+
+# check that we can list it
+marker "LIST KEYRING 4"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid
+
+# invalidate the keyring
+marker "INVALIDATE KEYRING"
+invalidate_key $keyringid
+
+# need to wait for the gc
+sleep 1
+
+# check that the keyring no longer exists
+marker "CHECK KEYRING"
+list_keyring --fail $keyringid
+expect_error ENOKEY
+
+# check that the key got gc'd also
+marker "CHECK KEY"
+describe_key --fail $keyid
+expect_error ENOKEY
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/link/bad-args/runtest.sh b/tests/keyctl/link/bad-args/runtest.sh
new file mode 100644
index 0000000..39612f4
--- /dev/null
+++ b/tests/keyctl/link/bad-args/runtest.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that a bad key ID fails correctly
+marker "CHECK LINK FROM BAD KEY ID"
+link_key --fail 0 @s
+expect_error EINVAL
+
+marker "CHECK LINK TO BAD KEY ID"
+link_key --fail @s 0
+expect_error EINVAL
+
+# create a non-keyring
+marker "CREATE KEY"
+create_key user lizard gizzard @s
+expect_keyid keyid
+
+# check that linking to a non-keyring ID fails correctly
+marker "CHECK LINK TO NON-KEYRING KEY"
+link_key --fail @s $keyid
+expect_error ENOTDIR
+
+# dispose of the key we were using
+marker "UNLINK KEY"
+unlink_key --wait $keyid @s
+
+# check that a non-existent key ID fails correctly
+marker "CHECK LINK TO NON-EXISTENT KEY ID"
+link_key --fail @s $keyid
+expect_error ENOKEY
+
+marker "CHECK LINK FROM NON-EXISTENT KEY ID"
+link_key --fail $keyid @s
+expect_error ENOKEY
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/link/noargs/runtest.sh b/tests/keyctl/link/noargs/runtest.sh
new file mode 100644
index 0000000..94c2a9f
--- /dev/null
+++ b/tests/keyctl/link/noargs/runtest.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that no arguments fails correctly
+marker "NO ARGS"
+expect_args_error keyctl link
+
+# check that one argument fails correctly
+marker "ONE ARGS"
+expect_args_error keyctl link 0
+
+# check that three arguments fails correctly
+marker "THREE ARGS"
+expect_args_error keyctl link 0 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/link/recursion/runtest.sh b/tests/keyctl/link/recursion/runtest.sh
new file mode 100644
index 0000000..1012871
--- /dev/null
+++ b/tests/keyctl/link/recursion/runtest.sh
@@ -0,0 +1,185 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "CREATE KEYRING 1"
+create_keyring "first" @s
+expect_keyid keyringid
+set_key_perm $keyringid 0x3f3f0000
+
+# attempt to link a keyring to itself
+marker "RECURSE 1"
+link_key --fail $keyringid $keyringid
+expect_error EDEADLK
+
+# create a second keyring in the first
+marker "CREATE KEYRING 2"
+create_keyring "second" $keyringid
+expect_keyid keyring2id
+set_key_perm $keyring2id 0x3f3f0000
+
+# attempt to link a keyring to its child keyring
+marker "RECURSE 2"
+link_key --fail $keyringid $keyring2id
+expect_error EDEADLK
+
+# create a third keyring in the second
+marker "CREATE KEYRING 3"
+create_keyring "third" $keyring2id
+expect_keyid keyring3id
+set_key_perm $keyring3id 0x3f3f0000
+
+# attempt to link a keyring to its grandchild keyring
+marker "RECURSE 3"
+link_key --fail $keyringid $keyring3id
+expect_error EDEADLK
+
+# create a fourth keyring in the third
+marker "CREATE KEYRING 4"
+create_keyring "fourth" $keyring3id
+expect_keyid keyring4id
+set_key_perm $keyring4id 0x3f3f0000
+
+# attempt to link a keyring to its great grandchild keyring
+marker "RECURSE 4"
+link_key --fail $keyringid $keyring4id
+expect_error EDEADLK
+
+# create a fifth keyring in the fourth
+marker "CREATE KEYRING 5"
+create_keyring "fifth" $keyring4id
+expect_keyid keyring5id
+set_key_perm $keyring5id 0x3f3f0000
+
+# attempt to link a keyring to its great great grandchild keyring
+marker "RECURSE 5"
+link_key --fail $keyringid $keyring5id
+expect_error EDEADLK
+
+# create a sixth keyring in the fifth
+marker "CREATE KEYRING 6"
+create_keyring "sixth" $keyring5id
+expect_keyid keyring6id
+set_key_perm $keyring6id 0x3f3f0000
+
+# attempt to link a keyring to its great great great grandchild keyring
+marker "RECURSE 6"
+link_key --fail $keyringid $keyring6id
+expect_error EDEADLK
+
+# create a seventh keyring in the sixth
+marker "CREATE KEYRING 7"
+create_keyring "seventh" $keyring6id
+expect_keyid keyring7id
+set_key_perm $keyring7id 0x3f3f0000
+
+# attempt to link a keyring to its great great great great grandchild keyring
+marker "RECURSE 7"
+link_key --fail $keyringid $keyring7id
+expect_error EDEADLK
+
+# create an eigth keyring in the seventh
+marker "CREATE KEYRING 8"
+create_keyring "eighth" @s
+expect_keyid keyring8id
+set_key_perm $keyring8id 0x3f3f0000
+link_key $keyring8id $keyring7id
+unlink_key $keyring8id @s
+
+# attempt to link a keyring to its great great great great great grandchild keyring
+marker "RECURSE 8"
+link_key --fail $keyringid $keyring8id
+expect_error EDEADLK
+
+# create a ninth keyring in the eighth
+marker "CREATE KEYRING 9"
+create_keyring "ninth" @s
+expect_keyid keyring9id
+set_key_perm $keyring9id 0x3f3f0000
+link_key $keyring9id $keyring8id
+unlink_key $keyring9id @s
+
+# attempt to link a keyring to its great great great great great great grandchild keyring
+marker "RECURSE 9"
+link_key --fail $keyringid $keyring9id
+expect_error ELOOP
+
+# remove the first keyring we added
+marker "UNLINK KEYRING"
+unlink_key $keyringid @s
+
+# create two stacks of keyrings
+marker "CREATE KEYRING STACKS"
+create_keyring "A1" @s
+expect_keyid aroot
+create_keyring "B1" @s
+expect_keyid broot
+a=$aroot
+b=$broot
+
+for ((i=2; i<=4; i++))
+ do
+ create_keyring "A$i" $a
+ expect_keyid a
+ create_keyring "B$i" $b
+ expect_keyid b
+done
+
+# make sure we can't create a cycle by linking the two stacks together
+marker "LINK A TO B"
+link_key $aroot $b
+
+marker "LINK B TO A"
+link_key --fail $broot $a
+expect_error EDEADLK
+
+marker "UNLINK A FROM B"
+unlink_key $aroot $b
+
+marker "LINK B TO A"
+link_key $broot $a
+
+marker "LINK A TO B"
+link_key --fail $aroot $b
+expect_error EDEADLK
+
+marker "UNLINK B FROM A"
+unlink_key $broot $a
+
+# extend the stacks
+marker "EXTEND STACKS"
+create_keyring "A5" $a
+expect_keyid a
+create_keyring "B5" $b
+expect_keyid b
+
+# make sure we can't hide a cycle by linking the two bigger stacks together
+marker "CHECK MAXDEPTH A TO B"
+link_key $aroot $b
+link_key --fail $broot $a
+expect_error ELOOP
+unlink_key $aroot $b
+
+marker "CHECK MAXDEPTH B TO A"
+link_key $broot $a
+link_key --fail $aroot $b
+expect_error ELOOP
+unlink_key $broot $a
+
+# remove the two stacks
+marker "UNLINK STACKS"
+unlink_key $aroot @s
+unlink_key $broot @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/link/valid/runtest.sh b/tests/keyctl/link/valid/runtest.sh
new file mode 100644
index 0000000..fc7f28a
--- /dev/null
+++ b/tests/keyctl/link/valid/runtest.sh
@@ -0,0 +1,139 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "ADD KEYRING"
+create_keyring wibble @s
+expect_keyid keyringid
+
+# validate the new keyring's name and type
+marker "VALIDATE KEYRING"
+describe_key $keyringid
+expect_key_rdesc rdesc 'keyring@.*@wibble'
+
+# check that we can list it
+marker "LIST KEYRING"
+list_keyring $keyringid
+expect_keyring_rlist rlist empty
+
+# stick a key in the keyring
+marker "ADD KEY"
+create_key user lizard gizzard $keyringid
+expect_keyid keyid
+
+# check that we can list it
+marker "LIST KEYRING WITH ONE"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid
+
+# link the key across to the session keyring
+marker "LINK KEY 1"
+link_key $keyid @s
+
+marker "CHECK KEY LINKAGE"
+list_keyring @s
+expect_keyring_rlist srlist $keyid
+
+# link the key across to the session keyring again and again
+marker "LINK KEY 2"
+link_key $keyid @s
+
+marker "LINK KEY 3"
+link_key $keyid @s
+
+# subsequent links should displace earlier links, giving us a maximum of 1 link
+marker "COUNT LINKS"
+list_keyring @s
+expect_keyring_rlist srlist
+
+nlinks=0
+for i in $srlist
+ do
+ if [ "x$i" = "x$keyid" ]
+ then
+ nlinks=$(($nlinks + 1))
+ fi
+done
+
+if [ $nlinks != 1 ]
+then
+ failed
+fi
+
+# remove the links
+marker "UNLINK KEY FROM SESSION"
+unlink_key $keyid @s
+
+# removing again should fail
+unlink_key --fail $keyid @s
+expect_error ENOENT
+
+# remove that key from the keyring (the key should be destroyed)
+marker "UNLINK KEY FROM KEYRING"
+unlink_key --wait $keyid $keyringid
+
+# and a second time should fail, but now the key doesn't exist
+unlink_key --fail $keyid $keyringid
+expect_error ENOKEY
+
+# create a second keyring in the first
+create_keyring "zebra" $keyringid
+expect_keyid keyring2id
+
+# link thrice across to the session keyring
+marker "LINK 2ND KEYRING TO SESSION"
+link_key $keyring2id @s
+link_key $keyring2id @s
+link_key $keyring2id @s
+
+# subsequent links should displace earlier links, giving us a maximum of 1 link
+marker "COUNT KEYRING LINKS"
+list_keyring @s
+expect_keyring_rlist srlist
+
+nlinks=0
+for i in $srlist
+ do
+ if [ "x$i" = "x$keyring2id" ]
+ then
+ nlinks=$(($nlinks + 1))
+ fi
+done
+
+if [ $nlinks != 1 ]
+then
+ failed
+fi
+
+# remove the keyring links
+marker "UNLINK 2ND KEYRING FROM SESSION"
+unlink_key $keyring2id @s
+
+# removing again should fail
+unlink_key --fail $keyring2id @s
+expect_error ENOENT
+
+# make another keyring link
+marker "LINK 2ND KEYRING TO SESSION"
+link_key $keyring2id @s
+
+# remove the first keyring we added
+marker "UNLINK KEYRING"
+unlink_key --wait $keyringid @s
+
+# remove the second keyring we added
+marker "UNLINK 2ND KEYRING"
+unlink_key --wait $keyring2id @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/listing/bad-args/runtest.sh b/tests/keyctl/listing/bad-args/runtest.sh
new file mode 100644
index 0000000..beb6c99
--- /dev/null
+++ b/tests/keyctl/listing/bad-args/runtest.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that a bad key ID fails correctly
+marker "CHECK BAD KEY ID"
+list_keyring --fail 0
+expect_error ENOKEY
+pretty_list_keyring --fail 0
+expect_error ENOKEY
+
+# create a non-keyring
+marker "CREATE KEY"
+create_key user lizard gizzard @s
+expect_keyid keyid
+
+# dispose of the key we were using
+marker "UNLINK KEY"
+unlink_key --wait $keyid @s
+
+# check that a non-existent key ID fails correctly
+marker "CHECK NON-EXISTENT KEY ID"
+list_keyring --fail $keyid
+expect_error ENOKEY
+pretty_list_keyring --fail $keyid
+expect_error ENOKEY
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/listing/noargs/runtest.sh b/tests/keyctl/listing/noargs/runtest.sh
new file mode 100644
index 0000000..2d9da99
--- /dev/null
+++ b/tests/keyctl/listing/noargs/runtest.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+marker "NO ARGS"
+expect_args_error keyctl list
+expect_args_error keyctl rlist
+
+marker "TWO ARGS"
+expect_args_error keyctl list 0 0
+expect_args_error keyctl rlist 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/listing/valid/runtest.sh b/tests/keyctl/listing/valid/runtest.sh
new file mode 100644
index 0000000..fe67bd4
--- /dev/null
+++ b/tests/keyctl/listing/valid/runtest.sh
@@ -0,0 +1,109 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "ADD KEYRING"
+create_keyring wibble @s
+expect_keyid keyringid
+
+# validate the new keyring's name and type
+marker "VALIDATE KEYRING"
+describe_key $keyringid
+expect_key_rdesc rdesc 'keyring@.*@wibble'
+
+# check that we have an empty keyring
+marker "LIST KEYRING"
+list_keyring $keyringid
+expect_keyring_rlist rlist empty
+
+marker "PRETTY LIST KEYRING"
+pretty_list_keyring $keyringid
+expect_payload payload "keyring is empty"
+
+# stick a key in the keyring
+marker "ADD KEY"
+create_key user lizard gizzard $keyringid
+expect_keyid keyid
+
+# check that we can list it
+marker "LIST KEYRING WITH ONE"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid
+
+# check that we can pretty list it
+marker "PRETTY LIST KEYRING WITH ONE"
+pretty_list_keyring $keyringid
+expect_payload payload
+
+if ! expr "$payload" : " *$keyid:.*user: lizard" >&/dev/null
+then
+ failed
+fi
+
+# stick a second key in the keyring
+marker "ADD KEY 2"
+create_key user snake skin $keyringid
+expect_keyid keyid2
+
+# check that we can see both keys
+marker "LIST KEYRING WITH TWO"
+list_keyring $keyringid
+expect_keyring_rlist rlist
+
+if [ "x$rlist" != "x$keyid $keyid2" ]
+then
+ failed
+fi
+
+# check that we can see both keys prettily
+marker "PRETTY LIST KEYRING WITH TWO"
+pretty_list_keyring $keyringid
+prlist=""
+for i in `tail -2 $OUTPUTFILE | cut -d: -f1 | sed -e 's@ +@@g'`
+ do
+ prlist="$prlist $i"
+done
+
+if [ "x$prlist" != "x $keyid $keyid2" ]
+then
+ failed
+fi
+
+# turn off read permission on the keyring
+marker "DISABLE READ PERM"
+set_key_perm $keyringid 0x3d0000
+list_keyring $keyringid
+
+# turn off read and search permission on the keyring
+marker "DISABLE SEARCH PERM"
+set_key_perm $keyringid 0x350000
+list_keyring --fail $keyringid
+expect_error EACCES
+
+# turn on read permission on the keyring
+marker "REINSTATE READ PERM"
+set_key_perm $keyringid 0x370000
+list_keyring $keyringid
+
+# revoke the keyring
+marker "REVOKE KEYRING"
+revoke_key $keyringid
+list_keyring --fail $keyringid
+expect_error EKEYREVOKED
+
+# remove the keyring we added
+marker "UNLINK KEY"
+unlink_key $keyringid @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/move/bad-args/runtest.sh b/tests/keyctl/move/bad-args/runtest.sh
new file mode 100644
index 0000000..8ce62ff
--- /dev/null
+++ b/tests/keyctl/move/bad-args/runtest.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that a bad key ID fails correctly
+marker "CHECK MOVE OF BAD KEY ID"
+move_key --fail 0 @u @s
+expect_error EINVAL
+
+marker "CHECK MOVE FROM BAD KEYRING ID"
+move_key --fail @u 0 @s
+expect_error EINVAL
+
+marker "CHECK MOVE TO BAD KEYRING ID"
+move_key --fail @u @s 0
+expect_error EINVAL
+
+marker "CHECK FORCED MOVE OF BAD KEY ID"
+move_key --fail -f 0 @u @s
+expect_error EINVAL
+
+marker "CHECK FORCED MOVE FROM BAD KEYRING ID"
+move_key --fail -f @u 0 @s
+expect_error EINVAL
+
+marker "CHECK FORCED MOVE TO BAD KEYRING ID"
+move_key --fail -f @u @s 0
+expect_error EINVAL
+
+# create a pair of non-keyrings
+marker "CREATE KEY"
+create_key user lizard gizzard @s
+expect_keyid keyid
+
+marker "CREATE KEY2"
+create_key user zebra stripes @s
+expect_keyid keyid2
+
+# check that linking to a non-keyring ID fails correctly
+marker "CHECK MOVE FROM NON-KEYRING KEY"
+move_key --fail $keyid $keyid2 @s
+expect_error ENOTDIR
+
+marker "CHECK MOVE TO NON-KEYRING KEY"
+move_key --fail $keyid @s $keyid2
+expect_error ENOTDIR
+
+# dispose of the keys we were using
+marker "UNLINK KEY"
+unlink_key --wait $keyid @s
+marker "UNLINK KEY2"
+unlink_key --wait $keyid2 @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/move/noargs/runtest.sh b/tests/keyctl/move/noargs/runtest.sh
new file mode 100644
index 0000000..29a91f1
--- /dev/null
+++ b/tests/keyctl/move/noargs/runtest.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that no arguments fails correctly
+marker "NO ARGS"
+expect_args_error keyctl move
+marker "NO ARGS (F)"
+expect_args_error keyctl move -f
+
+# check that one argument fails correctly
+marker "ONE ARGS"
+expect_args_error keyctl move 0
+marker "ONE ARGS (F)"
+expect_args_error keyctl move -f 0
+
+# check that two arguments fails correctly
+marker "TWO ARGS"
+expect_args_error keyctl move 0 0
+marker "TWO ARGS (F)"
+expect_args_error keyctl move -f 0 0
+
+# check that four arguments fails correctly
+marker "FOUR ARGS"
+expect_args_error keyctl link 0 0 0 0
+marker "FOUR ARGS (F)"
+expect_args_error keyctl link -f 0 0 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/move/recursion/runtest.sh b/tests/keyctl/move/recursion/runtest.sh
new file mode 100644
index 0000000..1e68963
--- /dev/null
+++ b/tests/keyctl/move/recursion/runtest.sh
@@ -0,0 +1,213 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "CREATE KEYRING 1"
+create_keyring "first" @s
+expect_keyid keyringid
+set_key_perm $keyringid 0x3f3f0000
+
+# attempt to move a keyring to itself
+marker "RECURSE 1"
+move_key --fail $keyringid @s $keyringid
+expect_error EDEADLK
+marker "RECURSE 1F"
+move_key --fail -f $keyringid @s $keyringid
+expect_error EDEADLK
+
+# create a second keyring in the first
+marker "CREATE KEYRING 2"
+create_keyring "second" $keyringid
+expect_keyid keyring2id
+set_key_perm $keyring2id 0x3f3f0000
+
+# attempt to move a keyring to its child keyring
+marker "RECURSE 2"
+move_key --fail $keyringid @s $keyring2id
+expect_error EDEADLK
+marker "RECURSE 2F"
+move_key --fail -f $keyringid @s $keyring2id
+expect_error EDEADLK
+
+# create a third keyring in the second
+marker "CREATE KEYRING 3"
+create_keyring "third" $keyring2id
+expect_keyid keyring3id
+set_key_perm $keyring3id 0x3f3f0000
+
+# attempt to move a keyring to its grandchild keyring
+marker "RECURSE 3"
+move_key --fail $keyringid @s $keyring3id
+expect_error EDEADLK
+marker "RECURSE 3F"
+move_key --fail -f $keyringid @s $keyring3id
+expect_error EDEADLK
+
+# create a fourth keyring in the third
+marker "CREATE KEYRING 4"
+create_keyring "fourth" $keyring3id
+expect_keyid keyring4id
+set_key_perm $keyring4id 0x3f3f0000
+
+# attempt to move a keyring to its great grandchild keyring
+marker "RECURSE 4"
+move_key --fail $keyringid @s $keyring4id
+expect_error EDEADLK
+marker "RECURSE 4F"
+move_key --fail -f $keyringid @s $keyring4id
+expect_error EDEADLK
+
+# create a fifth keyring in the fourth
+marker "CREATE KEYRING 5"
+create_keyring "fifth" $keyring4id
+expect_keyid keyring5id
+set_key_perm $keyring5id 0x3f3f0000
+
+# attempt to move a keyring to its great great grandchild keyring
+marker "RECURSE 5"
+move_key --fail $keyringid @s $keyring5id
+expect_error EDEADLK
+marker "RECURSE 5F"
+move_key --fail -f $keyringid @s $keyring5id
+expect_error EDEADLK
+
+# create a sixth keyring in the fifth
+marker "CREATE KEYRING 6"
+create_keyring "sixth" $keyring5id
+expect_keyid keyring6id
+set_key_perm $keyring6id 0x3f3f0000
+
+# attempt to move a keyring to its great great great grandchild keyring
+marker "RECURSE 6"
+move_key --fail $keyringid @s $keyring6id
+expect_error EDEADLK
+marker "RECURSE 6F"
+move_key --fail -f $keyringid @s $keyring6id
+expect_error EDEADLK
+
+# create a seventh keyring in the sixth
+marker "CREATE KEYRING 7"
+create_keyring "seventh" $keyring6id
+expect_keyid keyring7id
+set_key_perm $keyring7id 0x3f3f0000
+
+# attempt to move a keyring to its great great great great grandchild keyring
+marker "RECURSE 7"
+move_key --fail $keyringid @s $keyring7id
+expect_error EDEADLK
+marker "RECURSE 7F"
+move_key --fail -f $keyringid @s $keyring7id
+expect_error EDEADLK
+
+# create an eigth keyring in the seventh
+marker "CREATE KEYRING 8"
+create_keyring "eighth" @s
+expect_keyid keyring8id
+set_key_perm $keyring8id 0x3f3f0000
+move_key $keyring8id @s $keyring7id
+
+# attempt to move a keyring to its great great great great great grandchild keyring
+marker "RECURSE 8"
+move_key --fail $keyringid @s $keyring8id
+expect_error EDEADLK
+
+# create a ninth keyring in the eighth
+marker "CREATE KEYRING 9"
+create_keyring "ninth" @s
+expect_keyid keyring9id
+set_key_perm $keyring9id 0x3f3f0000
+move_key $keyring9id @s $keyring8id
+
+# attempt to move a keyring to its great great great great great great grandchild keyring
+marker "RECURSE 9"
+move_key --fail $keyringid @s $keyring9id
+expect_error ELOOP
+marker "RECURSE 9F"
+move_key --fail -f $keyringid @s $keyring9id
+expect_error ELOOP
+
+# remove the first keyring we added
+marker "UNLINK KEYRING"
+unlink_key $keyringid @s
+
+# create two stacks of keyrings
+marker "CREATE KEYRING STACKS"
+create_keyring "A1" @s
+expect_keyid aroot
+create_keyring "B1" @s
+expect_keyid broot
+a=$aroot
+b=$broot
+
+for ((i=2; i<=4; i++))
+ do
+ create_keyring "A$i" $a
+ expect_keyid a
+ create_keyring "B$i" $b
+ expect_keyid b
+done
+
+# make sure we can't create a cycle by linking the two stacks together
+marker "LINK A TO B"
+link_key $aroot $b
+
+marker "MOVE B TO A"
+move_key --fail $broot @s $a
+expect_error EDEADLK
+marker "FORCE MOVE B TO A"
+move_key --fail -f $broot @s $a
+expect_error EDEADLK
+
+marker "UNLINK A FROM B"
+unlink_key $aroot $b
+
+marker "LINK B TO A"
+link_key $broot $a
+
+marker "MOVE A TO B"
+move_key --fail $aroot @s $b
+expect_error EDEADLK
+marker "FORCE MOVE A TO B"
+move_key --fail -f $aroot @s $b
+expect_error EDEADLK
+
+marker "UNLINK B FROM A"
+unlink_key $broot $a
+
+# extend the stacks
+marker "EXTEND STACKS"
+create_keyring "A5" $a
+expect_keyid a
+create_keyring "B5" $b
+expect_keyid b
+
+# make sure we can't hide a cycle by linking the two bigger stacks together
+marker "CHECK MAXDEPTH A TO B"
+link_key $aroot $b
+move_key --fail $broot @s $a
+expect_error ELOOP
+unlink_key $aroot $b
+
+marker "CHECK MAXDEPTH B TO A"
+link_key $broot $a
+move_key --fail $aroot @s $b
+expect_error ELOOP
+unlink_key $broot $a
+
+# remove the two stacks
+marker "UNLINK STACKS"
+unlink_key $aroot @s
+unlink_key $broot @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/move/valid/runtest.sh b/tests/keyctl/move/valid/runtest.sh
new file mode 100644
index 0000000..73bf7c2
--- /dev/null
+++ b/tests/keyctl/move/valid/runtest.sh
@@ -0,0 +1,230 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "ADD KEYRING"
+create_keyring wibble @s
+expect_keyid keyringid
+
+# stick a key in the keyring
+marker "ADD KEY"
+create_key user lizard gizzard $keyringid
+expect_keyid keyid
+
+# check that we can list it
+marker "LIST KEYRING WITH ONE"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid
+
+# move the key across to the session keyring
+marker "MOVE KEY 1"
+move_key $keyid $keyringid @s
+
+marker "CHECK KEY LINKAGE"
+list_keyring @s
+expect_keyring_rlist srlist $keyid
+
+marker "CHECK KEY REMOVED"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid --absent
+
+# Repeating the move should fail
+marker "MOVE KEY 2"
+move_key --fail $keyid $keyringid @s
+expect_error ENOENT
+
+marker "FORCE MOVE KEY 2"
+move_key --fail -f $keyid $keyringid @s
+expect_error ENOENT
+
+# Move the key back again
+marker "MOVE KEY 3"
+move_key $keyid @s $keyringid
+
+marker "MOVE KEY 4"
+move_key --fail -f $keyid @s $keyringid
+expect_error ENOENT
+
+# Create a conflicting key and try to have an unforced move displace it
+marker "ADD KEY 2"
+create_key user lizard gizzard @s
+expect_keyid keyid2
+
+marker "MOVE KEY 5"
+move_key --fail $keyid $keyringid @s
+expect_error EEXIST
+
+marker "CHECK KEY UNMOVED"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid
+
+marker "CHECK KEY UNDISPLACED"
+list_keyring @s
+expect_keyring_rlist srlist $keyid --absent
+expect_keyring_rlist srlist $keyid2
+
+# Now try a forced move
+marker "FORCE MOVE KEY 6"
+move_key -f $keyid $keyringid @s
+
+marker "CHECK KEY REMOVED"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid --absent
+expect_keyring_rlist rlist $keyid2 --absent
+
+marker "CHECK KEY DISPLACED"
+list_keyring @s
+expect_keyring_rlist srlist $keyid
+expect_keyring_rlist srlist $keyid2 --absent
+
+# Remove the link (the key should be destroyed)
+marker "UNLINK KEY FROM SESSION"
+unlink_key --wait $keyid @s
+
+# Removing again should fail
+unlink_key --fail $keyid @s
+expect_error ENOKEY
+
+# Remove that key from the keyring should also fail
+marker "UNLINK KEY FROM KEYRING"
+unlink_key --fail $keyid $keyringid
+expect_error ENOKEY
+
+###############################################################################
+# Create a second keyring in the first
+create_keyring "zebra" $keyringid
+expect_keyid keyring2id
+
+# Move thrice between the session keyring and back
+marker "LINK 2ND KEYRING TO SESSION"
+move_key $keyring2id $keyringid @s
+move_key $keyring2id @s $keyringid
+move_key $keyring2id $keyringid @s
+
+# Subsequent links should displace earlier links, giving us a maximum of 1 link
+marker "COUNT KEYRING LINKS"
+list_keyring @s
+expect_keyring_rlist srlist
+
+nlinks=0
+for i in $srlist
+ do
+ if [ "x$i" = "x$keyring2id" ]
+ then
+ nlinks=$(($nlinks + 1))
+ fi
+done
+
+if [ $nlinks != 1 ]
+then
+ failed
+fi
+
+# Remove the keyring links, destroying it
+marker "UNLINK 2ND KEYRING FROM SESSION"
+unlink_key --wait $keyring2id @s
+
+# Removing again should fail
+marker "RE-UNLINK"
+unlink_key --fail $keyring2id @s
+expect_error ENOKEY
+marker "RE-UNLINK 2"
+unlink_key --fail $keyring2id $keyringid
+expect_error ENOKEY
+
+###############################################################################
+# Create a second keyring in the session keyring
+create_keyring "zebra" @s
+expect_keyid keyring2id
+
+# Add a key to the session keyring and link it into each keyring
+marker "ADD KEY 3"
+create_key user lizard gizzard @s
+expect_keyid keyid
+
+marker "LINK KEY"
+link_key $keyid $keyringid
+marker "LINK KEY 2"
+link_key $keyid $keyring2id
+
+# Try to move the links from the keyrings into the session keyring
+marker "MOVE LINK"
+move_key --fail $keyid $keyringid @s
+expect_error EEXIST
+
+marker "CHECK LINK"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid
+
+marker "MOVE LINK 2"
+move_key --fail $keyid $keyring2id @s
+expect_error EEXIST
+
+marker "CHECK LINK 2"
+list_keyring $keyring2id
+expect_keyring_rlist rlist $keyid
+
+marker "MOVE LINK 3"
+move_key $keyid @s @s
+
+marker "CHECK LINK 3"
+list_keyring @s
+expect_keyring_rlist srlist $keyid
+
+# Try to force move the links from the keyrings into the session keyring
+marker "FORCE MOVE LINK"
+move_key -f $keyid $keyringid @s
+
+marker "CHECK LINK 4"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid --absent
+
+marker "CHECK LINK 4s"
+list_keyring @s
+expect_keyring_rlist srlist $keyid
+
+marker "FORCE MOVE LINK 2"
+move_key -f $keyid $keyring2id @s
+
+marker "CHECK LINK 5"
+list_keyring $keyring2id
+expect_keyring_rlist rlist $keyid --absent
+
+marker "CHECK LINK 5s"
+list_keyring @s
+expect_keyring_rlist srlist $keyid
+
+marker "FORCE MOVE LINK 3"
+move_key -f $keyid @s @s
+
+marker "CHECK LINK 6"
+list_keyring @s
+expect_keyring_rlist srlist $keyid
+
+# Move the key between keyrings
+marker "ROTATE"
+move_key $keyid @s $keyringid
+move_key $keyid $keyringid $keyring2id
+move_key $keyid $keyring2id @s
+
+marker "UNLINK KEY"
+unlink_key $keyid @s
+
+# remove the keyrings
+marker "UNLINK KEYRING 1"
+unlink_key --wait $keyringid @s
+marker "UNLINK KEYRING 2"
+unlink_key --wait $keyring2id @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/newring/bad-args/runtest.sh b/tests/keyctl/newring/bad-args/runtest.sh
new file mode 100644
index 0000000..9b0fe01
--- /dev/null
+++ b/tests/keyctl/newring/bad-args/runtest.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that an max length key description works correctly (4096 inc NUL)
+if [ $PAGE_SIZE -lt $maxsquota ]
+then
+ marker "CHECK MAXLEN DESC"
+ create_keyring $maxdesc @p
+ expect_keyid keyid
+else
+ marker "CHECK MAXLEN DESC FAILS WITH EDQUOT"
+ create_keyring --fail $maxdesc @p
+ expect_error EDQUOT
+fi
+
+# This doesn't work on MIPS earler than 3.19 because of a kernel bug
+kver=`uname -r`
+kmch=`uname -m`
+if kernel_at_or_later_than 3.19 ||
+ [ "$kmch" != "mips" -a "$kmch" != "mips64" ]
+then
+ # check that an overlong key description fails correctly (>4095 inc NUL)
+ marker "CHECK OVERLONG DESC"
+ create_keyring --fail a$maxdesc @p
+ expect_error EINVAL
+fi
+
+# check that an empty keyring name fails
+marker "CHECK EMPTY KEYRING NAME"
+create_keyring --fail "" @p
+expect_error EINVAL
+
+# check that a bad key ID fails correctly
+marker "CHECK BAD KEY ID"
+create_keyring --fail wibble 0
+expect_error EINVAL
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/newring/noargs/runtest.sh b/tests/keyctl/newring/noargs/runtest.sh
new file mode 100644
index 0000000..eedef68
--- /dev/null
+++ b/tests/keyctl/newring/noargs/runtest.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that no arguments fails correctly
+marker "ADD NO ARGS"
+expect_args_error keyctl newring
+
+# check that one argument fails correctly
+marker "ADD ONE ARG"
+expect_args_error keyctl newring user
+
+# check that three arguments fail correctly
+marker "ADD THREE ARGS"
+expect_args_error keyctl newring user wibble stuff
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/newring/valid/runtest.sh b/tests/keyctl/newring/valid/runtest.sh
new file mode 100644
index 0000000..b89a0d1
--- /dev/null
+++ b/tests/keyctl/newring/valid/runtest.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "ADD KEYRING"
+create_keyring wibble @s
+expect_keyid keyringid
+
+# check that we now have an empty keyring
+marker "LIST KEYRING"
+list_keyring $keyringid
+expect_keyring_rlist rlist empty
+
+# check that creating a second keyring of the same name displaces the first
+marker "ADD KEYRING AGAIN"
+create_keyring wibble @s
+expect_keyid keyringid2
+
+# should be different keyrings
+if [ "x$keyringid" == "x$keyringid2" ]
+then
+ failed
+fi
+
+# the first should no longer exist in the session keyring
+marker "LIST SESSION KEYRING"
+list_keyring @s
+expect_keyring_rlist sessionrlist $keyringid --absent
+
+# and should no longer be accessible
+marker "VALIDATE NEW KEYRING"
+pause_till_key_destroyed $keyringid
+describe_key --fail $keyringid
+expect_error ENOKEY
+
+# list the session keyring
+marker "LIST SESSION KEYRING2"
+list_keyring @s
+expect_keyring_rlist sessionrlist $keyringid2
+
+# validate the new keyring's name and type
+marker "VALIDATE NEW KEYRING2"
+describe_key $keyringid2
+expect_key_rdesc rdesc 'keyring@.*@wibble'
+
+# remove the keyring we added
+marker "UNLINK KEY"
+unlink_key $keyringid2 @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/noargs/runtest.sh b/tests/keyctl/noargs/runtest.sh
new file mode 100644
index 0000000..c00e3e7
--- /dev/null
+++ b/tests/keyctl/noargs/runtest.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+. ../../prepare.inc.sh
+. ../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+marker "CHECK NO ARGS"
+expect_args_error keyctl
+
+if [ "`sed -n -e 3p $OUTPUTFILE | cut -d: -f1`" != "Format" ]
+then
+ failed
+fi
+
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/padd/bad-args/runtest.sh b/tests/keyctl/padd/bad-args/runtest.sh
new file mode 100644
index 0000000..fc82682
--- /dev/null
+++ b/tests/keyctl/padd/bad-args/runtest.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that an empty key type fails correctly
+marker "CHECK EMPTY KEY TYPE"
+pcreate_key --fail stuff "" wibble @p
+expect_error EINVAL
+
+# check that an unsupported key type fails correctly
+marker "CHECK UNSUPPORTED KEY TYPE"
+pcreate_key --fail stuff lizardsgizzards wibble @p
+expect_error ENODEV
+
+# check that an invalid key type fails correctly
+marker "CHECK INVALID KEY TYPE"
+pcreate_key --fail stuff .user wibble @p
+expect_error EPERM
+
+# check that an maximum length invalid key type fails correctly
+marker "CHECK MAXLEN KEY TYPE"
+pcreate_key --fail stuff $maxtype wibble @p
+expect_error ENODEV
+
+# check that an overlong key type fails correctly
+marker "CHECK OVERLONG KEY TYPE"
+pcreate_key --fail stuff a$maxtype wibble @p
+expect_error EINVAL
+
+# check that creation of a keyring with non-empty payload fails correctly
+marker "CHECK ADD KEYRING WITH PAYLOAD"
+pcreate_key --fail stuff keyring wibble @p
+expect_error EINVAL
+
+# check that an max length key description works correctly
+if [ $PAGE_SIZE -lt $maxsquota ]
+then
+ marker "CHECK MAXLEN DESC"
+ pcreate_key stuff user $maxdesc @p
+ expect_keyid keyid
+else
+ marker "CHECK MAXLEN DESC FAILS WITH EDQUOT"
+ pcreate_key --fail stuff user $maxdesc @p
+ expect_error EDQUOT
+fi
+
+# This doesn't work on MIPS earler than 3.19 because of a kernel bug
+kver=`uname -r`
+kmch=`uname -m`
+if kernel_at_or_later_than 3.19 ||
+ [ "$kmch" != "mips" -a "$kmch" != "mips64" ]
+then
+ # check that an overlong key description fails correctly (>4095 inc NUL)
+ marker "CHECK OVERLONG DESC"
+ pcreate_key --fail stuff user a$maxdesc @p
+ expect_error EINVAL
+fi
+
+# check that a bad key ID fails correctly
+marker "CHECK BAD KEY ID"
+pcreate_key --fail stuff user wibble 0
+expect_error EINVAL
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/padd/noargs/runtest.sh b/tests/keyctl/padd/noargs/runtest.sh
new file mode 100644
index 0000000..5839b07
--- /dev/null
+++ b/tests/keyctl/padd/noargs/runtest.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that no arguments fails correctly
+marker "ADD NO ARGS"
+expect_args_error keyctl padd
+
+# check that one argument fails correctly
+marker "ADD ONE ARG"
+expect_args_error keyctl padd user
+
+# check that two arguments fail correctly
+marker "ADD TWO ARGS"
+expect_args_error keyctl padd user wibble
+
+# check that four arguments fail correctly
+marker "ADD FOUR ARGS"
+expect_args_error keyctl padd user wibble @s x
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/padd/useradd/runtest.sh b/tests/keyctl/padd/useradd/runtest.sh
new file mode 100644
index 0000000..206a163
--- /dev/null
+++ b/tests/keyctl/padd/useradd/runtest.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that we can add a user key to the session keyring
+marker "ADD USER KEY"
+pcreate_key stuff user wibble @s
+expect_keyid keyid
+
+# read back what we put in it
+marker "PRINT PAYLOAD"
+print_key $keyid
+expect_payload payload "stuff"
+
+# check that we can update a user key
+marker "UPDATE USER KEY"
+pcreate_key lizard user wibble @s
+
+# check we get the same key ID back
+expect_keyid keyid2
+
+if [ "x$keyid" != "x$keyid2" ]
+then
+ failed
+fi
+
+# read back what we changed it to
+marker "PRINT UPDATED PAYLOAD"
+print_key $keyid
+expect_payload payload "lizard"
+
+# remove the key we added
+marker "UNLINK KEY"
+unlink_key $keyid @s
+
+if [ $skip_root_required = 0 ] && {
+ [ $OSDIST = RHEL ] && ! version_less_than $OSRELEASE 6.6 ||
+ keyutils_at_or_later_than 1.5.6 ;
+ }
+then
+ # add keys with huge payloads
+ old_root_quota=`cat /proc/sys/kernel/keys/root_maxbytes`
+ if [ $old_root_quota -lt 65536 ]
+ then
+ marker "INCREASE QUOTA"
+ echo 65536 >/proc/sys/kernel/keys/root_maxbytes
+ fi
+
+ marker "ADD LARGE USER KEY"
+ pcreate_key_by_size 32767 user large @s
+ expect_keyid keyid
+ md5sum_key $keyid
+ expect_payload payload "f128f774ede3fe931e7c6745c4292f40"
+
+ if [ $have_big_key_type = 1 ]
+ then
+ marker "ADD SMALL BIG KEY"
+ pcreate_key_by_size 128 big_key small @s
+ expect_keyid keyid
+ md5sum_key $keyid
+ expect_payload payload "f09f35a5637839458e462e6350ecbce4"
+
+ marker "ADD HUGE BIG KEY"
+ pcreate_key_by_size $((1024*1024-1)) big_key huge @s
+ expect_keyid keyid
+ md5sum_key $keyid
+ expect_payload payload "e57598cd670284cf7d09e16ed9d4b2ac"
+ fi
+
+ marker "CLEAR KEYRING"
+ clear_keyring @s
+
+ if [ $old_root_quota -lt 65536 ]
+ then
+ marker "RESET QUOTA"
+ echo $old_root_quota >/proc/sys/kernel/keys/root_maxbytes
+ sleep 1
+ fi
+fi
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/permitting/bad-args/runtest.sh b/tests/keyctl/permitting/bad-args/runtest.sh
new file mode 100644
index 0000000..630f85f
--- /dev/null
+++ b/tests/keyctl/permitting/bad-args/runtest.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that a bad key ID fails correctly
+marker "CHECK BAD KEY ID"
+chown_key --fail 0 0
+expect_error EINVAL
+chgrp_key --fail 0 0
+expect_error EINVAL
+set_key_perm --fail 0 0
+expect_error EINVAL
+
+# create a non-keyring
+marker "CREATE KEY"
+create_key user lizard gizzard @s
+expect_keyid keyid
+
+# check that unsupported permissions aren't permitted
+marker "CHECK PERMS"
+set_key_perm --fail $keyid 0xffffffff
+expect_error EINVAL
+set_key_perm --fail $keyid 0x7f7f7f7f
+expect_error EINVAL
+
+# dispose of the key we just made
+marker "UNLINK KEY"
+unlink_key --wait $keyid @s
+
+# check that a non-existent key ID fails correctly
+marker "CHECK CLEAR NON-EXISTENT KEY ID"
+chown_key --fail $keyid 0
+expect_error ENOKEY
+chgrp_key --fail $keyid 0
+expect_error ENOKEY
+set_key_perm --fail $keyid 0
+expect_error ENOKEY
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/permitting/noargs/runtest.sh b/tests/keyctl/permitting/noargs/runtest.sh
new file mode 100644
index 0000000..5a9408f
--- /dev/null
+++ b/tests/keyctl/permitting/noargs/runtest.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+marker "NO ARGS"
+expect_args_error keyctl read
+expect_args_error keyctl pipe
+expect_args_error keyctl print
+
+marker "ONE ARG"
+expect_args_error keyctl chown 0
+expect_args_error keyctl chgrp 0
+expect_args_error keyctl setperm 0
+
+marker "THREE ARGS"
+expect_args_error keyctl chown 0 0 0
+expect_args_error keyctl chgrp 0 0 0
+expect_args_error keyctl setperm 0 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/permitting/valid/runtest.sh b/tests/keyctl/permitting/valid/runtest.sh
new file mode 100644
index 0000000..70600e7
--- /dev/null
+++ b/tests/keyctl/permitting/valid/runtest.sh
@@ -0,0 +1,99 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "ADD KEYRING"
+create_keyring wibble @s
+expect_keyid keyringid
+
+# stick a key in the keyring
+marker "ADD KEY"
+create_key user lizard gizzard $keyringid
+expect_keyid keyid
+
+# changing the key's ownership is not supported before 2.6.18-rc1
+if kernel_older_than 2.6.18
+then
+ marker "CHOWN"
+ chown_key --fail $keyid 1
+ expect_error EOPNOTSUPP
+elif [ `id -u` != 0 ]
+then
+ # must be running as root for this to work
+ marker "CHOWN"
+ chown_key --fail $keyid 1
+ expect_error EACCES
+else
+ marker "CHOWN"
+ chown_key $keyid 1
+
+ marker "CHOWN BACK"
+ chown_key $keyid 0
+fi
+
+# changing the key's group ownership is supported (change to "bin" group)
+if [ `id -u` != 0 ]
+then
+ marker "CHGRP"
+ chgrp_key --fail $keyid 1
+ expect_error EACCES
+else
+ marker "CHGRP"
+ chgrp_key $keyid 1
+ describe_key $keyid
+ expect_key_rdesc rdesc "user@.*@1@[0-9a-f]*@lizard"
+fi
+
+# check that each permission can be granted to the key
+marker "ITERATE PERMISSIONS"
+for i in \
+ 00210002 00210004 00210008 00210010 \
+ 00210200 00210400 00210800 00211000 \
+ 00230000 00250000 00290000 00310000 \
+ 02210000 04210000 08210000 10210000
+ do
+ set_key_perm $keyid 0x$i
+ describe_key $keyid
+ expect_key_rdesc rdesc "user@.*@.*@$i@lizard"
+done
+
+# check that we can't use group perms instead of user perms to view the key
+# (our UID matches that of the key)
+marker "VIEW GROUP PERMISSIONS"
+set_key_perm $keyid 0x00201f00
+describe_key --fail $keyid
+expect_error EACCES
+
+# check that we can't use other perms instead of user perms to view the key
+# (our UID matches that of the key)
+marker "VIEW OTHER PERMISSIONS"
+set_key_perm $keyid 0x0020001f
+describe_key --fail $keyid
+expect_error EACCES
+
+# check that taking away setattr permission renders the key immune to setperm
+marker "REMOVE SETATTR"
+set_key_perm $keyid 0x1f1f1f1f
+describe_key $keyid
+expect_key_rdesc rdesc "user@.*@.*@.*@lizard"
+
+marker "REINSTATE SETATTR"
+set_key_perm --fail $keyid 0x3f3f1f1f
+expect_error EACCES
+
+# remove the keyring we added
+marker "UNLINK KEYRING"
+unlink_key $keyringid @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/pupdate/bad-args/runtest.sh b/tests/keyctl/pupdate/bad-args/runtest.sh
new file mode 100644
index 0000000..bced352
--- /dev/null
+++ b/tests/keyctl/pupdate/bad-args/runtest.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# attempt to update the session keyring
+marker "CHECK UPDATE SESSION KEYRING"
+pupdate_key --fail @s a
+expect_error EOPNOTSUPP
+
+# attempt to update an invalid key
+marker "CHECK UPDATE INVALID KEY"
+pupdate_key --fail 0 a
+expect_error EINVAL
+
+# add a user key to the session keyring for us to play with
+marker "ADD USER KEY"
+create_key user wibble stuff @s
+expect_keyid keyid
+
+# remove the key we just added
+marker "UNLINK KEY"
+unlink_key --wait $keyid @s
+
+# it should fail when we attempt to update it
+marker "UPDATE UNLINKED KEY"
+pupdate_key --fail $keyid @s
+expect_error ENOKEY
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/pupdate/noargs/runtest.sh b/tests/keyctl/pupdate/noargs/runtest.sh
new file mode 100644
index 0000000..d995851
--- /dev/null
+++ b/tests/keyctl/pupdate/noargs/runtest.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that no arguments fails correctly
+marker "PUPDATE NO ARGS"
+expect_args_error keyctl pupdate
+
+# check that two arguments fail correctly
+marker "PUPDATE TWO ARGS"
+expect_args_error keyctl pupdate yyy xxxx
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/pupdate/userupdate/runtest.sh b/tests/keyctl/pupdate/userupdate/runtest.sh
new file mode 100644
index 0000000..05a77c1
--- /dev/null
+++ b/tests/keyctl/pupdate/userupdate/runtest.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that we can add a user key to the session keyring
+marker "ADD USER KEY"
+create_key user wibble stuff @s
+expect_keyid keyid
+
+# read back what we put in it
+marker "PRINT PAYLOAD"
+print_key $keyid
+expect_payload payload "stuff"
+
+# check that we can update a user key
+marker "PUPDATE USER KEY"
+pupdate_key $keyid "lizard"
+
+# read back what we changed it to
+marker "PRINT UPDATED PAYLOAD"
+print_key $keyid
+expect_payload payload "lizard"
+
+# remove the key we added
+marker "UNLINK KEY"
+unlink_key $keyid @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/reading/bad-args/runtest.sh b/tests/keyctl/reading/bad-args/runtest.sh
new file mode 100644
index 0000000..35ce9a3
--- /dev/null
+++ b/tests/keyctl/reading/bad-args/runtest.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that a bad key ID fails correctly
+marker "CHECK BAD KEY ID"
+read_key --fail 0
+expect_error ENOKEY
+print_key --fail 0
+expect_error ENOKEY
+pipe_key --fail 0
+expect_error ENOKEY
+
+# create a non-keyring
+marker "CREATE KEY"
+create_key user lizard gizzard @s
+expect_keyid keyid
+
+# dispose of the key we just made
+marker "UNLINK KEY"
+unlink_key --wait $keyid @s
+
+# check that a non-existent key ID fails correctly
+marker "CHECK CLEAR NON-EXISTENT KEY ID"
+read_key --fail $keyid
+expect_error ENOKEY
+print_key --fail $keyid
+expect_error ENOKEY
+pipe_key --fail $keyid
+expect_error ENOKEY
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/reading/noargs/runtest.sh b/tests/keyctl/reading/noargs/runtest.sh
new file mode 100644
index 0000000..4053b65
--- /dev/null
+++ b/tests/keyctl/reading/noargs/runtest.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+marker "NO ARGS"
+expect_args_error keyctl read
+expect_args_error keyctl pipe
+expect_args_error keyctl print
+
+marker "TWO ARGS"
+expect_args_error keyctl read 0 0
+expect_args_error keyctl pipe 0 0
+expect_args_error keyctl print 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/reading/valid/runtest.sh b/tests/keyctl/reading/valid/runtest.sh
new file mode 100644
index 0000000..8e4d864
--- /dev/null
+++ b/tests/keyctl/reading/valid/runtest.sh
@@ -0,0 +1,96 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "ADD KEYRING"
+create_keyring wibble @s
+expect_keyid keyringid
+
+# stick a key in the keyring
+marker "ADD KEY"
+create_key user lizard gizzard $keyringid
+expect_keyid keyid
+
+# check that the key is in the keyring
+marker "LIST KEYRING"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid
+
+# read the contents of the key
+marker "PRINT KEY"
+print_key $keyid
+expect_payload payload "gizzard"
+
+# pipe the contents of the key and add a LF as the key doesn't have one
+marker "PIPE KEY"
+pipe_key $keyid
+echo >>$OUTPUTFILE
+expect_payload payload "gizzard"
+
+# read the key as hex
+marker "READ KEY"
+read_key $keyid
+expect_payload payload "67697a7a 617264"
+
+# read the contents of the keyring as hex and match it to the key ID
+marker "READ KEYRING"
+read_key $keyringid
+tmp=`printf %08x $keyid`
+if [ "$endian" = "LE" ]
+then
+ tmp=`echo $tmp | sed 's/\(..\)\(..\)\(..\)\(..\)/\4\3\2\1/'`
+fi
+expect_payload payload $tmp
+
+# remove read permission from the key and try reading it again
+# - we should still have read permission because it's searchable in our
+# keyrings
+marker "REMOVE READ PERM"
+set_key_perm $keyid 0x3d0000
+print_key $keyid
+expect_payload payload "gizzard"
+
+# remove search permission from the key as well
+# - we should still have read permission because it's searchable in our
+# keyrings
+marker "REMOVE SEARCH PERM"
+set_key_perm $keyid 0x350000
+print_key --fail $keyid
+expect_error EACCES
+
+# check that we can read it if we have to rely on possessor perms
+# - we should still have read permission because it's searchable in our
+# keyrings
+marker "CHECK POSSESSOR READ"
+set_key_perm $keyid 0x3d000000
+print_key $keyid
+expect_payload payload "gizzard"
+
+# put read permission back again
+marker "REINSTATE READ PERM"
+set_key_perm $keyid 0x370000
+print_key $keyid
+expect_payload payload "gizzard"
+
+# revoke the key
+marker "REVOKE KEY"
+revoke_key $keyid
+print_key --fail $keyid
+expect_error EKEYREVOKED
+
+# remove the keyring we added
+marker "UNLINK KEYRING"
+unlink_key $keyringid @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/requesting/bad-args/runtest.sh b/tests/keyctl/requesting/bad-args/runtest.sh
new file mode 100644
index 0000000..83c114f
--- /dev/null
+++ b/tests/keyctl/requesting/bad-args/runtest.sh
@@ -0,0 +1,128 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+if [ $skip_install_required -eq 1 ]
+then
+ echo "++++ SKIPPING TEST" >$OUTPUTFILE
+ marker "SKIP BECAUSE TEST REQUIRES FULL INSTALL (for /sbin/request-key)"
+ toolbox_report_result $TEST PASS
+ exit 0
+else
+ echo "++++ BEGINNING TEST" >$OUTPUTFILE
+fi
+
+# check that an empty key type fails correctly
+marker "CHECK EMPTY KEY TYPE"
+request_key --fail "" debug:wibble
+expect_error EINVAL
+request_key --fail "" debug:wibble @p
+expect_error EINVAL
+request_key_callout --fail "" debug:wibble stuff
+expect_error EINVAL
+request_key_callout --fail "" debug:wibble stuff @p
+expect_error EINVAL
+prequest_key_callout --fail stuff "" debug:wibble
+expect_error EINVAL
+prequest_key_callout --fail stuff "" debug:wibble @p
+expect_error EINVAL
+
+# check that an unsupported key type fails correctly
+marker "CHECK UNSUPPORTED KEY TYPE"
+request_key --fail "lizardsgizzards" debug:wibble
+expect_error ENOKEY
+request_key --fail "lizardsgizzards" debug:wibble @p
+expect_error ENOKEY
+request_key_callout --fail "lizardsgizzards" debug:wibble stuff
+expect_error ENOKEY
+request_key_callout --fail "lizardsgizzards" debug:wibble stuff @p
+expect_error ENOKEY
+prequest_key_callout --fail stuff "lizardsgizzards" debug:wibble
+expect_error ENOKEY
+prequest_key_callout --fail stuff "lizardsgizzards" debug:wibble @p
+expect_error ENOKEY
+
+# check that an invalid key type fails correctly
+# - key types beginning with a dot are internal use only
+marker "CHECK INVALID KEY TYPE"
+request_key --fail ".user" debug:wibble
+expect_error EPERM
+request_key --fail ".user" debug:wibble @p
+expect_error EPERM
+request_key_callout --fail ".user" debug:wibble stuff
+expect_error EPERM
+request_key_callout --fail ".user" debug:wibble stuff @p
+expect_error EPERM
+prequest_key_callout --fail stuff ".user" debug:wibble
+expect_error EPERM
+prequest_key_callout --fail stuff ".user" debug:wibble @p
+expect_error EPERM
+
+# check that an maximum length invalid key type fails correctly
+marker "CHECK MAXLEN INVALID KEY TYPE"
+request_key --fail $maxtype debug:wibble
+expect_error ENOKEY
+request_key --fail $maxtype debug:wibble @p
+expect_error ENOKEY
+request_key_callout --fail $maxtype debug:wibble stuff
+expect_error ENOKEY
+request_key_callout --fail $maxtype debug:wibble stuff @p
+expect_error ENOKEY
+
+# check that an overlong key type fails correctly
+marker "CHECK OVERLONG KEY TYPE"
+request_key --fail a$maxtype debug:wibble
+expect_error EINVAL
+request_key --fail a$maxtype debug:wibble @p
+expect_error EINVAL
+request_key_callout --fail a$maxtype debug:wibble stuff
+expect_error EINVAL
+request_key_callout --fail a$maxtype debug:wibble stuff @p
+expect_error EINVAL
+
+# check that an max length key description works correctly
+marker "CHECK MAXLEN DESC"
+request_key --fail user $maxdesc
+expect_error ENOKEY
+
+# This doesn't work on MIPS earler than 3.19 because of a kernel bug
+kver=`uname -r`
+kmch=`uname -m`
+if kernel_at_or_later_than 3.19 ||
+ [ "$kmch" != "mips" -a "$kmch" != "mips64" ]
+then
+ # check that an overlong key description fails correctly
+ marker "CHECK OVERLONG DESC"
+ request_key --fail user a$maxdesc
+ expect_error EINVAL
+fi
+
+# check that a max length callout info works correctly
+marker "CHECK MAXLEN CALLOUT"
+request_key_callout --fail user wibble $maxdesc @p
+expect_error ENOKEY
+
+# check that an overlong callout info fails correctly
+marker "CHECK OVERLONG CALLOUT"
+request_key_callout --fail user wibble a$maxcall
+expect_error EINVAL
+
+# check that a max length callout info works correctly
+marker "CHECK MAXLEN PIPED CALLOUT"
+prequest_key_callout --fail $maxcall user wibble @p
+expect_error ENOKEY
+
+# check that an overlong callout info fails correctly
+marker "CHECK OVERLONG PIPED CALLOUT"
+prequest_key_callout --fail a$maxcall user wibble
+expect_error EINVAL
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/requesting/noargs/runtest.sh b/tests/keyctl/requesting/noargs/runtest.sh
new file mode 100644
index 0000000..4255122
--- /dev/null
+++ b/tests/keyctl/requesting/noargs/runtest.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+if [ $skip_install_required -eq 1 ]
+then
+ echo "++++ SKIPPING TEST" >$OUTPUTFILE
+ marker "SKIP BECAUSE TEST REQUIRES FULL INSTALL (for /sbin/request-key)"
+ toolbox_report_result $TEST PASS
+ exit 0
+else
+ echo "++++ BEGINNING TEST" >$OUTPUTFILE
+fi
+
+marker "NO ARGS"
+expect_args_error keyctl request
+expect_args_error keyctl request2
+expect_args_error keyctl prequest2
+
+marker "ONE ARG"
+expect_args_error keyctl request 0
+expect_args_error keyctl request2 0
+expect_args_error keyctl prequest2 0
+
+marker "TWO ARGS"
+expect_args_error keyctl request2 0 0
+
+marker "FOUR ARGS"
+expect_args_error keyctl request 0 0 0 0
+expect_args_error keyctl prequest2 0 0 0 0
+
+marker "FIVE ARGS"
+expect_args_error keyctl request2 0 0 0 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/requesting/piped/runtest.sh b/tests/keyctl/requesting/piped/runtest.sh
new file mode 100644
index 0000000..029ae08
--- /dev/null
+++ b/tests/keyctl/requesting/piped/runtest.sh
@@ -0,0 +1,106 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+if [ $skip_install_required -eq 1 ]
+then
+ echo "++++ SKIPPING TEST" >$OUTPUTFILE
+ marker "SKIP BECAUSE TEST REQUIRES FULL INSTALL (for /sbin/request-key)"
+ toolbox_report_result $TEST PASS
+ exit 0
+else
+ echo "++++ BEGINNING TEST" >$OUTPUTFILE
+fi
+
+set_gc_delay 10
+
+# create a pair of keyrings to play in
+marker "CREATE KEYRINGS"
+create_keyring "sandbox" @s
+expect_keyid keyringid
+
+# check that we can't yet request a non-existent key
+marker "CHECK REQUEST FAILS"
+request_key --fail user lizard $keyringid
+expect_error ENOKEY
+
+# add a user key to the first keyring
+marker "ADD USER KEY"
+create_key user lizard gizzard $keyringid
+expect_keyid keyid
+
+# request the key
+marker "REQUEST KEY"
+request_key user lizard
+expect_keyid keyid2 $keyid
+
+# remove the key from the keyring
+marker "DETACH KEY FROM KEYRING"
+unlink_key $keyid $keyringid
+
+# request a key from /sbin/request-key to the session keyring
+marker "PIPED CALL OUT REQUEST KEY TO SESSION"
+prequest_key_callout gizzard user debug:lizard
+expect_keyid keyid
+
+# should have appeared in the session keyring
+marker "CHECK ATTACHMENT TO SESSION KEYRING"
+list_keyring @s
+expect_keyring_rlist rlist $keyid
+
+# rerequesting should pick up that key again
+marker "REDO PIPED CALL OUT REQUEST KEY TO SESSION"
+prequest_key_callout gizzard user debug:lizard
+expect_keyid keyid2 $keyid
+
+# remove the key from the session
+# - it was installed twice
+# - once by request_key's keyring arg
+# - once from the instantiation call
+# but it will only have one link
+marker "DETACH KEY FROM SESSION"
+unlink_key --wait $keyid @s
+unlink_key --fail $keyid @s
+expect_error ENOKEY
+
+# request a key from /sbin/request-key to the keyring we made
+marker "PIPED CALL OUT REQUEST KEY TO KEYRING"
+prequest_key_callout gizzard user debug:lizard $keyringid
+expect_keyid keyid
+
+# should have appeared once each in the sandbox and session keyrings
+marker "CHECK ATTACHMENT TO KEYRING"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid
+
+marker "CHECK ATTACHMENT TO SESSION"
+list_keyring @s
+expect_keyring_rlist rlist $keyid
+
+# rerequesting should pick up that key again
+marker "REDO PIPED CALL OUT REQUEST KEY TO KEYRING"
+prequest_key_callout gizzard user debug:lizard $keyringid
+expect_keyid keyid2 $keyid
+
+# remove the key from the session
+marker "DETACH KEY"
+unlink_key $keyid $keyringid
+unlink_key --wait $keyid @s
+unlink_key --fail $keyid @s
+expect_error ENOKEY
+
+# remove the keyrings we added
+marker "UNLINK KEYRINGS"
+unlink_key $keyringid @s
+
+set_gc_delay $orig_gc_delay
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/requesting/valid/runtest.sh b/tests/keyctl/requesting/valid/runtest.sh
new file mode 100644
index 0000000..e49fb0f
--- /dev/null
+++ b/tests/keyctl/requesting/valid/runtest.sh
@@ -0,0 +1,106 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+if [ $skip_install_required -eq 1 ]
+then
+ echo "++++ SKIPPING TEST" >$OUTPUTFILE
+ marker "SKIP BECAUSE TEST REQUIRES FULL INSTALL (for /sbin/request-key)"
+ toolbox_report_result $TEST PASS
+ exit 0
+else
+ echo "++++ BEGINNING TEST" >$OUTPUTFILE
+fi
+
+set_gc_delay 10
+
+# create a pair of keyrings to play in
+marker "CREATE KEYRINGS"
+create_keyring "sandbox" @s
+expect_keyid keyringid
+
+# check that we can't yet request a non-existent key
+marker "CHECK REQUEST FAILS"
+request_key --fail user lizard $keyringid
+expect_error ENOKEY
+
+# add a user key to the first keyring
+marker "ADD USER KEY"
+create_key user lizard gizzard $keyringid
+expect_keyid keyid
+
+# request the key
+marker "REQUEST KEY"
+request_key user lizard
+expect_keyid keyid2 $keyid
+
+# remove the key from the keyring
+marker "DETACH KEY FROM KEYRING"
+unlink_key $keyid $keyringid
+
+# request a key from /sbin/request-key to the session keyring
+marker "CALL OUT REQUEST KEY TO SESSION"
+request_key_callout user debug:lizard gizzard
+expect_keyid keyid
+
+# should have appeared in the session keyring
+marker "CHECK ATTACHMENT TO SESSION KEYRING"
+list_keyring @s
+expect_keyring_rlist rlist $keyid
+
+# rerequesting should pick up that key again
+marker "REDO CALL OUT REQUEST KEY TO SESSION"
+request_key_callout user debug:lizard gizzard
+expect_keyid keyid2 $keyid
+
+# remove the key from the session
+# - it was installed twice
+# - once by request_key's keyring arg
+# - once from the instantiation call
+# but it will only have one link
+marker "DETACH KEY FROM SESSION"
+unlink_key --wait $keyid @s
+unlink_key --fail $keyid @s
+expect_error ENOKEY
+
+# request a key from /sbin/request-key to the keyring we made
+marker "CALL OUT REQUEST KEY TO KEYRING"
+request_key_callout user debug:lizard gizzard $keyringid
+expect_keyid keyid
+
+# should have appeared once each in the sandbox and session keyrings
+marker "CHECK ATTACHMENT TO KEYRING"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid
+
+marker "CHECK ATTACHMENT TO SESSION"
+list_keyring @s
+expect_keyring_rlist rlist $keyid
+
+# rerequesting should pick up that key again
+marker "REDO CALL OUT REQUEST KEY TO KEYRING"
+request_key_callout user debug:lizard gizzard $keyringid
+expect_keyid keyid2 $keyid
+
+# remove the key from the session
+marker "DETACH KEY"
+unlink_key $keyid $keyringid
+unlink_key --wait $keyid @s
+unlink_key --fail $keyid @s
+expect_error ENOKEY
+
+# remove the keyrings we added
+marker "UNLINK KEYRINGS"
+unlink_key $keyringid @s
+
+set_gc_delay $orig_gc_delay
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/restrict/bad-args/runtest.sh b/tests/keyctl/restrict/bad-args/runtest.sh
new file mode 100644
index 0000000..8cd073e
--- /dev/null
+++ b/tests/keyctl/restrict/bad-args/runtest.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+# ---- do the actual testing ----
+
+if [ $have_restrict_keyring = 0 ]
+then
+ toolbox_skip_test $TEST "SKIPPING DUE TO LACK OF KEYRING RESTRICTION"
+ exit 0
+fi
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring for CA keys
+marker "ADD CA KEYRING"
+create_keyring cakeyring @s
+expect_keyid cakeyringid
+
+# create a keyring
+marker "ADD KEYRING TO RESTRICT"
+create_keyring restrict @s
+expect_keyid restrictid
+
+# invalid payload
+marker "INVALID EXTRA PARAMETER 1"
+restrict_keyring --fail $restrictid "asymmetric" "key_or_keyring:$cakeyringid:bad_param"
+
+marker "INVALID EXTRA PARAMETER 2"
+restrict_keyring --fail $restrictid "asymmetric" "builtin_trusted:bad_param"
+
+marker "INVALID RESTRICT METHOD"
+restrict_keyring --fail $restrictid "asymmetric" "no_such_method:$cakeyringid"
+
+marker "INVALID KEY TYPE"
+restrict_keyring --fail $restrictid "not_a_key_type" "builtin_trusted"
+
+marker "INVALID KEY ID"
+restrict_keyring --fail $restrictid "asymmetric" "key_or_keyring:abcxyz"
+
+# invalid key option
+marker "USE KEY ID 0 FOR KEYRING"
+restrict_keyring --fail $restrictid "asymmetric" "key_or_keyring:0"
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/restrict/valid/runtest.sh b/tests/keyctl/restrict/valid/runtest.sh
new file mode 100644
index 0000000..2ed1eaf
--- /dev/null
+++ b/tests/keyctl/restrict/valid/runtest.sh
@@ -0,0 +1,512 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+if [ $have_restrict_keyring = 0 ]
+then
+ toolbox_skip_test $TEST "SKIPPING DUE TO LACK OF KEYRING RESTRICTION"
+ exit 0
+fi
+
+# ---- certs ----
+# Note: 2044-03-06 expiration
+
+# Root CA 1
+cacert1="\x30\x82\x03\xac\x30\x82\x02\x94\xa0\x03\x02\x01\x02\x02\x09\x00"
+cacert1+="\x8d\x38\x41\x1b\x39\xdd\x00\xf8\x30\x0d\x06\x09\x2a\x86\x48\x86"
+cacert1+="\xf7\x0d\x01\x01\x0b\x05\x00\x30\x41\x31\x0d\x30\x0b\x06\x03\x55"
+cacert1+="\x04\x0a\x0c\x04\x4f\x72\x67\x31\x31\x10\x30\x0e\x06\x03\x55\x04"
+cacert1+="\x03\x0c\x07\x49\x73\x73\x75\x65\x72\x31\x31\x1e\x30\x1c\x06\x09"
+cacert1+="\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x0f\x63\x61\x31\x40\x65"
+cacert1+="\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x30\x1e\x17\x0d\x31\x36"
+cacert1+="\x31\x30\x31\x39\x32\x32\x35\x34\x32\x35\x5a\x17\x0d\x34\x34\x30"
+cacert1+="\x33\x30\x36\x32\x32\x35\x34\x32\x35\x5a\x30\x41\x31\x0d\x30\x0b"
+cacert1+="\x06\x03\x55\x04\x0a\x0c\x04\x4f\x72\x67\x31\x31\x10\x30\x0e\x06"
+cacert1+="\x03\x55\x04\x03\x0c\x07\x49\x73\x73\x75\x65\x72\x31\x31\x1e\x30"
+cacert1+="\x1c\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x0f\x63\x61"
+cacert1+="\x31\x40\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x30\x82\x01"
+cacert1+="\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"
+cacert1+="\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xc4\x38"
+cacert1+="\x65\x78\x96\x72\x2b\x81\x11\x01\xea\xc0\x30\xe9\xfd\x8f\xa4\x34"
+cacert1+="\x49\x0e\x39\x94\x4c\x11\x6f\xa2\x95\xe3\x93\xb9\xdd\x8c\x7f\x8c"
+cacert1+="\xb5\xad\xe3\x62\x08\x4f\x8d\x52\x64\xfa\xcb\xdb\xe3\x3b\x58\xc3"
+cacert1+="\x71\x3d\x5e\xa3\x4f\xee\xcc\xe4\x7f\x33\xf6\xda\x76\x36\x49\x3b"
+cacert1+="\x96\x87\xdb\x24\x77\xf7\x03\xd0\x9b\x58\x4c\x28\x74\x86\x7e\xc2"
+cacert1+="\xd2\xea\x34\x67\xdc\xce\x16\x67\xef\xa6\x1e\xbf\xd5\xe1\x55\x0a"
+cacert1+="\xa3\x2f\x3b\xe5\x3d\xd6\xdb\x5c\xe8\x6b\xb9\x09\x16\xbe\x5e\x39"
+cacert1+="\xdd\xfb\xd6\x60\xfd\x41\x7b\xfa\xe5\x22\x34\x73\xcd\xcd\x71\xd0"
+cacert1+="\xca\xbc\xdf\x4d\x60\xb0\x16\xf2\x66\xe0\x79\x1a\xb1\x79\xed\xe2"
+cacert1+="\xa8\x21\xec\x7a\xc0\x65\x72\x04\xae\x03\x6a\x34\x8f\x52\x1b\x7d"
+cacert1+="\x13\xd6\x01\xb4\x12\x3f\xc0\x00\x9d\x7d\x47\xf2\x2b\x7a\x34\x6c"
+cacert1+="\xac\x70\xf0\xcc\xd7\x69\x36\x4e\x77\x8e\x7b\xdb\xa9\xeb\xc7\xfc"
+cacert1+="\x47\x2c\x74\xad\x4c\x03\x74\xe1\xa6\x60\x9f\xe1\xc5\xf1\xb4\xa4"
+cacert1+="\x65\xbf\xb9\x74\x9c\x22\x36\x8c\xd5\x9e\xef\xdd\x23\x9d\x49\x37"
+cacert1+="\x8d\x7a\xdb\xf1\x63\x23\x7e\x06\x8e\x96\x66\x2f\xbd\x5e\xf2\x53"
+cacert1+="\xb4\xc7\xb3\x0c\xb2\x85\x46\xf5\x4c\xec\x92\x38\x5d\x33\x02\x03"
+cacert1+="\x01\x00\x01\xa3\x81\xa6\x30\x81\xa3\x30\x0f\x06\x03\x55\x1d\x13"
+cacert1+="\x04\x08\x30\x06\x01\x01\xff\x02\x01\x00\x30\x1d\x06\x03\x55\x1d"
+cacert1+="\x0e\x04\x16\x04\x14\xa6\x90\x37\x33\x55\x73\x58\xcd\x8a\x4c\xaa"
+cacert1+="\xcb\x4a\xe7\x54\x0e\x00\x81\x7e\xe8\x30\x71\x06\x03\x55\x1d\x23"
+cacert1+="\x04\x6a\x30\x68\x80\x14\xa6\x90\x37\x33\x55\x73\x58\xcd\x8a\x4c"
+cacert1+="\xaa\xcb\x4a\xe7\x54\x0e\x00\x81\x7e\xe8\xa1\x45\xa4\x43\x30\x41"
+cacert1+="\x31\x0d\x30\x0b\x06\x03\x55\x04\x0a\x0c\x04\x4f\x72\x67\x31\x31"
+cacert1+="\x10\x30\x0e\x06\x03\x55\x04\x03\x0c\x07\x49\x73\x73\x75\x65\x72"
+cacert1+="\x31\x31\x1e\x30\x1c\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01"
+cacert1+="\x16\x0f\x63\x61\x31\x40\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f"
+cacert1+="\x6d\x82\x09\x00\x8d\x38\x41\x1b\x39\xdd\x00\xf8\x30\x0d\x06\x09"
+cacert1+="\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00"
+cacert1+="\x85\x84\x97\x71\xd8\x1a\xb5\xc0\x90\xbb\x95\x14\x19\xd7\x76\x30"
+cacert1+="\xf8\x42\x02\xad\x7b\x1f\x73\xbe\xf4\x78\x9d\x87\xb1\x99\xcf\xc7"
+cacert1+="\x59\xb4\x2f\xf7\x8a\xda\xbb\x07\xa1\x76\x29\x30\x4e\x41\x0f\x7e"
+cacert1+="\x97\x9c\xba\xfd\xf4\xc5\x08\x3c\x14\xb0\x7a\x6f\xf1\x67\xb2\xf9"
+cacert1+="\xb1\x46\xc8\x83\xfd\xa6\x4f\xc6\x25\x76\xf2\xc1\x11\x92\xba\xfb"
+cacert1+="\x25\x85\xe4\x1b\xc6\x92\x79\x63\xc0\x8a\xe7\xfb\x48\x69\x4d\x83"
+cacert1+="\x20\xb8\x3d\x8a\x55\xfa\x6d\xe9\x7b\xd1\x96\x75\x0a\x0f\x63\x53"
+cacert1+="\x01\x00\xf7\xc3\x28\xa1\xd8\x9e\x41\x6e\x52\xb9\x00\x43\x09\xf2"
+cacert1+="\x91\xb7\x06\x5f\xd2\x0c\x42\x03\x67\xd6\x77\xb2\xce\x96\xd4\x73"
+cacert1+="\x58\x59\xd9\xf8\x47\xb7\xc7\x23\x96\x65\xeb\xdf\x55\x1b\x1f\xc7"
+cacert1+="\xa3\x7c\x7a\x2a\x1b\x95\xec\xf8\xa0\x26\xba\x81\x4b\x94\xb6\x5f"
+cacert1+="\xa9\x7d\xb8\x0c\x46\x11\x3a\x70\x3c\xd5\x7c\x60\xd2\x38\x42\x0f"
+cacert1+="\xe1\x70\x69\xdc\x4f\x5a\xf9\x65\x45\x97\xbf\x49\xe1\xd2\x59\x5c"
+cacert1+="\x8a\xf4\xc4\xe0\x72\xb7\x2d\x4e\xbd\xe6\x69\x9d\x68\x8c\x9c\xfe"
+cacert1+="\x4a\xf7\xc0\x0d\xc9\xb9\x92\x50\xff\x5c\x9b\xb9\x9a\x51\xdd\x6c"
+cacert1+="\x91\xc7\xa9\x88\x83\xcc\xed\x49\xe7\xcf\x41\xe1\xef\x76\x55\x62"
+
+# Root CA 2
+cacert2="\x30\x82\x03\xac\x30\x82\x02\x94\xa0\x03\x02\x01\x02\x02\x09\x00"
+cacert2+="\xa7\x75\xd3\x8a\xe3\x8d\x8e\x3c\x30\x0d\x06\x09\x2a\x86\x48\x86"
+cacert2+="\xf7\x0d\x01\x01\x0b\x05\x00\x30\x41\x31\x0d\x30\x0b\x06\x03\x55"
+cacert2+="\x04\x0a\x0c\x04\x4f\x72\x67\x32\x31\x10\x30\x0e\x06\x03\x55\x04"
+cacert2+="\x03\x0c\x07\x49\x73\x73\x75\x65\x72\x32\x31\x1e\x30\x1c\x06\x09"
+cacert2+="\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x0f\x63\x61\x32\x40\x65"
+cacert2+="\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x30\x1e\x17\x0d\x31\x36"
+cacert2+="\x31\x30\x31\x39\x32\x32\x35\x34\x32\x35\x5a\x17\x0d\x34\x34\x30"
+cacert2+="\x33\x30\x36\x32\x32\x35\x34\x32\x35\x5a\x30\x41\x31\x0d\x30\x0b"
+cacert2+="\x06\x03\x55\x04\x0a\x0c\x04\x4f\x72\x67\x32\x31\x10\x30\x0e\x06"
+cacert2+="\x03\x55\x04\x03\x0c\x07\x49\x73\x73\x75\x65\x72\x32\x31\x1e\x30"
+cacert2+="\x1c\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x0f\x63\x61"
+cacert2+="\x32\x40\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x30\x82\x01"
+cacert2+="\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"
+cacert2+="\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xa5\xdf"
+cacert2+="\x33\xab\xa3\xb0\x60\xb5\x8e\x7f\xdf\x58\xfd\x71\xa0\x39\x40\x7a"
+cacert2+="\x4f\xf6\xc8\xec\xbe\xa7\x18\x4b\x4d\xd3\x73\x52\xca\xfa\xee\xa0"
+cacert2+="\x06\x21\xd8\x52\xf5\x40\x62\xc1\x4e\xd3\x6d\x9c\xc3\x12\x57\x78"
+cacert2+="\x15\x68\x67\x61\x4e\xfd\xfa\x71\xb6\xdc\xf9\x77\x36\x8f\x51\x1c"
+cacert2+="\x0a\x19\x42\x8a\x95\x6c\xf8\x2f\xd2\x1a\xe7\x39\x4c\xcd\x07\x62"
+cacert2+="\x12\x36\x8a\x5b\x97\x9b\x7a\x94\xce\x4c\x99\x36\x88\xc6\x17\x2c"
+cacert2+="\x91\xcd\x95\xa1\x48\x76\x9a\x22\xf8\x87\x84\xde\xc6\xae\x03\xa9"
+cacert2+="\x34\x93\xd8\xb3\x4c\x5f\xfb\x8b\x12\x0a\xc5\xad\xa4\x40\x56\xb6"
+cacert2+="\x44\x54\x50\xcb\x57\xe0\xb3\x77\x9d\xe2\x10\xa6\xb8\xfc\x21\x67"
+cacert2+="\x90\x61\x30\xbe\xfc\x84\xb3\x3a\x1f\x31\x1a\x8a\xc6\x37\x29\x3a"
+cacert2+="\x82\x8a\x1a\xa0\xcb\xa0\x65\x8a\x0e\x7d\xae\x03\x5c\x35\xa4\x64"
+cacert2+="\x5a\x79\xdd\xe1\xf7\x87\x91\xac\xe1\x67\x76\x48\x17\x05\x57\x01"
+cacert2+="\xa6\xf9\x36\x09\x3c\x0e\xc2\xa3\xf9\xfb\xbb\x4d\x05\xf3\x92\x42"
+cacert2+="\xf5\x4b\x9b\x2c\xab\xd5\xe8\x7c\x0a\x4a\xe6\x85\xba\x95\x28\xc1"
+cacert2+="\x04\x46\x94\x15\x1a\xe3\x61\xd5\x0b\x17\x50\xa7\x92\x98\xaa\x6a"
+cacert2+="\xe8\x91\x91\xae\x02\x6e\x35\x4b\x41\xcb\xbe\x02\xad\xb1\x02\x03"
+cacert2+="\x01\x00\x01\xa3\x81\xa6\x30\x81\xa3\x30\x0f\x06\x03\x55\x1d\x13"
+cacert2+="\x04\x08\x30\x06\x01\x01\xff\x02\x01\x00\x30\x1d\x06\x03\x55\x1d"
+cacert2+="\x0e\x04\x16\x04\x14\x44\xfb\x94\xd9\x3d\x4a\x0e\x82\x59\xc6\x90"
+cacert2+="\xa1\x38\xfa\x0e\x8c\xc4\x4a\x4f\x77\x30\x71\x06\x03\x55\x1d\x23"
+cacert2+="\x04\x6a\x30\x68\x80\x14\x44\xfb\x94\xd9\x3d\x4a\x0e\x82\x59\xc6"
+cacert2+="\x90\xa1\x38\xfa\x0e\x8c\xc4\x4a\x4f\x77\xa1\x45\xa4\x43\x30\x41"
+cacert2+="\x31\x0d\x30\x0b\x06\x03\x55\x04\x0a\x0c\x04\x4f\x72\x67\x32\x31"
+cacert2+="\x10\x30\x0e\x06\x03\x55\x04\x03\x0c\x07\x49\x73\x73\x75\x65\x72"
+cacert2+="\x32\x31\x1e\x30\x1c\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01"
+cacert2+="\x16\x0f\x63\x61\x32\x40\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f"
+cacert2+="\x6d\x82\x09\x00\xa7\x75\xd3\x8a\xe3\x8d\x8e\x3c\x30\x0d\x06\x09"
+cacert2+="\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00"
+cacert2+="\x62\xf7\xbc\xd7\x49\x2d\xa2\x47\x8b\x28\xb3\xd4\x3e\xe6\x76\x00"
+cacert2+="\x12\xe6\x15\xe2\xf7\xa5\x20\x0f\xcf\xbd\xa9\x8a\x0f\x97\x38\x57"
+cacert2+="\xac\x69\x5f\x76\x90\xdb\x52\x0a\x55\xbe\x6a\x34\xb1\x3e\xef\x45"
+cacert2+="\xed\x0a\x55\xe9\x46\xdc\x2c\xe2\xd3\x24\x7e\x3b\x10\x38\x72\x38"
+cacert2+="\x98\xae\xda\x55\xe0\x33\x7f\x86\x56\xbb\x12\x7a\x5b\x44\x4b\x8d"
+cacert2+="\xf5\xd3\xfb\x83\x1b\x07\x08\xab\x0f\x95\x07\x98\x60\xa1\x6e\x6c"
+cacert2+="\x0f\xb8\xa2\xe1\x74\xf4\x43\xd3\x91\x8e\xa0\x40\xea\x9b\x54\x3d"
+cacert2+="\x71\x1f\x75\xb4\x59\x67\x47\x95\xeb\x2e\xdc\x37\x1d\xd2\xd8\xe6"
+cacert2+="\xe5\x3a\x86\x60\xa4\x2a\xb4\x6e\x7e\x34\x09\x2b\x42\x0e\xe7\x3b"
+cacert2+="\x83\x5d\xdd\xee\xc9\xa4\x6c\x65\x49\x0b\x59\x1b\x46\xf1\x46\x2e"
+cacert2+="\xf7\x37\x0f\x06\xa1\x85\x33\x38\xee\x11\x89\x62\x3f\xf6\x42\x5e"
+cacert2+="\x1b\x6c\xfd\xcd\x4b\xa0\x8a\x71\x74\x27\xef\xbb\x6e\xbe\x68\xfc"
+cacert2+="\xb6\xcd\x88\xfe\x0e\x92\xec\xfe\x60\xe1\x26\xce\xee\x0a\x48\x6c"
+cacert2+="\x93\x1f\x85\x9f\xd8\x81\xa0\xd7\xfc\x35\xe6\xad\x63\x97\xf5\x12"
+cacert2+="\x2f\xdb\xf6\x38\x54\x0e\x6e\x7d\xcc\x88\x64\xa9\x4f\xc1\x6d\x60"
+cacert2+="\x03\x5d\x4c\x72\x6a\xa3\xf1\x29\xaa\x62\xf4\xa1\x04\x38\x78\xcd"
+
+# Intermediate CA (signed by CA 2)
+intcert="\x30\x82\x03\xa9\x30\x82\x02\x91\xa0\x03\x02\x01\x02\x02\x09\x00"
+intcert+="\xfe\x1e\xe6\xa8\x4e\x42\xc4\x72\x30\x0d\x06\x09\x2a\x86\x48\x86"
+intcert+="\xf7\x0d\x01\x01\x0b\x05\x00\x30\x41\x31\x0d\x30\x0b\x06\x03\x55"
+intcert+="\x04\x0a\x0c\x04\x4f\x72\x67\x32\x31\x10\x30\x0e\x06\x03\x55\x04"
+intcert+="\x03\x0c\x07\x49\x73\x73\x75\x65\x72\x32\x31\x1e\x30\x1c\x06\x09"
+intcert+="\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x0f\x63\x61\x32\x40\x65"
+intcert+="\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x30\x1e\x17\x0d\x31\x36"
+intcert+="\x31\x30\x31\x39\x32\x32\x35\x34\x32\x35\x5a\x17\x0d\x34\x34\x30"
+intcert+="\x33\x30\x36\x32\x32\x35\x34\x32\x35\x5a\x30\x41\x31\x0d\x30\x0b"
+intcert+="\x06\x03\x55\x04\x0a\x0c\x04\x4f\x72\x67\x32\x31\x10\x30\x0e\x06"
+intcert+="\x03\x55\x04\x03\x0c\x07\x49\x73\x73\x75\x65\x72\x32\x31\x1e\x30"
+intcert+="\x1c\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x0f\x63\x61"
+intcert+="\x32\x40\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x30\x82\x01"
+intcert+="\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00"
+intcert+="\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xe2\x07"
+intcert+="\xfb\x98\x1b\x29\xd7\x82\x9c\x64\xce\x81\xfb\x8b\x2d\xe1\x44\xa0"
+intcert+="\x0e\x2e\xc9\xb6\xb8\x16\x7d\x9a\xcf\xa4\xb7\x21\x2f\xdc\x4c\x79"
+intcert+="\xfe\xe4\x34\xc8\x94\x93\xdf\x5a\xdd\x94\x33\xf8\xda\x0d\x17\xb7"
+intcert+="\x0a\x4d\x82\x72\x9a\x6f\xfa\xac\x3f\x4a\x1b\x78\x1a\x99\xa3\x24"
+intcert+="\x66\x0b\xc4\x72\x2a\xe0\xb4\xbd\xd5\xa1\x06\xbf\xdb\xd0\xdf\x8b"
+intcert+="\x15\xee\xa9\x18\x50\x7a\x83\x53\x21\xf7\xbb\x4b\xce\x57\xe8\x8b"
+intcert+="\x02\x9d\x3a\x73\x3e\x5d\x67\xa5\x11\xde\xec\xc2\x4e\xbe\xfe\x09"
+intcert+="\x40\xfd\x22\xb5\x2e\xb2\x2b\xe7\x07\x81\x0c\x72\xf3\xbf\x22\x55"
+intcert+="\xaa\xb9\x02\xb9\x48\x8c\x89\xb6\xf3\x5d\x27\x9a\xd5\xc2\x47\xf9"
+intcert+="\xb9\x0c\xb5\x90\xbf\x8b\xee\xd8\x8c\x3d\x2d\xbf\xa1\xb0\xcf\xe2"
+intcert+="\xd9\x44\x9b\x88\x66\xb3\x6e\xec\xe6\xaa\x2c\x67\x2e\x6f\x2b\x23"
+intcert+="\xd6\xe2\xa1\xb1\x7a\x0d\xb1\x50\x30\xe4\x22\xec\xbf\x87\x28\xcc"
+intcert+="\xa0\x73\x42\x4a\xdc\x5d\x2b\x37\x7e\x43\x63\x59\x01\xaa\x9e\xc4"
+intcert+="\x4b\xfa\x05\x38\x4d\xc2\xde\x41\xb1\x60\xd1\x4b\xfd\x89\x9e\xe0"
+intcert+="\xdf\xff\x21\x91\x1c\x6f\xba\x10\xba\x15\xe3\x44\xf0\x1f\xd8\x43"
+intcert+="\x2e\x03\xc7\xf3\x51\xa3\x36\xfd\x25\x71\x4b\x87\x65\x5b\x02\x03"
+intcert+="\x01\x00\x01\xa3\x81\xa3\x30\x81\xa0\x30\x0c\x06\x03\x55\x1d\x13"
+intcert+="\x04\x05\x30\x03\x01\x01\xff\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16"
+intcert+="\x04\x14\xf5\x71\x46\x2e\x96\x8c\x27\x3e\x47\xef\xba\x8a\x2c\x1c"
+intcert+="\xb5\x15\x88\xa7\x24\x14\x30\x71\x06\x03\x55\x1d\x23\x04\x6a\x30"
+intcert+="\x68\x80\x14\x44\xfb\x94\xd9\x3d\x4a\x0e\x82\x59\xc6\x90\xa1\x38"
+intcert+="\xfa\x0e\x8c\xc4\x4a\x4f\x77\xa1\x45\xa4\x43\x30\x41\x31\x0d\x30"
+intcert+="\x0b\x06\x03\x55\x04\x0a\x0c\x04\x4f\x72\x67\x32\x31\x10\x30\x0e"
+intcert+="\x06\x03\x55\x04\x03\x0c\x07\x49\x73\x73\x75\x65\x72\x32\x31\x1e"
+intcert+="\x30\x1c\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x0f\x63"
+intcert+="\x61\x32\x40\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x82\x09"
+intcert+="\x00\xa7\x75\xd3\x8a\xe3\x8d\x8e\x3c\x30\x0d\x06\x09\x2a\x86\x48"
+intcert+="\x86\xf7\x0d\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x53\xb3\x9a"
+intcert+="\xfa\x63\xdb\x3e\x20\x7b\x8a\xa3\xc0\x8e\x31\x18\x80\x6a\x75\x8b"
+intcert+="\x9c\xdc\x34\xfb\xc6\xd2\x52\x4b\x60\x17\x33\x72\x62\xa4\xf8\xfd"
+intcert+="\x7d\x19\x52\x88\x25\x5b\xbe\x86\x46\x75\x8b\x77\x21\xe7\x47\x98"
+intcert+="\xf5\x8b\xbc\x09\x1a\x33\xa8\xdf\xdb\x98\x5a\x92\x40\x74\x3e\x65"
+intcert+="\xd8\x9e\x56\x65\xbb\x62\x26\xa7\x32\xe2\x4b\x03\xd5\x16\xbe\xb6"
+intcert+="\xa7\x23\xb2\xe7\x36\x4f\x11\x05\xd1\x1c\x12\x70\x35\x70\xcb\xe6"
+intcert+="\x99\xf8\x4b\x3a\x87\x27\xe5\xe6\x08\x5f\x06\x7e\x83\x85\x9a\x34"
+intcert+="\xc2\x8a\xfa\x52\x82\xf7\xbd\x71\xac\xc9\xec\x9b\x9a\x70\xa6\x6b"
+intcert+="\x2e\xee\x62\xa3\x0c\x07\xd2\xbd\x8a\xfc\x8a\x4b\xe9\x4a\xe9\x11"
+intcert+="\xdd\x6e\xbb\x94\xb8\x9b\xc7\x79\x4d\x82\xdd\x0f\x62\x1d\x84\xf7"
+intcert+="\x00\xec\x37\xe5\x56\x5f\x39\x18\x85\x0e\xef\x20\xdf\x3f\x97\x96"
+intcert+="\x03\x84\x50\x21\x9d\xe0\x00\x44\x19\x82\x58\x6b\x79\xfa\x32\x34"
+intcert+="\x65\x4b\xed\x36\xae\x1a\xd8\x78\xae\x59\xf0\xb8\xb7\xb0\xe3\xb4"
+intcert+="\x23\x71\xe7\xda\x18\xff\x6d\x6e\x3b\x8b\xc8\x13\x52\x1c\xc5\xff"
+intcert+="\x66\xfd\xe1\x7a\x28\xac\x3e\xe5\x0e\x80\x03\x91\xce\x30\x2b\x98"
+intcert+="\x3b\x71\x2f\x37\x80\xf1\x29\xbc\xdb\x42\xa1\x6e\xd0"
+
+# End entity cert (signed by CA 2)
+signed="\x30\x82\x03\xa3\x30\x82\x02\x8b\xa0\x03\x02\x01\x02\x02\x09\x00"
+signed+="\xfe\x1e\xe6\xa8\x4e\x42\xc4\x73\x30\x0d\x06\x09\x2a\x86\x48\x86"
+signed+="\xf7\x0d\x01\x01\x0b\x05\x00\x30\x41\x31\x0d\x30\x0b\x06\x03\x55"
+signed+="\x04\x0a\x0c\x04\x4f\x72\x67\x32\x31\x10\x30\x0e\x06\x03\x55\x04"
+signed+="\x03\x0c\x07\x49\x73\x73\x75\x65\x72\x32\x31\x1e\x30\x1c\x06\x09"
+signed+="\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x0f\x63\x61\x32\x40\x65"
+signed+="\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x30\x1e\x17\x0d\x31\x36"
+signed+="\x31\x30\x31\x39\x32\x32\x35\x34\x32\x35\x5a\x17\x0d\x34\x34\x30"
+signed+="\x33\x30\x36\x32\x32\x35\x34\x32\x35\x5a\x30\x3e\x31\x0c\x30\x0a"
+signed+="\x06\x03\x55\x04\x0a\x0c\x03\x4f\x72\x67\x31\x0f\x30\x0d\x06\x03"
+signed+="\x55\x04\x03\x0c\x06\x49\x73\x73\x75\x65\x72\x31\x1d\x30\x1b\x06"
+signed+="\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x0e\x65\x65\x40\x65"
+signed+="\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x30\x82\x01\x22\x30\x0d"
+signed+="\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01"
+signed+="\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xa2\xc7\x25\x7d\x83"
+signed+="\xb3\xbb\x04\x04\xae\xc1\x53\x85\xdc\x61\x4f\xb9\x5c\x80\x03\x86"
+signed+="\x4c\x1b\xbe\x57\x39\xb0\x43\x09\x37\xce\x5d\x98\xcc\x74\x38\xa8"
+signed+="\xf6\x6f\x6a\x9e\x3d\xd6\x88\x62\x61\x35\x7f\x85\x73\x06\x48\x1e"
+signed+="\x65\x69\xc3\xf4\xf3\x6e\xa2\x79\xfa\x12\xd0\x80\xb4\x11\xc6\x8f"
+signed+="\x13\xb5\xc4\x42\xe0\x22\x54\x4a\x46\x47\xaf\x61\xcb\x35\xd2\xea"
+signed+="\xe9\x5e\x0d\x0e\xc1\x36\x72\x6a\xd3\xa0\x4e\xe9\xf1\x9e\xbd\xbd"
+signed+="\x21\xd6\x8d\x0b\x24\x7a\x8b\xc1\xcc\x52\x48\xc9\xb5\x4f\x1c\xe0"
+signed+="\xaf\xe3\x8b\x24\xbb\x34\x9f\x8f\x03\x13\xe8\x2e\xee\xd4\xda\x17"
+signed+="\x25\x3f\x2d\xf3\x7e\x33\x3f\x97\x42\xef\xaf\x4c\x35\xb8\x02\x60"
+signed+="\xc0\x4f\x20\xbe\x97\xb7\xd3\xc3\x38\x73\x14\x7d\x68\x2a\xab\x04"
+signed+="\x96\x81\xde\xe6\x5a\x03\x1e\xd6\x07\xe8\x0a\x38\xc0\xf0\x2b\x54"
+signed+="\xb2\x9b\xbd\x54\x72\x8e\x97\x76\x5e\x4c\x80\xb8\x19\xf8\x69\x17"
+signed+="\x99\x41\xd9\x61\xb5\x9d\x4c\x8d\xbb\x19\x5f\x39\x7e\xc9\x19\x8d"
+signed+="\xdc\xa4\x21\xf1\x32\xfe\x89\x9a\xa3\x92\xe5\x76\x73\x64\x67\x29"
+signed+="\x34\x4a\x31\x02\xaa\x35\xaf\x88\x06\x0b\x63\x4d\xe4\xa2\x1f\xaa"
+signed+="\x48\x9f\x85\xf5\xc4\xd4\x03\x52\x29\xb7\x51\x02\x03\x01\x00\x01"
+signed+="\xa3\x81\xa0\x30\x81\x9d\x30\x09\x06\x03\x55\x1d\x13\x04\x02\x30"
+signed+="\x00\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\xfc\x91\xdf\xb8"
+signed+="\x99\x8a\x5c\x07\xee\xb6\x32\xcc\x79\x0b\x31\x0a\x3a\xca\x65\x23"
+signed+="\x30\x71\x06\x03\x55\x1d\x23\x04\x6a\x30\x68\x80\x14\x44\xfb\x94"
+signed+="\xd9\x3d\x4a\x0e\x82\x59\xc6\x90\xa1\x38\xfa\x0e\x8c\xc4\x4a\x4f"
+signed+="\x77\xa1\x45\xa4\x43\x30\x41\x31\x0d\x30\x0b\x06\x03\x55\x04\x0a"
+signed+="\x0c\x04\x4f\x72\x67\x32\x31\x10\x30\x0e\x06\x03\x55\x04\x03\x0c"
+signed+="\x07\x49\x73\x73\x75\x65\x72\x32\x31\x1e\x30\x1c\x06\x09\x2a\x86"
+signed+="\x48\x86\xf7\x0d\x01\x09\x01\x16\x0f\x63\x61\x32\x40\x65\x78\x61"
+signed+="\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x82\x09\x00\xa7\x75\xd3\x8a\xe3"
+signed+="\x8d\x8e\x3c\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b"
+signed+="\x05\x00\x03\x82\x01\x01\x00\x36\x04\xff\x32\x8c\x5e\x7b\x2d\x99"
+signed+="\x2e\x84\x2f\xae\xed\x82\x96\x99\xe1\x18\xe0\x8c\xe9\x61\x3c\x0f"
+signed+="\xb1\xd7\x53\x5e\x24\x84\xe7\xe9\x19\xaa\xfe\xed\x21\x82\x3b\x6c"
+signed+="\xf4\xaa\xe9\x0a\x84\x88\x84\x1a\x46\xc7\xc8\x00\x3f\x94\x86\x2c"
+signed+="\xc5\x28\xa3\xac\xbd\x8a\x6f\x53\x38\x7f\x06\x12\x51\xbc\x1b\x4d"
+signed+="\x2d\xcb\x63\x65\x4b\x74\x42\x4d\xc8\xd6\x56\x03\xfe\x81\x9b\x52"
+signed+="\x3a\xc8\xb7\xcc\xd0\x03\x85\xad\x30\x7c\x3d\x73\xd1\x06\x16\x70"
+signed+="\x4a\xcc\x15\xa4\x83\xcf\x54\x0f\xd7\xab\x9a\xf0\xb9\x39\x80\x06"
+signed+="\x7f\x75\x45\x2e\xec\x76\xb2\xbd\xb8\xfd\xe3\x38\xdb\x18\x2f\x09"
+signed+="\xea\xe3\x1c\xfe\xd2\x6c\xab\xae\xaa\x9c\x47\xfb\xe5\xd6\x0d\xd6"
+signed+="\x6f\x42\x5f\x0b\xde\xa3\xc1\xb5\xf7\xaf\x5a\xf1\xab\xe8\xc4\x7c"
+signed+="\xeb\xaf\x48\xe4\x5d\x61\xa1\x5b\xf1\x28\x8f\xd0\x58\x1d\x9f\x64"
+signed+="\x8b\xf8\xf0\x33\xa6\xeb\x0b\xb2\xc5\x62\x09\xd8\x4c\x72\x4d\xae"
+signed+="\x04\xc7\x45\xeb\xdc\xa6\xb2\xf0\xfb\x2b\xb0\x81\xb6\x4f\x3e\x12"
+signed+="\x56\x61\x42\x74\x64\x66\x90\xb5\x72\x8f\x09\x8a\x8d\x6c\xc7\x02"
+signed+="\x0a\xc8\x51\xe0\x11\x00\x3c\xad\xf2\x5b\x29\xeb\x60\x2f\xb4\xdd"
+signed+="\x73\xc0\xb7\xdb\x1f\x6d\xb1"
+
+# End entity cert (signed by intermediate CA)
+intsigned="\x30\x82\x03\xa4\x30\x82\x02\x8c\xa0\x03\x02\x01\x02\x02\x09\x00"
+intsigned+="\xe5\x03\xc2\x77\xbb\xb3\x95\x45\x30\x0d\x06\x09\x2a\x86\x48\x86"
+intsigned+="\xf7\x0d\x01\x01\x0b\x05\x00\x30\x41\x31\x0d\x30\x0b\x06\x03\x55"
+intsigned+="\x04\x0a\x0c\x04\x4f\x72\x67\x32\x31\x10\x30\x0e\x06\x03\x55\x04"
+intsigned+="\x03\x0c\x07\x49\x73\x73\x75\x65\x72\x32\x31\x1e\x30\x1c\x06\x09"
+intsigned+="\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x0f\x63\x61\x32\x40\x65"
+intsigned+="\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x30\x1e\x17\x0d\x31\x36"
+intsigned+="\x31\x30\x31\x39\x32\x32\x35\x34\x32\x35\x5a\x17\x0d\x34\x34\x30"
+intsigned+="\x33\x30\x36\x32\x32\x35\x34\x32\x35\x5a\x30\x3f\x31\x0c\x30\x0a"
+intsigned+="\x06\x03\x55\x04\x0a\x0c\x03\x4f\x72\x67\x31\x0f\x30\x0d\x06\x03"
+intsigned+="\x55\x04\x03\x0c\x06\x49\x73\x73\x75\x65\x72\x31\x1e\x30\x1c\x06"
+intsigned+="\x09\x2a\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x0f\x65\x65\x63\x40"
+intsigned+="\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x30\x82\x01\x22\x30"
+intsigned+="\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82"
+intsigned+="\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\x92\xfd\x9d\x2c"
+intsigned+="\x31\xf2\xac\x18\xb3\x73\x7c\xf3\x7c\xd1\xea\x59\x48\xb0\xde\x55"
+intsigned+="\xc7\xa3\xf6\xbd\xa4\xd7\x34\x86\xb3\x2d\x38\x8c\xb4\x44\xec\xd0"
+intsigned+="\x02\xcf\x9a\xff\xf3\xa7\x9b\x44\xd5\x83\x8a\xb5\x0c\x44\xde\x68"
+intsigned+="\x14\xc3\x65\xeb\x34\x64\x90\xff\x00\x7e\x1e\x2e\x97\x12\xf3\x94"
+intsigned+="\x24\x08\x73\xeb\x59\x2d\x54\x35\x76\x7a\x43\x97\x7c\xe4\xe8\xa0"
+intsigned+="\x2c\x20\x5e\x5b\xc8\xaf\xa7\xf7\x0e\x3e\xc4\x4a\x2c\xd6\xd4\x3a"
+intsigned+="\x0e\x20\x70\x33\x57\x46\x12\xbe\x89\xd2\x4a\x17\xde\xca\x9a\xde"
+intsigned+="\x7f\x40\xa9\x06\x27\x61\xe2\x4b\x3f\xda\x24\x02\x20\x06\x83\xb2"
+intsigned+="\x87\x7e\xa0\xf6\xc7\xfd\x4e\x91\xba\x9f\x32\x9b\x61\x32\x0a\xc0"
+intsigned+="\xc1\x21\xaa\xce\xfc\xae\x22\xd9\x85\xc6\xeb\x94\xca\xe3\xb3\x05"
+intsigned+="\xf6\x00\xa6\xe4\xc8\xf3\xfe\x3e\xb0\xc0\x4d\x1c\x13\x44\xa2\x61"
+intsigned+="\xb0\x3d\xaa\xda\xbe\x60\x69\xbc\x91\x24\x96\x33\x0f\x6d\x12\x51"
+intsigned+="\x32\x27\xb3\x5e\x04\x02\x81\x5c\x48\x6b\x50\xcb\xad\x91\xe1\xfa"
+intsigned+="\xb1\x00\x56\x00\x22\xd6\x1a\xb6\xad\xf0\x1c\x1a\x30\x58\x0c\x45"
+intsigned+="\x6e\xd8\xcc\x50\xbb\xe1\x14\x8a\x20\x6b\xc3\x0b\x32\x66\x43\x9e"
+intsigned+="\xf4\x49\x52\x79\x41\x59\x49\xff\x2d\xe1\xbc\x49\x02\x03\x01\x00"
+intsigned+="\x01\xa3\x81\xa0\x30\x81\x9d\x30\x09\x06\x03\x55\x1d\x13\x04\x02"
+intsigned+="\x30\x00\x30\x1d\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\xf7\xde\x3c"
+intsigned+="\x11\x91\x03\x2f\x77\xa7\x71\x1b\xa3\xa1\x73\x36\x01\x66\x29\xcc"
+intsigned+="\x2b\x30\x71\x06\x03\x55\x1d\x23\x04\x6a\x30\x68\x80\x14\xf5\x71"
+intsigned+="\x46\x2e\x96\x8c\x27\x3e\x47\xef\xba\x8a\x2c\x1c\xb5\x15\x88\xa7"
+intsigned+="\x24\x14\xa1\x45\xa4\x43\x30\x41\x31\x0d\x30\x0b\x06\x03\x55\x04"
+intsigned+="\x0a\x0c\x04\x4f\x72\x67\x32\x31\x10\x30\x0e\x06\x03\x55\x04\x03"
+intsigned+="\x0c\x07\x49\x73\x73\x75\x65\x72\x32\x31\x1e\x30\x1c\x06\x09\x2a"
+intsigned+="\x86\x48\x86\xf7\x0d\x01\x09\x01\x16\x0f\x63\x61\x32\x40\x65\x78"
+intsigned+="\x61\x6d\x70\x6c\x65\x2e\x63\x6f\x6d\x82\x09\x00\xfe\x1e\xe6\xa8"
+intsigned+="\x4e\x42\xc4\x72\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01"
+intsigned+="\x0b\x05\x00\x03\x82\x01\x01\x00\xc3\xce\xd4\x43\x63\x19\xa3\xa2"
+intsigned+="\x4c\x84\x26\xeb\x62\x88\xb7\xcd\xe1\x83\xff\x61\x49\x54\xcf\x2e"
+intsigned+="\xad\x6e\x68\x1f\xe5\x2a\xd8\xd5\x5a\x92\x1a\xa4\x7a\x23\x74\x0a"
+intsigned+="\x7d\x75\xf6\xc6\x3c\x79\x14\xe6\x52\xb1\xfd\xdb\x42\x04\xdb\x79"
+intsigned+="\xc4\xfa\x74\x4b\x26\x26\x01\xfc\x70\xc4\xa5\xfb\xd4\x89\x63\xd7"
+intsigned+="\xf5\x9b\xd5\xae\xf5\xd4\x8b\x3a\x3c\xc6\x51\x36\xff\xf8\xb3\x21"
+intsigned+="\x68\x4d\xa2\xd2\x92\xe2\x5e\x78\xc6\x4a\x10\xa5\x88\x7c\x1e\xf2"
+intsigned+="\x1c\x68\xdc\xe9\x8f\x58\xb1\xa6\xe5\xc6\x4d\xdb\xf7\xc9\x66\x2f"
+intsigned+="\xd8\xc7\x86\x83\x63\x14\xf3\x4e\x2a\x39\xf2\x2d\x50\x3a\x53\x7d"
+intsigned+="\xfb\x91\xe4\x54\x7d\x33\xdf\x13\x63\x6f\x21\x9d\x12\xad\x48\x5d"
+intsigned+="\xf9\x41\xa1\x10\x80\xcd\x28\x9a\x4c\x82\x60\xae\x66\xc4\x85\x6f"
+intsigned+="\x55\x1e\x5d\x97\x49\x60\x1d\xdb\x26\x31\x1e\x0f\x08\x80\x11\x30"
+intsigned+="\x0f\x75\xad\x32\xfd\x28\xe4\xb9\x09\x30\x26\x32\xcd\x32\x83\x0c"
+intsigned+="\x4b\x51\x4e\x68\x70\xfc\x5b\x52\x9f\x3f\x27\xe7\xe9\xe2\x89\xa9"
+intsigned+="\xc0\x45\x42\xc7\xb7\x6d\x93\xb4\x02\xf4\xdb\xfd\x1c\x32\xc9\x25"
+intsigned+="\x4d\x4d\xff\xab\x72\xb5\x78\xb9\xd1\xcc\xdb\x73\x39\x6f\x7c\x84"
+intsigned+="\x6c\xc9\xf1\xb5\xcb\x81\x53\x8c"
+
+# Self-signed cert
+selfsign="\x30\x82\x03\x9d\x30\x82\x02\x85\xa0\x03\x02\x01\x02\x02\x09\x00"
+selfsign+="\xe1\xaa\x44\xda\xac\x8f\x12\x04\x30\x0d\x06\x09\x2a\x86\x48\x86"
+selfsign+="\xf7\x0d\x01\x01\x0b\x05\x00\x30\x3e\x31\x0c\x30\x0a\x06\x03\x55"
+selfsign+="\x04\x0a\x0c\x03\x4f\x72\x67\x31\x0f\x30\x0d\x06\x03\x55\x04\x03"
+selfsign+="\x0c\x06\x49\x73\x73\x75\x65\x72\x31\x1d\x30\x1b\x06\x09\x2a\x86"
+selfsign+="\x48\x86\xf7\x0d\x01\x09\x01\x16\x0e\x73\x73\x40\x65\x78\x61\x6d"
+selfsign+="\x70\x6c\x65\x2e\x63\x6f\x6d\x30\x1e\x17\x0d\x31\x36\x31\x30\x31"
+selfsign+="\x39\x32\x32\x35\x34\x32\x35\x5a\x17\x0d\x34\x34\x30\x33\x30\x36"
+selfsign+="\x32\x32\x35\x34\x32\x35\x5a\x30\x3e\x31\x0c\x30\x0a\x06\x03\x55"
+selfsign+="\x04\x0a\x0c\x03\x4f\x72\x67\x31\x0f\x30\x0d\x06\x03\x55\x04\x03"
+selfsign+="\x0c\x06\x49\x73\x73\x75\x65\x72\x31\x1d\x30\x1b\x06\x09\x2a\x86"
+selfsign+="\x48\x86\xf7\x0d\x01\x09\x01\x16\x0e\x73\x73\x40\x65\x78\x61\x6d"
+selfsign+="\x70\x6c\x65\x2e\x63\x6f\x6d\x30\x82\x01\x22\x30\x0d\x06\x09\x2a"
+selfsign+="\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x00\x30"
+selfsign+="\x82\x01\x0a\x02\x82\x01\x01\x00\xba\x2e\x4e\x75\x7a\x1f\xf9\xe7"
+selfsign+="\x58\xda\x54\xb1\xf5\xcf\x26\xad\x1a\x5d\xd6\xa5\xf7\xf1\xe4\xef"
+selfsign+="\x2d\x8c\xc9\x03\x69\x39\x55\xbd\xa2\xb1\x8b\x00\xf1\x08\x21\x4b"
+selfsign+="\x1d\x47\xff\x88\x2c\x56\x1a\x90\x2b\xaa\x32\x88\xa0\xa7\xd8\x58"
+selfsign+="\x5c\x74\xde\xaa\x8a\x17\x89\xc9\x3c\x6b\x17\x97\x90\x82\x8c\xf6"
+selfsign+="\xa1\x1e\x1a\x6e\x30\xb3\x56\xad\x4a\xdb\x50\xb3\x0b\xe2\x70\xdd"
+selfsign+="\xf2\x61\xf8\xa6\x8c\xf7\xcb\x2a\xb0\xd3\xd3\x14\x3e\xe6\x80\xe3"
+selfsign+="\x32\xd9\x25\x5c\x8f\x40\x5e\xb4\x71\x33\xd8\xfc\x1c\x9f\xd0\x95"
+selfsign+="\x90\x56\xe6\x7d\x4e\x39\x6d\xc1\x3a\xd9\x28\x9f\x40\xe7\x13\x17"
+selfsign+="\x7c\x9d\xdd\x6b\xd0\xf7\xe9\x68\xfa\xc2\x28\xb3\x45\x12\x5b\xfe"
+selfsign+="\x57\x72\x6c\x86\x9f\x18\xd6\xb6\x6b\xc8\x7c\x12\xca\x4e\x2f\xc7"
+selfsign+="\x25\x88\x92\x4e\x9f\xdd\x02\x6d\x37\x82\xf1\x8b\xd3\xf5\x6f\xf1"
+selfsign+="\x4c\xfa\xd8\x80\xe2\x23\xc6\xb7\x8f\x1e\x76\x45\x91\xc5\xf6\x30"
+selfsign+="\x48\x9f\xdb\x56\x31\x24\xed\xc8\xfd\x85\x21\x53\x79\xb0\x96\x51"
+selfsign+="\x5a\x1e\x82\x21\x95\x5a\x7f\xf1\x32\x25\x7a\x3c\xb4\x16\x6f\x6e"
+selfsign+="\xb8\xf2\xf2\xed\x75\xe9\x8b\x7a\xdb\xee\x5c\xf2\x63\x92\x47\xb7"
+selfsign+="\x7f\x07\x53\x8e\x14\x5c\x9e\xb3\x02\x03\x01\x00\x01\xa3\x81\x9d"
+selfsign+="\x30\x81\x9a\x30\x09\x06\x03\x55\x1d\x13\x04\x02\x30\x00\x30\x1d"
+selfsign+="\x06\x03\x55\x1d\x0e\x04\x16\x04\x14\x6a\x1d\x32\xc2\x5a\x0e\x9d"
+selfsign+="\xef\xb3\x59\x2e\x0e\xcf\x2e\x99\x4e\x30\x8d\x61\x1a\x30\x6e\x06"
+selfsign+="\x03\x55\x1d\x23\x04\x67\x30\x65\x80\x14\x6a\x1d\x32\xc2\x5a\x0e"
+selfsign+="\x9d\xef\xb3\x59\x2e\x0e\xcf\x2e\x99\x4e\x30\x8d\x61\x1a\xa1\x42"
+selfsign+="\xa4\x40\x30\x3e\x31\x0c\x30\x0a\x06\x03\x55\x04\x0a\x0c\x03\x4f"
+selfsign+="\x72\x67\x31\x0f\x30\x0d\x06\x03\x55\x04\x03\x0c\x06\x49\x73\x73"
+selfsign+="\x75\x65\x72\x31\x1d\x30\x1b\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01"
+selfsign+="\x09\x01\x16\x0e\x73\x73\x40\x65\x78\x61\x6d\x70\x6c\x65\x2e\x63"
+selfsign+="\x6f\x6d\x82\x09\x00\xe1\xaa\x44\xda\xac\x8f\x12\x04\x30\x0d\x06"
+selfsign+="\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x0b\x05\x00\x03\x82\x01\x01"
+selfsign+="\x00\xb6\x43\x7f\xb0\x66\xfd\x00\x99\x0e\x23\x9c\x17\x5e\x11\xe5"
+selfsign+="\x1f\x24\x85\xee\x5e\x12\x9b\xa0\xb0\x4f\x24\x8f\xf1\xaf\x78\x01"
+selfsign+="\x64\xc4\x32\x0c\x97\xb0\x10\x88\xac\x2c\xf1\xe4\x2c\x51\x0f\x16"
+selfsign+="\x6b\xd8\x66\xca\x8e\x63\xda\x41\x36\xec\x75\x3d\x99\xe3\x54\x27"
+selfsign+="\xd4\x91\xc7\xcf\x6b\x41\xa8\xa6\xd0\x42\x5e\xe0\x93\xb8\x77\xd2"
+selfsign+="\x83\x09\xf7\x56\x1e\x03\xb3\x12\x8f\x22\xa6\x50\xfd\x59\x6e\x22"
+selfsign+="\x3f\xc5\xe3\x0b\x76\xb7\x8a\x06\x90\x3a\x91\xdd\x74\x02\x42\x00"
+selfsign+="\x21\xb6\x1b\x92\xb9\x34\x84\x36\x89\x2c\xba\xf5\x1c\xa1\xfa\x39"
+selfsign+="\x95\x44\x5b\xdc\x44\xaf\x53\x79\x5f\x34\x43\xcc\x91\x01\xce\x8d"
+selfsign+="\x6b\x33\xbe\xe3\x25\xaf\xb7\xc6\xa7\x46\x3a\xb5\x94\x37\xcc\xd9"
+selfsign+="\xdb\x40\x56\x49\x2f\x4d\x5e\xfb\x7d\xab\x09\xf1\x66\xde\xb6\x67"
+selfsign+="\x9f\x80\xc7\xa0\xce\x46\x5a\x10\xb4\xf3\x42\x21\x14\x69\x0c\x4e"
+selfsign+="\x25\x6f\xd1\x0a\xc1\x33\x27\xcf\x09\x0e\x10\x83\xb9\x3d\x4f\x0c"
+selfsign+="\x2f\x29\x2c\xb3\x19\x37\x3d\x57\x0e\xe3\xbe\x53\x8d\x54\x96\xcd"
+selfsign+="\x8f\x6f\x1d\x15\x2b\x28\xfa\x4f\x5c\x0c\x4e\x1d\x9d\xf9\x38\x7a"
+selfsign+="\x9d\x44\x3d\x85\x89\xee\x70\xb4\xe6\x73\xe7\x36\x33\x21\xa5\x84"
+selfsign+="\x18"
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring for CA keys
+marker "ADD CA KEYRING"
+create_keyring ca @s
+expect_keyid cakeyring
+
+# create a keyring using the keys in user CA for validation
+marker "ADD RESTRICTED USER KEYRING (parent keyring)"
+create_keyring rbkr @s
+expect_keyid restricted_by_keyring
+restrict_keyring $restricted_by_keyring "asymmetric" "key_or_keyring:$cakeyring"
+
+# verify cycle detection
+marker "REJECT RESTRICTION CYCLE"
+restrict_keyring --fail $cakeyring "asymmetric" "key_or_keyring:$restricted_by_keyring"
+
+# confirm that a restricted keyring cannot be restricted again
+marker "REJECT REPEATED RESTRICTION"
+restrict_keyring --fail $restricted_by_keyring "asymmetric" "builtin_trusted"
+
+# create a keyring using the keys in builtin CA for validation
+marker "ADD RESTRICTED BUILTIN KEYRING"
+create_keyring rbb @s
+expect_keyid restricted_by_builtin
+restrict_keyring $restricted_by_builtin "asymmetric" "builtin_trusted"
+
+# add CA certificate to CA keyring
+marker "ADD USER SIGNED CERT"
+pcreate_key "-e $cacert1" asymmetric "" $cakeyring
+expect_keyid cacert1id
+
+# attempt validation of signed key
+marker "REJECT KEY SIGNED BY UNKNOWN CA"
+pcreate_key --fail "-e $signed" asymmetric "" $restricted_by_keyring
+
+# attempt validation of signed key
+marker "REJECT KEY NOT SIGNED BY BUILTIN"
+pcreate_key --fail "-e $signed" asymmetric "" $restricted_by_builtin
+
+# add another CA certificate to CA keyring
+marker "ADD SECOND CA KEY"
+pcreate_key "-e $cacert2" asymmetric "" $cakeyring
+expect_keyid cacert2id
+
+# create a keyring restricted on a single key
+marker "ADD RESTRICTED USER KEYRING (parent key)"
+create_keyring rbk @s
+expect_keyid restricted_by_key
+restrict_keyring $restricted_by_key "asymmetric" "key_or_keyring:$cacert2id"
+
+# attempt validation of signed key
+marker "ADD KEY SIGNED BY KNOWN CA"
+pcreate_key "-e $signed" asymmetric "" $restricted_by_keyring
+expect_keyid signedid
+link_key $signedid $restricted_by_key
+
+# confirm that self-signed key cannot be added
+marker "REJECT SELF-SIGNED KEY"
+pcreate_key --fail "-e $selfsign" asymmetric "" $restricted_by_keyring
+pcreate_key --fail "-e $selfsign" asymmetric "" $restricted_by_key
+
+# confirm that the keyring restriction cannot be changed
+marker "REJECT RESTRICTION CHANGE"
+restrict_keyring --fail $restricted_by_key "asymmetric" "builtin_trusted"
+
+# create two self-restricted keyrings
+marker "ADD USER KEYRINGS (self)"
+create_keyring rbs1 @s
+expect_keyid restricted_by_self1
+create_keyring rbs2 @s
+expect_keyid restricted_by_self2
+
+# add first certificate (treated as root cert) to self-restricted keyrings
+marker "ADD FIRST CERT TO SELF-RESTRICTED KEYRINGS"
+link_key $signedid $restricted_by_self1
+link_key $cacert2id $restricted_by_self2
+
+# apply restrictions
+marker "RESTRICT USER KEYRINGS"
+restrict_keyring $restricted_by_self1 "asymmetric" "key_or_keyring:0:chain"
+restrict_keyring $restricted_by_self2 "asymmetric" "key_or_keyring:0:chain"
+
+# reject certificate addition to self-restricted keyring
+marker "REJECT CERTS FOR SELF-RESTRICTED KEYRING"
+link_key --fail $cacert2id $restricted_by_self1
+pcreate_key --fail "-e $selfsign" asymmetric "" $restricted_by_self1
+pcreate_key --fail "-e $selfsign" asymmetric "" $restricted_by_self2
+
+# add certificate to self-restricted keyring
+marker "ADD CERT TO SELF-RESTRICTED KEYRING"
+link_key $signedid $restricted_by_self2
+
+# check certificate signed using an intermediate CA
+marker "ADD INTERMEDIATE SIGNED CERT TO SELF-RESTRICTED KEYRING"
+pcreate_key "-e $intcert" asymmetric "" $restricted_by_self2
+expect_keyid intcertid
+pcreate_key "-e $intsigned" asymmetric "" $restricted_by_self2
+expect_keyid intsignedid
+
+# check intermediate CA with a parent keyring when CA is missing
+marker "REJECT INTERMEDIATE SIGNED CERT WITHOUT INTERMEDIATE CA"
+create_keyring restricted_by_int @s
+expect_keyid restricted_by_int
+restrict_keyring $restricted_by_int "asymmetric" "key_or_keyring:$cakeyring:chain"
+link_key --fail $intsignedid $restricted_by_int
+
+# check intermediate CA with a parent keyring and valid CA
+marker "ADD INTERMEDIATE SIGNED CERT WITH INTERMEDIATE CA"
+link_key $intcertid $restricted_by_int
+link_key $intsignedid $restricted_by_int
+
+# create a fully-restricted keyring (no links allowed)
+marker "ADD FULLY-RESTRICTED KEYRING"
+create_keyring fr @s
+expect_keyid fully_restricted
+link_key $cacert2id $fully_restricted
+restrict_keyring $fully_restricted
+
+# reject certificate addition to fully-restricted keyring
+marker "REJECT CERTS FOR FULLY-RESTRICTED KEYRING"
+link_key --fail $cacert2id $fully_restricted
+pcreate_key --fail "-e $selfsign" asymmetric "" $fully_restricted
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/revoke/bad-args/runtest.sh b/tests/keyctl/revoke/bad-args/runtest.sh
new file mode 100644
index 0000000..a6c0082
--- /dev/null
+++ b/tests/keyctl/revoke/bad-args/runtest.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that a bad key ID fails correctly
+marker "CHECK BAD KEY ID"
+revoke_key --fail 0
+expect_error EINVAL
+
+# check that a non-existent key ID fails correctly
+marker "CHECK NON-EXISTENT KEY ID"
+revoke_key --fail @t
+expect_error ENOKEY
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/revoke/noargs/runtest.sh b/tests/keyctl/revoke/noargs/runtest.sh
new file mode 100644
index 0000000..94e1b7a
--- /dev/null
+++ b/tests/keyctl/revoke/noargs/runtest.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that no arguments fails correctly
+marker "ADD NO ARGS"
+expect_args_error keyctl revoke
+
+# check that two arguments fail correctly
+marker "ADD TWO ARGS"
+expect_args_error keyctl revoke 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/revoke/valid/runtest.sh b/tests/keyctl/revoke/valid/runtest.sh
new file mode 100644
index 0000000..55bbbdf
--- /dev/null
+++ b/tests/keyctl/revoke/valid/runtest.sh
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "ADD KEYRING"
+create_keyring wibble @s
+expect_keyid keyringid
+
+# create a key and attach it to the new keyring
+marker "ADD KEY"
+create_key user lizard gizzard $keyringid
+expect_keyid keyid
+
+# check that we can list the keyring
+marker "LIST KEYRING"
+list_keyring $keyringid
+expect_keyring_rlist ringlist $keyid
+
+# check we can read the key description
+marker "CHECK VALIDATE KEY"
+describe_key $keyid
+expect_key_rdesc kdesc 'user@.*@lizard'
+
+# check we can read the key's payload
+marker "CHECK READ PAYLOAD"
+print_key $keyid
+expect_payload kpayload "gizzard"
+
+# revoke the key
+marker "REVOKE KEY"
+revoke_key $keyid
+
+# check we can no longer read the key description
+marker "CHECK NO VALIDATE KEY"
+describe_key --fail $keyid
+expect_error EKEYREVOKED
+
+# check we can no longer read the key's payload
+marker "CHECK NO READ PAYLOAD"
+print_key --fail $keyid
+expect_error EKEYREVOKED
+
+# remove the key we added
+marker "UNLINK KEY"
+unlink_key $keyid $keyringid
+
+# revoke the keyring
+marker "REVOKE KEYRING"
+revoke_key $keyringid
+
+# listing the session keyring should fail
+marker "CHECK NO LIST SESSION KEYRING"
+list_keyring --fail $keyringid
+expect_error EKEYREVOKED
+
+# validating the new keyring's name and type should also fail
+marker "CHECK NO VALIDATE KEYRING"
+describe_key --fail $keyringid
+expect_error EKEYREVOKED
+
+# remove the keyring we added
+marker "UNLINK KEYRING"
+unlink_key $keyringid @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/search/bad-args/runtest.sh b/tests/keyctl/search/bad-args/runtest.sh
new file mode 100644
index 0000000..dfec8dd
--- /dev/null
+++ b/tests/keyctl/search/bad-args/runtest.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that an empty key type fails correctly
+marker "CHECK EMPTY KEY TYPE"
+search_for_key --fail @s "" wibble
+expect_error EINVAL
+search_for_key --fail @s "" wibble @p
+expect_error EINVAL
+
+# check that an unsupported key type fails correctly
+marker "CHECK UNSUPPORTED KEY TYPE"
+search_for_key --fail @s lizardsgizzards wibble
+expect_error ENOKEY
+search_for_key --fail @s lizardsgizzards wibble @p
+expect_error ENOKEY
+
+# check that an invalid key type fails correctly
+marker "CHECK INVALID KEY TYPE"
+search_for_key --fail @s .user wibble
+expect_error EPERM
+search_for_key --fail @s .user wibble @p
+expect_error EPERM
+
+# check that an overlong key type fails correctly
+marker "CHECK OVERLONG KEY TYPE"
+search_for_key --fail @s $maxtype wibble
+expect_error ENOKEY
+search_for_key --fail @s a$maxtype wibble @p
+expect_error EINVAL
+
+# check that an max length key description works correctly (4095 inc NUL)
+marker "CHECK MAXLEN DESC"
+search_for_key --fail @s user $maxdesc
+expect_error ENOKEY
+
+search_for_key --fail @s user $maxdesc @p
+expect_error ENOKEY
+
+# This doesn't work on MIPS earler than 3.19 because of a kernel bug
+kver=`uname -r`
+kmch=`uname -m`
+if kernel_at_or_later_than 3.19 ||
+ [ "$kmch" != "mips" -a "$kmch" != "mips64" ]
+then
+ # check that an overlong key description fails correctly (>4095 inc NUL)
+ marker "CHECK OVERLONG DESC"
+ search_for_key --fail @s user a$maxdesc
+ expect_error EINVAL
+fi
+
+search_for_key --fail @s user a$maxdesc @p
+expect_error EINVAL
+
+# check that a bad key ID fails correctly
+marker "CHECK BAD KEY ID"
+search_for_key --fail @s user wibble -2000
+expect_error EINVAL
+
+# create a non-keyring key
+marker "CREATE KEY"
+create_key user a a @s
+expect_keyid keyid
+
+# search the non-keyring key
+marker "SEARCH KEY"
+search_for_key --fail $keyid user a
+expect_error ENOTDIR
+search_for_key --fail $keyid user a @p
+expect_error ENOTDIR
+
+# dispose of the key
+marker "UNLINK KEY"
+unlink_key $keyid @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/search/noargs/runtest.sh b/tests/keyctl/search/noargs/runtest.sh
new file mode 100644
index 0000000..912d614
--- /dev/null
+++ b/tests/keyctl/search/noargs/runtest.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that no arguments fails correctly
+marker "NO ARGS"
+expect_args_error keyctl search
+
+# check that one argument fails correctly
+marker "ONE ARGS"
+expect_args_error keyctl search 0
+
+# check that two arguments fails correctly
+marker "TWO ARGS"
+expect_args_error keyctl search 0 0
+
+# check that five arguments fails correctly
+marker "FIVE ARGS"
+expect_args_error keyctl search 0 0 0 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/search/valid/runtest.sh b/tests/keyctl/search/valid/runtest.sh
new file mode 100644
index 0000000..fb51b21
--- /dev/null
+++ b/tests/keyctl/search/valid/runtest.sh
@@ -0,0 +1,186 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a pair of keyrings and attach them to the session keyring
+marker "ADD KEYRING"
+create_keyring wibble @s
+expect_keyid keyringid
+
+create_keyring wibble2 @s
+expect_keyid keyring2id
+
+# stick a key in the keyring
+marker "ADD KEY"
+create_key user lizard gizzard $keyringid
+expect_keyid keyid
+
+# check that we can list it
+marker "LIST KEYRING WITH ONE"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid
+
+# search the session keyring for a non-existent key
+marker "SEARCH SESSION FOR NON-EXISTENT KEY"
+search_for_key --fail @s user snake
+expect_error ENOKEY
+
+# search the session keyring for the key
+marker "SEARCH SESSION"
+search_for_key @s user lizard
+expect_keyid found $keyid
+
+# search the session keyring for the key and attach to second keyring
+marker "SEARCH SESSION AND ATTACH"
+search_for_key @s user lizard $keyring2id
+expect_keyid found $keyid
+
+# check it's attached to the second keyring
+marker "CHECK ATTACHED"
+list_keyring $keyring2id
+expect_keyring_rlist rlist $keyid
+
+# check the key contains what we expect
+marker "CHECK PAYLOAD"
+print_key $keyid
+expect_payload payload "gizzard"
+
+# detach the attachment just made
+marker "DETACH KEY"
+unlink_key $found $keyring2id
+
+# create an overlapping key in the second keyring
+create_key user lizard skin $keyring2id
+expect_keyid keyid2
+
+# check the two keys contain what we expect
+marker "CHECK PAYLOADS"
+print_key $keyid
+expect_payload payload "gizzard"
+print_key $keyid2
+expect_payload payload "skin"
+
+# a search from the session keyring should find the first key
+marker "SEARCH SESSION AGAIN"
+search_for_key @s user lizard
+expect_keyid found $keyid
+
+# a search from the first keyring should find the first key
+marker "SEARCH FIRST KEYRING"
+search_for_key $keyringid user lizard
+expect_keyid found $keyid
+
+# a search from the second keyring should find the second key
+marker "SEARCH SECOND KEYRING"
+search_for_key $keyring2id user lizard
+expect_keyid found $keyid2
+
+# link the second keyring to the first
+marker "LINK FIRST KEYRING TO SECOND"
+link_key $keyring2id $keyringid
+
+# a search from the first keyring should again find the first key
+marker "SEARCH FIRST KEYRING AGAIN"
+search_for_key $keyringid user lizard
+expect_keyid found $keyid
+
+# revoking the first key should cause the second key to be available
+revoke_key $keyid
+search_for_key $keyringid user lizard
+expect_keyid found $keyid2
+
+# get rid of the dead key
+marker "UNLINK FIRST KEY"
+unlink_key $keyid $keyringid
+
+# a search from the first keyring should now find the second key
+marker "SEARCH FIRST KEYRING AGAIN 2"
+search_for_key $keyringid user lizard
+expect_keyid found $keyid2
+
+# a search from the session keyring should now find the second key
+marker "SEARCH SESSION KEYRING AGAIN 2"
+search_for_key @s user lizard
+expect_keyid found $keyid2
+
+# unlink the second keyring from the first
+marker "UNLINK SECOND KEYRING FROM FIRST"
+unlink_key $keyring2id $keyringid
+
+# a search from the first keyring should now fail
+marker "SEARCH FIRST KEYRING FOR FAIL"
+search_for_key --fail $keyringid user lizard
+expect_error ENOKEY
+
+# a search from the session keyring should still find the second key
+marker "SEARCH SESSION KEYRING AGAIN 3"
+search_for_key @s user lizard
+expect_keyid found $keyid2
+
+# move the second keyring into the first
+marker "MOVE SECOND KEYRING INTO FIRST"
+link_key $keyring2id $keyringid
+unlink_key $keyring2id @s
+
+# a search from the first keyring should now find the second key once again
+marker "SEARCH FIRST KEYRING AGAIN 4"
+search_for_key $keyringid user lizard
+expect_keyid found $keyid2
+
+# removing search permission on the second keyring should hide the key
+marker "SEARCH WITH NO-SEARCH KEYRING"
+set_key_perm $keyring2id 0x370000
+search_for_key --fail $keyringid user lizard
+expect_error ENOKEY
+
+# putting search permission on the second keyring back again should make it
+# available again
+set_key_perm $keyring2id 0x3f0000
+search_for_key $keyringid user lizard
+expect_keyid found $keyid2
+
+# removing search permission on the second key should hide the key
+marker "SEARCH WITH NO-SEARCH KEYRING2"
+set_key_perm $keyring2id 0x370000
+search_for_key --fail $keyringid user lizard
+expect_error ENOKEY
+
+# putting search permission on the second key back again should make it
+# available again
+set_key_perm $keyring2id 0x3f0000
+search_for_key $keyringid user lizard
+expect_keyid found $keyid2
+
+# revoking the key should make the key unavailable
+revoke_key $keyid2
+search_for_key --fail $keyringid user lizard
+kver=`uname -r`
+case $kver in
+ *.el7*)
+ expect_error EKEYREVOKED
+ ;;
+ *)
+ if kernel_older_than 3.13
+ then
+ expect_error ENOKEY
+ else
+ expect_error EKEYREVOKED
+ fi
+ ;;
+esac
+
+# remove the keyrings we added
+marker "UNLINK KEYRING"
+unlink_key $keyringid @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/session/bad-args/runtest.sh b/tests/keyctl/session/bad-args/runtest.sh
new file mode 100644
index 0000000..8bae0d4
--- /dev/null
+++ b/tests/keyctl/session/bad-args/runtest.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that an empty keyring name fails correctly
+marker "SESSION WITH EMPTY KEYRING NAME"
+new_session --fail ""
+expect_error EINVAL
+
+# This doesn't work on MIPS earler than 3.19 because of a kernel bug
+kver=`uname -r`
+kmch=`uname -m`
+if kernel_at_or_later_than 3.19 ||
+ [ "$kmch" != "mips" -a "$kmch" != "mips64" ]
+then
+ # check that an overlong keyring name fails correctly
+ marker "SESSION WITH OVERLONG KEYRING NAME"
+ new_session --fail a$maxdesc
+ expect_error EINVAL
+fi
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/session/valid/runtest.sh b/tests/keyctl/session/valid/runtest.sh
new file mode 100644
index 0000000..4c83131
--- /dev/null
+++ b/tests/keyctl/session/valid/runtest.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# describe the keyring created for an anonymous session
+if [ $OSDIST = RHEL ] && version_less_than $OSRELEASE 6
+then
+ marker "ANON SESSION"
+ new_session - keyctl rdescribe @s "@"
+ expect_key_rdesc rdesc "keyring@.*@.*@.*@_ses[^@]*\$"
+
+ # check the session keyring ID is shown
+ seskeyring="`tail -2 $OUTPUTFILE | head -1`"
+ if ! expr "$seskeyring" : "Joined session keyring: [0-9]*" >&/dev/null
+ then
+ failed
+ fi
+fi
+
+# describe the keyring created for a named session
+marker "NAMED SESSION"
+new_session qwerty keyctl rdescribe @s "@"
+expect_key_rdesc rdesc "keyring@.*@.*@.*@qwerty"
+
+# check the session keyring ID is shown
+seskeyring="`tail -2 $OUTPUTFILE | head -1`"
+if ! expr "$seskeyring" : "Joined session keyring: [0-9]*" >&/dev/null
+then
+ failed
+fi
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/show/noargs/runtest.sh b/tests/keyctl/show/noargs/runtest.sh
new file mode 100644
index 0000000..d507271
--- /dev/null
+++ b/tests/keyctl/show/noargs/runtest.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that show shows us our session keyring
+marker "SHOW SESSION KEYRING"
+keyctl show >>$OUTPUTFILE 2>&1
+if [ $? != 0 ]
+then
+ failed
+fi
+
+# must be at least two lines in the output (plus the test banner lines)
+nlines=`wc -l $OUTPUTFILE | cut -d\ -f1`
+if [ "$nlines" -lt 4 ]
+then
+ failed
+fi
+
+# there must be a session keyring section on the third line
+if [ "`sed -n -e 3p $OUTPUTFILE`" != "Session Keyring" ]
+then
+ failed
+fi
+
+# the first key listed (line 2) should be a keying (the session keyring) ...
+keyring1="`grep -n keyring $OUTPUTFILE | cut -d: -f1 | head -1`"
+if [ "$keyring1" != "4" ]
+then
+ failed
+fi
+
+# ... and it should be the session keyring
+keyring1name="`sed -n -e 4p $OUTPUTFILE | awk '{print $6}'`"
+if ! expr "$keyring1name" : "^RHTS/keyctl" >&/dev/null
+then
+ failed
+fi
+
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/show/valid/runtest.sh b/tests/keyctl/show/valid/runtest.sh
new file mode 100644
index 0000000..cbdd85f
--- /dev/null
+++ b/tests/keyctl/show/valid/runtest.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# add some keyrings, nested one inside the other
+nr_keyrings=7
+keyrings=
+parent=@s
+for ((i=1; i<=$nr_keyrings; i++))
+do
+ marker "ADD KEYRING $i"
+ create_keyring wibble$i $parent
+ expect_keyid keyringid
+ parent=$keyringid
+ keyrings="$keyrings $keyringid"
+done
+
+# check that show works
+marker "SHOW SESSION KEYRING"
+keyctl show >>$OUTPUTFILE 2>&1
+if [ $? != 0 ]
+then
+ failed
+fi
+
+if [ $OSDIST = RHEL ] && ! version_less_than $OSRELEASE 6.6 ||
+ keyutils_at_or_later_than 1.5.6
+then
+ # should be eight lines in the output (banner + session + 6 keyrings)
+ marker "COUNT LINES"
+ nlines=`keyctl show | wc -l`
+ if [ "$nlines" -ne $(($nr_keyrings + 2)) ]
+ then
+ failed
+ fi
+
+ # check the key ID list
+ marker "CHECK KEY ID LIST"
+ keyids=`keyctl show | tail -n +3 | cut -c1-11`
+
+ # we need to fix up the whitespace
+ keyids=`echo $keyids`
+ keyrings=`echo $keyrings`
+
+ echo "Compare '$keyids'" >>$OUTPUTFILE
+ echo "And '$keyrings'" >>$OUTPUTFILE
+ if [ "$keyids" != "$keyrings" ]
+ then
+ failed
+ fi
+fi
+
+# check that shows of specified keyrings also work
+if keyutils_at_or_later_than 1.5.4
+then
+ declare -i j
+ j=$nr_keyrings
+ for i in $keyrings
+ do
+ marker "CHECK SHOW OTHERS $j"
+ echo --- $i >>$OUTPUTFILE
+ if ! keyctl show $i >>$OUTPUTFILE
+ then
+ failed
+ fi
+ k=`keyctl show $i | wc -l`
+ if [ $(($j + 1)) != $k ]
+ then
+ failed
+ fi
+ j=$(($j - 1))
+ done
+fi
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/supports/bad-args/runtest.sh b/tests/keyctl/supports/bad-args/runtest.sh
new file mode 100644
index 0000000..05581a4
--- /dev/null
+++ b/tests/keyctl/supports/bad-args/runtest.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that two arguments fails correctly
+marker "TWO ARGS"
+expect_args_error keyctl support 0 0
+
+# check that three arguments fails correctly
+marker "THREE ARGS"
+expect_args_error keyctl support 0 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/supports/valid/runtest.sh b/tests/keyctl/supports/valid/runtest.sh
new file mode 100644
index 0000000..2c62ef2
--- /dev/null
+++ b/tests/keyctl/supports/valid/runtest.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# Check that listing caps works
+marker "LIST CAPS"
+supports
+
+# check that querying a cap works
+marker "QUERY CAP"
+supports --unrecognised xxx
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/timeout/bad-args/runtest.sh b/tests/keyctl/timeout/bad-args/runtest.sh
new file mode 100644
index 0000000..a93cd2d
--- /dev/null
+++ b/tests/keyctl/timeout/bad-args/runtest.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that a bad key ID fails correctly
+marker "CHECK BAD KEY ID"
+timeout_key --fail 0 10
+expect_error EINVAL
+
+# get a key
+marker "CREATE KEY"
+create_key user a a @s
+expect_keyid keyid
+
+# dispose of the key so we can use its ID
+marker "DESTROY KEY ID"
+unlink_key --wait $keyid @s
+
+# check that a non-existent key ID fails correctly
+marker "CHECK NON-EXISTENT KEY ID"
+timeout_key --fail $keyid 10
+expect_error ENOKEY
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/timeout/noargs/runtest.sh b/tests/keyctl/timeout/noargs/runtest.sh
new file mode 100644
index 0000000..a2e730f
--- /dev/null
+++ b/tests/keyctl/timeout/noargs/runtest.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that no arguments fails correctly
+marker "ADD NO ARGS"
+expect_args_error keyctl timeout
+
+# check that one argument fails correctly
+marker "ADD ONE ARG"
+expect_args_error keyctl timeout 0
+
+# check that three arguments fail correctly
+marker "ADD THREE ARGS"
+expect_args_error keyctl timeout 0 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/timeout/valid/runtest.sh b/tests/keyctl/timeout/valid/runtest.sh
new file mode 100644
index 0000000..4d772bb
--- /dev/null
+++ b/tests/keyctl/timeout/valid/runtest.sh
@@ -0,0 +1,130 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "ADD KEYRING"
+create_keyring wibble @s
+expect_keyid keyringid
+
+# create a key and attach it to the new keyring
+marker "ADD KEY"
+create_key user lizard gizzard $keyringid
+expect_keyid keyid
+
+# check that we can list the keyring
+marker "LIST KEYRING"
+list_keyring $keyringid
+expect_keyring_rlist ringlist $keyid
+
+# check we can read the key description
+marker "CHECK VALIDATE KEY"
+describe_key $keyid
+expect_key_rdesc kdesc 'user@.*@lizard'
+
+# check we can read the key's payload
+marker "CHECK READ PAYLOAD"
+print_key $keyid
+expect_payload kpayload "gizzard"
+
+# set a silly timeout on the key
+marker "SET BIG TIMEOUT"
+timeout_key $keyid 10000000
+
+# check we can still read the key's payload
+marker "CHECK READ PAYLOAD 2"
+print_key $keyid
+expect_payload kpayload "gizzard"
+
+# set a small timeout on the key
+marker "SET SMALL TIMEOUT"
+timeout_key $keyid 2
+
+marker "WAIT FOR TIMEOUT"
+sleep_at_least 2
+
+# check the key has expired
+marker "CHECK NO READ PAYLOAD"
+print_key --fail $keyid
+if kernel_at_or_later_than 3.8 && kernel_older_than 3.13 &&
+ ! rhel7_kernel_at_or_later_than 3.10.0-42.el7
+then
+ expect_error ENOKEY
+else
+ expect_error EKEYEXPIRED
+fi
+
+# check revocation doesn't work
+marker "CHECK NO REVOKE KEY"
+revoke_key --fail $keyid
+expect_error EKEYEXPIRED
+
+# check timeout setting doesn't work
+marker "CHECK NO TIMEOUT KEY"
+timeout_key --fail $keyid 20
+expect_error EKEYEXPIRED
+
+# remove the key we added
+marker "UNLINK KEY"
+unlink_key $keyid $keyringid
+
+###############################################################################
+# create a key and attach it to the new keyring
+marker "ADD KEY"
+create_key user lizard gizzard $keyringid
+expect_keyid keyid
+
+# set a silly timeout on the key
+marker "SET BIG TIMEOUT"
+timeout_key $keyid 10000000
+
+# revoke the key
+marker "REVOKE KEY"
+revoke_key $keyid
+
+# check we can no longer set the key's timeout
+marker "CHECK NO SET KEY TIMEOUT"
+timeout_key --fail $keyid 20
+expect_error EKEYREVOKED
+
+# remove the key we added
+marker "UNLINK KEY"
+unlink_key $keyid $keyringid
+
+# revoke the keyring
+marker "TIMEOUT KEYRING"
+timeout_key $keyringid 1
+
+marker "WAIT FOR KEYRING TIMEOUT"
+sleep_at_least 1
+
+# listing the session keyring should fail
+marker "CHECK NO LIST SESSION KEYRING"
+list_keyring --fail $keyringid
+if kernel_at_or_later_than 3.8 && kernel_older_than 3.13 &&
+ ! rhel7_kernel_at_or_later_than 3.10.0-42.el7
+then
+ expect_error ENOKEY
+else
+ expect_error EKEYEXPIRED
+fi
+
+# validating the new keyring's name and type should also fail
+marker "CHECK NO VALIDATE KEYRING"
+describe_key --fail $keyringid
+expect_error EKEYEXPIRED
+
+# remove the keyring we added
+marker "UNLINK KEYRING"
+unlink_key $keyringid @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/unlink/all/runtest.sh b/tests/keyctl/unlink/all/runtest.sh
new file mode 100644
index 0000000..24aaa3f
--- /dev/null
+++ b/tests/keyctl/unlink/all/runtest.sh
@@ -0,0 +1,103 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+
+if keyutils_at_or_later_than 1.5
+then
+ echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+ # create a keyring and attach it to the session keyring
+ marker "ADD KEYRING"
+ create_keyring wibble @s
+ expect_keyid keyringid
+
+ # stick a key in the keyring
+ marker "ADD KEY"
+ create_key user lizard gizzard $keyringid
+ expect_keyid keyid
+
+ # check that we can list it
+ marker "LIST KEYRING WITH ONE"
+ list_keyring $keyringid
+ expect_keyring_rlist rlist $keyid
+
+ # dispose of the key and make sure it gets destroyed
+ marker "UNLINK KEY FROM KEYRING"
+ unlink_key --wait $keyid $keyringid
+
+ # trying a tree-wide unlink should succeed with no links removed
+ marker "CHECK NO UNLINK KEY FROM TREE"
+ unlink_key $keyid
+ expect_unlink_count n_unlinked 0
+
+ # check that the keyring is now empty
+ marker "LIST KEYRING"
+ list_keyring $keyringid
+ expect_keyring_rlist rlist empty
+
+ # create a key to be massively linked
+ marker "ADD MULTI KEY"
+ create_key user lizard gizzard $keyringid
+ expect_keyid keyid
+
+ # stick twenty keyrings in the keyring with twenty links
+ marker "ADD TWENTY KEYRINGS WITH LINKS"
+ subrings=
+ for ((i=0; i<20; i++))
+ do
+ create_keyring ring$i $keyringid
+ expect_keyid x
+ keys="$keys $x"
+ subrings="$subrings $x"
+ list_keyring $keyringid
+ expect_keyring_rlist rlist $x
+
+ link_key $keyid $x
+ list_keyring $x
+ expect_keyring_rlist rlist $keyid
+ done
+
+ marker "SHOW"
+ if ! keyctl show >>$OUTPUTFILE 2>&1
+ then
+ failed
+ fi
+
+ # delete all the keys from the keyring tree
+ marker "REMOVE ALL LINKS TO KEY"
+ unlink_key $keyid
+ expect_unlink_count n_unlinked 21
+
+ # there should not now be any left
+ unlink_key $keyid
+ expect_unlink_count n_unlinked 0
+
+ # check that the key is no longer in the main keyring
+ marker "CHECK GONE"
+ list_keyring $keyringid
+ expect_keyring_rlist rlist $keyid --absent
+
+ for i in $subrings
+ do
+ list_keyring $i
+ expect_keyring_rlist rlist $keyid --absent
+ done
+
+ # remove the keyring we added
+ marker "UNLINK KEY"
+ unlink_key $keyringid @s
+
+ echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+else
+ toolbox_skip_test $TEST "SKIPPING TEST DUE TO LACK OF UNLINK-ALL"
+ exit 0
+fi
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/unlink/bad-args/runtest.sh b/tests/keyctl/unlink/bad-args/runtest.sh
new file mode 100644
index 0000000..67cc498
--- /dev/null
+++ b/tests/keyctl/unlink/bad-args/runtest.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that a bad key ID fails correctly
+marker "CHECK UNLINK BAD KEY ID"
+unlink_key --fail 0 @s
+expect_error EINVAL
+
+marker "CHECK UNLINK FROM BAD KEY ID"
+unlink_key --fail @s 0
+expect_error EINVAL
+
+# create a non-keyring
+marker "CREATE KEY"
+create_key user lizard gizzard @s
+expect_keyid keyid
+
+# check that unlinking from a non-keyring ID fails correctly
+marker "CHECK UNLINK FROM NON-KEYRING KEY"
+unlink_key --fail @s $keyid
+expect_error ENOTDIR
+
+# dispose of the key we were using
+marker "UNLINK KEY"
+unlink_key --wait $keyid @s
+
+# check that a non-existent key ID fails correctly
+marker "CHECK UNLINK FROM NON-EXISTENT KEY ID"
+unlink_key --fail @s $keyid
+expect_error ENOKEY
+
+marker "CHECK UNLINK NON-EXISTENT KEY ID"
+unlink_key --fail $keyid @s
+expect_error ENOKEY
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/unlink/noargs/runtest.sh b/tests/keyctl/unlink/noargs/runtest.sh
new file mode 100644
index 0000000..e1baf1f
--- /dev/null
+++ b/tests/keyctl/unlink/noargs/runtest.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that no arguments fails correctly
+marker "NO ARGS"
+expect_args_error keyctl unlink
+
+if keyutils_older_than 1.5
+then
+ # check that one argument fails correctly
+ marker "ONE ARGS"
+ expect_args_error keyctl unlink 0
+fi
+
+# check that three arguments fails correctly
+marker "THREE ARGS"
+expect_args_error keyctl unlink 0 0 0
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/unlink/valid/runtest.sh b/tests/keyctl/unlink/valid/runtest.sh
new file mode 100644
index 0000000..9b9fa2e
--- /dev/null
+++ b/tests/keyctl/unlink/valid/runtest.sh
@@ -0,0 +1,99 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# create a keyring and attach it to the session keyring
+marker "ADD KEYRING"
+create_keyring wibble @s
+expect_keyid keyringid
+
+# stick a key in the keyring
+marker "ADD KEY"
+create_key user lizard gizzard $keyringid
+expect_keyid keyid
+
+# check that we can list it
+marker "LIST KEYRING WITH ONE"
+list_keyring $keyringid
+expect_keyring_rlist rlist $keyid
+
+# dispose of the key and make sure it gets destroyed
+marker "UNLINK KEY FROM KEYRING"
+unlink_key --wait $keyid $keyringid
+
+# trying again should fail
+marker "CHECK NO UNLINK KEY FROM KEYRING"
+unlink_key --fail $keyid $keyringid
+expect_error ENOKEY
+
+# check that the keyring is now empty
+marker "LIST KEYRING"
+list_keyring $keyringid
+expect_keyring_rlist rlist empty
+
+# stick twenty keys and twenty keyrings in the keyring
+marker "ADD TWENTY KEYS"
+keys=""
+for ((i=0; i<20; i++))
+ do
+ create_key user lizard$i gizzard$i $keyringid
+ expect_keyid x
+ keys="$keys $x"
+ list_keyring $keyringid
+ expect_keyring_rlist rlist $x
+done
+
+marker "ADD TWENTY KEYRINGS"
+for ((i=0; i<20; i++))
+ do
+ create_keyring ring$i $keyringid
+ expect_keyid x
+ keys="$keys $x"
+ list_keyring $keyringid
+ expect_keyring_rlist rlist $x
+done
+
+marker "CHECK KEYRING CONTENTS"
+list_keyring $keyringid
+for i in $keys
+do
+ expect_keyring_rlist rlist $i
+done
+
+marker "SHOW"
+if ! keyctl show >>$OUTPUTFILE 2>&1
+then
+ failed
+fi
+
+# delete all the keys from the keyring
+marker "DELETE CONTENTS OF KEYRING"
+for i in $keys
+ do
+ unlink_key --wait $i $keyringid
+ unlink_key --fail $i $keyringid
+ expect_error ENOKEY
+done
+
+keyctl show
+
+# check that it's now empty
+marker "LIST KEYRING"
+list_keyring $keyringid
+expect_keyring_rlist rlist empty
+
+# remove the keyring we added
+marker "UNLINK KEY"
+unlink_key $keyringid @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/update/bad-args/runtest.sh b/tests/keyctl/update/bad-args/runtest.sh
new file mode 100644
index 0000000..14e7a19
--- /dev/null
+++ b/tests/keyctl/update/bad-args/runtest.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# attempt to update the session keyring
+marker "CHECK UPDATE SESSION KEYRING"
+update_key --fail @s a
+expect_error EOPNOTSUPP
+
+# attempt to update an invalid key
+marker "CHECK UPDATE INVALID KEY"
+update_key --fail 0 a
+expect_error EINVAL
+
+# add a user key to the session keyring for us to play with
+marker "ADD USER KEY"
+create_key user wibble stuff @s
+expect_keyid keyid
+
+# remove the key we just added
+marker "UNLINK KEY"
+unlink_key --wait $keyid @s
+
+# it should fail when we attempt to update it
+marker "UPDATE UNLINKED KEY"
+update_key --fail $keyid @s
+expect_error ENOKEY
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/update/noargs/runtest.sh b/tests/keyctl/update/noargs/runtest.sh
new file mode 100644
index 0000000..8e570b9
--- /dev/null
+++ b/tests/keyctl/update/noargs/runtest.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that no arguments fails correctly
+marker "ADD NO ARGS"
+expect_args_error keyctl update
+
+# check that one argument fails correctly
+marker "ADD ONE ARG"
+expect_args_error keyctl update user
+
+# check that three arguments fail correctly
+marker "ADD THREE ARGS"
+expect_args_error keyctl update user wibble stuff
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/keyctl/update/userupdate/runtest.sh b/tests/keyctl/update/userupdate/runtest.sh
new file mode 100644
index 0000000..ddc7cd4
--- /dev/null
+++ b/tests/keyctl/update/userupdate/runtest.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+. ../../../prepare.inc.sh
+. ../../../toolbox.inc.sh
+
+
+# ---- do the actual testing ----
+
+result=PASS
+echo "++++ BEGINNING TEST" >$OUTPUTFILE
+
+# check that we can add a user key to the session keyring
+marker "ADD USER KEY"
+create_key user wibble stuff @s
+expect_keyid keyid
+
+# read back what we put in it
+marker "PRINT PAYLOAD"
+print_key $keyid
+expect_payload payload "stuff"
+
+# check that we can update a user key
+marker "UPDATE USER KEY"
+update_key $keyid "lizard"
+
+# read back what we changed it to
+marker "PRINT UPDATED PAYLOAD"
+print_key $keyid
+expect_payload payload "lizard"
+
+# remove the key we added
+marker "UNLINK KEY"
+unlink_key $keyid @s
+
+echo "++++ FINISHED TEST: $result" >>$OUTPUTFILE
+
+# --- then report the results in the database ---
+toolbox_report_result $TEST $result
diff --git a/tests/prepare.inc.sh b/tests/prepare.inc.sh
new file mode 100644
index 0000000..448e42b
--- /dev/null
+++ b/tests/prepare.inc.sh
@@ -0,0 +1,138 @@
+# preparation script for running keyring tests
+
+# Find the relative path from pwd to the directory holding this file
+includes=${BASH_SOURCE[0]}
+includes=${includes%/*}/
+
+# --- need to run in own session keyring
+if [ "x`keyctl rdescribe @s | sed 's/.*;//'`" != "xRHTS/keyctl/$$" ]
+then
+ echo "Running with session keyring RHTS/keyctl/$$"
+ exec keyctl session "RHTS/keyctl/$$" bash $0 $@ || exit 8
+fi
+
+# Set up for the Red Hat Test System
+RUNNING_UNDER_RHTS=0
+if [ -x /usr/bin/rhts_environment.sh ]
+then
+ PACKAGE=$(rpm -q --qf "%{name}" --whatprovides /bin/keyctl)
+ . /usr/bin/rhts_environment.sh
+ RUNNING_UNDER_RHTS=1
+elif [ -z "$OUTPUTFILE" ]
+then
+ OUTPUTFILE=$PWD/test.out
+ echo -n >$OUTPUTFILE
+fi
+
+case `lsb_release -i -s` in
+ Fedora*) OSDIST=Fedora;;
+ RedHatEnterprise*) OSDIST=RHEL;;
+ *) OSDIST=Unknown;;
+esac
+
+OSRELEASE=`lsb_release -r -s`
+
+KEYUTILSVER=`keyctl --version 2>/dev/null`
+if [ -n "$KEYUTILSVER" ]
+then
+ :
+elif [ -x /bin/rpm ]
+then
+ KEYUTILSVER=`rpm -q keyutils`
+else
+ echo "Can't determine keyutils version" >&2
+ exit 9
+fi
+
+echo "keyutils version: $KEYUTILSVER"
+KEYUTILSVER=`expr "$KEYUTILSVER" : '.*keyutils-\([0-9.]*\).*'`
+
+. $includes/version.inc.sh
+
+KERNELVER=`uname -r`
+
+#
+# Make sure the TEST envvar is set.
+#
+if [ -z "$TEST" ]
+then
+ p=`pwd`
+ case $p in
+ */keyctl/*)
+ TEST=keyctl/${p#*/keyctl/}
+ ;;
+ */bugzillas/*)
+ TEST=bugzillas/${p#*/bugzillas/}
+ ;;
+ *)
+ TEST=unknown
+ ;;
+ esac
+fi
+
+have_key_invalidate=0
+have_big_key_type=0
+have_dh_compute=0
+have_restrict_keyring=0
+
+if keyctl supports capabilities >&/dev/null
+then
+ eval `keyctl supports`
+else
+ #
+ # Work out whether key invalidation is supported by the kernel
+ #
+ if keyutils_at_or_later_than 1.5.6 && kernel_at_or_later_than 3.5-rc1
+ then
+ have_key_invalidate=1
+ fi
+
+ #
+ # Work out whether the big_key type is supported by the kernel.
+ #
+ if [ $OSDIST = RHEL ] && ! version_less_than $OSRELEASE 7
+ then
+ # big_key is backported to 3.10 for RHEL-7
+ have_big_key_type=1
+ elif kernel_at_or_later_than 3.13-rc1
+ then
+ have_big_key_type=1
+ fi
+
+ #
+ # Work out whether Diffie-Hellman is supported by the kernel
+ #
+ if [ $OSDIST = RHEL ]
+ then
+ :
+ elif keyutils_at_or_later_than 1.5.10 && kernel_at_or_later_than 4.7-rc1
+ then
+ have_dh_compute=1
+ fi
+
+ #
+ # Work out whether keyring restrictions are supported by the kernel
+ #
+ if keyutils_at_or_later_than 1.6 && kernel_at_or_later_than 4.12-rc1
+ then
+ have_restrict_keyring=1
+ fi
+fi
+
+#
+# Check if skipping of tests requiring root was requested
+#
+skip_root_required=0
+if [ "$SKIPROOTREQ" = "yes" ]
+then
+ skip_root_required=1
+fi
+
+#
+# Check if skipping of tests requiring installation was requested
+#
+skip_install_required=0
+if [ "$SKIPINSTALLREQ" = "yes" ]
+then
+ skip_install_required=1
+fi
diff --git a/tests/runtest.sh b/tests/runtest.sh
new file mode 100644
index 0000000..b6eaa7c
--- /dev/null
+++ b/tests/runtest.sh
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+# set the $AUTOMATED environment variable to non-zero for automated mode
+# automated mode will run all the tests to completion
+# non-automated mode (default) stops running the test suite on the first error
+AUTOMATED=${AUTOMATED:-0}
+
+TESTS=$*
+
+PARENTTEST=${TEST}
+
+if [ `id -u` != 0 ]
+then
+ echo "#### Some tests require root privileges." >&2
+ echo "#### It is recommended that this be run as root." >&2
+fi
+
+for i in ${TESTS}; do
+ export TEST=$i
+ pushd $i >/dev/null
+ echo "### RUNNING TEST $i"
+ if [[ $AUTOMATED != 0 ]] ; then
+ bash ./runtest.sh
+ else
+ bash ./runtest.sh || exit 1
+ fi
+ popd >/dev/null
+done
+
+if [ `id -u` != 0 ]
+then
+ echo "#### Some tests required root privileges." >&2
+ echo "#### They have been tested for the appropriate failure." >&2
+ echo "#### It is recommended that this be run as root." >&2
+fi
diff --git a/tests/toolbox.inc.sh b/tests/toolbox.inc.sh
new file mode 100644
index 0000000..e7b9635
--- /dev/null
+++ b/tests/toolbox.inc.sh
@@ -0,0 +1,1261 @@
+###############################################################################
+#
+# Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+# 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
+# 2 of the License, or (at your option) any later version.
+#
+###############################################################################
+
+echo === $OUTPUTFILE ===
+
+endian=`file -L /proc/$$/exe`
+if expr "$endian" : '.* MSB \+\(executable\|shared object\).*' >&/dev/null
+then
+ endian=BE
+elif expr "$endian" : '.* LSB \+\(executable\|shared object\).*' >&/dev/null
+then
+ endian=LE
+else
+ echo -e "+++ \e[31;1mCan't Determine Endianness\e[0m"
+ echo "+++ Can't Determine Endianness" >>$OUTPUTFILE
+ exit 2
+fi
+
+maxtypelen=31
+maxtype=`for ((i=0; i<$((maxtypelen)); i++)); do echo -n a; done`
+
+PAGE_SIZE=`getconf PAGESIZE`
+pagelen=$((PAGE_SIZE - 1))
+fullpage=`for ((i=0; i<$((pagelen)); i++)); do echo -n a; done`
+string4095=`for ((i=0; i<4095; i++)); do echo -n a; done`
+
+if kernel_at_or_later_than 3.18
+then
+ maxdesc=$string4095
+elif rhel6_kernel_at_or_later_than 2.6.32-589.el6
+then
+ maxdesc=$string4095
+else
+ maxdesc=$fullpage
+fi
+
+maxcall=$fullpage
+
+maxsquota=`grep '^ *0': /proc/key-users | sed s@.*/@@`
+
+key_gc_delay_file="/proc/sys/kernel/keys/gc_delay"
+if [ -f $key_gc_delay_file ]; then
+ orig_gc_delay=`cat $key_gc_delay_file`
+else
+ orig_gc_delay=300
+fi
+
+
+function marker ()
+{
+ echo -e "+++ \e[33m$*\e[0m"
+ echo +++ $* >>$OUTPUTFILE
+}
+
+function failed()
+{
+ echo -e "\e[31;1mFAILED\e[0m"
+ echo === FAILED === >>$OUTPUTFILE
+ keyctl show >>$OUTPUTFILE
+ echo ============== >>$OUTPUTFILE
+ result=FAIL
+}
+
+function expect_args_error ()
+{
+ "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != 2 ]
+ then
+ failed
+ fi
+
+}
+
+function toolbox_report_result()
+{
+ if [ $RUNNING_UNDER_RHTS = 1 ]
+ then
+ report_result $1 $2
+ fi
+ if [ $2 = FAIL ]
+ then
+ exit 1
+ fi
+}
+
+function toolbox_skip_test()
+{
+ echo "++++ SKIPPING TEST" >>$OUTPUTFILE
+ marker "$2"
+ toolbox_report_result $1 PASS
+}
+
+###############################################################################
+#
+# Return true if the command is found in $PATH. If not, log that the test is
+# being skipped, report the result as PASS, and exit.
+#
+###############################################################################
+function require_command ()
+{
+ which "$1" >&/dev/null
+ if [ $? != 0 ]
+ then
+ toolbox_skip_test "SKIP DUE TO MISSING COMMAND: $1"
+ exit 0
+ fi
+}
+
+function require_selinux ()
+{
+ if ! grep -q selinuxfs /proc/mounts;
+ then
+ toolbox_skip_test $TEST "SKIP DUE TO DISABLED SELINUX"
+ exit 0
+ fi
+}
+
+###############################################################################
+#
+# extract an error message from the log file and check it
+#
+###############################################################################
+function expect_error ()
+{
+ my_varname=$1
+
+ my_errmsg="`tail -1 $OUTPUTFILE`"
+ eval $my_varname="\"$my_errmsg\""
+
+ if [ $# != 1 ]
+ then
+ echo "Format: expect_error <symbol>" >>$OUTPUTFILE
+ failed
+ fi
+
+ case $1 in
+ EPERM) my_err="Operation not permitted";;
+ EAGAIN) my_err="Resource temporarily unavailable";;
+ ENOENT) my_err="No such file or directory";;
+ EEXIST) my_err="File exists";;
+ ENOTDIR) my_err="Not a directory";;
+ EACCES) my_err="Permission denied";;
+ EINVAL) my_err="Invalid argument";;
+ ENODEV) my_err="No such device";;
+ ELOOP) my_err="Too many levels of symbolic links";;
+ EOPNOTSUPP) my_err="Operation not supported";;
+ EDEADLK) my_err="Resource deadlock avoided";;
+ EDQUOT) my_err="Disk quota exceeded";;
+ ENOKEY)
+ my_err="Required key not available"
+ old_err="Requested key not available"
+ alt_err="Unknown error 126"
+ ;;
+ EKEYEXPIRED)
+ my_err="Key has expired"
+ alt_err="Unknown error 127"
+ ;;
+ EKEYREVOKED)
+ my_err="Key has been revoked"
+ alt_err="Unknown error 128"
+ ;;
+ EKEYREJECTED)
+ my_err="Key has been rejected"
+ alt_err="Unknown error 129"
+ ;;
+ *)
+ echo "Unknown error message $1" >>$OUTPUTFILE
+ failed
+ ;;
+ esac
+
+ if expr "$my_errmsg" : ".*: $my_err" >&/dev/null
+ then
+ :
+ elif [ "x$alt_err" != "x" ] && expr "$my_errmsg" : ".*: $alt_err" >&/dev/null
+ then
+ :
+ elif [ "x$old_err" != "x" ] && expr "$my_errmsg" : ".*: $old_err" >&/dev/null
+ then
+ :
+ else
+ failed
+ fi
+}
+
+###############################################################################
+#
+# wait for a key to be destroyed (get removed from /proc/keys)
+#
+###############################################################################
+function pause_till_key_destroyed ()
+{
+ echo "+++ WAITING FOR KEY TO BE DESTROYED" >>$OUTPUTFILE
+ hexkeyid=`printf %08x $1`
+
+ while grep $hexkeyid /proc/keys
+ do
+ sleep 1
+ done
+}
+
+###############################################################################
+#
+# wait for a key to be unlinked
+#
+###############################################################################
+function pause_till_key_unlinked ()
+{
+ echo "+++ WAITING FOR KEY TO BE UNLINKED" >>$OUTPUTFILE
+
+ while true
+ do
+ echo keyctl unlink $1 $2 >>$OUTPUTFILE
+ keyctl unlink $1 $2 >>$OUTPUTFILE 2>&1
+ if [ $? != 1 ]
+ then
+ failed
+ fi
+
+ my_errmsg="`tail -1 $OUTPUTFILE`"
+ if ! expr "$my_errmsg" : ".*: No such file or directory" >&/dev/null
+ then
+ break
+ fi
+ sleep 1
+ done
+}
+
+###############################################################################
+#
+# request a key and attach it to the new keyring
+#
+###############################################################################
+function request_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl request "$@" >>$OUTPUTFILE
+ keyctl request "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# request a key and attach it to the new keyring, calling out if necessary
+#
+###############################################################################
+function request_key_callout ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl request2 "$@" >>$OUTPUTFILE
+ keyctl request2 "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# request a key and attach it to the new keyring, calling out if necessary and
+# passing the callout data in on stdin
+#
+###############################################################################
+function prequest_key_callout ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ data="$1"
+ shift
+
+ echo echo -n $data \| keyctl prequest2 "$@" >>$OUTPUTFILE
+ echo -n $data | keyctl prequest2 "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# create a key and attach it to the new keyring
+#
+###############################################################################
+function create_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl add "$@" >>$OUTPUTFILE
+ keyctl add "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# create a key and attach it to the new keyring, piping in the data
+#
+###############################################################################
+function pcreate_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ data="$1"
+ shift
+
+ echo echo -n $data \| keyctl padd "$@" >>$OUTPUTFILE
+ echo -n $data | keyctl padd "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# create a key and attach it to the new keyring, piping in the data
+#
+###############################################################################
+function pcreate_key_by_size ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ data="$1"
+ shift
+
+ echo dd if=/dev/zero count=1 bs=$data \| keyctl padd "$@" >>$OUTPUTFILE
+ dd if=/dev/zero count=1 bs=$data 2>/dev/null | keyctl padd "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# create a key and attach it to the new keyring
+#
+###############################################################################
+function create_keyring ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl newring "$@" >>$OUTPUTFILE
+ keyctl newring "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# extract a key ID from the log file
+#
+###############################################################################
+function expect_keyid ()
+{
+ my_varname=$1
+
+ my_keyid="`tail -1 $OUTPUTFILE`"
+ if expr "$my_keyid" : '[1-9][0-9]*' >&/dev/null
+ then
+ eval $my_varname=$my_keyid
+
+ if [ $# = 2 -a "x$my_keyid" != "x$2" ]
+ then
+ failed
+ fi
+ else
+ eval $my_varname=no
+ result=FAIL
+ fi
+}
+
+###############################################################################
+#
+# prettily list a keyring
+#
+###############################################################################
+function pretty_list_keyring ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl list $1 >>$OUTPUTFILE
+ keyctl list $1 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# list a keyring
+#
+###############################################################################
+function list_keyring ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl rlist $1 >>$OUTPUTFILE
+ keyctl rlist $1 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# extract a keyring listing from the log file and see if a key ID is contained
+# therein
+#
+###############################################################################
+function expect_keyring_rlist ()
+{
+ my_varname=$1
+
+ my_rlist="`tail -1 $OUTPUTFILE`"
+ eval $my_varname="\"$my_rlist\""
+
+ if [ $# = 2 -o $# = 3 ]
+ then
+ if [ "$2" = "empty" ]
+ then
+ if [ "x$my_rlist" != "x" ]
+ then
+ failed
+ fi
+ else
+ my_keyid=$2
+ my_found=0
+ my_expected=1
+ if [ $# = 3 -a "x$3" = "x--absent" ]; then my_expected=0; fi
+
+ for k in $my_rlist
+ do
+ if [ $k = $my_keyid ]
+ then
+ my_found=1
+ break;
+ fi
+ done
+
+ if [ $my_found != $my_expected ]
+ then
+ failed
+ fi
+ fi
+ fi
+}
+
+###############################################################################
+#
+# prettily describe a key
+#
+###############################################################################
+function pretty_describe_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl describe $1 >>$OUTPUTFILE
+ keyctl describe $1 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# describe a key
+#
+###############################################################################
+function describe_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl rdescribe $1 "@" >>$OUTPUTFILE
+ keyctl rdescribe $1 "@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# extract a raw key description from the log file and check it
+#
+###############################################################################
+function expect_key_rdesc ()
+{
+ my_varname=$1
+
+ my_rdesc="`tail -1 $OUTPUTFILE`"
+ eval $my_varname="\"$my_rdesc\""
+
+ if ! expr "$my_rdesc" : "$2" >&/dev/null
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# read a key's payload as a hex dump
+#
+###############################################################################
+function read_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl read $1 >>$OUTPUTFILE
+ keyctl read $1 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# read a key's payload as a printable string
+#
+###############################################################################
+function print_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl print $1 >>$OUTPUTFILE
+ keyctl print $1 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# pipe a key's raw payload to stdout
+#
+###############################################################################
+function pipe_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl pipe $1 >>$OUTPUTFILE
+ keyctl pipe $1 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# pipe a key's raw payload through md5sum
+#
+###############################################################################
+function md5sum_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl pipe $1 \| md5sum \| cut -c1-32 >>$OUTPUTFILE
+ keyctl pipe $1 | md5sum | cut -c1-32 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# extract a printed payload from the log file
+#
+###############################################################################
+function expect_payload ()
+{
+ my_varname=$1
+
+ my_payload="`tail -1 $OUTPUTFILE`"
+ eval $my_varname="\"$my_payload\""
+
+ if [ $# == 2 -a "x$my_payload" != "x$2" ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# extract multiline output from the log file
+#
+###############################################################################
+function expect_multiline ()
+{
+ my_varname=$1
+ my_linecount="`echo \"$2\" | wc -l`"
+
+ my_payload=$(tail -$my_linecount $OUTPUTFILE)
+ eval $my_varname="\"$my_payload\""
+
+ if [ $# != 2 -o "x$my_payload" != "x$2" ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# revoke a key
+#
+###############################################################################
+function revoke_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl revoke $1 >>$OUTPUTFILE
+ keyctl revoke $1 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# unlink a key from a keyring
+#
+###############################################################################
+function unlink_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ my_wait=0
+ if [ "x$1" = "x--wait" ]
+ then
+ my_wait=1
+ shift
+ fi
+
+ echo keyctl unlink $1 $2 >>$OUTPUTFILE
+ keyctl unlink $1 $2 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+
+ # keys are destroyed lazily
+ if [ $my_wait = 1 ]
+ then
+ pause_till_key_unlinked $1 $2
+ fi
+}
+
+###############################################################################
+#
+# extract a message about the number of keys unlinked
+#
+###############################################################################
+function expect_unlink_count ()
+{
+ my_varname=$1
+
+ my_nunlinks="`tail -1 $OUTPUTFILE`"
+
+ if ! expr "$my_nunlinks" : '^[0-9][0-9]* links removed$'
+ then
+ failed
+ fi
+
+ my_nunlinks=`echo $my_nunlinks | awk '{printf $1}'`
+ eval $my_varname="\"$my_nunlinks\""
+
+ if [ $# == 2 -a $my_nunlinks != $2 ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# update a key from a keyring
+#
+###############################################################################
+function update_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl update $1 $2 >>$OUTPUTFILE
+ keyctl update $1 $2 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# update a key from a keyring, piping the data in over stdin
+#
+###############################################################################
+function pupdate_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo echo -n $2 \| keyctl pupdate $1 >>$OUTPUTFILE
+ echo -n $2 | keyctl pupdate $1 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# clear a keyring
+#
+###############################################################################
+function clear_keyring ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl clear $1 >>$OUTPUTFILE
+ keyctl clear $1 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# restrict a keyring
+#
+###############################################################################
+function restrict_keyring ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl restrict_keyring $1 $2 $3 >>$OUTPUTFILE
+ keyctl restrict_keyring $1 $2 $3 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# link a key to a keyring
+#
+###############################################################################
+function link_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl link $1 $2 >>$OUTPUTFILE
+ keyctl link $1 $2 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# search for a key in a keyring
+#
+###############################################################################
+function search_for_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl search "$@" >>$OUTPUTFILE
+ keyctl search "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# set the permissions mask on a key
+#
+###############################################################################
+function set_key_perm ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl setperm "$@" >>$OUTPUTFILE
+ keyctl setperm "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# set the ownership of a key
+#
+###############################################################################
+function chown_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl chown "$@" >>$OUTPUTFILE
+ keyctl chown "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# set the group ownership of a key
+#
+###############################################################################
+function chgrp_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl chgrp "$@" >>$OUTPUTFILE
+ keyctl chgrp "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# run as a new session
+#
+###############################################################################
+function new_session ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl session "$@" >>$OUTPUTFILE
+ keyctl session "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# instantiate a key
+#
+###############################################################################
+function instantiate_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl instantiate "$@" >>$OUTPUTFILE
+ keyctl instantiate "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# instantiate a key, piping the data in over stdin
+#
+###############################################################################
+function pinstantiate_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ data="$1"
+ shift
+
+ echo echo -n $data \| keyctl pinstantiate "$@" >>$OUTPUTFILE
+ echo -n $data | keyctl pinstantiate "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# negate a key
+#
+###############################################################################
+function negate_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl negate "$@" >>$OUTPUTFILE
+ keyctl negate "$@" >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# set a key's expiry time
+#
+###############################################################################
+function timeout_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl timeout $1 $2 >>$OUTPUTFILE
+ keyctl timeout $1 $2 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# Invalidate a key
+#
+###############################################################################
+function invalidate_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl invalidate $1 >>$OUTPUTFILE
+ keyctl invalidate $1 >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# Do a DH computation
+#
+###############################################################################
+function dh_compute ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl dh_compute $@ >>$OUTPUTFILE
+ keyctl dh_compute $@ >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# Do a DH computation post-processed by a KDF
+#
+###############################################################################
+function dh_compute_kdf ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl dh_compute_kdf $@ >>$OUTPUTFILE
+ keyctl dh_compute_kdf $@ >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# Do a DH computation post-processed by a KDF with other information
+#
+###############################################################################
+function dh_compute_kdf_oi ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl dh_compute_kdf_oi $@ >>$OUTPUTFILE
+ keyctl dh_compute_kdf_oi $@ >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# Move a key between keyrings
+#
+###############################################################################
+function move_key ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ fi
+
+ echo keyctl move $* >>$OUTPUTFILE
+ keyctl move $* >>$OUTPUTFILE 2>&1
+ if [ $? != $my_exitval ]
+ then
+ failed
+ fi
+}
+
+###############################################################################
+#
+# Query supported features
+#
+###############################################################################
+function supports ()
+{
+ my_exitval=0
+ if [ "x$1" = "x--fail" ]
+ then
+ my_exitval=1
+ shift
+ elif [ "x$1" = "x--unrecognised" ]
+ then
+ my_exitval=3
+ shift
+ fi
+
+ echo keyctl supports $* >>$OUTPUTFILE
+ keyctl supports $* >>$OUTPUTFILE 2>&1
+ err=$?
+ if [ $err != $my_exitval ]
+ then
+ echo exitcode=$err >>$OUTPUTFILE
+ failed
+ fi
+}
+
+###############################################################################
+#
+# Make sure we sleep at least N seconds
+#
+###############################################################################
+function sleep_at_least ()
+{
+ my_now=`date +%s`
+ my_done_at=$(($my_now+$1+1))
+ sleep $1
+ while [ `date +%s` -lt $my_done_at ]
+ do
+ # Sleep in 1/50th of a second bursts till the time catches up
+ sleep .02
+ done
+}
+
+###############################################################################
+#
+# set gc delay time, return original value
+#
+###############################################################################
+function set_gc_delay()
+{
+ delay=$1
+ if [ -f $key_gc_delay_file ]; then
+ echo $delay > $key_gc_delay_file
+ echo "Set $key_gc_delay_file to $delay, orig: $orig_gc_delay"
+ fi
+}
diff --git a/tests/vercmp.sh b/tests/vercmp.sh
new file mode 100644
index 0000000..8310c01
--- /dev/null
+++ b/tests/vercmp.sh
@@ -0,0 +1,31 @@
+# Commandline-driver tester for version comparison shell functions
+#
+###############################################################################
+#
+# Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+# 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
+# 2 of the License, or (at your option) any later version.
+#
+###############################################################################
+
+. ./version.inc.sh
+
+#
+# Compare versions
+#
+if [ "$1" = "" -o "$2" = "" ]
+then
+ echo "Missing version parameters" >&2
+ exit 2
+fi
+
+if version_less_than $1 $2
+then
+ echo "$1 < $2"
+else
+ echo "$1 >= $2"
+fi
diff --git a/tests/version.inc.sh b/tests/version.inc.sh
new file mode 100644
index 0000000..3630c5a
--- /dev/null
+++ b/tests/version.inc.sh
@@ -0,0 +1,199 @@
+# Version comparison shell functions
+#
+###############################################################################
+#
+# Copyright (C) 2005, 2013 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+# 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
+# 2 of the License, or (at your option) any later version.
+#
+###############################################################################
+
+###############################################################################
+#
+# compare version numbers to see if the first is less (older) than the second
+#
+###############################################################################
+function version_less_than ()
+{
+ a=$1
+ b=$2
+
+ if [ "$a" = "$b" ]
+ then
+ return 1
+ fi
+
+ # grab the leaders
+ a_version=${a%%-*} a_release=${a#*-}
+ b_version=${b%%-*} b_release=${b#*-}
+
+ if [ "$a_version" = "$b_version" ]
+ then
+ case "$a_release" in
+ rc[0-9]*)
+ case "$b_release" in
+ rc[0-9]*)
+ __version_less_than_dot "${a_release#rc}" "${b_release#rc}"
+ return $?
+ ;;
+ *)
+ return 0;
+ ;;
+ esac
+ ;;
+ esac
+
+ case "$b_release" in
+ rc[0-9]*)
+ return 1;
+ ;;
+ esac
+
+ if [ "$a_version" = "$a" -o "$b_version" = "$b" ]
+ then
+ if [ "$a_version" = "$b_version" ]
+ then
+ [ "$a_version" = "$a" ]
+ else
+ __version_less_than_dot "$a_version" "$b_version"
+ fi
+ fi
+ __version_less_than_dot "$a_release" "$b_release"
+ else
+ __version_less_than_dot "$a_version" "$b_version"
+ fi
+}
+
+function __version_less_than_dot ()
+{
+ a=$1
+ b=$2
+
+ if [ "$a" = "$b" ]
+ then
+ return 1
+ fi
+
+ # grab the leaders
+ x=${a%%.*}
+ y=${b%%.*}
+
+ if [ "$x" = "$a" -o "$y" = "$b" ]
+ then
+ if [ "$x" = "$y" ]
+ then
+ [ "$x" = "$a" ]
+ else
+ expr "$x" \< "$y" >/dev/null
+ fi
+ elif [ "$x" = "$y" ]
+ then
+ __version_less_than_dot "${a#*.}" "${b#*.}"
+ else
+ expr "$x" \< "$y" >/dev/null
+ fi
+}
+
+###############################################################################
+#
+# Return true if the keyutils package being tested is older than the given
+# version.
+#
+###############################################################################
+function keyutils_older_than ()
+{
+ version_less_than $KEYUTILSVER $1
+}
+
+###############################################################################
+#
+# Return true if the keyutils package being tested is at or later than the
+# given version.
+#
+###############################################################################
+function keyutils_at_or_later_than ()
+{
+ ! keyutils_older_than $1
+}
+
+###############################################################################
+#
+# Return true if the keyutils package being tested is newer than the given
+# version.
+#
+###############################################################################
+function keyutils_newer_than ()
+{
+ version_less_than $1 $KEYUTILSVER
+}
+
+###############################################################################
+#
+# Return true if the keyutils package being tested is at or older than the
+# given version.
+#
+###############################################################################
+function keyutils_at_or_older_than ()
+{
+ ! keyutils_newer_than $1
+}
+
+###############################################################################
+#
+# Return true if the kernel being tested is older than the given version.
+#
+###############################################################################
+function kernel_older_than ()
+{
+ version_less_than $KERNELVER $1
+}
+
+###############################################################################
+#
+# Return true if the kernel being tested is at or later than the given version.
+#
+###############################################################################
+function kernel_at_or_later_than ()
+{
+ ! kernel_older_than $1
+}
+
+###############################################################################
+#
+# Return true if the kernel being tested is a RHEL-6 kernel and is at or later
+# than the given version.
+#
+###############################################################################
+function rhel6_kernel_at_or_later_than ()
+{
+ case $OSDIST-$OSRELEASE in
+ RHEL-6.*)
+ ! kernel_older_than $1
+ ;;
+ *)
+ false
+ ;;
+ esac
+}
+
+###############################################################################
+#
+# Return true if the kernel being tested is a RHEL-7 kernel and is at or later
+# than the given version.
+#
+###############################################################################
+function rhel7_kernel_at_or_later_than ()
+{
+ case $OSDIST-$OSRELEASE in
+ RHEL-7.*)
+ ! kernel_older_than $1
+ ;;
+ *)
+ false
+ ;;
+ esac
+}
diff --git a/version.lds b/version.lds
new file mode 100644
index 0000000..d2e4d26
--- /dev/null
+++ b/version.lds
@@ -0,0 +1,100 @@
+KEYUTILS_0.3 {
+
+ /* primary syscalls; may be overridden by glibc */
+ add_key;
+ request_key;
+ keyctl;
+
+ /* management functions */
+ keyctl_chown;
+ keyctl_clear;
+ keyctl_describe;
+ keyctl_describe_alloc;
+ keyctl_get_keyring_ID;
+ keyctl_instantiate;
+ keyctl_join_session_keyring;
+ keyctl_link;
+ keyctl_negate;
+ keyctl_read;
+ keyctl_read_alloc;
+ keyctl_revoke;
+ keyctl_search;
+ keyctl_setperm;
+ keyctl_set_reqkey_keyring;
+ keyctl_unlink;
+ keyctl_update;
+
+};
+
+KEYUTILS_1.0 {
+ /* management functions */
+ keyctl_assume_authority;
+ keyctl_set_timeout;
+
+} KEYUTILS_0.3;
+
+KEYUTILS_1.3 {
+ /* management functions */
+ keyctl_get_security;
+ keyctl_get_security_alloc;
+ keyctl_session_to_parent;
+
+} KEYUTILS_1.0;
+
+KEYUTILS_1.4 {
+ /* management functions */
+ keyctl_reject;
+ keyctl_instantiate_iov;
+ keyctl_invalidate;
+
+ /* utility functions */
+ recursive_key_scan;
+ recursive_session_key_scan;
+
+} KEYUTILS_1.3;
+
+KEYUTILS_1.5 {
+ /* management functions */
+ keyctl_get_persistent;
+
+ /* utility functions */
+ find_key_by_type_and_desc;
+
+} KEYUTILS_1.4;
+
+KEYUTILS_1.6 {
+ /* management functions */
+ keyctl_dh_compute;
+ keyctl_dh_compute_alloc;
+ keyctl_pkey_query;
+ keyctl_pkey_encrypt;
+ keyctl_pkey_decrypt;
+ keyctl_pkey_sign;
+ keyctl_pkey_verify;
+
+} KEYUTILS_1.5;
+
+KEYUTILS_1.7 {
+ /* management functions */
+ keyctl_restrict;
+ keyctl_dh_compute_kdf;
+ keyctl_dh_compute_kdf_alloc;
+
+} KEYUTILS_1.6;
+
+KEYUTILS_1.8 {
+ /* Public key cryptography functions */
+ keyctl_pkey_query;
+ keyctl_pkey_encrypt;
+ keyctl_pkey_decrypt;
+ keyctl_pkey_sign;
+ keyctl_pkey_verify;
+
+} KEYUTILS_1.7;
+
+KEYUTILS_1.9 {
+ /* Management functions */
+ keyctl_move;
+ keyctl_capabilities;
+
+} KEYUTILS_1.8;