Coverage Report

Created: 2026-06-09 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cryptsetup/lib/crypto_backend/crypto_cipher_kernel.c
Line
Count
Source
1
// SPDX-License-Identifier: LGPL-2.1-or-later
2
/*
3
 * Linux kernel userspace API crypto backend implementation (skcipher)
4
 *
5
 * Copyright (C) 2012-2025 Red Hat, Inc. All rights reserved.
6
 * Copyright (C) 2012-2025 Milan Broz
7
 */
8
9
#include <stdlib.h>
10
#include <stdio.h>
11
#include <errno.h>
12
#include <unistd.h>
13
#include <sys/socket.h>
14
#include <sys/stat.h>
15
#include "crypto_backend_internal.h"
16
17
#if ENABLE_AF_ALG
18
19
#include <linux/if_alg.h>
20
21
#ifndef AF_ALG
22
#define AF_ALG 38
23
#endif
24
#ifndef SOL_ALG
25
#define SOL_ALG 279
26
#endif
27
28
#ifndef ALG_SET_AEAD_AUTHSIZE
29
#define ALG_SET_AEAD_AUTHSIZE 5
30
#endif
31
32
/*
33
 * ciphers
34
 *
35
 * ENOENT - algorithm not available
36
 * ENOTSUP - AF_ALG family not available
37
 * (but cannot check specifically for skcipher API)
38
 */
39
static int _crypt_cipher_init(struct crypt_cipher_kernel *ctx,
40
            const void *key, size_t key_length,
41
            size_t tag_length, struct sockaddr_alg *sa)
42
2
{
43
2
  void *optval = NULL;
44
45
2
  if (!ctx)
46
0
    return -EINVAL;
47
48
2
  ctx->opfd = -1;
49
2
  ctx->tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
50
2
  if (ctx->tfmfd < 0) {
51
0
    crypt_cipher_destroy_kernel(ctx);
52
0
    return -ENOTSUP;
53
0
  }
54
55
2
  if (bind(ctx->tfmfd, (struct sockaddr *)sa, sizeof(*sa)) < 0) {
56
0
    crypt_cipher_destroy_kernel(ctx);
57
0
    return -ENOENT;
58
0
  }
59
60
2
  if (setsockopt(ctx->tfmfd, SOL_ALG, ALG_SET_KEY, key, key_length) < 0) {
61
0
    crypt_cipher_destroy_kernel(ctx);
62
0
    return -EINVAL;
63
0
  }
64
65
2
  if (tag_length && setsockopt(ctx->tfmfd, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, &optval, tag_length) < 0) {
66
0
    crypt_cipher_destroy_kernel(ctx);
67
0
    return -EINVAL;
68
0
  }
69
70
2
  ctx->opfd = accept(ctx->tfmfd, NULL, 0);
71
2
  if (ctx->opfd < 0) {
72
0
    crypt_cipher_destroy_kernel(ctx);
73
0
    return -EINVAL;
74
0
  }
75
76
2
  return 0;
77
2
}
78
79
int crypt_cipher_init_kernel(struct crypt_cipher_kernel *ctx, const char *name,
80
           const char *mode, const void *key, size_t key_length)
