Coverage Report

Created: 2025-11-07 06:58

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
0
{
43
0
  void *optval = NULL;
44
45
0
  if (!ctx)
46
0
    return -EINVAL;
47
48
0
  ctx->opfd = -1;
49
0
  ctx->tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
50
0
  if (ctx->tfmfd < 0) {
51
0
    crypt_cipher_destroy_kernel(ctx);
52
0
    return -ENOTSUP;
53
0
  }
54
55
0
  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
0
  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
0
  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
0
  ctx->opfd = accept(ctx->tfmfd, NULL, 0);
71
0
  if (ctx->opfd < 0) {
72
0
    crypt_cipher_destroy_kernel(ctx);
73
0
    return -EINVAL;
74
0
  }
75
76
0
  return 0;
77
0
}
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
0
{
82
0
  struct sockaddr_alg sa = {
83
0
    .salg_family = AF_ALG,
84
0
    .salg_type = "skcipher",
85
0
  };
86
0
  int r;
87
88
0
  if (!strcmp(name, "cipher_null"))
89
0
    key_length = 0;
90
91
0
  if (!strncmp(name, "capi:", 5))
92
0
    strncpy((char *)sa.salg_name, &name[5], sizeof(sa.salg_name) - 1);
93
0
  else {
94
0
    r = snprintf((char *)sa.salg_name, sizeof(sa.salg_name), "%s(%s)", mode, name);
95
0
    if (r < 0 || (size_t)r >= sizeof(sa.salg_name))
96
0
      return -EINVAL;
97
0
  }
98
99
0
  return _crypt_cipher_init(ctx, key, key_length, 0, &sa);
100
0
}
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
0
{
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
0
  return CMSG_NXTHDR(mhdr, cmsg);
113
0
#endif
114
0
}
115
116
/* The in/out should be aligned to page boundary */
117
/* coverity[ -taint_source : arg-3 ] */
118
static int _crypt_cipher_crypt(struct crypt_cipher_kernel *ctx,
119
             const char *in, size_t in_length,
120
             char *out, size_t out_length,
121
             const char *iv, size_t iv_length,
122
             uint32_t direction)
123
0
{
124
0
  int r = 0;
125
0
  ssize_t len;
126
0
  struct af_alg_iv *alg_iv;
127
0
  struct cmsghdr *header;
128
0
  uint32_t *type;
129
0
  struct iovec iov = {
130
0
    .iov_base = (void*)(uintptr_t)in,
131
0
    .iov_len = in_length,
132
0
  };
133
0
  int iv_msg_size = iv ? CMSG_SPACE(sizeof(*alg_iv) + iv_length) : 0;
134
0
  char buffer[CMSG_SPACE(sizeof(*type)) + iv_msg_size];
135
0
  struct msghdr msg = {
136
0
    .msg_control = buffer,
137
0
    .msg_controllen = sizeof(buffer),
138
0
    .msg_iov = &iov,
139
0
    .msg_iovlen = 1,
140
0
  };
141
142
0
  if (!in || !out || !in_length)
143
0
    return -EINVAL;
144
145
0
  if ((!iv && iv_length) || (iv && !iv_length))
146
0
    return -EINVAL;
147
148
0
  memset(buffer, 0, sizeof(buffer));
149
150
  /* Set encrypt/decrypt operation */
151
0
  header = CMSG_FIRSTHDR(&msg);
152
0
  if (!header)
153
0
    return -EINVAL;
154
155
0
  header->cmsg_level = SOL_ALG;
156
0
  header->cmsg_type = ALG_SET_OP;
157
0
  header->cmsg_len = CMSG_LEN(sizeof(*type));
158
0
  type = (void*)CMSG_DATA(header);
159
0
  *type = direction;
160
161
  /* Set IV */
162
0
  if (iv) {
163
0
    header = _CMSG_NXTHDR(&msg, header);
164
0
    if (!header)
165
0
      return -EINVAL;
166
167
0
    header->cmsg_level = SOL_ALG;
168
0
    header->cmsg_type = ALG_SET_IV;
169
0
    header->cmsg_len = iv_msg_size;
170
0
    alg_iv = (void*)CMSG_DATA(header);
171
0
    alg_iv->ivlen = iv_length;
172
0
    crypt_backend_memcpy(alg_iv->iv, iv, iv_length);
173
0
  }
174
175
0
  len = sendmsg(ctx->opfd, &msg, 0);
176
0
  if (len != (ssize_t)(in_length))
177
0
    r = -EIO;
178
0
  else {
179
0
    len = read(ctx->opfd, out, out_length);
180
0
    if (len != (ssize_t)out_length)
181
0
      r = -EIO;
182
0
  }
183
184
0
  crypt_backend_memzero(buffer, sizeof(buffer));
185
0
  return r;
186
0
}
187
188
int crypt_cipher_encrypt_kernel(struct crypt_cipher_kernel *ctx,
189
        const char *in, char *out, size_t length,
190
        const char *iv, size_t iv_length)
