1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/kdb/ldap/libkdb_ldap/kdb_ldap_conn.c */
3 /*
4 * Copyright (c) 2004-2005, Novell, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * * Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * * The copyright holder's name is not used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "autoconf.h"
32 #if HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35
36 #include "ldap_main.h"
37 #include "ldap_service_stash.h"
38 #include <kdb5.h>
39 #ifdef HAVE_SASL_SASL_H
40 #include <sasl/sasl.h>
41 #endif
42
43 /* Ensure that we have the parameters we need to authenticate to the LDAP
44 * server. Read the password if necessary. */
45 static krb5_error_code
validate_context(krb5_context context,krb5_ldap_context * ctx)46 validate_context(krb5_context context, krb5_ldap_context *ctx)
47 {
48 krb5_error_code ret;
49
50 if (ctx->sasl_mech != NULL) {
51 /* Read the password for use as the SASL secret if we can, but do not
52 * require one as not all mechanisms need it. */
53 if (ctx->bind_pwd == NULL && ctx->sasl_authcid != NULL &&
54 ctx->service_password_file != NULL) {
55 (void)krb5_ldap_readpassword(context, ctx->service_password_file,
56 ctx->sasl_authcid, &ctx->bind_pwd);
57 }
58 return 0;
59 }
60
61 /* For a simple bind, a DN and password are required. */
62
63 if (ctx->bind_dn == NULL) {
64 k5_setmsg(context, EINVAL, _("LDAP bind dn value missing"));
65 return EINVAL;
66 }
67
68 if (ctx->bind_pwd == NULL && ctx->service_password_file == NULL) {
69 k5_setmsg(context, EINVAL, _("LDAP bind password value missing"));
70 return EINVAL;
71 }
72
73 if (ctx->bind_pwd == NULL && ctx->service_password_file != NULL) {
74 ret = krb5_ldap_readpassword(context, ctx->service_password_file,
75 ctx->bind_dn, &ctx->bind_pwd);
76 if (ret) {
77 k5_prependmsg(context, ret,
78 _("Error reading password from stash"));
79 return ret;
80 }
81 }
82
83 /* An empty password is not allowed. */
84 if (*ctx->bind_pwd == '\0') {
85 k5_setmsg(context, EINVAL, _("Service password length is zero"));
86 return EINVAL;
87 }
88
89 return 0;
90 }
91
92 /*
93 * Internal Functions called by init functions.
94 */
95
96 #ifdef HAVE_SASL_SASL_H
97
98 static int
interact(LDAP * ld,unsigned flags,void * defaults,void * sin)99 interact(LDAP *ld, unsigned flags, void *defaults, void *sin)
100 {
101 sasl_interact_t *in = NULL;
102 krb5_ldap_context *ctx = defaults;
103
104 for (in = sin; in != NULL && in->id != SASL_CB_LIST_END; in++) {
105 if (in->id == SASL_CB_AUTHNAME)
106 in->result = ctx->sasl_authcid;
107 else if (in->id == SASL_CB_USER)
108 in->result = ctx->sasl_authzid;
109 else if (in->id == SASL_CB_GETREALM)
110 in->result = ctx->sasl_realm;
111 else if (in->id == SASL_CB_PASS)
112 in->result = ctx->bind_pwd;
113 else
114 return LDAP_OTHER;
115 in->len = (in->result != NULL) ? strlen(in->result) : 0;
116 }
117
118 return LDAP_SUCCESS;
119 }
120
121 #else /* HAVE_SASL_SASL_H */
122
123 /* We can't define an interaction function, so only non-interactive mechs like
124 * EXTERNAL can work. */
125 static int
interact(LDAP * ld,unsigned flags,void * defaults,void * sin)126 interact(LDAP *ld, unsigned flags, void *defaults, void *sin)
127 {
128 return LDAP_OTHER;
129 }
130
131 #endif
132
133 static krb5_error_code
authenticate(krb5_ldap_context * ctx,krb5_ldap_server_handle * server)134 authenticate(krb5_ldap_context *ctx, krb5_ldap_server_handle *server)
135 {
136 int st;
137 struct berval bv;
138
139 if (ctx->sasl_mech != NULL) {
140 st = ldap_sasl_interactive_bind_s(server->ldap_handle, NULL,
141 ctx->sasl_mech, NULL, NULL,
142 LDAP_SASL_QUIET, interact, ctx);
143 if (st != LDAP_SUCCESS) {
144 k5_setmsg(ctx->kcontext, KRB5_KDB_ACCESS_ERROR,
145 _("Cannot bind to LDAP server '%s' with SASL mechanism "
146 "'%s': %s"), server->server_info->server_name,
147 ctx->sasl_mech, ldap_err2string(st));
148 return KRB5_KDB_ACCESS_ERROR;
149 }
150 } else {
151 /* Do a simple bind with DN and password. */
152 bv.bv_val = ctx->bind_pwd;
153 bv.bv_len = strlen(ctx->bind_pwd);
154 st = ldap_sasl_bind_s(server->ldap_handle, ctx->bind_dn, NULL, &bv,
155 NULL, NULL, NULL);
156 if (st != LDAP_SUCCESS) {
157 k5_setmsg(ctx->kcontext, KRB5_KDB_ACCESS_ERROR,
158 _("Cannot bind to LDAP server '%s' as '%s': %s"),
159 server->server_info->server_name, ctx->bind_dn,
160 ldap_err2string(st));
161 return KRB5_KDB_ACCESS_ERROR;
162 }
163 }
164 return 0;
165 }
166
167 static krb5_error_code
initialize_server(krb5_ldap_context * ldap_context,krb5_ldap_server_info * info)168 initialize_server(krb5_ldap_context *ldap_context, krb5_ldap_server_info *info)
169 {
170 krb5_ldap_server_handle *server;
171 krb5_error_code ret;
172 int st;
173
174 server = calloc(1, sizeof(krb5_ldap_server_handle));
175 if (server == NULL)
176 return ENOMEM;
177 server->server_info = info;
178
179 st = ldap_initialize(&server->ldap_handle, info->server_name);
180 if (st) {
181 free(server);
182 k5_setmsg(ldap_context->kcontext, KRB5_KDB_ACCESS_ERROR,
183 _("Cannot create LDAP handle for '%s': %s"),
184 info->server_name, ldap_err2string(st));
185 return KRB5_KDB_ACCESS_ERROR;
186 }
187
188 ret = authenticate(ldap_context, server);
189 if (ret) {
190 info->server_status = OFF;
191 time(&info->downtime);
192 free(server);
193 return ret;
194 }
195
196 server->next = info->ldap_server_handles;
197 info->ldap_server_handles = server;
198 info->num_conns++;
199 info->server_status = ON;
200 return 0;
201 }
202
203 /*
204 * initialization for data base routines.
205 */
206
207 krb5_error_code
krb5_ldap_db_init(krb5_context context,krb5_ldap_context * ctx)208 krb5_ldap_db_init(krb5_context context, krb5_ldap_context *ctx)
209 {
210 krb5_error_code ret;
211 int i, version = LDAP_VERSION3;
212 unsigned int conns;
213 krb5_ldap_server_info *info;
214 struct timeval local_timelimit = { 10, 0 };
215
216 ret = validate_context(context, ctx);
217 if (ret)
218 return ret;
219
220 #ifdef LDAP_OPT_DEBUG_LEVEL
221 ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &ctx->ldap_debug);
222 #endif
223 ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &version);
224 #ifdef LDAP_OPT_NETWORK_TIMEOUT
225 ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &local_timelimit);
226 #elif defined LDAP_X_OPT_CONNECT_TIMEOUT
227 ldap_set_option(NULL, LDAP_X_OPT_CONNECT_TIMEOUT, &local_timelimit);
228 #endif
229
230 HNDL_LOCK(ctx);
231 for (i = 0; ctx->server_info_list[i] != NULL; i++) {
232 info = ctx->server_info_list[i];
233 if (info->server_status == NOTSET) {
234 krb5_clear_error_message(context);
235
236 #ifdef LDAP_MOD_INCREMENT
237 info->modify_increment = has_modify_increment(context,
238 info->server_name);
239 #else
240 info->modify_increment = 0;
241 #endif
242
243 for (conns = 0; conns < ctx->max_server_conns; conns++) {
244 ret = initialize_server(ctx, info);
245 if (ret)
246 break;
247 }
248
249 /* If we opened a connection, don't try any more servers. */
250 if (info->server_status == ON)
251 break;
252 }
253 }
254 HNDL_UNLOCK(ctx);
255
256 return ret;
257 }
258
259
260 /*
261 * get a single handle. Do not lock the mutex
262 */
263
264 krb5_error_code
krb5_ldap_db_single_init(krb5_ldap_context * ldap_context)265 krb5_ldap_db_single_init(krb5_ldap_context *ldap_context)
266 {
267 krb5_error_code st=0;
268 int cnt=0;
269 krb5_ldap_server_info *server_info=NULL;
270
271 while (ldap_context->server_info_list[cnt] != NULL) {
272 server_info = ldap_context->server_info_list[cnt];
273 if ((server_info->server_status == NOTSET || server_info->server_status == ON)) {
274 if (server_info->num_conns < ldap_context->max_server_conns-1) {
275 st = initialize_server(ldap_context, server_info);
276 if (st == LDAP_SUCCESS)
277 goto cleanup;
278 }
279 }
280 ++cnt;
281 }
282
283 /* If we are here, try to connect to all the servers */
284
285 cnt = 0;
286 while (ldap_context->server_info_list[cnt] != NULL) {
287 server_info = ldap_context->server_info_list[cnt];
288 st = initialize_server(ldap_context, server_info);
289 if (st == LDAP_SUCCESS)
290 goto cleanup;
291 ++cnt;
292 }
293 cleanup:
294 return (st);
295 }
296
297 krb5_error_code
krb5_ldap_rebind(krb5_ldap_context * ldap_context,krb5_ldap_server_handle ** ldap_server_handle)298 krb5_ldap_rebind(krb5_ldap_context *ldap_context,
299 krb5_ldap_server_handle **ldap_server_handle)
300 {
301 krb5_ldap_server_handle *handle = *ldap_server_handle;
302
303 ldap_unbind_ext_s(handle->ldap_handle, NULL, NULL);
304 if (ldap_initialize(&handle->ldap_handle,
305 handle->server_info->server_name) != LDAP_SUCCESS ||
306 authenticate(ldap_context, handle) != 0) {
307 return krb5_ldap_request_next_handle_from_pool(ldap_context,
308 ldap_server_handle);
309 }
310 return LDAP_SUCCESS;
311 }
312
313 /*
314 * DAL API functions
315 */
316 krb5_error_code
krb5_ldap_lib_init()317 krb5_ldap_lib_init()
318 {
319 return 0;
320 }
321
322 krb5_error_code
krb5_ldap_lib_cleanup()323 krb5_ldap_lib_cleanup()
324 {
325 /* right now, no cleanup required */
326 return 0;
327 }
328
329 krb5_error_code
krb5_ldap_free_ldap_context(krb5_ldap_context * ldap_context)330 krb5_ldap_free_ldap_context(krb5_ldap_context *ldap_context)
331 {
332 if (ldap_context == NULL)
333 return 0;
334
335 free(ldap_context->container_dn);
336 ldap_context->container_dn = NULL;
337
338 krb5_ldap_free_realm_params(ldap_context->lrparams);
339 ldap_context->lrparams = NULL;
340
341 krb5_ldap_free_server_params(ldap_context);
342
343 return 0;
344 }
345
346 krb5_error_code
krb5_ldap_close(krb5_context context)347 krb5_ldap_close(krb5_context context)
348 {
349 kdb5_dal_handle *dal_handle=NULL;
350 krb5_ldap_context *ldap_context=NULL;
351
352 if (context == NULL ||
353 context->dal_handle == NULL ||
354 context->dal_handle->db_context == NULL)
355 return 0;
356
357 dal_handle = context->dal_handle;
358 ldap_context = (krb5_ldap_context *) dal_handle->db_context;
359 dal_handle->db_context = NULL;
360
361 krb5_ldap_free_ldap_context(ldap_context);
362
363 return 0;
364 }
365