Print this page
3364 dboot should check boot archive integrity
Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com>
Reviewed by: Dan McDonald <danmcd@nexenta.com>
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Reviewed by: Garrett D'Amore <garrett@damore.org>

Split Close
Expand all
Collapse all
          --- old/usr/src/uts/i86pc/dboot/dboot_startkern.c
          +++ new/usr/src/uts/i86pc/dboot/dboot_startkern.c
↓ open down ↓ 14 lines elided ↑ open up ↑
  15   15   * If applicable, add the following below this CDDL HEADER, with the
  16   16   * fields enclosed by brackets "[]" replaced with your own identifying
  17   17   * information: Portions Copyright [yyyy] [name of copyright owner]
  18   18   *
  19   19   * CDDL HEADER END
  20   20   */
  21   21  
  22   22  /*
  23   23   * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24   24   * Use is subject to license terms.
       25 + *
       26 + * Copyright 2012 Joyent, Inc.  All rights reserved.
  25   27   */
  26   28  
  27   29  
  28   30  #include <sys/types.h>
  29   31  #include <sys/machparam.h>
  30   32  #include <sys/x86_archext.h>
  31   33  #include <sys/systm.h>
  32   34  #include <sys/mach_mmu.h>
  33   35  #include <sys/multiboot.h>
       36 +#include <sys/sha1.h>
  34   37  
  35   38  #if defined(__xpv)
  36   39  
  37   40  #include <sys/hypervisor.h>
  38   41  uintptr_t xen_virt_start;
  39   42  pfn_t *mfn_to_pfn_mapping;
  40   43  
  41   44  #else /* !__xpv */
  42   45  
  43   46  extern multiboot_header_t mb_header;
↓ open down ↓ 4 lines elided ↑ open up ↑
  48   51  #include <sys/inttypes.h>
  49   52  #include <sys/bootinfo.h>
  50   53  #include <sys/mach_mmu.h>
  51   54  #include <sys/boot_console.h>
  52   55  
  53   56  #include "dboot_asm.h"
  54   57  #include "dboot_printf.h"
  55   58  #include "dboot_xboot.h"
  56   59  #include "dboot_elfload.h"
  57   60  
       61 +#define SHA1_ASCII_LENGTH       (SHA1_DIGEST_LENGTH * 2)
       62 +
  58   63  /*
  59   64   * This file contains code that runs to transition us from either a multiboot
  60   65   * compliant loader (32 bit non-paging) or a XPV domain loader to
  61   66   * regular kernel execution. Its task is to setup the kernel memory image
  62   67   * and page tables.
  63   68   *
  64   69   * The code executes as:
  65   70   *      - 32 bits under GRUB (for 32 or 64 bit Solaris)
  66   71   *      - a 32 bit program for the 32-bit PV hypervisor
  67   72   *      - a 64 bit program for the 64-bit PV hypervisor (at least for now)
↓ open down ↓ 692 lines elided ↑ open up ↑
 760  765                  /*LINTED: constant in conditional context*/
 761  766                  set_xen_guest_handle(map.buffer, map_buffer);
 762  767                  if (HYPERVISOR_memory_op(XENMEM_machine_memory_map, &map) != 0)
 763  768                          dboot_panic("getting XENMEM_machine_memory_map failed");
 764  769                  build_pcimemlists(map_buffer, map.nr_entries);
 765  770          }
 766  771  }
 767  772  
 768  773  #else   /* !__xpv */
 769  774  
      775 +static uint8_t
      776 +dboot_a2h(char v)
      777 +{
      778 +        if (v >= 'a')
      779 +                return (v - 'a' + 0xa);
      780 +        else if (v >= 'A')
      781 +                return (v - 'A' + 0xa);
      782 +        else if (v >= '0')
      783 +                return (v - '0');
      784 +        else
      785 +                dboot_panic("bad ASCII hex character %c\n", v);
      786 +
      787 +        return (0);
      788 +}
      789 +
      790 +static void
      791 +digest_a2h(const char *ascii, uint8_t *digest)
      792 +{
      793 +        unsigned int i;
      794 +
      795 +        for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
      796 +                digest[i] = dboot_a2h(ascii[i * 2]) << 4;
      797 +                digest[i] |= dboot_a2h(ascii[i * 2 + 1]);
      798 +        }
      799 +}
      800 +
 770  801  /*
      802 + * Generate a SHA-1 hash of the first len bytes of image, and compare it with
      803 + * the ASCII-format hash found in the 40-byte buffer at ascii.  If they
      804 + * match, return 0, otherwise -1.  This works only for images smaller than
      805 + * 4 GB, which should not be a problem.
      806 + */
      807 +static int
      808 +check_image_hash(const char *ascii, const void *image, size_t len)
      809 +{
      810 +        SHA1_CTX ctx;
      811 +        uint8_t digest[SHA1_DIGEST_LENGTH];
      812 +        uint8_t baseline[SHA1_DIGEST_LENGTH];
      813 +        unsigned int i;
      814 +
      815 +        digest_a2h(ascii, baseline);
      816 +
      817 +        SHA1Init(&ctx);
      818 +        SHA1Update(&ctx, image, len);
      819 +        SHA1Final(digest, &ctx);
      820 +
      821 +        for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
      822 +                if (digest[i] != baseline[i])
      823 +                        return (-1);
      824 +        }
      825 +
      826 +        return (0);
      827 +}
      828 +
      829 +static void
      830 +check_images(void)
      831 +{
      832 +        int i;
      833 +        char *hashes;
      834 +        mb_module_t *mod, *hashmod;
      835 +        char *hash;
      836 +        char displayhash[SHA1_ASCII_LENGTH + 1];
      837 +        size_t hashlen;
      838 +        size_t len;
      839 +
      840 +        /*
      841 +         * A brief note on lengths and sizes: GRUB, for reasons unknown, passes
      842 +         * the address of the last valid byte in a module plus 1 as mod_end.
      843 +         * This is of course a bug; the multiboot specification simply states
      844 +         * that mod_start and mod_end "contain the start and end addresses of
      845 +         * the boot module itself" which is pretty obviously not what GRUB is
      846 +         * doing.  However, fixing it requires that not only this code be
      847 +         * changed but also that other code consuming this value and values
      848 +         * derived from it be fixed, and that the kernel and GRUB must either
      849 +         * both have the bug or neither.  While there are a lot of combinations
      850 +         * that will work, there are also some that won't, so for simplicity
      851 +         * we'll just cope with the bug.  That means we won't actually hash the
      852 +         * byte at mod_end, and we will expect that mod_end for the hash file
      853 +         * itself is one greater than some multiple of 41 (40 bytes of ASCII
      854 +         * hash plus a newline for each module).
      855 +         */
      856 +
      857 +        if (mb_info->mods_count > 1) {
      858 +                mod = (mb_module_t *)mb_info->mods_addr;
      859 +                hashmod = mod + (mb_info->mods_count - 1);
      860 +                hashes = (char *)hashmod->mod_start;
      861 +                hashlen = (size_t)(hashmod->mod_end - hashmod->mod_start);
      862 +                hash = hashes;
      863 +                if (prom_debug) {
      864 +                        dboot_printf("Hash module found at %lx size %lx\n",
      865 +                            (ulong_t)hashes, (ulong_t)hashlen);
      866 +                }
      867 +        } else {
      868 +                DBG_MSG("Skipping hash check; no hash module found.\n");
      869 +                return;
      870 +        }
      871 +
      872 +        for (mod = (mb_module_t *)(mb_info->mods_addr), i = 0;
      873 +            i < mb_info->mods_count - 1; ++mod, ++i) {
      874 +                if ((hash - hashes) + SHA1_ASCII_LENGTH + 1 > hashlen) {
      875 +                        dboot_printf("Short hash module of length 0x%lx bytes; "
      876 +                            "skipping hash checks\n", (ulong_t)hashlen);
      877 +                        break;
      878 +                }
      879 +
      880 +                (void) memcpy(displayhash, hash, SHA1_ASCII_LENGTH);
      881 +                displayhash[SHA1_ASCII_LENGTH] = '\0';
      882 +                if (prom_debug) {
      883 +                        dboot_printf("Checking hash for module %d [%s]: ",
      884 +                            i, displayhash);
      885 +                }
      886 +
      887 +                len = mod->mod_end - mod->mod_start;    /* see above */
      888 +                if (check_image_hash(hash, (void *)mod->mod_start, len) != 0) {
      889 +                        dboot_panic("SHA-1 hash mismatch on %s; expected %s\n",
      890 +                            (char *)mod->mod_name, displayhash);
      891 +                } else {
      892 +                        DBG_MSG("OK\n");
      893 +                }
      894 +                hash += SHA1_ASCII_LENGTH + 1;
      895 +        }
      896 +}
      897 +
      898 +/*
 771  899   * During memory allocation, find the highest address not used yet.
 772  900   */
 773  901  static void
 774  902  check_higher(paddr_t a)
 775  903  {
 776  904          if (a < next_avail_addr)
 777  905                  return;
 778  906          next_avail_addr = RNDUP(a + 1, MMU_PAGESIZE);
 779  907          DBG(next_avail_addr);
 780  908  }
