1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * lib/kdb/kdb_ldap/kdb_ldap_conn.c 10 * 11 * Copyright (c) 2004-2005, Novell, Inc. 12 * All rights reserved. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are met: 16 * 17 * * Redistributions of source code must retain the above copyright notice, 18 * this list of conditions and the following disclaimer. 19 * * Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * * The copyright holder's name is not used to endorse or promote products 23 * derived from this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 26 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 29 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35 * POSSIBILITY OF SUCH DAMAGE. 36 */ 37 38 #include "autoconf.h" 39 #if HAVE_UNISTD_H 40 #include <unistd.h> 41 #endif 42 43 #include "ldap_main.h" 44 #include "ldap_service_stash.h" 45 #include <kdb5.h> 46 #include <libintl.h> 47 48 static krb5_error_code 49 krb5_validate_ldap_context(krb5_context context, krb5_ldap_context *ldap_context) 50 { 51 krb5_error_code st=0; 52 unsigned char *password=NULL; 53 54 if (ldap_context->bind_dn == NULL) { 55 st = EINVAL; 56 /* Solaris Kerberos: Keep error messages consistent */ 57 krb5_set_error_message(context, st, gettext("LDAP bind dn value missing")); 58 goto err_out; 59 } 60 61 if (ldap_context->bind_pwd == NULL && ldap_context->service_password_file == NULL) { 62 st = EINVAL; 63 /* Solaris Kerberos: Keep error messages consistent */ 64 krb5_set_error_message(context, st, gettext("LDAP bind password value missing")); 65 goto err_out; 66 } 67 68 if (ldap_context->bind_pwd == NULL && 69 ldap_context->service_password_file != NULL && 70 ldap_context->service_cert_path == NULL) { 71 72 if ((st=krb5_ldap_readpassword(context, ldap_context, &password)) != 0) { 73 prepend_err_str(context, gettext("Error reading password from stash: "), st, st); 74 goto err_out; 75 } 76 77 /* Check if the returned 'password' is actually the path of a certificate */ 78 if (!strncmp("{FILE}", (char *)password, 6)) { 79 /* 'password' format: <path>\0<password> */ 80 ldap_context->service_cert_path = strdup((char *)password + strlen("{FILE}")); 81 if (ldap_context->service_cert_path == NULL) { 82 st = ENOMEM; 83 krb5_set_error_message(context, st, gettext("Error: memory allocation failed")); 84 goto err_out; 85 } 86 if (password[strlen((char *)password) + 1] == '\0') 87 ldap_context->service_cert_pass = NULL; 88 else { 89 ldap_context->service_cert_pass = strdup((char *)password + 90 strlen((char *)password) + 1); 91 if (ldap_context->service_cert_pass == NULL) { 92 st = ENOMEM; 93 krb5_set_error_message(context, st, gettext("Error: memory allocation failed")); 94 goto err_out; 95 } 96 } 97 free(password); 98 } else { 99 ldap_context->bind_pwd = (char *)password; 100 if (ldap_context->bind_pwd == NULL) { 101 st = EINVAL; 102 krb5_set_error_message(context, st, gettext("Error reading password from stash")); 103 goto err_out; 104 } 105 } 106 } 107 108 /* NULL password not allowed */ 109 if (ldap_context->bind_pwd != NULL && strlen(ldap_context->bind_pwd) == 0) { 110 st = EINVAL; 111 krb5_set_error_message(context, st, gettext("Service password length is zero")); 112 goto err_out; 113 } 114 115 err_out: 116 return st; 117 } 118 119 /* 120 * Internal Functions called by init functions. 121 */ 122 123 static krb5_error_code 124 krb5_ldap_bind(ldap_context, ldap_server_handle) 125 krb5_ldap_context *ldap_context; 126 krb5_ldap_server_handle *ldap_server_handle; 127 { 128 krb5_error_code st=0; 129 struct berval bv={0, NULL}, *servercreds=NULL; 130 131 if (ldap_context->service_cert_path != NULL) { 132 /* Certificate based bind (SASL EXTERNAL mechanism) */ 133 134 st = ldap_sasl_bind_s(ldap_server_handle->ldap_handle, 135 NULL, /* Authenticating dn */ 136 LDAP_SASL_EXTERNAL, /* Method used for authentication */ 137 &bv, 138 NULL, 139 NULL, 140 &servercreds); 141 142 while (st == LDAP_SASL_BIND_IN_PROGRESS) { 143 st = ldap_sasl_bind_s(ldap_server_handle->ldap_handle, 144 NULL, 145 LDAP_SASL_EXTERNAL, 146 servercreds, 147 NULL, 148 NULL, 149 &servercreds); 150 } 151 } else { 152 /* password based simple bind */ 153 bv.bv_val = ldap_context->bind_pwd; 154 bv.bv_len = strlen(ldap_context->bind_pwd); 155 st = ldap_sasl_bind_s(ldap_server_handle->ldap_handle, 156 ldap_context->bind_dn, 157 LDAP_SASL_SIMPLE, &bv, NULL, 158 NULL, NULL); 159 } 160 return st; 161 } 162 163 static krb5_error_code 164 krb5_ldap_initialize(ldap_context, server_info) 165 krb5_ldap_context *ldap_context; 166 krb5_ldap_server_info *server_info; 167 { 168 krb5_error_code st=0; 169 krb5_ldap_server_handle *ldap_server_handle=NULL; 170 char *errstr = NULL; 171 172 ldap_server_handle = calloc(1, sizeof(krb5_ldap_server_handle)); 173 if (ldap_server_handle == NULL) { 174 st = ENOMEM; 175 goto err_out; 176 } 177 else { 178 /* 179 * Solaris Kerbreros: need ldap_handle to be NULL so calls to 180 * ldap_initialize won't leak handles 181 */ 182 ldap_server_handle->ldap_handle = NULL; 183 } 184 185 if (strncasecmp(server_info->server_name, "ldapi:", 6) == 0) { 186 /* 187 * Solaris Kerberos: ldapi is not supported on Solaris at this time. 188 * return an error. 189 */ 190 if (ldap_context->kcontext) 191 krb5_set_error_message (ldap_context->kcontext, KRB5_KDB_ACCESS_ERROR, 192 gettext("ldapi is not supported")); 193 st = KRB5_KDB_ACCESS_ERROR; 194 goto err_out; 195 } else { 196 /* 197 * Solaris Kerbreros: need to use SSL to protect LDAP simple and 198 * External binds. 199 */ 200 if (ldap_context->root_certificate_file == NULL) { 201 if (ldap_context->kcontext) 202 krb5_set_error_message (ldap_context->kcontext, KRB5_KDB_ACCESS_ERROR, 203 gettext("ldap_cert_path not set, can not create SSL connection")); 204 st = KRB5_KDB_ACCESS_ERROR; 205 goto err_out; 206 } 207 208 /* setup for SSL */ 209 if ((st = ldapssl_client_init(ldap_context->root_certificate_file, NULL)) < 0) { 210 if (ldap_context->kcontext) 211 krb5_set_error_message (ldap_context->kcontext, KRB5_KDB_ACCESS_ERROR, "%s", 212 ldapssl_err2string(st)); 213 st = KRB5_KDB_ACCESS_ERROR; 214 goto err_out; 215 } 216 217 /* ldap init, use SSL */ 218 if ((st = ldap_initialize(&ldap_server_handle->ldap_handle, 219 server_info->server_name, SSL_ON, &errstr)) != LDAP_SUCCESS) { 220 if (ldap_context->kcontext) { 221 krb5_set_error_message (ldap_context->kcontext, KRB5_KDB_ACCESS_ERROR, "%s", 222 errstr); 223 } 224 st = KRB5_KDB_ACCESS_ERROR; 225 goto err_out; 226 } 227 228 if (ldap_context->service_cert_path != NULL) { 229 /* 230 * Solaris Kerbreros: for LDAP_SASL_EXTERNAL bind which requires the 231 * client offer its cert to the server. 232 */ 233 if ((st = ldapssl_enable_clientauth(ldap_server_handle->ldap_handle, 234 NULL, ldap_context->service_cert_pass, 235 "XXX WAF need cert nickname/label")) < 0) { 236 if (ldap_context->kcontext) { 237 krb5_set_error_message (ldap_context->kcontext, 238 KRB5_KDB_ACCESS_ERROR, "%s", 239 ldap_err2string(st)); 240 } 241 st = KRB5_KDB_ACCESS_ERROR; 242 goto err_out; 243 } 244 } 245 } 246 247 if ((st=krb5_ldap_bind(ldap_context, ldap_server_handle)) == 0) { 248 ldap_server_handle->server_info_update_pending = FALSE; 249 server_info->server_status = ON; 250 krb5_update_ldap_handle(ldap_server_handle, server_info); 251 } else { 252 if (ldap_context->kcontext) 253 /* Solaris Kerberos: Better error message */ 254 krb5_set_error_message (ldap_context->kcontext, 255 KRB5_KDB_ACCESS_ERROR, 256 gettext("Failed to bind to ldap server \"%s\": %s"), 257 server_info->server_name, ldap_err2string(st)); 258 st = KRB5_KDB_ACCESS_ERROR; 259 server_info->server_status = OFF; 260 time(&server_info->downtime); 261 (void)ldap_unbind_s(ldap_server_handle->ldap_handle); 262 free(ldap_server_handle); 263 } 264 265 err_out: 266 return st; 267 } 268 269 /* 270 * initialization for data base routines. 271 */ 272 273 krb5_error_code 274 krb5_ldap_db_init(krb5_context context, krb5_ldap_context *ldap_context) 275 { 276 krb5_error_code st=0; 277 krb5_boolean sasl_mech_supported=TRUE; 278 int cnt=0, version=LDAP_VERSION3; 279 #ifdef LDAP_OPT_NETWORK_TIMEOUT 280 struct timeval local_timelimit = {10,0}; 281 #elif defined LDAP_X_OPT_CONNECT_TIMEOUT 282 int local_timelimit = 10; 283 #endif 284 285 if ((st=krb5_validate_ldap_context(context, ldap_context)) != 0) 286 goto err_out; 287 288 ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &version); 289 #ifdef LDAP_OPT_NETWORK_TIMEOUT 290 ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &local_timelimit); 291 #elif defined LDAP_X_OPT_CONNECT_TIMEOUT 292 ldap_set_option(NULL, LDAP_X_OPT_CONNECT_TIMEOUT, &local_timelimit); 293 #endif 294 295 HNDL_LOCK(ldap_context); 296 while (ldap_context->server_info_list[cnt] != NULL) { 297 krb5_ldap_server_info *server_info=NULL; 298 299 server_info = ldap_context->server_info_list[cnt]; 300 301 if (server_info->server_status == NOTSET) { 302 int conns=0; 303 304 /* 305 * Check if the server has to perform certificate-based authentication 306 */ 307 if (ldap_context->service_cert_path != NULL) { 308 /* Find out if the server supports SASL EXTERNAL mechanism */ 309 if (has_sasl_external_mech(context, server_info->server_name) == 1) { 310 cnt++; 311 sasl_mech_supported = FALSE; 312 continue; /* Check the next LDAP server */ 313 } 314 sasl_mech_supported = TRUE; 315 } 316 317 krb5_clear_error_message(context); 318 319 for (conns=0; conns < ldap_context->max_server_conns; ++conns) { 320 if ((st=krb5_ldap_initialize(ldap_context, server_info)) != 0) 321 break; 322 } /* for (conn= ... */ 323 324 if (server_info->server_status == ON) 325 break; /* server init successful, so break */ 326 } 327 ++cnt; 328 } 329 HNDL_UNLOCK(ldap_context); 330 331 err_out: 332 if (sasl_mech_supported == FALSE) { 333 st = KRB5_KDB_ACCESS_ERROR; 334 krb5_set_error_message (context, st, 335 gettext("Certificate based authentication requested but " 336 "not supported by LDAP servers")); 337 } 338 return (st); 339 } 340 341 342 /* 343 * get a single handle. Do not lock the mutex 344 */ 345 346 krb5_error_code 347 krb5_ldap_db_single_init(krb5_ldap_context *ldap_context) 348 { 349 krb5_error_code st=0; 350 int cnt=0; 351 krb5_ldap_server_info *server_info=NULL; 352 353 while (ldap_context->server_info_list[cnt] != NULL) { 354 server_info = ldap_context->server_info_list[cnt]; 355 if ((server_info->server_status == NOTSET || server_info->server_status == ON)) { 356 if (server_info->num_conns < ldap_context->max_server_conns-1) { 357 st = krb5_ldap_initialize(ldap_context, server_info); 358 if (st == LDAP_SUCCESS) 359 goto cleanup; 360 } 361 } 362 ++cnt; 363 } 364 365 /* If we are here, try to connect to all the servers */ 366 367 cnt = 0; 368 while (ldap_context->server_info_list[cnt] != NULL) { 369 server_info = ldap_context->server_info_list[cnt]; 370 st = krb5_ldap_initialize(ldap_context, server_info); 371 if (st == LDAP_SUCCESS) 372 goto cleanup; 373 ++cnt; 374 } 375 cleanup: 376 return (st); 377 } 378 379 krb5_error_code 380 krb5_ldap_rebind(ldap_context, ldap_server_handle) 381 krb5_ldap_context *ldap_context; 382 krb5_ldap_server_handle **ldap_server_handle; 383 { 384 krb5_ldap_server_handle *handle = *ldap_server_handle; 385 int use_ssl; 386 387 /* 388 * Solaris Kerberos: use SSL unless ldapi (unix domain sockets is specified) 389 */ 390 if (strncasecmp(handle->server_info->server_name, "ldapi:", 6) == 0) 391 use_ssl = SSL_OFF; 392 else 393 use_ssl = SSL_ON; 394 395 if ((ldap_initialize(&handle->ldap_handle, handle->server_info->server_name, 396 use_ssl, NULL) != LDAP_SUCCESS) 397 || (krb5_ldap_bind(ldap_context, handle) != LDAP_SUCCESS)) 398 return krb5_ldap_request_next_handle_from_pool(ldap_context, ldap_server_handle); 399 return LDAP_SUCCESS; 400 } 401 402 /* 403 * DAL API functions 404 */ 405 krb5_error_code krb5_ldap_lib_init() 406 { 407 return 0; 408 } 409 410 krb5_error_code krb5_ldap_lib_cleanup() 411 { 412 /* right now, no cleanup required */ 413 return 0; 414 } 415 416 krb5_error_code 417 krb5_ldap_free_ldap_context(krb5_ldap_context *ldap_context) 418 { 419 if (ldap_context == NULL) 420 return 0; 421 422 krb5_ldap_free_krbcontainer_params(ldap_context->krbcontainer); 423 ldap_context->krbcontainer = NULL; 424 425 krb5_ldap_free_realm_params(ldap_context->lrparams); 426 ldap_context->lrparams = NULL; 427 428 krb5_ldap_free_server_params(ldap_context); 429 430 return 0; 431 } 432 433 krb5_error_code 434 krb5_ldap_close(krb5_context context) 435 { 436 kdb5_dal_handle *dal_handle=NULL; 437 krb5_ldap_context *ldap_context=NULL; 438 439 if (context == NULL || 440 context->db_context == NULL || 441 ((kdb5_dal_handle *)context->db_context)->db_context == NULL) 442 return 0; 443 444 dal_handle = (kdb5_dal_handle *) context->db_context; 445 ldap_context = (krb5_ldap_context *) dal_handle->db_context; 446 dal_handle->db_context = NULL; 447 448 krb5_ldap_free_ldap_context(ldap_context); 449 450 return 0; 451 } 452