81
2
{
82
2
  struct sockaddr_alg sa = {
83
2
    .salg_family = AF_ALG,
84
2
    .salg_type = "skcipher",
85
2
  };
86
2
  int r;
87
88
2
  if (!strcmp(name, "cipher_null"))
89
0
    key_length = 0;
90
91
2
  if (!strncmp(name, "capi:", 5))
92
0
    strncpy((char *)sa.salg_name, &name[5], sizeof(sa.salg_name) - 1);
93
2
  else {
94
2
    r = snprintf((char *)sa.salg_name, sizeof(sa.salg_name), "%s(%s)", mode, name);
95
2
    if (r < 0 || (size_t)r >= sizeof(sa.salg_name))
96
0
      return -EINVAL;
97
2
  }
98
99
2
  return _crypt_cipher_init(ctx, key, key_length, 0, &sa);
100
2
}
101
102
/* musl has broken CMSG_NXTHDR macro in system headers */
103
static inline struct cmsghdr *_CMSG_NXTHDR(struct msghdr* mhdr, struct cmsghdr* cmsg)
104
2
{
105
#if !defined(__GLIBC__) && defined(__clang__)
106
#pragma clang diagnostic push
107
#pragma clang diagnostic ignored "-Wcast-align"
108
#pragma clang diagnostic ignored "-Wsign-compare"
109
  return CMSG_NXTHDR(mhdr, cmsg);
110
#pragma clang diagnostic pop
111
#else
112
2
  return CMSG_NXTHDR(mhdr, cmsg);
113
2
#endif
114
2
}
115
116
/* coverity[ -taint_source : arg-3 ] */
117
static int _crypt_cipher_crypt(struct crypt_cipher_kernel *ctx,
118
             const char *in, size_t in_length,
119
             char *out, size_t out_length,
120
             const char *iv, size_t iv_length,
121
             uint32_t direction)
122
2
{
123
2
  int r = 0;
124
2
  ssize_t len;
125
2
  struct af_alg_iv *alg_iv;
126
2
  struct cmsghdr *header;
127
2
  uint32_t *type;
128
2
  struct iovec iov = {
129
2
    .iov_base = (void*)(uintptr_t)in,
130
2
    .iov_len = in_length,
131
2
  };
132
2
  int iv_msg_size = iv ? CMSG_SPACE(sizeof(*alg_iv) + iv_length) : 0;
133
2
  int buffer_size = CMSG_SPACE(sizeof(*type)) + iv_msg_size;
134
2
  char *buffer = NULL;
135
2
  struct msghdr msg = {
136
2
    .msg_iov = &iov,
137
2
    .msg_iovlen = 1,
138
2
  };
139
140
2
  if (!in || !out || !in_length)
141
0
    return -EINVAL;
142
143
2
  if ((!iv && iv_length) || (iv && !iv_length))
144
0
    return -EINVAL;
145
146
2
  buffer = malloc(buffer_size);
147
2
  if (!buffer)
148
0
    return -ENOMEM;
149
2
  crypt_backend_memzero(buffer, buffer_size);
150
151
2
  msg.msg_control = buffer;
152
2
  msg.msg_controllen = buffer_size;
153
154
  /* Set encrypt/decrypt operation */
155
2
  header = CMSG_FIRSTHDR(&msg);
156
2
  if (!header) {
157
0
    r = -EINVAL;
158
0
    goto out;
159
0
  }
160
161
2
  header->cmsg_level = SOL_ALG;
162
2
  header->cmsg_type = ALG_SET_OP;
163
2
  header->cmsg_len = CMSG_LEN(sizeof(*type));
164
2
  type = (void*)CMSG_DATA(header);
165
2
  *type = direction;
166
167
  /* Set IV */
168
2
  if (iv) {
169
2
    header = _CMSG_NXTHDR(&msg, header);
170
2
    if (!header) {
171
0
      r = -EINVAL;
172
0
      goto out;
173
0
    }
174
175
2
    header->cmsg_level = SOL_ALG;
176
2
    header->cmsg_type = ALG_SET_IV;
177
2
    header->cmsg_len = iv_msg_size;
178
2
    alg_iv = (void*)CMSG_DATA(header);
179
2
    alg_iv->ivlen = iv_length;
180
2
    crypt_backend_memcpy(alg_iv->iv, iv, iv_length);
181
2
  }
182
183
2
  len = sendmsg(ctx->opfd, &msg, 0);
184
2
  if (len != (ssize_t)(in_length))
185
0
    r = -EIO;
186
2
  else {
187
2
    len = read(ctx->opfd, out, out_length);
188
2
    if (len != (ssize_t)out_length)
189
0
      r = -EIO;
190
2
  }
191
2
out:
192
2
  crypt_backend_memzero(buffer, buffer_size);
193
2
  free(buffer);
194
2
  return r;
195
2
}
196
197
int crypt_cipher_encrypt_kernel(struct crypt_cipher_kernel *ctx,
198
        const char *in, char *out, size_t length,
199
        const char *iv, size_t iv_length)
