keyctl(2)
โจ keyctl(2) โ System Calls Manual โจ
okay bestie, buckle UP because we're about to get into the kernel's whole key management situation ๐
this is keyctl(2), and yes it's the system call, not the libkeyutils one (keyctl(3) is its own thing, we'll get there).
๐ธ NAME ๐ธ
keyctl โ manipulate the kernel's key management facility ๐โจ
๐ LIBRARY ๐
Standard C library (libc, -lc) ๐ซถ
๐ฆ SYNOPSIS ๐ฆ
#include <linux/keyctl.h> /* Definition of KEY* constants */
#include <sys/syscall.h> /* Definition of SYS_* constants */
#include <unistd.h>
long syscall(SYS_keyctl, int op, ...);
๐ DESCRIPTION ๐
so basically keyctl() lets user-space programs do key manipulation. that's it, that's the vibe ๐
which operation actually happens? that's entirely up to the value of the op argument, bestie. the way that works is: every single one of these operations is wrapped by the libkeyutils library (from the keyutils package) into its own individual function, which you can peep at in keyctl(3) โ and the reason that's iconic is because it lets the compiler actually check your types for you ๐
the permitted values for op are:
KEYCTL_GET_KEYRING_ID(2const)KEYCTL_JOIN_SESSION_KEYRING(2const)KEYCTL_UPDATE(2const)KEYCTL_REVOKE(2const)KEYCTL_CHOWN(2const)KEYCTL_SETPERM(2const)KEYCTL_DESCRIBE(2const)KEYCTL_CLEAR(2const)KEYCTL_LINK(2const)KEYCTL_UNLINK(2const)KEYCTL_SEARCH(2const)KEYCTL_READ(2const)KEYCTL_INSTANTIATE(2const)KEYCTL_INSTANTIATE_IOV(2const)KEYCTL_NEGATE(2const)KEYCTL_REJECT(2const)KEYCTL_SET_REQKEY_KEYRING(2const)KEYCTL_SET_TIMEOUT(2const)KEYCTL_ASSUME_AUTHORITY(2const)KEYCTL_GET_SECURITY(2const)KEYCTL_SESSION_TO_PARENT(2const)KEYCTL_INVALIDATE(2const)KEYCTL_GET_PERSISTENT(2const)KEYCTL_DH_COMPUTE(2const)KEYCTL_RESTRICT_KEYRING(2const)
like, that's the whole lineup, no notes ๐
โญ RETURN VALUE โญ
so on a successful call? the return value depends on the operation, bestie. it's giving "it depends" energy, because it literally does ๐ each op has its own idea of what a good result looks like.
on error, -1 is returned, and errno is set to indicate the error. classic.
๐ ERRORS ๐
EACCESโ the requested operation wasn't permitted. nope. not today. ๐ซEDQUOTโ the key quota for the caller's user would be exceeded by creating a key or linking it to the keyring. you're out of room bestie ๐ญEINVALโ size of the string (including the terminating null byte) specified inarg3(the key type) orarg4(the key description) exceeded the limit (32 bytes and 4096 bytes respectively). too long, too much, it's giving overflow ๐ซEKEYEXPIREDโ an expired key was found or specified. it's past its moment ๐EKEYREJECTEDโ a rejected key was found or specified. not me getting rejected ๐ญEKEYREVOKEDโ a revoked key was found or specified. access denied, sweetie โENOKEYโ no matching key was found or an invalid key was specified. she's not here ๐ปENOMEMโ one of kernel memory allocation routines failed during the execution of the syscall. the kernel ran out of vibes (memory) ๐ตENOTDIRโ a key of keyring type was expected but the ID of a key with a different type was provided. wrong type of key, bestie, that's not even a keyring ๐
๐ VERSIONS ๐
a wrapper is provided in the libkeyutils library. (the accompanying package provides the <keyutils.h> header file.) ๐ซถ
but real talk โ rather than using this system call directly, you probably want to use the various library functions mentioned in the descriptions of the individual operations above. trust, it's nicer. no cap.
๐ STANDARDS ๐
Linux. that's it, that's the standard ๐
๐ง HISTORY ๐ง
Linux 2.6.10. vintage, but make it iconic โจ
โจ EXAMPLES โจ
okay bestie, so this little program? it gives you a subset of the functionality of the request-key(8) program that ships in the keyutils package ๐
and for informational purposes, it writes a bunch of stuff into a log file. yes, a whole lil diary. iconic behavior honestly.
as request_key(2) explains, the request-key(8) program gets invoked with command-line arguments describing a key that's about to be instantiated. our example program grabs those arguments and logs them, then assumes authority to instantiate the requested key, and instantiates it. main character energy fr fr. ๐
so here's a shell session showing it off. we compile the thing, then use it to temporarily swap out the standard request-key(8) program.
ngl, temporarily disabling the standard request-key(8) program may not be safe on some systems. consider yourself warned, bestie ๐ญ
while our example program is installed, we use the example program from request_key(2) to request a key:
$ cc -o key_instantiate key_instantiate.c -lkeyutils;
$ sudo mv /sbin/request-key /sbin/request-key.backup;
$ sudo cp key_instantiate /sbin/request-key;
$ ./t_request_key user mykey somepayloaddata;
Key ID is 20d035bf
$ sudo mv /sbin/request-key.backup /sbin/request-key;
and now, peeking at the log file our program made? we can literally see the command-line arguments that got handed to it:
$ cat /tmp/key_instantiate.log;
Time: Mon Nov 7 13:06:47 2016
Command line arguments:
argv[0]: /sbin/request-key
operation: create
key_to_instantiate: 20d035bf
UID: 1000
GID: 1000
thread_keyring: 0
process_keyring: 0
session_keyring: 256e6a6
Key description: user;1000;1000;3f010000;mykey
Auth key payload: somepayloaddata
Destination keyring: 256e6a6
Auth key description: .request_key_auth;1000;1000;0b010000;20d035bf
the last few lines up there prove our example program was able to fetch:
- the description of the key to be instantiated, which included the name of the key (
mykey); - the payload of the authorization key, which was the data (
somepayloaddata) passed torequest_key(2); - the destination keyring that was specified in the call to
request_key(2); and - the description of the authorization key โ and look at that, the name of the authorization key matches the ID of the key that's to be instantiated (
20d035bf). the math is mathing ๐
the example program in request_key(2) passed the destination keyring as KEY_SPEC_SESSION_KEYRING. and by examining the contents of /proc/keys we can see that got translated into the ID of the destination keyring (0256e6a6) showing up in the log output above. we can also see the newly created key, named mykey, ID 20d035bf. slay ๐ฆ
$ cat /proc/keys | egrep 'mykey|256e6a6';
0256e6a6 I--Q--- 194 perm 3f030000 1000 1000 keyring _ses: 3
20d035bf I--Q--- 1 perm 3f010000 1000 1000 user mykey: 16
๐งโ๐ป Program source ๐งโ๐ป
/* key_instantiate.c */
#include <errno.h>
#include <keyutils.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#ifndef KEY_SPEC_REQUESTOR_KEYRING
#define KEY_SPEC_REQUESTOR_KEYRING (-8)
#endif
int
main(int argc, char *argv[])
{
int akp_size; /* Size of auth_key_payload */
int auth_key;
char dbuf[256];
char auth_key_payload[256];
char *operation;
FILE *fp;
gid_t gid;
uid_t uid;
time_t t;
key_serial_t key_to_instantiate, dest_keyring;
key_serial_t thread_keyring, process_keyring, session_keyring;
if (argc != 8) {
fprintf(stderr, "Usage: %s op key uid gid thread_keyring "
"process_keyring session_keyring\n", argv[0]);
exit(EXIT_FAILURE);
}
fp = fopen("/tmp/key_instantiate.log", "w");
if (fp == NULL)
exit(EXIT_FAILURE);
setbuf(fp, NULL);
t = time(NULL);
fprintf(fp, "Time: %s\n", ctime(&t));
/*
* The kernel passes a fixed set of arguments to the program
* that it execs; fetch them.
*/
operation = argv[1];
key_to_instantiate = atoi(argv[2]);
uid = atoi(argv[3]);
gid = atoi(argv[4]);
thread_keyring = atoi(argv[5]);
process_keyring = atoi(argv[6]);
session_keyring = atoi(argv[7]);
fprintf(fp, "Command line arguments:\n");
fprintf(fp, " argv[0]: %s\n", argv[0]);
fprintf(fp, " operation: %s\n", operation);
fprintf(fp, " key_to_instantiate: %jx\n",
(uintmax_t) key_to_instantiate);
fprintf(fp, " UID: %jd\n", (intmax_t) uid);
fprintf(fp, " GID: %jd\n", (intmax_t) gid);
fprintf(fp, " thread_keyring: %jx\n",
(uintmax_t) thread_keyring);
fprintf(fp, " process_keyring: %jx\n",
(uintmax_t) process_keyring);
fprintf(fp, " session_keyring: %jx\n",
(uintmax_t) session_keyring);
fprintf(fp, "\n");
/*
* Assume the authority to instantiate the key named in argv[2].
*/
if (keyctl(KEYCTL_ASSUME_AUTHORITY, key_to_instantiate) == -1) {
fprintf(fp, "KEYCTL_ASSUME_AUTHORITY failed: %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
/*
* Fetch the description of the key that is to be instantiated.
*/
if (keyctl(KEYCTL_DESCRIBE, key_to_instantiate,
dbuf, sizeof(dbuf)) == -1) {
fprintf(fp, "KEYCTL_DESCRIBE failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
fprintf(fp, "Key description: %s\n", dbuf);
/*
* Fetch the payload of the authorization key, which is
* actually the callout data given to request_key().
*/
akp_size = keyctl(KEYCTL_READ, KEY_SPEC_REQKEY_AUTH_KEY,
auth_key_payload, sizeof(auth_key_payload));
if (akp_size == -1) {
fprintf(fp, "KEYCTL_READ failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
auth_key_payload[akp_size] = '\0';
fprintf(fp, "Auth key payload: %s\n", auth_key_payload);
/*
* For interest, get the ID of the authorization key and
* display it.
*/
auth_key = keyctl(KEYCTL_GET_KEYRING_ID,
KEY_SPEC_REQKEY_AUTH_KEY);
if (auth_key == -1) {
fprintf(fp, "KEYCTL_GET_KEYRING_ID failed: %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
fprintf(fp, "Auth key ID: %jx\n", (uintmax_t) auth_key);
/*
* Fetch key ID for the request_key(2) destination keyring.
*/
dest_keyring = keyctl(KEYCTL_GET_KEYRING_ID,
KEY_SPEC_REQUESTOR_KEYRING);
if (dest_keyring == -1) {
fprintf(fp, "KEYCTL_GET_KEYRING_ID failed: %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
fprintf(fp, "Destination keyring: %jx\n", (uintmax_t) dest_keyring);
/*
* Fetch the description of the authorization key. This
* allows us to see the key type, UID, GID, permissions,
* and description (name) of the key. Among other things,
* we will see that the name of the key is a hexadecimal
* string representing the ID of the key to be instantiated.
*/
if (keyctl(KEYCTL_DESCRIBE, KEY_SPEC_REQKEY_AUTH_KEY,
dbuf, sizeof(dbuf)) == -1)
{
fprintf(fp, "KEYCTL_DESCRIBE failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
fprintf(fp, "Auth key description: %s\n", dbuf);
/*
* Instantiate the key using the callout data that was supplied
* in the payload of the authorization key.
*/
if (keyctl(KEYCTL_INSTANTIATE, key_to_instantiate,
auth_key_payload, akp_size + 1, dest_keyring) == -1)
{
fprintf(fp, "KEYCTL_INSTANTIATE failed: %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
๐ SEE ALSO ๐
go read these next, bestie, it's a whole rabbit hole and it's so worth it ๐ซถ
keyctl(1)add_key(2)request_key(2)keyctl(3)recursive_key_scan(3)recursive_session_key_scan(3)capabilities(7)credentials(7)keyrings(7)keyutils(7)persistent-keyring(7)process-keyring(7)session-keyring(7)thread-keyring(7)user-keyring(7)user_namespaces(7)user-session-keyring(7)request-key(8)
and if you wanna go straight to the source (literally), the kernel source files live under Documentation/security/keys/. ๐
and that's a wrap on keyctl(2), my love ๐ this page came from Linux man-pages 6.20, dated 2026-02-08. drink some water, go touch grass, and remember: instantiate responsibly. periodt. ๐
โจ๐ธ
๐ฆ where this page comes from
- arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- ๐ the page's license: Linux-man-pages-copyleft
- Copyright 2016, Michael Kerrisk <mtk.manpages@gmail.com>
- Copyright 2016, Eugene Syromyatnikov <evgsyr@gmail.com>
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms