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
41static char *realmcontclass[] = {"krbRealmContainer", NULL};
42
43/*
44 * create the service object from Directory
45 */
46
47krb5_error_code
48krb5_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
164cleanup:
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
179krb5_error_code
180krb5_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
326cleanup:
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
346krb5_error_code
347krb5_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
390cleanup:
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
401krb5_error_code
402krb5_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 */
413krb5_error_code
414krb5_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
516cleanup:
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
533krb5_error_code
534krb5_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
568krb5_error_code
569krb5_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
595cleanup:
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