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
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
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
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
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
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
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
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