1/*
2 * lib/kdb/kdb_ldap/ldap_tkt_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#include "ldap_main.h"
32#include "kdb_ldap.h"
33#include "ldap_tkt_policy.h"
34#include "ldap_err.h"
35#include <libintl.h>
36
37/* Ticket policy object management */
38
39/*
40 * create the Ticket policy object in Directory.
41 */
42krb5_error_code
43krb5_ldap_create_policy(context, policy, mask)
44    krb5_context	        context;
45    krb5_ldap_policy_params     *policy;
46    int                         mask;
47{
48    krb5_error_code             st=0;
49    LDAP                        *ld=NULL;
50    char                        *strval[3]={NULL}, *policy_dn = NULL;
51    LDAPMod                     **mods=NULL;
52    kdb5_dal_handle             *dal_handle=NULL;
53    krb5_ldap_context           *ldap_context=NULL;
54    krb5_ldap_server_handle     *ldap_server_handle=NULL;
55
56    /* validate the input parameters */
57    if (policy == NULL || policy->policy == NULL) {
58	st = EINVAL;
59	krb5_set_error_message (context, st, gettext("Ticket Policy Name missing"));
60	goto cleanup;
61    }
62
63    SETUP_CONTEXT();
64    GET_HANDLE();
65
66    if ((st = krb5_ldap_name_to_policydn (context, policy->policy, &policy_dn)) != 0)
67	goto cleanup;
68
69    memset(strval, 0, sizeof(strval));
70    strval[0] = policy->policy;
71    if ((st=krb5_add_str_mem_ldap_mod(&mods, "cn", LDAP_MOD_ADD, strval)) != 0)
72	goto cleanup;
73
74    memset(strval, 0, sizeof(strval));
75    strval[0] = "krbTicketPolicy";
76    strval[1] = "krbTicketPolicyaux";
77    if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
78	goto cleanup;
79
80    if (mask & LDAP_POLICY_MAXTKTLIFE) {
81	if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxticketlife", LDAP_MOD_ADD,
82					  policy->maxtktlife)) != 0)
83	    goto cleanup;
84    }
85
86    if (mask & LDAP_POLICY_MAXRENEWLIFE) {
87	if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxrenewableage", LDAP_MOD_ADD,
88					  policy->maxrenewlife)) != 0)
89	    goto cleanup;
90    }
91
92    if (mask & LDAP_POLICY_TKTFLAGS) {
93	if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbticketflags", LDAP_MOD_ADD,
94					  policy->tktflags)) != 0)
95	    goto cleanup;
96    }
97
98    /* ldap add operation */
99    if ((st=ldap_add_ext_s(ld, policy_dn, mods, NULL, NULL)) != LDAP_SUCCESS) {
100	st = set_ldap_error (context, st, OP_ADD);
101	goto cleanup;
102    }
103
104cleanup:
105    if (policy_dn != NULL)
106	free(policy_dn);
107
108    ldap_mods_free(mods, 1);
109    krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
110    return st;
111}
112
113
114/*
115 * modify the Ticket policy object in Directory.
116 */
117
118krb5_error_code
119krb5_ldap_modify_policy(context, policy, mask)
120    krb5_context	        context;
121    krb5_ldap_policy_params     *policy;
122    int                         mask;
123{
124    int                         objectmask=0;
125    krb5_error_code             st=0;
126    LDAP                        *ld=NULL;
127    char                        *attrvalues[]={"krbTicketPolicy", "krbTicketPolicyAux", NULL}, *strval[2]={NULL};
128    char                        *policy_dn = NULL;
129    LDAPMod                     **mods=NULL;
130    kdb5_dal_handle             *dal_handle=NULL;
131    krb5_ldap_context           *ldap_context=NULL;
132    krb5_ldap_server_handle     *ldap_server_handle=NULL;
133
134    /* validate the input parameters */
135    if (policy == NULL || policy->policy==NULL) {
136	st = EINVAL;
137	krb5_set_error_message (context, st, gettext("Ticket Policy Name missing"));
138	goto cleanup;
139    }
140
141    SETUP_CONTEXT();
142    GET_HANDLE();
143
144    if ((st = krb5_ldap_name_to_policydn (context, policy->policy, &policy_dn)) != 0)
145	goto cleanup;
146
147    /* the policydn object should be of the krbTicketPolicy object class */
148    st = checkattributevalue(ld, policy_dn, "objectClass", attrvalues, &objectmask);
149    CHECK_CLASS_VALIDITY(st, objectmask, "ticket policy object: ");
150
151    if ((objectmask & 0x02) == 0) { /* add krbticketpolicyaux to the object class list */
152	memset(strval, 0, sizeof(strval));
153	strval[0] = "krbTicketPolicyAux";
154	if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
155	    goto cleanup;
156    }
157
158    if (mask & LDAP_POLICY_MAXTKTLIFE) {
159	if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxticketlife", LDAP_MOD_REPLACE,
160					  policy->maxtktlife)) != 0)
161	    goto cleanup;
162    }
163
164    if (mask & LDAP_POLICY_MAXRENEWLIFE) {
165	if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxrenewableage", LDAP_MOD_REPLACE,
166					  policy->maxrenewlife)) != 0)
167	    goto cleanup;
168    }
169
170    if (mask & LDAP_POLICY_TKTFLAGS) {
171	if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbticketflags", LDAP_MOD_REPLACE,
172					  policy->tktflags)) != 0)
173	    goto cleanup;
174    }
175
176    if ((st=ldap_modify_ext_s(ld, policy_dn, mods, NULL, NULL)) != LDAP_SUCCESS) {
177	st = set_ldap_error (context, st, OP_MOD);
178	goto cleanup;
179    }
180
181cleanup:
182    if (policy_dn != NULL)
183        free(policy_dn);
184
185    ldap_mods_free(mods, 1);
186    krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
187    return st;
188}
189
190
191/*
192 * Read the policy object from the Directory and populate the krb5_ldap_policy_params
193 * structure.
194 */
195
196krb5_error_code
197krb5_ldap_read_policy(context, policyname, policy, omask)
198    krb5_context	        context;
199    char                        *policyname;
200    krb5_ldap_policy_params     **policy;
201    unsigned int                *omask; /* Solaris kerberos: unsigned better for mask */
202{
203    krb5_error_code             st=0, tempst=0;
204    int                         objectmask=0;
205    LDAP                        *ld=NULL;
206    LDAPMessage                 *result=NULL,*ent=NULL;
207    char                        *attributes[] = { "krbMaxTicketLife", "krbMaxRenewableAge", "krbTicketFlags", NULL};
208    char                        *attrvalues[] = { "krbTicketPolicy", NULL}, *policy_dn = NULL;
209    krb5_ldap_policy_params     *lpolicy=NULL;
210    kdb5_dal_handle             *dal_handle=NULL;
211    krb5_ldap_context           *ldap_context=NULL;
212    krb5_ldap_server_handle     *ldap_server_handle=NULL;
213
214    /* validate the input parameters */
215    if (policyname == NULL  || policy == NULL) {
216	st = EINVAL;
217	krb5_set_error_message(context, st, gettext("Ticket Policy Object information missing"));
218	goto cleanup;
219    }
220
221    SETUP_CONTEXT();
222    GET_HANDLE();
223
224    if ((st = krb5_ldap_name_to_policydn (context, policyname, &policy_dn)) != 0)
225	goto cleanup;
226
227    /* the policydn object should be of the krbTicketPolicy object class */
228    st = checkattributevalue(ld, policy_dn, "objectClass", attrvalues, &objectmask);
229    CHECK_CLASS_VALIDITY(st, objectmask, "ticket policy object: ");
230
231    /* Initialize ticket policy structure */
232    lpolicy =(krb5_ldap_policy_params *) malloc(sizeof(krb5_ldap_policy_params));
233    CHECK_NULL(lpolicy);
234    memset(lpolicy, 0, sizeof(krb5_ldap_policy_params));
235
236    if ((lpolicy->policy = strdup (policyname)) == NULL) {
237	st = ENOMEM;
238	goto cleanup;
239    }
240
241    lpolicy->tl_data = calloc (1, sizeof(*lpolicy->tl_data));
242    CHECK_NULL(lpolicy->tl_data);
243    lpolicy->tl_data->tl_data_type = KDB_TL_USER_INFO;
244
245    LDAP_SEARCH(policy_dn, LDAP_SCOPE_BASE, "(objectclass=krbTicketPolicy)", attributes);
246
247    *omask = 0;
248
249    ent=ldap_first_entry(ld, result);
250    if (ent != NULL) {
251	if (krb5_ldap_get_value(ld, ent, "krbmaxticketlife", (int *) &(lpolicy->maxtktlife)) == 0)
252	    *omask |= LDAP_POLICY_MAXTKTLIFE;
253
254	if (krb5_ldap_get_value(ld, ent, "krbmaxrenewableage", (int *) &(lpolicy->maxrenewlife)) == 0)
255	    *omask |= LDAP_POLICY_MAXRENEWLIFE;
256
257	if (krb5_ldap_get_value(ld, ent, "krbticketflags", (int *) &(lpolicy->tktflags)) == 0)
258	    *omask |= LDAP_POLICY_TKTFLAGS;
259    }
260    ldap_msgfree(result);
261
262    lpolicy->mask = *omask;
263    store_tl_data(lpolicy->tl_data, KDB_TL_MASK, omask);
264    *policy = lpolicy;
265
266cleanup:
267    if (st != 0) {
268	krb5_ldap_free_policy(context, lpolicy);
269	*policy = NULL;
270    }
271    krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
272    return st;
273}
274
275
276/*
277 * Function to delete ticket policy object from the directory.  Before
278 * calling this function krb5_ldap_read_policy should be called to
279 * check the existence of the object.  This serves one major purpose,
280 * i.e., if the object to be is anything other than the ticket policy
281 * object then the krb5_ldap_read_policy returns an error and thus is
282 * not accidently deleted in this function.
283 *
284 * NOTE: Other kerberos objects (user/realm object) might be having
285 * references to the policy object to be deleted. This situation is
286 * not handled here, instead is taken care of at all the places where
287 * the deleted policy object is read, to ignore a return status of
288 * LDAP_NO_SUCH_OBJECT and continue.
289 */
290
291krb5_error_code
292krb5_ldap_delete_policy(context, policyname)
293    krb5_context                context;
294    char                        *policyname;
295{
296	int                         refcount = 0;
297	char                        *policy_dn = NULL;
298    krb5_error_code             st = 0;
299    LDAP                        *ld = NULL;
300    kdb5_dal_handle             *dal_handle=NULL;
301    krb5_ldap_context           *ldap_context=NULL;
302    krb5_ldap_server_handle     *ldap_server_handle=NULL;
303
304	if (policyname == NULL) {
305	st = EINVAL;
306	prepend_err_str (context, gettext("Ticket Policy Object DN missing"),st,st);
307	goto cleanup;
308    }
309
310
311    SETUP_CONTEXT();
312    GET_HANDLE();
313
314    if ((st = krb5_ldap_name_to_policydn (context, policyname, &policy_dn)) != 0)
315        goto cleanup;
316
317    /* Checking for policy count for 0 and will not permit delete if
318     * it is greater than 0.  */
319
320    if ((st = krb5_ldap_get_reference_count (context, policy_dn,
321                    "krbTicketPolicyReference", &refcount, ld)) != 0)
322        goto cleanup;
323
324    if (refcount == 0) {
325	if ((st=ldap_delete_ext_s(ld, policy_dn, NULL, NULL)) != 0) {
326	    prepend_err_str (context,ldap_err2string(st),st,st);
327
328	    goto cleanup;
329	}
330    } else {
331	st = EINVAL;
332	prepend_err_str (context, gettext("Delete Failed: One or more Principals associated with the Ticket Policy"),st,st);
333	goto cleanup;
334    }
335
336cleanup:
337    if (policy_dn != NULL)
338        free (policy_dn);
339    krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
340    return st;
341}
342
343
344/*
345 * list policy objects from Directory
346 */
347
348krb5_error_code
349krb5_ldap_list_policy(context, containerdn, policy)
350    krb5_context	        context;
351    char                        *containerdn;
352    char                        ***policy;
353{
354    int                         i, j, count;
355    char                        **list = NULL;
356    char                        *policycontainerdn = containerdn;
357    kdb5_dal_handle             *dal_handle=NULL;
358    krb5_ldap_context           *ldap_context=NULL;
359    krb5_error_code             st=0;
360
361    SETUP_CONTEXT();
362    if (policycontainerdn == NULL) {
363        policycontainerdn = ldap_context->lrparams->realmdn;
364    }
365
366    if ((st = krb5_ldap_list(context, &list, "krbTicketPolicy", policycontainerdn)) != 0)
367	goto cleanup;
368
369    for (i = 0; list[i] != NULL; i++);
370
371    count = i;
372
373    *policy = (char **) calloc ((unsigned) count + 1, sizeof(char *));
374    if (*policy == NULL) {
375	st = ENOMEM;
376	goto cleanup;
377    }
378
379    for (i = 0, j = 0; list[i] != NULL; i++, j++) {
380	int ret;
381	ret = krb5_ldap_policydn_to_name (context, list[i], &(*policy)[i]);
382	if (ret != 0)
383	    j--;
384    }
385
386cleanup:
387    return st;
388}
389
390/*
391 * Function to free the ticket policy object structure.
392 * Note: this function assumes that memory of the policy structure is dynamically allocated and hence the whole
393 * structure is freed up. Care should be taken not to call this function on a static structure
394 */
395
396krb5_error_code
397krb5_ldap_free_policy(context, policy)
398    krb5_context                context;
399    krb5_ldap_policy_params    *policy;
400{
401
402    krb5_error_code st=0;
403
404    if (policy == NULL)
405	return st;
406
407    if (policy->policy)
408	free (policy->policy);
409
410    if (policy->tl_data) {
411	if (policy->tl_data->tl_data_contents)
412	    free (policy->tl_data->tl_data_contents);
413	free (policy->tl_data);
414    }
415    free (policy);
416
417    return st;
418}
419
420/*
421 * This function is general object listing routine.  It is currently
422 * used for ticket policy object listing.
423 */
424
425krb5_error_code
426krb5_ldap_list(context, list, objectclass, containerdn)
427    krb5_context	        context;
428    char                        ***list;
429    char                        *objectclass;
430    char                        *containerdn;
431{
432    char                        *filter=NULL, *dn=NULL;
433    krb5_error_code             st=0, tempst=0;
434    int                         i=0, count=0, filterlen=0;
435    LDAP                        *ld=NULL;
436    LDAPMessage                 *result=NULL,*ent=NULL;
437    kdb5_dal_handle             *dal_handle=NULL;
438    krb5_ldap_context           *ldap_context=NULL;
439    krb5_ldap_server_handle     *ldap_server_handle=NULL;
440
441    SETUP_CONTEXT();
442    GET_HANDLE();
443
444    /* check if the containerdn exists */
445    if (containerdn) {
446	if ((st=checkattributevalue(ld, containerdn, NULL, NULL, NULL)) != 0) {
447	    prepend_err_str (context, gettext("Error reading container object: "), st, st);
448	    goto cleanup;
449	}
450    }
451
452    /* set the filter for the search operation */
453    filterlen = strlen("(objectclass=") + strlen(objectclass) + 1 + 1;
454    filter = malloc ((unsigned) filterlen);
455    if (filter == NULL) {
456	st = ENOMEM;
457	goto cleanup;
458    }
459    snprintf(filter, (unsigned) filterlen,"(objectclass=%s)",objectclass);
460
461    LDAP_SEARCH(containerdn, LDAP_SCOPE_SUBTREE, filter, NULL);
462
463    count = ldap_count_entries(ld, result);
464    if (count == -1) {
465	ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &st);
466	st = set_ldap_error(context, st, OP_SEARCH);
467	goto cleanup;
468    }
469    *list = (char **) calloc ((unsigned) count+1, sizeof(char *));
470    if (*list == NULL) {
471	st = ENOMEM;
472	goto cleanup;
473    }
474
475    for (ent=ldap_first_entry(ld, result), count=0; ent != NULL; ent=ldap_next_entry(ld, ent), ++count) {
476	if ((dn=ldap_get_dn(ld, ent)) == NULL)
477	    continue;
478	if (((*list)[count] = strdup(dn)) == NULL) {
479	    ldap_memfree (dn);
480	    st = ENOMEM;
481	    goto cleanup;
482	}
483	ldap_memfree(dn);
484    }
485    ldap_msgfree(result);
486
487cleanup:
488    if (filter)
489	free (filter);
490
491    /* some error, free up all the memory */
492    if (st != 0) {
493	if (*list) {
494	    for (i=0; (*list)[i]; ++i)
495		free ((*list)[i]);
496	    free (*list);
497	    *list = NULL;
498	}
499    }
500    krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
501    return st;
502}
503