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(context, match_expr, func, func_arg) 144 krb5_context context; 145 char *match_expr; 146 krb5_error_code (*func) (krb5_pointer, krb5_db_entry *); 147 krb5_pointer func_arg; 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 memset(&entry, 0, sizeof(krb5_db_entry)); 165 SETUP_CONTEXT(); 166 167 realm = ldap_context->lrparams->realm_name; 168 if (realm == NULL) { 169 realm = context->default_realm; 170 if (realm == NULL) { 171 st = EINVAL; 172 krb5_set_error_message(context, st, gettext("Default realm not set")); 173 goto cleanup; 174 } 175 } 176 177 /* 178 * If no match_expr then iterate through all krb princs like the db2 plugin 179 */ 180 if (match_expr == NULL) 181 match_expr = default_match_expr; 182 183 filterlen = strlen(FILTER) + strlen(match_expr) + 2 + 1; /* 2 for closing brackets */ 184 filter = malloc (filterlen); 185 CHECK_NULL(filter); 186 memset(filter, 0, filterlen); 187 /*LINTED*/ 188 sprintf(filter, FILTER"%s))", match_expr); 189 190 if ((st = krb5_get_subtree_info(ldap_context, &subtree, &ntree)) != 0) 191 goto cleanup; 192 193 GET_HANDLE(); 194 195 for (tree=0; tree < ntree; ++tree) { 196 197 LDAP_SEARCH(subtree[tree], ldap_context->lrparams->search_scope, filter, principal_attributes); 198 for (ent=ldap_first_entry(ld, result); ent != NULL; ent=ldap_next_entry(ld, ent)) { 199 if ((values=ldap_get_values(ld, ent, "krbprincipalname")) != NULL) { 200 for (i=0; values[i] != NULL; ++i) { 201 if (values[i]) 202 if (krb5_ldap_parse_principal_name(values[i], &princ_name) != 0) 203 continue; 204 if (krb5_parse_name(context, princ_name, &principal) != 0) 205 continue; 206 if (is_principal_in_realm(ldap_context, principal) == 0) { 207 if ((st = populate_krb5_db_entry(context, ldap_context, ld, ent, principal, 208 &entry)) != 0) 209 goto cleanup; 210 (*func)(func_arg, &entry); 211 krb5_dbe_free_contents(context, &entry); 212 (void) krb5_free_principal(context, principal); 213 if (princ_name) 214 free(princ_name); 215 break; 216 } 217 (void) krb5_free_principal(context, principal); 218 if (princ_name) 219 free(princ_name); 220 } 221 ldap_value_free(values); 222 } 223 } /* end of for (ent= ... */ 224 ldap_msgfree(result); 225 } /* end of for (tree= ... */ 226 227 cleanup: 228 if (filter) 229 free (filter); 230 231 for (;ntree; --ntree) 232 if (subtree[ntree-1]) 233 free (subtree[ntree-1]); 234 235 /* Solaris Kerberos: fix memory leak */ 236 if (subtree != NULL) { 237 free(subtree); 238 } 239 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); 240 return st; 241 } 242 243 244 /* 245 * delete a principal from the directory. 246 */ 247 krb5_error_code 248 krb5_ldap_delete_principal(context, searchfor, nentries) 249 krb5_context context; 250 krb5_const_principal searchfor; 251 int *nentries; /* how many found & deleted */ 252 { 253 char *user=NULL, *DN=NULL, *strval[10] = {NULL}; 254 LDAPMod **mods=NULL; 255 LDAP *ld=NULL; 256 int j=0, ptype=0, pcount=0; 257 unsigned int attrsetmask=0; 258 krb5_error_code st=0; 259 krb5_boolean singleentry=FALSE; 260 KEY *secretkey=NULL; 261 kdb5_dal_handle *dal_handle=NULL; 262 krb5_ldap_context *ldap_context=NULL; 263 krb5_ldap_server_handle *ldap_server_handle=NULL; 264 krb5_db_entry entries; 265 krb5_boolean more=0; 266 267 /* Clear the global error string */ 268 krb5_clear_error_message(context); 269 270 SETUP_CONTEXT(); 271 /* get the principal info */ 272 if ((st=krb5_ldap_get_principal(context, searchfor, &entries, nentries, &more)) != 0 || *nentries == 0) 273 goto cleanup; 274 275 if (((st=krb5_get_princ_type(context, &entries, &(ptype))) != 0) || 276 ((st=krb5_get_attributes_mask(context, &entries, &(attrsetmask))) != 0) || 277 ((st=krb5_get_princ_count(context, &entries, &(pcount))) != 0) || 278 ((st=krb5_get_userdn(context, &entries, &(DN))) != 0)) 279 goto cleanup; 280 281 if (DN == NULL) { 282 st = EINVAL; 283 krb5_set_error_message(context, st, gettext("DN information missing")); 284 goto cleanup; 285 } 286 287 GET_HANDLE(); 288 289 if (ptype == KDB_STANDALONE_PRINCIPAL_OBJECT) { 290 st = ldap_delete_ext_s(ld, DN, NULL, NULL); 291 if (st != LDAP_SUCCESS) { 292 st = set_ldap_error (context, st, OP_DEL); 293 goto cleanup; 294 } 295 } else { 296 if (((st=krb5_unparse_name(context, searchfor, &user)) != 0) 297 || ((st=krb5_ldap_unparse_principal_name(user)) != 0)) 298 goto cleanup; 299 300 memset(strval, 0, sizeof(strval)); 301 strval[0] = user; 302 if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbprincipalname", LDAP_MOD_DELETE, 303 strval)) != 0) 304 goto cleanup; 305 306 singleentry = (pcount == 1) ? TRUE: FALSE; 307 if (singleentry == FALSE) { 308 if (secretkey != NULL) { 309 if ((st=krb5_add_ber_mem_ldap_mod(&mods, "krbprincipalkey", LDAP_MOD_DELETE | LDAP_MOD_BVALUES, 310 secretkey->keys)) != 0) 311 goto cleanup; 312 } 313 } else { 314 /* 315 * If the Kerberos user principal to be deleted happens to be the last one associated 316 * with the directory user object, then it is time to delete the other kerberos 317 * specific attributes like krbmaxticketlife, i.e, unkerberize the directory user. 318 * From the attrsetmask value, identify the attributes set on the directory user 319 * object and delete them. 320 * NOTE: krbsecretkey attribute has per principal entries. There can be chances that the 321 * other principals' keys are exisiting/left-over. So delete all the values. 322 */ 323 while (attrsetmask) { 324 if (attrsetmask & 1) { 325 if ((st=krb5_add_str_mem_ldap_mod(&mods, attributes_set[j], LDAP_MOD_DELETE, 326 NULL)) != 0) 327 goto cleanup; 328 } 329 attrsetmask >>= 1; 330 ++j; 331 } 332 333 /* the same should be done with the objectclass attributes */ 334 { 335 char *attrvalues[] = {"krbticketpolicyaux", "krbprincipalaux", NULL}; 336 /* char *attrvalues[] = {"krbpwdpolicyrefaux", "krbticketpolicyaux", "krbprincipalaux", NULL}; */ 337 int p, q, r=0, amask=0; 338 339 if ((st=checkattributevalue(ld, DN, "objectclass", attrvalues, &amask)) != 0) 340 goto cleanup; 341 memset(strval, 0, sizeof(strval)); 342 for (p=1, q=0; p<=4; p<<=1, ++q) 343 if (p & amask) 344 strval[r++] = attrvalues[q]; 345 strval[r] = NULL; 346 if (r > 0) { 347 if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_DELETE, 348 strval)) != 0) 349 goto cleanup; 350 } 351 } 352 } 353 st=ldap_modify_ext_s(ld, DN, mods, NULL, NULL); 354 if (st != LDAP_SUCCESS) { 355 st = set_ldap_error(context, st, OP_MOD); 356 goto cleanup; 357 } 358 } 359 360 cleanup: 361 if (user) 362 free (user); 363 364 if (DN) 365 free (DN); 366 367 if (secretkey != NULL) { 368 int i=0; 369 while (i < secretkey->nkey) { 370 free (secretkey->keys[i]->bv_val); 371 free (secretkey->keys[i]); 372 ++i; 373 } 374 free (secretkey->keys); 375 free (secretkey); 376 } 377 378 if (st == 0) 379 krb5_ldap_free_principal(context, &entries, *nentries); 380 381 ldap_mods_free(mods, 1); 382 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); 383 return st; 384 } 385 386 387 /* 388 * Function: krb5_ldap_unparse_principal_name 389 * 390 * Purpose: Removes '\\' that comes before every occurence of '@' 391 * in the principal name component. 392 * 393 * Arguments: 394 * user_name (input/output) Principal name 395 * 396 */ 397 398 krb5_error_code 399 krb5_ldap_unparse_principal_name(char *user_name) 400 { 401 char *tmp_princ_name=NULL, *princ_name=NULL, *tmp=NULL; 402 int l=0; 403 krb5_error_code st=0; 404 405 if (strstr(user_name, "\\@")) { 406 407 tmp_princ_name = strdup(user_name); 408 if (!tmp_princ_name) { 409 st = ENOMEM; 410 goto cleanup; 411 } 412 tmp = tmp_princ_name; 413 414 princ_name = (char *) malloc (strlen(user_name)); 415 if (!princ_name) { 416 st = ENOMEM; 417 goto cleanup; 418 } 419 memset(princ_name, 0, strlen(user_name)); 420 421 l = 0; 422 while (*tmp_princ_name) { 423 if ((*tmp_princ_name == '\\') && (*(tmp_princ_name+1) == '@')) { 424 tmp_princ_name += 1; 425 } else { 426 *(princ_name + l) = *tmp_princ_name++; 427 l++; 428 } 429 } 430 431 memset(user_name, 0, strlen(user_name)); 432 /*LINTED*/ 433 sprintf(user_name, "%s", princ_name); 434 } 435 436 cleanup: 437 if (tmp) { 438 free(tmp); 439 tmp = NULL; 440 } 441 442 if (princ_name) { 443 free(princ_name); 444 princ_name = NULL; 445 } 446 447 return st; 448 } 449 450 451 /* 452 * Function: krb5_ldap_parse_principal_name 453 * 454 * Purpose: Inserts '\\' before every occurence of '@' 455 * in the principal name component. 456 * 457 * Arguments: 458 * i_princ_name (input) Principal name without '\\' 459 * o_princ_name (output) Principal name with '\\' 460 * 461 * Note: The caller has to free the memory allocated for o_princ_name. 462 */ 463 464 krb5_error_code 465 krb5_ldap_parse_principal_name(i_princ_name, o_princ_name) 466 char *i_princ_name; 467 char **o_princ_name; 468 { 469 char *tmp_princ_name = NULL, *princ_name = NULL, *at_rlm_name = NULL; 470 int l = 0, m = 0, tmp_princ_name_len = 0, princ_name_len = 0, at_count = 0; 471 krb5_error_code st = 0; 472 473 at_rlm_name = strrchr(i_princ_name, '@'); 474 475 if (!at_rlm_name) { 476 *o_princ_name = strdup(i_princ_name); 477 if (!o_princ_name) { 478 st = ENOMEM; 479 goto cleanup; 480 } 481 } else { 482 tmp_princ_name_len = at_rlm_name - i_princ_name; 483 484 tmp_princ_name = (char *) malloc ((unsigned) tmp_princ_name_len + 1); 485 if (!tmp_princ_name) { 486 st = ENOMEM; 487 goto cleanup; 488 } 489 memset(tmp_princ_name, 0, (unsigned) tmp_princ_name_len + 1); 490 memcpy(tmp_princ_name, i_princ_name, (unsigned) tmp_princ_name_len); 491 492 l = 0; 493 while (tmp_princ_name[l]) { 494 if (tmp_princ_name[l++] == '@') 495 at_count++; 496 } 497 498 princ_name_len = strlen(i_princ_name) + at_count + 1; 499 princ_name = (char *) malloc ((unsigned) princ_name_len); 500 if (!princ_name) { 501 st = ENOMEM; 502 goto cleanup; 503 } 504 memset(princ_name, 0, (unsigned) princ_name_len); 505 506 l = 0; 507 m = 0; 508 while (tmp_princ_name[l]) { 509 if (tmp_princ_name[l] == '@') { 510 princ_name[m++]='\\'; 511 } 512 princ_name[m++]=tmp_princ_name[l++]; 513 } 514 /* Solaris Kerberos: using strlcat for safety */ 515 strlcat(princ_name, at_rlm_name, princ_name_len); 516 517 *o_princ_name = princ_name; 518 } 519 520 cleanup: 521 522 if (tmp_princ_name) { 523 free(tmp_princ_name); 524 tmp_princ_name = NULL; 525 } 526 527 return st; 528 } 529