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