Coverage Report

Created: 2026-01-07 06:10

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