[SCM] jenkins packaging branch, master, updated. debian/1.447.2+dfsg-1-4-gbd03136
James Page
james.page at ubuntu.com
Tue Sep 25 18:21:10 UTC 2012
The following commit has been merged in the master branch:
commit ee2f41b93c340b756567909693f81adeb14bc1e6
Author: James Page <james.page at ubuntu.com>
Date: Tue Sep 25 11:56:17 2012 +0100
Refreshed patch with full commit range
diff --git a/debian/patches/security/CVE-2012-4438_CVE-2012-4439.patch b/debian/patches/security/CVE-2012-4438_CVE-2012-4439.patch
index cdcd68e..13e53a7 100644
--- a/debian/patches/security/CVE-2012-4438_CVE-2012-4439.patch
+++ b/debian/patches/security/CVE-2012-4438_CVE-2012-4439.patch
@@ -5,9 +5,33 @@ Decription: Cherry picked fixes from 1.466.2 to resolve
Origin: Upstream, commits fb73bac50f22526a3d3b...43ff1688eee6ea
Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=688298
+diff --git a/core/src/main/java/hudson/model/DownloadService.java b/core/src/main/java/hudson/model/DownloadService.java
+index 7f8d86d..820a44a 100644
--- a/core/src/main/java/hudson/model/DownloadService.java
+++ b/core/src/main/java/hudson/model/DownloadService.java
-@@ -61,7 +61,8 @@ public class DownloadService extends Pag
+@@ -26,18 +26,20 @@ package hudson.model;
+ import hudson.Extension;
+ import hudson.ExtensionList;
+ import hudson.ExtensionPoint;
++import hudson.util.FormValidation;
++import hudson.util.FormValidation.Kind;
+ import hudson.util.IOException2;
+ import hudson.util.IOUtils;
+ import hudson.util.QuotedStringTokenizer;
+ import hudson.util.TextFile;
+ import hudson.util.TimeUnit2;
+ import jenkins.model.Jenkins;
++import jenkins.util.JSONSignatureValidator;
+ import net.sf.json.JSONException;
+ import org.kohsuke.stapler.Stapler;
+
+ import java.io.File;
+ import java.io.IOException;
+-import java.util.logging.Level;
+ import java.util.logging.Logger;
+
+ import net.sf.json.JSONObject;
+@@ -61,7 +63,8 @@ public class DownloadService extends PageDecorator {
*/
public String generateFragment() {
if (neverUpdate) return "";
@@ -17,7 +41,7 @@ Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=688298
StringBuilder buf = new StringBuilder();
if(Jenkins.getInstance().hasPermission(Jenkins.READ)) {
long now = System.currentTimeMillis();
-@@ -88,6 +89,23 @@ public class DownloadService extends Pag
+@@ -88,6 +91,23 @@ public class DownloadService extends PageDecorator {
return buf.toString();
}
@@ -41,6 +65,198 @@ Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=688298
private String mapHttps(String url) {
/*
HACKISH:
+@@ -223,11 +243,24 @@ public class DownloadService extends PageDecorator {
+ */
+ public void doPostBack(StaplerRequest req, StaplerResponse rsp) throws IOException {
+ long dataTimestamp = System.currentTimeMillis();
++ due = dataTimestamp+getInterval(); // success or fail, don't try too often
++
++ String json = IOUtils.toString(req.getInputStream(),"UTF-8");
++ JSONObject o = JSONObject.fromObject(json);
++
++ if (signatureCheck) {
++ FormValidation e = new JSONSignatureValidator("downloadable '"+id+"'").verifySignature(o);
++ if (e.kind!= Kind.OK) {
++ LOGGER.severe(e.renderHtml());
++ throw e;
++ }
++ }
++
+ TextFile df = getDataFile();
+- df.write(IOUtils.toString(req.getInputStream(),"UTF-8"));
++ df.write(json);
+ df.file.setLastModified(dataTimestamp);
+- due = dataTimestamp+getInterval();
+ LOGGER.info("Obtained the updated data file for "+id);
++
+ rsp.setContentType("text/plain"); // So browser won't try to parse response
+ }
+
+@@ -253,5 +286,10 @@ public class DownloadService extends PageDecorator {
+ }
+
+ public static boolean neverUpdate = Boolean.getBoolean(DownloadService.class.getName()+".never");
++
++ /**
++ * Off by default until we know this is reasonably working.
++ */
++ public static boolean signatureCheck = !Boolean.getBoolean(DownloadService.class.getName()+".noSignatureCheck");
+ }
+
+diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java
+index 74bccfb..578237e 100644
+--- a/core/src/main/java/hudson/model/UpdateSite.java
++++ b/core/src/main/java/hudson/model/UpdateSite.java
+@@ -25,7 +25,6 @@
+
+ package hudson.model;
+
+-import com.trilead.ssh2.crypto.Base64;
+ import hudson.PluginManager;
+ import hudson.PluginWrapper;
+ import hudson.lifecycle.Lifecycle;
+@@ -37,12 +36,9 @@ import hudson.util.IOUtils;
+ import hudson.util.TextFile;
+ import hudson.util.VersionNumber;
+ import jenkins.model.Jenkins;
++import jenkins.util.JSONSignatureValidator;
+ import net.sf.json.JSONException;
+ import net.sf.json.JSONObject;
+-import org.apache.commons.io.output.NullOutputStream;
+-import org.apache.commons.io.output.TeeOutputStream;
+-import org.jvnet.hudson.crypto.CertificateUtil;
+-import org.jvnet.hudson.crypto.SignatureOutputStream;
+ import org.kohsuke.stapler.DataBoundConstructor;
+ import org.kohsuke.stapler.HttpResponse;
+ import org.kohsuke.stapler.QueryParameter;
+@@ -52,23 +48,14 @@ import org.kohsuke.stapler.StaplerResponse;
+ import javax.servlet.ServletContext;
+ import java.io.ByteArrayInputStream;
+ import java.io.File;
+-import java.io.FileInputStream;
+ import java.io.IOException;
+ import java.io.OutputStreamWriter;
+ import java.io.Writer;
+ import java.security.DigestOutputStream;
+ import java.security.GeneralSecurityException;
+-import java.security.MessageDigest;
+-import java.security.Signature;
+-import java.security.cert.CertificateExpiredException;
+-import java.security.cert.CertificateFactory;
+-import java.security.cert.CertificateNotYetValidException;
+-import java.security.cert.TrustAnchor;
+-import java.security.cert.X509Certificate;
+ import java.util.ArrayList;
+ import java.util.Collections;
+ import java.util.HashMap;
+-import java.util.HashSet;
+ import java.util.List;
+ import java.util.Map;
+ import java.util.Set;
+@@ -174,100 +161,7 @@ public class UpdateSite {
+ * Verifies the signature in the update center data file.
+ */
+ private FormValidation verifySignature(JSONObject o) throws IOException {
+- try {
+- FormValidation warning = null;
+-
+- JSONObject signature = o.getJSONObject("signature");
+- if (signature.isNullObject()) {
+- return FormValidation.error("No signature block found in update center '"+id+"'");
+- }
+- o.remove("signature");
+-
+- List<X509Certificate> certs = new ArrayList<X509Certificate>();
+- {// load and verify certificates
+- CertificateFactory cf = CertificateFactory.getInstance("X509");
+- for (Object cert : signature.getJSONArray("certificates")) {
+- X509Certificate c = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(Base64.decode(cert.toString().toCharArray())));
+- try {
+- c.checkValidity();
+- } catch (CertificateExpiredException e) { // even if the certificate isn't valid yet, we'll proceed it anyway
+- warning = FormValidation.warning(e,String.format("Certificate %s has expired in update center '%s'",cert.toString(),id));
+- } catch (CertificateNotYetValidException e) {
+- warning = FormValidation.warning(e,String.format("Certificate %s is not yet valid in update center '%s'",cert.toString(),id));
+- }
+- 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<TrustAnchor> anchors = new HashSet<TrustAnchor>(); // CertificateUtil.getDefaultRootCAs();
+- Jenkins j = Jenkins.getInstance();
+- for (String cert : (Set<String>) j.servletContext.getResourcePaths("/WEB-INF/update-center-rootCAs")) {
+- if (cert.endsWith(".txt")) continue; // skip text files that are meant to be documentation
+- anchors.add(new TrustAnchor((X509Certificate)cf.generateCertificate(j.servletContext.getResourceAsStream(cert)),null));
+- }
+- File[] cas = new File(j.root, "update-center-rootCAs").listFiles();
+- if (cas!=null) {
+- for (File cert : cas) {
+- if (cert.getName().endsWith(".txt")) continue; // skip text files that are meant to be documentation
+- FileInputStream in = new FileInputStream(cert);
+- try {
+- anchors.add(new TrustAnchor((X509Certificate)cf.generateCertificate(in),null));
+- } finally {
+- in.close();
+- }
+- }
+- }
+- CertificateUtil.validatePath(certs,anchors);
+- }
+-
+- // this is for computing a digest to check sanity
+- MessageDigest sha1 = MessageDigest.getInstance("SHA1");
+- DigestOutputStream dos = new DigestOutputStream(new NullOutputStream(),sha1);
+-
+- // this is for computing a signature
+- Signature sig = Signature.getInstance("SHA1withRSA");
+- sig.initVerify(certs.get(0));
+- SignatureOutputStream sos = new SignatureOutputStream(sig);
+-
+- // until JENKINS-11110 fix, UC used to serve invalid digest (and therefore unverifiable signature)
+- // that only covers the earlier portion of the file. This was caused by the lack of close() call
+- // in the canonical writing, which apparently leave some bytes somewhere that's not flushed to
+- // the digest output stream. This affects Jenkins [1.424,1,431].
+- // Jenkins 1.432 shipped with the "fix" (1eb0c64abb3794edce29cbb1de50c93fa03a8229) that made it
+- // compute the correct digest, but it breaks all the existing UC json metadata out there. We then
+- // quickly discovered ourselves in the catch-22 situation. If we generate UC with the correct signature,
+- // it'll cut off [1.424,1.431] from the UC. But if we don't, we'll cut off [1.432,*).
+- //
+- // In 1.433, we revisited 1eb0c64abb3794edce29cbb1de50c93fa03a8229 so that the original "digest"/"signature"
+- // pair continues to be generated in a buggy form, while "correct_digest"/"correct_signature" are generated
+- // correctly.
+- //
+- // Jenkins should ignore "digest"/"signature" pair. Accepting it creates a vulnerability that allows
+- // the attacker to inject a fragment at the end of the json.
+- o.writeCanonical(new OutputStreamWriter(new TeeOutputStream(dos,sos),"UTF-8")).close();
+-
+- // did the digest match? this is not a part of the signature validation, but if we have a bug in the c14n
+- // (which is more likely than someone tampering with update center), we can tell
+- String computedDigest = new String(Base64.encode(sha1.digest()));
+- String providedDigest = signature.optString("correct_digest");
+- if (providedDigest==null) {
+- return FormValidation.error("No correct_digest parameter in update center '"+id+"'. This metadata appears to be old.");
+- }
+- if (!computedDigest.equalsIgnoreCase(providedDigest)) {
+- return FormValidation.error("Digest mismatch: "+computedDigest+" vs "+providedDigest+" in update center '"+id+"'");
+- }
+-
+- String providedSignature = signature.getString("correct_signature");
+- if (!sig.verify(Base64.decode(providedSignature.toCharArray()))) {
+- return FormValidation.error("Signature in the update center doesn't match with the certificate in update center '"+id+"'");
+- }
+-
+- if (warning!=null) return warning;
+- return FormValidation.ok();
+- } catch (GeneralSecurityException e) {
+- return FormValidation.error(e,"Signature verification failed in the update center '"+id+"'");
+- }
++ return new JSONSignatureValidator("update site '"+id+"'").verifySignature(o);
+ }
+
+ /**
+diff --git a/core/src/main/java/hudson/search/Search.java b/core/src/main/java/hudson/search/Search.java
+index 55737b8..f439d8a 100644
--- a/core/src/main/java/hudson/search/Search.java
+++ b/core/src/main/java/hudson/search/Search.java
@@ -84,6 +84,7 @@ public class Search {
@@ -51,6 +267,8 @@ Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=688298
DataWriter w = Flavor.JSON.createDataWriter(null, rsp);
w.startArray();
w.value(q);
+diff --git a/core/src/main/java/hudson/tasks/junit/History.java b/core/src/main/java/hudson/tasks/junit/History.java
+index 6d5333c..7a46b60 100644
--- a/core/src/main/java/hudson/tasks/junit/History.java
+++ b/core/src/main/java/hudson/tasks/junit/History.java
@@ -293,4 +293,12 @@ public class History {
@@ -66,6 +284,154 @@ Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=688298
+ }
+ }
}
+diff --git a/core/src/main/java/jenkins/util/JSONSignatureValidator.java b/core/src/main/java/jenkins/util/JSONSignatureValidator.java
+new file mode 100644
+index 0000000..69c6144
+--- /dev/null
++++ b/core/src/main/java/jenkins/util/JSONSignatureValidator.java
+@@ -0,0 +1,140 @@
++package jenkins.util;
++
++import com.trilead.ssh2.crypto.Base64;
++import hudson.util.FormValidation;
++import jenkins.model.Jenkins;
++import net.sf.json.JSONObject;
++import org.apache.commons.io.output.NullOutputStream;
++import org.apache.commons.io.output.TeeOutputStream;
++import org.jvnet.hudson.crypto.CertificateUtil;
++import org.jvnet.hudson.crypto.SignatureOutputStream;
++
++import java.io.ByteArrayInputStream;
++import java.io.File;
++import java.io.FileInputStream;
++import java.io.IOException;
++import java.io.OutputStreamWriter;
++import java.security.DigestOutputStream;
++import java.security.GeneralSecurityException;
++import java.security.MessageDigest;
++import java.security.Signature;
++import java.security.cert.CertificateExpiredException;
++import java.security.cert.CertificateFactory;
++import java.security.cert.CertificateNotYetValidException;
++import java.security.cert.TrustAnchor;
++import java.security.cert.X509Certificate;
++import java.util.ArrayList;
++import java.util.HashSet;
++import java.util.List;
++import java.util.Set;
++
++/**
++ * @author Kohsuke Kawaguchi
++ */
++public class JSONSignatureValidator {
++ private final String name;
++
++ public JSONSignatureValidator(String name) {
++ this.name = name;
++ }
++
++ /**
++ * Verifies the signature in the update center data file.
++ */
++ public FormValidation verifySignature(JSONObject o) throws IOException {
++ try {
++ FormValidation warning = null;
++
++ JSONObject signature = o.getJSONObject("signature");
++ if (signature.isNullObject()) {
++ return FormValidation.error("No signature block found in "+name);
++ }
++ o.remove("signature");
++
++ List<X509Certificate> certs = new ArrayList<X509Certificate>();
++ {// load and verify certificates
++ CertificateFactory cf = CertificateFactory.getInstance("X509");
++ for (Object cert : signature.getJSONArray("certificates")) {
++ X509Certificate c = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(Base64.decode(cert.toString().toCharArray())));
++ try {
++ c.checkValidity();
++ } catch (CertificateExpiredException e) { // even if the certificate isn't valid yet, we'll proceed it anyway
++ warning = FormValidation.warning(e,String.format("Certificate %s has expired in %s",cert.toString(),name));
++ } catch (CertificateNotYetValidException e) {
++ warning = FormValidation.warning(e,String.format("Certificate %s is not yet valid in %s",cert.toString(),name));
++ }
++ 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<TrustAnchor> anchors = new HashSet<TrustAnchor>(); // CertificateUtil.getDefaultRootCAs();
++ Jenkins j = Jenkins.getInstance();
++ for (String cert : (Set<String>) j.servletContext.getResourcePaths("/WEB-INF/update-center-rootCAs")) {
++ if (cert.endsWith(".txt")) continue; // skip text files that are meant to be documentation
++ anchors.add(new TrustAnchor((X509Certificate)cf.generateCertificate(j.servletContext.getResourceAsStream(cert)),null));
++ }
++ File[] cas = new File(j.root, "update-center-rootCAs").listFiles();
++ if (cas!=null) {
++ for (File cert : cas) {
++ if (cert.getName().endsWith(".txt")) continue; // skip text files that are meant to be documentation
++ FileInputStream in = new FileInputStream(cert);
++ try {
++ anchors.add(new TrustAnchor((X509Certificate)cf.generateCertificate(in),null));
++ } finally {
++ in.close();
++ }
++ }
++ }
++ CertificateUtil.validatePath(certs, anchors);
++ }
++
++ // this is for computing a digest to check sanity
++ MessageDigest sha1 = MessageDigest.getInstance("SHA1");
++ DigestOutputStream dos = new DigestOutputStream(new NullOutputStream(),sha1);
++
++ // this is for computing a signature
++ Signature sig = Signature.getInstance("SHA1withRSA");
++ sig.initVerify(certs.get(0));
++ SignatureOutputStream sos = new SignatureOutputStream(sig);
++
++ // until JENKINS-11110 fix, UC used to serve invalid digest (and therefore unverifiable signature)
++ // that only covers the earlier portion of the file. This was caused by the lack of close() call
++ // in the canonical writing, which apparently leave some bytes somewhere that's not flushed to
++ // the digest output stream. This affects Jenkins [1.424,1,431].
++ // Jenkins 1.432 shipped with the "fix" (1eb0c64abb3794edce29cbb1de50c93fa03a8229) that made it
++ // compute the correct digest, but it breaks all the existing UC json metadata out there. We then
++ // quickly discovered ourselves in the catch-22 situation. If we generate UC with the correct signature,
++ // it'll cut off [1.424,1.431] from the UC. But if we don't, we'll cut off [1.432,*).
++ //
++ // In 1.433, we revisited 1eb0c64abb3794edce29cbb1de50c93fa03a8229 so that the original "digest"/"signature"
++ // pair continues to be generated in a buggy form, while "correct_digest"/"correct_signature" are generated
++ // correctly.
++ //
++ // Jenkins should ignore "digest"/"signature" pair. Accepting it creates a vulnerability that allows
++ // the attacker to inject a fragment at the end of the json.
++ o.writeCanonical(new OutputStreamWriter(new TeeOutputStream(dos,sos),"UTF-8")).close();
++
++ // did the digest match? this is not a part of the signature validation, but if we have a bug in the c14n
++ // (which is more likely than someone tampering with update center), we can tell
++ String computedDigest = new String(Base64.encode(sha1.digest()));
++ String providedDigest = signature.optString("correct_digest");
++ if (providedDigest==null) {
++ return FormValidation.error("No correct_digest parameter in "+name+". This metadata appears to be old.");
++ }
++ if (!computedDigest.equalsIgnoreCase(providedDigest)) {
++ return FormValidation.error("Digest mismatch: "+computedDigest+" vs "+providedDigest+" in "+name);
++ }
++
++ String providedSignature = signature.getString("correct_signature");
++ if (!sig.verify(Base64.decode(providedSignature.toCharArray()))) {
++ return FormValidation.error("Signature in the update center doesn't match with the certificate in "+name);
++ }
++
++ if (warning!=null) return warning;
++ return FormValidation.ok();
++ } catch (GeneralSecurityException e) {
++ return FormValidation.error(e,"Signature verification failed in "+name);
++ }
++ }
++}
+diff --git a/core/src/main/resources/hudson/tasks/junit/History/index.jelly b/core/src/main/resources/hudson/tasks/junit/History/index.jelly
+index 8ad7ee4..cda7a24 100644
--- a/core/src/main/resources/hudson/tasks/junit/History/index.jelly
+++ b/core/src/main/resources/hudson/tasks/junit/History/index.jelly
@@ -26,8 +26,8 @@ THE SOFTWARE.
@@ -79,6 +445,8 @@ Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=688298
<j:set var="rangeParameters" value="start=${start}&end=${end+1}"/>
<script type="text/javascript">
function setCount() {
+diff --git a/war/src/main/webapp/scripts/hudson-behavior.js b/war/src/main/webapp/scripts/hudson-behavior.js
+index f4abe0b..7b74dda 100644
--- a/war/src/main/webapp/scripts/hudson-behavior.js
+++ b/war/src/main/webapp/scripts/hudson-behavior.js
@@ -2308,12 +2308,69 @@ function loadScript(href,callback) {
--
jenkins packaging
More information about the pkg-java-commits
mailing list