001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2011-2015 ForgeRock AS.
015 */
016
017package org.forgerock.opendj.examples;
018
019import static org.forgerock.opendj.ldap.LDAPConnectionFactory.*;
020
021import java.io.File;
022import java.io.IOException;
023import java.security.GeneralSecurityException;
024
025import javax.net.ssl.SSLContext;
026import javax.net.ssl.TrustManager;
027
028import org.forgerock.opendj.ldap.Connection;
029import org.forgerock.opendj.ldap.LdapException;
030import org.forgerock.opendj.ldap.LDAPConnectionFactory;
031import org.forgerock.opendj.ldap.ResultCode;
032import org.forgerock.opendj.ldap.SSLContextBuilder;
033import org.forgerock.opendj.ldap.TrustManagers;
034import org.forgerock.util.Options;
035
036/**
037 * An example client application which performs simple authentication to a
038 * directory server. This example takes the following command line parameters:
039 * <ul>
040 * <li>host - host name of the directory server</li>
041 * <li>port - port number of the directory server, e.g. 1389, 1636</li>
042 * <li>bind-dn - DN of the user to authenticate</li>
043 * <li>bind-password - Password of the user to authenticate</li>
044 * <li>use-starttls - (Optional) connect with StartTLS</li>
045 * <li>use-ssl - (Optional) connect over SSL</li>
046 * </ul>
047 * The host, port, bind-dn, and bind-password arguments are required.
048 * The use-starttls and use-ssl arguments are optional and mutually exclusive.
049 * <p>
050 * If the server certificate is self-signed,
051 * or otherwise not trusted out-of-the-box,
052 * then set the trust store by using the JSSE system property
053 * {@code -Djavax.net.ssl.trustStore=/path/to/opendj/config/keystore}
054 * and the trust store password if necessary by using the JSSE system property
055 * {@code -Djavax.net.ssl.trustStorePassword=`cat /path/to/opendj/config/keystore.pin`}.
056 */
057public final class SimpleAuth {
058
059    /**
060     * Authenticate to the directory either over LDAP, over LDAPS, or using
061     * StartTLS.
062     *
063     * @param args
064     *            The command line arguments
065     */
066    public static void main(final String[] args) {
067        parseArgs(args);
068        // Connect and bind to the server, then close the connection.
069        if (useStartTLS) {
070            connectStartTLS();
071        } else if (useSSL) {
072            connectSSL();
073        } else {
074            connect();
075        }
076    }
077
078    // --- JCite basic auth ---
079    /**
080     * Authenticate over LDAP.
081     */
082    private static void connect() {
083        final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port);
084        Connection connection = null;
085
086        try {
087            connection = factory.getConnection();
088            connection.bind(bindDN, bindPassword.toCharArray());
089            System.out.println("Authenticated as " + bindDN + ".");
090        } catch (final LdapException e) {
091            System.err.println(e.getMessage());
092            System.exit(e.getResult().getResultCode().intValue());
093            return;
094        } finally {
095            if (connection != null) {
096                connection.close();
097            }
098        }
099    }
100    // --- JCite basic auth ---
101
102    // --- JCite trust options ---
103    /**
104     * For StartTLS and SSL the connection factory needs SSL context options.
105     * In the general case, a trust manager in the SSL context serves
106     * to check server certificates, and a key manager handles client keys
107     * when the server checks certificates from our client.
108     * <p>
109     * This sample expects a directory server
110     * that allows use of Start TLS on the LDAP port.
111     * This sample checks the server certificate,
112     * verifying that the certificate is currently valid,
113     * and that the host name of the server matches that of the certificate,
114     * based on a Java Key Store-format trust store.
115     * This sample does not present a client certificate.
116     *
117     * @param hostname Host name expected in the server certificate
118     * @param truststore Path to trust store file for the trust manager
119     * @param storepass Password for the trust store
120     * @return SSL context options
121     * @throws GeneralSecurityException Could not load the trust store
122     */
123    private static Options getTrustOptions(final String hostname,
124                                           final String truststore,
125                                           final String storepass)
126            throws GeneralSecurityException {
127        Options options = Options.defaultOptions();
128
129        TrustManager trustManager = null;
130        try {
131            trustManager = TrustManagers.checkValidityDates(
132                    TrustManagers.checkHostName(hostname,
133                            TrustManagers.checkUsingTrustStore(
134                                    truststore, storepass.toCharArray(), null)));
135        } catch (IOException e) {
136            e.printStackTrace();
137            System.exit(1);
138        }
139
140        if (trustManager != null) {
141            SSLContext sslContext = new SSLContextBuilder()
142                    .setTrustManager(trustManager).getSSLContext();
143            options.set(SSL_CONTEXT, sslContext);
144        }
145
146        options.set(SSL_USE_STARTTLS, useStartTLS);
147
148        return options;
149    }
150    // --- JCite trust options ---
151
152    // --- JCite secure connect ---
153    /**
154     * Perform authentication over a secure connection.
155     */
156    private static void secureConnect() {
157        Connection connection = null;
158
159        try {
160
161            final LDAPConnectionFactory factory =
162                    new LDAPConnectionFactory(host, port,
163                            getTrustOptions(host, keystore, storepass));
164            connection = factory.getConnection();
165            connection.bind(bindDN, bindPassword.toCharArray());
166
167            System.out.println("Authenticated as " + bindDN + ".");
168
169        } catch (final LdapException e) {
170            System.err.println(e.getMessage());
171            System.exit(e.getResult().getResultCode().intValue());
172            return;
173        } catch (final GeneralSecurityException e) {
174            System.err.println(e.getMessage());
175            System.exit(ResultCode.CLIENT_SIDE_CONNECT_ERROR.intValue());
176        } finally {
177            if (connection != null) {
178                connection.close();
179            }
180        }
181    }
182    // --- JCite secure connect ---
183
184    // --- JCite trust all ---
185    /**
186     * For StartTLS and SSL the connection factory needs SSL context options. In
187     * the general case, a trust manager in the SSL context serves to check
188     * server certificates, and a key manager handles client keys when the
189     * server checks certificates from our client.
190     *
191     * OpenDJ directory server lets you install by default with a self-signed
192     * certificate that is not in the system trust store. To simplify this
193     * implementation trusts all server certificates.
194     */
195    private static Options getTrustAllOptions() throws GeneralSecurityException {
196        Options options = Options.defaultOptions();
197        SSLContext sslContext =
198                new SSLContextBuilder().setTrustManager(TrustManagers.trustAll())
199                        .getSSLContext();
200        options.set(SSL_CONTEXT, sslContext);
201        options.set(SSL_USE_STARTTLS, useStartTLS);
202        return options;
203    }
204    // --- JCite trust all ---
205
206    // --- JCite trust all connect ---
207    /**
208     * Perform authentication over a secure connection, trusting all server
209     * certificates.
210     */
211    private static void trustAllConnect() {
212        Connection connection = null;
213
214        try {
215            final LDAPConnectionFactory factory =
216                    new LDAPConnectionFactory(host, port, getTrustAllOptions());
217            connection = factory.getConnection();
218            connection.bind(bindDN, bindPassword.toCharArray());
219            System.out.println("Authenticated as " + bindDN + ".");
220        } catch (final LdapException e) {
221            System.err.println(e.getMessage());
222            System.exit(e.getResult().getResultCode().intValue());
223            return;
224        } catch (final GeneralSecurityException e) {
225            System.err.println(e.getMessage());
226            System.exit(ResultCode.CLIENT_SIDE_CONNECT_ERROR.intValue());
227        } finally {
228            if (connection != null) {
229                connection.close();
230            }
231        }
232    }
233    // --- JCite trust all connect ---
234
235    /**
236     * Authenticate using StartTLS.
237     */
238    private static void connectStartTLS() {
239        secureConnect();
240        // trustAllConnect();
241    }
242
243    /**
244     * Authenticate over LDAPS.
245     */
246    private static void connectSSL() {
247        secureConnect();
248        // trustAllConnect();
249    }
250
251    private static String host;
252    private static int port;
253    private static String bindDN;
254    private static String bindPassword;
255    private static boolean useStartTLS;
256    private static boolean useSSL;
257    private static String keystore;
258    private static String storepass;
259
260    /**
261     * Parse command line arguments.
262     *
263     * @param args
264     *            host port bind-dn bind-password [ use-starttls | use-ssl ]
265     */
266    private static void parseArgs(String[] args) {
267        if (args.length < 4 || args.length > 5) {
268            giveUp();
269        }
270
271        host = args[0];
272        port = Integer.parseInt(args[1]);
273        bindDN = args[2];
274        bindPassword = args[3];
275
276        if (args.length == 5) {
277            if ("use-starttls".equals(args[4].toLowerCase())) {
278                useStartTLS = true;
279                useSSL = false;
280            } else if ("use-ssl".equals(args[4].toLowerCase())) {
281                useStartTLS = false;
282                useSSL = true;
283            } else {
284                giveUp();
285            }
286        }
287
288        keystore = System.getProperty("javax.net.ssl.trustStore");
289        storepass = System.getProperty("javax.net.ssl.trustStorePassword");
290        if (keystore == null) { // Try to use Java's cacerts trust store.
291            keystore = System.getProperty("java.home") + File.separator
292                    + "lib" + File.separator
293                    + "security" + File.separator
294                    + "cacerts";
295            storepass = "changeit"; // Default password
296        }
297    }
298
299    private static void giveUp() {
300        printUsage();
301        System.exit(1);
302    }
303
304    private static void printUsage() {
305        System.err.println("Usage: host port bind-dn bind-password [ use-starttls | use-ssl ]");
306        System.err.println("\thost, port, bind-dn, and bind-password arguments are required.");
307        System.err.println("\tuse-starttls and use-ssl are optional and mutually exclusive.");
308        System.err.println("\tOptionally set javax.net.ssl.trustStore and javax.net.ssl.trustStorePassword.");
309    }
310
311    private SimpleAuth() {
312        // Not used.
313    }
314}