Skip to content

Commit

Permalink
Core: Handle security manager permission for deprecation log rolling
Browse files Browse the repository at this point in the history
When the deprecation log is written to within scripting support code
like ScriptDocValues, it runs under the reduces privileges of scripts.
Sometimes this can trigger log rolling, which then causes uncaught
security errors, as was handled in elastic#28485. While doing individual
deprecation handling within each deprecation scripting location is
possible, there are a growing number of deprecations in scripts.

This commit wraps the logging call within the deprecation logger use a
doPrivileged block, just was we would within individual logging call
sites for scripting utilities.
  • Loading branch information
rjernst committed Jan 9, 2019
1 parent 722b850 commit 778b077
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import org.elasticsearch.common.util.concurrent.ThreadContext;

import java.nio.charset.Charset;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
Expand Down Expand Up @@ -318,7 +320,10 @@ void deprecated(final Set<ThreadContext> threadContexts, final String message, f
}

if (log) {
logger.warn(message, params);
AccessController.doPrivileged((PrivilegedAction<Void>)() -> {
logger.warn(message, params);
return null;
});
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,46 @@
package org.elasticsearch.common.logging;

import com.carrotsearch.randomizedtesting.generators.CodepointSetGenerator;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.simple.SimpleLoggerContext;
import org.apache.logging.log4j.simple.SimpleLoggerContextFactory;
import org.apache.logging.log4j.spi.ExtendedLogger;
import org.apache.logging.log4j.spi.LoggerContext;
import org.apache.logging.log4j.spi.LoggerContextFactory;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.hamcrest.RegexMatcher;
import org.hamcrest.core.IsSame;

import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Permissions;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.IntStream;
import java.nio.charset.StandardCharsets;

import static org.elasticsearch.common.logging.DeprecationLogger.WARNING_HEADER_PATTERN;
import static org.elasticsearch.test.hamcrest.RegexMatcher.matches;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.core.Is.is;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

/**
* Tests {@link DeprecationLogger}
Expand Down Expand Up @@ -303,6 +319,49 @@ public void testWarningHeaderSizeSetting() throws IOException{
}
}

public void testLogPermissions() {
AtomicBoolean supplierCalled = new AtomicBoolean(false);

// mocking the logger used inside DeprecationLogger requires heavy hacking...
Logger parentLogger = mock(Logger.class);
when(parentLogger.getName()).thenReturn("logger");
ExtendedLogger mockLogger = mock(ExtendedLogger.class);
doAnswer(invocationOnMock -> {
supplierCalled.set(true);
createTempDir(); // trigger file permission, like rolling logs would
return null;
}).when(mockLogger).warn("foo", new Object[] {"bar"});
final LoggerContext context = new SimpleLoggerContext() {
@Override
public ExtendedLogger getLogger(String name) {
return mockLogger;
}
};

final LoggerContextFactory originalFactory = LogManager.getFactory();
try {
LogManager.setFactory(new SimpleLoggerContextFactory() {
@Override
public LoggerContext getContext(String fqcn, ClassLoader loader, Object externalContext, boolean currentContext,
URI configLocation, String name) {
return context;
}
});
DeprecationLogger deprecationLogger = new DeprecationLogger(parentLogger);

AccessControlContext noPermissionsAcc = new AccessControlContext(
new ProtectionDomain[]{new ProtectionDomain(null, new Permissions())}
);
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
deprecationLogger.deprecated("foo", "bar");
return null;
}, noPermissionsAcc);
assertThat("supplier called", supplierCalled.get(), is(true));
} finally {
LogManager.setFactory(originalFactory);
}
}

private String range(int lowerInclusive, int upperInclusive) {
return IntStream
.range(lowerInclusive, upperInclusive + 1)
Expand Down

0 comments on commit 778b077

Please sign in to comment.