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