[BACK]Return to verified_exec.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / dev

Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.

Diff for /src/sys/dev/Attic/verified_exec.c between version 1.5 and 1.5.2.14

version 1.5, 2005/02/27 00:26:58 version 1.5.2.14, 2005/08/15 12:38:03
Line 1 
Line 1 
 /*      $NetBSD$        */  /*      $NetBSD$        */
   
 /*-  /*-
  * Copyright (c) 1998-1999 Brett Lymn   * Copyright 2005 Elad Efrat <elad@bsd.org.il>
  *                         (blymn@baea.com.au, brett_lymn@yahoo.com.au)   * Copyright 2005 Brett Lymn <blymn@netbsd.org>
  * All rights reserved.  
  *   *
  * This code has been donated to The NetBSD Foundation by the Author.   * This code is derived from software contributed to The NetBSD Foundation
    * by Brett Lymn and Elad Efrat
  *   *
  * Redistribution and use in source and binary forms, with or without   * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions   * modification, are permitted provided that the following conditions
  * are met:   * are met:
  * 1. Redistributions of source code must retain the above copyright   * 1. Redistributions of source code must retain the above copyright
  *    notice, this list of conditions and the following disclaimer.   *    notice, this list of conditions and the following disclaimer.
  * 2. The name of the author may not be used to endorse or promote products   * 2. Neither the name of The NetBSD Foundation nor the names of its
  *    derived from this software withough specific prior written permission   *    contributors may be used to endorse or promote products derived
  *   *    from this software without specific prior written permission.
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR  
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES  
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,  
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT  
  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,  
  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY  
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF  
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
  *  
  *   *
    * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
    * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
    * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
    * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    * POSSIBILITY OF SUCH DAMAGE.
  */   */
   
 #include <sys/cdefs.h>  #include <sys/cdefs.h>
   #if defined(__NetBSD__)
 __KERNEL_RCSID(0, "$NetBSD$");  __KERNEL_RCSID(0, "$NetBSD$");
   #else
   __RCSID("$Id$\n$NetBSD$");
   #endif
   
 #include <sys/param.h>  #include <sys/param.h>
 #include <sys/systm.h>  #include <sys/systm.h>
 #include <sys/proc.h>  #include <sys/proc.h>
 #include <sys/errno.h>  #include <sys/errno.h>
 #include <sys/verified_exec.h>  
 #include <sys/buf.h>  #include <sys/buf.h>
 #include <sys/malloc.h>  #include <sys/malloc.h>
   
   #ifdef __FreeBSD__
   #include <sys/kernel.h>
   #include <sys/device_port.h>
   #include <sys/ioccom.h>
   #else
 #include <sys/ioctl.h>  #include <sys/ioctl.h>
 #include <sys/device.h>  #include <sys/device.h>
   #define DEVPORT_DEVICE struct device
   #endif
   
 #include <sys/conf.h>  #include <sys/conf.h>
 #include <sys/lock.h>  #include <sys/lock.h>
 #include <sys/queue.h>  #include <sys/queue.h>
 #include <sys/vnode.h>  #include <sys/vnode.h>
 #include <sys/fcntl.h>  #include <sys/fcntl.h>
 #include <sys/namei.h>  #include <sys/namei.h>
   #include <sys/sysctl.h>
   #define VERIEXEC_NEED_NODE
   #include <sys/verified_exec.h>
   
 struct verified_exec_softc {  /* count of number of times device is open (we really only allow one open) */
         struct device   veriexec_dev;  static unsigned int veriexec_dev_usage;
 };  
   
 const struct cdevsw verifiedexec_cdevsw = {  struct veriexec_softc {
         verifiedexecopen, verifiedexecclose, noread, nowrite,          DEVPORT_DEVICE veriexec_dev;
         verifiedexecioctl, nostop, notty, nopoll, nommap, nokqfilter,  
 };  };
   
 /* internal structures */  #if defined(__FreeBSD__)
 LIST_HEAD(veriexec_devhead, veriexec_dev_list) veriexec_dev_head;  # define CDEV_MAJOR 216
 /*LIST_HEAD(veriexec_file_devhead, veriexec_dev_list) veriexec_file_dev_head;*/  # define BDEV_MAJOR -1
 struct veriexec_devhead veriexec_file_dev_head;  #endif
   
   const struct cdevsw veriexec_cdevsw = {
           veriexecopen,
           veriexecclose,
           noread,
           nowrite,
           veriexecioctl,
   #ifdef __NetBSD__
           nostop,
           notty,
   #endif
           nopoll,
           nommap,
   #if defined(__NetBSD__)
          nokqfilter,
   #elif defined(__FreeBSD__)
          nostrategy,
          "veriexec",
          CDEV_MAJOR,
          nodump,
          nopsize,
          0,                              /* flags */
          BDEV_MAJOR
   #endif
   };
   
 /* Autoconfiguration glue */  /* Autoconfiguration glue */
 void    verifiedexecattach(struct device *parent, struct device *self,  void    veriexecattach(DEVPORT_DEVICE *parent, DEVPORT_DEVICE *self,
                          void *aux);                          void *aux);
 int     verifiedexecopen(dev_t dev, int flags, int fmt, struct proc *p);  int     veriexecopen(dev_t dev, int flags, int fmt, struct proc *p);
 int     verifiedexecclose(dev_t dev, int flags, int fmt, struct proc *p);  int     veriexecclose(dev_t dev, int flags, int fmt, struct proc *p);
 int     verifiedexecioctl(dev_t dev, u_long cmd, caddr_t data, int flags,  int     veriexecioctl(dev_t dev, u_long cmd, caddr_t data, int flags,
                         struct proc *p);                         struct proc *p);
 void    add_veriexec_inode(struct veriexec_dev_list *list, unsigned long inode,  
                         unsigned char fingerprint[MAXFINGERPRINTLEN],  
                         unsigned char type, unsigned char fp_type);  
 struct veriexec_dev_list *find_veriexec_dev(unsigned long dev,  
                                       struct veriexec_devhead *head);  
   
 /*  
  * Attach for autoconfig to find.  Initialise the lists and return...  
  */  
 void  void
 verifiedexecattach(struct device *parent, struct device *self, void *aux)  veriexecattach(DEVPORT_DEVICE *parent, DEVPORT_DEVICE *self,
                      void *aux)
 {  {
         LIST_INIT(&veriexec_dev_head);          veriexec_dev_usage = 0;
         LIST_INIT(&veriexec_file_dev_head);  
 }  
   
 int          if (veriexec_verbose >= 2)
 verifiedexecopen(dev_t dev, int flags, int fmt, struct proc *p)                  printf("Veriexec: veriexecattach: Veriexec pseudo-device"
 {                         "attached.\n");
         return 0;  
 }  }
   
 int  int
 verifiedexecclose(dev_t dev, int flags, int fmt, struct proc *p)  veriexecopen(dev_t dev __unused, int flags __unused,
                    int fmt __unused, struct proc *p __unused)
 {  {
 #ifdef VERIFIED_EXEC_DEBUG_VERBOSE          if (veriexec_verbose >= 2) {
         struct veriexec_dev_list *lp;                  printf("Veriexec: veriexecopen: Veriexec load device "
         struct veriexec_inode_list *ip;                         "open attempt by uid=%u, pid=%u. (dev=%d)\n",
                          p->p_ucred->cr_uid, p->p_pid, dev);
         printf("Loaded exec fingerprint list is:\n");  
         for (lp = LIST_FIRST(&veriexec_dev_head); lp != NULL;  
              lp = LIST_NEXT(lp, entries)) {  
                 for (ip = LIST_FIRST(&(lp->inode_head)); ip != NULL;  
                      ip = LIST_NEXT(ip, entries)) {  
                         printf("Got loaded fingerprint for dev %lu, inode %lu\n",  
                                lp->id, ip->inode);  
                 }  
         }          }
   
         printf("\n\nLoaded file fingerprint list is:\n");          if (suser(p->p_ucred, &p->p_acflag) != 0)
         for (lp = LIST_FIRST(&veriexec_file_dev_head); lp != NULL;                  return (EPERM);
              lp = LIST_NEXT(lp, entries)) {  
                 for (ip = LIST_FIRST(&(lp->inode_head)); ip != NULL;  
                      ip = LIST_NEXT(ip, entries)) {  
                         printf("Got loaded fingerprint for dev %lu, inode %lu\n",  
                                lp->id, ip->inode);  
                 }  
         }  
 #endif  
         return 0;  
 }  
   
 /*          if (veriexec_dev_usage > 0) {
  * Search the list of devices looking for the one given.  If it is not                  if (veriexec_verbose >= 2)
  * in the list then add it.                          printf("Veriexec: load device already in use.\n");
  */  
 struct veriexec_dev_list *  
 find_veriexec_dev(unsigned long dev, struct veriexec_devhead *head)  
 {  
         struct veriexec_dev_list *lp;  
   
         for (lp = LIST_FIRST(head); lp != NULL;                  return(EBUSY);
              lp = LIST_NEXT(lp, entries))  
                 if (lp->id == dev) break;  
   
         if (lp == NULL) {  
                   /* if pointer is null then entry not there, add a new one */  
                 MALLOC(lp, struct veriexec_dev_list *,  
                        sizeof(struct veriexec_dev_list), M_TEMP, M_WAITOK);  
                 LIST_INIT(&(lp->inode_head));  
                 lp->id = dev;  
                 LIST_INSERT_HEAD(head, lp, entries);  
         }          }
   
         return lp;          veriexec_dev_usage++;
           return (0);
 }  }
   
 /*  int
  * Add a file's inode and fingerprint to the list of inodes attached  veriexecclose(dev_t dev __unused, int flags __unused,
  * to the device id.  Only add the entry if it is not already on the                    int fmt __unused, struct proc *p __unused)
  * list.  
  */  
 void  
 add_veriexec_inode(struct veriexec_dev_list *list, unsigned long inode,  
                 unsigned char fingerprint[MAXFINGERPRINTLEN],  
                 unsigned char type, unsigned char fp_type)  
 {  {
         struct veriexec_inode_list *ip;          if (veriexec_dev_usage > 0)
                   veriexec_dev_usage--;
         for (ip = LIST_FIRST(&(list->inode_head)); ip != NULL;          return (0);
              ip = LIST_NEXT(ip, entries))  
                   /* check for a dupe inode in the list, skip if an entry  
                    * exists for this inode except for when the type is  
                    * VERIEXEC_INDIRECT, always set the type when it is so  
                    * we don't get a hole caused by conflicting types on  
                    * hardlinked files.  XXX maybe we should validate  
                    * fingerprint is same and complain if it is not...  
                    */  
                 if (ip->inode == inode) {  
                         if (type == VERIEXEC_INDIRECT)  
                                 ip->type = type;  
                         return;  
                 }  
   
   
         MALLOC(ip, struct veriexec_inode_list *, sizeof(struct veriexec_inode_list),  
                M_TEMP, M_WAITOK);  
         ip->type = type;  
         ip->fp_type = fp_type;  
         ip->inode = inode;  
         memcpy(ip->fingerprint, fingerprint, sizeof(ip->fingerprint));  
         LIST_INSERT_HEAD(&(list->inode_head), ip, entries);  
 }  }
   
 /*  
  * Handle the ioctl for the device  
  */  
 int  int
 verifiedexecioctl(dev_t dev, u_long cmd, caddr_t data, int flags,  veriexecioctl(dev_t dev __unused, u_long cmd, caddr_t data,
                 struct proc *p)                    int flags __unused, struct proc *p)
 {  {
         int error = 0;          struct veriexec_hashtbl *tbl;
         struct verified_exec_params *params = (struct verified_exec_params *)data;  
         struct nameidata nid;          struct nameidata nid;
         struct vattr vattr;          struct vattr va;
         struct veriexec_dev_list *dlp;          int error = 0;
           u_long hashmask;
   
 #ifdef VERIFIED_EXEC_DEBUG          if (veriexec_strict > 0) {
         printf("veriexec_ioctl: got cmd 0x%lx for file %s\n", cmd, params->file);                  printf("Veriexec: veriexecioctl: Strict mode, modifying "
 #endif                         "veriexec tables is not permitted.\n");
   
                   return (EPERM);
           }
   
         switch (cmd) {          switch (cmd) {
           case VERIEXECLOAD:          case VERIEXEC_TABLESIZE: {
                 if (securelevel > 0) {                  struct veriexec_sizing_params *params =
                           /* don't allow updates when secure */                          (struct veriexec_sizing_params *) data;
                         error = EPERM;                  u_char node_name[16];
                 } else {  
                           /*                  /* Check for existing table for device. */
                            * Get the attributes for the file name passed                  if (veriexec_tblfind(params->dev) != NULL)
                            * stash the file's device id and inode number                          return (EEXIST);
                            * along with it's fingerprint in a list for  
                            * exec to use later.                  /* Allocate and initialize a Veriexec hash table. */
                            */                  tbl = malloc(sizeof(struct veriexec_hashtbl), M_TEMP,
                         NDINIT(&nid, LOOKUP, FOLLOW, UIO_USERSPACE,                               M_WAITOK);
                                params->file, p);                  tbl->hash_size = params->hash_size;
                         if ((error = vn_open(&nid, FREAD, 0)) != 0) {                  tbl->hash_dev = params->dev;
                                 return(error);                  tbl->hash_tbl = hashinit(params->hash_size, HASH_LIST, M_TEMP,
                         }                                           M_WAITOK, &hashmask);
                         error = VOP_GETATTR(nid.ni_vp, &vattr, p->p_ucred, p);                  tbl->hash_count = 0;
                         if (error) {  
                                 nid.ni_vp->fp_status = FINGERPRINT_INVALID;                  LIST_INSERT_HEAD(&veriexec_tables, tbl, hash_list);
                                 VOP_UNLOCK(nid.ni_vp, 0);  
                                 (void) vn_close(nid.ni_vp, FREAD,                  snprintf(node_name, sizeof(node_name), "dev_%u",
                                                 p->p_ucred, p);                           tbl->hash_dev);
                                 return(error);  
                         }                  sysctl_createv(NULL, 0, &veriexec_count_node, NULL,
                         /* invalidate the node fingerprint status                                 CTLFLAG_READONLY, CTLTYPE_QUAD, node_name,
                          * which will have been set in the vn_open                                 NULL, NULL, 0, &tbl->hash_count, 0,
                          * and would always be FINGERPRINT_NOTFOUND                                 tbl->hash_dev, CTL_EOL);
   
                   break;
                   }
   
           case VERIEXEC_LOAD: {
                   struct veriexec_params *params =
                           (struct veriexec_params *) data;
                   struct veriexec_hash_entry *hh;
                   struct veriexec_hash_entry *e;
   
                   NDINIT(&nid, LOOKUP, FOLLOW, UIO_SYSSPACE, params->file, p);
                   error = namei(&nid);
                   if (error)
                           return (error);
   
                   /* Add only regular files. */
                   if (nid.ni_vp->v_type != VREG) {
                           printf("Veriexec: veriexecioctl: Not adding \"%s\": "
                               "Not a regular file.\n", params->file);
                           vrele(nid.ni_vp);
                           return (EINVAL);
                   }
   
                   /* Get attributes for device and inode. */
                   error = VOP_GETATTR(nid.ni_vp, &va, p->p_ucred, p);
                   if (error)
                           return (error);
   
                   /* Release our reference to the vnode. (namei) */
                   vrele(nid.ni_vp);
   
                   /* Get table for the device. */
                   tbl = veriexec_tblfind(va.va_fsid);
                   if (tbl == NULL) {
                           return (EINVAL);
                   }
   
                   hh = veriexec_lookup(va.va_fsid, va.va_fileid);
                   if (hh != NULL) {
                           /*
                            * Duplicate entry means something is wrong in
                            * the signature file. Just give collision info
                            * and return.
                          */                           */
                         nid.ni_vp->fp_status = FINGERPRINT_INVALID;                          printf("veriexec: Duplicate entry. [%s, %ld:%lu] "
                         VOP_UNLOCK(nid.ni_vp, 0);                                 "old[type=0x%02x, algorithm=%s], "
                         (void) vn_close(nid.ni_vp, FREAD, p->p_ucred, p);                                 "new[type=0x%02x, algorithm=%s] "
                           /* vattr.va_fsid = dev, vattr.va_fileid = inode */                                 "(%s fingerprint)\n",
                         if (params->type == VERIEXEC_FILE) {                                 params->file, va.va_fsid, va.va_fileid,
                                 dlp = find_veriexec_dev(vattr.va_fsid,                                 hh->type, hh->ops->type,
                                                      &veriexec_file_dev_head);                                 params->type, params->fp_type,
                         } else {                                 (((hh->ops->hash_len != params->size) ||
                                 dlp = find_veriexec_dev(vattr.va_fsid,                                  (memcmp(hh->fp, params->fingerprint,
                                                      &veriexec_dev_head);                                          min(hh->ops->hash_len, params->size))
                         }                                          != 0)) ? "different" : "same"));
   
                         add_veriexec_inode(dlp, vattr.va_fileid,                          return (0);
                                         params->fingerprint, params->type,                  }
                                         params->fp_type);  
                   e = malloc(sizeof(*e), M_TEMP, M_WAITOK);
                   e->inode = va.va_fileid;
                   e->type = params->type;
                   e->status = FINGERPRINT_NOTEVAL;
                   if ((e->ops = veriexec_find_ops(params->fp_type)) == NULL) {
                           free(e, M_TEMP);
                           printf("Veriexec: veriexecioctl: Invalid or unknown "
                                  "fingerprint type \"%s\" for file \"%s\" "
                                  "(dev=%ld, inode=%ld)\n", params->fp_type,
                                  params->file, va.va_fsid, va.va_fileid);
                           return(EINVAL);
                 }                  }
   
                   /*
                    * Just a bit of a sanity check - require the size of
                    * the fp to be passed in, check this against the expected
                    * size.  Of course userland could lie deliberately, this
                    * really only protects against the obvious fumble of
                    * changing the fp type but not updating the fingerprint
                    * string.
                    */
                   if (e->ops->hash_len != params->size) {
                           printf("Veriexec: veriexecioctl: Inconsistent "
                                  "fingerprint size for type \"%s\" for file "
                                  "\"%s\" (dev=%ld, inode=%ld), size was %u "
                                  "was expecting %d\n", params->fp_type,
                                  params->file, va.va_fsid, va.va_fileid,
                                  params->size, e->ops->hash_len);
                           free(e, M_TEMP);
                           return(EINVAL);
                   }
   
                   e->fp = malloc(e->ops->hash_len, M_TEMP, M_WAITOK);
                   memcpy(e->fp, params->fingerprint, e->ops->hash_len);
   
                   veriexec_report("New entry.", params->file, &va, NULL,
                                   REPORT_VERBOSE_HIGH, REPORT_NOALARM,
                                   REPORT_NOPANIC);
   
                   error = veriexec_hashadd(tbl, e);
   
                 break;                  break;
                   }
   
         default:          default:
                   /* Invalid operation. */
                 error = ENODEV;                  error = ENODEV;
   
                   break;
         }          }
   
         return (error);          return (error);
 }  }
   
   #if defined(__FreeBSD__)
   static void
   veriexec_drvinit(void *unused __unused)
   {
           make_dev(&verifiedexec_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
                    "veriexec");
           verifiedexecattach(0, 0, 0);
   }
   
   SYSINIT(veriexec, SI_SUB_PSEUDO, SI_ORDER_ANY, veriexec_drvinit, NULL);
   #endif

Legend:
Removed from v.1.5  
changed lines
  Added in v.1.5.2.14

CVSweb <webmaster@jp.NetBSD.org>