191
0
{
192
0
  return _crypt_cipher_crypt(ctx, in, length, out, length,
193
0
           iv, iv_length, ALG_OP_ENCRYPT);
194
0
}
195
196
int crypt_cipher_decrypt_kernel(struct crypt_cipher_kernel *ctx,
197
        const char *in, char *out, size_t length,
198
        const char *iv, size_t iv_length)
199
0
{
200
0
  return _crypt_cipher_crypt(ctx, in, length, out, length,
201
0
           iv, iv_length, ALG_OP_DECRYPT);
202
0
}
203
204
void crypt_cipher_destroy_kernel(struct crypt_cipher_kernel *ctx)
205
0
{
206
0
  if (ctx->tfmfd >= 0)
207
0
    close(ctx->tfmfd);
208
0
  if (ctx->opfd >= 0)
209
0
    close(ctx->opfd);
210
211
0
  ctx->tfmfd = -1;
212
0
  ctx->opfd = -1;
213
0
}
214
215
int crypt_cipher_check_kernel(const char *name, const char *mode,
216
            const char *integrity, size_t key_length)
217
0
{
218
0
  struct crypt_cipher_kernel c;
219
0
  char mode_name[64], tmp_salg_name[180], *cipher_iv = NULL, *key;
220
0
  const char *salg_type, *real_mode;
221
0
  bool aead;
222
0
  int r;
223
0
  struct sockaddr_alg sa = {
224
0
    .salg_family = AF_ALG,
225
0
  };
226
227
0
  aead = integrity && strcmp(integrity, "none");
228
0
  real_mode = NULL;
229
230
  /* Remove IV if present */
231
0
  if (mode) {
232
0
    strncpy(mode_name, mode, sizeof(mode_name));
233
0
    mode_name[sizeof(mode_name) - 1] = 0;
234
0
    cipher_iv = strchr(mode_name, '-');
235
0
    if (cipher_iv) {
236
0
      *cipher_iv = '\0';
237
0
      real_mode = mode_name;
238
0
    }
239
0
  }
240
241
0
  salg_type = aead ? "aead" : "skcipher";
242
0
  r = snprintf((char *)sa.salg_type, sizeof(sa.salg_type), "%s", salg_type);
243
0
  if (r < 0 || (size_t)r >= sizeof(sa.salg_name))
244
0
    return -EINVAL;
245
246
0
  memset(tmp_salg_name, 0, sizeof(tmp_salg_name));
247
248
  /* FIXME: this is duplicating a part of devmapper backend */
249
0
  if (aead) {
250
    /* In AEAD, mode parameter can be just IV like "random" */
251
0
    if (!strcmp(integrity, "poly1305"))
252
0
      r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "rfc7539(%s,%s)", name, integrity);
253
0
    else if (!real_mode)
254
0
      r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s", name);
255
0
    else if (!strcmp(real_mode, "ccm"))
256
0
      r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "rfc4309(%s(%s))", real_mode, name);
257
0
    else
258
0
      r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s(%s)", real_mode, name);
259
0
  } else {
260
0
    if (!mode)
261
0
      r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s", name);
262
0
    else
263
0
      r = snprintf(tmp_salg_name, sizeof(tmp_salg_name), "%s(%s)", real_mode ?: mode_name, name);
264
0
  }
