"Fossies" - the Fresh Open Source Software Archive

Member "cryptsetup-2.4.3/lib/verity/verity.c" (13 Jan 2022, 10405 Bytes) of package /linux/misc/cryptsetup-2.4.3.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "verity.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.3.6_vs_2.4.0.

    1 /*
    2  * dm-verity volume handling
    3  *
    4  * Copyright (C) 2012-2021 Red Hat, Inc. All rights reserved.
    5  *
    6  * This file is free software; you can redistribute it and/or
    7  * modify it under the terms of the GNU Lesser General Public
    8  * License as published by the Free Software Foundation; either
    9  * version 2.1 of the License, or (at your option) any later version.
   10  *
   11  * This file is distributed in the hope that it will be useful,
   12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14  * Lesser General Public License for more details.
   15  *
   16  * You should have received a copy of the GNU Lesser General Public
   17  * License along with this file; if not, write to the Free Software
   18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   19  */
   20 
   21 #include <errno.h>
   22 #include <stdio.h>
   23 #include <stdlib.h>
   24 #include <string.h>
   25 #include <stdint.h>
   26 #include <ctype.h>
   27 #include <sys/types.h>
   28 #include <sys/stat.h>
   29 #include <uuid/uuid.h>
   30 
   31 #include "libcryptsetup.h"
   32 #include "verity.h"
   33 #include "internal.h"
   34 
   35 #define VERITY_SIGNATURE "verity\0\0"
   36 
   37 /* https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity#verity-superblock-format */
   38 struct verity_sb {
   39     uint8_t  signature[8];  /* "verity\0\0" */
   40     uint32_t version;   /* superblock version */
   41     uint32_t hash_type; /* 0 - Chrome OS, 1 - normal */
   42     uint8_t  uuid[16];  /* UUID of hash device */
   43     uint8_t  algorithm[32];/* hash algorithm name */
   44     uint32_t data_block_size; /* data block in bytes */
   45     uint32_t hash_block_size; /* hash block in bytes */
   46     uint64_t data_blocks;   /* number of data blocks */
   47     uint16_t salt_size; /* salt size */
   48     uint8_t  _pad1[6];
   49     uint8_t  salt[256]; /* salt */
   50     uint8_t  _pad2[168];
   51 } __attribute__((packed));
   52 
   53 /* Read verity superblock from disk */
   54 int VERITY_read_sb(struct crypt_device *cd,
   55            uint64_t sb_offset,
   56            char **uuid_string,
   57            struct crypt_params_verity *params)
   58 {
   59     struct device *device = crypt_metadata_device(cd);
   60     struct verity_sb sb = {};
   61     ssize_t hdr_size = sizeof(struct verity_sb);
   62     int devfd, sb_version;
   63 
   64     log_dbg(cd, "Reading VERITY header of size %zu on device %s, offset %" PRIu64 ".",
   65         sizeof(struct verity_sb), device_path(device), sb_offset);
   66 
   67     if (params->flags & CRYPT_VERITY_NO_HEADER) {
   68         log_err(cd, _("Verity device %s does not use on-disk header."),
   69             device_path(device));
   70         return -EINVAL;
   71     }
   72 
   73     if (MISALIGNED_512(sb_offset)) {
   74         log_err(cd, _("Unsupported VERITY hash offset."));
   75         return -EINVAL;
   76     }
   77 
   78     devfd = device_open(cd, device, O_RDONLY);
   79     if (devfd < 0) {
   80         log_err(cd, _("Cannot open device %s."), device_path(device));
   81         return -EINVAL;
   82     }
   83 
   84     if (read_lseek_blockwise(devfd, device_block_size(cd, device),
   85                  device_alignment(device), &sb, hdr_size,
   86                  sb_offset) < hdr_size)
   87         return -EIO;
   88 
   89     if (memcmp(sb.signature, VERITY_SIGNATURE, sizeof(sb.signature))) {
   90         log_err(cd, _("Device %s is not a valid VERITY device."),
   91             device_path(device));
   92         return -EINVAL;
   93     }
   94 
   95     sb_version = le32_to_cpu(sb.version);
   96     if (sb_version != 1) {
   97         log_err(cd, _("Unsupported VERITY version %d."), sb_version);
   98         return -EINVAL;
   99     }
  100     params->hash_type = le32_to_cpu(sb.hash_type);
  101     if (params->hash_type > VERITY_MAX_HASH_TYPE) {
  102         log_err(cd, _("Unsupported VERITY hash type %d."), params->hash_type);
  103         return -EINVAL;
  104     }
  105 
  106     params->data_block_size = le32_to_cpu(sb.data_block_size);
  107     params->hash_block_size = le32_to_cpu(sb.hash_block_size);
  108     if (VERITY_BLOCK_SIZE_OK(params->data_block_size) ||
  109         VERITY_BLOCK_SIZE_OK(params->hash_block_size)) {
  110         log_err(cd, _("Unsupported VERITY block size."));
  111         return -EINVAL;
  112     }
  113     params->data_size = le64_to_cpu(sb.data_blocks);
  114 
  115     params->hash_name = strndup((const char*)sb.algorithm, sizeof(sb.algorithm));
  116     if (!params->hash_name)
  117         return -ENOMEM;
  118     if (crypt_hash_size(params->hash_name) <= 0) {
  119         log_err(cd, _("Hash algorithm %s not supported."),
  120             params->hash_name);
  121         free(CONST_CAST(char*)params->hash_name);
  122         params->hash_name = NULL;
  123         return -EINVAL;
  124     }
  125 
  126     params->salt_size = le16_to_cpu(sb.salt_size);
  127     if (params->salt_size > sizeof(sb.salt)) {
  128         log_err(cd, _("VERITY header corrupted."));
  129         free(CONST_CAST(char*)params->hash_name);
  130         params->hash_name = NULL;
  131         return -EINVAL;
  132     }
  133     params->salt = malloc(params->salt_size);
  134     if (!params->salt) {
  135         free(CONST_CAST(char*)params->hash_name);
  136         params->hash_name = NULL;
  137         return -ENOMEM;
  138     }
  139     memcpy(CONST_CAST(char*)params->salt, sb.salt, params->salt_size);
  140 
  141     if ((*uuid_string = malloc(40)))
  142         uuid_unparse(sb.uuid, *uuid_string);
  143 
  144     params->hash_area_offset = sb_offset;
  145     return 0;
  146 }
  147 
  148 static void _to_lower(char *str)
  149 {
  150     for(; *str; str++)
  151         if (isupper(*str))
  152             *str = tolower(*str);
  153 }
  154 
  155 /* Write verity superblock to disk */
  156 int VERITY_write_sb(struct crypt_device *cd,
  157            uint64_t sb_offset,
  158            const char *uuid_string,
  159            struct crypt_params_verity *params)
  160 {
  161     struct device *device = crypt_metadata_device(cd);
  162     struct verity_sb sb = {};
  163     ssize_t hdr_size = sizeof(struct verity_sb);
  164     size_t block_size;
  165     char *algorithm;
  166     uuid_t uuid;
  167     int r, devfd;
  168 
  169     log_dbg(cd, "Updating VERITY header of size %zu on device %s, offset %" PRIu64 ".",
  170         sizeof(struct verity_sb), device_path(device), sb_offset);
  171 
  172     if (!uuid_string || uuid_parse(uuid_string, uuid) == -1) {
  173         log_err(cd, _("Wrong VERITY UUID format provided on device %s."),
  174             device_path(device));
  175         return -EINVAL;
  176     }
  177 
  178     if (params->flags & CRYPT_VERITY_NO_HEADER) {
  179         log_err(cd, _("Verity device %s does not use on-disk header."),
  180             device_path(device));
  181         return -EINVAL;
  182     }
  183 
  184     /* Avoid possible increasing of image size - FEC could fail later because of it */
  185     block_size = device_block_size(cd, device);
  186     if (block_size > params->hash_block_size) {
  187         device_disable_direct_io(device);
  188         block_size = params->hash_block_size;
  189     }
  190 
  191     devfd = device_open(cd, device, O_RDWR);
  192     if (devfd < 0) {
  193         log_err(cd, _("Cannot open device %s."), device_path(device));
  194         return -EINVAL;
  195     }
  196 
  197     memcpy(&sb.signature, VERITY_SIGNATURE, sizeof(sb.signature));
  198     sb.version         = cpu_to_le32(1);
  199     sb.hash_type       = cpu_to_le32(params->hash_type);
  200     sb.data_block_size = cpu_to_le32(params->data_block_size);
  201     sb.hash_block_size = cpu_to_le32(params->hash_block_size);
  202     sb.salt_size       = cpu_to_le16(params->salt_size);
  203     sb.data_blocks     = cpu_to_le64(params->data_size);
  204 
  205     /* Kernel always use lower-case */
  206     algorithm = (char *)sb.algorithm;
  207     strncpy(algorithm, params->hash_name, sizeof(sb.algorithm)-1);
  208     algorithm[sizeof(sb.algorithm)-1] = '\0';
  209     _to_lower(algorithm);
  210 
  211     memcpy(sb.salt, params->salt, params->salt_size);
  212     memcpy(sb.uuid, uuid, sizeof(sb.uuid));
  213 
  214     r = write_lseek_blockwise(devfd, block_size, device_alignment(device),
  215                   (char*)&sb, hdr_size, sb_offset) < hdr_size ? -EIO : 0;
  216     if (r)
  217         log_err(cd, _("Error during update of verity header on device %s."),
  218             device_path(device));
  219 
  220     device_sync(cd, device);
  221 
  222     return r;
  223 }
  224 
  225 /* Calculate hash offset in hash blocks */
  226 uint64_t VERITY_hash_offset_block(struct crypt_params_verity *params)
  227 {
  228     uint64_t hash_offset = params->hash_area_offset;
  229 
  230     if (params->flags & CRYPT_VERITY_NO_HEADER)
  231         return hash_offset / params->hash_block_size;
  232 
  233     hash_offset += sizeof(struct verity_sb);
  234     hash_offset += params->hash_block_size - 1;
  235 
  236     return hash_offset / params->hash_block_size;
  237 }
  238 
  239 int VERITY_UUID_generate(struct crypt_device *cd __attribute__((unused)), char **uuid_string)
  240 {
  241     uuid_t uuid;
  242 
  243     *uuid_string = malloc(40);
  244     if (!*uuid_string)
  245         return -ENOMEM;
  246     uuid_generate(uuid);
  247     uuid_unparse(uuid, *uuid_string);
  248     return 0;
  249 }
  250 
  251 /* Activate verity device in kernel device-mapper */
  252 int VERITY_activate(struct crypt_device *cd,
  253              const char *name,
  254              const char *root_hash,
  255              size_t root_hash_size,
  256              const char *signature_description,
  257              struct device *fec_device,
  258              struct crypt_params_verity *verity_hdr,
  259              uint32_t activation_flags)
  260 {
  261     uint32_t dmv_flags;
  262     unsigned int fec_errors = 0;
  263     int r, v;
  264     struct crypt_dm_active_device dmd = {
  265         .size = verity_hdr->data_size * verity_hdr->data_block_size / 512,
  266         .flags = activation_flags,
  267         .uuid = crypt_get_uuid(cd),
  268     };
  269 
  270     log_dbg(cd, "Trying to activate VERITY device %s using hash %s.",
  271         name ?: "[none]", verity_hdr->hash_name);
  272 
  273     if (verity_hdr->flags & CRYPT_VERITY_CHECK_HASH) {
  274         if (signature_description) {
  275             log_err(cd, _("Root hash signature verification is not supported."));
  276             return -EINVAL;
  277         }
  278 
  279         log_dbg(cd, "Verification of data in userspace required.");
  280         r = VERITY_verify(cd, verity_hdr, root_hash, root_hash_size);
  281 
  282         if ((r == -EPERM || r == -EFAULT) && fec_device) {
  283             v = r;
  284             log_dbg(cd, "Verification failed, trying to repair with FEC device.");
  285             r = VERITY_FEC_process(cd, verity_hdr, fec_device, 1, &fec_errors);
  286             if (r < 0)
  287                 log_err(cd, _("Errors cannot be repaired with FEC device."));
  288             else if (fec_errors) {
  289                 log_err(cd, _("Found %u repairable errors with FEC device."),
  290                     fec_errors);
  291                 /* If root hash failed, we cannot be sure it was properly repaired */
  292             }
  293             if (v == -EFAULT)
  294                 r = -EPERM;
  295         }
  296 
  297         if (r < 0)
  298             return r;
  299     }
  300 
  301     if (!name)
  302         return 0;
  303 
  304     r = device_block_adjust(cd, crypt_metadata_device(cd), DEV_OK,
  305                 0, NULL, NULL);
  306     if (r)
  307         return r;
  308 
  309     r = device_block_adjust(cd, crypt_data_device(cd), DEV_EXCL,
  310                 0, &dmd.size, &dmd.flags);
  311     if (r)
  312         return r;
  313 
  314     if (fec_device) {
  315         r = device_block_adjust(cd, fec_device, DEV_OK,
  316                     0, NULL, NULL);
  317         if (r)
  318             return r;
  319     }
  320 
  321     r = dm_verity_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd),
  322             crypt_metadata_device(cd), fec_device, root_hash,
  323             root_hash_size, signature_description,
  324             VERITY_hash_offset_block(verity_hdr),
  325             VERITY_FEC_blocks(cd, fec_device, verity_hdr), verity_hdr);
  326 
  327     if (r)
  328         return r;
  329 
  330     r = dm_create_device(cd, name, CRYPT_VERITY, &dmd);
  331     if (r < 0 && (dm_flags(cd, DM_VERITY, &dmv_flags) || !(dmv_flags & DM_VERITY_SUPPORTED))) {
  332         log_err(cd, _("Kernel does not support dm-verity mapping."));
  333         r = -ENOTSUP;
  334     }
  335     if (r < 0 && signature_description && !(dmv_flags & DM_VERITY_SIGNATURE_SUPPORTED)) {
  336         log_err(cd, _("Kernel does not support dm-verity signature option."));
  337         r = -ENOTSUP;
  338     }
  339     if (r < 0)
  340         goto out;
  341 
  342     r = dm_status_verity_ok(cd, name);
  343     if (r < 0)
  344         goto out;
  345 
  346     if (!r)
  347         log_err(cd, _("Verity device detected corruption after activation."));
  348 
  349     r = 0;
  350 out:
  351     dm_targets_free(cd, &dmd);
  352     return r;
  353 }