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:

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 in arg3 (the key type) or arg4 (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 to request_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 ๐Ÿซถ

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
  • ๐Ÿ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms