]> sourceware.org Git - systemtap.git/commitdiff
2009-08-06 Dave Brolley <brolley@redhat.com>
authorDave Brolley <brolley@redhat.com>
Thu, 6 Aug 2009 16:25:50 +0000 (12:25 -0400)
committerDave Brolley <brolley@redhat.com>
Thu, 6 Aug 2009 16:25:50 +0000 (12:25 -0400)
        * modverify.c (staprun.h): #include it.
        (verify_it): Now accepts module data and signature data as arguments.
        Don't open and read the signature here. Don't read the module here.
        (verify_module): Now accepts module data as argument. Read the signature

        once here.
        * modverify.h (verify_module): Now accepts module data as argument.
        * staprun.c (main): Don't call check_permissions here.
        * staprun.h (check_permissions): Prototype removed.
        * staprun_funcs.c (check_permissions): Now static. Accepts module data
        as argument. Pass module data to check_signature.
        (insert_module): Canonicalize the module path early here. Call
        check_permissions here, passing it the mapped module data.
        (check_signature): Now accepts module data as argument. Pass the module
        data to verify_module.
        (check_path): Use the already-canonicalized module path.

runtime/staprun/modverify.c
runtime/staprun/modverify.h
runtime/staprun/staprun.c
runtime/staprun/staprun.h
runtime/staprun/staprun_funcs.c

index f4b15ac3d4a566c619acf8bb1283129cb1403d5c..15447279cd45dae80503ebd933369a37d7c635b7 100644 (file)
@@ -29,6 +29,7 @@
 #include <certt.h>
 
 #include "nsscommon.h"
+#include "staprun.h"
 #include "modverify.h"
 
 #include <sys/stat.h>
@@ -130,7 +131,11 @@ check_cert_db_permissions (const char *cert_db_path) {
       return 0;
     }
 
-  rc = 1; /* ok */
+  if (! S_ISDIR (info.st_mode))
+    {
+      fprintf (stderr, "Certificate database %s is not a directory.\n", cert_db_path);
+      return 0;
+    }
 
   /* The owner of the database must be root.  */
   if (info.st_uid != 0)
@@ -139,6 +144,8 @@ check_cert_db_permissions (const char *cert_db_path) {
       rc = 0;
     }
 
+  rc = 1; /* ok */
+
   /* Check the database directory access permissions  */
   if ((info.st_mode & S_IRUSR) == 0)
     fprintf (stderr, "Certificate database %s should be readable by the owner.\n", cert_db_path);
@@ -189,16 +196,75 @@ check_cert_db_permissions (const char *cert_db_path) {
 }
 
 static int
