提交 8886bea1 编写于 作者: K Kohsuke Kawaguchi

fixed a concurrency bug in the impersonation code

上级 435f6104
...@@ -36,6 +36,7 @@ import java.util.Collection; ...@@ -36,6 +36,7 @@ import java.util.Collection;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.acegisecurity.Authentication; import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder; import org.acegisecurity.context.SecurityContextHolder;
/** /**
...@@ -54,9 +55,7 @@ public class DependencyRunner implements Runnable { ...@@ -54,9 +55,7 @@ public class DependencyRunner implements Runnable {
} }
public void run() { public void run() {
Authentication saveAuth = SecurityContextHolder.getContext().getAuthentication(); SecurityContext oldContext = ACL.impersonate(ACL.SYSTEM);
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM);
try { try {
Set<AbstractProject> topLevelProjects = new HashSet<AbstractProject>(); Set<AbstractProject> topLevelProjects = new HashSet<AbstractProject>();
// Get all top-level projects // Get all top-level projects
...@@ -74,7 +73,7 @@ public class DependencyRunner implements Runnable { ...@@ -74,7 +73,7 @@ public class DependencyRunner implements Runnable {
runnable.run(p); runnable.run(p);
} }
} finally { } finally {
SecurityContextHolder.getContext().setAuthentication(saveAuth); SecurityContextHolder.setContext(oldContext);
} }
} }
......
...@@ -31,6 +31,7 @@ import java.io.IOException; ...@@ -31,6 +31,7 @@ import java.io.IOException;
import java.util.logging.Level; import java.util.logging.Level;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder; import org.acegisecurity.context.SecurityContextHolder;
/** /**
...@@ -70,8 +71,8 @@ public abstract class AsyncAperiodicWork extends AperiodicWork { ...@@ -70,8 +71,8 @@ public abstract class AsyncAperiodicWork extends AperiodicWork {
StreamTaskListener l = createListener(); StreamTaskListener l = createListener();
try { try {
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM); ACL.impersonate(ACL.SYSTEM);
execute(l); execute(l);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(l.fatalError(e.getMessage())); e.printStackTrace(l.fatalError(e.getMessage()));
......
...@@ -3,6 +3,7 @@ package hudson.model; ...@@ -3,6 +3,7 @@ package hudson.model;
import hudson.security.ACL; import hudson.security.ACL;
import hudson.util.StreamTaskListener; import hudson.util.StreamTaskListener;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder; import org.acegisecurity.context.SecurityContextHolder;
import java.io.File; import java.io.File;
...@@ -47,8 +48,8 @@ public abstract class AsyncPeriodicWork extends PeriodicWork { ...@@ -47,8 +48,8 @@ public abstract class AsyncPeriodicWork extends PeriodicWork {
StreamTaskListener l = createListener(); StreamTaskListener l = createListener();
try { try {
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM); ACL.impersonate(ACL.SYSTEM);
execute(l); execute(l);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(l.fatalError(e.getMessage())); e.printStackTrace(l.fatalError(e.getMessage()));
......
...@@ -84,12 +84,9 @@ public class DependencyGraph implements Comparator<AbstractProject> { ...@@ -84,12 +84,9 @@ public class DependencyGraph implements Comparator<AbstractProject> {
public void build() { public void build() {
// Set full privileges while computing to avoid missing any projects the current user cannot see. // Set full privileges while computing to avoid missing any projects the current user cannot see.
// Use setContext (NOT getContext().setAuthentication()) so we don't affect concurrent threads for same HttpSession. // Use setContext (NOT getContext().setAuthentication()) so we don't affect concurrent threads for same HttpSession.
SecurityContext saveCtx = SecurityContextHolder.getContext(); SecurityContext saveCtx = ACL.impersonate(ACL.SYSTEM);
try { try {
this.computationalData = new HashMap<Class<?>, Object>(); this.computationalData = new HashMap<Class<?>, Object>();
NotSerilizableSecurityContext system = new NotSerilizableSecurityContext();
system.setAuthentication(ACL.SYSTEM);
SecurityContextHolder.setContext(system);
for( AbstractProject p : getAllProjects() ) for( AbstractProject p : getAllProjects() )
p.buildDependencyGraph(this); p.buildDependencyGraph(this);
......
...@@ -38,6 +38,7 @@ import hudson.security.ACL; ...@@ -38,6 +38,7 @@ import hudson.security.ACL;
import jenkins.model.InterruptedBuildAction; import jenkins.model.InterruptedBuildAction;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
import org.acegisecurity.Authentication; import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
import org.kohsuke.stapler.HttpResponse; import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses; import org.kohsuke.stapler.HttpResponses;
...@@ -175,7 +176,7 @@ public class Executor extends Thread implements ModelObject { ...@@ -175,7 +176,7 @@ public class Executor extends Thread implements ModelObject {
@Override @Override
public void run() { public void run() {
// run as the system user. see ACL.SYSTEM for more discussion about why this is somewhat broken // run as the system user. see ACL.SYSTEM for more discussion about why this is somewhat broken
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM); ACL.impersonate(ACL.SYSTEM);
try { try {
finishTime = System.currentTimeMillis(); finishTime = System.currentTimeMillis();
......
...@@ -48,6 +48,7 @@ import hudson.util.XStream2; ...@@ -48,6 +48,7 @@ import hudson.util.XStream2;
import jenkins.RestartRequiredException; import jenkins.RestartRequiredException;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
import org.acegisecurity.Authentication; import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContext;
import org.apache.commons.io.input.CountingInputStream; import org.apache.commons.io.input.CountingInputStream;
import org.apache.commons.io.output.NullOutputStream; import org.apache.commons.io.output.NullOutputStream;
import org.jvnet.localizer.Localizable; import org.jvnet.localizer.Localizable;
...@@ -1121,13 +1122,14 @@ public class UpdateCenter extends AbstractModelObject implements Saveable { ...@@ -1121,13 +1122,14 @@ public class UpdateCenter extends AbstractModelObject implements Saveable {
// if this is a bundled plugin, make sure it won't get overwritten // if this is a bundled plugin, make sure it won't get overwritten
PluginWrapper pw = plugin.getInstalled(); PluginWrapper pw = plugin.getInstalled();
if (pw!=null && pw.isBundled()) if (pw!=null && pw.isBundled()) {
SecurityContext oldContext = ACL.impersonate(ACL.SYSTEM);
try { try {
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM);
pw.doPin(); pw.doPin();
} finally { } finally {
SecurityContextHolder.clearContext(); SecurityContextHolder.setContext(oldContext);
} }
}
if (dynamicLoad) { if (dynamicLoad) {
try { try {
......
...@@ -26,6 +26,8 @@ package hudson.security; ...@@ -26,6 +26,8 @@ package hudson.security;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
import org.acegisecurity.AccessDeniedException; import org.acegisecurity.AccessDeniedException;
import org.acegisecurity.Authentication; import org.acegisecurity.Authentication;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken; import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.acls.sid.PrincipalSid; import org.acegisecurity.acls.sid.PrincipalSid;
import org.acegisecurity.acls.sid.Sid; import org.acegisecurity.acls.sid.Sid;
...@@ -113,4 +115,22 @@ public abstract class ACL { ...@@ -113,4 +115,22 @@ public abstract class ACL {
* the user who triggered a build.) * the user who triggered a build.)
*/ */
public static final Authentication SYSTEM = new UsernamePasswordAuthenticationToken("SYSTEM","SYSTEM"); public static final Authentication SYSTEM = new UsernamePasswordAuthenticationToken("SYSTEM","SYSTEM");
/**
* Changes the {@link Authentication} associated with the current thread
* to the specified one, and returns the previous security context.
*
* <p>
* When the impersonation is over, be sure to restore the previous authentication
* via {@code SecurityContextHolder.setContext(returnValueFromThisMethod)}.
*
* <p>
* We need to create a new {@link SecurityContext} instead of {@link SecurityContext#setAuthentication(Authentication)}
* because the same {@link SecurityContext} object is reused for all the concurrent requests from the same session.
*/
public static SecurityContext impersonate(Authentication auth) {
SecurityContext old = SecurityContextHolder.getContext();
SecurityContextHolder.setContext(new NotSerilizableSecurityContext(ACL.SYSTEM));
return old;
}
} }
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
*/ */
package hudson.triggers; package hudson.triggers;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder; import org.acegisecurity.context.SecurityContextHolder;
import java.util.Timer; import java.util.Timer;
...@@ -48,14 +49,13 @@ public abstract class SafeTimerTask extends TimerTask { ...@@ -48,14 +49,13 @@ public abstract class SafeTimerTask extends TimerTask {
public final void run() { public final void run() {
// background activity gets system credential, // background activity gets system credential,
// just like executors get it. // just like executors get it.
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM); SecurityContext oldContext = ACL.impersonate(ACL.SYSTEM);
try { try {
doRun(); doRun();
} catch(Throwable t) { } catch(Throwable t) {
LOGGER.log(Level.SEVERE, "Timer task "+this+" failed",t); LOGGER.log(Level.SEVERE, "Timer task "+this+" failed",t);
} finally { } finally {
SecurityContextHolder.clearContext(); SecurityContextHolder.setContext(oldContext);
} }
} }
......
...@@ -200,6 +200,7 @@ import org.acegisecurity.AcegiSecurityException; ...@@ -200,6 +200,7 @@ import org.acegisecurity.AcegiSecurityException;
import org.acegisecurity.Authentication; import org.acegisecurity.Authentication;
import org.acegisecurity.GrantedAuthority; import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl; import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder; import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
import org.acegisecurity.ui.AbstractProcessingFilter; import org.acegisecurity.ui.AbstractProcessingFilter;
...@@ -702,7 +703,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe ...@@ -702,7 +703,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
// As Jenkins is starting, grant this process full control // As Jenkins is starting, grant this process full control
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM); ACL.impersonate(ACL.SYSTEM);
try { try {
this.root = root; this.root = root;
this.servletContext = context; this.servletContext = context;
...@@ -826,7 +827,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe ...@@ -826,7 +827,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
protected void runTask(Task task) throws Exception { protected void runTask(Task task) throws Exception {
if (is!=null && is.skipInitTask(task)) return; if (is!=null && is.skipInitTask(task)) return;
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM); // full access in the initialization thread ACL.impersonate(ACL.SYSTEM); // full access in the initialization thread
String taskName = task.getDisplayName(); String taskName = task.getDisplayName();
Thread t = Thread.currentThread(); Thread t = Thread.currentThread();
...@@ -2912,7 +2913,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe ...@@ -2912,7 +2913,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
@Override @Override
public void run() { public void run() {
try { try {
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM); ACL.impersonate(ACL.SYSTEM);
reload(); reload();
} catch (Exception e) { } catch (Exception e) {
LOGGER.log(SEVERE,"Failed to reload Jenkins config",e); LOGGER.log(SEVERE,"Failed to reload Jenkins config",e);
...@@ -3081,7 +3082,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe ...@@ -3081,7 +3082,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
@Override @Override
public void run() { public void run() {
try { try {
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM); ACL.impersonate(ACL.SYSTEM);
// give some time for the browser to load the "reloading" page // give some time for the browser to load the "reloading" page
Thread.sleep(5000); Thread.sleep(5000);
...@@ -3113,7 +3114,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe ...@@ -3113,7 +3114,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
@Override @Override
public void run() { public void run() {
try { try {
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM); ACL.impersonate(ACL.SYSTEM);
// Wait 'til we have no active executors. // Wait 'til we have no active executors.
doQuietDown(true, 0); doQuietDown(true, 0);
...@@ -3175,7 +3176,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe ...@@ -3175,7 +3176,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
@Override @Override
public void run() { public void run() {
try { try {
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM); ACL.impersonate(ACL.SYSTEM);
LOGGER.severe(String.format("Shutting down VM as requested by %s from %s", LOGGER.severe(String.format("Shutting down VM as requested by %s from %s",
exitUser, exitAddr)); exitUser, exitAddr));
// Wait 'til we have no active executors. // Wait 'til we have no active executors.
......
package jenkins.security; package jenkins.security;
import hudson.model.User; import hudson.model.User;
import hudson.security.ACL;
import hudson.util.Scrambler; import hudson.util.Scrambler;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder; import org.acegisecurity.context.SecurityContextHolder;
import javax.servlet.Filter; import javax.servlet.Filter;
...@@ -47,12 +49,12 @@ public class ApiTokenFilter implements Filter { ...@@ -47,12 +49,12 @@ public class ApiTokenFilter implements Filter {
if (t!=null && t.matchesPassword(password)) { if (t!=null && t.matchesPassword(password)) {
// even if we fail to match the password, we aren't rejecting it. // even if we fail to match the password, we aren't rejecting it.
// as the user might be passing in a real password. // as the user might be passing in a real password.
SecurityContextHolder.getContext().setAuthentication(u.impersonate()); SecurityContext oldContext = ACL.impersonate(u.impersonate());
try { try {
chain.doFilter(request,response); chain.doFilter(request,response);
return; return;
} finally { } finally {
SecurityContextHolder.clearContext(); SecurityContextHolder.setContext(oldContext);
} }
} }
} }
......
...@@ -131,6 +131,7 @@ import net.sourceforge.htmlunit.corejs.javascript.ContextFactory.Listener; ...@@ -131,6 +131,7 @@ import net.sourceforge.htmlunit.corejs.javascript.ContextFactory.Listener;
import org.acegisecurity.AuthenticationException; import org.acegisecurity.AuthenticationException;
import org.acegisecurity.BadCredentialsException; import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.GrantedAuthority; import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder; import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.userdetails.UserDetails; import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UsernameNotFoundException; import org.acegisecurity.userdetails.UsernameNotFoundException;
...@@ -394,7 +395,7 @@ public abstract class HudsonTestCase extends TestCase implements RootAction { ...@@ -394,7 +395,7 @@ public abstract class HudsonTestCase extends TestCase implements RootAction {
protected void runTest() throws Throwable { protected void runTest() throws Throwable {
System.out.println("=== Starting "+ getClass().getSimpleName() + "." + getName()); System.out.println("=== Starting "+ getClass().getSimpleName() + "." + getName());
// so that test code has all the access to the system // so that test code has all the access to the system
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM); ACL.impersonate(ACL.SYSTEM);
try { try {
super.runTest(); super.runTest();
......
...@@ -121,6 +121,7 @@ import net.sourceforge.htmlunit.corejs.javascript.ContextFactory; ...@@ -121,6 +121,7 @@ import net.sourceforge.htmlunit.corejs.javascript.ContextFactory;
import org.acegisecurity.AuthenticationException; import org.acegisecurity.AuthenticationException;
import org.acegisecurity.BadCredentialsException; import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.GrantedAuthority; import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder; import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.userdetails.UserDetails; import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UsernameNotFoundException; import org.acegisecurity.userdetails.UsernameNotFoundException;
...@@ -431,7 +432,7 @@ public class JenkinsRule implements TestRule, RootAction { ...@@ -431,7 +432,7 @@ public class JenkinsRule implements TestRule, RootAction {
try { try {
System.out.println("=== Starting " + testDescription.getDisplayName()); System.out.println("=== Starting " + testDescription.getDisplayName());
// so that test code has all the access to the system // so that test code has all the access to the system
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM); ACL.impersonate(ACL.SYSTEM);
try { try {
base.evaluate(); base.evaluate();
} catch (Throwable th) { } catch (Throwable th) {
......
...@@ -29,6 +29,8 @@ import hudson.tasks.MailMessageIdAction; ...@@ -29,6 +29,8 @@ import hudson.tasks.MailMessageIdAction;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder; import org.acegisecurity.context.SecurityContextHolder;
import org.jvnet.hudson.test.HudsonTestCase; import org.jvnet.hudson.test.HudsonTestCase;
import org.jvnet.hudson.test.Bug; import org.jvnet.hudson.test.Bug;
...@@ -114,7 +116,7 @@ public class DependencyGraphTest extends HudsonTestCase { ...@@ -114,7 +116,7 @@ public class DependencyGraphTest extends HudsonTestCase {
hudson.rebuildDependencyGraph(); hudson.rebuildDependencyGraph();
try { try {
// Switch to full access to check results: // Switch to full access to check results:
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM); ACL.impersonate(ACL.SYSTEM);
// @LocalData for this test has jobs w/o anonymous Item.READ // @LocalData for this test has jobs w/o anonymous Item.READ
AbstractProject up = (AbstractProject)hudson.getItem("hiddenUpstream"); AbstractProject up = (AbstractProject)hudson.getItem("hiddenUpstream");
assertNotNull("hiddenUpstream project not found", up); assertNotNull("hiddenUpstream project not found", up);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册