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