-verify_it (const char *inputName, const char *signatureName, SECKEYPublicKey *pubKey)
+verify_it (const char *signatureName, const SECItem *signature,
+          const void *module_data, off_t module_size,
+          const SECKEYPublicKey *pubKey)
 {
-  unsigned char buffer[4096];
-  PRFileInfo info;
+  VFYContext *vfy;
+  SECStatus secStatus;
+
+  /* Create a verification context.  */
+  vfy = VFY_CreateContextDirect (pubKey, signature, SEC_OID_PKCS1_RSA_ENCRYPTION,
+                                SEC_OID_UNKNOWN, NULL, NULL);
+  if (! vfy)
+    {
+      /* The key does not match the signature. This is not an error. It just
+        means we are currently trying the wrong certificate/key. i.e. the
+        module remains untrusted for now.  */
+      return MODULE_UNTRUSTED;
+    }
+
+  /* Begin the verification process.  */
+  secStatus = VFY_Begin(vfy);
+  if (secStatus != SECSuccess)
+    {
+      fprintf (stderr, "Unable to initialize verification context while verifying %s using the signature in %s.\n",
+              modpath, signatureName);
+      nssError ();
+      return MODULE_CHECK_ERROR;
+    }
+
+  /* Add the data to be verified.  */
+  secStatus = VFY_Update (vfy, module_data, module_size);
+  if (secStatus != SECSuccess)
+    {
+      fprintf (stderr, "Error while verifying %s using the signature in %s.\n",
+              modpath, signatureName);
+      nssError ();
+      return MODULE_CHECK_ERROR;
+    }
+
+  /* Complete the verification.  */
+  secStatus = VFY_End (vfy);
+  if (secStatus != SECSuccess) {
+    fprintf (stderr, "Unable to verify the signed module %s. It may have been altered since it was created.\n",
+            modpath);
+    nssError ();
+    return MODULE_ALTERED;
+  }
+
+  return MODULE_OK;
+}
+
+int verify_module (const char *signatureName, const void *module_data,
+                  off_t module_size)
+{
+  const char *dbdir  = SYSCONFDIR "/systemtap/staprun";
+  SECKEYPublicKey *pubKey;
+  SECStatus secStatus;
+  CERTCertList *certList;
+  CERTCertListNode *certListNode;
+  CERTCertificate *cert;
   PRStatus prStatus;
+  PRFileInfo info;
   PRInt32  numBytes;
   PRFileDesc *local_file_fd;
-  VFYContext *vfy;
   SECItem signature;
-  SECStatus secStatus;
+  int rc = 0;
+
+  /* Verify the permissions of the certificate database and its files.  */
+  if (! check_cert_db_permissions (dbdir))
+    return MODULE_UNTRUSTED;
 
   /* Get the size of the signature file.  */
   prStatus = PR_GetFileInfo (signatureName, &info);
@@ -246,94 +312,6 @@ verify_it (const char *inputName, const char *signatureName, SECKEYPublicKey *pu
   /* Done with the signature file.  */
   PR_Close (local_file_fd);
 
-  /* Create a verification context.  */
-  vfy = VFY_CreateContextDirect (pubKey, & signature, SEC_OID_PKCS1_RSA_ENCRYPTION,
-                                SEC_OID_UNKNOWN, NULL, NULL);
-  if (! vfy)
-    {
-      /* The key does not match the signature. This is not an error. It just means
-        we are currently trying the wrong certificate/key. i.e. the module
-        remains untrusted for now.  */
-      return MODULE_UNTRUSTED;
-    }
-
-  /* Begin the verification process.  */
-  secStatus = VFY_Begin(vfy);
-  if (secStatus != SECSuccess)
-    {
-      fprintf (stderr, "Unable to initialize verification context while verifying %s using the signature in %s.\n",
-              inputName, signatureName);
-      nssError ();
-      return MODULE_CHECK_ERROR;
-    }
-
-  /* Now read the data and add it to the signature.  */
-  local_file_fd = PR_Open (inputName, PR_RDONLY, 0);
-  if (local_file_fd == NULL)
-    {
-      fprintf (stderr, "Could not open module file %s.\n", inputName);
-      nssError ();
-      return MODULE_CHECK_ERROR;
-    }
-
-  for (;;)
-    {
-      numBytes = PR_Read (local_file_fd, buffer, sizeof (buffer));
-      if (numBytes == 0)
-       break;  /* EOF */
-
-      if (numBytes < 0)
-       {
-         fprintf (stderr, "Error reading module file %s.\n", inputName);
-         nssError ();
-         return MODULE_CHECK_ERROR;
-       }
-
-      /* Add the data to the signature.  */
-      secStatus = VFY_Update (vfy, buffer, numBytes);
-      if (secStatus != SECSuccess)
-       {
-         fprintf (stderr, "Error while verifying module file %s.\n", inputName);
-         nssError ();
-         return MODULE_CHECK_ERROR;
-       }
-    }
-
-  PR_Close(local_file_fd);
-
-  /* Complete the verification.  */
-  secStatus = VFY_End (vfy);
-  if (secStatus != SECSuccess) {
-    fprintf (stderr, "Unable to verify signed module %s. It may have been altered since it was created.\n", inputName);
-    nssError ();
-    return MODULE_ALTERED;
-  }
-
-  return MODULE_OK;
-}
-
-int verify_module (const char *module_name, const char *signature_name)
-{
-  const char *dbdir  = SYSCONFDIR "/systemtap/staprun";
-  SECKEYPublicKey *pubKey;
-  SECStatus secStatus;
-  CERTCertList *certList;
-  CERTCertListNode *certListNode;
-  CERTCertificate *cert;
-  PRStatus prStatus;
-  PRFileInfo info;
-  int rc = 0;
-
-  /* Look for the certificate database. If it's not there, it's not an error, it
-     just means that the module can't be verified.  */
-  prStatus = PR_GetFileInfo (dbdir, &info);
-  if (prStatus != PR_SUCCESS || info.type != PR_FILE_DIRECTORY)
-    return MODULE_UNTRUSTED;
-
-  /* Verify the permissions of the certificate database and its files.  */
-  if (! check_cert_db_permissions (dbdir))
-    return MODULE_UNTRUSTED;
-
   /* Call the NSPR initialization routines. */
   PR_Init (PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
 
@@ -373,7 +351,7 @@ int verify_module (const char *module_name, const char *signature_name)
        }
 
       /* Verify the file. */
-      rc = verify_it (module_name, signature_name, pubKey);
+      rc = verify_it (signatureName, & signature, module_data, module_size, pubKey);
       if (rc == MODULE_OK || rc == MODULE_ALTERED || rc == MODULE_CHECK_ERROR)
        break; /* resolved or error */
     }
index 49b90bfee68a18b69a074d1be227d7542fddab18..730a5e865954a662ba651687ab6d4bd683cda886 100644 (file)
@@ -1,4 +1,4 @@
-int verify_module (const char *module_name, const char *signature_name);
+int verify_module (const char *signature_name, const void *module_data, off_t module_size);
 
 /* return codes for verify_module.  */
 #define MODULE_OK           1
index 554eecc8d6f75c9bc959e4b5310749ba8cdb794c..c64bf5b302a68b2a0ffd83815e68407fb42bca24 100644 (file)
@@ -303,9 +303,6 @@ int main(int argc, char **argv)
                exit(1);
        }
 
