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
43static char *password_policy_attributes[] = { "cn", "krbmaxpwdlife", "krbminpwdlife",
44					      "krbpwdmindiffchars", "krbpwdminlength",
45					      "krbpwdhistorylength", NULL };
46
47/*
48 * Function to create password policy object.
49 */
50
51krb5_error_code
52krb5_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
112cleanup:
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
127krb5_error_code
128krb5_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
177cleanup:
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
185krb5_error_code
186populate_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
211cleanup:
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
220krb5_error_code
221krb5_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
281cleanup:
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 */
298krb5_error_code
299krb5_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
323cleanup:
324    if (policy_dn != NULL)
325	free (policy_dn);
326    return st;
327}
328
329krb5_error_code
330krb5_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
370cleanup:
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
378krb5_error_code
379krb5_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
445cleanup:
446    if (entry)
447	free (entry);
448
449    krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
450    return st;
451}
452
453void
454krb5_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