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