Coverage Report

Created: 2026-07-16 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cryptsetup/lib/utils_keyring.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * kernel keyring utilities
4
 *
5
 * Copyright (C) 2016-2026 Red Hat, Inc. All rights reserved.
6
 * Copyright (C) 2016-2026 Ondrej Kozina
7
 */
8
9
#include <assert.h>
10
#include <ctype.h>
11
#include <errno.h>
12
#include <fcntl.h>
13
#include <stdio.h>
14
#include <stdbool.h>
15
#include <stdlib.h>
16
#include <string.h>
17
#include <unistd.h>
18
#include <sys/syscall.h>
19
20
#include "libcryptsetup.h"
21
#include "libcryptsetup_macros.h"
22
#include "utils_keyring.h"
23
24
#if KERNEL_KEYRING
25
26
static const struct {
27
  key_type_t type;
28
  const char *type_name;
29
} key_types[] = {
30
  { LOGON_KEY,     "logon" },
31
  { USER_KEY,      "user" },
32
  { BIG_KEY,       "big_key" },
33
  { TRUSTED_KEY,   "trusted" },
34
  { ENCRYPTED_KEY, "encrypted" },
35
  { KEYRING_KEY,   "keyring" },
36
};
37
38
#include <linux/keyctl.h>
39
40
/* request_key */
41
static key_serial_t request_key(const char *type,
42
  const char *description,
43
  const char *callout_info,
44
  key_serial_t keyring)
45
0
{
46
0
  return syscall(__NR_request_key, type, description, callout_info, keyring);
47
0
}
48
49
/* add_key */
50
static key_serial_t add_key(const char *type,
51
  const char *description,
52
  const void *payload,
53
  size_t plen,
54
  key_serial_t keyring)
55
0
{
56
0
  return syscall(__NR_add_key, type, description, payload, plen, keyring);
57
0
}
58
59
/* keyctl_describe */
60
static long keyctl_describe(key_serial_t id, char *buffer, size_t buflen)
61
0
{
62
0
  return syscall(__NR_keyctl, KEYCTL_DESCRIBE, id, buffer, buflen);
63
0
}
64
65
/* keyctl_read */
66
static long keyctl_read(key_serial_t key, char *buffer, size_t buflen)
67
0
{
68
0
  return syscall(__NR_keyctl, KEYCTL_READ, key, buffer, buflen);
69
0
}
70
71
/* keyctl_link */
72
static long keyctl_link(key_serial_t key, key_serial_t keyring)
73
0
{
74
0
  return syscall(__NR_keyctl, KEYCTL_LINK, key, keyring);
75
0
}
76
77
/* keyctl_unlink */
78
static long keyctl_unlink(key_serial_t key, key_serial_t keyring)
79
0
{
80
0
  return syscall(__NR_keyctl, KEYCTL_UNLINK, key, keyring);
81
0
}
82
83
/* inspired by keyutils written by David Howells (dhowells@redhat.com) */
84
static key_serial_t keyring_process_proc_keys_line(char *line, const char *type, const char *desc,
85
               key_serial_t destringid)
