Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2010, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : * * Redistributions of source code must retain the above copyright
9 : : * notice, this list of conditions and the following disclaimer.
10 : : * * 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 : : * * Neither the name of XenSource Inc. nor the names of its contributors
14 : : * may be used to endorse or promote products derived from this software
15 : : * without specific prior written permission.
16 : : *
17 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 : : */
29 : :
30 : : #include <errno.h>
31 : : #include <fcntl.h>
32 : : #include <stdio.h>
33 : : #include <stdlib.h>
34 : : #include <unistd.h>
35 : : #include <syslog.h>
36 : : #include <sys/types.h>
37 : : #include <sys/stat.h>
38 : : #include <dlfcn.h>
39 : :
40 : : #include "libvhd.h"
41 : :
42 : : #define MAX_KEY_SIZE 512
43 : : int CRYPTO_SUPPORTED_KEYSIZE[] = { 512, 256, -1};
44 : :
45 : : #define ERR(_f, _a...) \
46 : : do { \
47 : : syslog(LOG_INFO, "%s: " _f, __func__, ##_a); \
48 : : fprintf(stderr, "%s: " _f, __func__, ##_a); \
49 : : } while (0)
50 : :
51 : :
52 : : typedef int (*vhd_calculate_keyhash)(struct vhd_keyhash *keyhash,
53 : : const uint8_t *key, size_t key_byte);
54 : : static vhd_calculate_keyhash pvhd_calculate_keyhash;
55 : : static void *crypto_handle;
56 : :
57 : : static int
58 : 0 : __load_crypto()
59 : : {
60 : 0 : crypto_handle = dlopen(LIBBLOCKCRYPTO_NAME, RTLD_LAZY);
61 [ # # ]: 0 : if (crypto_handle == NULL) {
62 : 0 : ERR("Failed to load crypto support library\n");
63 : 0 : return -EINVAL;
64 : : }
65 : 0 : pvhd_calculate_keyhash = (int (*)(struct vhd_keyhash *,
66 : : const uint8_t *, size_t))
67 : 0 : dlsym(crypto_handle, "vhd_calculate_keyhash");
68 [ # # ]: 0 : if (!pvhd_calculate_keyhash) {
69 : 0 : ERR("Calculate keyhash function not loaded\n");
70 : 0 : return -EINVAL;
71 : : }
72 : : return 0;
73 : : }
74 : :
75 : : char *
76 : 0 : vhd_util_get_vhd_basename(vhd_context_t *vhd)
77 : : {
78 : : char *basename, *ext;
79 : :
80 : : /* strip path */
81 : 0 : basename = strrchr(vhd->file, '/');
82 [ # # ]: 0 : if (basename == NULL)
83 : : basename = vhd->file;
84 : : else
85 : 0 : basename++;
86 : :
87 : 0 : basename = strdup(basename);
88 [ # # ]: 0 : if (!basename)
89 : : return NULL;
90 : :
91 : : /* cut off .vhd extension */
92 : 0 : ext = strstr(basename, ".vhd");
93 [ # # ]: 0 : if (ext)
94 : 0 : basename[ext - basename] = 0;
95 : 0 : return basename;
96 : : }
97 : :
98 : : static int
99 : 0 : vhd_util_validate_keypath(vhd_context_t *vhd, const char *keypath, size_t *key_bits)
100 : : {
101 : : int err, i;
102 : 0 : char expected_basename[256] = { 0 };
103 : 0 : char *vhd_basename = NULL;
104 : 0 : const char *keypath_basename = NULL;
105 : :
106 : 0 : err = -1;
107 : :
108 : 0 : vhd_basename = vhd_util_get_vhd_basename(vhd);
109 [ # # ]: 0 : if (!vhd_basename) {
110 : : err = -ENOMEM;
111 : : goto out;
112 : : }
113 : :
114 : 0 : keypath_basename = strrchr(keypath, '/');
115 [ # # ]: 0 : if (keypath_basename)
116 : 0 : keypath_basename += 1;
117 : : else
118 : : keypath_basename = keypath;
119 : :
120 : : /* Verify the filename */
121 : : /* <path>/<VHD-uuid>,aes-xts-plain,<key-length>.key */
122 [ # # ]: 0 : for (i = 0; CRYPTO_SUPPORTED_KEYSIZE[i] > 0; ++i) {
123 : 0 : snprintf(expected_basename, sizeof(expected_basename),
124 : : "%s,aes-xts-plain,%d.key",
125 : : vhd_basename, CRYPTO_SUPPORTED_KEYSIZE[i]);
126 [ # # ]: 0 : if (!strncmp(keypath_basename, expected_basename, strlen(expected_basename))) {
127 : 0 : *key_bits = CRYPTO_SUPPORTED_KEYSIZE[i];
128 : 0 : err = 0;
129 : 0 : goto out;
130 : : }
131 : : }
132 : : out:
133 : 0 : free(vhd_basename);
134 : 0 : return err;
135 : : }
136 : :
137 : : static int
138 : 0 : vhd_util_read_key(const char *keypath, uint8_t *key,
139 : : size_t *key_bytes)
140 : : {
141 : : int fd, err;
142 : : ssize_t size;
143 : : struct stat sb;
144 : :
145 : 0 : fd = open(keypath, O_RDONLY);
146 [ # # ]: 0 : if (fd == -1) {
147 : 0 : ERR("failed to open %s: %d\n", keypath, errno);
148 : 0 : err = -errno;
149 : 0 : goto out;
150 : : }
151 : :
152 : 0 : err = fstat(fd, &sb);
153 [ # # ]: 0 : if (err) {
154 : 0 : ERR("failed to stat %s: %d\n", keypath, errno);
155 : 0 : err = -errno;
156 : 0 : goto out;
157 : : }
158 : :
159 : 0 : size = read(fd, key, *key_bytes);
160 [ # # ]: 0 : if (size == -1) {
161 : 0 : ERR("failed to read key: %d\n", errno);
162 : 0 : err = -errno;
163 : 0 : goto out;
164 : : }
165 : 0 : *key_bytes = size;
166 : :
167 [ # # ]: 0 : if (size != sb.st_size) {
168 : 0 : ERR("short read of key\n");
169 : : err = -EIO;
170 : : goto out;
171 : : }
172 : :
173 : 0 : ERR("using keyfile %s, Size (bytes) %zu\n", keypath, *key_bytes);
174 : : out:
175 [ # # ]: 0 : if (fd != -1)
176 : 0 : close(fd);
177 : 0 : return err;
178 : : }
179 : :
180 : : static int
181 : 0 : vhd_util_calculate_keyhash(struct vhd_keyhash *keyhash, const char *keypath, size_t key_bytes)
182 : : {
183 : : int err;
184 : : size_t read_bytes;
185 : : uint8_t key[MAX_KEY_SIZE/8];
186 : :
187 [ # # ]: 0 : if (key_bytes == 0)
188 : 0 : read_bytes = MAX_KEY_SIZE/8;
189 : : else
190 : 0 : read_bytes = key_bytes;
191 : :
192 : 0 : err = vhd_util_read_key(keypath, key, &read_bytes);
193 [ # # ]: 0 : if (err) {
194 : 0 : ERR("failed to read key: %d\n", err);
195 : : goto out;
196 : : }
197 [ # # ][ # # ]: 0 : if (key_bytes > 0 && key_bytes != read_bytes) {
198 : 0 : ERR("incorrect key size: %zu != %zu\n", key_bytes, read_bytes);
199 : : err = -EINVAL;
200 : : goto out;
201 : : }
202 : :
203 : 0 : err = __load_crypto();
204 [ # # ]: 0 : if (err) {
205 : : /* __load_crypto already logged the failure */
206 : : goto out;
207 : : }
208 : :
209 : 0 : err = pvhd_calculate_keyhash(keyhash, key, read_bytes);
210 [ # # ]: 0 : if (err) {
211 : 0 : ERR("failed to calculate keyhash: %d\n", err);
212 : : goto out;
213 : : }
214 : :
215 : : out:
216 : : memset(key, 0, sizeof(key));
217 : 0 : return err;
218 : : }
219 : :
220 : : static int
221 : 0 : vhd_util_set_hex(uint8_t *dst, size_t size, const char *hex)
222 : : {
223 : : int i, n, err;
224 : :
225 : 0 : err = 0;
226 : :
227 : 0 : n = strlen(hex);
228 [ # # ]: 0 : if (n / 2 != size) {
229 : 0 : ERR("invalid size for hex string\n");
230 : 0 : err = -EINVAL;
231 : 0 : goto out;
232 : : }
233 : :
234 [ # # ]: 0 : for (i = 0; i < n; i++) {
235 : 0 : unsigned char c = (unsigned char)hex[i];
236 [ # # # # : 0 : switch (c) {
# ]
237 : : case 0:
238 : : break;
239 : : case '0'...'9':
240 : 0 : c -= '0';
241 : 0 : break;
242 : : case 'a' ... 'f':
243 : 0 : c = c - 'a' + 10;
244 : 0 : break;
245 : : case 'A' ... 'F':
246 : 0 : c = c - 'A' + 10;
247 : 0 : break;
248 : : default:
249 : 0 : ERR("invalid hex digit\n");
250 : 0 : err = -EINVAL;
251 : 0 : goto out;
252 : : }
253 : :
254 [ # # ]: 0 : if (i & 1)
255 : 0 : dst[i / 2] |= c;
256 : : else
257 : 0 : dst[i / 2] = (c << 4);
258 : : }
259 : :
260 : : out:
261 : 0 : return err;
262 : : }
263 : :
264 : : static int
265 : 0 : vhd_util_set_keyhash(vhd_context_t *vhd, struct vhd_keyhash *keyhash, const char *keypath,
266 : : const char *hash, const char *nonce)
267 : : {
268 : : int err;
269 : : size_t key_bits;
270 : :
271 : : memset(keyhash, 0, sizeof(*keyhash));
272 : :
273 [ # # ]: 0 : if (nonce) {
274 : 0 : err = vhd_util_set_hex(keyhash->nonce,
275 : : sizeof(keyhash->nonce), nonce);
276 [ # # ]: 0 : if (err)
277 : : goto out;
278 : : }
279 : :
280 [ # # ]: 0 : if (hash) {
281 : 0 : err = vhd_util_set_hex(keyhash->hash,
282 : : sizeof(keyhash->hash), hash);
283 [ # # ]: 0 : if (err)
284 : : goto out;
285 : : } else {
286 [ # # ]: 0 : if (vhd) {
287 : 0 : err = vhd_util_validate_keypath(vhd, keypath, &key_bits);
288 [ # # ]: 0 : if (err) {
289 : 0 : ERR("Invalid key name %s\n", keypath);
290 : : goto out;
291 : : }
292 : : } else
293 : 0 : key_bits = 0;
294 : 0 : err = vhd_util_calculate_keyhash(keyhash, keypath, key_bits/8);
295 [ # # ]: 0 : if (err) {
296 : 0 : ERR("failed to calculate keyhash: %d\n", err);
297 : : goto out;
298 : : }
299 : : }
300 : :
301 : 0 : keyhash->cookie = 1;
302 : :
303 : : out:
304 : 0 : return err;
305 : : }
306 : :
307 : : static int
308 : 0 : vhd_util_set_key(vhd_context_t *vhd, const char *keypath,
309 : : const char *hash, const char *nonce)
310 : : {
311 : : int err;
312 : : struct vhd_keyhash keyhash;
313 : : uint32_t i, used;
314 : :
315 : : memset(&keyhash, 0, sizeof(keyhash));
316 : :
317 [ # # ]: 0 : if (vhd->footer.type == HD_TYPE_FIXED) {
318 : 0 : ERR("can't save key hashes for fixed vhds\n");
319 : : err = -EINVAL;
320 : : goto out;
321 : : }
322 : :
323 [ # # ]: 0 : if (keypath && hash) {
324 : 0 : ERR("can't provide both keyhash and keypath\n");
325 : : err = -EINVAL;
326 : : goto out;
327 : : }
328 : :
329 : 0 : err = vhd_get_bat(vhd);
330 [ # # ]: 0 : if (err) {
331 : 0 : ERR("error reading bat: %d\n", err);
332 : : goto out;
333 : : }
334 [ # # ]: 0 : for (i = 0, used = 0; i < vhd->bat.entries; i++)
335 [ # # ]: 0 : if (vhd->bat.bat[i] != DD_BLK_UNUSED)
336 : 0 : used++;
337 [ # # ]: 0 : if (used != 0) {
338 : 0 : ERR("can't save key hashes for non-empty vhds\n");
339 : : err = -EINVAL;
340 : : goto out;
341 : : }
342 : :
343 : :
344 : 0 : err = vhd_util_set_keyhash(vhd, &keyhash, keypath, hash, nonce);
345 [ # # ]: 0 : if (err)
346 : : goto out;
347 : :
348 : 0 : err = vhd_set_keyhash(vhd, &keyhash);
349 [ # # ]: 0 : if (err) {
350 : 0 : ERR("failed to set keyhash: %d\n", err);
351 : : goto out;
352 : : }
353 : :
354 : : out:
355 : 0 : return err;
356 : : }
357 : :
358 : : static int
359 : 0 : vhd_util_check_key(vhd_context_t *vhd, const char *keypath)
360 : : {
361 : : int err;
362 : : struct vhd_keyhash vhdhash, keyhash;
363 : : size_t key_bits;
364 : :
365 : 0 : err = vhd_get_keyhash(vhd, &vhdhash);
366 [ # # ]: 0 : if (err) {
367 : 0 : ERR("failed to read keyhash: %d\n", err);
368 : : goto out;
369 : : }
370 : :
371 [ # # ]: 0 : if (!vhdhash.cookie) {
372 : 0 : ERR("this vhd has no keyhash\n");
373 : : err = -EINVAL;
374 : : goto out;
375 : : }
376 : :
377 : 0 : err = vhd_util_validate_keypath(vhd, keypath, &key_bits);
378 [ # # ]: 0 : if (err) {
379 : 0 : ERR("Invalid key name %s\n", keypath);
380 : : goto out;
381 : : }
382 : :
383 : : memcpy(keyhash.nonce, vhdhash.nonce, sizeof(keyhash.nonce));
384 : 0 : err = vhd_util_calculate_keyhash(&keyhash, keypath, key_bits/8);
385 [ # # ]: 0 : if (err) {
386 : 0 : ERR("failed to calculate keyhash: %d\n", err);
387 : : goto out;
388 : : }
389 : :
390 [ # # ]: 0 : if (memcmp(keyhash.hash, vhdhash.hash, sizeof(keyhash.hash))) {
391 : 0 : ERR("vhd hash doesn't match key hash\n");
392 : : err = -EINVAL;
393 : : goto out;
394 : : }
395 : :
396 : : out:
397 : 0 : return err;
398 : : }
399 : :
400 : : int
401 : 0 : vhd_util_key(int argc, char **argv)
402 : : {
403 : : vhd_context_t vhd;
404 : : const char *name, *nonce, *keypath, *keyhash;
405 : : int err, c, set, check, print, flags, calc;
406 : :
407 : 0 : err = -EINVAL;
408 : 0 : set = 0;
409 : 0 : check = 0;
410 : 0 : print = 0;
411 : 0 : calc = 0;
412 : 0 : name = NULL;
413 : 0 : nonce = NULL;
414 : 0 : keypath = NULL;
415 : 0 : keyhash = NULL;
416 : :
417 [ # # ]: 0 : if (!argc || !argv)
418 : : goto usage;
419 : :
420 : 0 : optind = 0;
421 [ # # ]: 0 : while ((c = getopt(argc, argv, "n:k:N:H:scCph")) != -1) {
422 [ # # # # : 0 : switch (c) {
# # # # #
# ]
423 : : case 'n':
424 : 0 : name = optarg;
425 : 0 : break;
426 : : case 'k':
427 : 0 : keypath = optarg;
428 : 0 : break;
429 : : case 'N':
430 : 0 : nonce = optarg;
431 : 0 : break;
432 : : case 'H':
433 : 0 : keyhash = optarg;
434 : 0 : break;
435 : : case 's':
436 : : set = 1;
437 : : break;
438 : : case 'c':
439 : 0 : check = 1;
440 : 0 : break;
441 : : case 'C':
442 : 0 : calc = 1;
443 : 0 : break;
444 : : case 'p':
445 : 0 : print = 1;
446 : 0 : break;
447 : : case 'h':
448 : 0 : err = 0;
449 : : default:
450 : : goto usage;
451 : : }
452 : : }
453 : :
454 [ # # ]: 0 : if (optind != argc)
455 : : goto usage;
456 : :
457 [ # # ]: 0 : if (calc) {
458 : : int i;
459 : : struct vhd_keyhash keyhash;
460 : 0 : err = vhd_util_set_keyhash(NULL, &keyhash, keypath, NULL, nonce);
461 [ # # ]: 0 : if (err) {
462 : 0 : ERR("calculating keyhash failed: %d\n", err);
463 : 0 : goto out;
464 : : }
465 : :
466 [ # # ]: 0 : for (i = 0; i < sizeof(keyhash.hash); i++)
467 : 0 : printf("%02x", keyhash.hash[i]);
468 : :
469 : : printf("\n");
470 : : goto out;
471 : : }
472 : :
473 [ # # ]: 0 : if (!name)
474 : : goto usage;
475 : :
476 [ # # ]: 0 : if (set) {
477 [ # # ]: 0 : if (check)
478 : : goto usage;
479 : :
480 [ # # ]: 0 : if (!(!!keypath ^ !!keyhash))
481 : : goto usage;
482 [ # # ]: 0 : } else if (check) {
483 [ # # ]: 0 : if (!keypath)
484 : : goto usage;
485 : :
486 [ # # ]: 0 : if (nonce || keyhash)
487 : : goto usage;
488 [ # # ]: 0 : } else if (!print) {
489 : : goto usage;
490 : : }
491 : :
492 [ # # ]: 0 : flags = (set ? VHD_OPEN_RDWR : VHD_OPEN_RDONLY);
493 : 0 : err = vhd_open(&vhd, name, flags);
494 [ # # ]: 0 : if (err) {
495 : 0 : fprintf(stderr, "failed to open %s: %d\n", name, err);
496 : : goto out;
497 : : }
498 : :
499 [ # # ]: 0 : if (set) {
500 : 0 : err = vhd_util_set_key(&vhd, keypath, keyhash, nonce);
501 [ # # ]: 0 : if (err)
502 : 0 : fprintf(stderr, "setting key failed: %d\n", err);
503 [ # # ]: 0 : } else if (check) {
504 : 0 : err = vhd_util_check_key(&vhd, keypath);
505 [ # # ]: 0 : if (err)
506 : 0 : fprintf(stderr, "key check failed: %d\n", err);
507 : : }
508 : :
509 [ # # ]: 0 : if (print) {
510 : : struct vhd_keyhash keyhash;
511 : :
512 : 0 : err = vhd_get_keyhash(&vhd, &keyhash);
513 [ # # ]: 0 : if (err) {
514 : 0 : fprintf(stderr, "failed to read keyhash: %d\n", err);
515 : : } else {
516 [ # # ]: 0 : if (keyhash.cookie != 1)
517 : : printf("none\n");
518 : : else {
519 : : int i;
520 : :
521 [ # # ]: 0 : for (i = 0; i < sizeof(keyhash.nonce); i++)
522 : 0 : printf("%02x", keyhash.nonce[i]);
523 : :
524 : : printf(" ");
525 : :
526 [ # # ]: 0 : for (i = 0; i < sizeof(keyhash.hash); i++)
527 : 0 : printf("%02x", keyhash.hash[i]);
528 : :
529 : : printf("\n");
530 : : }
531 : : }
532 : : }
533 : :
534 : 0 : vhd_close(&vhd);
535 : :
536 : : out:
537 : 0 : return err;
538 : :
539 : : usage:
540 : 0 : fprintf(stderr,
541 : : "usage:\n"
542 : : "-C -k KEYPATH [-N NONCE]: calculate keyhash for KEYPATH\n"
543 : : "-s -n NAME <-k KEYPATH> | -H HASH> [-N NONCE]: set keyhash for NAME\n"
544 : : "-c -n NAME <-k KEYPATH>: check keyhash for NAME\n"
545 : : "-p -n NAME: print keyhash for NAME\n"
546 : : "-h help\n");
547 : : return err;
548 : : }
|