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