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