-       if (check_permissions() != 1)
-               usage(argv[0]);
-
        if (init_staprun())
                exit(1);
 
index 0a1ca8852cb702ed9bc97b8a461737595034cafb..1dcfabbe444fd9c57139bcbddc1ae865249e52c3 100644 (file)
@@ -134,7 +134,6 @@ const char *moderror(int err);
 int insert_module(const char *path, const char *special_options,
        char **options);
 int mountfs(void);
-int check_permissions(void);
 void start_symbol_thread(void);
 void stop_symbol_thread(void);
 
index 1bbb59f52065e5812949d9c2cb1543099aeaa0af..4e525b006089bafc916c0f5c68bc06c51bdf4242 100644 (file)
@@ -23,6 +23,7 @@
 #include <assert.h>
 
 extern long init_module(void *, unsigned long, const char *);
+static int check_permissions(const void *, off_t);
 
 /* Module errors get translated. */
 const char *moderror(int err)
@@ -48,6 +49,7 @@ int insert_module(const char *path, const char *special_options, char **options)
        void *file;
        char *opts;
        int fd, saved_errno;
+       char module_realpath[PATH_MAX];
        struct stat sbuf;
 
        dbug(2, "inserting module\n");
@@ -71,8 +73,24 @@ int insert_module(const char *path, const char *special_options, char **options)
        }
        dbug(2, "module options: %s\n", opts);
 
-       /* Open the module file. */
-       fd = open(path, O_RDONLY);
+       /* Use realpath() to canonicalize the module path. */
+       if (realpath(modpath, module_realpath) == NULL) {
+               perr("Unable to canonicalize path \"%s\"", modpath);
+               return -1;
+       }
+
+        /* Overwrite the modpath with the canonicalized one, to defeat
+           a possible race between path and signature checking below and,
+          somewhat later, module loading. */
+        modpath = strdup (module_realpath);
+        if (modpath == NULL) {
+               _perr("allocating memory failed");
+                exit (1);
+        }
+
+       /* Open the module file. Work with the open file descriptor from this
+          point on to avoid TOCTOU problems. */
+       fd = open(modpath, O_RDONLY);
        if (fd < 0) {
                perr("Error opening '%s'", path);
                return -1;
@@ -94,6 +112,11 @@ int insert_module(const char *path, const char *special_options, char **options)
                return -1;
        }
 
+       /* Check whether this module can be loaded by the current user.  */
+       ret = check_permissions (file, sbuf.st_size);
+       if (ret != 1)
+               return -1;
+
        STAP_PROBE1(staprun, insert__module, path);
        /* Actually insert the module */
        ret = init_module(file, sbuf.st_size, opts);
@@ -216,28 +239,22 @@ int mountfs(void)
  * Returns: -1 on errors, 0 on failure, 1 on success.
  */
 static int