200
0
{
201
0
  return _crypt_cipher_crypt(ctx, in, length, out, length,
202
0
           iv, iv_length, ALG_OP_ENCRYPT);
203
0
}
204
205
int crypt_cipher_decrypt_kernel(struct crypt_cipher_kernel *ctx,
206
        const char *in, char *out, size_t length,
207
        const char *iv, size_t iv_length)
208
2
{
209
2
  return _crypt_cipher_crypt(ctx, in, length, out, length,
210
2
           iv, iv_length, ALG_OP_DECRYPT);
211
2
}
212
213
void crypt_cipher_destroy_kernel(struct crypt_cipher_kernel *ctx)
214
2
{
215
2
  if (ctx->tfmfd >= 0)
216
2
    close(ctx->tfmfd);
217
2
  if (ctx->opfd >= 0)
218
2
    close(ctx->opfd);
219
220
2
  ctx->tfmfd = -1;
221
2
  ctx->opfd = -1;
222
2
}
223
224
int crypt_cipher_check_kernel(const char *name, const char *mode,
225
            const char *integrity, size_t key_length)
226
0
{
227
0
  struct crypt_cipher_kernel c;
228
0
  char mode_name[64], tmp_salg_name[180], *cipher_iv = NULL, *key;
229
0
  const char *salg_type, *real_mode;
230
0
  bool aead;
231
0
  int r;
232
0
  struct sockaddr_alg sa = {
233
0
    .salg_family = AF_ALG,
234
0
  };
235
236
0
  aead = integrity && strcmp(integrity, "none");
237
0
  real_mode = NULL;
238
239
  /* Remove IV if present */
240
0
  if (mode) {
241
0
    strncpy(mode_name, mode, sizeof(mode_name));
242
0
    mode_name[sizeof(mode_name) - 1] = 0;
243
0
    cipher_iv = strchr(mode_name, '-');
244
0
    if (cipher_iv) {
245
0
      *cipher_iv = '\0';
246
0
      real_mode = mode_name;
247
0
    }
248
0
  }
249
250
0
  salg_type = aead ? "aead" : "skcipher";
251
0
  r = snprintf((char *)sa.salg_type, sizeof(sa.salg_type), "%s", salg_type);
252
0
  if (r < 0 || (size_t)r >= sizeof(sa.salg_type))
253
0
    return -EINVAL;
254
255
0
  memset(tmp_salg_name, 0, sizeof(tmp_salg_name));
256
257
  /* FIXME: this is duplicating a part of devmapper backend */
258
0
  if (aead) {
259
    /* In AEAD, mode parameter can be just IV like "random" */
260
0
    if (!strcmp(integrity, "poly1305"))
261
0
      r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "rfc7539(%s,%s)", name, integrity);
262
0
    else if (!real_mode)
263
0
      r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s", name);
264
0
    else if (!strcmp(real_mode, "ccm"))
265
0
      r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "rfc4309(%s(%s))", real_mode, name);
266
0
    else
267
0
      r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s(%s)", real_mode, name);
268
0
  } else {
269
0
    if (!mode)
270
0
      r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s", name);
271
0
    else
272
0
      r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s(%s)", real_mode ?: mode_name, name);
273
0
  }
274
275
0
  if (r < 0 || (size_t)r >= sizeof(tmp_salg_name))
276
0
    return -EINVAL;
277
278
0
  memcpy(sa.salg_name, tmp_salg_name, sizeof(sa.salg_name));
