1 /* 2 * Copyright 2007 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 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 krb5_set_error_message(context, st, gettext("LDAP bind password value missing ")); 63 goto err_out; 64 } 65 66 if (ldap_context->bind_pwd == NULL && 67 ldap_context->service_password_file != NULL && 68 ldap_context->service_cert_path == NULL) { 69 70 if ((st=krb5_ldap_readpassword(context, ldap_context, &password)) != 0) { 71 prepend_err_str(context, gettext("Error reading password from stash: "), st, st); 72 goto err_out; 73 } 74 75 /* Check if the returned 'password' is actually the path of a certificate */ 76 if (!strncmp("{FILE}", (char *)password, 6)) { 77 /* 'password' format: <path>\0<password> */ 78 ldap_context->service_cert_path = strdup((char *)password + strlen("{FILE}")); 79 if (ldap_context->service_cert_path == NULL) { 80 st = ENOMEM; 81 krb5_set_error_message(context, st, gettext("Error: memory allocation failed")); 82 goto err_out; 83 } 84 if (password[strlen((char *)password) + 1] == '\0') 85 ldap_context->service_cert_pass = NULL; 86 else { 87 ldap_context->service_cert_pass = strdup((char *)password + 88 strlen((char *)password) + 1); 89 if (ldap_context->service_cert_pass == NULL) { 90 st = ENOMEM; 91 krb5_set_error_message(context, st, gettext("Error: memory allocation failed")); 92 goto err_out; 93 } 94 } 95 free(password); 96 } else { 97 ldap_context->bind_pwd = (char *)password; 98 if (ldap_context->bind_pwd == NULL) { 99 st = EINVAL; 100 krb5_set_error_message(context, st, gettext("Error reading password from stash")); 101 goto err_out; 102 } 103 } 104 } 105 106 /* NULL password not allowed */ 107 if (ldap_context->bind_pwd != NULL && strlen(ldap_context->bind_pwd) == 0) { 108 st = EINVAL; 109 krb5_set_error_message(context, st, gettext("Service password length is zero")); 110 goto err_out; 111 } 112 113 err_out: 114 return st; 115 } 116 117 /* 118 * Internal Functions called by init functions. 119 */ 120 121 static krb5_error_code 122 krb5_ldap_bind(ldap_context, ldap_server_handle) 123 krb5_ldap_context *ldap_context; 124 krb5_ldap_server_handle *ldap_server_handle; 125 { 126 krb5_error_code st=0; 127 struct berval bv={0, NULL}, *servercreds=NULL; 128 129 if (ldap_context->service_cert_path != NULL) { 130 /* Certificate based bind (SASL EXTERNAL mechanism) */ 131 132 st = ldap_sasl_bind_s(ldap_server_handle->ldap_handle, 133 NULL, /* Authenticating dn */ 134 LDAP_SASL_EXTERNAL, /* Method used for authentication */ 135 &bv, 136 NULL, 137 NULL, 138 &servercreds); 139 140 while (st == LDAP_SASL_BIND_IN_PROGRESS) { 141 st = ldap_sasl_bind_s(ldap_server_handle->ldap_handle, 142 NULL, 143 LDAP_SASL_EXTERNAL, 144 servercreds, 145 NULL, 146 NULL, 147 &servercreds); 148 } 149 } else { 150 /* password based simple bind */ 151 bv.bv_val = ldap_context->bind_pwd; 152 bv.bv_len = strlen(ldap_context->bind_pwd); 153 st = ldap_sasl_bind_s(ldap_server_handle->ldap_handle, 154 ldap_context->bind_dn, 155 LDAP_SASL_SIMPLE, &bv, NULL, 156 NULL, NULL); 157 } 158 return st; 159 } 160 161 static krb5_error_code 162 krb5_ldap_initialize(ldap_context, server_info) 163 krb5_ldap_context *ldap_context; 164 krb5_ldap_server_info *server_info; 165 { 166 krb5_error_code st=0; 167 krb5_ldap_server_handle *ldap_server_handle=NULL; 168 char *errstr = NULL; 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 krb5_set_error_message (ldap_context->kcontext, 252 KRB5_KDB_ACCESS_ERROR, "%s", 253 ldap_err2string(st)); 254 st = KRB5_KDB_ACCESS_ERROR; 255 server_info->server_status = OFF; 256 time(&server_info->downtime); 257 (void)ldap_unbind_s(ldap_server_handle->ldap_handle); 258 free(ldap_server_handle); 259 } 260 261 err_out: 262 return st; 263 } 264 265 /* 266 * initialization for data base routines. 267 */ 268 269 krb5_error_code 270 krb5_ldap_db_init(krb5_context context, krb5_ldap_context *ldap_context) 271 { 272 krb5_error_code st=0; 273 krb5_boolean sasl_mech_supported=TRUE; 274 int cnt=0, version=LDAP_VERSION3; 275 struct timeval local_timelimit = {10,0}; 276 277 if ((st=krb5_validate_ldap_context(context, ldap_context)) != 0) 278 goto err_out; 279 280 ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &version); 281 #ifdef LDAP_OPT_NETWORK_TIMEOUT 282 ldap_set_option(NULL, LDAP_OPT_NETWORK_TIMEOUT, &local_timelimit); 283 #elif defined LDAP_X_OPT_CONNECT_TIMEOUT 284 ldap_set_option(NULL, LDAP_X_OPT_CONNECT_TIMEOUT, &local_timelimit); 285 #endif 286 287 HNDL_LOCK(ldap_context); 288 while (ldap_context->server_info_list[cnt] != NULL) { 289 krb5_ldap_server_info *server_info=NULL; 290 291 server_info = ldap_context->server_info_list[cnt]; 292 293 if (server_info->server_status == NOTSET) { 294 int conns=0; 295 296 /* 297 * Check if the server has to perform certificate-based authentication 298 */ 299 if (ldap_context->service_cert_path != NULL) { 300 /* Find out if the server supports SASL EXTERNAL mechanism */ 301 if (has_sasl_external_mech(context, server_info->server_name) == 1) { 302 cnt++; 303 sasl_mech_supported = FALSE; 304 continue; /* Check the next LDAP server */ 305 } 306 sasl_mech_supported = TRUE; 307 } 308 309 krb5_clear_error_message(context); 310 311 for (conns=0; conns < ldap_context->max_server_conns; ++conns) { 312 if ((st=krb5_ldap_initialize(ldap_context, server_info)) != 0) 313 break; 314 } /* for (conn= ... */ 315 316 if (server_info->server_status == ON) 317 break; /* server init successful, so break */ 318 } 319 ++cnt; 320 } 321 HNDL_UNLOCK(ldap_context); 322 323 err_out: 324 if (sasl_mech_supported == FALSE) { 325 st = KRB5_KDB_ACCESS_ERROR; 326 krb5_set_error_message (context, st, 327 gettext("Certificate based authentication requested but " 328 "not supported by LDAP servers")); 329 } 330 return (st); 331 } 332 333 334 /* 335 * get a single handle. Do not lock the mutex 336 */ 337 338 krb5_error_code 339 krb5_ldap_db_single_init(krb5_ldap_context *ldap_context) 340 { 341 krb5_error_code st=0; 342 int cnt=0; 343 krb5_ldap_server_info *server_info=NULL; 344 345 while (ldap_context->server_info_list[cnt] != NULL) { 346 server_info = ldap_context->server_info_list[cnt]; 347 if ((server_info->server_status == NOTSET || server_info->server_status == ON)) { 348 if (server_info->num_conns < ldap_context->max_server_conns-1) { 349 st = krb5_ldap_initialize(ldap_context, server_info); 350 if (st == LDAP_SUCCESS) 351 goto cleanup; 352 } 353 } 354 ++cnt; 355 } 356 357 /* If we are here, try to connect to all the servers */ 358 359 cnt = 0; 360 while (ldap_context->server_info_list[cnt] != NULL) { 361 server_info = ldap_context->server_info_list[cnt]; 362 st = krb5_ldap_initialize(ldap_context, server_info); 363 if (st == LDAP_SUCCESS) 364 goto cleanup; 365 ++cnt; 366 } 367 cleanup: 368 return (st); 369 } 370 371 krb5_error_code 372 krb5_ldap_rebind(ldap_context, ldap_server_handle) 373 krb5_ldap_context *ldap_context; 374 krb5_ldap_server_handle **ldap_server_handle; 375 { 376 krb5_ldap_server_handle *handle = *ldap_server_handle; 377 int use_ssl; 378 379 /* 380 * Solaris Kerberos: use SSL unless ldapi (unix domain sockets is specified) 381 */ 382 if (strncasecmp(handle->server_info->server_name, "ldapi:", 6) == 0) 383 use_ssl = SSL_OFF; 384 else 385 use_ssl = SSL_ON; 386 387 if ((ldap_initialize(&handle->ldap_handle, handle->server_info->server_name, 388 use_ssl, NULL) != LDAP_SUCCESS) 389 || (krb5_ldap_bind(ldap_context, handle) != LDAP_SUCCESS)) 390 return krb5_ldap_request_next_handle_from_pool(ldap_context, ldap_server_handle); 391 return LDAP_SUCCESS; 392 } 393 394 /* 395 * DAL API functions 396 */ 397 krb5_error_code krb5_ldap_lib_init() 398 { 399 return 0; 400 } 401 402 krb5_error_code krb5_ldap_lib_cleanup() 403 { 404 /* right now, no cleanup required */ 405 return 0; 406 } 407 408 krb5_error_code 409 krb5_ldap_free_ldap_context(krb5_ldap_context *ldap_context) 410 { 411 if (ldap_context == NULL) 412 return 0; 413 414 krb5_ldap_free_krbcontainer_params(ldap_context->krbcontainer); 415 ldap_context->krbcontainer = NULL; 416 417 krb5_ldap_free_realm_params(ldap_context->lrparams); 418 ldap_context->lrparams = NULL; 419 420 krb5_ldap_free_server_params(ldap_context); 421 422 return 0; 423 } 424 425 krb5_error_code 426 krb5_ldap_close(krb5_context context) 427 { 428 kdb5_dal_handle *dal_handle=NULL; 429 krb5_ldap_context *ldap_context=NULL; 430 431 if (context == NULL || 432 context->db_context == NULL || 433 ((kdb5_dal_handle *)context->db_context)->db_context == NULL) 434 return 0; 435 436 dal_handle = (kdb5_dal_handle *) context->db_context; 437 ldap_context = (krb5_ldap_context *) dal_handle->db_context; 438 dal_handle->db_context = NULL; 439 440 krb5_ldap_free_ldap_context(ldap_context); 441 442 return 0; 443 } 444