Home | History | Annotate | Download | only in libkdb_ldap
      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