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  */
42 krb5_error_code
krb5_ldap_create_policy(context,policy,mask)43 krb5_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 
104 cleanup:
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 
118 krb5_error_code
krb5_ldap_modify_policy(context,policy,mask)119 krb5_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 
181 cleanup:
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 
196 krb5_error_code
krb5_ldap_read_policy(context,policyname,policy,omask)197 krb5_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 
266 cleanup:
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 
291 krb5_error_code
krb5_ldap_delete_policy(context,policyname)292 krb5_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 
336 cleanup:
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 
348 krb5_error_code
krb5_ldap_list_policy(context,containerdn,policy)349 krb5_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 
386 cleanup:
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 
396 krb5_error_code
krb5_ldap_free_policy(context,policy)397 krb5_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 
425 krb5_error_code
krb5_ldap_list(context,list,objectclass,containerdn)426 krb5_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 
487 cleanup:
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