Add detection for corrupt TPM token objects to opencryptoki

Currently, opencryptoki will happily try and allocate huge buffers
when a file is corrupted and contains an incorrect length.  This
change adds a check to see that the length of the requested object is
less than the size of the whole file.  If not, then an error is
generated.

BUG=chromium-os:24853
TEST=corrupted a file in .tpm directory, and correctly detected it.

Change-Id: Id1f8a4bbec9e417b33d63c7e89ff623c0f4fa0fe
diff --git a/usr/lib/pkcs11/tpm_stdll/loadsave.c b/usr/lib/pkcs11/tpm_stdll/loadsave.c
index cf752fe..cb6cdfc 100644
--- a/usr/lib/pkcs11/tpm_stdll/loadsave.c
+++ b/usr/lib/pkcs11/tpm_stdll/loadsave.c
@@ -782,6 +782,8 @@
    CK_BYTE   tmp[PATH_MAX], fname[PATH_MAX], iname[PATH_MAX];
    CK_BBOOL  priv;
    CK_ULONG_32  size;
+   CK_RV     rc;
+   struct stat fp2_stat;
    struct passwd *pw = NULL;
 
    if ((pw = getpwuid(getuid())) == NULL){
@@ -811,24 +813,55 @@
          if (!fp2)
             continue;
 
-         fread( &size, sizeof(CK_ULONG_32), 1, fp2 );
-         fread( &priv, sizeof(CK_BBOOL), 1, fp2 );
+         // Get the file size for fp2, so we can do sanity checks on
+         // the sizes and skip when the files are corrupted.
+         if ( fstat( fileno(fp2), &fp2_stat ) == -1 ) {
+            LogError( "Unable to stat public token object %s: %s",
+                      fname, strerror(errno) );
+            rc = CKR_FUNCTION_FAILED;
+            goto error;
+         }
+
+         if ( fread( &size, sizeof(CK_ULONG_32), 1, fp2 ) != 1 ||
+              fread( &priv, sizeof(CK_BBOOL), 1, fp2 ) != 1 ) {
+            LogError( "Unable to read public token object %s", fname );
+            rc = CKR_FUNCTION_FAILED;
+            goto error;
+         }
+
          if (priv == TRUE) {
             fclose( fp2 );
             continue;
          }
 
+         // If the requested size is larger than the entire file or
+         // smaller than what we've already read, then we know it has
+         // to be bogus.
+         if ( size > fp2_stat.st_size ||
+              size < (sizeof(CK_ULONG_32) + sizeof(CK_BBOOL)) ) {
+            LogError( "Corrupt public token object: %s", fname );
+            rc = CKR_FUNCTION_FAILED;
+            goto error;
+         }
+
          // size--;
 	 size = size -sizeof(CK_ULONG_32) - sizeof(CK_BBOOL);
          buf = (CK_BYTE *)malloc(size);
          if (!buf) {
-            fclose(fp1);
-            fclose(fp2);
             st_err_log(0, __FILE__, __LINE__);
-            return CKR_HOST_MEMORY;
+            rc = CKR_HOST_MEMORY;
+            goto error;
          }
 
-         fread( buf, size, 1, fp2 );
+         // If the read didn't complete, then the buffer contains at
+         // least some bogus data (and since we know the file is big
+         // enough to read |size| bytes, something else must have
+         // happened).
+         if ( fread( buf, size, 1, fp2 ) != 1 ) {
+            st_err_log( 4, __FILE__, __LINE__, __FUNCTION__ );
+            rc = CKR_FUNCTION_FAILED;
+            goto error;
+         }
 
          // ... grab object mutex here.
          MY_LockMutex(&obj_list_mutex);
@@ -841,6 +874,12 @@
    fclose(fp1);
 
    return CKR_OK;
+
+error:
+   if (buf)  free( buf );
+   if (fp1)  fclose( fp1 );
+   if (fp2)  fclose( fp2 );
+   return rc;
 }
 
 
@@ -856,6 +895,7 @@
    CK_BBOOL  priv;
    CK_ULONG_32  size;
    CK_RV     rc;
+   struct stat fp2_stat;
    struct passwd *pw = NULL;
 
    if ((pw = getpwuid(getuid())) == NULL){
@@ -885,13 +925,37 @@
          if (!fp2)
             continue;
 
-         fread( &size, sizeof(CK_ULONG_32), 1, fp2 );
-         fread( &priv, sizeof(CK_BBOOL), 1, fp2 );
+         // Get the file size for fp2, so we can do sanity checks on
+         // the sizes and skip when the files are corrupted.
+         if (fstat( fileno(fp2), &fp2_stat ) == -1) {
+            LogError( "Unable to stat private token object %s: %s",
+                      fname, strerror(errno) );
+            rc = CKR_FUNCTION_FAILED;
+            goto error;
+         }
+
+         if ( fread( &size, sizeof(CK_ULONG_32), 1, fp2 ) != 1 ||
+              fread( &priv, sizeof(CK_BBOOL), 1, fp2 ) != 1 ) {
+            LogError( "Unable to read private token object %s", fname );
+            rc = CKR_FUNCTION_FAILED;
+            goto error;
+         }
+
          if (priv == FALSE) {
             fclose( fp2 );
             continue;
          }
 
+         // If the requested size is larger than the entire file or
+         // smaller than what we've already read, then we know it has
+         // to be bogus.
+         if ( size > fp2_stat.st_size ||
+              size < (sizeof(CK_ULONG_32) + sizeof(CK_BBOOL)) ) {
+            LogError( "Corrupt private token object: %s", fname );
+            rc = CKR_FUNCTION_FAILED;
+            goto error;
+         }
+
          //size--;
 	 size = size - sizeof(CK_ULONG_32) - sizeof(CK_BBOOL);
          buf = (CK_BYTE *)malloc(size);
@@ -901,9 +965,8 @@
             goto error;
          }
 
-         rc = fread( (char *)buf, size, 1, fp2 );
-         if (rc != 1) {
-            st_err_log(4, __FILE__, __LINE__, __FUNCTION__);
+         if ( fread( (char *)buf, size, 1, fp2 ) != 1 ) {
+            st_err_log( 4, __FILE__, __LINE__, __FUNCTION__ );
             rc = CKR_FUNCTION_FAILED;
             goto error;
          }