Coverage Report

Created: 2026-09-13 06:13

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-2026 Red Hat, Inc. All rights reserved.
6
 * Copyright (C) 2012-2026 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
1
{
43
1
  void *optval = NULL;
44
45
1
  if (!ctx)
46
0
    return -EINVAL;
47
48
1
  ctx->opfd = -1;
49
1
  ctx->tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
50
1
  if (ctx->tfmfd < 0) {
51
0
    crypt_cipher_destroy_kernel(ctx);
52
0
    return -ENOTSUP;
53
0
  }
54
55
1
  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
1
  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
1
  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
1
  ctx->opfd = accept(ctx->tfmfd, NULL, 0);
71
1
  if (ctx->opfd < 0) {
72
0
    crypt_cipher_destroy_kernel(ctx);
73
0
    return -EINVAL;
74
0
  }
75
76
1
  return 0;
77
1
}
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
1
{
82
1
  struct sockaddr_alg sa = {
83
1
    .salg_family = AF_ALG,
84
1
    .salg_type = "skcipher",
85
1
  };
86
1
  int r;
87
88
1
  if (!strcmp(name, "cipher_null"))
89
0
    key_length = 0;
90
91
1
  if (!strncmp(name, "capi:", 5))
92
0
    strncpy((char *)sa.salg_name, &name[5], sizeof(sa.salg_name) - 1);
93
1
  else {
94
1
    r = snprintf((char *)sa.salg_name, sizeof(sa.salg_name), "%s(%s)", mode, name);
95
1
    if (r < 0 || (size_t)r >= sizeof(sa.salg_name))
96
0
      return -EINVAL;
97
1
  }
98
99
1
  return _crypt_cipher_init(ctx, key, key_length, 0, &sa);
100
1
}
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
1
{
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
1
  return CMSG_NXTHDR(mhdr, cmsg);
113
1
#endif
114
1
}
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
1
{
123
1
  int r = 0;
124
1
  ssize_t len;
125
1
  struct af_alg_iv *alg_iv;
126
1
  struct cmsghdr *header;
127
1
  uint32_t *type;
128
1
  struct iovec iov = {
129
1
    .iov_base = (void*)(uintptr_t)in,
130
1
    .iov_len = in_length,
131
1
  };
132
1
  int iv_msg_size = iv ? CMSG_SPACE(sizeof(*alg_iv) + iv_length) : 0;
133
1
  int buffer_size = CMSG_SPACE(sizeof(*type)) + iv_msg_size;
134
1
  char *buffer = NULL;
135
1
  struct msghdr msg = {
136
1
    .msg_iov = &iov,
137
1
    .msg_iovlen = 1,
138
1
  };
139
140
1
  if (!in || !out || !in_length)
141
0
    return -EINVAL;
142
143
1
  if ((!iv && iv_length) || (iv && !iv_length))
144
0
    return -EINVAL;
145
146
1
  buffer = malloc(buffer_size);
147
1
  if (!buffer)
148
0
    return -ENOMEM;
149
1
  crypt_backend_memzero(buffer, buffer_size);
150
151
1
  msg.msg_control = buffer;
152
1
  msg.msg_controllen = buffer_size;
153
154
  /* Set encrypt/decrypt operation */
155
1
  header = CMSG_FIRSTHDR(&msg);
156
1
  if (!header) {
157
0
    r = -EINVAL;
158
0
    goto out;
159
0
  }
160
161
1
  header->cmsg_level = SOL_ALG;
162
1
  header->cmsg_type = ALG_SET_OP;
163
1
  header->cmsg_len = CMSG_LEN(sizeof(*type));
164
1
  type = (void*)CMSG_DATA(header);
165
1
  *type = direction;
166
167
  /* Set IV */
168
1
  if (iv) {
169
1
    header = _CMSG_NXTHDR(&msg, header);
170
1
    if (!header) {
171
0
      r = -EINVAL;
172
0
      goto out;
173
0
    }
174
175
1
    header->cmsg_level = SOL_ALG;
176
1
    header->cmsg_type = ALG_SET_IV;
177
1
    header->cmsg_len = iv_msg_size;
178
1
    alg_iv = (void*)CMSG_DATA(header);
179
1
    alg_iv->ivlen = iv_length;
180
1
    crypt_backend_memcpy(alg_iv->iv, iv, iv_length);
181
1
  }
182
183
1
  len = sendmsg(ctx->opfd, &msg, 0);
184
1
  if (len != (ssize_t)(in_length))
185
0
    r = -EIO;
186
1
  else {
187
1
    len = read(ctx->opfd, out, out_length);
188
1
    if (len != (ssize_t)out_length)
189
0
      r = -EIO;
190
1
  }
191
1
out:
192
1
  crypt_backend_memzero(buffer, buffer_size);
193
1
  free(buffer);
194
1
  return r;
195
1
}
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
1
{
209
1
  return _crypt_cipher_crypt(ctx, in, length, out, length,
210
1
           iv, iv_length, ALG_OP_DECRYPT);
211
1
}
212
213
void crypt_cipher_destroy_kernel(struct crypt_cipher_kernel *ctx)
214
1
{
215
1
  if (ctx->tfmfd >= 0)
216
1
    close(ctx->tfmfd);
217
1
  if (ctx->opfd >= 0)
218
1
    close(ctx->opfd);
219
220
1
  ctx->tfmfd = -1;
221
1
  ctx->opfd = -1;
222
1
}
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