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