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
krb5_ldap_create_password_policy(context,policy)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
krb5_ldap_put_password_policy(context,policy)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
populate_policy(krb5_context context,LDAP * ld,LDAPMessage * ent,char * pol_name,osa_policy_ent_t pol_entry)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
krb5_ldap_get_password_policy_from_dn(krb5_context context,char * pol_name,char * pol_dn,osa_policy_ent_t * policy,int * cnt)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
krb5_ldap_get_password_policy(context,name,policy,cnt)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
krb5_ldap_delete_password_policy(context,policy)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
krb5_ldap_iterate_password_policy(context,match_expr,func,func_arg)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
krb5_ldap_free_password_policy(context,entry)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