CVE-2012-6119 (published 2013-04-02T22:55Z)

Candlepin before 0.7.24, as used in Red Hat Subscription Asset Manager before 1.2.1, does not properly check manifest signatures, which allows local users to modify manifests.

Severity: LOW
CVSS v2 base score: 2.1 (AV:L/AC:L/Au:N/C:N/I:P/A:N)

Weakness information:
cwe_id: CWE-264
cwe_name: Permissions, Privileges, and Access Controls
description: Weaknesses in this category are related to the management of permissions, privileges, and other security features that are used to perform access control.

Fix information:
repo_url: https://github.com/candlepin/candlepin
commit_details: repo_url: https://github.com/candlepin/candlepin
author: Devan Goodwin
author_date: 2012-12-19 15:19:14-04:00
msg: 835977: Re-enable manifest signature checking.

The production/stage public candlepin certificate is now packaged in the
RPM. This is flagged as a config file but *without* noreplace, so any
deployment using their own upstream cert (we don't think there are any)
will have to restore it from the rpmsave file that results after a
candlepin rpm upgrade. This was done because we expect this to be the
desired behavior, there is no known usecase for using your own
upstream cert at this time.
repository: repo_url: https://github.com/candlepin/candlepin
repo_name: candlepin/candlepin
description: The Candlepin entitlement engine
repo_language: Java
owner: candlepin
file_changes: filename: candlepin.spec
change_type: ModificationType.MODIFY
diff: @@ -197,6 +197,7 @@ rm -rf $RPM_BUILD_ROOT
 # Create the directory structure required to lay down our files
 # common
 install -d -m 755 $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/certs/
+install -m 644 conf/candlepin-upstream-ca.crt %{buildroot}%{_sysconfdir}/%{name}/certs
 install -d -m 755 $RPM_BUILD_ROOT/%{_sysconfdir}/%{name}/
 install -d -m 755 $RPM_BUILD_ROOT/%{_datadir}/%{name}/
 install -m 755 code/setup/cpsetup $RPM_BUILD_ROOT/%{_datadir}/%{name}/cpsetup
@@ -283,6 +284,10 @@ fi
 %{_datadir}/%{name}/cpdb
 %{_sysconfdir}/%{name}/certs/
 %ghost %attr(644, root, root) %{_sysconfdir}/%{name}/certs/candlepin-ca.crt
+# Default is to track the rpm version of this cert for manifest signatures.
+# If a deployment is managing their own, they will need to restore from the
+# .rpmsave backup after upgrading the candlepin rpm.
+%config %attr(644, root, root) %{_sysconfdir}/%{name}/certs/candlepin-upstream-ca.crt
 %doc LICENSE
 %doc README
 

programming_language: Shell
filename: cpsetup
change_type: ModificationType.MODIFY
diff: @@ -129,7 +129,6 @@ class CertSetup(object):
         self.cert_home = '/etc/candlepin/certs'
         self.ca_key_passwd = self.cert_home + '/candlepin-ca-password.txt'
         self.ca_key = self.cert_home + '/candlepin-ca.key'
-        self.ca_upstream_cert = self.cert_home + '/candlepin-upstream-ca.crt'
         self.ca_pub_key = self.cert_home + '/candlepin-ca-pub.key'
         self.ca_cert = self.cert_home + '/candlepin-ca.crt'
         self.keystore = self.cert_home + '/keystore'
@@ -151,7 +150,6 @@ class CertSetup(object):
         print("Creating CA certificate")
         run_command_with_sudo('openssl req -new -x509 -days 365 -key %s -out %s -subj "/CN=%s/C=US/L=Raleigh/"' % (self.ca_key, self.ca_cert, socket.gethostname()))
         run_command_with_sudo('openssl pkcs12 -export -in %s -inkey %s -out %s -name tomcat -CAfile %s -caname root -chain -password pass:password' % (self.ca_cert, self.ca_key, self.keystore, self.ca_cert))
-        run_command_with_sudo('cp %s %s' % (self.ca_cert, self.ca_upstream_cert))
         run_command_with_sudo('chmod a+r %s' % self.keystore)
 
 

programming_language: Python
filename: candlepin-upstream-ca.crt
change_type: ModificationType.ADD
diff: @@ -0,0 +1,40 @@
+-----BEGIN CERTIFICATE-----
+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
+g6EHdPrR+A==
+-----END CERTIFICATE-----

programming_language: PowerShell
filename: Importer.java
change_type: ModificationType.MODIFY
diff: @@ -22,6 +22,7 @@ import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
+import java.security.cert.CertificateException;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -211,40 +212,37 @@ public class Importer {
             tmpDir = new SyncUtils(config).makeTempDir("import");
             extractArchive(tmpDir, exportFile);
 
-//           only need this call when sig file is verified
-//            exportStream = new FileInputStream(new File(tmpDir, "consumer_export.zip"));
-
-            /*
-             * Disabling this once again for a little bit longer. Dependent projects
-             * are not yet ready for it, and we're having some difficulty with the actual
-             * upstream cert to use.
-             *
-             * When we bring this back, we should probably report this conflict
-             * immediately, rather than continuing to extract and trying to find any
-             * other conflicts to pass back.
-             */
-//            boolean verifiedSignature = pki.verifySHA256WithRSAHashWithUpstreamCACert(
-//                exportStream,
-//                loadSignature(new File(tmpDir, "signature")));
-//            if (!verifiedSignature) {
-//                log.warn("Manifest signature check failed.");
-//                if (!forcedConflicts
-//                    .isForced(ImportConflicts.Conflict.SIGNATURE_CONFLICT)) {
-//                    conflicts.addConflict(
-//                        i18n.tr("Failed import file hash check."),
-//                        ImportConflicts.Conflict.SIGNATURE_CONFLICT);
-//                }
-//                else {
-//                    log.warn("Ignoring signature check failure.");
-//                }
-//            }
-
             File signature = new File(tmpDir, "signature");
             if (signature.length() == 0) {
                 throw new ImportExtractionException(i18n.tr("The archive does not " +
                                           "contain the required signature file"));
             }
 
+            exportStream = new FileInputStream(new File(tmpDir, "consumer_export.zip"));
+            boolean verifiedSignature = pki.verifySHA256WithRSAHashWithUpstreamCACert(
+                exportStream,
+                loadSignature(new File(tmpDir, "signature")));
+            if (!verifiedSignature) {
+                log.warn("Archive signature check failed.");
+                if (!overrides
+                    .isForced(Conflict.SIGNATURE_CONFLICT)) {
+
+                    /*
+                     * Normally for import conflicts that can be overridden, we try to
+                     * report them all the first time so if the user intends to override,
+                     * they can do so with just one more request. However in the case of
+                     * a bad signature, we're going to report immediately due to the nature
+                     * of what this might mean.
+                     */
+                    throw new ImportConflictException(
+                        i18n.tr("Archive failed signature check"),
+                        Conflict.SIGNATURE_CONFLICT);
+                }
+                else {
+                    log.warn("Ignoring signature check failure.");
+                }
+            }
+
             File consumerExport = new File(tmpDir, "consumer_export.zip");
             File exportDir = extractArchive(tmpDir, consumerExport);
 
@@ -265,10 +263,6 @@ public class Importer {
             result.put("meta", m);
             return result;
         }
-//        catch (CertificateException e) {
-//            log.error("Exception caught importing archive", e);
-//            throw new ImportExtractionException("unable to extract export archive", e);
-//        }
         catch (FileNotFoundException fnfe) {
             log.error("Archive file does not contain consumer_export.zip", fnfe);
             throw new ImportExtractionException(i18n.tr("The archive does not contain " +
@@ -288,6 +282,11 @@ public class Importer {
             log.error("Exception caught importing archive", e);
             throw new ImportExtractionException("unable to extract export archive", e);
         }
+        catch (CertificateException e) {
+            log.error("Certificate exception checking archive signature", e);
+            throw new ImportExtractionException(
+                "Certificate exception checking archive signature", e);
+        }
         finally {
             if (tmpDir != null) {
                 try {

programming_language: Java
filename: ImporterTest.java
change_type: ModificationType.MODIFY
diff: @@ -31,6 +31,7 @@ import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintStream;
 import java.io.Reader;
 import java.net.URISyntaxException;
@@ -49,6 +50,7 @@ import org.candlepin.config.ConfigProperties;
 import org.candlepin.model.ExporterMetadata;
 import org.candlepin.model.ExporterMetadataCurator;
 import org.candlepin.model.Owner;
+import org.candlepin.pki.PKIUtility;
 import org.candlepin.sync.Importer.ImportFile;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.map.JsonMappingException;
@@ -353,15 +355,43 @@ public class ImporterTest {
         assertTrue(false);
     }
 
-    @Test
-    public void testImportZipSigConsumerNotZip()
+    @Test(expected = ImportConflictException.class)
+    public void testImportBadSignature()
         throws IOException, ImporterException {
+        PKIUtility pki = mock(PKIUtility.class);
         Importer i = new Importer(null, null, null, null, null, null, null,
-            null, config, null, null, null, i18n);
+            pki, config, null, null, null, i18n);
+
+        Owner owner = mock(Owner.class);
+        ConflictOverrides co = mock(ConflictOverrides.class);
+
+        File archive = new File("/tmp/file.zip");
+        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(archive));
+        out.putNextEntry(new ZipEntry("signature"));
+        out.write("This is the placeholder for the signature file".getBytes());
+        File ceArchive = new File("/tmp/consumer_export.zip");
+        FileOutputStream fos = new FileOutputStream(ceArchive);
+        fos.write("This is just a flat file".getBytes());
+        fos.close();
+        addFileToArchive(out, ceArchive);
+        out.close();
+
+        i.loadExport(owner, archive, co);
+    }
+
+    @Test
+    public void testImportBadConsumerZip() throws Exception {
+        PKIUtility pki = mock(PKIUtility.class);
+        Importer i = new Importer(null, null, null, null, null, null, null,
+            pki, config, null, null, null, i18n);
 
         Owner owner = mock(Owner.class);
         ConflictOverrides co = mock(ConflictOverrides.class);
 
+        // Mock a passed signature check:
+        when(pki.verifySHA256WithRSAHashWithUpstreamCACert(any(InputStream.class),
+            any(byte [].class))).thenReturn(true);
+
         File archive = new File("/tmp/file.zip");
         ZipOutputStream out = new ZipOutputStream(new FileOutputStream(archive));
         out.putNextEntry(new ZipEntry("signature"));
@@ -377,22 +407,28 @@ public class ImporterTest {
             i.loadExport(owner, archive, co);
         }
         catch (ImportExtractionException e) {
-            assertEquals(e.getMessage(), i18n.tr("The archive {0} is " +
-                "not a properly compressed file or is empty", "consumer_export.zip"));
+            System.out.println(e.getMessage());
+            assertTrue(e.getMessage().contains(
+                "not a properly compressed file or is empty"));
             return;
         }
-        assertTrue(false);
+        fail();
     }
 
     @Test
     public void testImportZipSigAndEmptyConsumerZip()
-        throws IOException, ImporterException {
+        throws Exception {
+        PKIUtility pki = mock(PKIUtility.class);
         Importer i = new Importer(null, null, null, null, null, null, null,
-            null, config, null, null, null, i18n);
+            pki, config, null, null, null, i18n);
 
         Owner owner = mock(Owner.class);
         ConflictOverrides co = mock(ConflictOverrides.class);
 
+        // Mock a passed signature check:
+        when(pki.verifySHA256WithRSAHashWithUpstreamCACert(any(InputStream.class),
+            any(byte [].class))).thenReturn(true);
+
         File archive = new File("/tmp/file.zip");
         ZipOutputStream out = new ZipOutputStream(new FileOutputStream(archive));
         out.putNextEntry(new ZipEntry("signature"));
@@ -408,11 +444,10 @@ public class ImporterTest {
             i.loadExport(owner, archive, co);
         }
         catch (ImportExtractionException e) {
-            assertEquals(e.getMessage(), i18n.tr("The consumer_export " +
-                "archive has no contents"));
+            assertTrue(e.getMessage().contains("consumer_export archive has no contents"));
             return;
         }
-        assertTrue(false);
+        fail();
     }
 
     @Test

programming_language: Java