Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2018, 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 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #ifdef HAVE_CONFIG_H
32 : : #include "config.h"
33 : : #endif
34 : :
35 : : /* Would be nice to find a common place for this, also defined in tap-ctl.c */
36 : : #define MAX_AES_XTS_PLAIN_KEYSIZE 1024
37 : :
38 : : #include <stdio.h>
39 : : #include <errno.h>
40 : : #include <stdlib.h>
41 : : #include <unistd.h>
42 : : #include <sys/types.h>
43 : : #include <sys/stat.h>
44 : : #include <fcntl.h>
45 : : #include <dlfcn.h>
46 : :
47 : : #include "libvhd.h"
48 : :
49 : : typedef int (*vhd_calculate_keyhash)(struct vhd_keyhash *keyhash,
50 : : const uint8_t *key, size_t key_byte);
51 : : typedef int (*vhd_open_crypto)(vhd_context_t *, const uint8_t *, size_t,
52 : : const char *);
53 : : typedef int (*vhd_crypto_encrypt_block)(vhd_context_t *, uint64_t,
54 : : uint8_t *, uint8_t *,
55 : : unsigned int);
56 : : static vhd_calculate_keyhash pvhd_calculate_keyhash;
57 : : static vhd_open_crypto pvhd_open_crypto;
58 : : static vhd_crypto_encrypt_block pvhd_crypto_encrypt_block;
59 : : static void *crypto_handle;
60 : :
61 : : static int
62 : 0 : __load_crypto()
63 : : {
64 : 0 : crypto_handle = dlopen(LIBBLOCKCRYPTO_NAME, RTLD_LAZY);
65 [ # # ]: 0 : if (crypto_handle == NULL) {
66 : : return -EINVAL;
67 : : }
68 : 0 : pvhd_calculate_keyhash = (int (*)(struct vhd_keyhash *,
69 : : const uint8_t *, size_t))
70 : 0 : dlsym(crypto_handle, "vhd_calculate_keyhash");
71 [ # # ]: 0 : if (!pvhd_calculate_keyhash) {
72 : : return -EINVAL;
73 : : }
74 : 0 : pvhd_open_crypto = (int (*)(vhd_context_t *, const uint8_t *, size_t,
75 : : const char *))
76 : 0 : dlsym (crypto_handle, "vhd_open_crypto");
77 [ # # ]: 0 : if (!pvhd_open_crypto) {
78 : : return -EINVAL;
79 : : }
80 : 0 : pvhd_crypto_encrypt_block = (int (*)(vhd_context_t *, uint64_t,
81 : : uint8_t *, uint8_t *,
82 : : unsigned int))
83 : 0 : dlsym (crypto_handle, "vhd_crypto_encrypt_block");
84 [ # # ]: 0 : if (!pvhd_crypto_encrypt_block) {
85 : : return -EINVAL;
86 : : }
87 : 0 : return 0;
88 : : }
89 : :
90 : : static int
91 : 0 : vhd_encrypt_copy_block(vhd_context_t *source_vhd, vhd_context_t *target_vhd, uint64_t block)
92 : : {
93 : : int err;
94 : : int i;
95 : : void *buf;
96 : : char *map;
97 : : uint64_t sec;
98 : :
99 : 0 : buf = NULL;
100 : 0 : map = NULL;
101 : 0 : sec = block * source_vhd->spb;
102 : :
103 [ # # ]: 0 : if (source_vhd->bat.bat[block] == DD_BLK_UNUSED)
104 : 0 : return 0;
105 : :
106 : 0 : err = posix_memalign(&buf, 4096, source_vhd->header.block_size);
107 [ # # ]: 0 : if (err)
108 : 0 : return -err;
109 : :
110 : 0 : err = vhd_io_read(source_vhd, buf, sec, source_vhd->spb);
111 [ # # ]: 0 : if (err)
112 : : goto done;
113 : :
114 : 0 : err = vhd_read_bitmap(source_vhd, block, &map);
115 [ # # ]: 0 : if (err)
116 : : goto done;
117 : :
118 [ # # ]: 0 : if (target_vhd->xts_tfm) {
119 : : /* If the target is encryted, encrypt each block with data */
120 [ # # ]: 0 : for (i = 0; i < source_vhd->spb; i++) {
121 [ # # ]: 0 : if (vhd_bitmap_test(source_vhd, map, i)) {
122 : 0 : void * blk_ptr = buf + i * VHD_SECTOR_SIZE;
123 : 0 : pvhd_crypto_encrypt_block(target_vhd, sec + i, blk_ptr, blk_ptr, VHD_SECTOR_SIZE);
124 : : }
125 : : }
126 : : }
127 : :
128 : 0 : err = vhd_io_write(target_vhd, buf, sec, source_vhd->spb);
129 [ # # ]: 0 : if (err) {
130 : : printf("Failed to write block %lu : %d\n", block, err);
131 : : }
132 : :
133 : : done:
134 : 0 : free(buf);
135 : 0 : free(map);
136 : :
137 : 0 : return err;
138 : : }
139 : :
140 : : static int
141 : 0 : copy_vhd(const char *name, const char *new_name, int key_size, const uint8_t *encryption_key)
142 : : {
143 : 0 : int err = 0;
144 : : int i;
145 : :
146 : : vhd_context_t source_vhd, target_vhd;
147 : : struct vhd_keyhash keyhash;
148 : :
149 : 0 : source_vhd.fd = -1;
150 : 0 : source_vhd.file = NULL;
151 : 0 : source_vhd.custom_parent = NULL;
152 : 0 : target_vhd.fd = -1;
153 : 0 : target_vhd.file = NULL;
154 : 0 : target_vhd.custom_parent = NULL;
155 : :
156 : : memset(&keyhash, 0, sizeof(keyhash));
157 : :
158 : 0 : err = vhd_open(&source_vhd, name, VHD_OPEN_RDONLY);
159 [ # # ]: 0 : if (err) {
160 : : printf("error opening %s: %d\n", name, err);
161 : 0 : return -err;
162 : : }
163 : :
164 [ # # ]: 0 : if (access(new_name, F_OK) != -1) {
165 : : printf("VHD file %s already exists, chose a different name\n",
166 : : new_name);
167 : : err = -EINVAL;
168 : : goto out1;
169 : : }
170 : :
171 : 0 : err = vhd_create(new_name, source_vhd.footer.curr_size,
172 : 0 : source_vhd.footer.type, source_vhd.footer.curr_size, 0);
173 : :
174 [ # # ]: 0 : if (err) {
175 : : printf("error creating %s: %d\n", new_name, err);
176 : : goto out1;
177 : : }
178 : :
179 : 0 : err = vhd_open(&target_vhd, new_name, VHD_OPEN_RDWR);
180 [ # # ]: 0 : if (err) {
181 : : printf("error opening %s: %d\n", new_name, err);
182 : : goto out;
183 : : }
184 : :
185 [ # # ]: 0 : if (encryption_key) {
186 : 0 : err = __load_crypto();
187 [ # # ]: 0 : if(err) {
188 : : printf("failed to load crypto support library %d\n", err);
189 : : goto out;
190 : : }
191 : :
192 : 0 : err = pvhd_calculate_keyhash(&keyhash, encryption_key, key_size);
193 [ # # ]: 0 : if (err){
194 : : printf("Failed to calculate keyhash %d\n", err);
195 : : goto out;
196 : : }
197 : :
198 : 0 : keyhash.cookie = 1;
199 : :
200 : 0 : err = vhd_set_keyhash(&target_vhd, &keyhash);
201 [ # # ]: 0 : if (err) {
202 : : printf("failed to set keyhash %d\n", err);
203 : : goto out;
204 : : }
205 : :
206 : 0 : err = pvhd_open_crypto(&target_vhd, encryption_key, key_size, new_name);
207 [ # # ]: 0 : if (err) {
208 : : printf("failed to open crypto %d\n", err);
209 : : goto out;
210 : : }
211 : : }
212 : :
213 : 0 : err = vhd_get_bat(&source_vhd);
214 [ # # ]: 0 : if (err)
215 : : goto out;
216 : :
217 [ # # ]: 0 : if (vhd_has_batmap(&source_vhd) ) {
218 : 0 : err = vhd_get_batmap(&source_vhd);
219 [ # # ]: 0 : if (err)
220 : : goto out;
221 : : }
222 : :
223 [ # # ]: 0 : for (i = 0; i < source_vhd.bat.entries; i++) {
224 : 0 : err = vhd_encrypt_copy_block(&source_vhd, &target_vhd, i);
225 [ # # ]: 0 : if (err) {
226 : : printf("Failed to encrypt block %d: %d\n", i, err);
227 : : goto out;
228 : : }
229 : : }
230 : :
231 : : out:
232 : 0 : vhd_close(&target_vhd);
233 : : out1:
234 : 0 : vhd_close(&source_vhd);
235 : :
236 : : return err;
237 : : }
238 : :
239 : 0 : int read_encryption_key(int fd, uint8_t **encryption_key, int *key_size)
240 : : {
241 : : /* Allocate the space for the key, */
242 : 0 : *encryption_key = malloc(MAX_AES_XTS_PLAIN_KEYSIZE / sizeof(uint8_t));
243 [ # # ]: 0 : if (!*encryption_key) {
244 : 0 : fprintf(stderr, "Failed to allocate space for encrpytion key\n");
245 : 0 : return -1;
246 : : }
247 : :
248 : 0 : *key_size = read(fd, (void*)*encryption_key,
249 : : MAX_AES_XTS_PLAIN_KEYSIZE / sizeof(uint8_t));
250 [ # # ]: 0 : if (*key_size != 32 && *key_size != 64){
251 : 0 : fprintf(stderr, "Unsupported keysize, use either 256 bit or 512 bit key\n");
252 : 0 : free(*encryption_key);
253 : 0 : return -1;
254 : : }
255 : :
256 : : return 0;
257 : : }
258 : :
259 : : int
260 : 0 : vhd_util_copy(int argc, char **argv)
261 : : {
262 : : char *name;
263 : : char *new_name;
264 : : char *key_path;
265 : : uint8_t *encryption_key;
266 : : int c;
267 : : int key_fd;
268 : : int key_size;
269 : : int err;
270 : :
271 : 0 : name = NULL;
272 : 0 : new_name = NULL;
273 : 0 : encryption_key = NULL;
274 : 0 : key_size = 0;
275 : :
276 : :
277 [ # # ]: 0 : if (!argc || !argv)
278 : : goto usage;
279 : :
280 : 0 : optind = 0;
281 : :
282 [ # # ]: 0 : while ((c = getopt(argc, argv, "n:N:k:Eh")) != -1) {
283 [ # # # # : 0 : switch (c) {
# ]
284 : : case 'n':
285 : 0 : name = optarg;
286 : 0 : break;
287 : : case 'N':
288 : 0 : new_name = optarg;
289 : 0 : break;
290 : : case 'k':
291 [ # # ]: 0 : if (encryption_key) {
292 : 0 : fprintf(stderr, "Only supply -E or -k once\n");
293 : 0 : exit(1);
294 : : }
295 : 0 : key_path = optarg;
296 : 0 : key_fd = open(key_path, O_RDONLY);
297 [ # # ]: 0 : if (key_fd == -1) {
298 : 0 : printf("Failed to open key file %s: %d\n", key_path, errno);
299 : 0 : return -errno;
300 : : }
301 : 0 : err = read_encryption_key(key_fd, &encryption_key, &key_size);
302 : 0 : close(key_fd);
303 [ # # ]: 0 : if (err) {
304 : 0 : return -err;
305 : : }
306 : : break;
307 : : case 'E':
308 [ # # ]: 0 : if (encryption_key) {
309 : 0 : fprintf(stderr, "Only supply -E or -k once\n");
310 : 0 : exit(1);
311 : : }
312 : 0 : err = read_encryption_key(STDIN_FILENO, &encryption_key, &key_size);
313 [ # # ]: 0 : if (err) {
314 : 0 : return -err;
315 : : }
316 : : break;
317 : : case 'h':
318 : : default:
319 : : goto usage;
320 : : }
321 : : }
322 : :
323 [ # # ]: 0 : if (!name || !new_name) {
324 : 0 : fprintf(stderr, "Must supply both existing and new VHD names\n");
325 : : goto usage;
326 : : }
327 : :
328 : 0 : err = copy_vhd(name, new_name, key_size, encryption_key);
329 : 0 : free(encryption_key);
330 : 0 : return err;
331 : : usage:
332 : : printf("options: -n <name> -N <new VHD name> "
333 : : "[-k <keyfile> | -E (pass encryption key on stdin)] "
334 : : "[-h help] \n");
335 [ # # ]: 0 : if (encryption_key) {
336 : 0 : free(encryption_key);
337 : : }
338 : : return -EINVAL;
339 : : }
|