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