1 #pragma ident "%Z%%M% %I% %E% SMI"
2
3 /*
4 * lib/kdb/kdb_ldap/ldap_services.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_services.h"
36 #include "ldap_err.h"
37 #include <libintl.h>
38
39 #if defined(HAVE_EDIRECTORY)
40
41 static char *realmcontclass[] = {"krbRealmContainer", NULL};
42
43 /*
44 * create the service object from Directory
45 */
46
47 krb5_error_code
krb5_ldap_create_service(context,service,mask)48 krb5_ldap_create_service(context, service, mask)
49 krb5_context context;
50 krb5_ldap_service_params *service;
51 int mask;
52 {
53 int i=0, j=0;
54 krb5_error_code st=0;
55 LDAP *ld=NULL;
56 char **rdns=NULL, *realmattr=NULL, *strval[3]={NULL};
57 LDAPMod **mods=NULL;
58 kdb5_dal_handle *dal_handle=NULL;
59 krb5_ldap_context *ldap_context=NULL;
60 krb5_ldap_server_handle *ldap_server_handle=NULL;
61 char errbuf[1024];
62
63 /* validate the input parameter */
64 if (service == NULL || service->servicedn == NULL) {
65 st = EINVAL;
66 krb5_set_error_message (context, st, gettext("Service DN NULL"));
67 goto cleanup;
68 }
69
70 SETUP_CONTEXT();
71 GET_HANDLE();
72
73 /* identify the class that the object should belong to. This depends on the servicetype */
74 memset(strval, 0, sizeof(strval));
75 strval[0] = "krbService";
76 if (service->servicetype == LDAP_KDC_SERVICE) {
77 strval[1] = "krbKdcService";
78 realmattr = "krbKdcServers";
79 } else if (service->servicetype == LDAP_ADMIN_SERVICE) {
80 strval[1] = "krbAdmService";
81 realmattr = "krbAdmServers";
82 } else if (service->servicetype == LDAP_PASSWD_SERVICE) {
83 strval[1] = "krbPwdService";
84 realmattr = "krbPwdServers";
85 } else {
86 strval[1] = "krbKdcService";
87 realmattr = "krbKdcServers";
88 }
89 if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
90 goto cleanup;
91
92 rdns = ldap_explode_dn(service->servicedn, 1);
93 if (rdns == NULL) {
94 st = LDAP_INVALID_DN_SYNTAX;
95 goto cleanup;
96 }
97 memset(strval, 0, sizeof(strval));
98 strval[0] = rdns[0];
99 if ((st=krb5_add_str_mem_ldap_mod(&mods, "cn", LDAP_MOD_ADD, strval)) != 0)
100 goto cleanup;
101
102 if (mask & LDAP_SERVICE_SERVICEFLAG) {
103 if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbserviceflags", LDAP_MOD_ADD,
104 service->krbserviceflags)) != 0)
105 goto cleanup;
106 }
107
108 if (mask & LDAP_SERVICE_HOSTSERVER) {
109 if (service->krbhostservers != NULL) {
110 if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbhostserver", LDAP_MOD_ADD,
111 service->krbhostservers)) != 0)
112 goto cleanup;
113 } else {
114 st = EINVAL;
115 krb5_set_error_message (context, st, gettext("'krbhostserver' argument invalid"));
116 goto cleanup;
117 }
118 }
119
120 if (mask & LDAP_SERVICE_REALMREFERENCE) {
121 if (service->krbrealmreferences != NULL) {
122 unsigned int realmmask=0;
123
124 /* check for the validity of the values */
125 for (j=0; service->krbrealmreferences[j] != NULL; ++j) {
126 st = checkattributevalue(ld, service->krbrealmreferences[j], "ObjectClass",
127 realmcontclass, &realmmask);
128 CHECK_CLASS_VALIDITY(st, realmmask, "realm object value: ");
129 }
130 if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbrealmreferences", LDAP_MOD_ADD,
131 service->krbrealmreferences)) != 0)
132 goto cleanup;
133 } else {
134 st = EINVAL;
135 krb5_set_error_message (context, st, gettext("Server has no 'krbrealmreferences'"));
136 goto cleanup;
137 }
138 }
139
140 /* ldap add operation */
141 if ((st=ldap_add_ext_s(ld, service->servicedn, mods, NULL, NULL)) != LDAP_SUCCESS) {
142 st = set_ldap_error (context, st, OP_ADD);
143 goto cleanup;
144 }
145
146 /*
147 * If the service created has realm/s associated with it, then the realm should be updated
148 * to have a reference to the service object just created.
149 */
150 if (mask & LDAP_SERVICE_REALMREFERENCE) {
151 for (i=0; service->krbrealmreferences[i]; ++i) {
152 if ((st=updateAttribute(ld, service->krbrealmreferences[i], realmattr,
153 service->servicedn)) != 0) {
154 snprintf (errbuf, sizeof(errbuf), gettext("Error adding 'krbRealmReferences' to %s: "),
155 service->krbrealmreferences[i]);
156 prepend_err_str (context, errbuf, st, st);
157 /* delete service object, status ignored intentionally */
158 ldap_delete_ext_s(ld, service->servicedn, NULL, NULL);
159 goto cleanup;
160 }
161 }
162 }
163
164 cleanup:
165
166 if (rdns)
167 ldap_value_free (rdns);
168
169 ldap_mods_free(mods, 1);
170 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
171 return st;
172 }
173
174
175 /*
176 * modify the service object from Directory
177 */
178
179 krb5_error_code
krb5_ldap_modify_service(context,service,mask)180 krb5_ldap_modify_service(context, service, mask)
181 krb5_context context;
182 krb5_ldap_service_params *service;
183 int mask;
184 {
185 int i=0, j=0, count=0;
186 krb5_error_code st=0;
187 LDAP *ld=NULL;
188 char **values=NULL, *attr[] = { "krbRealmReferences", NULL};
189 char *realmattr=NULL;
190 char **oldrealmrefs=NULL, **newrealmrefs=NULL;
191 LDAPMod **mods=NULL;
192 LDAPMessage *result=NULL, *ent=NULL;
193 kdb5_dal_handle *dal_handle=NULL;
194 krb5_ldap_context *ldap_context=NULL;
195 krb5_ldap_server_handle *ldap_server_handle=NULL;
196
197 /* validate the input parameter */
198 if (service == NULL || service->servicedn == NULL) {
199 st = EINVAL;
200 krb5_set_error_message (context, st, gettext("Service DN is NULL"));
201 goto cleanup;
202 }
203
204 SETUP_CONTEXT();
205 GET_HANDLE();
206
207 if (mask & LDAP_SERVICE_SERVICEFLAG) {
208 if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbserviceflags", LDAP_MOD_REPLACE,
209 service->krbserviceflags)) != 0)
210 goto cleanup;
211 }
212
213 if (mask & LDAP_SERVICE_HOSTSERVER) {
214 if (service->krbhostservers != NULL) {
215 if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbhostserver", LDAP_MOD_REPLACE,
216 service->krbhostservers)) != 0)
217 goto cleanup;
218 } else {
219 st = EINVAL;
220 krb5_set_error_message (context, st, gettext("'krbhostserver' value invalid"));
221 goto cleanup;
222 }
223 }
224
225 if (mask & LDAP_SERVICE_REALMREFERENCE) {
226 if (service->krbrealmreferences != NULL) {
227 unsigned int realmmask=0;
228
229 /* check for the validity of the values */
230 for (j=0; service->krbrealmreferences[j]; ++j) {
231 st = checkattributevalue(ld, service->krbrealmreferences[j], "ObjectClass",
232 realmcontclass, &realmmask);
233 CHECK_CLASS_VALIDITY(st, realmmask, "realm object value: ");
234 }
235 if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbrealmreferences", LDAP_MOD_REPLACE,
236 service->krbrealmreferences)) != 0)
237 goto cleanup;
238
239
240 /* get the attribute of the realm to be set */
241 if (service->servicetype == LDAP_KDC_SERVICE)
242 realmattr = "krbKdcServers";
243 else if (service->servicetype == LDAP_ADMIN_SERVICE)
244 realmattr = "krbAdmservers";
245 else if (service->servicetype == LDAP_PASSWD_SERVICE)
246 realmattr = "krbPwdServers";
247 else
248 realmattr = "krbKdcServers";
249
250 /* read the existing list of krbRealmreferences. this will needed */
251 if ((st = ldap_search_ext_s (ld,
252 service->servicedn,
253 LDAP_SCOPE_BASE,
254 0,
255 attr,
256 0,
257 NULL,
258 NULL,
259 NULL,
260 0,
261 &result)) != LDAP_SUCCESS) {
262 st = set_ldap_error (context, st, OP_SEARCH);
263 goto cleanup;
264 }
265
266 ent = ldap_first_entry(ld, result);
267 if (ent) {
268 if ((values=ldap_get_values(ld, ent, "krbRealmReferences")) != NULL) {
269 count = ldap_count_values(values);
270 if ((st=copy_arrays(values, &oldrealmrefs, count)) != 0)
271 goto cleanup;
272 ldap_value_free(values);
273 }
274 }
275 ldap_msgfree(result);
276 } else {
277 st = EINVAL;
278 krb5_set_error_message (context, st, gettext("'krbRealmReferences' value invalid"));
279 goto cleanup;
280 }
281 }
282
283 /* ldap modify operation */
284 if ((st=ldap_modify_ext_s(ld, service->servicedn, mods, NULL, NULL)) != LDAP_SUCCESS) {
285 st = set_ldap_error (context, st, OP_MOD);
286 goto cleanup;
287 }
288
289 /*
290 * If the service modified had realm/s associations changed, then the realm should be
291 * updated to reflect the changes.
292 */
293
294 if (mask & LDAP_SERVICE_REALMREFERENCE) {
295 /* get the count of the new list of krbrealmreferences */
296 for (i=0; service->krbrealmreferences[i]; ++i)
297 ;
298
299 /* make a new copy of the krbrealmreferences */
300 if ((st=copy_arrays(service->krbrealmreferences, &newrealmrefs, i)) != 0)
301 goto cleanup;
302
303 /* find the deletions/additions to the list of krbrealmreferences */
304 if (disjoint_members(oldrealmrefs, newrealmrefs) != 0)
305 goto cleanup;
306
307 /* see if some of the attributes have to be deleted */
308 if (oldrealmrefs) {
309
310 /* update the dn represented by the attribute that is to be deleted */
311 for (i=0; oldrealmrefs[i]; ++i)
312 if ((st=deleteAttribute(ld, oldrealmrefs[i], realmattr, service->servicedn)) != 0) {
313 prepend_err_str (context, gettext("Error deleting realm attribute:"), st, st);
314 goto cleanup;
315 }
316 }
317
318 /* see if some of the attributes have to be added */
319 for (i=0; newrealmrefs[i]; ++i)
320 if ((st=updateAttribute(ld, newrealmrefs[i], realmattr, service->servicedn)) != 0) {
321 prepend_err_str (context, gettext("Error updating realm attribute: "), st, st);
322 goto cleanup;
323 }
324 }
325
326 cleanup:
327
328 if (oldrealmrefs) {
329 for (i=0; oldrealmrefs[i]; ++i)
330 free (oldrealmrefs[i]);
331 free (oldrealmrefs);
332 }
333
334 if (newrealmrefs) {
335 for (i=0; newrealmrefs[i]; ++i)
336 free (newrealmrefs[i]);
337 free (newrealmrefs);
338 }
339
340 ldap_mods_free(mods, 1);
341 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
342 return st;
343 }
344
345
346 krb5_error_code
krb5_ldap_delete_service(context,service,servicedn)347 krb5_ldap_delete_service(context, service, servicedn)
348 krb5_context context;
349 krb5_ldap_service_params *service;
350 char *servicedn;
351 {
352 krb5_error_code st = 0;
353 LDAP *ld=NULL;
354 kdb5_dal_handle *dal_handle=NULL;
355 krb5_ldap_context *ldap_context=NULL;
356 krb5_ldap_server_handle *ldap_server_handle=NULL;
357
358 SETUP_CONTEXT();
359 GET_HANDLE();
360
361 st = ldap_delete_ext_s(ld, servicedn, NULL, NULL);
362 if (st != 0) {
363 st = set_ldap_error (context, st, OP_DEL);
364 }
365
366 /* NOTE: This should be removed now as the backlinks are going off in OpenLDAP */
367 /* time to delete krbrealmreferences. This is only for OpenLDAP */
368 #ifndef HAVE_EDIRECTORY
369 {
370 int i=0;
371 char *attr=NULL;
372
373 if (service) {
374 if (service->krbrealmreferences) {
375 if (service->servicetype == LDAP_KDC_SERVICE)
376 attr = "krbkdcservers";
377 else if (service->servicetype == LDAP_ADMIN_SERVICE)
378 attr = "krbadmservers";
379 else if (service->servicetype == LDAP_PASSWD_SERVICE)
380 attr = "krbpwdservers";
381
382 for (i=0; service->krbrealmreferences[i]; ++i) {
383 deleteAttribute(ld, service->krbrealmreferences[i], attr, servicedn);
384 }
385 }
386 }
387 }
388 #endif
389
390 cleanup:
391
392 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
393 return st;
394 }
395
396
397 /*
398 * This function lists service objects from Directory
399 */
400
401 krb5_error_code
krb5_ldap_list_services(context,containerdn,services)402 krb5_ldap_list_services(context, containerdn, services)
403 krb5_context context;
404 char *containerdn;
405 char ***services;
406 {
407 return (krb5_ldap_list(context, services, "krbService", containerdn));
408 }
409
410 /*
411 * This function reads the service object from Directory
412 */
413 krb5_error_code
krb5_ldap_read_service(context,servicedn,service,omask)414 krb5_ldap_read_service(context, servicedn, service, omask)
415 krb5_context context;
416 char *servicedn;
417 krb5_ldap_service_params **service;
418 int *omask;
419 {
420 char **values=NULL;
421 int i=0, count=0, objectmask=0;
422 krb5_error_code st=0, tempst=0;
423 LDAPMessage *result=NULL,*ent=NULL;
424 char *attributes[] = {"krbHostServer", "krbServiceflags",
425 "krbRealmReferences", "objectclass", NULL};
426 char *attrvalues[] = {"krbService", NULL};
427 krb5_ldap_service_params *lservice=NULL;
428 krb5_ldap_context *ldap_context=NULL;
429 kdb5_dal_handle *dal_handle=NULL;
430 krb5_ldap_server_handle *ldap_server_handle=NULL;
431 LDAP *ld = NULL;
432
433 /* validate the input parameter */
434 if (servicedn == NULL) {
435 st = EINVAL;
436 krb5_set_error_message (context, st, gettext("Service DN NULL"));
437 goto cleanup;
438 }
439
440 SETUP_CONTEXT();
441 GET_HANDLE();
442
443 *omask = 0;
444
445 /* the policydn object should be of the krbService object class */
446 st = checkattributevalue(ld, servicedn, "objectClass", attrvalues, &objectmask);
447 CHECK_CLASS_VALIDITY(st, objectmask, "service object value: ");
448
449 /* Initialize service structure */
450 lservice =(krb5_ldap_service_params *) calloc(1, sizeof(krb5_ldap_service_params));
451 if (lservice == NULL) {
452 st = ENOMEM;
453 goto cleanup;
454 }
455
456 /* allocate tl_data structure to store MASK information */
457 lservice->tl_data = calloc (1, sizeof(*lservice->tl_data));
458 if (lservice->tl_data == NULL) {
459 st = ENOMEM;
460 goto cleanup;
461 }
462 lservice->tl_data->tl_data_type = KDB_TL_USER_INFO;
463
464 LDAP_SEARCH(servicedn, LDAP_SCOPE_BASE, "(objectclass=krbService)", attributes);
465
466 lservice->servicedn = strdup(servicedn);
467 CHECK_NULL(lservice->servicedn);
468
469 ent=ldap_first_entry(ld, result);
470 if (ent != NULL) {
471
472 if ((values=ldap_get_values(ld, ent, "krbServiceFlags")) != NULL) {
473 lservice->krbserviceflags = atoi(values[0]);
474 *omask |= LDAP_SERVICE_SERVICEFLAG;
475 ldap_value_free(values);
476 }
477
478 if ((values=ldap_get_values(ld, ent, "krbHostServer")) != NULL) {
479 count = ldap_count_values(values);
480 if ((st=copy_arrays(values, &(lservice->krbhostservers), count)) != 0)
481 goto cleanup;
482 *omask |= LDAP_SERVICE_HOSTSERVER;
483 ldap_value_free(values);
484 }
485
486 if ((values=ldap_get_values(ld, ent, "krbRealmReferences")) != NULL) {
487 count = ldap_count_values(values);
488 if ((st=copy_arrays(values, &(lservice->krbrealmreferences), count)) != 0)
489 goto cleanup;
490 *omask |= LDAP_SERVICE_REALMREFERENCE;
491 ldap_value_free(values);
492 }
493
494 if ((values=ldap_get_values(ld, ent, "objectClass")) != NULL) {
495 for (i=0; values[i]; ++i) {
496 if (strcasecmp(values[i], "krbKdcService") == 0) {
497 lservice->servicetype = LDAP_KDC_SERVICE;
498 break;
499 }
500
501 if (strcasecmp(values[i], "krbAdmService") == 0) {
502 lservice->servicetype = LDAP_ADMIN_SERVICE;
503 break;
504 }
505
506 if (strcasecmp(values[i], "krbPwdService") == 0) {
507 lservice->servicetype = LDAP_PASSWD_SERVICE;
508 break;
509 }
510 }
511 ldap_value_free(values);
512 }
513 }
514 ldap_msgfree(result);
515
516 cleanup:
517 if (st != 0) {
518 krb5_ldap_free_service(context, lservice);
519 *service = NULL;
520 } else {
521 store_tl_data(lservice->tl_data, KDB_TL_MASK, omask);
522 *service = lservice;
523 }
524
525 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
526 return st;
527 }
528
529 /*
530 * This function frees the krb5_ldap_service_params structure members.
531 */
532
533 krb5_error_code
krb5_ldap_free_service(context,service)534 krb5_ldap_free_service(context, service)
535 krb5_context context;
536 krb5_ldap_service_params *service;
537 {
538 int i=0;
539
540 if (service == NULL)
541 return 0;
542
543 if (service->servicedn)
544 free (service->servicedn);
545
546 if (service->krbrealmreferences) {
547 for (i=0; service->krbrealmreferences[i]; ++i)
548 free (service->krbrealmreferences[i]);
549 free (service->krbrealmreferences);
550 }
551
552 if (service->krbhostservers) {
553 for (i=0; service->krbhostservers[i]; ++i)
554 free (service->krbhostservers[i]);
555 free (service->krbhostservers);
556 }
557
558 if (service->tl_data) {
559 if (service->tl_data->tl_data_contents)
560 free (service->tl_data->tl_data_contents);
561 free (service->tl_data);
562 }
563
564 free (service);
565 return 0;
566 }
567
568 krb5_error_code
krb5_ldap_set_service_passwd(context,service,passwd)569 krb5_ldap_set_service_passwd(context, service, passwd)
570 krb5_context context;
571 char *service;
572 char *passwd;
573 {
574 krb5_error_code st=0;
575 LDAPMod **mods=NULL;
576 char *password[2] = {NULL};
577 LDAP *ld=NULL;
578 krb5_ldap_context *ldap_context=NULL;
579 kdb5_dal_handle *dal_handle=NULL;
580 krb5_ldap_server_handle *ldap_server_handle=NULL;
581
582 password[0] = passwd;
583
584 SETUP_CONTEXT();
585 GET_HANDLE();
586
587 if ((st=krb5_add_str_mem_ldap_mod(&mods, "userPassword", LDAP_MOD_REPLACE, password)) != 0)
588 goto cleanup;
589
590 st = ldap_modify_ext_s(ld, service, mods, NULL, NULL);
591 if (st) {
592 st = set_ldap_error (context, st, OP_MOD);
593 }
594
595 cleanup:
596 ldap_mods_free(mods, 1);
597 krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
598 return st;
599 }
600 #endif
601