CVE-2014-2058: Permissions, Privileges, and Access Controls (CWE-264)
value: BuildTrigger in Jenkins before 1.551 and LTS before 1.532.2 allows remote authenticated users to bypass access restrictions and execute arbitrary jobs by configuring a job to trigger another job. NOTE: this vulnerability exists because of an incomplete fix for CVE-2013-7330.
CWE description: Weaknesses in this category are related to the management of permissions, privileges, and other security features that are used to perform access control.
Published: 2014-10-17T15:55Z
Repository: https://github.com/jenkinsci/jenkins
Language: Java
Files changed: core/src/main/java/hudson/model/AbstractProject.java, test/src/test/java/hudson/tasks/BuildTriggerTest.java
Commit date: 2014-02-11 14:22:46 -0500
Commit message: [FIXED SECURITY-109] SECURITY-55 fix to BuildTrigger configuration failed if downstream project was not visible.

Diff: diff --git a/core/src/main/java/hudson/model/AbstractProject.java b/core/src/main/java/hudson/model/AbstractProject.java
index 99fb7054fa..bb5e06d998 100644
--- a/core/src/main/java/hudson/model/AbstractProject.java
+++ b/core/src/main/java/hudson/model/AbstractProject.java
@@ -1937,11 +1937,18 @@ public abstract class AbstractProject<P extends AbstractProject<P,R>,R extends A
         for (Trigger t : triggers)
             t.start(this,true);
 
         for (Publisher _t : Descriptor.newInstancesFromHeteroList(req, json, "publisher", Jenkins.getInstance().getExtensionList(BuildTrigger.DescriptorImpl.class))) {
             BuildTrigger t = (BuildTrigger) _t;
-            for (AbstractProject downstream : t.getChildProjects(this)) {
+            List<AbstractProject> childProjects;
+            SecurityContext orig = ACL.impersonate(ACL.SYSTEM);
+            try {
+                childProjects = t.getChildProjects(this);
+            } finally {
+                SecurityContextHolder.setContext(orig);
+            }
+            for (AbstractProject downstream : childProjects) {
                 downstream.checkPermission(BUILD);
             }
         }
     }
 
diff --git a/test/src/test/java/hudson/tasks/BuildTriggerTest.java b/test/src/test/java/hudson/tasks/BuildTriggerTest.java
index f9df59e122..2d4f5e5fcb 100644
--- a/test/src/test/java/hudson/tasks/BuildTriggerTest.java
+++ b/test/src/test/java/hudson/tasks/BuildTriggerTest.java
@@ -21,18 +21,30 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
 package hudson.tasks;
 
+import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
 import com.gargoylesoftware.htmlunit.html.HtmlForm;
 import com.gargoylesoftware.htmlunit.html.HtmlPage;
+import com.gargoylesoftware.htmlunit.html.HtmlTextInput;
 import hudson.maven.MavenModuleSet;
 import hudson.maven.MavenModuleSetBuild;
 import hudson.model.FreeStyleBuild;
 import hudson.model.FreeStyleProject;
+import hudson.model.Item;
 import hudson.model.Result;
 import hudson.model.Run;
