Path: blob/main/crypto/heimdal/lib/hdb/hdb-ldap.c
106846 views
/*1* Copyright (c) 1999-2001, 2003, PADL Software Pty Ltd.2* Copyright (c) 2004, Andrew Bartlett.3* Copyright (c) 2003 - 2008, Kungliga Tekniska Högskolan.4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9*10* 1. Redistributions of source code must retain the above copyright11* notice, this list of conditions and the following disclaimer.12*13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* 3. Neither the name of PADL Software nor the names of its contributors18* may be used to endorse or promote products derived from this software19* without specific prior written permission.20*21* THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND22* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE23* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE24* ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE25* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL26* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS27* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)28* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT29* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY30* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF31* SUCH DAMAGE.32*/3334#include "hdb_locl.h"3536#ifdef OPENLDAP3738#include <lber.h>39#include <ldap.h>40#include <sys/un.h>41#include <hex.h>4243static krb5_error_code LDAP__connect(krb5_context context, HDB *);44static krb5_error_code LDAP_close(krb5_context context, HDB *);4546static krb5_error_code hdb_ldap_create(krb5_context context, HDB **, const char *);47static krb5_error_code hdb_ldapi_create(krb5_context context, HDB **, const char *);4849static krb5_error_code50LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,51int flags, hdb_entry_ex * ent);5253static const char *default_structural_object = "account";54static char *structural_object;55static krb5_boolean samba_forwardable;5657struct hdbldapdb {58LDAP *h_lp;59int h_msgid;60char *h_base;61char *h_url;62char *h_createbase;63};6465#define HDB2LDAP(db) (((struct hdbldapdb *)(db)->hdb_db)->h_lp)66#define HDB2MSGID(db) (((struct hdbldapdb *)(db)->hdb_db)->h_msgid)67#define HDBSETMSGID(db,msgid) \68do { ((struct hdbldapdb *)(db)->hdb_db)->h_msgid = msgid; } while(0)69#define HDB2BASE(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_base)70#define HDB2URL(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_url)71#define HDB2CREATE(db) (((struct hdbldapdb *)(db)->hdb_db)->h_createbase)7273/*74*75*/7677static char * krb5kdcentry_attrs[] = {78"cn",79"createTimestamp",80"creatorsName",81"krb5EncryptionType",82"krb5KDCFlags",83"krb5Key",84"krb5KeyVersionNumber",85"krb5MaxLife",86"krb5MaxRenew",87"krb5PasswordEnd",88"krb5PrincipalName",89"krb5PrincipalRealm",90"krb5ValidEnd",91"krb5ValidStart",92"modifiersName",93"modifyTimestamp",94"objectClass",95"sambaAcctFlags",96"sambaKickoffTime",97"sambaNTPassword",98"sambaPwdLastSet",99"sambaPwdMustChange",100"uid",101NULL102};103104static char *krb5principal_attrs[] = {105"cn",106"createTimestamp",107"creatorsName",108"krb5PrincipalName",109"krb5PrincipalRealm",110"modifiersName",111"modifyTimestamp",112"objectClass",113"uid",114NULL115};116117static int118LDAP_no_size_limit(krb5_context context, LDAP *lp)119{120int ret, limit = LDAP_NO_LIMIT;121122ret = ldap_set_option(lp, LDAP_OPT_SIZELIMIT, (const void *)&limit);123if (ret != LDAP_SUCCESS) {124krb5_set_error_message(context, HDB_ERR_BADVERSION,125"ldap_set_option: %s",126ldap_err2string(ret));127return HDB_ERR_BADVERSION;128}129return 0;130}131132static int133check_ldap(krb5_context context, HDB *db, int ret)134{135switch (ret) {136case LDAP_SUCCESS:137return 0;138case LDAP_SERVER_DOWN:139LDAP_close(context, db);140return 1;141default:142return 1;143}144}145146static krb5_error_code147LDAP__setmod(LDAPMod *** modlist, int modop, const char *attribute,148int *pIndex)149{150int cMods;151152if (*modlist == NULL) {153*modlist = (LDAPMod **)ber_memcalloc(1, sizeof(LDAPMod *));154if (*modlist == NULL)155return ENOMEM;156}157158for (cMods = 0; (*modlist)[cMods] != NULL; cMods++) {159if ((*modlist)[cMods]->mod_op == modop &&160strcasecmp((*modlist)[cMods]->mod_type, attribute) == 0) {161break;162}163}164165*pIndex = cMods;166167if ((*modlist)[cMods] == NULL) {168LDAPMod *mod;169170*modlist = (LDAPMod **)ber_memrealloc(*modlist,171(cMods + 2) * sizeof(LDAPMod *));172if (*modlist == NULL)173return ENOMEM;174175(*modlist)[cMods] = (LDAPMod *)ber_memalloc(sizeof(LDAPMod));176if ((*modlist)[cMods] == NULL)177return ENOMEM;178179mod = (*modlist)[cMods];180mod->mod_op = modop;181mod->mod_type = ber_strdup(attribute);182if (mod->mod_type == NULL) {183ber_memfree(mod);184(*modlist)[cMods] = NULL;185return ENOMEM;186}187188if (modop & LDAP_MOD_BVALUES) {189mod->mod_bvalues = NULL;190} else {191mod->mod_values = NULL;192}193194(*modlist)[cMods + 1] = NULL;195}196197return 0;198}199200static krb5_error_code201LDAP_addmod_len(LDAPMod *** modlist, int modop, const char *attribute,202unsigned char *value, size_t len)203{204krb5_error_code ret;205int cMods, i = 0;206207ret = LDAP__setmod(modlist, modop | LDAP_MOD_BVALUES, attribute, &cMods);208if (ret)209return ret;210211if (value != NULL) {212struct berval **bv;213214bv = (*modlist)[cMods]->mod_bvalues;215if (bv != NULL) {216for (i = 0; bv[i] != NULL; i++)217;218bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv));219} else220bv = ber_memalloc(2 * sizeof(*bv));221if (bv == NULL)222return ENOMEM;223224(*modlist)[cMods]->mod_bvalues = bv;225226bv[i] = ber_memalloc(sizeof(**bv));;227if (bv[i] == NULL)228return ENOMEM;229230bv[i]->bv_val = (void *)value;231bv[i]->bv_len = len;232233bv[i + 1] = NULL;234}235236return 0;237}238239static krb5_error_code240LDAP_addmod(LDAPMod *** modlist, int modop, const char *attribute,241const char *value)242{243int cMods, i = 0;244krb5_error_code ret;245246ret = LDAP__setmod(modlist, modop, attribute, &cMods);247if (ret)248return ret;249250if (value != NULL) {251char **bv;252253bv = (*modlist)[cMods]->mod_values;254if (bv != NULL) {255for (i = 0; bv[i] != NULL; i++)256;257bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv));258} else259bv = ber_memalloc(2 * sizeof(*bv));260if (bv == NULL)261return ENOMEM;262263(*modlist)[cMods]->mod_values = bv;264265bv[i] = ber_strdup(value);266if (bv[i] == NULL)267return ENOMEM;268269bv[i + 1] = NULL;270}271272return 0;273}274275static krb5_error_code276LDAP_addmod_generalized_time(LDAPMod *** mods, int modop,277const char *attribute, KerberosTime * time)278{279char buf[22];280struct tm *tm;281282/* XXX not threadsafe */283tm = gmtime(time);284strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", tm);285286return LDAP_addmod(mods, modop, attribute, buf);287}288289static krb5_error_code290LDAP_addmod_integer(krb5_context context,291LDAPMod *** mods, int modop,292const char *attribute, unsigned long l)293{294krb5_error_code ret;295char *buf;296297ret = asprintf(&buf, "%ld", l);298if (ret < 0) {299krb5_set_error_message(context, ENOMEM,300"asprintf: out of memory:");301return ENOMEM;302}303ret = LDAP_addmod(mods, modop, attribute, buf);304free (buf);305return ret;306}307308static krb5_error_code309LDAP_get_string_value(HDB * db, LDAPMessage * entry,310const char *attribute, char **ptr)311{312struct berval **vals;313314vals = ldap_get_values_len(HDB2LDAP(db), entry, attribute);315if (vals == NULL || vals[0] == NULL) {316*ptr = NULL;317return HDB_ERR_NOENTRY;318}319320*ptr = malloc(vals[0]->bv_len + 1);321if (*ptr == NULL) {322ldap_value_free_len(vals);323return ENOMEM;324}325326memcpy(*ptr, vals[0]->bv_val, vals[0]->bv_len);327(*ptr)[vals[0]->bv_len] = 0;328329ldap_value_free_len(vals);330331return 0;332}333334static krb5_error_code335LDAP_get_integer_value(HDB * db, LDAPMessage * entry,336const char *attribute, int *ptr)337{338krb5_error_code ret;339char *val;340341ret = LDAP_get_string_value(db, entry, attribute, &val);342if (ret)343return ret;344*ptr = atoi(val);345free(val);346return 0;347}348349static krb5_error_code350LDAP_get_generalized_time_value(HDB * db, LDAPMessage * entry,351const char *attribute, KerberosTime * kt)352{353char *tmp, *gentime;354struct tm tm;355int ret;356357*kt = 0;358359ret = LDAP_get_string_value(db, entry, attribute, &gentime);360if (ret)361return ret;362363tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm);364if (tmp == NULL) {365free(gentime);366return HDB_ERR_NOENTRY;367}368369free(gentime);370371*kt = timegm(&tm);372373return 0;374}375376static int377bervalstrcmp(struct berval *v, const char *str)378{379size_t len = strlen(str);380return (v->bv_len == len) && strncasecmp(str, (char *)v->bv_val, len) == 0;381}382383384static krb5_error_code385LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent,386LDAPMessage * msg, LDAPMod *** pmods)387{388krb5_error_code ret;389krb5_boolean is_new_entry;390char *tmp = NULL;391LDAPMod **mods = NULL;392hdb_entry_ex orig;393unsigned long oflags, nflags;394int i;395396krb5_boolean is_samba_account = FALSE;397krb5_boolean is_account = FALSE;398krb5_boolean is_heimdal_entry = FALSE;399krb5_boolean is_heimdal_principal = FALSE;400401struct berval **vals;402403*pmods = NULL;404405if (msg != NULL) {406407ret = LDAP_message2entry(context, db, msg, 0, &orig);408if (ret)409goto out;410411is_new_entry = FALSE;412413vals = ldap_get_values_len(HDB2LDAP(db), msg, "objectClass");414if (vals) {415int num_objectclasses = ldap_count_values_len(vals);416for (i=0; i < num_objectclasses; i++) {417if (bervalstrcmp(vals[i], "sambaSamAccount"))418is_samba_account = TRUE;419else if (bervalstrcmp(vals[i], structural_object))420is_account = TRUE;421else if (bervalstrcmp(vals[i], "krb5Principal"))422is_heimdal_principal = TRUE;423else if (bervalstrcmp(vals[i], "krb5KDCEntry"))424is_heimdal_entry = TRUE;425}426ldap_value_free_len(vals);427}428429/*430* If this is just a "account" entry and no other objectclass431* is hanging on this entry, it's really a new entry.432*/433if (is_samba_account == FALSE && is_heimdal_principal == FALSE &&434is_heimdal_entry == FALSE) {435if (is_account == TRUE) {436is_new_entry = TRUE;437} else {438ret = HDB_ERR_NOENTRY;439goto out;440}441}442} else443is_new_entry = TRUE;444445if (is_new_entry) {446447/* to make it perfectly obvious we're depending on448* orig being intiialized to zero */449memset(&orig, 0, sizeof(orig));450451ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "top");452if (ret)453goto out;454455/* account is the structural object class */456if (is_account == FALSE) {457ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass",458structural_object);459is_account = TRUE;460if (ret)461goto out;462}463464ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5Principal");465is_heimdal_principal = TRUE;466if (ret)467goto out;468469ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5KDCEntry");470is_heimdal_entry = TRUE;471if (ret)472goto out;473}474475if (is_new_entry ||476krb5_principal_compare(context, ent->entry.principal, orig.entry.principal)477== FALSE)478{479if (is_heimdal_principal || is_heimdal_entry) {480481ret = krb5_unparse_name(context, ent->entry.principal, &tmp);482if (ret)483goto out;484485ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE,486"krb5PrincipalName", tmp);487if (ret) {488free(tmp);489goto out;490}491free(tmp);492}493494if (is_account || is_samba_account) {495ret = krb5_unparse_name_short(context, ent->entry.principal, &tmp);496if (ret)497goto out;498ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "uid", tmp);499if (ret) {500free(tmp);501goto out;502}503free(tmp);504}505}506507if (is_heimdal_entry && (ent->entry.kvno != orig.entry.kvno || is_new_entry)) {508ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,509"krb5KeyVersionNumber",510ent->entry.kvno);511if (ret)512goto out;513}514515if (is_heimdal_entry && ent->entry.valid_start) {516if (orig.entry.valid_end == NULL517|| (*(ent->entry.valid_start) != *(orig.entry.valid_start))) {518ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,519"krb5ValidStart",520ent->entry.valid_start);521if (ret)522goto out;523}524}525526if (ent->entry.valid_end) {527if (orig.entry.valid_end == NULL || (*(ent->entry.valid_end) != *(orig.entry.valid_end))) {528if (is_heimdal_entry) {529ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,530"krb5ValidEnd",531ent->entry.valid_end);532if (ret)533goto out;534}535if (is_samba_account) {536ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,537"sambaKickoffTime",538*(ent->entry.valid_end));539if (ret)540goto out;541}542}543}544545if (ent->entry.pw_end) {546if (orig.entry.pw_end == NULL || (*(ent->entry.pw_end) != *(orig.entry.pw_end))) {547if (is_heimdal_entry) {548ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE,549"krb5PasswordEnd",550ent->entry.pw_end);551if (ret)552goto out;553}554555if (is_samba_account) {556ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,557"sambaPwdMustChange",558*(ent->entry.pw_end));559if (ret)560goto out;561}562}563}564565566#if 0 /* we we have last_pw_change */567if (is_samba_account && ent->entry.last_pw_change) {568if (orig.entry.last_pw_change == NULL || (*(ent->entry.last_pw_change) != *(orig.entry.last_pw_change))) {569ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,570"sambaPwdLastSet",571*(ent->entry.last_pw_change));572if (ret)573goto out;574}575}576#endif577578if (is_heimdal_entry && ent->entry.max_life) {579if (orig.entry.max_life == NULL580|| (*(ent->entry.max_life) != *(orig.entry.max_life))) {581582ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,583"krb5MaxLife",584*(ent->entry.max_life));585if (ret)586goto out;587}588}589590if (is_heimdal_entry && ent->entry.max_renew) {591if (orig.entry.max_renew == NULL592|| (*(ent->entry.max_renew) != *(orig.entry.max_renew))) {593594ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,595"krb5MaxRenew",596*(ent->entry.max_renew));597if (ret)598goto out;599}600}601602oflags = HDBFlags2int(orig.entry.flags);603nflags = HDBFlags2int(ent->entry.flags);604605if (is_heimdal_entry && oflags != nflags) {606607ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,608"krb5KDCFlags",609nflags);610if (ret)611goto out;612}613614/* Remove keys if they exists, and then replace keys. */615if (!is_new_entry && orig.entry.keys.len > 0) {616vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key");617if (vals) {618ldap_value_free_len(vals);619620ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5Key", NULL);621if (ret)622goto out;623}624}625626for (i = 0; i < ent->entry.keys.len; i++) {627628if (is_samba_account629&& ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) {630char *ntHexPassword;631char *nt;632time_t now = time(NULL);633634/* the key might have been 'sealed', but samba passwords635are clear in the directory */636ret = hdb_unseal_key(context, db, &ent->entry.keys.val[i]);637if (ret)638goto out;639640nt = ent->entry.keys.val[i].key.keyvalue.data;641/* store in ntPassword, not krb5key */642ret = hex_encode(nt, 16, &ntHexPassword);643if (ret < 0) {644ret = ENOMEM;645krb5_set_error_message(context, ret, "hdb-ldap: failed to "646"hex encode key");647goto out;648}649ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "sambaNTPassword",650ntHexPassword);651free(ntHexPassword);652if (ret)653goto out;654ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE,655"sambaPwdLastSet", now);656if (ret)657goto out;658659/* have to kill the LM passwod if it exists */660vals = ldap_get_values_len(HDB2LDAP(db), msg, "sambaLMPassword");661if (vals) {662ldap_value_free_len(vals);663ret = LDAP_addmod(&mods, LDAP_MOD_DELETE,664"sambaLMPassword", NULL);665if (ret)666goto out;667}668669} else if (is_heimdal_entry) {670unsigned char *buf;671size_t len, buf_size;672673ASN1_MALLOC_ENCODE(Key, buf, buf_size, &ent->entry.keys.val[i], &len, ret);674if (ret)675goto out;676if(buf_size != len)677krb5_abortx(context, "internal error in ASN.1 encoder");678679/* addmod_len _owns_ the key, doesn't need to copy it */680ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5Key", buf, len);681if (ret)682goto out;683}684}685686if (ent->entry.etypes) {687int add_krb5EncryptionType = 0;688689/*690* Only add/modify krb5EncryptionType if it's a new heimdal691* entry or krb5EncryptionType already exists on the entry.692*/693694if (!is_new_entry) {695vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5EncryptionType");696if (vals) {697ldap_value_free_len(vals);698ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5EncryptionType",699NULL);700if (ret)701goto out;702add_krb5EncryptionType = 1;703}704} else if (is_heimdal_entry)705add_krb5EncryptionType = 1;706707if (add_krb5EncryptionType) {708for (i = 0; i < ent->entry.etypes->len; i++) {709if (is_samba_account &&710ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5)711{712;713} else if (is_heimdal_entry) {714ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_ADD,715"krb5EncryptionType",716ent->entry.etypes->val[i]);717if (ret)718goto out;719}720}721}722}723724/* for clarity */725ret = 0;726727out:728729if (ret == 0)730*pmods = mods;731else if (mods != NULL) {732ldap_mods_free(mods, 1);733*pmods = NULL;734}735736if (msg)737hdb_free_entry(context, &orig);738739return ret;740}741742static krb5_error_code743LDAP_dn2principal(krb5_context context, HDB * db, const char *dn,744krb5_principal * principal)745{746krb5_error_code ret;747int rc;748const char *filter = "(objectClass=krb5Principal)";749LDAPMessage *res = NULL, *e;750char *p;751752ret = LDAP_no_size_limit(context, HDB2LDAP(db));753if (ret)754goto out;755756rc = ldap_search_ext_s(HDB2LDAP(db), dn, LDAP_SCOPE_SUBTREE,757filter, krb5principal_attrs, 0,758NULL, NULL, NULL,7590, &res);760if (check_ldap(context, db, rc)) {761ret = HDB_ERR_NOENTRY;762krb5_set_error_message(context, ret, "ldap_search_ext_s: "763"filter: %s error: %s",764filter, ldap_err2string(rc));765goto out;766}767768e = ldap_first_entry(HDB2LDAP(db), res);769if (e == NULL) {770ret = HDB_ERR_NOENTRY;771goto out;772}773774ret = LDAP_get_string_value(db, e, "krb5PrincipalName", &p);775if (ret) {776ret = HDB_ERR_NOENTRY;777goto out;778}779780ret = krb5_parse_name(context, p, principal);781free(p);782783out:784if (res)785ldap_msgfree(res);786787return ret;788}789790static int791need_quote(unsigned char c)792{793return (c & 0x80) ||794(c < 32) ||795(c == '(') ||796(c == ')') ||797(c == '*') ||798(c == '\\') ||799(c == 0x7f);800}801802static const char hexchar[] = "0123456789ABCDEF";803804static krb5_error_code805escape_value(krb5_context context, const char *unquoted, char **quoted)806{807size_t i, len;808809for (i = 0, len = 0; unquoted[i] != '\0'; i++, len++) {810if (need_quote((unsigned char)unquoted[i]))811len += 2;812}813814*quoted = malloc(len + 1);815if (*quoted == NULL) {816krb5_set_error_message(context, ENOMEM, "malloc: out of memory");817return ENOMEM;818}819820for (i = 0; unquoted[0] ; unquoted++) {821if (need_quote((unsigned char)unquoted[0])) {822(*quoted)[i++] = '\\';823(*quoted)[i++] = hexchar[(unquoted[0] >> 4) & 0xf];824(*quoted)[i++] = hexchar[(unquoted[0] ) & 0xf];825} else826(*quoted)[i++] = (char)unquoted[0];827}828(*quoted)[i] = '\0';829return 0;830}831832833static krb5_error_code834LDAP__lookup_princ(krb5_context context,835HDB *db,836const char *princname,837const char *userid,838LDAPMessage **msg)839{840krb5_error_code ret;841int rc;842char *quote, *filter = NULL;843844ret = LDAP__connect(context, db);845if (ret)846return ret;847848/*849* Quote searches that contain filter language, this quote850* searches for *@REALM, which takes very long time.851*/852853ret = escape_value(context, princname, "e);854if (ret)855goto out;856857rc = asprintf(&filter,858"(&(objectClass=krb5Principal)(krb5PrincipalName=%s))",859quote);860free(quote);861862if (rc < 0) {863ret = ENOMEM;864krb5_set_error_message(context, ret, "malloc: out of memory");865goto out;866}867868ret = LDAP_no_size_limit(context, HDB2LDAP(db));869if (ret)870goto out;871872rc = ldap_search_ext_s(HDB2LDAP(db), HDB2BASE(db),873LDAP_SCOPE_SUBTREE, filter,874krb5kdcentry_attrs, 0,875NULL, NULL, NULL,8760, msg);877if (check_ldap(context, db, rc)) {878ret = HDB_ERR_NOENTRY;879krb5_set_error_message(context, ret, "ldap_search_ext_s: "880"filter: %s - error: %s",881filter, ldap_err2string(rc));882goto out;883}884885if (userid && ldap_count_entries(HDB2LDAP(db), *msg) == 0) {886free(filter);887filter = NULL;888ldap_msgfree(*msg);889*msg = NULL;890891ret = escape_value(context, userid, "e);892if (ret)893goto out;894895rc = asprintf(&filter,896"(&(|(objectClass=sambaSamAccount)(objectClass=%s))(uid=%s))",897structural_object, quote);898free(quote);899if (rc < 0) {900ret = ENOMEM;901krb5_set_error_message(context, ret, "asprintf: out of memory");902goto out;903}904905ret = LDAP_no_size_limit(context, HDB2LDAP(db));906if (ret)907goto out;908909rc = ldap_search_ext_s(HDB2LDAP(db), HDB2BASE(db), LDAP_SCOPE_SUBTREE,910filter, krb5kdcentry_attrs, 0,911NULL, NULL, NULL,9120, msg);913if (check_ldap(context, db, rc)) {914ret = HDB_ERR_NOENTRY;915krb5_set_error_message(context, ret,916"ldap_search_ext_s: filter: %s error: %s",917filter, ldap_err2string(rc));918goto out;919}920}921922ret = 0;923924out:925if (filter)926free(filter);927928return ret;929}930931static krb5_error_code932LDAP_principal2message(krb5_context context, HDB * db,933krb5_const_principal princ, LDAPMessage ** msg)934{935char *name, *name_short = NULL;936krb5_error_code ret;937krb5_realm *r, *r0;938939*msg = NULL;940941ret = krb5_unparse_name(context, princ, &name);942if (ret)943return ret;944945ret = krb5_get_default_realms(context, &r0);946if(ret) {947free(name);948return ret;949}950for (r = r0; *r != NULL; r++) {951if(strcmp(krb5_principal_get_realm(context, princ), *r) == 0) {952ret = krb5_unparse_name_short(context, princ, &name_short);953if (ret) {954krb5_free_host_realm(context, r0);955free(name);956return ret;957}958break;959}960}961krb5_free_host_realm(context, r0);962963ret = LDAP__lookup_princ(context, db, name, name_short, msg);964free(name);965free(name_short);966967return ret;968}969970/*971* Construct an hdb_entry from a directory entry.972*/973static krb5_error_code974LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,975int flags, hdb_entry_ex * ent)976{977char *unparsed_name = NULL, *dn = NULL, *ntPasswordIN = NULL;978char *samba_acct_flags = NULL;979struct berval **keys;980struct berval **vals;981int tmp, tmp_time, i, ret, have_arcfour = 0;982983memset(ent, 0, sizeof(*ent));984ent->entry.flags = int2HDBFlags(0);985986ret = LDAP_get_string_value(db, msg, "krb5PrincipalName", &unparsed_name);987if (ret == 0) {988ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal);989if (ret)990goto out;991} else {992ret = LDAP_get_string_value(db, msg, "uid",993&unparsed_name);994if (ret == 0) {995ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal);996if (ret)997goto out;998} else {999krb5_set_error_message(context, HDB_ERR_NOENTRY,1000"hdb-ldap: ldap entry missing"1001"principal name");1002return HDB_ERR_NOENTRY;1003}1004}10051006{1007int integer;1008ret = LDAP_get_integer_value(db, msg, "krb5KeyVersionNumber",1009&integer);1010if (ret)1011ent->entry.kvno = 0;1012else1013ent->entry.kvno = integer;1014}10151016keys = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key");1017if (keys != NULL) {1018int i;1019size_t l;10201021ent->entry.keys.len = ldap_count_values_len(keys);1022ent->entry.keys.val = (Key *) calloc(ent->entry.keys.len, sizeof(Key));1023if (ent->entry.keys.val == NULL) {1024ret = ENOMEM;1025krb5_set_error_message(context, ret, "calloc: out of memory");1026goto out;1027}1028for (i = 0; i < ent->entry.keys.len; i++) {1029decode_Key((unsigned char *) keys[i]->bv_val,1030(size_t) keys[i]->bv_len, &ent->entry.keys.val[i], &l);1031}1032ber_bvecfree(keys);1033} else {1034#if 11035/*1036* This violates the ASN1 but it allows a principal to1037* be related to a general directory entry without creating1038* the keys. Hopefully it's OK.1039*/1040ent->entry.keys.len = 0;1041ent->entry.keys.val = NULL;1042#else1043ret = HDB_ERR_NOENTRY;1044goto out;1045#endif1046}10471048vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5EncryptionType");1049if (vals != NULL) {1050int i;10511052ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes)));1053if (ent->entry.etypes == NULL) {1054ret = ENOMEM;1055krb5_set_error_message(context, ret,"malloc: out of memory");1056goto out;1057}1058ent->entry.etypes->len = ldap_count_values_len(vals);1059ent->entry.etypes->val = calloc(ent->entry.etypes->len, sizeof(int));1060if (ent->entry.etypes->val == NULL) {1061ret = ENOMEM;1062krb5_set_error_message(context, ret, "malloc: out of memory");1063ent->entry.etypes->len = 0;1064goto out;1065}1066for (i = 0; i < ent->entry.etypes->len; i++) {1067char *buf;10681069buf = malloc(vals[i]->bv_len + 1);1070if (buf == NULL) {1071ret = ENOMEM;1072krb5_set_error_message(context, ret, "malloc: out of memory");1073goto out;1074}1075memcpy(buf, vals[i]->bv_val, vals[i]->bv_len);1076buf[vals[i]->bv_len] = '\0';1077ent->entry.etypes->val[i] = atoi(buf);1078free(buf);1079}1080ldap_value_free_len(vals);1081}10821083for (i = 0; i < ent->entry.keys.len; i++) {1084if (ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) {1085have_arcfour = 1;1086break;1087}1088}10891090/* manually construct the NT (type 23) key */1091ret = LDAP_get_string_value(db, msg, "sambaNTPassword", &ntPasswordIN);1092if (ret == 0 && have_arcfour == 0) {1093unsigned *etypes;1094Key *keys;1095int i;10961097keys = realloc(ent->entry.keys.val,1098(ent->entry.keys.len + 1) * sizeof(ent->entry.keys.val[0]));1099if (keys == NULL) {1100free(ntPasswordIN);1101ret = ENOMEM;1102krb5_set_error_message(context, ret, "malloc: out of memory");1103goto out;1104}1105ent->entry.keys.val = keys;1106memset(&ent->entry.keys.val[ent->entry.keys.len], 0, sizeof(Key));1107ent->entry.keys.val[ent->entry.keys.len].key.keytype = ETYPE_ARCFOUR_HMAC_MD5;1108ret = krb5_data_alloc (&ent->entry.keys.val[ent->entry.keys.len].key.keyvalue, 16);1109if (ret) {1110krb5_set_error_message(context, ret, "malloc: out of memory");1111free(ntPasswordIN);1112ret = ENOMEM;1113goto out;1114}1115ret = hex_decode(ntPasswordIN,1116ent->entry.keys.val[ent->entry.keys.len].key.keyvalue.data, 16);1117ent->entry.keys.len++;11181119if (ent->entry.etypes == NULL) {1120ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes)));1121if (ent->entry.etypes == NULL) {1122ret = ENOMEM;1123krb5_set_error_message(context, ret, "malloc: out of memory");1124goto out;1125}1126ent->entry.etypes->val = NULL;1127ent->entry.etypes->len = 0;1128}11291130for (i = 0; i < ent->entry.etypes->len; i++)1131if (ent->entry.etypes->val[i] == ETYPE_ARCFOUR_HMAC_MD5)1132break;1133/* If there is no ARCFOUR enctype, add one */1134if (i == ent->entry.etypes->len) {1135etypes = realloc(ent->entry.etypes->val,1136(ent->entry.etypes->len + 1) *1137sizeof(ent->entry.etypes->val[0]));1138if (etypes == NULL) {1139ret = ENOMEM;1140krb5_set_error_message(context, ret, "malloc: out of memory");1141goto out;1142}1143ent->entry.etypes->val = etypes;1144ent->entry.etypes->val[ent->entry.etypes->len] =1145ETYPE_ARCFOUR_HMAC_MD5;1146ent->entry.etypes->len++;1147}1148}11491150ret = LDAP_get_generalized_time_value(db, msg, "createTimestamp",1151&ent->entry.created_by.time);1152if (ret)1153ent->entry.created_by.time = time(NULL);11541155ent->entry.created_by.principal = NULL;11561157if (flags & HDB_F_ADMIN_DATA) {1158ret = LDAP_get_string_value(db, msg, "creatorsName", &dn);1159if (ret == 0) {1160LDAP_dn2principal(context, db, dn, &ent->entry.created_by.principal);1161free(dn);1162}11631164ent->entry.modified_by = calloc(1, sizeof(*ent->entry.modified_by));1165if (ent->entry.modified_by == NULL) {1166ret = ENOMEM;1167krb5_set_error_message(context, ret, "malloc: out of memory");1168goto out;1169}11701171ret = LDAP_get_generalized_time_value(db, msg, "modifyTimestamp",1172&ent->entry.modified_by->time);1173if (ret == 0) {1174ret = LDAP_get_string_value(db, msg, "modifiersName", &dn);1175if (ret == 0) {1176LDAP_dn2principal(context, db, dn, &ent->entry.modified_by->principal);1177free(dn);1178} else {1179free(ent->entry.modified_by);1180ent->entry.modified_by = NULL;1181}1182}1183}11841185ent->entry.valid_start = malloc(sizeof(*ent->entry.valid_start));1186if (ent->entry.valid_start == NULL) {1187ret = ENOMEM;1188krb5_set_error_message(context, ret, "malloc: out of memory");1189goto out;1190}1191ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidStart",1192ent->entry.valid_start);1193if (ret) {1194/* OPTIONAL */1195free(ent->entry.valid_start);1196ent->entry.valid_start = NULL;1197}11981199ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end));1200if (ent->entry.valid_end == NULL) {1201ret = ENOMEM;1202krb5_set_error_message(context, ret, "malloc: out of memory");1203goto out;1204}1205ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidEnd",1206ent->entry.valid_end);1207if (ret) {1208/* OPTIONAL */1209free(ent->entry.valid_end);1210ent->entry.valid_end = NULL;1211}12121213ret = LDAP_get_integer_value(db, msg, "sambaKickoffTime", &tmp_time);1214if (ret == 0) {1215if (ent->entry.valid_end == NULL) {1216ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end));1217if (ent->entry.valid_end == NULL) {1218ret = ENOMEM;1219krb5_set_error_message(context, ret, "malloc: out of memory");1220goto out;1221}1222}1223*ent->entry.valid_end = tmp_time;1224}12251226ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));1227if (ent->entry.pw_end == NULL) {1228ret = ENOMEM;1229krb5_set_error_message(context, ret, "malloc: out of memory");1230goto out;1231}1232ret = LDAP_get_generalized_time_value(db, msg, "krb5PasswordEnd",1233ent->entry.pw_end);1234if (ret) {1235/* OPTIONAL */1236free(ent->entry.pw_end);1237ent->entry.pw_end = NULL;1238}12391240ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time);1241if (ret == 0) {1242time_t delta;12431244if (ent->entry.pw_end == NULL) {1245ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));1246if (ent->entry.pw_end == NULL) {1247ret = ENOMEM;1248krb5_set_error_message(context, ret, "malloc: out of memory");1249goto out;1250}1251}12521253delta = krb5_config_get_time_default(context, NULL,1254365 * 24 * 60 * 60,1255"kadmin",1256"password_lifetime",1257NULL);1258*ent->entry.pw_end = tmp_time + delta;1259}12601261ret = LDAP_get_integer_value(db, msg, "sambaPwdMustChange", &tmp_time);1262if (ret == 0) {1263if (ent->entry.pw_end == NULL) {1264ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));1265if (ent->entry.pw_end == NULL) {1266ret = ENOMEM;1267krb5_set_error_message(context, ret, "malloc: out of memory");1268goto out;1269}1270}1271*ent->entry.pw_end = tmp_time;1272}12731274/* OPTIONAL */1275ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time);1276if (ret == 0)1277hdb_entry_set_pw_change_time(context, &ent->entry, tmp_time);12781279{1280int max_life;12811282ent->entry.max_life = malloc(sizeof(*ent->entry.max_life));1283if (ent->entry.max_life == NULL) {1284ret = ENOMEM;1285krb5_set_error_message(context, ret, "malloc: out of memory");1286goto out;1287}1288ret = LDAP_get_integer_value(db, msg, "krb5MaxLife", &max_life);1289if (ret) {1290free(ent->entry.max_life);1291ent->entry.max_life = NULL;1292} else1293*ent->entry.max_life = max_life;1294}12951296{1297int max_renew;12981299ent->entry.max_renew = malloc(sizeof(*ent->entry.max_renew));1300if (ent->entry.max_renew == NULL) {1301ret = ENOMEM;1302krb5_set_error_message(context, ret, "malloc: out of memory");1303goto out;1304}1305ret = LDAP_get_integer_value(db, msg, "krb5MaxRenew", &max_renew);1306if (ret) {1307free(ent->entry.max_renew);1308ent->entry.max_renew = NULL;1309} else1310*ent->entry.max_renew = max_renew;1311}13121313ret = LDAP_get_integer_value(db, msg, "krb5KDCFlags", &tmp);1314if (ret)1315tmp = 0;13161317ent->entry.flags = int2HDBFlags(tmp);13181319/* Try and find Samba flags to put into the mix */1320ret = LDAP_get_string_value(db, msg, "sambaAcctFlags", &samba_acct_flags);1321if (ret == 0) {1322/* parse the [UXW...] string:13231324'N' No password1325'D' Disabled1326'H' Homedir required1327'T' Temp account.1328'U' User account (normal)1329'M' MNS logon user account - what is this ?1330'W' Workstation account1331'S' Server account1332'L' Locked account1333'X' No Xpiry on password1334'I' Interdomain trust account13351336*/13371338int i;1339int flags_len = strlen(samba_acct_flags);13401341if (flags_len < 2)1342goto out2;13431344if (samba_acct_flags[0] != '['1345|| samba_acct_flags[flags_len - 1] != ']')1346goto out2;13471348/* Allow forwarding */1349if (samba_forwardable)1350ent->entry.flags.forwardable = TRUE;13511352for (i=0; i < flags_len; i++) {1353switch (samba_acct_flags[i]) {1354case ' ':1355case '[':1356case ']':1357break;1358case 'N':1359/* how to handle no password in kerberos? */1360break;1361case 'D':1362ent->entry.flags.invalid = TRUE;1363break;1364case 'H':1365break;1366case 'T':1367/* temp duplicate */1368ent->entry.flags.invalid = TRUE;1369break;1370case 'U':1371ent->entry.flags.client = TRUE;1372break;1373case 'M':1374break;1375case 'W':1376case 'S':1377ent->entry.flags.server = TRUE;1378ent->entry.flags.client = TRUE;1379break;1380case 'L':1381ent->entry.flags.invalid = TRUE;1382break;1383case 'X':1384if (ent->entry.pw_end) {1385free(ent->entry.pw_end);1386ent->entry.pw_end = NULL;1387}1388break;1389case 'I':1390ent->entry.flags.server = TRUE;1391ent->entry.flags.client = TRUE;1392break;1393}1394}1395out2:1396free(samba_acct_flags);1397}13981399ret = 0;14001401out:1402if (unparsed_name)1403free(unparsed_name);14041405if (ret)1406hdb_free_entry(context, ent);14071408return ret;1409}14101411static krb5_error_code1412LDAP_close(krb5_context context, HDB * db)1413{1414if (HDB2LDAP(db)) {1415ldap_unbind_ext(HDB2LDAP(db), NULL, NULL);1416((struct hdbldapdb *)db->hdb_db)->h_lp = NULL;1417}14181419return 0;1420}14211422static krb5_error_code1423LDAP_lock(krb5_context context, HDB * db, int operation)1424{1425return 0;1426}14271428static krb5_error_code1429LDAP_unlock(krb5_context context, HDB * db)1430{1431return 0;1432}14331434static krb5_error_code1435LDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry_ex * entry)1436{1437int msgid, rc, parserc;1438krb5_error_code ret;1439LDAPMessage *e;14401441msgid = HDB2MSGID(db);1442if (msgid < 0)1443return HDB_ERR_NOENTRY;14441445do {1446rc = ldap_result(HDB2LDAP(db), msgid, LDAP_MSG_ONE, NULL, &e);1447switch (rc) {1448case LDAP_RES_SEARCH_REFERENCE:1449ldap_msgfree(e);1450ret = 0;1451break;1452case LDAP_RES_SEARCH_ENTRY:1453/* We have an entry. Parse it. */1454ret = LDAP_message2entry(context, db, e, flags, entry);1455ldap_msgfree(e);1456break;1457case LDAP_RES_SEARCH_RESULT:1458/* We're probably at the end of the results. If not, abandon. */1459parserc =1460ldap_parse_result(HDB2LDAP(db), e, NULL, NULL, NULL,1461NULL, NULL, 1);1462ret = HDB_ERR_NOENTRY;1463if (parserc != LDAP_SUCCESS1464&& parserc != LDAP_MORE_RESULTS_TO_RETURN) {1465krb5_set_error_message(context, ret, "ldap_parse_result: %s",1466ldap_err2string(parserc));1467ldap_abandon_ext(HDB2LDAP(db), msgid, NULL, NULL);1468}1469HDBSETMSGID(db, -1);1470break;1471case LDAP_SERVER_DOWN:1472ldap_msgfree(e);1473LDAP_close(context, db);1474HDBSETMSGID(db, -1);1475ret = ENETDOWN;1476break;1477default:1478/* Some unspecified error (timeout?). Abandon. */1479ldap_msgfree(e);1480ldap_abandon_ext(HDB2LDAP(db), msgid, NULL, NULL);1481ret = HDB_ERR_NOENTRY;1482HDBSETMSGID(db, -1);1483break;1484}1485} while (rc == LDAP_RES_SEARCH_REFERENCE);14861487if (ret == 0) {1488if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {1489ret = hdb_unseal_keys(context, db, &entry->entry);1490if (ret)1491hdb_free_entry(context, entry);1492}1493}14941495return ret;1496}14971498static krb5_error_code1499LDAP_firstkey(krb5_context context, HDB *db, unsigned flags,1500hdb_entry_ex *entry)1501{1502krb5_error_code ret;1503int msgid;15041505ret = LDAP__connect(context, db);1506if (ret)1507return ret;15081509ret = LDAP_no_size_limit(context, HDB2LDAP(db));1510if (ret)1511return ret;15121513ret = ldap_search_ext(HDB2LDAP(db), HDB2BASE(db),1514LDAP_SCOPE_SUBTREE,1515"(|(objectClass=krb5Principal)(objectClass=sambaSamAccount))",1516krb5kdcentry_attrs, 0,1517NULL, NULL, NULL, 0, &msgid);1518if (msgid < 0)1519return HDB_ERR_NOENTRY;15201521HDBSETMSGID(db, msgid);15221523return LDAP_seq(context, db, flags, entry);1524}15251526static krb5_error_code1527LDAP_nextkey(krb5_context context, HDB * db, unsigned flags,1528hdb_entry_ex * entry)1529{1530return LDAP_seq(context, db, flags, entry);1531}15321533static krb5_error_code1534LDAP__connect(krb5_context context, HDB * db)1535{1536int rc, version = LDAP_VERSION3;1537/*1538* Empty credentials to do a SASL bind with LDAP. Note that empty1539* different from NULL credentials. If you provide NULL1540* credentials instead of empty credentials you will get a SASL1541* bind in progress message.1542*/1543struct berval bv = { 0, "" };15441545if (HDB2LDAP(db)) {1546/* connection has been opened. ping server. */1547struct sockaddr_un addr;1548socklen_t len = sizeof(addr);1549int sd;15501551if (ldap_get_option(HDB2LDAP(db), LDAP_OPT_DESC, &sd) == 0 &&1552getpeername(sd, (struct sockaddr *) &addr, &len) < 0) {1553/* the other end has died. reopen. */1554LDAP_close(context, db);1555}1556}15571558if (HDB2LDAP(db) != NULL) /* server is UP */1559return 0;15601561rc = ldap_initialize(&((struct hdbldapdb *)db->hdb_db)->h_lp, HDB2URL(db));1562if (rc != LDAP_SUCCESS) {1563krb5_set_error_message(context, HDB_ERR_NOENTRY, "ldap_initialize: %s",1564ldap_err2string(rc));1565return HDB_ERR_NOENTRY;1566}15671568rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_PROTOCOL_VERSION,1569(const void *)&version);1570if (rc != LDAP_SUCCESS) {1571krb5_set_error_message(context, HDB_ERR_BADVERSION,1572"ldap_set_option: %s", ldap_err2string(rc));1573LDAP_close(context, db);1574return HDB_ERR_BADVERSION;1575}15761577rc = ldap_sasl_bind_s(HDB2LDAP(db), NULL, "EXTERNAL", &bv,1578NULL, NULL, NULL);1579if (rc != LDAP_SUCCESS) {1580krb5_set_error_message(context, HDB_ERR_BADVERSION,1581"ldap_sasl_bind_s: %s", ldap_err2string(rc));1582LDAP_close(context, db);1583return HDB_ERR_BADVERSION;1584}15851586return 0;1587}15881589static krb5_error_code1590LDAP_open(krb5_context context, HDB * db, int flags, mode_t mode)1591{1592/* Not the right place for this. */1593#ifdef HAVE_SIGACTION1594struct sigaction sa;15951596sa.sa_flags = 0;1597sa.sa_handler = SIG_IGN;1598sigemptyset(&sa.sa_mask);15991600sigaction(SIGPIPE, &sa, NULL);1601#else1602signal(SIGPIPE, SIG_IGN);1603#endif /* HAVE_SIGACTION */16041605return LDAP__connect(context, db);1606}16071608static krb5_error_code1609LDAP_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal,1610unsigned flags, krb5_kvno kvno, hdb_entry_ex * entry)1611{1612LDAPMessage *msg, *e;1613krb5_error_code ret;16141615ret = LDAP_principal2message(context, db, principal, &msg);1616if (ret)1617return ret;16181619e = ldap_first_entry(HDB2LDAP(db), msg);1620if (e == NULL) {1621ret = HDB_ERR_NOENTRY;1622goto out;1623}16241625ret = LDAP_message2entry(context, db, e, flags, entry);1626if (ret == 0) {1627if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {1628ret = hdb_unseal_keys(context, db, &entry->entry);1629if (ret)1630hdb_free_entry(context, entry);1631}1632}16331634out:1635ldap_msgfree(msg);16361637return ret;1638}16391640static krb5_error_code1641LDAP_fetch(krb5_context context, HDB * db, krb5_const_principal principal,1642unsigned flags, hdb_entry_ex * entry)1643{1644return LDAP_fetch_kvno(context, db, principal,1645flags & (~HDB_F_KVNO_SPECIFIED), 0, entry);1646}16471648static krb5_error_code1649LDAP_store(krb5_context context, HDB * db, unsigned flags,1650hdb_entry_ex * entry)1651{1652LDAPMod **mods = NULL;1653krb5_error_code ret;1654const char *errfn;1655int rc;1656LDAPMessage *msg = NULL, *e = NULL;1657char *dn = NULL, *name = NULL;16581659ret = LDAP_principal2message(context, db, entry->entry.principal, &msg);1660if (ret == 0)1661e = ldap_first_entry(HDB2LDAP(db), msg);16621663ret = krb5_unparse_name(context, entry->entry.principal, &name);1664if (ret) {1665free(name);1666return ret;1667}16681669ret = hdb_seal_keys(context, db, &entry->entry);1670if (ret)1671goto out;16721673/* turn new entry into LDAPMod array */1674ret = LDAP_entry2mods(context, db, entry, e, &mods);1675if (ret)1676goto out;16771678if (e == NULL) {1679ret = asprintf(&dn, "krb5PrincipalName=%s,%s", name, HDB2CREATE(db));1680if (ret < 0) {1681ret = ENOMEM;1682krb5_set_error_message(context, ret, "asprintf: out of memory");1683goto out;1684}1685} else if (flags & HDB_F_REPLACE) {1686/* Entry exists, and we're allowed to replace it. */1687dn = ldap_get_dn(HDB2LDAP(db), e);1688} else {1689/* Entry exists, but we're not allowed to replace it. Bail. */1690ret = HDB_ERR_EXISTS;1691goto out;1692}16931694/* write entry into directory */1695if (e == NULL) {1696/* didn't exist before */1697rc = ldap_add_ext_s(HDB2LDAP(db), dn, mods, NULL, NULL );1698errfn = "ldap_add_ext_s";1699} else {1700/* already existed, send deltas only */1701rc = ldap_modify_ext_s(HDB2LDAP(db), dn, mods, NULL, NULL );1702errfn = "ldap_modify_ext_s";1703}17041705if (check_ldap(context, db, rc)) {1706char *ld_error = NULL;1707ldap_get_option(HDB2LDAP(db), LDAP_OPT_ERROR_STRING,1708&ld_error);1709ret = HDB_ERR_CANT_LOCK_DB;1710krb5_set_error_message(context, ret, "%s: %s (DN=%s) %s: %s",1711errfn, name, dn, ldap_err2string(rc), ld_error);1712} else1713ret = 0;17141715out:1716/* free stuff */1717if (dn)1718free(dn);1719if (msg)1720ldap_msgfree(msg);1721if (mods)1722ldap_mods_free(mods, 1);1723if (name)1724free(name);17251726return ret;1727}17281729static krb5_error_code1730LDAP_remove(krb5_context context, HDB *db, krb5_const_principal principal)1731{1732krb5_error_code ret;1733LDAPMessage *msg, *e;1734char *dn = NULL;1735int rc, limit = LDAP_NO_LIMIT;17361737ret = LDAP_principal2message(context, db, principal, &msg);1738if (ret)1739goto out;17401741e = ldap_first_entry(HDB2LDAP(db), msg);1742if (e == NULL) {1743ret = HDB_ERR_NOENTRY;1744goto out;1745}17461747dn = ldap_get_dn(HDB2LDAP(db), e);1748if (dn == NULL) {1749ret = HDB_ERR_NOENTRY;1750goto out;1751}17521753rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_SIZELIMIT, (const void *)&limit);1754if (rc != LDAP_SUCCESS) {1755ret = HDB_ERR_BADVERSION;1756krb5_set_error_message(context, ret, "ldap_set_option: %s",1757ldap_err2string(rc));1758goto out;1759}17601761rc = ldap_delete_ext_s(HDB2LDAP(db), dn, NULL, NULL );1762if (check_ldap(context, db, rc)) {1763ret = HDB_ERR_CANT_LOCK_DB;1764krb5_set_error_message(context, ret, "ldap_delete_ext_s: %s",1765ldap_err2string(rc));1766} else1767ret = 0;17681769out:1770if (dn != NULL)1771free(dn);1772if (msg != NULL)1773ldap_msgfree(msg);17741775return ret;1776}17771778static krb5_error_code1779LDAP_destroy(krb5_context context, HDB * db)1780{1781krb5_error_code ret;17821783LDAP_close(context, db);17841785ret = hdb_clear_master_key(context, db);1786if (HDB2BASE(db))1787free(HDB2BASE(db));1788if (HDB2CREATE(db))1789free(HDB2CREATE(db));1790if (HDB2URL(db))1791free(HDB2URL(db));1792if (db->hdb_name)1793free(db->hdb_name);1794free(db->hdb_db);1795free(db);17961797return ret;1798}17991800static krb5_error_code1801hdb_ldap_common(krb5_context context,1802HDB ** db,1803const char *search_base,1804const char *url)1805{1806struct hdbldapdb *h;1807const char *create_base = NULL;18081809if (search_base == NULL && search_base[0] == '\0') {1810krb5_set_error_message(context, ENOMEM, "ldap search base not configured");1811return ENOMEM; /* XXX */1812}18131814if (structural_object == NULL) {1815const char *p;18161817p = krb5_config_get_string(context, NULL, "kdc",1818"hdb-ldap-structural-object", NULL);1819if (p == NULL)1820p = default_structural_object;1821structural_object = strdup(p);1822if (structural_object == NULL) {1823krb5_set_error_message(context, ENOMEM, "malloc: out of memory");1824return ENOMEM;1825}1826}18271828samba_forwardable =1829krb5_config_get_bool_default(context, NULL, TRUE,1830"kdc", "hdb-samba-forwardable", NULL);18311832*db = calloc(1, sizeof(**db));1833if (*db == NULL) {1834krb5_set_error_message(context, ENOMEM, "malloc: out of memory");1835return ENOMEM;1836}1837memset(*db, 0, sizeof(**db));18381839h = calloc(1, sizeof(*h));1840if (h == NULL) {1841free(*db);1842*db = NULL;1843krb5_set_error_message(context, ENOMEM, "malloc: out of memory");1844return ENOMEM;1845}1846(*db)->hdb_db = h;18471848/* XXX */1849if (asprintf(&(*db)->hdb_name, "ldap:%s", search_base) == -1) {1850LDAP_destroy(context, *db);1851*db = NULL;1852krb5_set_error_message(context, ENOMEM, "strdup: out of memory");1853return ENOMEM;1854}18551856h->h_url = strdup(url);1857h->h_base = strdup(search_base);1858if (h->h_url == NULL || h->h_base == NULL) {1859LDAP_destroy(context, *db);1860*db = NULL;1861krb5_set_error_message(context, ENOMEM, "strdup: out of memory");1862return ENOMEM;1863}18641865create_base = krb5_config_get_string(context, NULL, "kdc",1866"hdb-ldap-create-base", NULL);1867if (create_base == NULL)1868create_base = h->h_base;18691870h->h_createbase = strdup(create_base);1871if (h->h_createbase == NULL) {1872LDAP_destroy(context, *db);1873*db = NULL;1874krb5_set_error_message(context, ENOMEM, "strdup: out of memory");1875return ENOMEM;1876}18771878(*db)->hdb_master_key_set = 0;1879(*db)->hdb_openp = 0;1880(*db)->hdb_capability_flags = 0;1881(*db)->hdb_open = LDAP_open;1882(*db)->hdb_close = LDAP_close;1883(*db)->hdb_fetch_kvno = LDAP_fetch_kvno;1884(*db)->hdb_store = LDAP_store;1885(*db)->hdb_remove = LDAP_remove;1886(*db)->hdb_firstkey = LDAP_firstkey;1887(*db)->hdb_nextkey = LDAP_nextkey;1888(*db)->hdb_lock = LDAP_lock;1889(*db)->hdb_unlock = LDAP_unlock;1890(*db)->hdb_rename = NULL;1891(*db)->hdb__get = NULL;1892(*db)->hdb__put = NULL;1893(*db)->hdb__del = NULL;1894(*db)->hdb_destroy = LDAP_destroy;18951896return 0;1897}18981899krb5_error_code1900hdb_ldap_create(krb5_context context, HDB ** db, const char *arg)1901{1902return hdb_ldap_common(context, db, arg, "ldapi:///");1903}19041905krb5_error_code1906hdb_ldapi_create(krb5_context context, HDB ** db, const char *arg)1907{1908krb5_error_code ret;1909char *search_base, *p;19101911asprintf(&p, "ldapi:%s", arg);1912if (p == NULL) {1913*db = NULL;1914krb5_set_error_message(context, ENOMEM, "out of memory");1915return ENOMEM;1916}1917search_base = strchr(p + strlen("ldapi://"), ':');1918if (search_base == NULL) {1919*db = NULL;1920krb5_set_error_message(context, HDB_ERR_BADVERSION,1921"search base missing");1922return HDB_ERR_BADVERSION;1923}1924*search_base = '\0';1925search_base++;19261927ret = hdb_ldap_common(context, db, search_base, p);1928free(p);1929return ret;1930}19311932#ifdef OPENLDAP_MODULE19331934struct hdb_so_method hdb_ldap_interface = {1935HDB_INTERFACE_VERSION,1936"ldap",1937hdb_ldap_create1938};19391940struct hdb_so_method hdb_ldapi_interface = {1941HDB_INTERFACE_VERSION,1942"ldapi",1943hdb_ldapi_create1944};19451946#endif19471948#endif /* OPENLDAP */194919501951