Update Red Hat Copyright Notices
[nbdkit.git] / filters / luks / luks.c
blobcfd02c21ec3ffe8f49283beca277606a725d0de1
1 /* nbdkit
2 * Copyright Red Hat
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * 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.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
33 #include <config.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdint.h>
38 #include <inttypes.h>
39 #include <string.h>
40 #include <limits.h>
41 #include <assert.h>
42 #include <pthread.h>
44 #include <gnutls/crypto.h>
46 #include <nbdkit-filter.h>
48 #include "luks-encryption.h"
50 #include "cleanup.h"
51 #include "isaligned.h"
52 #include "minmax.h"
54 static char *passphrase = NULL;
56 static void
57 luks_unload (void)
59 /* XXX We should really store the passphrase (and master key)
60 * in mlock-ed memory.
62 if (passphrase) {
63 memset (passphrase, 0, strlen (passphrase));
64 free (passphrase);
68 static int
69 luks_thread_model (void)
71 return NBDKIT_THREAD_MODEL_PARALLEL;
74 static int
75 luks_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
76 const char *key, const char *value)
78 if (strcmp (key, "passphrase") == 0) {
79 if (nbdkit_read_password (value, &passphrase) == -1)
80 return -1;
81 return 0;
84 return next (nxdata, key, value);
87 static int
88 luks_config_complete (nbdkit_next_config_complete *next, nbdkit_backend *nxdata)
90 if (passphrase == NULL) {
91 nbdkit_error ("LUKS \"passphrase\" parameter is missing");
92 return -1;
94 return next (nxdata);
97 #define luks_config_help \
98 "passphrase=<SECRET> Secret passphrase."
100 /* Per-connection handle. */
101 struct handle {
102 struct luks_data *h;
105 static void *
106 luks_open (nbdkit_next_open *next, nbdkit_context *nxdata,
107 int readonly, const char *exportname, int is_tls)
109 struct handle *h;
111 if (next (nxdata, readonly, exportname) == -1)
112 return NULL;
114 h = calloc (1, sizeof *h);
115 if (h == NULL) {
116 nbdkit_error ("calloc: %m");
117 return NULL;
120 return h;
123 static void
124 luks_close (void *handle)
126 struct handle *h = handle;
128 free_luks_data (h->h);
129 free (h);
132 static int
133 luks_prepare (nbdkit_next *next, void *handle, int readonly)
135 struct handle *h = handle;
137 /* Check we haven't been called before, this should never happen. */
138 assert (h->h == NULL);
140 h->h = load_header (next, passphrase);
141 if (h->h == NULL)
142 return -1;
144 return 0;
147 static int64_t
148 luks_get_size (nbdkit_next *next, void *handle)
150 struct handle *h = handle;
151 int64_t size;
153 /* Check that prepare has been called already. */
154 assert (h->h != NULL);
156 const uint64_t payload_offset = get_payload_offset (h->h) * LUKS_SECTOR_SIZE;
158 size = next->get_size (next);
159 if (size == -1)
160 return -1;
162 if (size < payload_offset) {
163 nbdkit_error ("disk too small, or contains an incomplete LUKS partition");
164 return -1;
167 return size - payload_offset;
170 /* Whatever the plugin says, several operations are not supported by
171 * this filter:
173 * - extents
174 * - trim
175 * - zero
177 static int
178 luks_can_extents (nbdkit_next *next, void *handle)
180 return 0;
183 static int
184 luks_can_trim (nbdkit_next *next, void *handle)
186 return 0;
189 static int
190 luks_can_zero (nbdkit_next *next, void *handle)
192 return NBDKIT_ZERO_EMULATE;
195 static int
196 luks_can_fast_zero (nbdkit_next *next, void *handle)
198 return 0;
201 /* Rely on nbdkit to call .pread to emulate .cache calls. We will
202 * respond by decrypting the block which could be stored by the cache
203 * filter or similar on top.
205 static int
206 luks_can_cache (nbdkit_next *next, void *handle)
208 return NBDKIT_CACHE_EMULATE;
211 /* Advertise minimum/preferred sector-sized blocks, although we can in
212 * fact handle any read or write.
214 static int
215 luks_block_size (nbdkit_next *next, void *handle,
216 uint32_t *minimum, uint32_t *preferred, uint32_t *maximum)
218 if (next->block_size (next, minimum, preferred, maximum) == -1)
219 return -1;
221 if (*minimum == 0) { /* No constraints set by the plugin. */
222 *minimum = LUKS_SECTOR_SIZE;
223 *preferred = LUKS_SECTOR_SIZE;
224 *maximum = 0xffffffff;
226 else {
227 *minimum = MAX (*minimum, LUKS_SECTOR_SIZE);
228 *preferred = MAX (*minimum, MAX (*preferred, LUKS_SECTOR_SIZE));
230 return 0;
233 /* Decrypt data. */
234 static int
235 luks_pread (nbdkit_next *next, void *handle,
236 void *buf, uint32_t count, uint64_t offset,
237 uint32_t flags, int *err)
239 struct handle *h = handle;
240 const uint64_t payload_offset = get_payload_offset (h->h) * LUKS_SECTOR_SIZE;
241 CLEANUP_FREE uint8_t *sector = NULL;
242 uint64_t sectnum, sectoffs;
243 gnutls_cipher_hd_t cipher;
245 if (!h->h) {
246 *err = EIO;
247 return -1;
250 if (!IS_ALIGNED (count | offset, LUKS_SECTOR_SIZE)) {
251 sector = malloc (LUKS_SECTOR_SIZE);
252 if (sector == NULL) {
253 *err = errno;
254 nbdkit_error ("malloc: %m");
255 return -1;
259 sectnum = offset / LUKS_SECTOR_SIZE; /* sector number */
260 sectoffs = offset % LUKS_SECTOR_SIZE; /* offset within the sector */
262 cipher = create_cipher (h->h);
263 if (!cipher)
264 return -1;
266 /* Unaligned head */
267 if (sectoffs) {
268 uint64_t n = MIN (LUKS_SECTOR_SIZE - sectoffs, count);
270 assert (sector);
271 if (next->pread (next, sector, LUKS_SECTOR_SIZE,
272 sectnum * LUKS_SECTOR_SIZE + payload_offset,
273 flags, err) == -1)
274 goto err;
276 if (do_decrypt (h->h, cipher, sectnum, sector, 1) == -1)
277 goto err;
279 memcpy (buf, &sector[sectoffs], n);
281 buf += n;
282 count -= n;
283 sectnum++;
286 /* Aligned body */
287 while (count >= LUKS_SECTOR_SIZE) {
288 if (next->pread (next, buf, LUKS_SECTOR_SIZE,
289 sectnum * LUKS_SECTOR_SIZE + payload_offset,
290 flags, err) == -1)
291 goto err;
293 if (do_decrypt (h->h, cipher, sectnum, buf, 1) == -1)
294 goto err;
296 buf += LUKS_SECTOR_SIZE;
297 count -= LUKS_SECTOR_SIZE;
298 sectnum++;
301 /* Unaligned tail */
302 if (count) {
303 assert (sector);
304 if (next->pread (next, sector, LUKS_SECTOR_SIZE,
305 sectnum * LUKS_SECTOR_SIZE + payload_offset,
306 flags, err) == -1)
307 goto err;
309 if (do_decrypt (h->h, cipher, sectnum, sector, 1) == -1)
310 goto err;
312 memcpy (buf, sector, count);
315 gnutls_cipher_deinit (cipher);
316 return 0;
318 err:
319 gnutls_cipher_deinit (cipher);
320 goto err;
323 /* Lock preventing read-modify-write cycles from overlapping. */
324 static pthread_mutex_t read_modify_write_lock = PTHREAD_MUTEX_INITIALIZER;
326 /* Encrypt data. */
327 static int
328 luks_pwrite (nbdkit_next *next, void *handle,
329 const void *buf, uint32_t count, uint64_t offset,
330 uint32_t flags, int *err)
332 struct handle *h = handle;
333 const uint64_t payload_offset = get_payload_offset (h->h) * LUKS_SECTOR_SIZE;
334 CLEANUP_FREE uint8_t *sector = NULL;
335 uint64_t sectnum, sectoffs;
336 gnutls_cipher_hd_t cipher;
338 if (!h->h) {
339 *err = EIO;
340 return -1;
343 sector = malloc (LUKS_SECTOR_SIZE);
344 if (sector == NULL) {
345 *err = errno;
346 nbdkit_error ("malloc: %m");
347 return -1;
350 sectnum = offset / LUKS_SECTOR_SIZE; /* sector number */
351 sectoffs = offset % LUKS_SECTOR_SIZE; /* offset within the sector */
353 cipher = create_cipher (h->h);
354 if (!cipher)
355 return -1;
357 /* Unaligned head */
358 if (sectoffs) {
359 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&read_modify_write_lock);
361 uint64_t n = MIN (LUKS_SECTOR_SIZE - sectoffs, count);
363 if (next->pread (next, sector, LUKS_SECTOR_SIZE,
364 sectnum * LUKS_SECTOR_SIZE + payload_offset,
365 flags, err) == -1)
366 goto err;
368 memcpy (&sector[sectoffs], buf, n);
370 if (do_encrypt (h->h, cipher, sectnum, sector, 1) == -1)
371 goto err;
373 if (next->pwrite (next, sector, LUKS_SECTOR_SIZE,
374 sectnum * LUKS_SECTOR_SIZE + payload_offset,
375 flags, err) == -1)
376 goto err;
378 buf += n;
379 count -= n;
380 sectnum++;
383 /* Aligned body */
384 while (count >= LUKS_SECTOR_SIZE) {
385 memcpy (sector, buf, LUKS_SECTOR_SIZE);
387 if (do_encrypt (h->h, cipher, sectnum, sector, 1) == -1)
388 goto err;
390 if (next->pwrite (next, sector, LUKS_SECTOR_SIZE,
391 sectnum * LUKS_SECTOR_SIZE + payload_offset,
392 flags, err) == -1)
393 goto err;
395 buf += LUKS_SECTOR_SIZE;
396 count -= LUKS_SECTOR_SIZE;
397 sectnum++;
400 /* Unaligned tail */
401 if (count) {
402 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&read_modify_write_lock);
404 if (next->pread (next, sector, LUKS_SECTOR_SIZE,
405 sectnum * LUKS_SECTOR_SIZE + payload_offset,
406 flags, err) == -1)
407 goto err;
409 memcpy (sector, buf, count);
411 if (do_encrypt (h->h, cipher, sectnum, sector, 1) == -1)
412 goto err;
414 if (next->pwrite (next, sector, LUKS_SECTOR_SIZE,
415 sectnum * LUKS_SECTOR_SIZE + payload_offset,
416 flags, err) == -1)
417 goto err;
420 gnutls_cipher_deinit (cipher);
421 return 0;
423 err:
424 gnutls_cipher_deinit (cipher);
425 return -1;
428 static struct nbdkit_filter filter = {
429 .name = "luks",
430 .longname = "nbdkit luks filter",
431 .unload = luks_unload,
432 .thread_model = luks_thread_model,
433 .config = luks_config,
434 .config_complete = luks_config_complete,
435 .config_help = luks_config_help,
436 .open = luks_open,
437 .close = luks_close,
438 .prepare = luks_prepare,
439 .get_size = luks_get_size,
440 .can_extents = luks_can_extents,
441 .can_trim = luks_can_trim,
442 .can_zero = luks_can_zero,
443 .can_fast_zero = luks_can_fast_zero,
444 .can_cache = luks_can_cache,
445 .block_size = luks_block_size,
446 .pread = luks_pread,
447 .pwrite = luks_pwrite,
450 NBDKIT_REGISTER_FILTER (filter)