+import hudson.security.AuthorizationMatrixProperty;
+import hudson.security.LegacySecurityRealm;
+import hudson.security.Permission;
+import hudson.security.ProjectMatrixAuthorizationStrategy;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import jenkins.model.Jenkins;
 import org.jvnet.hudson.test.ExtractResourceSCM;
 import org.jvnet.hudson.test.HudsonTestCase;
 import org.jvnet.hudson.test.MockBuilder;
 
 /**
@@ -129,6 +141,38 @@ public class BuildTriggerTest extends HudsonTestCase {
     }
 
     public void testMavenTriggerEvenWhenUnstable() throws Exception {
         doMavenTriggerTest(true);
     }
+
+    public void testConfigureDownstreamProjectSecurity() throws Exception {
+        jenkins.setSecurityRealm(new LegacySecurityRealm());
+        ProjectMatrixAuthorizationStrategy auth = new ProjectMatrixAuthorizationStrategy();
+        auth.add(Jenkins.READ, "alice");
+        jenkins.setAuthorizationStrategy(auth);
+        FreeStyleProject upstream = createFreeStyleProject("upstream");
+        Map<Permission,Set<String>> perms = new HashMap<Permission,Set<String>>();
+        perms.put(Item.READ, Collections.singleton("alice"));
+        perms.put(Item.CONFIGURE, Collections.singleton("alice"));
+        upstream.addProperty(new AuthorizationMatrixProperty(perms));
+        FreeStyleProject downstream = createFreeStyleProject("downstream");
+        /* Original SECURITY-55 test case:
+        downstream.addProperty(new AuthorizationMatrixProperty(Collections.singletonMap(Item.READ, Collections.singleton("alice"))));
+        */
+        WebClient wc = createWebClient();
+        wc.login("alice");
+        HtmlPage page = wc.getPage(upstream, "configure");
+        HtmlForm config = page.getFormByName("config");
+        config.getButtonByCaption("Add post-build action").click(); // lib/hudson/project/config-publishers2.jelly
+        page.getAnchorByText("Build other projects").click();
+        HtmlTextInput childProjects = config.getInputByName("buildTrigger.childProjects");
+        childProjects.setValueAttribute("downstream");
+        try {
+            submit(config);
+            fail();
+        } catch (FailingHttpStatusCodeException x) {
+            assertEquals(403, x.getStatusCode());
+        }
+        assertEquals(Collections.emptyList(), upstream.getDownstreamProjects());
+    }
+
 }

Vulnerable code:             for (AbstractProject downstream : t.getChildProjects(this)) {
Fixed code:             List<AbstractProject> childProjects;
            SecurityContext orig = ACL.impersonate(ACL.SYSTEM);
            try {
                childProjects = t.getChildProjects(this);
            } finally {
                SecurityContextHolder.setContext(orig);
            }
            for (AbstractProject downstream : childProjects) {
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.html.HtmlTextInput;
import hudson.model.Item;
import hudson.security.AuthorizationMatrixProperty;
import hudson.security.LegacySecurityRealm;
import hudson.security.Permission;
import hudson.security.ProjectMatrixAuthorizationStrategy;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import jenkins.model.Jenkins;

    public void testConfigureDownstreamProjectSecurity() throws Exception {
        jenkins.setSecurityRealm(new LegacySecurityRealm());
        ProjectMatrixAuthorizationStrategy auth = new ProjectMatrixAuthorizationStrategy();
        auth.add(Jenkins.READ, "alice");
        jenkins.setAuthorizationStrategy(auth);
        FreeStyleProject upstream = createFreeStyleProject("upstream");
        Map<Permission,Set<String>> perms = new HashMap<Permission,Set<String>>();
        perms.put(Item.READ, Collections.singleton("alice"));
        perms.put(Item.CONFIGURE, Collections.singleton("alice"));
        upstream.addProperty(new AuthorizationMatrixProperty(perms));
        FreeStyleProject downstream = createFreeStyleProject("downstream");
        /* Original SECURITY-55 test case:
        downstream.addProperty(new AuthorizationMatrixProperty(Collections.singletonMap(Item.READ, Collections.singleton("alice"))));
        */
        WebClient wc = createWebClient();
        wc.login("alice");
        HtmlPage page = wc.getPage(upstream, "configure");
        HtmlForm config = page.getFormByName("config");
        config.getButtonByCaption("Add post-build action").click(); // lib/hudson/project/config-publishers2.jelly
        page.getAnchorByText("Build other projects").click();
        HtmlTextInput childProjects = config.getInputByName("buildTrigger.childProjects");
        childProjects.setValueAttribute("downstream");
        try {
            submit(config);
            fail();
        } catch (FailingHttpStatusCodeException x) {
            assertEquals(403, x.getStatusCode());
        }
        assertEquals(Collections.emptyList(), upstream.getDownstreamProjects());
    }