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