↓ open down ↓ 25 lines elided ↑ open up ↑
 806  934          /*
 807  935           * search the modules to find the last used address
 808  936           * we'll build the module list while we're walking through here
 809  937           */
 810  938          DBG_MSG("\nFinding Modules\n");
 811  939          check_higher((paddr_t)(uintptr_t)&_end);
 812  940          for (mod = (mb_module_t *)(mb_info->mods_addr), i = 0;
 813  941              i < mb_info->mods_count;
 814  942              ++mod, ++i) {
 815  943                  if (prom_debug) {
 816      -                        dboot_printf("\tmodule #%d: %s at: 0x%lx, len 0x%lx\n",
      944 +                        dboot_printf("\tmodule #%d: %s at: 0x%lx, end 0x%lx\n",
 817  945                              i, (char *)(mod->mod_name),
 818  946                              (ulong_t)mod->mod_start, (ulong_t)mod->mod_end);
 819  947                  }
 820  948                  modules[i].bm_addr = mod->mod_start;
 821  949                  if (mod->mod_start > mod->mod_end) {
 822  950                          dboot_panic("module[%d]: Invalid module start address "
 823  951                              "(0x%llx)", i, (uint64_t)mod->mod_start);
 824  952                  }
 825  953                  modules[i].bm_size = mod->mod_end - mod->mod_start;
 826  954  
 827  955                  check_higher(mod->mod_end);
 828  956          }
 829  957          bi->bi_modules = (native_ptr_t)(uintptr_t)modules;
 830  958          DBG(bi->bi_modules);
 831  959          bi->bi_module_cnt = mb_info->mods_count;
 832  960          DBG(bi->bi_module_cnt);
 833  961  
      962 +        check_images();
      963 +
 834  964          /*
 835  965           * Walk through the memory map from multiboot and build our memlist
 836  966           * structures. Note these will have native format pointers.
 837  967           */
 838  968          DBG_MSG("\nFinding Memory Map\n");
 839  969          DBG(mb_info->flags);
 840  970          max_mem = 0;
 841  971          if (mb_info->flags & 0x40) {
 842  972                  int cnt = 0;
 843  973  
↓ open down ↓ 609 lines elided ↑ open up ↑
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX