1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* plugins/kdb/ldap/libkdb_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 
36 /* Ticket policy object management */
37 
38 static void
free_list(char ** list)39 free_list(char **list)
40 {
41     int i;
42 
43     for (i = 0; list != NULL && list[i] != NULL; i++)
44         free(list[i]);
45     free(list);
46 }
47 
48 /*
49  * create the Ticket policy object in Directory.
50  */
51 krb5_error_code
krb5_ldap_create_policy(krb5_context context,krb5_ldap_policy_params * policy,int mask)52 krb5_ldap_create_policy(krb5_context context, krb5_ldap_policy_params *policy,
53                         int mask)
54 {
55     krb5_error_code             st=0;
56     LDAP                        *ld=NULL;
57     char                        *strval[3]={NULL}, *policy_dn = 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 
63     /* validate the input parameters */
64     if (policy == NULL || policy->policy == NULL) {
65         st = EINVAL;
66         k5_setmsg(context, st, _("Ticket Policy Name missing"));
67         goto cleanup;
68     }
69 
70     SETUP_CONTEXT();
71     GET_HANDLE();
72 
73     if ((st = krb5_ldap_name_to_policydn (context, policy->policy, &policy_dn)) != 0)
74         goto cleanup;
75 
76     memset(strval, 0, sizeof(strval));
77     strval[0] = policy->policy;
78     if ((st=krb5_add_str_mem_ldap_mod(&mods, "cn", LDAP_MOD_ADD, strval)) != 0)
79         goto cleanup;
80 
81     memset(strval, 0, sizeof(strval));
82     strval[0] = "krbTicketPolicy";
83     strval[1] = "krbTicketPolicyaux";
84     if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
85         goto cleanup;
86 
87     if (mask & LDAP_POLICY_MAXTKTLIFE) {
88         if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxticketlife", LDAP_MOD_ADD,
89                                           policy->maxtktlife)) != 0)
90             goto cleanup;
91     }
92 
93     if (mask & LDAP_POLICY_MAXRENEWLIFE) {
94         if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxrenewableage", LDAP_MOD_ADD,
95                                           policy->maxrenewlife)) != 0)
96             goto cleanup;
97     }
98 
99     if (mask & LDAP_POLICY_TKTFLAGS) {
100         if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbticketflags", LDAP_MOD_ADD,
101                                           policy->tktflags)) != 0)
102             goto cleanup;
103     }
104 
105     /* ldap add operation */
106     if ((st=ldap_add_ext_s(ld, policy_dn, mods, NULL, NULL)) != LDAP_SUCCESS) {
107         st = set_ldap_error (context, st, OP_ADD);
108         goto cleanup;
109     }
110 
111 cleanup:
112     if (policy_dn != NULL)
113         free(policy_dn);
114 
115     ldap_mods_free(mods, 1);
116     krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
117     return st;
118 }
119 
120 
121 /*
122  * modify the Ticket policy object in Directory.
123  */
124 
125 krb5_error_code
krb5_ldap_modify_policy(krb5_context context,krb5_ldap_policy_params * policy,int mask)126 krb5_ldap_modify_policy(krb5_context context, krb5_ldap_policy_params *policy,
127                         int mask)
128 {
129     int                         objectmask=0;
130     krb5_error_code             st=0;
131     LDAP                        *ld=NULL;
132     char                        *attrvalues[]={"krbTicketPolicy", "krbTicketPolicyAux", NULL}, *strval[2]={NULL};
133     char                        *policy_dn = NULL;
134     LDAPMod                     **mods=NULL;
135     kdb5_dal_handle             *dal_handle=NULL;
136     krb5_ldap_context           *ldap_context=NULL;
137     krb5_ldap_server_handle     *ldap_server_handle=NULL;
138 
139     /* validate the input parameters */
140     if (policy == NULL || policy->policy==NULL) {
141         st = EINVAL;
142         k5_setmsg(context, st, _("Ticket Policy Name missing"));
143         goto cleanup;
144     }
145 
146     SETUP_CONTEXT();
147     GET_HANDLE();
148 
149     if ((st = krb5_ldap_name_to_policydn (context, policy->policy, &policy_dn)) != 0)
150         goto cleanup;
151 
152     /* the policydn object should be of the krbTicketPolicy object class */
153     st = checkattributevalue(ld, policy_dn, "objectClass", attrvalues, &objectmask);
154     CHECK_CLASS_VALIDITY(st, objectmask, _("ticket policy object: "));
155 
156     if ((objectmask & 0x02) == 0) { /* add krbticketpolicyaux to the object class list */
157         memset(strval, 0, sizeof(strval));
158         strval[0] = "krbTicketPolicyAux";
159         if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
160             goto cleanup;
161     }
162 
163     if (mask & LDAP_POLICY_MAXTKTLIFE) {
164         if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxticketlife", LDAP_MOD_REPLACE,
165                                           policy->maxtktlife)) != 0)
166             goto cleanup;
167     }
168 
169     if (mask & LDAP_POLICY_MAXRENEWLIFE) {
170         if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxrenewableage", LDAP_MOD_REPLACE,
171                                           policy->maxrenewlife)) != 0)
172             goto cleanup;
173     }
174 
175     if (mask & LDAP_POLICY_TKTFLAGS) {
176         if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbticketflags", LDAP_MOD_REPLACE,
177                                           policy->tktflags)) != 0)
178             goto cleanup;
179     }
180 
181     if ((st=ldap_modify_ext_s(ld, policy_dn, mods, NULL, NULL)) != LDAP_SUCCESS) {
182         st = set_ldap_error (context, st, OP_MOD);
183         goto cleanup;
184     }
185 
186 cleanup:
187     if (policy_dn != NULL)
188         free(policy_dn);
189 
190     ldap_mods_free(mods, 1);
191     krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
192     return st;
193 }
194 
195 
196 /*
197  * Read the policy object from the Directory and populate the krb5_ldap_policy_params
198  * structure.
199  */
200 
201 krb5_error_code
krb5_ldap_read_policy(krb5_context context,char * policyname,krb5_ldap_policy_params ** policy,int * omask)202 krb5_ldap_read_policy(krb5_context context, char *policyname,
203                       krb5_ldap_policy_params **policy, int *omask)
204 {
205     krb5_error_code             st=0, tempst=0;
206     int                         objectmask=0, val=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         k5_setmsg(context, st, _("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", &val) == 0) {
254             lpolicy->maxtktlife = val;
255             *omask |= LDAP_POLICY_MAXTKTLIFE;
256         }
257         if (krb5_ldap_get_value(ld, ent, "krbmaxrenewableage", &val) == 0) {
258             lpolicy->maxrenewlife = val;
259             *omask |= LDAP_POLICY_MAXRENEWLIFE;
260         }
261         if (krb5_ldap_get_value(ld, ent, "krbticketflags", &val) == 0) {
262             lpolicy->tktflags = val;
263             *omask |= LDAP_POLICY_TKTFLAGS;
264         }
265     }
266 
267     lpolicy->mask = *omask;
268     store_tl_data(lpolicy->tl_data, KDB_TL_MASK, omask);
269     *policy = lpolicy;
270 
271 cleanup:
272     if (st != 0) {
273         krb5_ldap_free_policy(context, lpolicy);
274         *policy = NULL;
275     }
276     free(policy_dn);
277     ldap_msgfree(result);
278     krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
279     return st;
280 }
281 
282 
283 /*
284  * Function to delete ticket policy object from the directory.  Before
285  * calling this function krb5_ldap_read_policy should be called to
286  * check the existence of the object.  This serves one major purpose,
287  * i.e., if the object to be is anything other than the ticket policy
288  * object then the krb5_ldap_read_policy returns an error and thus is
289  * not accidently deleted in this function.
290  *
291  * NOTE: Other kerberos objects (user/realm object) might be having
292  * references to the policy object to be deleted. This situation is
293  * not handled here, instead is taken care of at all the places where
294  * the deleted policy object is read, to ignore a return status of
295  * LDAP_NO_SUCH_OBJECT and continue.
296  */
297 
298 krb5_error_code
krb5_ldap_delete_policy(krb5_context context,char * policyname)299 krb5_ldap_delete_policy(krb5_context context, char *policyname)
300 {
301     int                         refcount = 0;
302     char                        *policy_dn = NULL;
303     krb5_error_code             st = 0;
304     LDAP                        *ld = NULL;
305     kdb5_dal_handle             *dal_handle=NULL;
306     krb5_ldap_context           *ldap_context=NULL;
307     krb5_ldap_server_handle     *ldap_server_handle=NULL;
308 
309     if (policyname == NULL) {
310         st = EINVAL;
311         k5_prependmsg(context, st, _("Ticket Policy Object DN missing"));
312         goto cleanup;
313     }
314 
315 
316     SETUP_CONTEXT();
317     GET_HANDLE();
318 
319     if ((st = krb5_ldap_name_to_policydn (context, policyname, &policy_dn)) != 0)
320         goto cleanup;
321 
322     /* Checking for policy count for 0 and will not permit delete if
323      * it is greater than 0.  */
324 
325     if ((st = krb5_ldap_get_reference_count (context, policy_dn,
326                                              "krbTicketPolicyReference", &refcount, ld)) != 0)
327         goto cleanup;
328 
329     if (refcount == 0) {
330         if ((st=ldap_delete_ext_s(ld, policy_dn, NULL, NULL)) != 0) {
331             k5_prependmsg(context, st, "%s", ldap_err2string(st));
332 
333             goto cleanup;
334         }
335     } else {
336         st = EINVAL;
337         k5_prependmsg(context, st,
338                       _("Delete Failed: One or more Principals associated "
339                         "with the Ticket Policy"));
340         goto cleanup;
341     }
342 
343 cleanup:
344     if (policy_dn != NULL)
345         free (policy_dn);
346     krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
347     return st;
348 }
349 
350 
351 /*
352  * list policy objects from Directory
353  */
354 
355 krb5_error_code
krb5_ldap_list_policy(krb5_context context,char * containerdn,char *** policy)356 krb5_ldap_list_policy(krb5_context context, char *containerdn, char ***policy)
357 {
358     int                         i, j, count;
359     char                        **list = NULL;
360     char                        *policycontainerdn = containerdn;
361     kdb5_dal_handle             *dal_handle=NULL;
362     krb5_ldap_context           *ldap_context=NULL;
363     krb5_error_code             st=0;
364 
365     SETUP_CONTEXT();
366     if (policycontainerdn == NULL) {
367         policycontainerdn = ldap_context->lrparams->realmdn;
368     }
369 
370     if ((st = krb5_ldap_list(context, &list, "krbTicketPolicy", policycontainerdn)) != 0)
371         goto cleanup;
372 
373     for (i = 0; list[i] != NULL; i++);
374 
375     count = i;
376 
377     *policy = (char **) calloc ((unsigned) count + 1, sizeof(char *));
378     if (*policy == NULL) {
379         st = ENOMEM;
380         goto cleanup;
381     }
382 
383     for (i = 0, j = 0; list[i] != NULL; i++, j++) {
384         int ret;
385         ret = krb5_ldap_policydn_to_name (context, list[i], &(*policy)[i]);
386         if (ret != 0)
387             j--;
388     }
389 
390 cleanup:
391     free_list(list);
392     return st;
393 }
394 
395 /*
396  * Function to free the ticket policy object structure.
397  * Note: this function assumes that memory of the policy structure is dynamically allocated and hence the whole
398  * structure is freed up. Care should be taken not to call this function on a static structure
399  */
400 
401 krb5_error_code
krb5_ldap_free_policy(krb5_context context,krb5_ldap_policy_params * policy)402 krb5_ldap_free_policy(krb5_context context, krb5_ldap_policy_params *policy)
403 {
404 
405     krb5_error_code st=0;
406 
407     if (policy == NULL)
408         return st;
409 
410     if (policy->policy)
411         free (policy->policy);
412 
413     if (policy->tl_data) {
414         if (policy->tl_data->tl_data_contents)
415             free (policy->tl_data->tl_data_contents);
416         free (policy->tl_data);
417     }
418     free (policy);
419 
420     return st;
421 }
422 
423 /*
424  * This function is general object listing routine.  It is currently
425  * used for ticket policy object listing.
426  */
427 
428 krb5_error_code
krb5_ldap_list(krb5_context context,char *** list,char * objectclass,char * containerdn)429 krb5_ldap_list(krb5_context context, char ***list, char *objectclass,
430                char *containerdn)
431 {
432     char                        *filter=NULL, *dn=NULL;
433     krb5_error_code             st=0, tempst=0;
434     int                         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             k5_prependmsg(context, st, _("Error reading container object"));
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 
486 cleanup:
487     if (filter)
488         free (filter);
489 
490     /* some error, free up all the memory */
491     if (st != 0) {
492         free_list(*list);
493         *list = NULL;
494     }
495     ldap_msgfree(result);
496     krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
497     return st;
498 }
499