Skip to content
Permalink
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dgoodwin committed Jan 8, 2013
1 parent 9cd5d70 commit f4d9323
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 45 deletions.
5 changes: 5 additions & 0 deletions candlepin.spec
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
2 changes: 0 additions & 2 deletions code/setup/cpsetup
Expand Up @@ -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'
Expand All @@ -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)


Expand Down
40 changes: 40 additions & 0 deletions conf/candlepin-upstream-ca.crt
@@ -0,0 +1,40 @@
-----BEGIN CERTIFICATE-----
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
g6EHdPrR+A==
-----END CERTIFICATE-----
63 changes: 31 additions & 32 deletions src/main/java/org/candlepin/sync/Importer.java
Expand Up @@ -22,6 +22,7 @@
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;
Expand Down Expand Up @@ -211,40 +212,37 @@ public Map<String, Object> loadExport(Owner owner, File exportFile,
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);

Expand All @@ -265,10 +263,6 @@ public Map<String, Object> loadExport(Owner owner, File exportFile,
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 " +
Expand All @@ -288,6 +282,11 @@ public Map<String, Object> loadExport(Owner owner, File exportFile,
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 {
Expand Down
57 changes: 46 additions & 11 deletions src/test/java/org/candlepin/sync/ImporterTest.java
Expand Up @@ -31,6 +31,7 @@
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;
Expand All @@ -49,6 +50,7 @@
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;
Expand Down Expand Up @@ -353,15 +355,43 @@ public void testImportZipArchiveNoContent()
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"));
Expand All @@ -377,22 +407,28 @@ public void testImportZipSigConsumerNotZip()
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"));
Expand All @@ -408,11 +444,10 @@ public void testImportZipSigAndEmptyConsumerZip()
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
Expand Down

0 comments on commit f4d9323

Please sign in to comment.