86
0
{
87
0
  char typebuf[41], rdesc[1024], *kdesc, *cp;
88
0
  int ndesc, n;
89
0
  key_serial_t id;
90
0
  int dlen;
91
92
0
  assert(desc);
93
0
  dlen = strlen(desc);
94
0
  cp = line + strlen(line);
95
96
0
  ndesc = 0;
97
0
  n = sscanf(line, "%x %*s %*u %*s %*x %*d %*d %40s %n",
98
0
      &id, typebuf, &ndesc);
99
0
  if (n == 2 && ndesc > 0 && ndesc <= cp - line) {
100
0
    if (strcmp(typebuf, type) != 0)
101
0
      return 0;
102
0
    kdesc = line + ndesc;
103
0
    if (memcmp(kdesc, desc, dlen) != 0)
104
0
      return 0;
105
0
    if (kdesc[dlen] != ':' &&
106
0
        kdesc[dlen] != '\0' &&
107
0
        kdesc[dlen] != ' ')
108
0
      return 0;
109
0
    kdesc[dlen] = '\0';
110
111
    /* The key type appends extra stuff to the end of the
112
     * description after a colon in /proc/keys.  Colons,
113
     * however, are allowed in descriptions, so we need to
114
     * make a further check. */
115
0
    n = keyctl_describe(id, rdesc, sizeof(rdesc) - 1);
116
0
    if (n < 0)
117
0
      return 0;
118
0
    if ((size_t)n >= sizeof(rdesc) - 1)
119
0
      return 0;
120
0
    rdesc[n] = '\0';
121
122
0
    cp = strrchr(rdesc, ';');
123
0
    if (!cp)
124
0
      return 0;
125
0
    cp++;
126
0
    if (strcmp(cp, desc) != 0)
127
0
      return 0;
128
129
130
0
    if (destringid && keyctl_link(id, destringid) == -1)
131
0
      return 0;
132
133
0
    return id;
134
0
  }
135
136
0
  return 0;
137
0
}
138
139
/* inspired by keyutils written by David Howells (dhowells@redhat.com), returns 0 ID on failure */
140
141
static key_serial_t find_key_by_type_and_desc(const char *type, const char *desc, key_serial_t destringid)
142
0
{
143
0
  key_serial_t id;
144
0
  int f;
145
0
  char buf[1024];
146
0
  char *newline;
147
0
  size_t buffer_len = 0;
148
149
0
  ssize_t n;
150
151
0
  do {
152
0
    id = request_key(type, desc, NULL, 0);
153
0
  } while (id < 0 && errno == EINTR);
154
155
0
  if (id < 0 && errno == ENOMEM)
156
0
    return 0;
157
158
0
  if (id >= 0)
159
0
    return id;
160
161
0
  f = open("/proc/keys", O_RDONLY);
162
0
  if (f < 0)
163
0
    return 0;
164
165
0
  while ((n = read(f, buf + buffer_len, sizeof(buf) - buffer_len - 1)) > 0) {
166
    /* coverity[overflow:FALSE] */
167
0
    buffer_len += (size_t)n;
168
0
    buf[buffer_len] = '\0';
169
0
    newline = strchr(buf, '\n');
170
0
    while (newline != NULL && buffer_len != 0) {
171
0
      *newline = '\0';
172
173
0
      if ((id = keyring_process_proc_keys_line(buf, type, desc, destringid))) {
174
0
        close(f);
175
0
        return id;
176
0
      }
177
178
0
      buffer_len -= newline - buf + 1;
179
0
      if (buffer_len >= sizeof(buf)) {
180
0
        close(f);
181
0
        return 0;
182
0
      }
183
0
      memmove(buf, newline + 1, buffer_len);
184
0
      buf[buffer_len] = '\0';
185
0
      newline = strchr(buf, '\n');
186
0
    }
187
0
  }
188
189
0
  close(f);
190
0
  return 0;
191
0
}
192
193
int keyring_check(void)
194
0
{
195
  /* logon type key descriptions must be in format "prefix:description" */
196
0
  return syscall(__NR_request_key, "logon", "dummy", NULL, 0) == -1l && errno != ENOSYS;
197
0
}
198
199
key_serial_t keyring_add_key_to_keyring(key_type_t ktype,
200
    const char *key_desc,
201
    const void *key,
202
    size_t key_size,
203
    key_serial_t keyring)
204
0
{
205
0
  const char *type_name = key_type_name(ktype);
206
207
0
  if (!type_name || !key_desc)
208
0
    return -EINVAL;
209
210
0
  return add_key(type_name, key_desc, key, key_size, keyring);
211
0
}
212
213
key_serial_t keyring_add_key_in_thread_keyring(key_type_t ktype, const char *key_desc, const void *key, size_t key_size)
214
0
{
215
0
  return keyring_add_key_to_keyring(ktype, key_desc, key, key_size, KEY_SPEC_THREAD_KEYRING);
216
0
}
217
218
key_serial_t keyring_request_key_id(key_type_t key_type,
219
    const char *key_description)
220
0
{
221
0
  key_serial_t kid;
222
223
0
  do {
224
0
    kid = request_key(key_type_name(key_type), key_description, NULL, 0);
225
0
  } while (kid < 0 && errno == EINTR);
226
227
0
  return kid;
228
0
}
229
230
int keyring_read_keysize(key_serial_t kid,
231
    size_t *r_key_size)
