1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /* Setup:
18  *  - Create or edit the file data/host.data and add an
19  *     ldap server DN.  Multiple DNs may be listed on
20  *     a single line.
21  *  - Copy the server certificates to the data/ directory.
22  *     All DER type certificates must have the .der extention.
23  *     All BASE64 or PEM certificates must have the .b64
24  *     extension.  All certificate files copied to the /data
25  *     directory will be added to the ldap certificate store.
26  */
27
28 /* This test covers the following three types of connections:
29  *  - Unsecure ldap://
30  *  - Secure ldaps://
31  *  - Secure ldap://+Start_TLS
32  *
33  *  - (TBD) Mutual authentication
34  *
35  * There are other variations that should be tested:
36  *  - All of the above with multiple redundant LDAP servers
37  *     This can be done by listing more than one server DN
38  *      in the host.data file.  The DNs should all be listed
39  *      on one line separated by a space.
40  *  - All of the above with multiple certificates
41  *     If more than one certificate is found in the data/
42  *      directory, each certificate found will be added
43  *      to the certificate store.
44  *  - All of the above on alternate ports
45  *     An alternate port can be specified as part of the
46  *      host in the host.data file.  The ":port" should
47  *      follow each DN listed.  Default is 389 and 636.
48  *  - Secure connections with mutual authentication
49  */
50
51#include "testutil.h"
52
53#include "apr.h"
54#include "apr_general.h"
55#include "apr_ldap.h"
56#include "apr_file_io.h"
57#include "apr_file_info.h"
58#include "apr_strings.h"
59#if APR_HAVE_STDLIB_H
60#include <stdlib.h>
61#endif
62#define APR_WANT_STDIO
63#define APR_WANT_STRFUNC
64#include "apr_want.h"
65
66#define DIRNAME "data"
67#define FILENAME DIRNAME "/host.data"
68#define CERTFILEDER DIRNAME "/*.der"
69#define CERTFILEB64 DIRNAME "/*.b64"
70
71#if APR_HAS_LDAP
72
73static char ldap_host[256];
74
75static int get_ldap_host(void)
76{
77    apr_status_t rv;
78    apr_file_t *thefile = NULL;
79    char *ptr;
80
81    ldap_host[0] = '\0';
82    rv = apr_file_open(&thefile, FILENAME,
83                       APR_FOPEN_READ,
84                       APR_UREAD | APR_UWRITE | APR_GREAD, p);
85    if (rv != APR_SUCCESS) {
86        return 0;
87    }
88
89    rv = apr_file_gets(ldap_host, sizeof(ldap_host), thefile);
90    if (rv != APR_SUCCESS) {
91        return 0;
92    }
93
94    ptr = strstr (ldap_host, "\r\n");
95    if (ptr) {
96        *ptr = '\0';
97    }
98    apr_file_close(thefile);
99
100    return 1;
101
102}
103
104static int add_ldap_certs(abts_case *tc)
105{
106    apr_status_t status;
107    apr_dir_t *thedir;
108    apr_finfo_t dirent;
109    apr_ldap_err_t *result = NULL;
110
111    if ((status = apr_dir_open(&thedir, DIRNAME, p)) == APR_SUCCESS) {
112        apr_ldap_opt_tls_cert_t *cert = (apr_ldap_opt_tls_cert_t *)apr_pcalloc(p, sizeof(apr_ldap_opt_tls_cert_t));
113
114        do {
115            status = apr_dir_read(&dirent, APR_FINFO_MIN | APR_FINFO_NAME, thedir);
116            if (APR_STATUS_IS_INCOMPLETE(status)) {
117                continue; /* ignore un-stat()able files */
118            }
119            else if (status != APR_SUCCESS) {
120                break;
121            }
122
123            if (strstr(dirent.name, ".der")) {
124                cert->type = APR_LDAP_CA_TYPE_DER;
125                cert->path = apr_pstrcat (p, DIRNAME, "/", dirent.name, NULL);
126                apr_ldap_set_option(p, NULL, APR_LDAP_OPT_TLS_CERT, (void *)cert, &result);
127                ABTS_TRUE(tc, result->rc == LDAP_SUCCESS);
128            }
129            if (strstr(dirent.name, ".b64")) {
130                cert->type = APR_LDAP_CA_TYPE_BASE64;
131                cert->path = apr_pstrcat (p, DIRNAME, "/", dirent.name, NULL);
132                apr_ldap_set_option(p, NULL, APR_LDAP_OPT_TLS_CERT, (void *)cert, &result);
133                ABTS_TRUE(tc, result->rc == LDAP_SUCCESS);
134            }
135
136        } while (1);
137
138        apr_dir_close(thedir);
139    }
140    return 0;
141}
142
143static void test_ldap_connection(abts_case *tc, LDAP *ldap)
144{
145    int version  = LDAP_VERSION3;
146    int failures, result;
147
148    /* always default to LDAP V3 */
149    ldap_set_option(ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
150
151    for (failures=0; failures<10; failures++)
152    {
153        result = ldap_simple_bind_s(ldap,
154                                    (char *)NULL,
155                                    (char *)NULL);
156        if (LDAP_SERVER_DOWN != result)
157            break;
158    }
159
160    ABTS_TRUE(tc, result == LDAP_SUCCESS);
161    if (result != LDAP_SUCCESS) {
162        abts_log_message("%s\n", ldap_err2string(result));
163    }
164
165    ldap_unbind_s(ldap);
166
167    return;
168}
169
170static void test_ldap(abts_case *tc, void *data)
171{
172    apr_pool_t *pool = p;
173    LDAP *ldap;
174    apr_ldap_err_t *result = NULL;
175
176
177    ABTS_ASSERT(tc, "failed to get host", ldap_host[0] != '\0');
178
179    apr_ldap_init(pool, &ldap,
180                  ldap_host, LDAP_PORT,
181                  APR_LDAP_NONE, &(result));
182
183    ABTS_TRUE(tc, ldap != NULL);
184    ABTS_PTR_NOTNULL(tc, result);
185
186    if (result->rc == LDAP_SUCCESS) {
187        test_ldap_connection(tc, ldap);
188    }
189}
190
191static void test_ldaps(abts_case *tc, void *data)
192{
193    apr_pool_t *pool = p;
194    LDAP *ldap;
195    apr_ldap_err_t *result = NULL;
196
197    apr_ldap_init(pool, &ldap,
198                  ldap_host, LDAPS_PORT,
199                  APR_LDAP_SSL, &(result));
200
201    ABTS_TRUE(tc, ldap != NULL);
202    ABTS_PTR_NOTNULL(tc, result);
203
204    if (result->rc == LDAP_SUCCESS) {
205        add_ldap_certs(tc);
206
207        test_ldap_connection(tc, ldap);
208    }
209}
210
211static void test_ldap_tls(abts_case *tc, void *data)
212{
213    apr_pool_t *pool = p;
214    LDAP *ldap;
215    apr_ldap_err_t *result = NULL;
216
217    apr_ldap_init(pool, &ldap,
218                  ldap_host, LDAP_PORT,
219                  APR_LDAP_STARTTLS, &(result));
220
221    ABTS_TRUE(tc, ldap != NULL);
222    ABTS_PTR_NOTNULL(tc, result);
223
224    if (result->rc == LDAP_SUCCESS) {
225        add_ldap_certs(tc);
226
227        test_ldap_connection(tc, ldap);
228    }
229}
230
231#endif /* APR_HAS_LDAP */
232
233abts_suite *testldap(abts_suite *suite)
234{
235#if APR_HAS_LDAP
236    apr_ldap_err_t *result = NULL;
237    suite = ADD_SUITE(suite);
238
239    apr_ldap_ssl_init(p, NULL, 0, &result);
240
241    if (get_ldap_host()) {
242        abts_run_test(suite, test_ldap, NULL);
243        abts_run_test(suite, test_ldaps, NULL);
244        abts_run_test(suite, test_ldap_tls, NULL);
245    }
246#endif /* APR_HAS_LDAP */
247
248    return suite;
249}
250
251