265
266
0
  if (r < 0 || (size_t)r >= sizeof(tmp_salg_name))
267
0
    return -EINVAL;
268
269
0
  memcpy(sa.salg_name, tmp_salg_name, sizeof(sa.salg_name));
270
271
0
  key = malloc(key_length);
272
0
  if (!key)
273
0
    return -ENOMEM;
274
275
  /* We cannot use RNG yet, any key works here, tweak the first part if it is split key (XTS). */
276
0
  memset(key, 0xab, key_length);
277
0
  *key = 0xef;
278
279
0
  r = _crypt_cipher_init(&c, key, key_length, 0, &sa);
280
0
  crypt_cipher_destroy_kernel(&c);
281
0
  free(key);
282
283
0
  return r;
284
0
}
285
286
int crypt_bitlk_decrypt_key_kernel(const void *key, size_t key_length,
287
           const char *in, char *out, size_t length,
288
           const char *iv, size_t iv_length,
289
           const char *tag, size_t tag_length)
290
0
{
291
0
  struct crypt_cipher_kernel c;
292
0
  struct sockaddr_alg sa = {
293
0
    .salg_family = AF_ALG,
294
0
    .salg_type = "aead",
295
0
    .salg_name = "ccm(aes)",
296
0
  };
297
0
  int r;
298
0
  char buffer[128], ccm_iv[16];
299
300
0
  if (length + tag_length > sizeof(buffer))
301
0
    return -EINVAL;
302
303
0
  if (iv_length > sizeof(ccm_iv) - 2)
304
0
    return -EINVAL;
305
306
0
  r = _crypt_cipher_init(&c, key, key_length, tag_length, &sa);
307
0
  if (r < 0)
308
0
    return r;
309
310
0
  memcpy(buffer, in, length);
311
0
  memcpy(buffer + length, tag, tag_length);
312
313
  /* CCM IV - RFC3610 */
314
0
  memset(ccm_iv, 0, sizeof(ccm_iv));
315
0
  ccm_iv[0] = 15 - iv_length - 1;
316
0
  memcpy(ccm_iv + 1, iv, iv_length);
317
0
  memset(ccm_iv + 1 + iv_length, 0, ccm_iv[0] + 1);
318
0
  iv_length = sizeof(ccm_iv);
319
320
0
  r =  _crypt_cipher_crypt(&c, buffer, length + tag_length, out, length,
321
0
         ccm_iv, iv_length, ALG_OP_DECRYPT);
322
323
0
  crypt_cipher_destroy_kernel(&c);
324
0
  crypt_backend_memzero(buffer, sizeof(buffer));
325
326
0
  return r;
327
0
}
328
329
#else /* ENABLE_AF_ALG */
330
#pragma GCC diagnostic ignored "-Wunused-parameter"
331
332
int crypt_cipher_init_kernel(struct crypt_cipher_kernel *ctx, const char *name,
333
           const char *mode, const void *key, size_t key_length)
334
{
335
  return -ENOTSUP;
336
}
337
338
void crypt_cipher_destroy_kernel(struct crypt_cipher_kernel *ctx)
339
{
340
  return;
341
}
342
343
int crypt_cipher_encrypt_kernel(struct crypt_cipher_kernel *ctx,
344
        const char *in, char *out, size_t length,
345
        const char *iv, size_t iv_length)
346
{
347
  return -EINVAL;
348
}
349
int crypt_cipher_decrypt_kernel(struct crypt_cipher_kernel *ctx,
350
        const char *in, char *out, size_t length,
351
        const char *iv, size_t iv_length)
352
{
353
  return -EINVAL;
354
}
355
int crypt_cipher_check_kernel(const char *name, const char *mode,
356
            const char *integrity, size_t key_length)
357
{
358
  /* Cannot check, expect success. */
359
  return 0;
360
}
361
int crypt_bitlk_decrypt_key_kernel(const void *key, size_t key_length,
362
           const char *in, char *out, size_t length,
363
           const char *iv, size_t iv_length,
364
           const char *tag, size_t tag_length)
365
{
366
  return -ENOTSUP;
367
}
368
#endif