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