1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 7 /* 8 * lib/kdb/kdb_ldap/kdb_ldap.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 <ctype.h> 43 #include "kdb_ldap.h" 44 #include "ldap_misc.h" 45 #include "ldap_main.h" 46 #include <kdb5.h> 47 #include <kadm5/admin.h> 48 /* Solaris Kerberos: needed for MAKE_INIT_FUNCTION() */ 49 #include <k5-platform.h> 50 #include <k5-int.h> 51 #include <libintl.h> 52 53 krb5_error_code 54 krb5_ldap_get_db_opt(char *input, char **opt, char **val) 55 { 56 char *pos = strchr(input, '='); 57 58 *val = NULL; 59 if (pos == NULL) { 60 *opt = strdup(input); 61 if (*opt == NULL) { 62 return ENOMEM; 63 } 64 } else { 65 int len = pos - input; 66 *opt = malloc((unsigned) len + 1); 67 if (!*opt) { 68 return ENOMEM; 69 } 70 memcpy(*opt, input, (unsigned) len); 71 /* ignore trailing blanks */ 72 while (isblank((*opt)[len-1])) 73 --len; 74 (*opt)[len] = '\0'; 75 76 pos += 1; /* move past '=' */ 77 while (isblank(*pos)) /* ignore leading blanks */ 78 pos += 1; 79 if (*pos != '\0') { 80 *val = strdup (pos); 81 if (!*val) { 82 free (*opt); 83 return ENOMEM; 84 } 85 } 86 } 87 return (0); 88 89 } 90 91 92 /* 93 * ldap get age 94 */ 95 /*ARGSUSED*/ 96 krb5_error_code 97 krb5_ldap_db_get_age(context, db_name, age) 98 krb5_context context; 99 char *db_name; 100 time_t *age; 101 { 102 time (age); 103 return 0; 104 } 105 106 /* 107 * read startup information - kerberos and realm container 108 */ 109 krb5_error_code 110 krb5_ldap_read_startup_information(krb5_context context) 111 { 112 krb5_error_code retval = 0; 113 kdb5_dal_handle *dal_handle=NULL; 114 krb5_ldap_context *ldap_context=NULL; 115 int mask = 0; 116 117 SETUP_CONTEXT(); 118 if ((retval=krb5_ldap_read_krbcontainer_params(context, &(ldap_context->krbcontainer)))) { 119 prepend_err_str (context, gettext("Unable to read Kerberos container"), retval, retval); 120 goto cleanup; 121 } 122 123 if ((retval=krb5_ldap_read_realm_params(context, context->default_realm, &(ldap_context->lrparams), &mask))) { 124 prepend_err_str (context, gettext("Unable to read Realm"), retval, retval); 125 goto cleanup; 126 } 127 128 if (((mask & LDAP_REALM_MAXTICKETLIFE) == 0) || ((mask & LDAP_REALM_MAXRENEWLIFE) == 0) 129 || ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0)) { 130 kadm5_config_params params_in, params_out; 131 132 memset((char *) ¶ms_in, 0, sizeof(params_in)); 133 memset((char *) ¶ms_out, 0, sizeof(params_out)); 134 135 retval = kadm5_get_config_params(context, 1, ¶ms_in, ¶ms_out); 136 if (retval) { 137 if ((mask & LDAP_REALM_MAXTICKETLIFE) == 0) { 138 ldap_context->lrparams->max_life = 24 * 60 * 60; /* 1 day */ 139 } 140 if ((mask & LDAP_REALM_MAXRENEWLIFE) == 0) { 141 ldap_context->lrparams->max_renewable_life = 0; 142 } 143 if ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0) { 144 ldap_context->lrparams->tktflags = KRB5_KDB_DEF_FLAGS; 145 } 146 retval = 0; 147 goto cleanup; 148 } 149 150 if ((mask & LDAP_REALM_MAXTICKETLIFE) == 0) { 151 if (params_out.mask & KADM5_CONFIG_MAX_LIFE) 152 ldap_context->lrparams->max_life = params_out.max_life; 153 } 154 155 if ((mask & LDAP_REALM_MAXRENEWLIFE) == 0) { 156 if (params_out.mask & KADM5_CONFIG_MAX_RLIFE) 157 ldap_context->lrparams->max_renewable_life = params_out.max_rlife; 158 } 159 160 if ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0) { 161 if (params_out.mask & KADM5_CONFIG_FLAGS) 162 ldap_context->lrparams->tktflags = params_out.flags; 163 } 164 165 kadm5_free_config_params(context, ¶ms_out); 166 } 167 168 cleanup: 169 return retval; 170 } 171 172 173 /* Function to check if a LDAP server supports the SASL external mechanism 174 *Return values: 175 * 0 => supports 176 * 1 => does not support 177 * 2 => don't know 178 */ 179 #define ERR_MSG1 "Unable to check if SASL EXTERNAL mechanism is supported by LDAP server. Proceeding anyway ..." 180 #define ERR_MSG2 "SASL EXTERNAL mechanism not supported by LDAP server. Can't perform certificate-based bind." 181 182 int 183 has_sasl_external_mech(context, ldap_server) 184 krb5_context context; 185 char *ldap_server; 186 { 187 int i=0, flag=0, ret=0, retval=0; 188 char *attrs[]={"supportedSASLMechanisms", NULL}, **values=NULL; 189 LDAP *ld=NULL; 190 LDAPMessage *msg=NULL, *res=NULL; 191 192 /* 193 * Solaris Kerberos: don't use SSL since we are checking to see if SASL 194 * Externnal mech is supported. 195 */ 196 retval = ldap_initialize(&ld, ldap_server, SSL_OFF, NULL); 197 if (retval != LDAP_SUCCESS) { 198 krb5_set_error_message(context, 2, "%s", ERR_MSG1); 199 ret = 2; /* Don't know */ 200 goto cleanup; 201 } 202 203 /* Solaris Kerberos: anon bind not needed */ 204 #if 0 /************** Begin IFDEF'ed OUT *******************************/ 205 /* Anonymous bind */ 206 retval = ldap_sasl_bind_s(ld, NULL, NULL, NULL, NULL, NULL, NULL); 207 if (retval != LDAP_SUCCESS) { 208 krb5_set_error_message(context, 2, "%s", ERR_MSG1); 209 ret = 2; /* Don't know */ 210 goto cleanup; 211 } 212 #endif /**************** END IFDEF'ed OUT *******************************/ 213 214 retval = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, NULL, attrs, 0, NULL, NULL, NULL, 0, &res); 215 if (retval != LDAP_SUCCESS) { 216 krb5_set_error_message(context, 2, "%s", ERR_MSG1); 217 ret = 2; /* Don't know */ 218 goto cleanup; 219 } 220 221 #if 0 /************** Begin IFDEF'ed OUT *******************************/ 222 msg = ldap_first_message(ld, res); 223 #else 224 /* Solaris Kerberos: more accurate */ 225 msg = ldap_first_entry(ld, res); 226 #endif /**************** END IFDEF'ed OUT *******************************/ 227 if (msg == NULL) { 228 krb5_set_error_message(context, 2, "%s", ERR_MSG1); 229 ret = 2; /* Don't know */ 230 goto cleanup; 231 } 232 233 values = ldap_get_values(ld, msg, "supportedSASLMechanisms"); 234 if (values == NULL) { 235 krb5_set_error_message(context, 1, "%s", ERR_MSG2); 236 ret = 1; /* Not supported */ 237 goto cleanup; 238 } 239 240 for (i = 0; values[i] != NULL; i++) { 241 if (strcmp(values[i], "EXTERNAL")) 242 continue; 243 flag = 1; 244 } 245 246 if (flag != 1) { 247 krb5_set_error_message(context, 1, "%s", ERR_MSG2); 248 ret = 1; /* Not supported */ 249 goto cleanup; 250 } 251 252 cleanup: 253 254 if (values != NULL) 255 ldap_value_free(values); 256 257 if (res != NULL) 258 ldap_msgfree(res); 259 260 if (ld != NULL) 261 ldap_unbind_ext_s(ld, NULL, NULL); 262 263 return ret; 264 } 265 266 /*ARGSUSED*/ 267 void * krb5_ldap_alloc(krb5_context context, void *ptr, size_t size) 268 { 269 return realloc(ptr, size); 270 } 271 272 /*ARGSUSED*/ 273 void krb5_ldap_free(krb5_context context, void *ptr) 274 275 { 276 free(ptr); 277 } 278 279 krb5_error_code krb5_ldap_open(krb5_context context, 280 char *conf_section, 281 char **db_args, 282 int mode) 283 { 284 krb5_error_code status = 0; 285 char **t_ptr = db_args; 286 krb5_ldap_context *ldap_context=NULL; 287 int srv_cnt = 0; 288 kdb5_dal_handle *dal_handle=NULL; 289 290 /* Clear the global error string */ 291 krb5_clear_error_message(context); 292 293 ldap_context = calloc(1, sizeof(krb5_ldap_context)); 294 if (ldap_context == NULL) { 295 status = ENOMEM; 296 goto clean_n_exit; 297 } 298 299 ldap_context->kcontext = context; 300 301 while (t_ptr && *t_ptr) { 302 char *opt = NULL, *val = NULL; 303 304 if ((status = krb5_ldap_get_db_opt(*t_ptr, &opt, &val)) != 0) { 305 goto clean_n_exit; 306 } 307 if (opt && !strcmp(opt, "binddn")) { 308 if (ldap_context->bind_dn) { 309 free (opt); 310 free (val); 311 status = EINVAL; 312 krb5_set_error_message (context, status, gettext("'binddn' missing")); 313 goto clean_n_exit; 314 } 315 if (val == NULL) { 316 status = EINVAL; 317 krb5_set_error_message (context, status, gettext("'binddn' value missing")); 318 free(opt); 319 goto clean_n_exit; 320 } 321 ldap_context->bind_dn = strdup(val); 322 if (ldap_context->bind_dn == NULL) { 323 free (opt); 324 free (val); 325 status = ENOMEM; 326 goto clean_n_exit; 327 } 328 } else if (opt && !strcmp(opt, "nconns")) { 329 if (ldap_context->max_server_conns) { 330 free (opt); 331 free (val); 332 status = EINVAL; 333 krb5_set_error_message (context, status, gettext("'nconns' missing")); 334 goto clean_n_exit; 335 } 336 if (val == NULL) { 337 status = EINVAL; 338 krb5_set_error_message (context, status, gettext("'nconns' value missing")); 339 free(opt); 340 goto clean_n_exit; 341 } 342 ldap_context->max_server_conns = atoi(val) ? atoi(val) : DEFAULT_CONNS_PER_SERVER; 343 } else if (opt && !strcmp(opt, "bindpwd")) { 344 if (ldap_context->bind_pwd) { 345 free (opt); 346 free (val); 347 status = EINVAL; 348 krb5_set_error_message (context, status, gettext("'bindpwd' missing")); 349 goto clean_n_exit; 350 } 351 if (val == NULL) { 352 status = EINVAL; 353 krb5_set_error_message (context, status, gettext("'bindpwd' value missing")); 354 free(opt); 355 goto clean_n_exit; 356 } 357 ldap_context->bind_pwd = strdup(val); 358 if (ldap_context->bind_pwd == NULL) { 359 free (opt); 360 free (val); 361 status = ENOMEM; 362 goto clean_n_exit; 363 } 364 } else if (opt && !strcmp(opt, "host")) { 365 if (val == NULL) { 366 status = EINVAL; 367 krb5_set_error_message (context, status, gettext("'host' value missing")); 368 free(opt); 369 goto clean_n_exit; 370 } 371 if (ldap_context->server_info_list == NULL) 372 ldap_context->server_info_list = (krb5_ldap_server_info **) calloc (SERV_COUNT+1, sizeof (krb5_ldap_server_info *)) ; 373 374 if (ldap_context->server_info_list == NULL) { 375 free (opt); 376 free (val); 377 status = ENOMEM; 378 goto clean_n_exit; 379 } 380 381 ldap_context->server_info_list[srv_cnt] = (krb5_ldap_server_info *) calloc (1, sizeof (krb5_ldap_server_info)); 382 if (ldap_context->server_info_list[srv_cnt] == NULL) { 383 free (opt); 384 free (val); 385 status = ENOMEM; 386 goto clean_n_exit; 387 } 388 389 ldap_context->server_info_list[srv_cnt]->server_status = NOTSET; 390 391 ldap_context->server_info_list[srv_cnt]->server_name = strdup(val); 392 if (ldap_context->server_info_list[srv_cnt]->server_name == NULL) { 393 free (opt); 394 free (val); 395 status = ENOMEM; 396 goto clean_n_exit; 397 } 398 399 srv_cnt++; 400 #ifdef HAVE_EDIRECTORY 401 } else if (opt && !strcmp(opt, "cert")) { 402 if (val == NULL) { 403 status = EINVAL; 404 krb5_set_error_message (context, status, gettext("'cert' value missing")); 405 free(opt); 406 goto clean_n_exit; 407 } 408 409 if (ldap_context->root_certificate_file == NULL) { 410 ldap_context->root_certificate_file = strdup(val); 411 if (ldap_context->root_certificate_file == NULL) { 412 free (opt); 413 free (val); 414 status = ENOMEM; 415 goto clean_n_exit; 416 } 417 } else { 418 void *tmp=NULL; 419 char *oldstr = NULL; 420 unsigned int len=0; 421 422 oldstr = strdup(ldap_context->root_certificate_file); 423 if (oldstr == NULL) { 424 free (opt); 425 free (val); 426 status = ENOMEM; 427 goto clean_n_exit; 428 } 429 430 tmp = ldap_context->root_certificate_file; 431 len = strlen(ldap_context->root_certificate_file) + 2 + strlen(val); 432 ldap_context->root_certificate_file = realloc(ldap_context->root_certificate_file, 433 len); 434 if (ldap_context->root_certificate_file == NULL) { 435 free (tmp); 436 free (opt); 437 free (val); 438 status = ENOMEM; 439 goto clean_n_exit; 440 } 441 memset(ldap_context->root_certificate_file, 0, len); 442 sprintf(ldap_context->root_certificate_file,"%s %s", oldstr, val); 443 free (oldstr); 444 } 445 #endif 446 } else { 447 /* ignore hash argument. Might have been passed from create */ 448 status = EINVAL; 449 if (opt && !strcmp(opt, "temporary")) { 450 /* 451 * temporary is passed in when kdb5_util load without -update is done. 452 * This is unsupported by the LDAP plugin. 453 */ 454 krb5_set_error_message (context, status, 455 gettext("open of LDAP directory aborted, plugin requires -update argument")); 456 } else { 457 krb5_set_error_message (context, status, gettext("unknown option \'%s\'"), 458 opt?opt:val); 459 } 460 free(opt); 461 free(val); 462 goto clean_n_exit; 463 } 464 465 free(opt); 466 free(val); 467 t_ptr++; 468 } 469 470 dal_handle = (kdb5_dal_handle *) context->db_context; 471 dal_handle->db_context = ldap_context; 472 status = krb5_ldap_read_server_params(context, conf_section, mode & 0x0300); 473 if (status) { 474 if (ldap_context) 475 krb5_ldap_free_ldap_context(ldap_context); 476 ldap_context = NULL; 477 dal_handle->db_context = NULL; 478 prepend_err_str (context, gettext("Error reading LDAP server params: "), status, status); 479 goto clean_n_exit; 480 } 481 if ((status=krb5_ldap_db_init(context, ldap_context)) != 0) { 482 goto clean_n_exit; 483 } 484 485 if ((status=krb5_ldap_read_startup_information(context)) != 0) { 486 goto clean_n_exit; 487 } 488 489 clean_n_exit: 490 /* may be clearing up is not required db_fini might do it for us, check out */ 491 if (status) { 492 krb5_ldap_close(context); 493 } 494 return status; 495 } 496 497 #include "ldap_err.h" 498 int 499 set_ldap_error (krb5_context ctx, int st, int op) 500 { 501 int translated_st = translate_ldap_error(st, op); 502 krb5_set_error_message(ctx, translated_st, "%s", ldap_err2string(st)); 503 return translated_st; 504 } 505 506 void 507 prepend_err_str (krb5_context ctx, const char *str, krb5_error_code err, 508 krb5_error_code oerr) 509 { 510 const char *omsg; 511 if (oerr == 0) oerr = err; 512 omsg = krb5_get_error_message (ctx, err); 513 krb5_set_error_message (ctx, err, "%s %s", str, omsg); 514 /* Solaris Kerberos: Memleak */ 515 krb5_free_error_message(ctx, omsg); 516 } 517 518 extern krb5int_access accessor; 519 MAKE_INIT_FUNCTION(kldap_init_fn); 520 521 int kldap_init_fn(void) 522 { 523 /* Global (per-module) initialization. */ 524 return krb5int_accessor (&accessor, KRB5INT_ACCESS_VERSION); 525 } 526 527 int kldap_ensure_initialized(void) 528 { 529 return CALL_INIT_FUNCTION (kldap_init_fn); 530 } 531