7
7
*/
8
8
package svnserver .auth ;
9
9
10
- import com .unboundid .ldap .sdk .DIGESTMD5BindRequest ;
11
- import com .unboundid .ldap .sdk .LDAPConnection ;
12
- import com .unboundid .ldap .sdk .LDAPException ;
13
- import com .unboundid .ldap .sdk .ResultCode ;
10
+ import com .unboundid .ldap .sdk .*;
14
11
import com .unboundid .util .ssl .SSLUtil ;
15
12
import com .unboundid .util .ssl .TrustAllTrustManager ;
16
13
import org .jetbrains .annotations .NotNull ;
24
21
25
22
import javax .naming .NamingException ;
26
23
import javax .net .SocketFactory ;
24
+ import javax .net .ssl .TrustManager ;
25
+ import javax .net .ssl .TrustManagerFactory ;
26
+ import javax .net .ssl .X509TrustManager ;
27
+ import javax .xml .bind .DatatypeConverter ;
28
+ import java .io .ByteArrayInputStream ;
29
+ import java .io .File ;
30
+ import java .io .IOException ;
27
31
import java .net .URI ;
32
+ import java .nio .charset .StandardCharsets ;
33
+ import java .nio .file .Files ;
28
34
import java .security .GeneralSecurityException ;
35
+ import java .security .KeyStore ;
36
+ import java .security .KeyStoreException ;
37
+ import java .security .cert .CertificateException ;
38
+ import java .security .cert .CertificateFactory ;
39
+ import java .security .cert .X509Certificate ;
29
40
import java .text .MessageFormat ;
30
41
import java .util .Collection ;
31
42
import java .util .Collections ;
@@ -54,7 +65,7 @@ public final class LDAPUserDB implements UserDB, PasswordChecker {
54
65
@ Nullable
55
66
private final SocketFactory socketFactory ;
56
67
57
- public LDAPUserDB (@ NotNull LDAPUserDBConfig config ) {
68
+ public LDAPUserDB (@ NotNull LDAPUserDBConfig config , @ NotNull File basePath ) {
58
69
URI ldapUri = URI .create (config .getConnectionUrl ());
59
70
SocketFactory factory ;
60
71
int defaultPort ;
@@ -64,7 +75,7 @@ public LDAPUserDB(@NotNull LDAPUserDBConfig config) {
64
75
defaultPort = 389 ;
65
76
break ;
66
77
case "ldaps" :
67
- factory = createSslFactory (config );
78
+ factory = createSslFactory (config , basePath );
68
79
defaultPort = 636 ;
69
80
break ;
70
81
default :
@@ -77,11 +88,25 @@ public LDAPUserDB(@NotNull LDAPUserDBConfig config) {
77
88
this .ldapHost = ldapUri .getHost ();
78
89
}
79
90
80
- private static SocketFactory createSslFactory (@ NotNull LDAPUserDBConfig config ) {
91
+ private static SocketFactory createSslFactory (@ NotNull LDAPUserDBConfig config , @ NotNull File basePath ) {
81
92
try {
82
- return new SSLUtil (null , new TrustAllTrustManager ()).createSSLSocketFactory ();
93
+ final TrustManager trustManager ;
94
+ final String certPem = config .getLdapCertPem ();
95
+ if (certPem != null ) {
96
+ final File certFile = new File (basePath , certPem );
97
+ log .info ("Loading CA certificate from: {}" , certFile .getAbsolutePath ());
98
+ trustManager = createTrustManager (Files .readAllBytes (certFile .toPath ()));
99
+ } else {
100
+ log .error ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" );
101
+ log .error ("CA certificate for LDAP server is not defined. LDAP server validation is disabled" );
102
+ log .error ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" );
103
+ trustManager = new TrustAllTrustManager ();
104
+ }
105
+ return new SSLUtil (null , trustManager ).createSSLSocketFactory ();
83
106
} catch (GeneralSecurityException e ) {
84
- throw new IllegalStateException ("Can't create SSL Socket Factory" , e );
107
+ throw new IllegalStateException (e );
108
+ } catch (IOException e ) {
109
+ throw new IllegalStateException ("Can't load certificate file" , e );
85
110
}
86
111
}
87
112
@@ -92,9 +117,9 @@ public User check(@NotNull String username, @NotNull String password) throws SVN
92
117
LDAPConnection ldap = new LDAPConnection (socketFactory , ldapHost , ldapPort );
93
118
try {
94
119
ldap .bind (new DIGESTMD5BindRequest (username , password ));
95
- com . unboundid . ldap . sdk . SearchResult search = ldap .search (
120
+ SearchResult search = ldap .search (
96
121
baseDn ,
97
- config .isUserSubtree () ? com . unboundid . ldap . sdk . SearchScope .SUB : com . unboundid . ldap . sdk . SearchScope .ONE ,
122
+ config .isUserSubtree () ? SearchScope .SUB : SearchScope .ONE ,
98
123
MessageFormat .format (config .getUserSearch (), username ),
99
124
config .getNameAttribute (), config .getEmailAttribute ()
100
125
);
@@ -105,7 +130,7 @@ public User check(@NotNull String username, @NotNull String password) throws SVN
105
130
log .error ("Multiple LDAP entries found for {}" , username );
106
131
return null ;
107
132
}
108
- final com . unboundid . ldap . sdk . SearchResultEntry entry = search .getSearchEntries ().get (0 );
133
+ final SearchResultEntry entry = search .getSearchEntries ().get (0 );
109
134
final String realName = getAttribute (entry , config .getNameAttribute ());
110
135
final String email = getAttribute (entry , config .getEmailAttribute ());
111
136
return new User (username , realName != null ? realName : username , email );
@@ -123,8 +148,8 @@ public User check(@NotNull String username, @NotNull String password) throws SVN
123
148
}
124
149
125
150
@ Nullable
126
- private String getAttribute (@ NotNull com . unboundid . ldap . sdk . SearchResultEntry entry , @ NotNull String name ) throws NamingException {
127
- com . unboundid . ldap . sdk . Attribute attribute = entry .getAttribute (name );
151
+ private String getAttribute (@ NotNull SearchResultEntry entry , @ NotNull String name ) throws NamingException {
152
+ Attribute attribute = entry .getAttribute (name );
128
153
return attribute == null ? null : attribute .getValue ();
129
154
}
130
155
@@ -133,4 +158,60 @@ private String getAttribute(@NotNull com.unboundid.ldap.sdk.SearchResultEntry en
133
158
public Collection <Authenticator > authenticators () {
134
159
return authenticators ;
135
160
}
161
+
162
+ @ NotNull
163
+ public static byte [] parseDERFromPEM (@ NotNull byte [] pem , @ NotNull String beginDelimiter , @ NotNull String endDelimiter ) throws GeneralSecurityException {
164
+ final String data = new String (pem , StandardCharsets .ISO_8859_1 );
165
+ String [] tokens = data .split (beginDelimiter );
166
+ if (tokens .length != 2 ) {
167
+ throw new GeneralSecurityException ("Invalid PEM certificate data. Delimiter not found: " + beginDelimiter );
168
+ }
169
+ tokens = tokens [1 ].split (endDelimiter );
170
+ if (tokens .length != 2 ) {
171
+ throw new GeneralSecurityException ("Invalid PEM certificate data. Delimiter not found: " + endDelimiter );
172
+ }
173
+ return DatatypeConverter .parseBase64Binary (tokens [0 ]);
174
+ }
175
+
176
+ @ NotNull
177
+ public static KeyStore getKeyStoreFromDER (@ NotNull byte [] certBytes ) throws GeneralSecurityException {
178
+ try {
179
+ final CertificateFactory factory = CertificateFactory .getInstance ("X.509" );
180
+ final KeyStore keystore = KeyStore .getInstance (KeyStore .getDefaultType ());
181
+ keystore .load (null );
182
+ keystore .setCertificateEntry ("alias" , factory .generateCertificate (new ByteArrayInputStream (certBytes )));
183
+ return keystore ;
184
+ } catch (IOException e ) {
185
+ throw new KeyStoreException (e );
186
+ }
187
+ }
188
+
189
+ @ NotNull
190
+ public static TrustManager createTrustManager (@ NotNull byte [] pem ) throws GeneralSecurityException {
191
+ final TrustManagerFactory factory = TrustManagerFactory .getInstance (TrustManagerFactory .getDefaultAlgorithm ());
192
+ final KeyStore keystore = getKeyStoreFromDER (parseDERFromPEM (pem , "-----BEGIN CERTIFICATE-----" , "-----END CERTIFICATE-----" ));
193
+ factory .init (keystore );
194
+
195
+ final TrustManager [] trustManagers = factory .getTrustManagers ();
196
+ return new X509TrustManager () {
197
+ @ Override
198
+ public void checkClientTrusted (X509Certificate [] x509Certificates , String s ) throws CertificateException {
199
+ for (TrustManager trustManager : trustManagers ) {
200
+ ((X509TrustManager ) trustManager ).checkClientTrusted (x509Certificates , s );
201
+ }
202
+ }
203
+
204
+ @ Override
205
+ public void checkServerTrusted (X509Certificate [] x509Certificates , String s ) throws CertificateException {
206
+ for (TrustManager trustManager : trustManagers ) {
207
+ ((X509TrustManager ) trustManager ).checkServerTrusted (x509Certificates , s );
208
+ }
209
+ }
210
+
211
+ @ Override
212
+ public X509Certificate [] getAcceptedIssuers () {
213
+ return new X509Certificate [0 ];
214
+ }
215
+ };
216
+ }
136
217
}
0 commit comments