1 #pragma ident "%Z%%M% %I% %E% SMI" 2 3 /* 4 * lib/kdb/kdb_ldap/ldap_pwd_policy.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_pwd_policy.h" 40 #include "ldap_err.h" 41 #include <libintl.h> 42 43 static char *password_policy_attributes[] = { "cn", "krbmaxpwdlife", "krbminpwdlife", 44 "krbpwdmindiffchars", "krbpwdminlength", 45 "krbpwdhistorylength", NULL }; 46 47 /* 48 * Function to create password policy object. 49 */ 50 51 krb5_error_code 52 krb5_ldap_create_password_policy (context, policy) 53 krb5_context context; 54 osa_policy_ent_t policy; 55 { 56 krb5_error_code st=0; 57 LDAP *ld=NULL; 58 LDAPMod **mods={NULL}; 59 kdb5_dal_handle *dal_handle=NULL; 60 krb5_ldap_context *ldap_context=NULL; 61 krb5_ldap_server_handle *ldap_server_handle=NULL; 62 char **rdns=NULL, *strval[2]={NULL}, *policy_dn; 63 64 /* Clear the global error string */ 65 krb5_clear_error_message(context); 66 67 /* validate the input parameters */ 68 if (policy == NULL || policy->name == NULL) 69 return EINVAL; 70 71 SETUP_CONTEXT(); 72 GET_HANDLE(); 73 74 st = krb5_ldap_name_to_policydn (context, policy->name, &policy_dn); 75 if (st != 0) 76 goto cleanup; 77 78 /* get the first component of the dn to set the cn attribute */ 79 rdns = ldap_explode_dn(policy_dn, 1); 80 if (rdns == NULL) { 81 st = EINVAL; 82 krb5_set_error_message(context, st, gettext("Invalid password policy DN syntax")); 83 goto cleanup; 84 } 85 86 strval[0] = rdns[0]; 87 if ((st=krb5_add_str_mem_ldap_mod(&mods, "cn", LDAP_MOD_ADD, strval)) != 0) 88 goto cleanup; 89 90 strval[0] = "krbPwdPolicy"; 91 if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0) 92 goto cleanup; 93 94 if (((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxpwdlife", LDAP_MOD_ADD, 95 (signed) policy->pw_max_life)) != 0) 96 || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbminpwdlife", LDAP_MOD_ADD, 97 (signed) policy->pw_min_life)) != 0) 98 || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdmindiffchars", LDAP_MOD_ADD, 99 (signed) policy->pw_min_classes)) != 0) 100 || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdminlength", LDAP_MOD_ADD, 101 (signed) policy->pw_min_length)) != 0) 102 || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdhistorylength", LDAP_MOD_ADD, 103 (signed) policy->pw_history_num)) != 0)) 104 goto cleanup; 105 106 /* password policy object creation */ 107 if ((st=ldap_add_ext_s(ld, policy_dn, mods, NULL, NULL)) != LDAP_SUCCESS) { 108 st = set_ldap_error (context, st, OP_ADD); 109 goto cleanup; 110 } 111 112 cleanup: 113 if (rdns) 114 ldap_value_free(rdns); 115 116 if (policy_dn != NULL) 117 free (policy_dn); 118 ldap_mods_free(mods, 1); 119 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); 120 return(st); 121 } 122 123 /* 124 * Function to modify password policy object. 125 */ 126 127 krb5_error_code 128 krb5_ldap_put_password_policy (context, policy) 129 krb5_context context; 130 osa_policy_ent_t policy; 131 { 132 char *policy_dn; 133 krb5_error_code st=0; 134 LDAP *ld=NULL; 135 LDAPMod **mods=NULL; 136 kdb5_dal_handle *dal_handle=NULL; 137 krb5_ldap_context *ldap_context=NULL; 138 krb5_ldap_server_handle *ldap_server_handle=NULL; 139 140 /* Clear the global error string */ 141 krb5_clear_error_message(context); 142 143 /* validate the input parameters */ 144 if (policy == NULL || policy->name == NULL) 145 return EINVAL; 146 147 SETUP_CONTEXT(); 148 GET_HANDLE(); 149 150 st = krb5_ldap_name_to_policydn (context, policy->name, &policy_dn); 151 if (st != 0) 152 goto cleanup; 153 154 if (((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxpwdlife", LDAP_MOD_REPLACE, 155 (signed) policy->pw_max_life)) != 0) 156 || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbminpwdlife", LDAP_MOD_REPLACE, 157 (signed) policy->pw_min_life)) != 0) 158 || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdmindiffchars", LDAP_MOD_REPLACE, 159 (signed) policy->pw_min_classes)) != 0) 160 || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdminlength", LDAP_MOD_REPLACE, 161 (signed) policy->pw_min_length)) != 0) 162 || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdhistorylength", LDAP_MOD_REPLACE, 163 (signed) policy->pw_history_num)) != 0)) 164 goto cleanup; 165 166 /* modify the password policy object. */ 167 /* 168 * This will fail if the 'policy_dn' is anywhere other than under the realm 169 * container. This is correct behaviour. 'kdb5_ldap_util' will support 170 * management of only such policy objects. 171 */ 172 if ((st=ldap_modify_ext_s(ld, policy_dn, mods, NULL, NULL)) != LDAP_SUCCESS) { 173 st = set_ldap_error (context, st, OP_MOD); 174 goto cleanup; 175 } 176 177 cleanup: 178 if (policy_dn != NULL) 179 free (policy_dn); 180 ldap_mods_free(mods, 1); 181 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); 182 return(st); 183 } 184 185 krb5_error_code 186 populate_policy(krb5_context context, 187 LDAP *ld, 188 LDAPMessage *ent, 189 char *pol_name, 190 osa_policy_ent_t pol_entry) 191 { 192 int st = 0; 193 char *pol_dn; 194 195 pol_entry->name = strdup(pol_name); 196 CHECK_NULL(pol_entry->name); 197 pol_entry->version = 1; 198 199 krb5_ldap_get_value(ld, ent, "krbmaxpwdlife", (int *)&(pol_entry->pw_max_life)); 200 krb5_ldap_get_value(ld, ent, "krbminpwdlife", (int *)&(pol_entry->pw_min_life)); 201 krb5_ldap_get_value(ld, ent, "krbpwdmindiffchars", (int *)&(pol_entry->pw_min_classes)); 202 krb5_ldap_get_value(ld, ent, "krbpwdminlength", (int *)&(pol_entry->pw_min_length)); 203 krb5_ldap_get_value(ld, ent, "krbpwdhistorylength", (int *)&(pol_entry->pw_history_num)); 204 205 /* Get the reference count */ 206 pol_dn = ldap_get_dn(ld, ent); 207 st = krb5_ldap_get_reference_count (context, pol_dn, "krbPwdPolicyReference", 208 (int *)&(pol_entry->policy_refcnt), ld); 209 ldap_memfree(pol_dn); 210 211 cleanup: 212 /* Solaris Kerberos: trying to avoid memory leaks */ 213 if (st != 0) { 214 free(pol_entry->name); 215 pol_entry->name = NULL; 216 } 217 return st; 218 } 219 220 krb5_error_code 221 krb5_ldap_get_password_policy_from_dn (krb5_context context, 222 char *pol_name, 223 char *pol_dn, 224 osa_policy_ent_t *policy, 225 int *cnt) 226 { 227 krb5_error_code st=0, tempst=0; 228 LDAP *ld=NULL; 229 LDAPMessage *result=NULL,*ent=NULL; 230 kdb5_dal_handle *dal_handle=NULL; 231 krb5_ldap_context *ldap_context=NULL; 232 krb5_ldap_server_handle *ldap_server_handle=NULL; 233 234 /* Clear the global error string */ 235 krb5_clear_error_message(context); 236 237 /* validate the input parameters */ 238 if (pol_dn == NULL) 239 return EINVAL; 240 241 *policy = NULL; 242 SETUP_CONTEXT(); 243 GET_HANDLE(); 244 245 *cnt = 0; 246 *(policy) = (osa_policy_ent_t) malloc(sizeof(osa_policy_ent_rec)); 247 if (*policy == NULL) { 248 st = ENOMEM; 249 goto cleanup; 250 } 251 memset(*policy, 0, sizeof(osa_policy_ent_rec)); 252 253 LDAP_SEARCH(pol_dn, LDAP_SCOPE_BASE, "(objectclass=krbPwdPolicy)", password_policy_attributes); 254 *cnt = 1; 255 #if 0 /************** Begin IFDEF'ed OUT *******************************/ 256 (*policy)->name = strdup(name); 257 CHECK_NULL((*policy)->name); 258 (*policy)->version = 1; 259 #endif /**************** END IFDEF'ed OUT *******************************/ 260 261 ent=ldap_first_entry(ld, result); 262 if (ent != NULL) { 263 if ((st = populate_policy(context, ld, ent, pol_name, *policy)) != 0) 264 goto cleanup; 265 #if 0 /************** Begin IFDEF'ed OUT *******************************/ 266 krb5_ldap_get_value(ld, ent, "krbmaxpwdlife", &((*policy)->pw_max_life)); 267 krb5_ldap_get_value(ld, ent, "krbminpwdlife", &((*policy)->pw_min_life)); 268 krb5_ldap_get_value(ld, ent, "krbpwdmindiffchars", &((*policy)->pw_min_classes)); 269 krb5_ldap_get_value(ld, ent, "krbpwdminlength", &((*policy)->pw_min_length)); 270 krb5_ldap_get_value(ld, ent, "krbpwdhistorylength", &((*policy)->pw_history_num)); 271 272 /* Get the reference count */ 273 st = krb5_ldap_get_reference_count (context, 274 name, 275 "krbPwdPolicyReference", 276 &(*policy)->policy_refcnt, 277 ld); 278 #endif /**************** END IFDEF'ed OUT *******************************/ 279 } 280 281 cleanup: 282 ldap_msgfree(result); 283 if (st != 0) { 284 if (*policy != NULL) { 285 krb5_ldap_free_password_policy(context, *policy); 286 *policy = NULL; 287 } 288 } 289 290 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); 291 return st; 292 } 293 294 /* 295 * Convert 'name' into a directory DN and call 296 * 'krb5_ldap_get_password_policy_from_dn' 297 */ 298 krb5_error_code 299 krb5_ldap_get_password_policy (context, name, policy, cnt) 300 krb5_context context; 301 char *name; 302 osa_policy_ent_t *policy; 303 int *cnt; 304 { 305 krb5_error_code st = 0; 306 char *policy_dn = NULL; 307 308 /* Clear the global error string */ 309 krb5_clear_error_message(context); 310 311 /* validate the input parameters */ 312 if (name == NULL) { 313 st = EINVAL; 314 goto cleanup; 315 } 316 317 st = krb5_ldap_name_to_policydn(context, name, &policy_dn); 318 if (st != 0) 319 goto cleanup; 320 321 st = krb5_ldap_get_password_policy_from_dn(context, name, policy_dn, policy, cnt); 322 323 cleanup: 324 if (policy_dn != NULL) 325 free (policy_dn); 326 return st; 327 } 328 329 krb5_error_code 330 krb5_ldap_delete_password_policy (context, policy) 331 krb5_context context; 332 char *policy; 333 { 334 int mask = 0; 335 char *policy_dn = NULL, *class[] = {"krbpwdpolicy", NULL}; 336 krb5_error_code st=0; 337 LDAP *ld=NULL; 338 kdb5_dal_handle *dal_handle=NULL; 339 krb5_ldap_context *ldap_context=NULL; 340 krb5_ldap_server_handle *ldap_server_handle=NULL; 341 342 /* Clear the global error string */ 343 krb5_clear_error_message(context); 344 345 /* validate the input parameters */ 346 if (policy == NULL) 347 return EINVAL; 348 349 SETUP_CONTEXT(); 350 GET_HANDLE(); 351 352 st = krb5_ldap_name_to_policydn (context, policy, &policy_dn); 353 if (st != 0) 354 goto cleanup; 355 356 /* Ensure that the object is a password policy */ 357 if ((st=checkattributevalue(ld, policy_dn, "objectclass", class, &mask)) != 0) 358 goto cleanup; 359 360 if (mask == 0) { 361 st = KRB5_KDB_NOENTRY; 362 goto cleanup; 363 } 364 365 if ((st=ldap_delete_ext_s(ld, policy_dn, NULL, NULL)) != LDAP_SUCCESS) { 366 st = set_ldap_error (context, st, OP_DEL); 367 goto cleanup; 368 } 369 370 cleanup: 371 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); 372 if (policy_dn != NULL) 373 free (policy_dn); 374 375 return st; 376 } 377 378 krb5_error_code 379 krb5_ldap_iterate_password_policy(context, match_expr, func, func_arg) 380 krb5_context context; 381 char *match_expr; 382 void (*func) (krb5_pointer, osa_policy_ent_t); 383 krb5_pointer func_arg; 384 { 385 osa_policy_ent_rec *entry=NULL; 386 char *policy=NULL; 387 krb5_error_code st=0, tempst=0; 388 LDAP *ld=NULL; 389 LDAPMessage *result=NULL, *ent=NULL; 390 kdb5_dal_handle *dal_handle=NULL; 391 krb5_ldap_context *ldap_context=NULL; 392 krb5_ldap_server_handle *ldap_server_handle=NULL; 393 394 /* Clear the global error string */ 395 krb5_clear_error_message(context); 396 397 SETUP_CONTEXT(); 398 GET_HANDLE(); 399 400 if (ldap_context->lrparams->realmdn == NULL) { 401 st = EINVAL; 402 goto cleanup; 403 } 404 405 LDAP_SEARCH(ldap_context->lrparams->realmdn, LDAP_SCOPE_ONELEVEL, "(objectclass=krbpwdpolicy)", password_policy_attributes); 406 for (ent=ldap_first_entry(ld, result); ent != NULL; ent=ldap_next_entry(ld, ent)) { 407 krb5_boolean attr_present; 408 409 st = krb5_ldap_get_string(ld, ent, "cn", &policy, &attr_present); 410 if (st != 0) 411 goto cleanup; 412 if (attr_present == FALSE) 413 continue; 414 415 entry = (osa_policy_ent_t) malloc(sizeof(osa_policy_ent_rec)); 416 CHECK_NULL(entry); 417 memset(entry, 0, sizeof(osa_policy_ent_rec)); 418 if ((st = populate_policy(context, ld, ent, policy, entry)) != 0) 419 goto cleanup; 420 #if 0 /************** Begin IFDEF'ed OUT *******************************/ 421 entry->name = policy; 422 entry->version = 1; 423 424 krb5_ldap_get_value(ld, ent, "krbmaxpwdlife", &(entry->pw_max_life)); 425 krb5_ldap_get_value(ld, ent, "krbminpwdlife", &(entry->pw_min_life)); 426 krb5_ldap_get_value(ld, ent, "krbpwdmindiffchars", &(entry->pw_min_classes)); 427 krb5_ldap_get_value(ld, ent, "krbpwdminlength", &(entry->pw_min_length)); 428 krb5_ldap_get_value(ld, ent, "krbpwdhistorylength", &(entry->pw_history_num)); 429 430 /* Get the reference count */ 431 st = krb5_ldap_get_reference_count (context, 432 policy, 433 "krbPwdPolicyReference", 434 &(entry->policy_refcnt), 435 ld); 436 #endif /**************** END IFDEF'ed OUT *******************************/ 437 438 (*func)(func_arg, entry); 439 /* XXX this will free policy so don't free it */ 440 krb5_ldap_free_password_policy(context, entry); 441 entry = NULL; 442 } 443 ldap_msgfree(result); 444 445 cleanup: 446 if (entry) 447 free (entry); 448 449 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); 450 return st; 451 } 452 453 void 454 krb5_ldap_free_password_policy (context, entry) 455 krb5_context context; 456 osa_policy_ent_t entry; 457 { 458 if (entry) { 459 if (entry->name) 460 free(entry->name); 461 free(entry); 462 } 463 return; 464 } 465