The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/security/mac_veriexec_parser/mac_veriexec_parser.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2019 Stormshield.
    3  * Copyright (c) 2019 Semihalf.
    4  *
    5  * Redistribution and use in source and binary forms, with or without
    6  * modification, are permitted provided that the following conditions
    7  * are met:
    8  * 1. Redistributions of source code must retain the above copyright
    9  *    notice, this list of conditions and the following disclaimer.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
   16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   17  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
   18  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
   20  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
   22  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   23  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   24  * POSSIBILITY OF SUCH DAMAGE.
   25  */
   26 
   27 #include <sys/cdefs.h>
   28 __FBSDID("$FreeBSD$");
   29 
   30 #include <sys/param.h>
   31 #include <sys/ctype.h>
   32 #include <sys/eventhandler.h>
   33 #include <sys/fcntl.h>
   34 #include <sys/lock.h>
   35 #include <sys/module.h>
   36 #include <sys/mutex.h>
   37 #include <sys/namei.h>
   38 #include <sys/proc.h>
   39 #include <sys/systm.h>
   40 #include <sys/vnode.h>
   41 
   42 #include <crypto/sha2/sha256.h>
   43 #include <crypto/sha2/sha384.h>
   44 #include <crypto/sha2/sha512.h>
   45 
   46 #include <security/mac_veriexec/mac_veriexec.h>
   47 #include <security/mac_veriexec/mac_veriexec_internal.h>
   48 
   49 /* The following are based on sbin/veriexec */
   50 struct fingerprint_type {
   51         const char      *fp_type;
   52         int             fp_size;
   53 };
   54 
   55 struct fp_flag {
   56         const char      *flag_name;
   57         int             flag;
   58 };
   59 
   60 static const struct fingerprint_type fp_table[] = {
   61         {"sha256=", SHA256_DIGEST_LENGTH},
   62 #if MAXFINGERPRINTLEN >= SHA384_DIGEST_LENGTH
   63         {"sha384=", SHA384_DIGEST_LENGTH},
   64 #endif
   65 #if MAXFINGERPRINTLEN >= SHA512_DIGEST_LENGTH
   66         {"sha512=", SHA512_DIGEST_LENGTH},
   67 #endif
   68         {NULL, 0}
   69 };
   70 
   71 static const struct fp_flag flags_table[] = {
   72         {"indirect",  VERIEXEC_INDIRECT},
   73         {"no_ptrace", VERIEXEC_NOTRACE},
   74         {"trusted",   VERIEXEC_TRUSTED},
   75         {"no_fips",   VERIEXEC_NOFIPS},
   76         {NULL, 0}
   77 };
   78 
   79 extern struct mtx ve_mutex;
   80 
   81 static unsigned char    hexchar_to_byte(unsigned char c);
   82 static int              hexstring_to_bin(unsigned char *buf);
   83 
   84 static int      get_flags(const char *entry);
   85 static int      get_fp(const char *entry, char **type,
   86                     unsigned char **digest, int *flags);
   87 static int      verify_digest(const char *data, size_t len,
   88                     const unsigned char *expected_hash);
   89 
   90 static int      open_file(const char *path, struct nameidata *nid);
   91 static char     *read_manifest(char *path, unsigned char *digest);
   92 static int      parse_entry(char *entry, char *prefix);
   93 static int      parse_manifest(char *path, unsigned char *hash, char *prefix);
   94 
   95 static unsigned char
   96 hexchar_to_byte(unsigned char c)
   97 {
   98 
   99         if (isdigit(c))
  100                 return (c - '');
  101 
  102         return (isupper(c) ? c - 'A' + 10 : c - 'a' + 10);
  103 }
  104 
  105 static int
  106 hexstring_to_bin(unsigned char *buf)
  107 {
  108         size_t          i, len;
  109         unsigned char   byte;
  110 
  111         len = strlen(buf);
  112         for (i = 0; i < len / 2; i++) {
  113                 if (!isxdigit(buf[2 * i]) || !isxdigit(buf[2 * i + 1]))
  114                         return (EINVAL);
  115 
  116                 byte = hexchar_to_byte(buf[2 * i]) << 4;
  117                 byte += hexchar_to_byte(buf[2 * i + 1]);
  118                 buf[i] = byte;
  119         }
  120         return (0);
  121 }
  122 
  123 static int
  124 get_flags(const char *entry)
  125 {
  126         int     i;
  127         int     result = 0;
  128 
  129         for (i = 0; flags_table[i].flag_name != NULL; i++)
  130                 if (strstr(entry, flags_table[i].flag_name) != NULL)
  131                         result |= flags_table[i].flag;
  132 
  133         return (result);
  134 }
  135 
  136 /*
  137  * Parse a single line of manifest looking for a digest and its type.
  138  * We expect it to be in form of "path shaX=hash".
  139  * The line will be split into path, hash type and hash value.
  140  */
  141 static int
  142 get_fp(const char *entry, char **type, unsigned char **digest, int *flags)
  143 {
  144         char    *delimiter;
  145         char    *local_digest;
  146         char    *fp_type;
  147         char    *prev_fp_type;
  148         size_t  min_len;
  149         int     i;
  150 
  151         delimiter = NULL;
  152         fp_type = NULL;
  153         prev_fp_type = NULL;
  154 
  155         for (i = 0; fp_table[i].fp_type != NULL; i++) {
  156                 fp_type = strstr(entry, fp_table[i].fp_type);
  157                 /* Look for the last "shaX=hash" in line */
  158                 while (fp_type != NULL) {
  159                         prev_fp_type = fp_type;
  160                         fp_type++;
  161                         fp_type = strstr(fp_type, fp_table[i].fp_type);
  162                 }
  163                 fp_type = prev_fp_type;
  164                 if (fp_type != NULL) {
  165                         if (fp_type == entry || fp_type[-1] != ' ')
  166                                 return (EINVAL);
  167 
  168                         /*
  169                          * The entry should contain at least
  170                          * fp_type and digest in hexadecimal form.
  171                          */
  172                         min_len = strlen(fp_table[i].fp_type) +
  173                                 2 * fp_table[i].fp_size;
  174 
  175                         if (strnlen(fp_type, min_len) < min_len)
  176                                 return (EINVAL);
  177 
  178                         local_digest = &fp_type[strlen(fp_table[i].fp_type)];
  179                         delimiter = &local_digest[2 * fp_table[i].fp_size];
  180 
  181                         /*
  182                          * Make sure that digest is followed by
  183                          * some kind of delimiter.
  184                          */
  185                         if (*delimiter != '\n' &&
  186                             *delimiter != '\0' &&
  187                             *delimiter != ' ')
  188                                 return (EINVAL);
  189 
  190                         /*
  191                          * Does the entry contain flags we need to parse?
  192                          */
  193                         if (*delimiter == ' ' && flags != NULL)
  194                                 *flags = get_flags(delimiter);
  195 
  196                         /*
  197                          * Split entry into three parts:
  198                          * path, fp_type and digest.
  199                          */
  200                         local_digest[-1] = '\0';
  201                         *delimiter = '\0';
  202                         fp_type[-1] = '\0';
  203                         break;
  204                 }
  205         }
  206 
  207         if (fp_type == NULL)
  208                 return (EINVAL);
  209 
  210         if (type != NULL)
  211                 *type = fp_type;
  212 
  213         if (digest != NULL)
  214                 *digest = local_digest;
  215 
  216         return (0);
  217 }
  218 
  219 /*
  220  * Currently we verify manifest using sha256.
  221  * In future another env with hash type could be introduced.
  222  */
  223 static int
  224 verify_digest(const char *data, size_t len, const unsigned char *expected_hash)
  225 {
  226         SHA256_CTX      ctx;
  227         unsigned char   hash[SHA256_DIGEST_LENGTH];
  228 
  229         SHA256_Init(&ctx);
  230         SHA256_Update(&ctx, data, len);
  231         SHA256_Final(hash, &ctx);
  232 
  233         return (memcmp(expected_hash, hash, SHA256_DIGEST_LENGTH));
  234 }
  235 
  236 static int
  237 open_file(const char *path, struct nameidata *nid)
  238 {
  239         int flags, rc;
  240 
  241         flags = FREAD;
  242 
  243         pwd_ensure_dirs();
  244 
  245         NDINIT(nid, LOOKUP, 0, UIO_SYSSPACE, path);
  246         rc = vn_open(nid, &flags, 0, NULL);
  247         NDFREE_PNBUF(nid);
  248         if (rc != 0)
  249                 return (rc);
  250 
  251         return (0);
  252 }
  253 
  254 /*
  255  * Read the manifest from location specified in path and verify its digest.
  256  */
  257 static char*
  258 read_manifest(char *path, unsigned char *digest)
  259 {
  260         struct nameidata        nid;
  261         struct vattr            va;
  262         char                    *data;
  263         ssize_t                 bytes_read, resid;
  264         int                     rc;
  265 
  266         data = NULL;
  267         bytes_read = 0;
  268 
  269         rc = open_file(path, &nid);
  270         if (rc != 0)
  271                 goto fail;
  272 
  273         rc = VOP_GETATTR(nid.ni_vp, &va, curthread->td_ucred);
  274         if (rc != 0)
  275                 goto fail;
  276 
  277         data = (char *)malloc(va.va_size + 1, M_VERIEXEC, M_WAITOK);
  278 
  279         while (bytes_read < va.va_size) {
  280                 rc = vn_rdwr(
  281                     UIO_READ, nid.ni_vp, data,
  282                     va.va_size - bytes_read, bytes_read,
  283                     UIO_SYSSPACE, IO_NODELOCKED,
  284                     curthread->td_ucred, NOCRED, &resid, curthread);
  285                 if (rc != 0)
  286                         goto fail;
  287 
  288                 bytes_read = va.va_size - resid;
  289         }
  290 
  291         data[bytes_read] = '\0';
  292 
  293         VOP_UNLOCK(nid.ni_vp);
  294         (void)vn_close(nid.ni_vp, FREAD, curthread->td_ucred, curthread);
  295 
  296         /*
  297          * If digest is wrong someone might be trying to fool us.
  298          */
  299         if (verify_digest(data, va.va_size, digest))
  300                 panic("Manifest hash doesn't match expected value!");
  301 
  302         return (data);
  303 
  304 fail:
  305         if (data != NULL)
  306                 free(data, M_VERIEXEC);
  307 
  308         return (NULL);
  309 }
  310 
  311 /*
  312  * Process single line.
  313  * First split it into path, digest_type and digest.
  314  * Then try to open the file and insert its fingerprint into metadata store.
  315  */
  316 static int
  317 parse_entry(char *entry, char *prefix)
  318 {
  319         struct nameidata        nid;
  320         struct vattr            va;
  321         char                    path[MAXPATHLEN];
  322         char                    *fp_type;
  323         unsigned char           *digest;
  324         int                     rc, is_exec, flags;
  325 
  326         fp_type = NULL;
  327         digest = NULL;
  328         flags = 0;
  329 
  330         rc = get_fp(entry, &fp_type, &digest, &flags);
  331         if (rc != 0)
  332                 return (rc);
  333 
  334         rc = hexstring_to_bin(digest);
  335         if (rc != 0)
  336                 return (rc);
  337 
  338         if (strnlen(entry, MAXPATHLEN) == MAXPATHLEN)
  339                 return (EINVAL);
  340 
  341         /* If the path is not absolute prepend it with a prefix */
  342         if (prefix != NULL && entry[0] != '/') {
  343                 rc = snprintf(path, MAXPATHLEN, "%s/%s",
  344                             prefix, entry);
  345                 if (rc < 0)
  346                         return (-rc);
  347         } else {
  348                 strcpy(path, entry);
  349         }
  350 
  351         rc = open_file(path, &nid);
  352         NDFREE_PNBUF(&nid);
  353         if (rc != 0)
  354                 return (rc);
  355 
  356         rc = VOP_GETATTR(nid.ni_vp, &va, curthread->td_ucred);
  357         if (rc != 0)
  358                 goto out;
  359 
  360         is_exec = (va.va_mode & VEXEC);
  361 
  362         mtx_lock(&ve_mutex);
  363         rc = mac_veriexec_metadata_add_file(
  364             is_exec == 0,
  365             va.va_fsid, va.va_fileid, va.va_gen,
  366             digest,
  367             NULL, 0,
  368             flags, fp_type, 1);
  369         mtx_unlock(&ve_mutex);
  370 
  371 out:
  372         VOP_UNLOCK(nid.ni_vp);
  373         vn_close(nid.ni_vp, FREAD, curthread->td_ucred, curthread);
  374         return (rc);
  375 }
  376 
  377 /*
  378  * Look for manifest in env that have beed passed by loader.
  379  * This routine should be called right after the rootfs is mounted.
  380  */
  381 static int
  382 parse_manifest(char *path, unsigned char *hash, char *prefix)
  383 {
  384         char    *data;
  385         char    *entry;
  386         char    *next_entry;
  387         int     rc, success_count;
  388 
  389         data = NULL;
  390         success_count = 0;
  391         rc = 0;
  392 
  393         data = read_manifest(path, hash);
  394         if (data == NULL) {
  395                 rc = EIO;
  396                 goto out;
  397         }
  398 
  399         entry = data;
  400         while (entry != NULL) {
  401                 next_entry = strchr(entry, '\n');
  402                 if (next_entry != NULL) {
  403                         *next_entry = '\0';
  404                         next_entry++;
  405                 }
  406                 if (entry[0] == '\n' || entry[0] == '\0') {
  407                         entry = next_entry;
  408                         continue;
  409                 }
  410                 if ((rc = parse_entry(entry, prefix)))
  411                         printf("mac_veriexec_parser: Warning: Failed to parse"
  412                                " entry with rc:%d, entry:\"%s\"\n", rc, entry);
  413                 else
  414                         success_count++;
  415 
  416                 entry = next_entry;
  417         }
  418         rc = 0;
  419 
  420 out:
  421         if (data != NULL)
  422                 free(data, M_VERIEXEC);
  423 
  424         if (success_count == 0)
  425                 rc = EINVAL;
  426 
  427         return (rc);
  428 }
  429 
  430 static void
  431 parse_manifest_event(void *dummy)
  432 {
  433         char            *manifest_path;
  434         char            *manifest_prefix;
  435         unsigned char   *manifest_hash;
  436         int             rc;
  437 
  438         /* If the envs are not set fail silently */
  439         manifest_path = kern_getenv("veriexec.manifest_path");
  440         if (manifest_path == NULL)
  441                 return;
  442 
  443         manifest_hash = kern_getenv("veriexec.manifest_hash");
  444         if (manifest_hash == NULL) {
  445                 freeenv(manifest_path);
  446                 return;
  447         }
  448 
  449         manifest_prefix = kern_getenv("veriexec.manifest_prefix");
  450 
  451         if (strlen(manifest_hash) != 2 * SHA256_DIGEST_LENGTH)
  452                 panic("veriexec.manifest_hash has incorrect size");
  453 
  454         rc = hexstring_to_bin(manifest_hash);
  455         if (rc != 0)
  456                 panic("mac_veriexec: veriexec.loader.manifest_hash"
  457                     " doesn't contain a hash in hexadecimal form");
  458 
  459         rc = parse_manifest(manifest_path, manifest_hash, manifest_prefix);
  460         if (rc != 0)
  461                 panic("mac_veriexec: Failed to parse manifest err=%d", rc);
  462 
  463         mtx_lock(&ve_mutex);
  464         mac_veriexec_set_state(
  465             VERIEXEC_STATE_LOADED | VERIEXEC_STATE_ACTIVE |
  466             VERIEXEC_STATE_LOCKED | VERIEXEC_STATE_ENFORCE);
  467         mtx_unlock(&ve_mutex);
  468 
  469         freeenv(manifest_path);
  470         freeenv(manifest_hash);
  471         if (manifest_prefix != NULL)
  472                 freeenv(manifest_prefix);
  473 }
  474 
  475 EVENTHANDLER_DEFINE(mountroot, parse_manifest_event, NULL, 0);

Cache object: 2de97596d662f155859d631f5e8cca2e


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.