1
2
3
4
5
6
7
8
9
10 package org.eclipse.jgit.internal.transport.sshd;
11
12 import static org.apache.sshd.core.CoreModuleProperties.PASSWORD_PROMPTS;
13
14 import java.io.IOException;
15 import java.net.URISyntaxException;
16 import java.security.GeneralSecurityException;
17 import java.util.Arrays;
18 import java.util.Map;
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.concurrent.atomic.AtomicInteger;
21 import java.util.function.Supplier;
22
23 import org.apache.sshd.common.AttributeRepository.AttributeKey;
24 import org.apache.sshd.common.NamedResource;
25 import org.apache.sshd.common.config.keys.FilePasswordProvider;
26 import org.apache.sshd.common.session.SessionContext;
27 import org.eclipse.jgit.annotations.NonNull;
28 import org.eclipse.jgit.transport.CredentialsProvider;
29 import org.eclipse.jgit.transport.URIish;
30 import org.eclipse.jgit.transport.sshd.KeyPasswordProvider;
31
32
33
34
35
36 public class PasswordProviderWrapper implements FilePasswordProvider {
37
38 private static final AttributeKey<PerSessionState> STATE = new AttributeKey<>();
39
40 private static class PerSessionState {
41
42 Map<String, AtomicInteger> counts = new ConcurrentHashMap<>();
43
44 KeyPasswordProvider delegate;
45
46 }
47
48 private final Supplier<KeyPasswordProvider> factory;
49
50
51
52
53
54
55
56 public PasswordProviderWrapper(
57 @NonNull Supplier<KeyPasswordProvider> factory) {
58 this.factory = factory;
59 }
60
61 private PerSessionState getState(SessionContext context) {
62 PerSessionState state = context.getAttribute(STATE);
63 if (state == null) {
64 state = new PerSessionState();
65 state.delegate = factory.get();
66 state.delegate.setAttempts(
67 PASSWORD_PROMPTS.getRequiredDefault().intValue());
68 context.setAttribute(STATE, state);
69 }
70 return state;
71 }
72
73 @Override
74 public String getPassword(SessionContext session, NamedResource resource,
75 int attemptIndex) throws IOException {
76 String key = resource.getName();
77 PerSessionState state = getState(session);
78 int attempt = state.counts
79 .computeIfAbsent(key, k -> new AtomicInteger()).get();
80 char[] passphrase = state.delegate.getPassphrase(toUri(key), attempt);
81 if (passphrase == null) {
82 return null;
83 }
84 try {
85 return new String(passphrase);
86 } finally {
87 Arrays.fill(passphrase, '\000');
88 }
89 }
90
91 @Override
92 public ResourceDecodeResult handleDecodeAttemptResult(
93 SessionContext session, NamedResource resource, int retryIndex,
94 String password, Exception err)
95 throws IOException, GeneralSecurityException {
96 String key = resource.getName();
97 PerSessionState state = getState(session);
98 AtomicInteger count = state.counts.get(key);
99 int numberOfAttempts = count == null ? 0 : count.incrementAndGet();
100 ResourceDecodeResult result = null;
101 try {
102 if (state.delegate.keyLoaded(toUri(key), numberOfAttempts, err)) {
103 result = ResourceDecodeResult.RETRY;
104 } else {
105 result = ResourceDecodeResult.TERMINATE;
106 }
107 } finally {
108 if (result != ResourceDecodeResult.RETRY) {
109 state.counts.remove(key);
110 }
111 }
112 return result;
113 }
114
115
116
117
118
119
120
121
122
123 private URIish toUri(String resourceKey) {
124 try {
125 return new URIish(resourceKey);
126 } catch (URISyntaxException e) {
127 return new URIish().setPath(resourceKey);
128 }
129 }
130
131 }