View | Details | Raw Unified | Return to bug 49785
Collapse All | Expand All

(-)a/java/org/apache/catalina/realm/JNDIRealm.java (-16 / +202 lines)
Lines 17-27 Link Here
17
17
18
package org.apache.catalina.realm;
18
package org.apache.catalina.realm;
19
19
20
import java.io.IOException;
20
import java.net.URI;
21
import java.net.URI;
21
import java.net.URISyntaxException;
22
import java.net.URISyntaxException;
23
import java.security.KeyManagementException;
24
import java.security.NoSuchAlgorithmException;
22
import java.security.Principal;
25
import java.security.Principal;
23
import java.text.MessageFormat;
26
import java.text.MessageFormat;
24
import java.util.ArrayList;
27
import java.util.ArrayList;
28
import java.util.Arrays;
25
import java.util.Collections;
29
import java.util.Collections;
26
import java.util.HashMap;
30
import java.util.HashMap;
27
import java.util.Hashtable;
31
import java.util.Hashtable;
Lines 49-54 import javax.naming.directory.DirContext; Link Here
49
import javax.naming.directory.InitialDirContext;
53
import javax.naming.directory.InitialDirContext;
50
import javax.naming.directory.SearchControls;
54
import javax.naming.directory.SearchControls;
51
import javax.naming.directory.SearchResult;
55
import javax.naming.directory.SearchResult;
56
import javax.naming.ldap.InitialLdapContext;
57
import javax.naming.ldap.LdapContext;
58
import javax.naming.ldap.StartTlsRequest;
59
import javax.naming.ldap.StartTlsResponse;
60
import javax.net.ssl.HostnameVerifier;
61
import javax.net.ssl.SSLContext;
62
import javax.net.ssl.SSLSession;
63
import javax.net.ssl.SSLSocketFactory;
52
64
53
import org.apache.catalina.LifecycleException;
65
import org.apache.catalina.LifecycleException;
54
import org.ietf.jgss.GSSCredential;
66
import org.ietf.jgss.GSSCredential;
Lines 439-444 public class JNDIRealm extends RealmBase { Link Here
439
     */
451
     */
440
    protected String spnegoDelegationQop = "auth-conf";
452
    protected String spnegoDelegationQop = "auth-conf";
441
453
454
    /**
455
     * Whether to use TLS for connections
456
     */
457
    private boolean useTls = false;
458
459
    private StartTlsResponse tls = null;
460
461
    /**
462
     * The list of enabled cipher suites used for establishing tls connections.
463
     * <code>null</code> means to use the default cipher suites.
464
     */
465
    private String[] cipherSuites = null;
466
467
    /**
468
     * Verifier for hostnames in a tls secured connection. <code>null</code>
469
     * means to use the default verifier.
470
     */
471
    private HostnameVerifier verifier = null;
472
473
    private enum Verifier {
474
        IGNORE(new HostnameVerifier() {
475
            @Override
476
            public boolean verify(String hostname, SSLSession session) {
477
                return true;
478
            }
479
        });
480
        private final HostnameVerifier verifier;
481
482
        public HostnameVerifier getVerifier() {
483
            return this.verifier;
484
        }
485
486
        private Verifier(HostnameVerifier verifier) {
487
            this.verifier = verifier;
488
        }
489
    }
490
491
    private SSLSocketFactory sslSocketFactory = null;
492
442
    // ------------------------------------------------------------- Properties
493
    // ------------------------------------------------------------- Properties
443
494
444
    /**
495
    /**
Lines 1022-1027 public class JNDIRealm extends RealmBase { Link Here
1022
    }
1073
    }
1023
1074
1024
1075
1076
    public boolean getUseTls() {
1077
        return useTls;
1078
    }
1079
1080
    public void setUseTls(boolean useTls) {
1081
        this.useTls = useTls;
1082
    }
1083
1084
    private String[] getCipherSuitesArray() {
1085
        return cipherSuites;
1086
    }
1087
1088
    public String[] getCipherSuites() {
1089
        return cipherSuites;
1090
    }
1091
1092
    public void setCipherSuites(String suites) {
1093
        if (suites == null || suites.trim().isEmpty()) {
1094
            containerLog.warn(sm.getString("jndiRealm.emptyCipherSuites"));
1095
            this.cipherSuites = null;
1096
        } else {
1097
            this.cipherSuites = suites.trim().split("\\s*,\\s*");
1098
            containerLog.debug(sm.getString("jndiRealm.cipherSuites",
1099
                    Arrays.asList(this.cipherSuites)));
1100
        }
1101
    }
1102
1103
    public void setHostVerifierClassname(String verifierClassname) {
1104
        if (Verifier.valueOf(verifierClassname) != null) {
1105
            this.verifier = Verifier.valueOf(verifierClassname).getVerifier();
1106
        }
1107
    }
1108
1109
    public HostnameVerifier getHostnameVerifier() {
1110
        return this.verifier;
1111
    }
1112
1113
    public void setTlsProtocol(String protocol) {
1114
        try {
1115
            SSLContext sslContext = SSLContext.getInstance(protocol);
1116
            sslContext.init(null, null, null);
1117
            this.sslSocketFactory = sslContext.getSocketFactory();
1118
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
1119
            List<String> allowedProtocols;
1120
            allowedProtocols = Arrays.asList(getSupportedTlsProtocols());
1121
            throw new IllegalArgumentException(
1122
                    sm.getString("jndiRealm.invalidTlsProtocol", protocol,
1123
                            allowedProtocols), e);
1124
        }
1125
    }
1126
1127
    private String[] getSupportedTlsProtocols() {
1128
        try {
1129
            SSLContext sslContext = SSLContext.getDefault();
1130
            sslContext.init(null, null, null);
1131
            return sslContext.getSupportedSSLParameters().getProtocols();
1132
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
1133
            throw new RuntimeException(sm.getString("jndiRealm.exception"), e);
1134
        }
1135
    }
1136
1025
    // ---------------------------------------------------------- Realm Methods
1137
    // ---------------------------------------------------------- Realm Methods
1026
1138
1027
    /**
1139
    /**
Lines 1933-1938 public class JNDIRealm extends RealmBase { Link Here
1933
        if (context == null)
2045
        if (context == null)
1934
            return;
2046
            return;
1935
2047
2048
        // Close tls startResponse if used
2049
        if (tls != null) {
2050
            try {
2051
                tls.close();
2052
            } catch (IOException e) {
2053
                containerLog.error(sm.getString("jndiRealm.tlsClose"), e);
2054
            }
2055
        }
1936
        // Close our opened connection
2056
        // Close our opened connection
1937
        try {
2057
        try {
1938
            if (containerLog.isDebugEnabled())
2058
            if (containerLog.isDebugEnabled())
Lines 2125-2131 public class JNDIRealm extends RealmBase { Link Here
2125
        try {
2245
        try {
2126
2246
2127
            // Ensure that we have a directory context available
2247
            // Ensure that we have a directory context available
2128
            context = new InitialDirContext(getDirectoryContextEnvironment());
2248
            context = createDirContext(getDirectoryContextEnvironment());
2129
2249
2130
        } catch (Exception e) {
2250
        } catch (Exception e) {
2131
2251
Lines 2135-2141 public class JNDIRealm extends RealmBase { Link Here
2135
            containerLog.info(sm.getString("jndiRealm.exception.retry"), e);
2255
            containerLog.info(sm.getString("jndiRealm.exception.retry"), e);
2136
2256
2137
            // Try connecting to the alternate url.
2257
            // Try connecting to the alternate url.
2138
            context = new InitialDirContext(getDirectoryContextEnvironment());
2258
            context = createDirContext(getDirectoryContextEnvironment());
2139
2259
2140
        } finally {
2260
        } finally {
2141
2261
Lines 2149-2154 public class JNDIRealm extends RealmBase { Link Here
2149
2269
2150
    }
2270
    }
2151
2271
2272
    private DirContext createDirContext(Hashtable<String, String> env) throws NamingException {
2273
        if (useTls) {
2274
            return createTlsDirContext(env);
2275
        } else {
2276
            return new InitialDirContext(env);
2277
        }
2278
    }
2279
2280
    /**
2281
     * Create a tls enabled LdapContext and set the StartTlsResponse tls
2282
     * instance variable.
2283
     *
2284
     * @param env
2285
     *            Environment to use for context creation
2286
     * @return configured {@link LdapContext}
2287
     * @throws NamingException
2288
     *             when something goes wrong while negotiating the connection
2289
     */
2290
    private DirContext createTlsDirContext(
2291
            Hashtable<String, String> env) throws NamingException {
2292
        Map<String, Object> savedEnv = new HashMap<>();
2293
        for (String key : Arrays.asList(Context.SECURITY_AUTHENTICATION,
2294
                Context.SECURITY_CREDENTIALS, Context.SECURITY_PRINCIPAL,
2295
                Context.SECURITY_PROTOCOL)) {
2296
            Object entry = env.remove(key);
2297
            if (entry != null) {
2298
                savedEnv.put(key, entry);
2299
            }
2300
        }
2301
        LdapContext result = null;
2302
        try {
2303
            result = new InitialLdapContext(env, null);
2304
            tls = (StartTlsResponse) result
2305
                    .extendedOperation(new StartTlsRequest());
2306
            if (verifier != null) {
2307
                tls.setHostnameVerifier(verifier);
2308
            }
2309
            if (getCipherSuites() != null) {
2310
                tls.setEnabledCipherSuites(getCipherSuitesArray());
2311
            }
2312
            try {
2313
                SSLSession sslSession;
2314
                if (sslSocketFactory == null) {
2315
                    sslSession = tls.negotiate();
2316
                } else {
2317
                    sslSession = tls.negotiate(sslSocketFactory);
2318
                }
2319
                containerLog.debug(sm.getString("jndiRealm.negotiatedTls",
2320
                        sslSession.getPeerPrincipal().getName(),
2321
                        sslSession.getCipherSuite(), sslSession.getProtocol()));
2322
            } catch (IOException e) {
2323
                throw new NamingException(e.getMessage());
2324
            }
2325
        } finally {
2326
            if (result != null) {
2327
                for (Map.Entry<String, Object> savedEntry : savedEnv.entrySet()) {
2328
                    result.addToEnvironment(savedEntry.getKey(),
2329
                            savedEntry.getValue());
2330
                }
2331
            }
2332
        }
2333
        return result;
2334
    }
2335
2152
    /**
2336
    /**
2153
     * Create our directory context configuration.
2337
     * Create our directory context configuration.
2154
     *
2338
     *
Lines 2164-2192 public class JNDIRealm extends RealmBase { Link Here
2164
        else if (containerLog.isDebugEnabled() && connectionAttempt > 0)
2348
        else if (containerLog.isDebugEnabled() && connectionAttempt > 0)
2165
            containerLog.debug("Connecting to URL " + alternateURL);
2349
            containerLog.debug("Connecting to URL " + alternateURL);
2166
        env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
2350
        env.put(Context.INITIAL_CONTEXT_FACTORY, contextFactory);
2167
        if (connectionName != null)
2351
2168
            env.put(Context.SECURITY_PRINCIPAL, connectionName);
2352
        putIfNotNull(env, Context.SECURITY_PRINCIPAL, connectionName);
2169
        if (connectionPassword != null)
2353
        putIfNotNull(env, Context.SECURITY_CREDENTIALS, connectionPassword);
2170
            env.put(Context.SECURITY_CREDENTIALS, connectionPassword);
2354
2171
        if (connectionURL != null && connectionAttempt == 0)
2355
        if (connectionURL != null && connectionAttempt == 0)
2172
            env.put(Context.PROVIDER_URL, connectionURL);
2356
            env.put(Context.PROVIDER_URL, connectionURL);
2173
        else if (alternateURL != null && connectionAttempt > 0)
2357
        else if (alternateURL != null && connectionAttempt > 0)
2174
            env.put(Context.PROVIDER_URL, alternateURL);
2358
            env.put(Context.PROVIDER_URL, alternateURL);
2175
        if (authentication != null)
2359
2176
            env.put(Context.SECURITY_AUTHENTICATION, authentication);
2360
        putIfNotNull(env, Context.SECURITY_AUTHENTICATION, authentication);
2177
        if (protocol != null)
2361
        putIfNotNull(env, Context.SECURITY_PROTOCOL, protocol);
2178
            env.put(Context.SECURITY_PROTOCOL, protocol);
2362
        putIfNotNull(env, Context.REFERRAL, referrals);
2179
        if (referrals != null)
2363
        putIfNotNull(env, JNDIRealm.DEREF_ALIASES, derefAliases);
2180
            env.put(Context.REFERRAL, referrals);
2364
        putIfNotNull(env, "com.sun.jndi.ldap.connect.timeout", connectionTimeout);
2181
        if (derefAliases != null)
2182
            env.put(JNDIRealm.DEREF_ALIASES, derefAliases);
2183
        if (connectionTimeout != null)
2184
            env.put("com.sun.jndi.ldap.connect.timeout", connectionTimeout);
2185
2365
2186
        return env;
2366
        return env;
2187
2367
2188
    }
2368
    }
2189
2369
2370
    private void putIfNotNull(Hashtable<String, String> env, String key, String value) {
2371
        if (value == null) {
2372
            return;
2373
        }
2374
        env.put(key, value);
2375
    }
2190
2376
2191
    /**
2377
    /**
2192
     * Release our use of this connection so that it can be recycled.
2378
     * Release our use of this connection so that it can be recycled.
(-)a/java/org/apache/catalina/realm/LocalStrings.properties (-1 / +5 lines)
Lines 40-49 jdbcRealm.open=Exception opening database connection Link Here
40
jdbcRealm.open.invalidurl=Driver "{0}" does not support the url "{1}"
40
jdbcRealm.open.invalidurl=Driver "{0}" does not support the url "{1}"
41
jndiRealm.authenticateFailure=Username {0} NOT successfully authenticated
41
jndiRealm.authenticateFailure=Username {0} NOT successfully authenticated
42
jndiRealm.authenticateSuccess=Username {0} successfully authenticated
42
jndiRealm.authenticateSuccess=Username {0} successfully authenticated
43
jndiRealm.emptyCipherSuites=Empty String for cipher suites given. Using default cipher suites.
44
jndiRealm.cipherSuites=Enable [{0}] as cipher suites for tls connection.
43
jndiRealm.close=Exception closing directory server connection
45
jndiRealm.close=Exception closing directory server connection
44
jndiRealm.exception=Exception performing authentication
46
jndiRealm.exception=Exception performing authentication
45
jndiRealm.exception.retry=Exception performing authentication. Retrying...
47
jndiRealm.exception.retry=Exception performing authentication. Retrying...
48
jndiRealm.negotiatedTls=Negotiated tls connection to "{0}" using cipher suite "{1}" and protocol "{2}"
49
jndiRealm.invalidTlsProtocol=Given protocol "{0}" is invalid. It has to be one of {1}
46
jndiRealm.open=Exception opening directory server connection
50
jndiRealm.open=Exception opening directory server connection
51
jndiRealm.tlsClose=Exception closing tls response
47
memoryRealm.authenticateFailure=Username {0} NOT successfully authenticated
52
memoryRealm.authenticateFailure=Username {0} NOT successfully authenticated
48
memoryRealm.authenticateSuccess=Username {0} successfully authenticated
53
memoryRealm.authenticateSuccess=Username {0} successfully authenticated
49
memoryRealm.loadExist=Memory database file {0} cannot be read
54
memoryRealm.loadExist=Memory database file {0} cannot be read
50
- 

Return to bug 49785