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