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 ldap_unbind_ext_s(server->ldap_handle, NULL, NULL);
193 free(server);
194 return ret;
195 }
196
197 server->next = info->ldap_server_handles;
198 info->ldap_server_handles = server;
199 info->num_conns++;
200 info->server_status = ON;
201 return 0;
202 }
203
204 /*
205 * initialization for data base routines.
206 */
207
208 krb5_error_code
krb5_ldap_db_init(krb5_context context,krb5_ldap_context * ctx)209 krb5_ldap_db_init(krb5_context context, krb5_ldap_context *ctx)
210 {
211 krb5_error_code ret;
212 int i, version = LDAP_VERSION3;
213 unsigned int conns;
214 krb5_ldap_server_info *info;
215 struct timeval local_timelimit = { 10, 0 };
216
217 ret = validate_context(context, ctx);
218 if (ret)
219 return ret;
220
221 #ifdef LDAP_OPT_DEBUG_LEVEL
222 ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &ctx->ldap_debug);
223 #endif
224 ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &version);
225 #ifdef LDAP_OPT_NETWORK_TIMEOUT
226 ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &local_timelimit);
227 #elif defined LDAP_X_OPT_CONNECT_TIMEOUT
228 ldap_set_option(NULL, LDAP_X_OPT_CONNECT_TIMEOUT, &local_timelimit);
229 #endif
230
231 HNDL_LOCK(ctx);
232 for (i = 0; ctx->server_info_list[i] != NULL; i++) {
233 info = ctx->server_info_list[i];
234 if (info->server_status == NOTSET) {
235 krb5_clear_error_message(context);
236
237 #ifdef LDAP_MOD_INCREMENT
238 info->modify_increment = has_modify_increment(context,
239 info->server_name);
240 #else
241 info->modify_increment = 0;
242 #endif
243
244 for (conns = 0; conns < ctx->max_server_conns; conns++) {
245 ret = initialize_server(ctx, info);
246 if (ret)
247 break;
248 }
249
250 /* If we opened a connection, don't try any more servers. */
251 if (info->server_status == ON)
252 break;
253 }
254 }
255 HNDL_UNLOCK(ctx);
256
257 return ret;
258 }
259
260
261 /*
262 * get a single handle. Do not lock the mutex
263 */
264
265 krb5_error_code
krb5_ldap_db_single_init(krb5_ldap_context * ldap_context)266 krb5_ldap_db_single_init(krb5_ldap_context *ldap_context)
267 {
268 krb5_error_code st=0;
269 int cnt=0;
270 krb5_ldap_server_info *server_info=NULL;
271
272 while (ldap_context->server_info_list[cnt] != NULL) {
273 server_info = ldap_context->server_info_list[cnt];
274 if ((server_info->server_status == NOTSET || server_info->server_status == ON)) {
275 if (server_info->num_conns < ldap_context->max_server_conns-1) {
276 st = initialize_server(ldap_context, server_info);
277 if (st == LDAP_SUCCESS)
278 goto cleanup;
279 }
280 }
281 ++cnt;
282 }
283
284 /* If we are here, try to connect to all the servers */
285
286 cnt = 0;
287 while (ldap_context->server_info_list[cnt] != NULL) {
288 server_info = ldap_context->server_info_list[cnt];
289 st = initialize_server(ldap_context, server_info);
290 if (st == LDAP_SUCCESS)
291 goto cleanup;
292 ++cnt;
293 }
294 cleanup:
295 return (st);
296 }
297
298 krb5_error_code
krb5_ldap_rebind(krb5_ldap_context * ldap_context,krb5_ldap_server_handle ** ldap_server_handle)299 krb5_ldap_rebind(krb5_ldap_context *ldap_context,
300 krb5_ldap_server_handle **ldap_server_handle)
301 {
302 krb5_ldap_server_handle *handle = *ldap_server_handle;
303
304 ldap_unbind_ext_s(handle->ldap_handle, NULL, NULL);
305 if (ldap_initialize(&handle->ldap_handle,
306 handle->server_info->server_name) != LDAP_SUCCESS ||
307 authenticate(ldap_context, handle) != 0) {
308 return krb5_ldap_request_next_handle_from_pool(ldap_context,
309 ldap_server_handle);
310 }
311 return LDAP_SUCCESS;
312 }
313
314 /*
315 * DAL API functions
316 */
317 krb5_error_code
krb5_ldap_lib_init(void)318 krb5_ldap_lib_init(void)
319 {
320 return 0;
321 }
322
323 krb5_error_code
krb5_ldap_lib_cleanup(void)324 krb5_ldap_lib_cleanup(void)
325 {
326 /* right now, no cleanup required */
327 return 0;
328 }
329
330 krb5_error_code
krb5_ldap_free_ldap_context(krb5_ldap_context * ldap_context)331 krb5_ldap_free_ldap_context(krb5_ldap_context *ldap_context)
332 {
333 if (ldap_context == NULL)
334 return 0;
335
336 free(ldap_context->container_dn);
337 ldap_context->container_dn = NULL;
338
339 krb5_ldap_free_realm_params(ldap_context->lrparams);
340 ldap_context->lrparams = NULL;
341
342 krb5_ldap_free_server_params(ldap_context);
343
344 return 0;
345 }
346
347 krb5_error_code
krb5_ldap_close(krb5_context context)348 krb5_ldap_close(krb5_context context)
349 {
350 kdb5_dal_handle *dal_handle=NULL;
351 krb5_ldap_context *ldap_context=NULL;
352
353 if (context == NULL ||
354 context->dal_handle == NULL ||
355 context->dal_handle->db_context == NULL)
356 return 0;
357
358 dal_handle = context->dal_handle;
359 ldap_context = (krb5_ldap_context *) dal_handle->db_context;
360 dal_handle->db_context = NULL;
361
362 krb5_ldap_free_ldap_context(ldap_context);
363
364 return 0;
365 }
366