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