Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sbin/decryptcore/decryptcore.c
39475 views
1
/*-
2
* Copyright (c) 2016 Konrad Witaszczyk <[email protected]>
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/types.h>
28
#include <sys/capsicum.h>
29
#include <sys/endian.h>
30
#include <sys/kerneldump.h>
31
#include <sys/wait.h>
32
33
#include <ctype.h>
34
#include <capsicum_helpers.h>
35
#include <fcntl.h>
36
#include <stdbool.h>
37
#include <stdlib.h>
38
#include <string.h>
39
#include <unistd.h>
40
41
#include <openssl/err.h>
42
#include <openssl/evp.h>
43
#include <openssl/pem.h>
44
#include <openssl/rsa.h>
45
#include <openssl/engine.h>
46
47
#include "pjdlog.h"
48
49
#define DECRYPTCORE_CRASHDIR "/var/crash"
50
51
static void
52
usage(void)
53
{
54
55
pjdlog_exitx(1,
56
"usage: decryptcore [-fLv] -p privatekeyfile -k keyfile -e encryptedcore -c core\n"
57
" decryptcore [-fLv] [-d crashdir] -p privatekeyfile -n dumpnr");
58
}
59
60
static int
61
wait_for_process(pid_t pid)
62
{
63
int status;
64
65
if (waitpid(pid, &status, WUNTRACED | WEXITED) == -1) {
66
pjdlog_errno(LOG_ERR, "Unable to wait for a child process");
67
return (1);
68
}
69
70
if (WIFEXITED(status))
71
return (WEXITSTATUS(status));
72
73
return (1);
74
}
75
76
static struct kerneldumpkey *
77
read_key(int kfd)
78
{
79
struct kerneldumpkey *kdk;
80
ssize_t size;
81
size_t kdksize;
82
83
PJDLOG_ASSERT(kfd >= 0);
84
85
kdksize = sizeof(*kdk);
86
kdk = calloc(1, kdksize);
87
if (kdk == NULL) {
88
pjdlog_errno(LOG_ERR, "Unable to allocate kernel dump key");
89
goto failed;
90
}
91
92
size = read(kfd, kdk, kdksize);
93
if (size == (ssize_t)kdksize) {
94
kdk->kdk_encryptedkeysize = dtoh32(kdk->kdk_encryptedkeysize);
95
kdksize += (size_t)kdk->kdk_encryptedkeysize;
96
kdk = realloc(kdk, kdksize);
97
if (kdk == NULL) {
98
pjdlog_errno(LOG_ERR, "Unable to reallocate kernel dump key");
99
goto failed;
100
}
101
size += read(kfd, &kdk->kdk_encryptedkey,
102
kdk->kdk_encryptedkeysize);
103
}
104
if (size != (ssize_t)kdksize) {
105
pjdlog_errno(LOG_ERR, "Unable to read key");
106
goto failed;
107
}
108
109
return (kdk);
110
failed:
111
free(kdk);
112
return (NULL);
113
}
114
115
static bool
116
decrypt(int ofd, const char *privkeyfile, const char *keyfile,
117
const char *input)
118
{
119
uint8_t buf[KERNELDUMP_BUFFER_SIZE], key[KERNELDUMP_KEY_MAX_SIZE],
120
chachaiv[4 * 4];
121
EVP_CIPHER_CTX *ctx;
122
const EVP_CIPHER *cipher;
123
FILE *fp;
124
struct kerneldumpkey *kdk;
125
RSA *privkey;
126
int ifd, kfd, olen, privkeysize;
127
ssize_t bytes;
128
pid_t pid;
129
130
PJDLOG_ASSERT(ofd >= 0);
131
PJDLOG_ASSERT(privkeyfile != NULL);
132
PJDLOG_ASSERT(keyfile != NULL);
133
PJDLOG_ASSERT(input != NULL);
134
135
ctx = NULL;
136
privkey = NULL;
137
138
/*
139
* Decrypt a core dump in a child process so we can unlink a partially
140
* decrypted core if the child process fails.
141
*/
142
pid = fork();
143
if (pid == -1) {
144
pjdlog_errno(LOG_ERR, "Unable to create child process");
145
close(ofd);
146
return (false);
147
}
148
149
if (pid > 0) {
150
close(ofd);
151
return (wait_for_process(pid) == 0);
152
}
153
154
kfd = open(keyfile, O_RDONLY);
155
if (kfd == -1) {
156
pjdlog_errno(LOG_ERR, "Unable to open %s", keyfile);
157
goto failed;
158
}
159
ifd = open(input, O_RDONLY);
160
if (ifd == -1) {
161
pjdlog_errno(LOG_ERR, "Unable to open %s", input);
162
goto failed;
163
}
164
fp = fopen(privkeyfile, "r");
165
if (fp == NULL) {
166
pjdlog_errno(LOG_ERR, "Unable to open %s", privkeyfile);
167
goto failed;
168
}
169
170
/*
171
* Obsolescent OpenSSL only knows about /dev/random, and needs to
172
* pre-seed before entering cap mode. For whatever reason,
173
* RSA_pub_encrypt uses the internal PRNG.
174
*/
175
#if OPENSSL_VERSION_NUMBER < 0x10100000L
176
{
177
unsigned char c[1];
178
RAND_bytes(c, 1);
179
}
180
ERR_load_crypto_strings();
181
#else
182
OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
183
#endif
184
185
caph_cache_catpages();
186
if (caph_enter() < 0) {
187
pjdlog_errno(LOG_ERR, "Unable to enter capability mode");
188
goto failed;
189
}
190
191
privkey = RSA_new();
192
if (privkey == NULL) {
193
pjdlog_error("Unable to allocate an RSA structure: %s",
194
ERR_error_string(ERR_get_error(), NULL));
195
goto failed;
196
}
197
ctx = EVP_CIPHER_CTX_new();
198
if (ctx == NULL)
199
goto failed;
200
201
kdk = read_key(kfd);
202
close(kfd);
203
if (kdk == NULL)
204
goto failed;
205
206
privkey = PEM_read_RSAPrivateKey(fp, &privkey, NULL, NULL);
207
fclose(fp);
208
if (privkey == NULL) {
209
pjdlog_error("Unable to read data from %s.", privkeyfile);
210
goto failed;
211
}
212
213
privkeysize = RSA_size(privkey);
214
if (privkeysize != (int)kdk->kdk_encryptedkeysize) {
215
pjdlog_error("RSA modulus size mismatch: equals %db and should be %ub.",
216
8 * privkeysize, 8 * kdk->kdk_encryptedkeysize);
217
goto failed;
218
}
219
220
switch (kdk->kdk_encryption) {
221
case KERNELDUMP_ENC_AES_256_CBC:
222
cipher = EVP_aes_256_cbc();
223
break;
224
case KERNELDUMP_ENC_CHACHA20:
225
cipher = EVP_chacha20();
226
break;
227
default:
228
pjdlog_error("Invalid encryption algorithm.");
229
goto failed;
230
}
231
232
if (RSA_private_decrypt(kdk->kdk_encryptedkeysize,
233
kdk->kdk_encryptedkey, key, privkey,
234
RSA_PKCS1_OAEP_PADDING) != sizeof(key) &&
235
/* Fallback to deprecated, formerly-used PKCS 1.5 padding. */
236
RSA_private_decrypt(kdk->kdk_encryptedkeysize,
237
kdk->kdk_encryptedkey, key, privkey,
238
RSA_PKCS1_PADDING) != sizeof(key)) {
239
pjdlog_error("Unable to decrypt key: %s",
240
ERR_error_string(ERR_get_error(), NULL));
241
goto failed;
242
}
243
RSA_free(privkey);
244
privkey = NULL;
245
246
if (kdk->kdk_encryption == KERNELDUMP_ENC_CHACHA20) {
247
/*
248
* OpenSSL treats the IV as 4 little-endian 32 bit integers.
249
*
250
* The first two represent a 64-bit counter, where the low half
251
* is the first 32-bit word.
252
*
253
* Start at counter block zero...
254
*/
255
memset(chachaiv, 0, 4 * 2);
256
/*
257
* And use the IV specified by the dump.
258
*/
259
memcpy(&chachaiv[4 * 2], kdk->kdk_iv, 4 * 2);
260
EVP_DecryptInit_ex(ctx, cipher, NULL, key, chachaiv);
261
} else
262
EVP_DecryptInit_ex(ctx, cipher, NULL, key, kdk->kdk_iv);
263
EVP_CIPHER_CTX_set_padding(ctx, 0);
264
265
explicit_bzero(key, sizeof(key));
266
267
do {
268
bytes = read(ifd, buf, sizeof(buf));
269
if (bytes < 0) {
270
pjdlog_errno(LOG_ERR, "Unable to read data from %s",
271
input);
272
goto failed;
273
}
274
275
if (bytes > 0) {
276
if (EVP_DecryptUpdate(ctx, buf, &olen, buf,
277
bytes) == 0) {
278
pjdlog_error("Unable to decrypt core.");
279
goto failed;
280
}
281
} else {
282
if (EVP_DecryptFinal_ex(ctx, buf, &olen) == 0) {
283
pjdlog_error("Unable to decrypt core.");
284
goto failed;
285
}
286
}
287
288
if (olen > 0 && write(ofd, buf, olen) != olen) {
289
pjdlog_errno(LOG_ERR, "Unable to write core");
290
goto failed;
291
}
292
} while (bytes > 0);
293
294
explicit_bzero(buf, sizeof(buf));
295
EVP_CIPHER_CTX_free(ctx);
296
exit(0);
297
failed:
298
explicit_bzero(key, sizeof(key));
299
explicit_bzero(buf, sizeof(buf));
300
RSA_free(privkey);
301
if (ctx != NULL)
302
EVP_CIPHER_CTX_free(ctx);
303
exit(1);
304
}
305
306
int
307
main(int argc, char **argv)
308
{
309
char core[PATH_MAX], encryptedcore[PATH_MAX], keyfile[PATH_MAX];
310
const char *crashdir, *dumpnr, *privatekey;
311
int ch, debug, error, ofd;
312
size_t ii;
313
bool force, usesyslog;
314
315
error = 1;
316
317
pjdlog_init(PJDLOG_MODE_STD);
318
pjdlog_prefix_set("(decryptcore) ");
319
320
debug = 0;
321
*core = '\0';
322
crashdir = NULL;
323
dumpnr = NULL;
324
*encryptedcore = '\0';
325
force = false;
326
*keyfile = '\0';
327
privatekey = NULL;
328
usesyslog = false;
329
while ((ch = getopt(argc, argv, "Lc:d:e:fk:n:p:v")) != -1) {
330
switch (ch) {
331
case 'L':
332
usesyslog = true;
333
break;
334
case 'c':
335
if (strlcpy(core, optarg, sizeof(core)) >= sizeof(core))
336
pjdlog_exitx(1, "Core file path is too long.");
337
break;
338
case 'd':
339
crashdir = optarg;
340
break;
341
case 'e':
342
if (strlcpy(encryptedcore, optarg,
343
sizeof(encryptedcore)) >= sizeof(encryptedcore)) {
344
pjdlog_exitx(1, "Encrypted core file path is too long.");
345
}
346
break;
347
case 'f':
348
force = true;
349
break;
350
case 'k':
351
if (strlcpy(keyfile, optarg, sizeof(keyfile)) >=
352
sizeof(keyfile)) {
353
pjdlog_exitx(1, "Key file path is too long.");
354
}
355
break;
356
case 'n':
357
dumpnr = optarg;
358
break;
359
case 'p':
360
privatekey = optarg;
361
break;
362
case 'v':
363
debug++;
364
break;
365
default:
366
usage();
367
}
368
}
369
argc -= optind;
370
argv += optind;
371
372
if (argc != 0)
373
usage();
374
375
/* Verify mutually exclusive options. */
376
if ((crashdir != NULL || dumpnr != NULL) &&
377
(*keyfile != '\0' || *encryptedcore != '\0' || *core != '\0')) {
378
usage();
379
}
380
381
/*
382
* Set key, encryptedcore and core file names using crashdir and dumpnr.
383
*/
384
if (dumpnr != NULL) {
385
for (ii = 0; ii < strnlen(dumpnr, PATH_MAX); ii++) {
386
if (isdigit((int)dumpnr[ii]) == 0)
387
usage();
388
}
389
390
if (crashdir == NULL)
391
crashdir = DECRYPTCORE_CRASHDIR;
392
PJDLOG_VERIFY(snprintf(keyfile, sizeof(keyfile),
393
"%s/key.%s", crashdir, dumpnr) > 0);
394
PJDLOG_VERIFY(snprintf(core, sizeof(core),
395
"%s/vmcore.%s", crashdir, dumpnr) > 0);
396
PJDLOG_VERIFY(snprintf(encryptedcore, sizeof(encryptedcore),
397
"%s/vmcore_encrypted.%s", crashdir, dumpnr) > 0);
398
}
399
400
if (privatekey == NULL || *keyfile == '\0' || *encryptedcore == '\0' ||
401
*core == '\0') {
402
usage();
403
}
404
405
if (usesyslog)
406
pjdlog_mode_set(PJDLOG_MODE_SYSLOG);
407
pjdlog_debug_set(debug);
408
409
if (force && unlink(core) == -1 && errno != ENOENT) {
410
pjdlog_errno(LOG_ERR, "Unable to remove old core");
411
goto out;
412
}
413
ofd = open(core, O_WRONLY | O_CREAT | O_EXCL, 0600);
414
if (ofd == -1) {
415
pjdlog_errno(LOG_ERR, "Unable to open %s", core);
416
goto out;
417
}
418
419
if (!decrypt(ofd, privatekey, keyfile, encryptedcore)) {
420
if (unlink(core) == -1 && errno != ENOENT)
421
pjdlog_errno(LOG_ERR, "Unable to remove core");
422
goto out;
423
}
424
425
error = 0;
426
out:
427
pjdlog_fini();
428
exit(error);
429
}
430
431