232
0
{
233
0
  long r;
234
235
0
  assert(r_key_size);
236
237
  /* just get payload size */
238
0
  r = keyctl_read(kid, NULL, 0);
239
0
  if (r > 0) {
240
0
    *r_key_size = r;
241
0
    return 0;
242
0
  }
243
244
0
  return -EINVAL;
245
0
}
246
247
int keyring_read_key(key_serial_t kid,
248
    char **key,
249
    size_t *key_size)
250
0
{
251
0
  int r;
252
0
  size_t len;
253
0
  char *buf = NULL;
254
255
0
  assert(key);
256
0
  assert(key_size);
257
258
  /* just get payload size */
259
0
  r = keyring_read_keysize(kid, &len);
260
0
  if (r < 0)
261
0
    return r;
262
263
0
  buf = crypt_safe_alloc(len);
264
0
  if (!buf)
265
0
    return -ENOMEM;
266
267
  /* retrieve actual payload data */
268
0
  r = keyctl_read(kid, buf, len);
269
0
  if (r < 0) {
270
0
    crypt_safe_free(buf);
271
0
    return -EINVAL;
272
0
  }
273
274
0
  *key = buf;
275
0
  *key_size = len;
276
277
0
  return 0;
278
0
}
279
280
int keyring_unlink_key_from_keyring(key_serial_t kid, key_serial_t keyring_id)
281
0
{
282
0
  return keyctl_unlink(kid, keyring_id) < 0 ? -EINVAL : 0;
283
0
}
284
285
int keyring_unlink_key_from_thread_keyring(key_serial_t kid)
286
0
{
287
0
  return keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING) < 0 ? -EINVAL : 0;
288
0
}
289
290
const char *key_type_name(key_type_t type)
291
0
{
292
0
  unsigned int i;
293
294
0
  for (i = 0; i < ARRAY_SIZE(key_types); i++)
295
0
    if (type == key_types[i].type)
296
0
      return key_types[i].type_name;
297
298
0
  return NULL;
299
0
}
300
301
key_type_t keyring_type_and_name(const char *key_name, const char **name)
302
0
{
303
0
  const char *name_tmp;
304
0
  char type[16];
305
0
  size_t type_len;
306
307
0
  if (!key_name || key_name[0] != '%')
308
0
    return INVALID_KEY;
309
310
0
  key_name++;
311
0
  if (!*key_name || *key_name == ':')
312
0
    return INVALID_KEY;
313
314
0
  name_tmp = strchr(key_name, ':');
315
0
  if (!name_tmp)
316
0
    return INVALID_KEY;
317
0
  name_tmp++;
318
319
0
  type_len = name_tmp - key_name - 1;
320
0
  if (type_len >= sizeof(type) - 1)
321
0
    return INVALID_KEY;
322
323
0
  memcpy(type, key_name, type_len);
324
0
  type[type_len] = '\0';
325
326
0
  if (name)
327
0
    *name = name_tmp;
328
329
0
  return key_type_by_name(type);
330
0
}
331
332
key_serial_t keyring_find_key_id_by_name(const char *key_name)
333
0
{
334
0
  key_serial_t id = 0;
335
0
  char *end;
336
0
  char *name_copy, *name_copy_p;
337
338
0
  assert(key_name);
339
340
0
  if (key_name[0] == '@') {
341
0
    if (strcmp(key_name, "@t" ) == 0) return KEY_SPEC_THREAD_KEYRING;
342
0
    if (strcmp(key_name, "@p" ) == 0) return KEY_SPEC_PROCESS_KEYRING;
343
0
    if (strcmp(key_name, "@s" ) == 0) return KEY_SPEC_SESSION_KEYRING;
344
0
    if (strcmp(key_name, "@u" ) == 0) return KEY_SPEC_USER_KEYRING;
345
0
    if (strcmp(key_name, "@us") == 0) return KEY_SPEC_USER_SESSION_KEYRING;
346
0
    if (strcmp(key_name, "@g" ) == 0) return KEY_SPEC_GROUP_KEYRING;
347
0
    if (strcmp(key_name, "@a" ) == 0) return KEY_SPEC_REQKEY_AUTH_KEY;
348
349
0
    return 0;
350
0
  }
351
352
  /* handle a lookup-by-name request "%<type>:<desc>", eg: "%keyring:_ses" */
353
0
  name_copy = strdup(key_name);
354
0
  if (!name_copy)
355
0
    goto out;
356
0
  name_copy_p = name_copy;
357
358
0
  if (name_copy_p[0] == '%') {
359
0
    const char *type;
360
361
0
    name_copy_p++;
362
0
    if (!*name_copy_p)
363
0
      goto out;
364
365
0
    if (*name_copy_p == ':') {
366
0
      type = "keyring";
367
0
      name_copy_p++;
368
0
    } else {
369
0
      type = name_copy_p;
370
0
      name_copy_p = strchr(name_copy_p, ':');
371
0
      if (!name_copy_p)
372
0
        goto out;
373
0
      *(name_copy_p++) = '\0';
374
0
    }
375
376
0
    if (!*name_copy_p)
377
0
      goto out;
378
379
0
    id = find_key_by_type_and_desc(type, name_copy_p, 0);
380
0
    goto out;
381
0
  }
382
383
0
  id = strtoul(key_name, &end, 0);
384
0
  if (*end)
385
0
    id = 0;
386
387
0
out:
388
0
  free(name_copy);
389
390
0
  return id;
391
0
}
392
393
static bool numbered(const char *str)
394
0
{
395
0
  char *endp;
396
397
0
  errno = 0;
398
0
  (void) strtol(str, &endp, 0);
399
0
  if (errno == ERANGE)
400
0
    return false;
401
402
0
  return *endp == '\0' ? true : false;
403
0
}
404
405
key_serial_t keyring_find_keyring_id_by_name(const char *keyring_name)
406
0
{
407
0
  assert(keyring_name);
408
409
  /* "%:" is abbreviation for the type keyring */
410
0
  if ((keyring_name[0] == '@' && keyring_name[1] != 'a') ||
411
0
      strstr(keyring_name, "%:") || strstr(keyring_name, "%keyring:") ||
412
0
      numbered(keyring_name))
413
0
    return keyring_find_key_id_by_name(keyring_name);
414
415
0
  return 0;
416
0
}
417
418
key_type_t key_type_by_name(const char *name)
419
0
{
420
0
  unsigned int i;
421
422
0
  for (i = 0; i < ARRAY_SIZE(key_types); i++)
423
0
    if (!strcmp(key_types[i].type_name, name))
424
0
      return key_types[i].type;
425
426
0
  return INVALID_KEY;
427
0
}
428
429
#else /* KERNEL_KEYRING */
430
#pragma GCC diagnostic ignored "-Wunused-parameter"
431
432
int keyring_check(void)
433
{
434
  return 0;
435
}
436
437
key_serial_t keyring_add_key_in_thread_keyring(key_type_t ktype, const char *key_desc, const void *key, size_t key_size)
438
{
439
  return -ENOTSUP;
440
}
441
442
key_serial_t keyring_request_key_id(key_type_t key_type,
443
    const char *key_description)
