1 #pragma ident "%Z%%M% %I% %E% SMI" 2 3 /* 4 * lib/kdb/kdb_ldap/ldap_principal.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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 34 * Use is subject to license terms. 35 */ 36 37 #include "ldap_main.h" 38 #include "kdb_ldap.h" 39 #include "ldap_principal.h" 40 #include "princ_xdr.h" 41 #include "ldap_err.h" 42 #include <libintl.h> 43 44 struct timeval timelimit = {300, 0}; /* 5 minutes */ 45 char *principal_attributes[] = { "krbprincipalname", 46 "objectclass", 47 "krbprincipalkey", 48 "krbmaxrenewableage", 49 "krbmaxticketlife", 50 "krbticketflags", 51 "krbprincipalexpiration", 52 "krbticketpolicyreference", 53 "krbUpEnabled", 54 "krbpwdpolicyreference", 55 "krbpasswordexpiration", 56 "krbLastFailedAuth", 57 "krbLoginFailedCount", 58 "krbLastSuccessfulAuth", 59 #ifdef HAVE_EDIRECTORY 60 "loginexpirationtime", 61 "logindisabled", 62 #endif 63 "loginexpirationtime", 64 "logindisabled", 65 "modifytimestamp", 66 "krbLastPwdChange", 67 "krbExtraData", 68 "krbObjectReferences", 69 NULL }; 70 71 /* Must match KDB_*_ATTR macros in ldap_principal.h. */ 72 static char *attributes_set[] = { "krbmaxticketlife", 73 "krbmaxrenewableage", 74 "krbticketflags", 75 "krbprincipalexpiration", 76 "krbticketpolicyreference", 77 "krbUpEnabled", 78 "krbpwdpolicyreference", 79 "krbpasswordexpiration", 80 "krbprincipalkey", 81 "krblastpwdchange", 82 "krbextradata", 83 "krbLastSuccessfulAuth", 84 "krbLastFailedAuth", 85 "krbLoginFailedCount", 86 NULL }; 87 88 void 89 krb5_dbe_free_contents(context, entry) 90 krb5_context context; 91 krb5_db_entry *entry; 92 { 93 krb5_tl_data *tl_data_next=NULL; 94 krb5_tl_data *tl_data=NULL; 95 int i, j; 96 97 if (entry->e_data) 98 free(entry->e_data); 99 if (entry->princ) 100 krb5_free_principal(context, entry->princ); 101 for (tl_data = entry->tl_data; tl_data; tl_data = tl_data_next) { 102 tl_data_next = tl_data->tl_data_next; 103 if (tl_data->tl_data_contents) 104 free(tl_data->tl_data_contents); 105 free(tl_data); 106 } 107 if (entry->key_data) { 108 for (i = 0; i < entry->n_key_data; i++) { 109 for (j = 0; j < entry->key_data[i].key_data_ver; j++) { 110 if (entry->key_data[i].key_data_length[j]) { 111 if (entry->key_data[i].key_data_contents[j]) { 112 memset(entry->key_data[i].key_data_contents[j], 113 0, 114 (unsigned) entry->key_data[i].key_data_length[j]); 115 free (entry->key_data[i].key_data_contents[j]); 116 } 117 } 118 entry->key_data[i].key_data_contents[j] = NULL; 119 entry->key_data[i].key_data_length[j] = 0; 120 entry->key_data[i].key_data_type[j] = 0; 121 } 122 } 123 free(entry->key_data); 124 } 125 memset(entry, 0, sizeof(*entry)); 126 return; 127 } 128 129 130 krb5_error_code 131 krb5_ldap_free_principal(kcontext , entries, nentries) 132 krb5_context kcontext; 133 krb5_db_entry *entries; 134 int nentries; 135 { 136 register int i; 137 for (i = 0; i < nentries; i++) 138 krb5_dbe_free_contents(kcontext, &entries[i]); 139 return 0; 140 } 141 142 krb5_error_code 143 krb5_ldap_iterate( 144 krb5_context context, 145 char *match_expr, 146 krb5_error_code (*func) (krb5_pointer, krb5_db_entry *), 147 krb5_pointer func_arg, 148 /* Solaris Kerberos: adding support for -rev/recurse flags */ 149 char **db_args) 150 { 151 krb5_db_entry entry; 152 krb5_principal principal; 153 char **subtree=NULL, *princ_name=NULL, *realm=NULL, **values=NULL, *filter=NULL; 154 unsigned int filterlen=0, tree=0, ntree=1, i=0; 155 krb5_error_code st=0, tempst=0; 156 LDAP *ld=NULL; 157 LDAPMessage *result=NULL, *ent=NULL; 158 kdb5_dal_handle *dal_handle=NULL; 159 krb5_ldap_context *ldap_context=NULL; 160 krb5_ldap_server_handle *ldap_server_handle=NULL; 161 char *default_match_expr = "*"; 162 163 /* Clear the global error string */ 164 krb5_clear_error_message(context); 165 166 /* Solaris Kerberos: adding support for -rev/recurse flags */ 167 if (db_args) { 168 /* LDAP does not support db_args DB arguments for krb5_ldap_iterate */ 169 krb5_set_error_message(context, EINVAL, 170 gettext("Unsupported argument \"%s\" for ldap"), 171 db_args[0]); 172 return EINVAL; 173 } 174 175 memset(&entry, 0, sizeof(krb5_db_entry)); 176 SETUP_CONTEXT(); 177 178 realm = ldap_context->lrparams->realm_name; 179 if (realm == NULL) { 180 realm = context->default_realm; 181 if (realm == NULL) { 182 st = EINVAL; 183 krb5_set_error_message(context, st, gettext("Default realm not set")); 184 goto cleanup; 185 } 186 } 187 188 /* 189 * If no match_expr then iterate through all krb princs like the db2 plugin 190 */ 191 if (match_expr == NULL) 192 match_expr = default_match_expr; 193 194 filterlen = strlen(FILTER) + strlen(match_expr) + 2 + 1; /* 2 for closing brackets */ 195 filter = malloc (filterlen); 196 CHECK_NULL(filter); 197 memset(filter, 0, filterlen); 198 /*LINTED*/ 199 sprintf(filter, FILTER"%s))", match_expr); 200 201 if ((st = krb5_get_subtree_info(ldap_context, &subtree, &ntree)) != 0) 202 goto cleanup; 203 204 GET_HANDLE(); 205 206 for (tree=0; tree < ntree; ++tree) { 207 208 LDAP_SEARCH(subtree[tree], ldap_context->lrparams->search_scope, filter, principal_attributes); 209 for (ent=ldap_first_entry(ld, result); ent != NULL; ent=ldap_next_entry(ld, ent)) { 210 if ((values=ldap_get_values(ld, ent, "krbprincipalname")) != NULL) { 211 for (i=0; values[i] != NULL; ++i) { 212 if (values[i]) 213 if (krb5_ldap_parse_principal_name(values[i], &princ_name) != 0) 214 continue; 215 if (krb5_parse_name(context, princ_name, &principal) != 0) 216 continue; 217 if (is_principal_in_realm(ldap_context, principal) == 0) { 218 if ((st = populate_krb5_db_entry(context, ldap_context, ld, ent, principal, 219 &entry)) != 0) 220 goto cleanup; 221 (*func)(func_arg, &entry); 222 krb5_dbe_free_contents(context, &entry); 223 (void) krb5_free_principal(context, principal); 224 if (princ_name) 225 free(princ_name); 226 break; 227 } 228 (void) krb5_free_principal(context, principal); 229 if (princ_name) 230 free(princ_name); 231 } 232 ldap_value_free(values); 233 } 234 } /* end of for (ent= ... */ 235 ldap_msgfree(result); 236 } /* end of for (tree= ... */ 237 238 cleanup: 239 if (filter) 240 free (filter); 241 242 for (;ntree; --ntree) 243 if (subtree[ntree-1]) 244 free (subtree[ntree-1]); 245 246 /* Solaris Kerberos: fix memory leak */ 247 if (subtree != NULL) { 248 free(subtree); 249 } 250 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); 251 return st; 252 } 253 254 255 /* 256 * delete a principal from the directory. 257 */ 258 krb5_error_code 259 krb5_ldap_delete_principal(context, searchfor, nentries) 260 krb5_context context; 261 krb5_const_principal searchfor; 262 int *nentries; /* how many found & deleted */ 263 { 264 char *user=NULL, *DN=NULL, *strval[10] = {NULL}; 265 LDAPMod **mods=NULL; 266 LDAP *ld=NULL; 267 int j=0, ptype=0, pcount=0; 268 unsigned int attrsetmask=0; 269 krb5_error_code st=0; 270 krb5_boolean singleentry=FALSE; 271 KEY *secretkey=NULL; 272 kdb5_dal_handle *dal_handle=NULL; 273 krb5_ldap_context *ldap_context=NULL; 274 krb5_ldap_server_handle *ldap_server_handle=NULL; 275 krb5_db_entry entries; 276 krb5_boolean more=0; 277 278 /* Clear the global error string */ 279 krb5_clear_error_message(context); 280 281 SETUP_CONTEXT(); 282 /* get the principal info */ 283 if ((st=krb5_ldap_get_principal(context, searchfor, &entries, nentries, &more)) != 0 || *nentries == 0) 284 goto cleanup; 285 286 if (((st=krb5_get_princ_type(context, &entries, &(ptype))) != 0) || 287 ((st=krb5_get_attributes_mask(context, &entries, &(attrsetmask))) != 0) || 288 ((st=krb5_get_princ_count(context, &entries, &(pcount))) != 0) || 289 ((st=krb5_get_userdn(context, &entries, &(DN))) != 0)) 290 goto cleanup; 291 292 if (DN == NULL) { 293 st = EINVAL; 294 krb5_set_error_message(context, st, gettext("DN information missing")); 295 goto cleanup; 296 } 297 298 GET_HANDLE(); 299 300 if (ptype == KDB_STANDALONE_PRINCIPAL_OBJECT) { 301 st = ldap_delete_ext_s(ld, DN, NULL, NULL); 302 if (st != LDAP_SUCCESS) { 303 st = set_ldap_error (context, st, OP_DEL); 304 goto cleanup; 305 } 306 } else { 307 if (((st=krb5_unparse_name(context, searchfor, &user)) != 0) 308 || ((st=krb5_ldap_unparse_principal_name(user)) != 0)) 309 goto cleanup; 310 311 memset(strval, 0, sizeof(strval)); 312 strval[0] = user; 313 if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbprincipalname", LDAP_MOD_DELETE, 314 strval)) != 0) 315 goto cleanup; 316 317 singleentry = (pcount == 1) ? TRUE: FALSE; 318 if (singleentry == FALSE) { 319 if (secretkey != NULL) { 320 if ((st=krb5_add_ber_mem_ldap_mod(&mods, "krbprincipalkey", LDAP_MOD_DELETE | LDAP_MOD_BVALUES, 321 secretkey->keys)) != 0) 322 goto cleanup; 323 } 324 } else { 325 /* 326 * If the Kerberos user principal to be deleted happens to be the last one associated 327 * with the directory user object, then it is time to delete the other kerberos 328 * specific attributes like krbmaxticketlife, i.e, unkerberize the directory user. 329 * From the attrsetmask value, identify the attributes set on the directory user 330 * object and delete them. 331 * NOTE: krbsecretkey attribute has per principal entries. There can be chances that the 332 * other principals' keys are exisiting/left-over. So delete all the values. 333 */ 334 while (attrsetmask) { 335 if (attrsetmask & 1) { 336 if ((st=krb5_add_str_mem_ldap_mod(&mods, attributes_set[j], LDAP_MOD_DELETE, 337 NULL)) != 0) 338 goto cleanup; 339 } 340 attrsetmask >>= 1; 341 ++j; 342 } 343 344 /* the same should be done with the objectclass attributes */ 345 { 346 char *attrvalues[] = {"krbticketpolicyaux", "krbprincipalaux", NULL}; 347 /* char *attrvalues[] = {"krbpwdpolicyrefaux", "krbticketpolicyaux", "krbprincipalaux", NULL}; */ 348 int p, q, r=0, amask=0; 349 350 if ((st=checkattributevalue(ld, DN, "objectclass", attrvalues, &amask)) != 0) 351 goto cleanup; 352 memset(strval, 0, sizeof(strval)); 353 for (p=1, q=0; p<=4; p<<=1, ++q) 354 if (p & amask) 355 strval[r++] = attrvalues[q]; 356 strval[r] = NULL; 357 if (r > 0) { 358 if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_DELETE, 359 strval)) != 0) 360 goto cleanup; 361 } 362 } 363 } 364 st=ldap_modify_ext_s(ld, DN, mods, NULL, NULL); 365 if (st != LDAP_SUCCESS) { 366 st = set_ldap_error(context, st, OP_MOD); 367 goto cleanup; 368 } 369 } 370 371 cleanup: 372 if (user) 373 free (user); 374 375 if (DN) 376 free (DN); 377 378 if (secretkey != NULL) { 379 int i=0; 380 while (i < secretkey->nkey) { 381 free (secretkey->keys[i]->bv_val); 382 free (secretkey->keys[i]); 383 ++i; 384 } 385 free (secretkey->keys); 386 free (secretkey); 387 } 388 389 if (st == 0) 390 krb5_ldap_free_principal(context, &entries, *nentries); 391 392 ldap_mods_free(mods, 1); 393 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); 394 return st; 395 } 396 397 398 /* 399 * Function: krb5_ldap_unparse_principal_name 400 * 401 * Purpose: Removes '\\' that comes before every occurence of '@' 402 * in the principal name component. 403 * 404 * Arguments: 405 * user_name (input/output) Principal name 406 * 407 */ 408 409 krb5_error_code 410 krb5_ldap_unparse_principal_name(char *user_name) 411 { 412 char *tmp_princ_name=NULL, *princ_name=NULL, *tmp=NULL; 413 int l=0; 414 krb5_error_code st=0; 415 416 if (strstr(user_name, "\\@")) { 417 418 tmp_princ_name = strdup(user_name); 419 if (!tmp_princ_name) { 420 st = ENOMEM; 421 goto cleanup; 422 } 423 tmp = tmp_princ_name; 424 425 princ_name = (char *) malloc (strlen(user_name)); 426 if (!princ_name) { 427 st = ENOMEM; 428 goto cleanup; 429 } 430 memset(princ_name, 0, strlen(user_name)); 431 432 l = 0; 433 while (*tmp_princ_name) { 434 if ((*tmp_princ_name == '\\') && (*(tmp_princ_name+1) == '@')) { 435 tmp_princ_name += 1; 436 } else { 437 *(princ_name + l) = *tmp_princ_name++; 438 l++; 439 } 440 } 441 442 memset(user_name, 0, strlen(user_name)); 443 /*LINTED*/ 444 sprintf(user_name, "%s", princ_name); 445 } 446 447 cleanup: 448 if (tmp) { 449 free(tmp); 450 tmp = NULL; 451 } 452 453 if (princ_name) { 454 free(princ_name); 455 princ_name = NULL; 456 } 457 458 return st; 459 } 460 461 462 /* 463 * Function: krb5_ldap_parse_principal_name 464 * 465 * Purpose: Inserts '\\' before every occurence of '@' 466 * in the principal name component. 467 * 468 * Arguments: 469 * i_princ_name (input) Principal name without '\\' 470 * o_princ_name (output) Principal name with '\\' 471 * 472 * Note: The caller has to free the memory allocated for o_princ_name. 473 */ 474 475 krb5_error_code 476 krb5_ldap_parse_principal_name(i_princ_name, o_princ_name) 477 char *i_princ_name; 478 char **o_princ_name; 479 { 480 char *tmp_princ_name = NULL, *princ_name = NULL, *at_rlm_name = NULL; 481 int l = 0, m = 0, tmp_princ_name_len = 0, princ_name_len = 0, at_count = 0; 482 krb5_error_code st = 0; 483 484 at_rlm_name = strrchr(i_princ_name, '@'); 485 486 if (!at_rlm_name) { 487 *o_princ_name = strdup(i_princ_name); 488 if (!o_princ_name) { 489 st = ENOMEM; 490 goto cleanup; 491 } 492 } else { 493 tmp_princ_name_len = at_rlm_name - i_princ_name; 494 495 tmp_princ_name = (char *) malloc ((unsigned) tmp_princ_name_len + 1); 496 if (!tmp_princ_name) { 497 st = ENOMEM; 498 goto cleanup; 499 } 500 memset(tmp_princ_name, 0, (unsigned) tmp_princ_name_len + 1); 501 memcpy(tmp_princ_name, i_princ_name, (unsigned) tmp_princ_name_len); 502 503 l = 0; 504 while (tmp_princ_name[l]) { 505 if (tmp_princ_name[l++] == '@') 506 at_count++; 507 } 508 509 princ_name_len = strlen(i_princ_name) + at_count + 1; 510 princ_name = (char *) malloc ((unsigned) princ_name_len); 511 if (!princ_name) { 512 st = ENOMEM; 513 goto cleanup; 514 } 515 memset(princ_name, 0, (unsigned) princ_name_len); 516 517 l = 0; 518 m = 0; 519 while (tmp_princ_name[l]) { 520 if (tmp_princ_name[l] == '@') { 521 princ_name[m++]='\\'; 522 } 523 princ_name[m++]=tmp_princ_name[l++]; 524 } 525 /* Solaris Kerberos: using strlcat for safety */ 526 strlcat(princ_name, at_rlm_name, princ_name_len); 527 528 *o_princ_name = princ_name; 529 } 530 531 cleanup: 532 533 if (tmp_princ_name) { 534 free(tmp_princ_name); 535 tmp_princ_name = NULL; 536 } 537 538 return st; 539 } 540