LCOV - code coverage report
Current view: top level - drivers - block-crypto.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 55 0.0 %
Date: 2024-12-18 23:41:32 Functions: 0 7 0.0 %
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (c) 2010, XenSource Inc.
       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 are met:
       7                 :            :  *     * Redistributions of source code must retain the above copyright
       8                 :            :  *       notice, this list of conditions and the following disclaimer.
       9                 :            :  *     * Redistributions in binary form must reproduce the above copyright
      10                 :            :  *       notice, this list of conditions and the following disclaimer in the
      11                 :            :  *       documentation and/or other materials provided with the distribution.
      12                 :            :  *     * Neither the name of XenSource Inc. nor the names of its contributors
      13                 :            :  *       may be used to endorse or promote products derived from this software
      14                 :            :  *       without specific prior written permission.
      15                 :            :  *
      16                 :            :  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      17                 :            :  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      18                 :            :  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      19                 :            :  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
      20                 :            :  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
      21                 :            :  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
      22                 :            :  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
      23                 :            :  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
      24                 :            :  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
      25                 :            :  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
      26                 :            :  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      27                 :            :  */
      28                 :            : 
      29                 :            : /*
      30                 :            :  * Copyright (c) 2014 Citrix Systems, Inc.
      31                 :            :  */
      32                 :            : 
      33                 :            : 
      34                 :            : #include <stdio.h>
      35                 :            : #include <sys/types.h>
      36                 :            : #include <sys/stat.h>
      37                 :            : #include <fcntl.h>
      38                 :            : #include <errno.h>
      39                 :            : #include <unistd.h>
      40                 :            : 
      41                 :            : #include "list.h"
      42                 :            : #include "libvhd.h"
      43                 :            : #include "tapdisk.h"
      44                 :            : #include "vhd-util.h"
      45                 :            : 
      46                 :            : #include "crypto/compat-crypto-openssl.h"
      47                 :            : #include "crypto/xts_aes.h"
      48                 :            : 
      49                 :            : #define MAX_AES_XTS_PLAIN_KEYSIZE 1024
      50                 :            : 
      51                 :            : char * vhd_util_get_vhd_basename(vhd_context_t *vhd);
      52                 :            : extern int CRYPTO_SUPPORTED_KEYSIZE[];
      53                 :            : 
      54                 :            : /*
      55                 :            :  * calculates keyhash by taking a SHA256 hash of @keyhash->nonce + key
      56                 :            :  */
      57                 :            : int
      58                 :          0 : vhd_calculate_keyhash(struct vhd_keyhash *keyhash,
      59                 :            :                 const uint8_t *key, size_t key_bytes)
      60                 :            : {
      61                 :            :         int err;
      62                 :          0 :         EVP_MD_CTX *evp = NULL;
      63                 :            : 
      64                 :          0 :         err = -1;
      65                 :          0 :         evp = EVP_MD_CTX_new();
      66                 :          0 :         if (!EVP_DigestInit_ex(evp, EVP_sha256(), NULL)) {
      67                 :            :                 EPRINTF("failed to init sha256 context\n");
      68                 :            :                 goto cleanup;
      69                 :            :         }
      70                 :            : 
      71                 :          0 :         if (!EVP_DigestUpdate(evp, keyhash->nonce, sizeof(keyhash->nonce))) {
      72                 :            :                 EPRINTF("failed to hash nonce\n");
      73                 :            :                 goto cleanup;
      74                 :            :         }
      75                 :            : 
      76                 :          0 :         if (!EVP_DigestUpdate(evp, key, key_bytes)) {
      77                 :            :                 EPRINTF("failed to hash key\n");
      78                 :            :                 goto cleanup;
      79                 :            :         }
      80                 :            : 
      81                 :          0 :         if (!EVP_DigestFinal_ex(evp, keyhash->hash, NULL)) {
      82                 :            :                 EPRINTF("failed to finalize hash\n");
      83                 :            :                 goto cleanup;
      84                 :            :         }
      85                 :            : 
      86                 :            :         err = 0;
      87                 :            : 
      88                 :            : cleanup:
      89                 :          0 :         EVP_MD_CTX_free(evp);
      90                 :          0 :         return err;
      91                 :            : }
      92                 :            : 
      93                 :            : static int
      94                 :          0 : check_key(const uint8_t *keybuf, unsigned int keysize,
      95                 :            :           const struct vhd_keyhash *vhdhash)
      96                 :            : {
      97                 :            :         int err;
      98                 :            :         struct vhd_keyhash keyhash;
      99                 :            : 
     100                 :          0 :         if (!vhdhash->cookie) {
     101                 :            :                 DPRINTF("missing key hash\n");
     102                 :            :                 err = 1;
     103                 :            :                 goto out;
     104                 :            :         }
     105                 :            : 
     106                 :          0 :         memcpy(keyhash.nonce, vhdhash->nonce, sizeof(keyhash.nonce));
     107                 :          0 :         err = vhd_calculate_keyhash(&keyhash, keybuf, keysize / 8);
     108                 :          0 :         if (err) {
     109                 :            :                 DPRINTF("failed to calculate keyhash: %d\n", err);
     110                 :            :                 goto out;
     111                 :            :         }
     112                 :            : 
     113                 :          0 :         if (memcmp(keyhash.hash, vhdhash->hash, sizeof(keyhash.hash))) {
     114                 :            :                 DPRINTF("key hash mismatch\n");
     115                 :            :                 err = 1;
     116                 :            :                 goto out;
     117                 :            :         }
     118                 :            : 
     119                 :            : out:
     120                 :          0 :         if (err) {
     121                 :            :                 DPRINTF("key check failed\n");
     122                 :            :                 err = -ENOKEY;
     123                 :            :         }
     124                 :          0 :         return err;
     125                 :            : }
     126                 :            : 
     127                 :            : #ifdef OPEN_XT
     128                 :            : static int
     129                 :            : find_keyfile(char **keyfile, const char *dirs,
     130                 :            :              const char *basename, int keysize)
     131                 :            : {
     132                 :            :         char *sep = NULL;
     133                 :            :         *keyfile  = NULL;
     134                 :            : 
     135                 :            :         while (dirs && strlen(dirs) > 0) {
     136                 :            :                 char keydir[256] = { 0 }, path[256] = { 0 };
     137                 :            :                 struct stat st;
     138                 :            :                 int err;
     139                 :            : 
     140                 :            :                 sep = strchr(dirs, ',');
     141                 :            :                 /* get directory element */
     142                 :            :                 if (sep == NULL) {
     143                 :            :                         safe_strncpy(keydir, dirs, sizeof(keydir));
     144                 :            :                         dirs = NULL;
     145                 :            :                 } else {
     146                 :            :                         size_t len = sep - dirs;
     147                 :            :                         safe_strncpy(keydir, dirs, len);
     148                 :            :                         dirs = sep+1;
     149                 :            :                 }
     150                 :            : 
     151                 :            :                 /* check if keyfile is inside dir */
     152                 :            :                 snprintf(path, sizeof(path),
     153                 :            :                          "%s/%s,aes-xts-plain,%d.key",
     154                 :            :                          keydir, basename, keysize);
     155                 :            :                 err = stat(path, &st);
     156                 :            :                 if (err == 0) {
     157                 :            :                         /* found */
     158                 :            :                         *keyfile = strdup(path);
     159                 :            :                         if (*keyfile == NULL) {
     160                 :            :                                 return -ENOMEM;
     161                 :            :                         }
     162                 :            :                         DPRINTF("found keyfile %s\n", path);
     163                 :            :                         return 0;
     164                 :            :                 } else if (err < 0 && errno != ENOENT) {
     165                 :            :                         return -errno;
     166                 :            :                 } else {
     167                 :            :                         DPRINTF("keyfile %s not found\n", path);
     168                 :            :                 }
     169                 :            :         }
     170                 :            : 
     171                 :            :         return -ENOENT;
     172                 :            : }
     173                 :            : 
     174                 :            : static int
     175                 :            : read_keyfile(const char *keydir, const char *basename,
     176                 :            :              uint8_t *keybuf, size_t keysize)
     177                 :            : {
     178                 :            :         int err, fd = -1;
     179                 :            :         char *keyfile = NULL;
     180                 :            : 
     181                 :            :         err = find_keyfile(&keyfile, keydir, basename, keysize);
     182                 :            :         if (err) {
     183                 :            :                 keyfile = NULL;
     184                 :            :                 goto out;
     185                 :            :         }
     186                 :            : 
     187                 :            :         fd = open(keyfile, O_RDONLY);
     188                 :            :         if (fd == -1) {
     189                 :            :                 err = -errno;
     190                 :            :                 goto out;
     191                 :            :         }
     192                 :            : 
     193                 :            :         err = read(fd, keybuf, keysize / 8);
     194                 :            :         if (err != keysize / 8) {
     195                 :            :                 err = err == -1 ? -errno : -EINVAL;
     196                 :            :                 goto out;
     197                 :            :         }
     198                 :            : 
     199                 :            :         DPRINTF("using keyfile %s, keysize %d\n", keyfile, (int)keysize);
     200                 :            :         err = 0;
     201                 :            : 
     202                 :            : out:
     203                 :            :         if (fd != -1)
     204                 :            :                 close(fd);
     205                 :            :         free(keyfile);
     206                 :            :         return err;
     207                 :            : }
     208                 :            : 
     209                 :            : // try 512bit, 256bit keys
     210                 :            : static int
     211                 :            : read_preferred_keyfile(const char *keydir, const char *basename, uint8_t *keybuf, int *keysize)
     212                 :            : {
     213                 :            :     int err, i;
     214                 :            :     *keysize = 0;
     215                 :            :     err = -EINVAL;
     216                 :            :     for (i = 0; CRYPTO_SUPPORTED_KEYSIZE[i] > 0; ++i) {
     217                 :            :         err = read_keyfile(keydir, basename, keybuf, CRYPTO_SUPPORTED_KEYSIZE[i]);
     218                 :            :         if (err == 0) {
     219                 :            :             *keysize = CRYPTO_SUPPORTED_KEYSIZE[i];
     220                 :            :             return 0;
     221                 :            :         }
     222                 :            :     }
     223                 :            :     return err;
     224                 :            : }
     225                 :            : 
     226                 :            : 
     227                 :            : static vhd_context_t *
     228                 :            : vhd_open_parent(vhd_context_t *ctx)
     229                 :            : {
     230                 :            :     vhd_context_t *parent = NULL;
     231                 :            :     char *next = NULL;
     232                 :            :     int err;
     233                 :            :     if (ctx->footer.type != HD_TYPE_DIFF)
     234                 :            :         goto out;
     235                 :            :     if (vhd_parent_raw(ctx))
     236                 :            :         goto out;
     237                 :            :     err = vhd_parent_locator_get(ctx, &next);
     238                 :            :     if (err)
     239                 :            :         goto out;
     240                 :            : 
     241                 :            :     parent = calloc(1, sizeof(*parent));
     242                 :            :     if (!parent)
     243                 :            :         goto out;
     244                 :            : 
     245                 :            :     err = vhd_open(parent, next, VHD_OPEN_RDONLY);
     246                 :            :     if (err) {
     247                 :            :         DPRINTF("vhd_open failed: %d\n", err);
     248                 :            :         free(parent);
     249                 :            :         parent = NULL;
     250                 :            :         goto out;
     251                 :            :     }
     252                 :            : out:
     253                 :            :     free(next);
     254                 :            :     return parent;
     255                 :            : }
     256                 :            : 
     257                 :            : /* look up the chain for first parent VHD with encryption key */
     258                 :            : static int
     259                 :            : chain_find_keyed_vhd(vhd_context_t *vhd, uint8_t *key, int *keysize, struct vhd_keyhash *out_keyhash)
     260                 :            : {
     261                 :            :     int err;
     262                 :            :     struct vhd_keyhash keyhash;
     263                 :            :     vhd_context_t *p = vhd, *p2;
     264                 :            :     char *basename;
     265                 :            :     const char *keydir;
     266                 :            :     int found = 0;
     267                 :            : 
     268                 :            :     memset(out_keyhash, 0, sizeof(*out_keyhash));
     269                 :            : 
     270                 :            :     keydir = getenv("TAPDISK3_CRYPTO_KEYDIR");
     271                 :            :     if (keydir == NULL) {
     272                 :            :       keydir = CRYPTO_DEFAULT_KEYDIR;
     273                 :            :     }
     274                 :            : 
     275                 :            :     while (p) {
     276                 :            :         err = vhd_get_keyhash(p, &keyhash);
     277                 :            :         if (err) {
     278                 :            :             DPRINTF("error getting keyhash: %d\n", err);
     279                 :            :             return err;
     280                 :            :         }
     281                 :            : 
     282                 :            :         if (keyhash.cookie && keydir == NULL) {
     283                 :            :             DPRINTF("this vhd requires TAPDISK3_CRYPTO_KEYDIR\n");
     284                 :            :             return -ENOKEY;
     285                 :            :         }
     286                 :            : 
     287                 :            :         /* if keydir is set, we check if a key exists (with the same basename)
     288                 :            :          * regardless the keyhash.cookie value to prevent an issue where
     289                 :            :          * the vhd has been replaced by another one that is clear */
     290                 :            :         if (keydir) {
     291                 :            :             basename = vhd_util_get_vhd_basename(p);
     292                 :            :             if (!basename) {
     293                 :            :                 err = -ENOMEM;
     294                 :            :                 goto out;
     295                 :            :             }
     296                 :            : 
     297                 :            :             err = read_preferred_keyfile(keydir, basename, key, keysize);
     298                 :            :             free(basename);
     299                 :            :             switch (err) {
     300                 :            :             case 0: /* a key has been found with the same basename */
     301                 :            :                 if (keyhash.cookie == 0) {
     302                 :            :                     DPRINTF("key found for %s but no hash set\n", p->file);
     303                 :            :                     err = -EACCES;
     304                 :            :                     goto out;
     305                 :            :                 }
     306                 :            :                 err = check_key(key, *keysize, &keyhash);
     307                 :            :                 if (err)
     308                 :            :                     goto out;
     309                 :            :                 DPRINTF("using key from vhd: %s\n", p->file);
     310                 :            :                 *out_keyhash = keyhash;
     311                 :            :                 found = 1;
     312                 :            :                 break;
     313                 :            :             case -ENOENT: /* no key found, get to the next one if the cookie's not set */
     314                 :            :                 if (keyhash.cookie != 0) {
     315                 :            :                     err = -ENOKEY;
     316                 :            :                     goto out;
     317                 :            :                 }
     318                 :            :                 break;
     319                 :            :             default: /* some another error */
     320                 :            :                 goto out;
     321                 :            :             }
     322                 :            :         }
     323                 :            : 
     324                 :            :         if (found)
     325                 :            :             goto out;
     326                 :            : 
     327                 :            :         p2 = p;
     328                 :            :         p = vhd_open_parent(p);
     329                 :            : 
     330                 :            :         if (p2 != vhd) {
     331                 :            :             vhd_close(p2);
     332                 :            :             free(p2);
     333                 :            :         }
     334                 :            :     }
     335                 :            :     return 0;
     336                 :            : out:
     337                 :            :     if (p != vhd) {
     338                 :            :         vhd_close(p);
     339                 :            :         free(p);
     340                 :            :     }
     341                 :            :     return err;
     342                 :            : }
     343                 :            : #endif
     344                 :            : 
     345                 :            : int
     346                 :          0 : vhd_open_crypto(vhd_context_t *vhd, const uint8_t *key, size_t key_bytes, const char *name)
     347                 :            : {
     348                 :            :         struct vhd_keyhash keyhash;
     349                 :            :         int err;
     350                 :            : #ifdef OPEN_XT
     351                 :            :         uint8_t key[MAX_AES_XTS_PLAIN_KEYSIZE / sizeof(uint8_t)] = { 0 };
     352                 :            :         int keysize = 0;
     353                 :            : #endif
     354                 :            : 
     355                 :          0 :         if (vhd->xts_tfm)
     356                 :          0 :                 return 0;
     357                 :            : 
     358                 :            : #ifdef OPEN_XT
     359                 :            :         err = chain_find_keyed_vhd(vhd, key, &keysize, &keyhash);
     360                 :            :         if (err) {
     361                 :            :             DPRINTF("error in vhd chain: %d\n", err);
     362                 :            :             return err;
     363                 :            :         }
     364                 :            : 
     365                 :            :         if (keyhash.cookie == 0) {
     366                 :            :                 return 0;
     367                 :            :         }
     368                 :            : #else
     369                 :            :         memset(&keyhash, 0, sizeof(keyhash));
     370                 :          0 :         err = vhd_get_keyhash(vhd, &keyhash);
     371                 :          0 :         if (err) {
     372                 :            :                 EPRINTF("error getting keyhash: %d\n", err);
     373                 :            :                 return err;
     374                 :            :         }
     375                 :            : 
     376                 :          0 :         if (keyhash.cookie == 0) {
     377                 :          0 :                 if (!key) {
     378                 :            :                         DPRINTF("No crypto, not starting crypto\n");
     379                 :            :                         return 0;
     380                 :            :                 }
     381                 :            : 
     382                 :            :                 EPRINTF("VHD %s has no keyhash when encryption is requested\n", name);
     383                 :            :                 return -EINVAL;
     384                 :          0 :         } else if (!key) {
     385                 :            :                 EPRINTF("No encryption key supplied for encrypted VHD, %s\n", name);
     386                 :            :                 return -EINVAL;
     387                 :            :         }
     388                 :            : 
     389                 :          0 :         err = check_key(key, key_bytes * 8, &keyhash);
     390                 :          0 :         if (err) {
     391                 :            :                 EPRINTF("Keyhash doesn't match vhd key for %s\n", name);
     392                 :            :                 return err;
     393                 :            :         }
     394                 :            : 
     395                 :            :         DPRINTF("Keyhash verified, starting crypto for %s\n", name);
     396                 :            : #endif
     397                 :            : 
     398                 :          0 :         vhd->xts_tfm = xts_aes_setup();
     399                 :          0 :         if (vhd->xts_tfm == NULL) {
     400                 :            :                 err = -EINVAL;
     401                 :            :                 return err;
     402                 :            :         }
     403                 :            : 
     404                 :          0 :         xts_aes_setkey(vhd->xts_tfm, key, key_bytes);
     405                 :            :         return 0;
     406                 :            : }
     407                 :            : 
     408                 :            : void
     409                 :          0 : vhd_close_crypto(vhd_context_t *vhd)
     410                 :            : {
     411                 :          0 :         if (vhd->xts_tfm)
     412                 :            :         {
     413                 :          0 :                 EVP_CIPHER_CTX_free(vhd->xts_tfm->en_ctx);
     414                 :          0 :                 EVP_CIPHER_CTX_free(vhd->xts_tfm->de_ctx);
     415                 :          0 :                 free(vhd->xts_tfm);
     416                 :            :         }
     417                 :          0 : }
     418                 :            : 
     419                 :            : void
     420                 :          0 : vhd_crypto_decrypt(vhd_context_t *vhd, td_request_t *t)
     421                 :            : {
     422                 :            :         int sec, ret;
     423                 :            : 
     424                 :          0 :         for (sec = 0; sec < t->secs; sec++) {
     425                 :          0 :                 ret = xts_aes_plain_decrypt(vhd->xts_tfm, t->sec + sec,
     426                 :          0 :                                             (uint8_t *)t->buf +
     427                 :          0 :                                             sec * VHD_SECTOR_SIZE,
     428                 :            :                                             (uint8_t *)t->buf +
     429                 :            :                                             sec * VHD_SECTOR_SIZE,
     430                 :            :                                             VHD_SECTOR_SIZE);
     431                 :          0 :                 if (ret) {
     432                 :            :                         DPRINTF("crypto decrypt failed: %d : TERMINATED\n", ret);
     433                 :          0 :                         exit(1); /* XXX */
     434                 :            :                 }
     435                 :            :         }
     436                 :          0 : }
     437                 :            : 
     438                 :            : int
     439                 :          0 : vhd_crypto_encrypt_block(vhd_context_t *vhd, sector_t sector, uint8_t *source,
     440                 :            :                          uint8_t *dst, unsigned int block_size)
     441                 :            : {
     442                 :          0 :         return xts_aes_plain_encrypt(vhd->xts_tfm, sector, dst, source, block_size);
     443                 :            : }
     444                 :            : 
     445                 :            : void
     446                 :          0 : vhd_crypto_encrypt(vhd_context_t *vhd, td_request_t *t, char *orig_buf)
     447                 :            : {
     448                 :            :         int sec, ret;
     449                 :            : 
     450                 :          0 :         for (sec = 0; sec < t->secs; sec++) {
     451                 :          0 :                 ret = vhd_crypto_encrypt_block(
     452                 :          0 :                         vhd, t->sec + sec,
     453                 :            :                         (uint8_t *)orig_buf + sec * VHD_SECTOR_SIZE,
     454                 :          0 :                         (uint8_t *)t->buf + sec * VHD_SECTOR_SIZE,
     455                 :            :                         VHD_SECTOR_SIZE);
     456                 :          0 :                 if (ret) {
     457                 :            :                         DPRINTF("crypto encrypt failed: %d : TERMINATED\n", ret);
     458                 :          0 :                         exit(1); /* XXX */
     459                 :            :                 }
     460                 :            :         }
     461                 :          0 : }
     462                 :            : 

Generated by: LCOV version 1.13