444
{
445
  return -ENOTSUP;
446
}
447
448
int keyring_read_keysize(key_serial_t kid,
449
    size_t *r_key_size)
450
{
451
  return -ENOTSUP;
452
}
453
454
int keyring_read_key(key_serial_t kid,
455
    char **key,
456
    size_t *key_size)
457
{
458
  return -ENOTSUP;
459
}
460
461
int keyring_read_by_id(const char *key_desc, char **passphrase, size_t *passphrase_len)
462
{
463
  return -ENOTSUP;
464
}
465
466
const char *key_type_name(key_type_t type)
467
{
468
  return NULL;
469
}
470
471
key_type_t keyring_type_and_name(const char *key_name, const char **name)
472
{
473
  return INVALID_KEY;
474
}
475
476
key_serial_t keyring_find_key_id_by_name(const char *key_name)
477
{
478
  return 0;
479
}
480
481
key_serial_t keyring_find_keyring_id_by_name(const char *keyring_name)
482
{
483
  return 0;
484
}
485
486
key_type_t key_type_by_name(const char *name)
487
{
488
  return INVALID_KEY;
489
}
490
491
key_serial_t keyring_add_key_to_keyring(key_type_t ktype,
492
          const char *key_desc,
493
          const void *key,
494
          size_t key_size,
495
          key_serial_t keyring_to_link)
496
{
497
  return -ENOTSUP;
498
}
499
500
int keyring_unlink_key_from_keyring(key_serial_t kid, key_serial_t keyring_id)
501
{
502
  return -ENOTSUP;
503
}
504
505
int keyring_unlink_key_from_thread_keyring(key_serial_t kid)
506
{
507
  return -ENOTSUP;
508
}
509
#endif