Skip to content

Commit

Permalink
SECURITY-3342
Browse files Browse the repository at this point in the history
  • Loading branch information
BorisYaoA committed Apr 24, 2024
1 parent 20c48ed commit eb68868
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 1 deletion.
14 changes: 13 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,24 @@
<groupId>org.jenkins-ci.modules</groupId>
<artifactId>sshd</artifactId>
</dependency>

<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>git-client</artifactId>
</dependency>

<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit.ssh.apache</artifactId>
<version>6.9.0.202403050737-r</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>git-userContent</artifactId>
<version>1.4</version>
<scope>test</scope>
</dependency>

</dependencies>

<repositories>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ protected FileBackedHttpGitRepository(File workspace) {

@Override
public Repository openRepository() throws IOException {
checkPullPermission();
Repository r = new FileRepositoryBuilder().setWorkTree(workspace).build();

// if the repository doesn't exist, create it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ public HttpGitRepository() {
*/
public abstract UploadPack createUploadPack(HttpServletRequest context, Repository db) throws ServiceNotEnabledException, ServiceNotAuthorizedException;

/**
* to make sure the user has the permission to pull.
*/
public void checkPullPermission() {
Jenkins.getInstance().checkPermission(Jenkins.READ);
}

protected GitServlet init() {
GitServlet g = new GitServlet();
g.setRepositoryResolver(new org.eclipse.jgit.transport.resolver.RepositoryResolver<HttpServletRequest>() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package org.jenkinsci.plugins.gitserver.ssh;

import hudson.Functions;
import java.net.InetSocketAddress;
import java.security.PublicKey;
import java.util.Collections;
import java.util.List;
import jenkins.model.Jenkins;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.transport.SshSessionFactory;
import org.eclipse.jgit.transport.sshd.ServerKeyDatabase;
import org.eclipse.jgit.transport.sshd.SshdSessionFactory;
import org.jenkinsci.main.modules.cli.auth.ssh.PublicKeySignatureWriter;
import org.jenkinsci.main.modules.cli.auth.ssh.UserPropertyImpl;
import org.jenkinsci.main.modules.sshd.SSHD;
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.jvnet.hudson.test.MockAuthorizationStrategy;

import java.io.File;
import java.io.IOException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;

public class Security3342Test {

@Rule
public JenkinsRule j = new JenkinsRule();

@Rule
public TemporaryFolder tmp = new TemporaryFolder();

@Before
public void setUp() {
assumeFalse(Functions.isWindows());
}

@Test
@Issue("SECURITY-3342")
public void openRepositoryPermissionCheckTest() throws Exception {

j.jenkins.setSecurityRealm(j.createDummySecurityRealm());
j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy().grant(Jenkins.READ).everywhere().to("tester"));
hudson.model.User tester = hudson.model.User.getOrCreateByIdOrFullName("tester");
KeyPair keyPair = generateKeys(tester);
j.jenkins.save();

// Fixed ssh port for Jenkins ssh server
SSHD server = SSHD.get();
server.setPort(2222);

final SshdSessionFactory instance = new SshdSessionFactory() {
@Override
protected Iterable<KeyPair> getDefaultKeys(File sshDir) {
return List.of(keyPair);
}

@Override
protected ServerKeyDatabase getServerKeyDatabase(File homeDir, File sshDir) {
return new ServerKeyDatabase() {
@Override
public List<PublicKey> lookup(String connectAddress, InetSocketAddress remoteAddress, Configuration config) {
return Collections.emptyList();
}

@Override
public boolean accept(String connectAddress, InetSocketAddress remoteAddress, PublicKey serverKey, Configuration config, CredentialsProvider provider) {
return true;
}
};
}
};
SshSessionFactory.setInstance(instance);

CloneCommand clone = Git.cloneRepository();
clone.setURI("ssh://tester@localhost:2222/userContent.git");
File dir1 = tmp.newFolder();
clone.setDirectory(dir1);

// Do the git clone for a user with Jenkins.READ permission
Git gitClone1 = clone.call();
File gitDir1 = new File(dir1, ".git");
assertTrue(".git directory exist, clone operation succeed", gitDir1.exists());

// Do the git clone for a user without Jenkins.READ permission
j.jenkins.setAuthorizationStrategy(new MockAuthorizationStrategy());
j.jenkins.save();

File dir2 = tmp.newFolder();
clone.setDirectory(dir2);

try {
Git gitClone2 = clone.call();
} catch (Exception e) {
// Verify that the expected exception was caught with the correct message
assertTrue(e.getCause() != null && e.getCause().getMessage().contains("hudson.security.AccessDeniedException3: tester is missing the Overall/Read permission"));
}
// Verify that the .git directory is not created
File gitDir2 = new File(dir2, ".git");
assertFalse(".git directory exist, clone operation succeed", gitDir2.exists());

}

private static KeyPair generateKeys(hudson.model.User user) throws NoSuchAlgorithmException, IOException {
// I'd prefer to generate Ed25519 keys here, but the API is too awkward currently
// ECDSA keys would be even more awkward as we'd need a copy of the curve parameters
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(2048);
KeyPair keyPair = generator.generateKeyPair();
String encodedPublicKey = "ssh-rsa " + new PublicKeySignatureWriter().asString(keyPair.getPublic());
user.addProperty(new UserPropertyImpl(encodedPublicKey));
return keyPair;
}
}

0 comments on commit eb68868

Please sign in to comment.