-check_signature(void)
+check_signature(const void *module_data, off_t module_size)
 {
-  char module_realpath[PATH_MAX];
   char signature_realpath[PATH_MAX];
   int rc;
 
   dbug(2, "checking signature for %s\n", modpath);
 
-  /* Use realpath() to canonicalize the module path. */
-  if (realpath(modpath, module_realpath) == NULL) {
-    perr("Unable to canonicalize module path \"%s\"", modpath);
-    return MODULE_CHECK_ERROR;
-  }
-
-  /* Now add the .sgn suffix to get the signature file name.  */
-  if (strlen (module_realpath) > PATH_MAX - 4) {
-    err("Path \"%s\" is too long.", modpath);
-    return MODULE_CHECK_ERROR;
+  /* Add the .sgn suffix to the canonicalized module path to get the signature
+     file path.  */
+  if (strlen (modpath) >= PATH_MAX - 4) {
+    err("Path \"%s.sgn\" is too long.", modpath);
+    return -1;
   }
-  sprintf (signature_realpath, "%s.sgn", module_realpath);
+  sprintf (signature_realpath, "%s.sgn", modpath);
 
-  rc = verify_module (module_realpath, signature_realpath);
+  rc = verify_module (signature_realpath, module_data, module_size);
 
   dbug(2, "verify_module returns %d\n", rc);
 
@@ -255,11 +272,10 @@ check_signature(void)
 static int
 check_path(void)
 {
-       struct utsname utsbuf;
-       struct stat sb;
        char staplib_dir_path[PATH_MAX];
        char staplib_dir_realpath[PATH_MAX];
-       char module_realpath[PATH_MAX];
+       struct utsname utsbuf;
+       struct stat sb;
 
        /* First, we need to figure out what the kernel
         * version is and build the '/lib/modules/KVER/systemtap' path. */
@@ -308,21 +324,6 @@ check_path(void)
                return -1;
        }
 
-       /* Use realpath() to canonicalize the module path. */
-       if (realpath(modpath, module_realpath) == NULL) {
-               perr("Unable to canonicalize path \"%s\"", modpath);
-               return -1;
-       }
-
-        /* Overwrite the modpath with the canonicalized one, to defeat
-           a possible race between path checking below and somewhat later
-           module loading. */
-        modpath = strdup (module_realpath);
-        if (modpath == NULL) {
-               _perr("allocating memory failed");
-                exit (1);
-        }
-
        /* To make sure the user can't specify something like
         * /lib/modules/`uname -r`/systemtapmod.ko, put a '/' on the
         * end of staplib_dir_realpath. */
@@ -334,8 +335,8 @@ check_path(void)
        }
 
        /* Now we've got two canonicalized paths.  Make sure
-        * module_realpath starts with staplib_dir_realpath. */
-       if (strncmp(staplib_dir_realpath, module_realpath,
+        * modpath starts with staplib_dir_realpath. */
+       if (strncmp(staplib_dir_realpath, modpath,
                    strlen(staplib_dir_realpath)) != 0) {
                err("ERROR: Members of the \"stapusr\" group can only use modules within\n"
                    "  the \"%s\" directory.\n"
@@ -347,7 +348,7 @@ check_path(void)
 }
 
 /*
- * Check the user's group membership.  Is he allowed to run staprun (or is
+ * Check the user's group membership.
  *
  * o members of stapdev can do anything
  * o members of stapusr can load modules from /lib/modules/KVER/systemtap
@@ -433,8 +434,8 @@ check_groups (void)
 }
 
 /*
- * Check the user's permissions.  Is he allowed to run staprun (or is
- * he limited to "blessed" modules)?
+ * Check the user's permissions.  Is he allowed to run staprunor is
+ * he limited to "blessed" modules?
  *
  * There are several levels of possible permission:
  *
@@ -447,15 +448,15 @@ check_groups (void)
  *
  * Returns: -1 on errors, 0 on failure, 1 on success.
  */
-int check_permissions(void)
+int check_permissions(const void *module_data, off_t module_size)
 {
        int check_groups_rc;
        int check_signature_rc = 0;
-#if HAVE_NSS
 
+#if HAVE_NSS
        /* Attempt to verify the module against its signature. Return failure
           if the module has been tampered with (altered).  */
-       check_signature_rc = check_signature ();
+       check_signature_rc = check_signature (module_data, module_size);
        if (check_signature_rc == MODULE_ALTERED)
                return 0;
 #endif
@@ -483,7 +484,7 @@ int check_permissions(void)
                return 1;
 
        /* The user is an ordinary user. If the module has been signed with
-        * a "blessed" certificate and private key, then we will load it for
+        * an authorized certificate and private key, then we will load it for
         * anyone.  */
 #if HAVE_NSS
        if (check_signature_rc == MODULE_OK)
This page took 0.057135 seconds and 5 git commands to generate.