279
280
0
  key = malloc(key_length);
281
0
  if (!key)
282
0
    return -ENOMEM;
283
284
  /* We cannot use RNG yet, any key works here, tweak the first part if it is split key (XTS). */
285
0
  memset(key, 0xab, key_length);
286
0
  *key = 0xef;
287
288
0
  r = _crypt_cipher_init(&c, key, key_length, 0, &sa);
289
0
  crypt_cipher_destroy_kernel(&c);
290
0
  free(key);
291
292
0
  return r;
293
0
}
294
295
int crypt_bitlk_decrypt_key_kernel(const void *key, size_t key_length,
296
           const char *in, char *out, size_t length,
297
           const char *iv, size_t iv_length,
298
           const char *tag, size_t tag_length)
299
0
{
300
0
  struct crypt_cipher_kernel c;
301
0
  struct sockaddr_alg sa = {
302
0
    .salg_family = AF_ALG,
303
0
    .salg_type = "aead",
304
0
    .salg_name = "ccm(aes)",
305
0
  };
306
0
  int r;
307
0
  char buffer[128], ccm_iv[16];
308
309
0
  if (length + tag_length > sizeof(buffer))
310
0
    return -EINVAL;
311
312
0
  if (iv_length > sizeof(ccm_iv) - 2)
313
0
    return -EINVAL;
314
315
0
  r = _crypt_cipher_init(&c, key, key_length, tag_length, &sa);
316
0
  if (r < 0)
317
0
    return r;
318
319
0
  memcpy(buffer, in, length);
320
0
  memcpy(buffer + length, tag, tag_length);
321
322
  /* CCM IV - RFC3610 */
323
0
  memset(ccm_iv, 0, sizeof(ccm_iv));
324
0
  ccm_iv[0] = 15 - iv_length - 1;
325
0
  memcpy(ccm_iv + 1, iv, iv_length);
326
0
  memset(ccm_iv + 1 + iv_length, 0, ccm_iv[0] + 1);
327
0
  iv_length = sizeof(ccm_iv);
328
329
0
  r =  _crypt_cipher_crypt(&c, buffer, length + tag_length, out, length,
330
0
         ccm_iv, iv_length, ALG_OP_DECRYPT);
331
332
0
  crypt_cipher_destroy_kernel(&c);
333
0
  crypt_backend_memzero(buffer, sizeof(buffer));
334
335
0
  return r;
336
0
}
337
338
#else /* ENABLE_AF_ALG */
339
#pragma GCC diagnostic ignored "-Wunused-parameter"
340
341
int crypt_cipher_init_kernel(struct crypt_cipher_kernel *ctx, const char *name,
342
           const char *mode, const void *key, size_t key_length)
343
{
344
  return -ENOTSUP;
345
}
346
347
void crypt_cipher_destroy_kernel(struct crypt_cipher_kernel *ctx)
348
{
349
  return;
350
}
351
352
int crypt_cipher_encrypt_kernel(struct crypt_cipher_kernel *ctx,
353
        const char *in, char *out, size_t length,
354
        const char *iv, size_t iv_length)
355
{
356
  return -EINVAL;
357
}
358
int crypt_cipher_decrypt_kernel(struct crypt_cipher_kernel *ctx,
359
        const char *in, char *out, size_t length,
360
        const char *iv, size_t iv_length)
361
{
362
  return -EINVAL;
363
}
364
int crypt_cipher_check_kernel(const char *name, const char *mode,
365
            const char *integrity, size_t key_length)
366
{
367
  return -ENOTSUP;
368
}
369
int crypt_bitlk_decrypt_key_kernel(const void *key, size_t key_length,
370
           const char *in, char *out, size_t length,
371
           const char *iv, size_t iv_length,
372
           const char *tag, size_t tag_length)
373
{
374
  return -ENOTSUP;
375
}
376
#endif