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