From 86ea65ab6fe768e7fb97782d3e20b44494bdba6c Mon Sep 17 00:00:00 2001 From: Stephen Connolly Date: Wed, 29 Oct 2014 16:55:41 +0000 Subject: [PATCH] Make it possible for sub-classes to have a sub-class specific trust anchor chain --- .../main/java/hudson/model/UpdateSite.java | 11 +- .../jenkins/util/JSONSignatureValidator.java | 130 +++++++++--------- 2 files changed, 77 insertions(+), 64 deletions(-) diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java index 7d4ddd2bfe..77b5926c4b 100644 --- a/core/src/main/java/hudson/model/UpdateSite.java +++ b/core/src/main/java/hudson/model/UpdateSite.java @@ -218,7 +218,16 @@ public class UpdateSite { * Verifies the signature in the update center data file. */ private FormValidation verifySignature(JSONObject o) throws IOException { - return new JSONSignatureValidator("update site '"+id+"'").verifySignature(o); + return getJsonSignatureValidator().verifySignature(o); + } + + /** + * Let sub-classes of UpdateSite provide their own signature validator. + * @return the signature validator. + */ + @Nonnull + protected JSONSignatureValidator getJsonSignatureValidator() { + return new JSONSignatureValidator("update site '"+id+"'"); } /** diff --git a/core/src/main/java/jenkins/util/JSONSignatureValidator.java b/core/src/main/java/jenkins/util/JSONSignatureValidator.java index 758b95cedc..d2adb2394a 100644 --- a/core/src/main/java/jenkins/util/JSONSignatureValidator.java +++ b/core/src/main/java/jenkins/util/JSONSignatureValidator.java @@ -72,69 +72,7 @@ public class JSONSignatureValidator { certs.add(c); } - // if we trust default root CAs, we end up trusting anyone who has a valid certificate, - // which isn't useful at all - Set anchors = new HashSet(); // CertificateUtil.getDefaultRootCAs(); - Jenkins j = Jenkins.getInstance(); - for (String cert : (Set) j.servletContext.getResourcePaths("/WEB-INF/update-center-rootCAs")) { - if (cert.endsWith("/") || cert.endsWith(".txt")) { - continue; // skip directories also any text files that are meant to be documentation - } - InputStream in = j.servletContext.getResourceAsStream(cert); - if (in == null) continue; // our test for paths ending in / should prevent this from happening - Certificate certificate; - try { - certificate = cf.generateCertificate(in); - } catch (CertificateException e) { - LOGGER.log(Level.WARNING, String.format("Webapp resources in /WEB-INF/update-center-rootCAs are " - + "expected to be either certificates or .txt files documenting the " - + "certificates, but %s did not parse as a certificate. Skipping this " - + "resource for now.", - cert), e); - continue; - } finally { - in.close(); - } - try { - anchors.add(new TrustAnchor((X509Certificate) certificate, null)); - } catch (IllegalArgumentException e) { - LOGGER.log(Level.WARNING, - String.format("The name constraints in the certificate resource %s could not be " - + "decoded. Skipping this resource for now.", - cert), e); - } - } - File[] cas = new File(j.root, "update-center-rootCAs").listFiles(); - if (cas!=null) { - for (File cert : cas) { - if (cert.isDirectory() || cert.getName().endsWith(".txt")) { - continue; // skip directories also any text files that are meant to be documentation - } - FileInputStream in = new FileInputStream(cert); - Certificate certificate; - try { - certificate = cf.generateCertificate(in); - } catch (CertificateException e) { - LOGGER.log(Level.WARNING, String.format("Files in %s are expected to be either " - + "certificates or .txt files documenting the certificates, " - + "but %s did not parse as a certificate. Skipping this file for now.", - cert.getParentFile().getAbsolutePath(), - cert.getAbsolutePath()), e); - continue; - } finally { - in.close(); - } - try { - anchors.add(new TrustAnchor((X509Certificate) certificate, null)); - } catch (IllegalArgumentException e) { - LOGGER.log(Level.WARNING, - String.format("The name constraints in the certificate file %s could not be " - + "decoded. Skipping this file for now.", - cert.getAbsolutePath()), e); - } - } - } - CertificateUtil.validatePath(certs, anchors); + CertificateUtil.validatePath(certs, loadTrustAnchors(cf)); } // this is for computing a digest to check sanity @@ -191,5 +129,71 @@ public class JSONSignatureValidator { } } + protected Set loadTrustAnchors(CertificateFactory cf) throws IOException { + // if we trust default root CAs, we end up trusting anyone who has a valid certificate, + // which isn't useful at all + Set anchors = new HashSet(); // CertificateUtil.getDefaultRootCAs(); + Jenkins j = Jenkins.getInstance(); + for (String cert : (Set) j.servletContext.getResourcePaths("/WEB-INF/update-center-rootCAs")) { + if (cert.endsWith("/") || cert.endsWith(".txt")) { + continue; // skip directories also any text files that are meant to be documentation + } + InputStream in = j.servletContext.getResourceAsStream(cert); + if (in == null) continue; // our test for paths ending in / should prevent this from happening + Certificate certificate; + try { + certificate = cf.generateCertificate(in); + } catch (CertificateException e) { + LOGGER.log(Level.WARNING, String.format("Webapp resources in /WEB-INF/update-center-rootCAs are " + + "expected to be either certificates or .txt files documenting the " + + "certificates, but %s did not parse as a certificate. Skipping this " + + "resource for now.", + cert), e); + continue; + } finally { + in.close(); + } + try { + anchors.add(new TrustAnchor((X509Certificate) certificate, null)); + } catch (IllegalArgumentException e) { + LOGGER.log(Level.WARNING, + String.format("The name constraints in the certificate resource %s could not be " + + "decoded. Skipping this resource for now.", + cert), e); + } + } + File[] cas = new File(j.root, "update-center-rootCAs").listFiles(); + if (cas!=null) { + for (File cert : cas) { + if (cert.isDirectory() || cert.getName().endsWith(".txt")) { + continue; // skip directories also any text files that are meant to be documentation + } + FileInputStream in = new FileInputStream(cert); + Certificate certificate; + try { + certificate = cf.generateCertificate(in); + } catch (CertificateException e) { + LOGGER.log(Level.WARNING, String.format("Files in %s are expected to be either " + + "certificates or .txt files documenting the certificates, " + + "but %s did not parse as a certificate. Skipping this file for now.", + cert.getParentFile().getAbsolutePath(), + cert.getAbsolutePath()), e); + continue; + } finally { + in.close(); + } + try { + anchors.add(new TrustAnchor((X509Certificate) certificate, null)); + } catch (IllegalArgumentException e) { + LOGGER.log(Level.WARNING, + String.format("The name constraints in the certificate file %s could not be " + + "decoded. Skipping this file for now.", + cert.getAbsolutePath()), e); + } + } + } + return anchors; + } + private static final Logger LOGGER = Logger.getLogger(JSONSignatureValidator.class.getName()); } -- GitLab