1 #pragma ident "%Z%%M% %I% %E% SMI" 2 3 /* 4 * lib/kdb/kdb_ldap/ldap_services.c 5 * 6 * Copyright (c) 2004-2005, Novell, Inc. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions are met: 11 * 12 * * Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * * Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * * The copyright holder's name is not used to endorse or promote products 18 * derived from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include "ldap_main.h" 34 #include "kdb_ldap.h" 35 #include "ldap_services.h" 36 #include "ldap_err.h" 37 #include <libintl.h> 38 39 #if defined(HAVE_EDIRECTORY) 40 41 static char *realmcontclass[] = {"krbRealmContainer", NULL}; 42 43 /* 44 * create the service object from Directory 45 */ 46 47 krb5_error_code 48 krb5_ldap_create_service(context, service, mask) 49 krb5_context context; 50 krb5_ldap_service_params *service; 51 int mask; 52 { 53 int i=0, j=0; 54 krb5_error_code st=0; 55 LDAP *ld=NULL; 56 char **rdns=NULL, *realmattr=NULL, *strval[3]={NULL}; 57 LDAPMod **mods=NULL; 58 kdb5_dal_handle *dal_handle=NULL; 59 krb5_ldap_context *ldap_context=NULL; 60 krb5_ldap_server_handle *ldap_server_handle=NULL; 61 char errbuf[1024]; 62 63 /* validate the input parameter */ 64 if (service == NULL || service->servicedn == NULL) { 65 st = EINVAL; 66 krb5_set_error_message (context, st, gettext("Service DN NULL")); 67 goto cleanup; 68 } 69 70 SETUP_CONTEXT(); 71 GET_HANDLE(); 72 73 /* identify the class that the object should belong to. This depends on the servicetype */ 74 memset(strval, 0, sizeof(strval)); 75 strval[0] = "krbService"; 76 if (service->servicetype == LDAP_KDC_SERVICE) { 77 strval[1] = "krbKdcService"; 78 realmattr = "krbKdcServers"; 79 } else if (service->servicetype == LDAP_ADMIN_SERVICE) { 80 strval[1] = "krbAdmService"; 81 realmattr = "krbAdmServers"; 82 } else if (service->servicetype == LDAP_PASSWD_SERVICE) { 83 strval[1] = "krbPwdService"; 84 realmattr = "krbPwdServers"; 85 } else { 86 strval[1] = "krbKdcService"; 87 realmattr = "krbKdcServers"; 88 } 89 if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0) 90 goto cleanup; 91 92 rdns = ldap_explode_dn(service->servicedn, 1); 93 if (rdns == NULL) { 94 st = LDAP_INVALID_DN_SYNTAX; 95 goto cleanup; 96 } 97 memset(strval, 0, sizeof(strval)); 98 strval[0] = rdns[0]; 99 if ((st=krb5_add_str_mem_ldap_mod(&mods, "cn", LDAP_MOD_ADD, strval)) != 0) 100 goto cleanup; 101 102 if (mask & LDAP_SERVICE_SERVICEFLAG) { 103 if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbserviceflags", LDAP_MOD_ADD, 104 service->krbserviceflags)) != 0) 105 goto cleanup; 106 } 107 108 if (mask & LDAP_SERVICE_HOSTSERVER) { 109 if (service->krbhostservers != NULL) { 110 if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbhostserver", LDAP_MOD_ADD, 111 service->krbhostservers)) != 0) 112 goto cleanup; 113 } else { 114 st = EINVAL; 115 krb5_set_error_message (context, st, gettext("'krbhostserver' argument invalid")); 116 goto cleanup; 117 } 118 } 119 120 if (mask & LDAP_SERVICE_REALMREFERENCE) { 121 if (service->krbrealmreferences != NULL) { 122 unsigned int realmmask=0; 123 124 /* check for the validity of the values */ 125 for (j=0; service->krbrealmreferences[j] != NULL; ++j) { 126 st = checkattributevalue(ld, service->krbrealmreferences[j], "ObjectClass", 127 realmcontclass, &realmmask); 128 CHECK_CLASS_VALIDITY(st, realmmask, "realm object value: "); 129 } 130 if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbrealmreferences", LDAP_MOD_ADD, 131 service->krbrealmreferences)) != 0) 132 goto cleanup; 133 } else { 134 st = EINVAL; 135 krb5_set_error_message (context, st, gettext("Server has no 'krbrealmreferences'")); 136 goto cleanup; 137 } 138 } 139 140 /* ldap add operation */ 141 if ((st=ldap_add_ext_s(ld, service->servicedn, mods, NULL, NULL)) != LDAP_SUCCESS) { 142 st = set_ldap_error (context, st, OP_ADD); 143 goto cleanup; 144 } 145 146 /* 147 * If the service created has realm/s associated with it, then the realm should be updated 148 * to have a reference to the service object just created. 149 */ 150 if (mask & LDAP_SERVICE_REALMREFERENCE) { 151 for (i=0; service->krbrealmreferences[i]; ++i) { 152 if ((st=updateAttribute(ld, service->krbrealmreferences[i], realmattr, 153 service->servicedn)) != 0) { 154 snprintf (errbuf, sizeof(errbuf), gettext("Error adding 'krbRealmReferences' to %s: "), 155 service->krbrealmreferences[i]); 156 prepend_err_str (context, errbuf, st, st); 157 /* delete service object, status ignored intentionally */ 158 ldap_delete_ext_s(ld, service->servicedn, NULL, NULL); 159 goto cleanup; 160 } 161 } 162 } 163 164 cleanup: 165 166 if (rdns) 167 ldap_value_free (rdns); 168 169 ldap_mods_free(mods, 1); 170 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); 171 return st; 172 } 173 174 175 /* 176 * modify the service object from Directory 177 */ 178 179 krb5_error_code 180 krb5_ldap_modify_service(context, service, mask) 181 krb5_context context; 182 krb5_ldap_service_params *service; 183 int mask; 184 { 185 int i=0, j=0, count=0; 186 krb5_error_code st=0; 187 LDAP *ld=NULL; 188 char **values=NULL, *attr[] = { "krbRealmReferences", NULL}; 189 char *realmattr=NULL; 190 char **oldrealmrefs=NULL, **newrealmrefs=NULL; 191 LDAPMod **mods=NULL; 192 LDAPMessage *result=NULL, *ent=NULL; 193 kdb5_dal_handle *dal_handle=NULL; 194 krb5_ldap_context *ldap_context=NULL; 195 krb5_ldap_server_handle *ldap_server_handle=NULL; 196 197 /* validate the input parameter */ 198 if (service == NULL || service->servicedn == NULL) { 199 st = EINVAL; 200 krb5_set_error_message (context, st, gettext("Service DN is NULL")); 201 goto cleanup; 202 } 203 204 SETUP_CONTEXT(); 205 GET_HANDLE(); 206 207 if (mask & LDAP_SERVICE_SERVICEFLAG) { 208 if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbserviceflags", LDAP_MOD_REPLACE, 209 service->krbserviceflags)) != 0) 210 goto cleanup; 211 } 212 213 if (mask & LDAP_SERVICE_HOSTSERVER) { 214 if (service->krbhostservers != NULL) { 215 if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbhostserver", LDAP_MOD_REPLACE, 216 service->krbhostservers)) != 0) 217 goto cleanup; 218 } else { 219 st = EINVAL; 220 krb5_set_error_message (context, st, gettext("'krbhostserver' value invalid")); 221 goto cleanup; 222 } 223 } 224 225 if (mask & LDAP_SERVICE_REALMREFERENCE) { 226 if (service->krbrealmreferences != NULL) { 227 unsigned int realmmask=0; 228 229 /* check for the validity of the values */ 230 for (j=0; service->krbrealmreferences[j]; ++j) { 231 st = checkattributevalue(ld, service->krbrealmreferences[j], "ObjectClass", 232 realmcontclass, &realmmask); 233 CHECK_CLASS_VALIDITY(st, realmmask, "realm object value: "); 234 } 235 if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbrealmreferences", LDAP_MOD_REPLACE, 236 service->krbrealmreferences)) != 0) 237 goto cleanup; 238 239 240 /* get the attribute of the realm to be set */ 241 if (service->servicetype == LDAP_KDC_SERVICE) 242 realmattr = "krbKdcServers"; 243 else if (service->servicetype == LDAP_ADMIN_SERVICE) 244 realmattr = "krbAdmservers"; 245 else if (service->servicetype == LDAP_PASSWD_SERVICE) 246 realmattr = "krbPwdServers"; 247 else 248 realmattr = "krbKdcServers"; 249 250 /* read the existing list of krbRealmreferences. this will needed */ 251 if ((st = ldap_search_ext_s (ld, 252 service->servicedn, 253 LDAP_SCOPE_BASE, 254 0, 255 attr, 256 0, 257 NULL, 258 NULL, 259 NULL, 260 0, 261 &result)) != LDAP_SUCCESS) { 262 st = set_ldap_error (context, st, OP_SEARCH); 263 goto cleanup; 264 } 265 266 ent = ldap_first_entry(ld, result); 267 if (ent) { 268 if ((values=ldap_get_values(ld, ent, "krbRealmReferences")) != NULL) { 269 count = ldap_count_values(values); 270 if ((st=copy_arrays(values, &oldrealmrefs, count)) != 0) 271 goto cleanup; 272 ldap_value_free(values); 273 } 274 } 275 ldap_msgfree(result); 276 } else { 277 st = EINVAL; 278 krb5_set_error_message (context, st, gettext("'krbRealmReferences' value invalid")); 279 goto cleanup; 280 } 281 } 282 283 /* ldap modify operation */ 284 if ((st=ldap_modify_ext_s(ld, service->servicedn, mods, NULL, NULL)) != LDAP_SUCCESS) { 285 st = set_ldap_error (context, st, OP_MOD); 286 goto cleanup; 287 } 288 289 /* 290 * If the service modified had realm/s associations changed, then the realm should be 291 * updated to reflect the changes. 292 */ 293 294 if (mask & LDAP_SERVICE_REALMREFERENCE) { 295 /* get the count of the new list of krbrealmreferences */ 296 for (i=0; service->krbrealmreferences[i]; ++i) 297 ; 298 299 /* make a new copy of the krbrealmreferences */ 300 if ((st=copy_arrays(service->krbrealmreferences, &newrealmrefs, i)) != 0) 301 goto cleanup; 302 303 /* find the deletions/additions to the list of krbrealmreferences */ 304 if (disjoint_members(oldrealmrefs, newrealmrefs) != 0) 305 goto cleanup; 306 307 /* see if some of the attributes have to be deleted */ 308 if (oldrealmrefs) { 309 310 /* update the dn represented by the attribute that is to be deleted */ 311 for (i=0; oldrealmrefs[i]; ++i) 312 if ((st=deleteAttribute(ld, oldrealmrefs[i], realmattr, service->servicedn)) != 0) { 313 prepend_err_str (context, gettext("Error deleting realm attribute:"), st, st); 314 goto cleanup; 315 } 316 } 317 318 /* see if some of the attributes have to be added */ 319 for (i=0; newrealmrefs[i]; ++i) 320 if ((st=updateAttribute(ld, newrealmrefs[i], realmattr, service->servicedn)) != 0) { 321 prepend_err_str (context, gettext("Error updating realm attribute: "), st, st); 322 goto cleanup; 323 } 324 } 325 326 cleanup: 327 328 if (oldrealmrefs) { 329 for (i=0; oldrealmrefs[i]; ++i) 330 free (oldrealmrefs[i]); 331 free (oldrealmrefs); 332 } 333 334 if (newrealmrefs) { 335 for (i=0; newrealmrefs[i]; ++i) 336 free (newrealmrefs[i]); 337 free (newrealmrefs); 338 } 339 340 ldap_mods_free(mods, 1); 341 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); 342 return st; 343 } 344 345 346 krb5_error_code 347 krb5_ldap_delete_service(context, service, servicedn) 348 krb5_context context; 349 krb5_ldap_service_params *service; 350 char *servicedn; 351 { 352 krb5_error_code st = 0; 353 LDAP *ld=NULL; 354 kdb5_dal_handle *dal_handle=NULL; 355 krb5_ldap_context *ldap_context=NULL; 356 krb5_ldap_server_handle *ldap_server_handle=NULL; 357 358 SETUP_CONTEXT(); 359 GET_HANDLE(); 360 361 st = ldap_delete_ext_s(ld, servicedn, NULL, NULL); 362 if (st != 0) { 363 st = set_ldap_error (context, st, OP_DEL); 364 } 365 366 /* NOTE: This should be removed now as the backlinks are going off in OpenLDAP */ 367 /* time to delete krbrealmreferences. This is only for OpenLDAP */ 368 #ifndef HAVE_EDIRECTORY 369 { 370 int i=0; 371 char *attr=NULL; 372 373 if (service) { 374 if (service->krbrealmreferences) { 375 if (service->servicetype == LDAP_KDC_SERVICE) 376 attr = "krbkdcservers"; 377 else if (service->servicetype == LDAP_ADMIN_SERVICE) 378 attr = "krbadmservers"; 379 else if (service->servicetype == LDAP_PASSWD_SERVICE) 380 attr = "krbpwdservers"; 381 382 for (i=0; service->krbrealmreferences[i]; ++i) { 383 deleteAttribute(ld, service->krbrealmreferences[i], attr, servicedn); 384 } 385 } 386 } 387 } 388 #endif 389 390 cleanup: 391 392 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); 393 return st; 394 } 395 396 397 /* 398 * This function lists service objects from Directory 399 */ 400 401 krb5_error_code 402 krb5_ldap_list_services(context, containerdn, services) 403 krb5_context context; 404 char *containerdn; 405 char ***services; 406 { 407 return (krb5_ldap_list(context, services, "krbService", containerdn)); 408 } 409 410 /* 411 * This function reads the service object from Directory 412 */ 413 krb5_error_code 414 krb5_ldap_read_service(context, servicedn, service, omask) 415 krb5_context context; 416 char *servicedn; 417 krb5_ldap_service_params **service; 418 int *omask; 419 { 420 char **values=NULL; 421 int i=0, count=0, objectmask=0; 422 krb5_error_code st=0, tempst=0; 423 LDAPMessage *result=NULL,*ent=NULL; 424 char *attributes[] = {"krbHostServer", "krbServiceflags", 425 "krbRealmReferences", "objectclass", NULL}; 426 char *attrvalues[] = {"krbService", NULL}; 427 krb5_ldap_service_params *lservice=NULL; 428 krb5_ldap_context *ldap_context=NULL; 429 kdb5_dal_handle *dal_handle=NULL; 430 krb5_ldap_server_handle *ldap_server_handle=NULL; 431 LDAP *ld = NULL; 432 433 /* validate the input parameter */ 434 if (servicedn == NULL) { 435 st = EINVAL; 436 krb5_set_error_message (context, st, gettext("Service DN NULL")); 437 goto cleanup; 438 } 439 440 SETUP_CONTEXT(); 441 GET_HANDLE(); 442 443 *omask = 0; 444 445 /* the policydn object should be of the krbService object class */ 446 st = checkattributevalue(ld, servicedn, "objectClass", attrvalues, &objectmask); 447 CHECK_CLASS_VALIDITY(st, objectmask, "service object value: "); 448 449 /* Initialize service structure */ 450 lservice =(krb5_ldap_service_params *) calloc(1, sizeof(krb5_ldap_service_params)); 451 if (lservice == NULL) { 452 st = ENOMEM; 453 goto cleanup; 454 } 455 456 /* allocate tl_data structure to store MASK information */ 457 lservice->tl_data = calloc (1, sizeof(*lservice->tl_data)); 458 if (lservice->tl_data == NULL) { 459 st = ENOMEM; 460 goto cleanup; 461 } 462 lservice->tl_data->tl_data_type = KDB_TL_USER_INFO; 463 464 LDAP_SEARCH(servicedn, LDAP_SCOPE_BASE, "(objectclass=krbService)", attributes); 465 466 lservice->servicedn = strdup(servicedn); 467 CHECK_NULL(lservice->servicedn); 468 469 ent=ldap_first_entry(ld, result); 470 if (ent != NULL) { 471 472 if ((values=ldap_get_values(ld, ent, "krbServiceFlags")) != NULL) { 473 lservice->krbserviceflags = atoi(values[0]); 474 *omask |= LDAP_SERVICE_SERVICEFLAG; 475 ldap_value_free(values); 476 } 477 478 if ((values=ldap_get_values(ld, ent, "krbHostServer")) != NULL) { 479 count = ldap_count_values(values); 480 if ((st=copy_arrays(values, &(lservice->krbhostservers), count)) != 0) 481 goto cleanup; 482 *omask |= LDAP_SERVICE_HOSTSERVER; 483 ldap_value_free(values); 484 } 485 486 if ((values=ldap_get_values(ld, ent, "krbRealmReferences")) != NULL) { 487 count = ldap_count_values(values); 488 if ((st=copy_arrays(values, &(lservice->krbrealmreferences), count)) != 0) 489 goto cleanup; 490 *omask |= LDAP_SERVICE_REALMREFERENCE; 491 ldap_value_free(values); 492 } 493 494 if ((values=ldap_get_values(ld, ent, "objectClass")) != NULL) { 495 for (i=0; values[i]; ++i) { 496 if (strcasecmp(values[i], "krbKdcService") == 0) { 497 lservice->servicetype = LDAP_KDC_SERVICE; 498 break; 499 } 500 501 if (strcasecmp(values[i], "krbAdmService") == 0) { 502 lservice->servicetype = LDAP_ADMIN_SERVICE; 503 break; 504 } 505 506 if (strcasecmp(values[i], "krbPwdService") == 0) { 507 lservice->servicetype = LDAP_PASSWD_SERVICE; 508 break; 509 } 510 } 511 ldap_value_free(values); 512 } 513 } 514 ldap_msgfree(result); 515 516 cleanup: 517 if (st != 0) { 518 krb5_ldap_free_service(context, lservice); 519 *service = NULL; 520 } else { 521 store_tl_data(lservice->tl_data, KDB_TL_MASK, omask); 522 *service = lservice; 523 } 524 525 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); 526 return st; 527 } 528 529 /* 530 * This function frees the krb5_ldap_service_params structure members. 531 */ 532 533 krb5_error_code 534 krb5_ldap_free_service(context, service) 535 krb5_context context; 536 krb5_ldap_service_params *service; 537 { 538 int i=0; 539 540 if (service == NULL) 541 return 0; 542 543 if (service->servicedn) 544 free (service->servicedn); 545 546 if (service->krbrealmreferences) { 547 for (i=0; service->krbrealmreferences[i]; ++i) 548 free (service->krbrealmreferences[i]); 549 free (service->krbrealmreferences); 550 } 551 552 if (service->krbhostservers) { 553 for (i=0; service->krbhostservers[i]; ++i) 554 free (service->krbhostservers[i]); 555 free (service->krbhostservers); 556 } 557 558 if (service->tl_data) { 559 if (service->tl_data->tl_data_contents) 560 free (service->tl_data->tl_data_contents); 561 free (service->tl_data); 562 } 563 564 free (service); 565 return 0; 566 } 567 568 krb5_error_code 569 krb5_ldap_set_service_passwd(context, service, passwd) 570 krb5_context context; 571 char *service; 572 char *passwd; 573 { 574 krb5_error_code st=0; 575 LDAPMod **mods=NULL; 576 char *password[2] = {NULL}; 577 LDAP *ld=NULL; 578 krb5_ldap_context *ldap_context=NULL; 579 kdb5_dal_handle *dal_handle=NULL; 580 krb5_ldap_server_handle *ldap_server_handle=NULL; 581 582 password[0] = passwd; 583 584 SETUP_CONTEXT(); 585 GET_HANDLE(); 586 587 if ((st=krb5_add_str_mem_ldap_mod(&mods, "userPassword", LDAP_MOD_REPLACE, password)) != 0) 588 goto cleanup; 589 590 st = ldap_modify_ext_s(ld, service, mods, NULL, NULL); 591 if (st) { 592 st = set_ldap_error (context, st, OP_MOD); 593 } 594 595 cleanup: 596 ldap_mods_free(mods, 1); 597 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); 598 return st; 599 } 600 #endif 601