172445Sassar/* 2178825Sdfr * Copyright (c) 1999-2001, 2003, PADL Software Pty Ltd. 3178825Sdfr * Copyright (c) 2004, Andrew Bartlett. 4233294Sstas * Copyright (c) 2003 - 2008, Kungliga Tekniska H��gskolan. 572445Sassar * All rights reserved. 672445Sassar * 772445Sassar * Redistribution and use in source and binary forms, with or without 872445Sassar * modification, are permitted provided that the following conditions 972445Sassar * are met: 1072445Sassar * 1172445Sassar * 1. Redistributions of source code must retain the above copyright 1272445Sassar * notice, this list of conditions and the following disclaimer. 1372445Sassar * 1472445Sassar * 2. Redistributions in binary form must reproduce the above copyright 1572445Sassar * notice, this list of conditions and the following disclaimer in the 1672445Sassar * documentation and/or other materials provided with the distribution. 1772445Sassar * 1872445Sassar * 3. Neither the name of PADL Software nor the names of its contributors 1972445Sassar * may be used to endorse or promote products derived from this software 2072445Sassar * without specific prior written permission. 2172445Sassar * 2272445Sassar * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND 2372445Sassar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2472445Sassar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2572445Sassar * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE 2672445Sassar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2772445Sassar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2872445Sassar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2972445Sassar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3072445Sassar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3172445Sassar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3272445Sassar * SUCH DAMAGE. 3372445Sassar */ 3472445Sassar 3572445Sassar#include "hdb_locl.h" 3672445Sassar 3772445Sassar#ifdef OPENLDAP 3872445Sassar 3978527Sassar#include <lber.h> 4072445Sassar#include <ldap.h> 4172445Sassar#include <sys/un.h> 42178825Sdfr#include <hex.h> 4372445Sassar 44178825Sdfrstatic krb5_error_code LDAP__connect(krb5_context context, HDB *); 45178825Sdfrstatic krb5_error_code LDAP_close(krb5_context context, HDB *); 4672445Sassar 4772445Sassarstatic krb5_error_code 4872445SassarLDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, 49233294Sstas int flags, hdb_entry_ex * ent); 5072445Sassar 51178825Sdfrstatic const char *default_structural_object = "account"; 52178825Sdfrstatic char *structural_object; 53178825Sdfrstatic krb5_boolean samba_forwardable; 54178825Sdfr 55178825Sdfrstruct hdbldapdb { 56178825Sdfr LDAP *h_lp; 57178825Sdfr int h_msgid; 58178825Sdfr char *h_base; 59178825Sdfr char *h_url; 60178825Sdfr char *h_createbase; 61178825Sdfr}; 62178825Sdfr 63178825Sdfr#define HDB2LDAP(db) (((struct hdbldapdb *)(db)->hdb_db)->h_lp) 64178825Sdfr#define HDB2MSGID(db) (((struct hdbldapdb *)(db)->hdb_db)->h_msgid) 65178825Sdfr#define HDBSETMSGID(db,msgid) \ 66178825Sdfr do { ((struct hdbldapdb *)(db)->hdb_db)->h_msgid = msgid; } while(0) 67178825Sdfr#define HDB2BASE(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_base) 68178825Sdfr#define HDB2URL(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_url) 69178825Sdfr#define HDB2CREATE(db) (((struct hdbldapdb *)(db)->hdb_db)->h_createbase) 70178825Sdfr 71178825Sdfr/* 72178825Sdfr * 73178825Sdfr */ 74178825Sdfr 75233294Sstasstatic char * krb5kdcentry_attrs[] = { 76178825Sdfr "cn", 77178825Sdfr "createTimestamp", 78178825Sdfr "creatorsName", 79178825Sdfr "krb5EncryptionType", 80178825Sdfr "krb5KDCFlags", 81178825Sdfr "krb5Key", 82178825Sdfr "krb5KeyVersionNumber", 83178825Sdfr "krb5MaxLife", 84178825Sdfr "krb5MaxRenew", 85178825Sdfr "krb5PasswordEnd", 86178825Sdfr "krb5PrincipalName", 87178825Sdfr "krb5PrincipalRealm", 88178825Sdfr "krb5ValidEnd", 89178825Sdfr "krb5ValidStart", 90178825Sdfr "modifiersName", 91178825Sdfr "modifyTimestamp", 92178825Sdfr "objectClass", 93178825Sdfr "sambaAcctFlags", 94178825Sdfr "sambaKickoffTime", 95178825Sdfr "sambaNTPassword", 96178825Sdfr "sambaPwdLastSet", 97178825Sdfr "sambaPwdMustChange", 98178825Sdfr "uid", 9972445Sassar NULL 10072445Sassar}; 10172445Sassar 102178825Sdfrstatic char *krb5principal_attrs[] = { 103178825Sdfr "cn", 104178825Sdfr "createTimestamp", 105178825Sdfr "creatorsName", 106178825Sdfr "krb5PrincipalName", 107178825Sdfr "krb5PrincipalRealm", 108178825Sdfr "modifiersName", 109178825Sdfr "modifyTimestamp", 110178825Sdfr "objectClass", 111178825Sdfr "uid", 11272445Sassar NULL 11372445Sassar}; 11472445Sassar 115178825Sdfrstatic int 116178825SdfrLDAP_no_size_limit(krb5_context context, LDAP *lp) 117178825Sdfr{ 118178825Sdfr int ret, limit = LDAP_NO_LIMIT; 119178825Sdfr 120178825Sdfr ret = ldap_set_option(lp, LDAP_OPT_SIZELIMIT, (const void *)&limit); 121178825Sdfr if (ret != LDAP_SUCCESS) { 122233294Sstas krb5_set_error_message(context, HDB_ERR_BADVERSION, 123233294Sstas "ldap_set_option: %s", 124233294Sstas ldap_err2string(ret)); 125178825Sdfr return HDB_ERR_BADVERSION; 126178825Sdfr } 127178825Sdfr return 0; 128178825Sdfr} 129178825Sdfr 130178825Sdfrstatic int 131178825Sdfrcheck_ldap(krb5_context context, HDB *db, int ret) 132178825Sdfr{ 133178825Sdfr switch (ret) { 134178825Sdfr case LDAP_SUCCESS: 135178825Sdfr return 0; 136178825Sdfr case LDAP_SERVER_DOWN: 137178825Sdfr LDAP_close(context, db); 138178825Sdfr return 1; 139178825Sdfr default: 140178825Sdfr return 1; 141178825Sdfr } 142178825Sdfr} 143178825Sdfr 14472445Sassarstatic krb5_error_code 145127808SnectarLDAP__setmod(LDAPMod *** modlist, int modop, const char *attribute, 146178825Sdfr int *pIndex) 14772445Sassar{ 148127808Snectar int cMods; 14972445Sassar 150127808Snectar if (*modlist == NULL) { 151127808Snectar *modlist = (LDAPMod **)ber_memcalloc(1, sizeof(LDAPMod *)); 152178825Sdfr if (*modlist == NULL) 15372445Sassar return ENOMEM; 15472445Sassar } 15572445Sassar 156127808Snectar for (cMods = 0; (*modlist)[cMods] != NULL; cMods++) { 157127808Snectar if ((*modlist)[cMods]->mod_op == modop && 158127808Snectar strcasecmp((*modlist)[cMods]->mod_type, attribute) == 0) { 15972445Sassar break; 16072445Sassar } 16172445Sassar } 16272445Sassar 163127808Snectar *pIndex = cMods; 164127808Snectar 165127808Snectar if ((*modlist)[cMods] == NULL) { 166127808Snectar LDAPMod *mod; 167127808Snectar 168127808Snectar *modlist = (LDAPMod **)ber_memrealloc(*modlist, 169127808Snectar (cMods + 2) * sizeof(LDAPMod *)); 170178825Sdfr if (*modlist == NULL) 17172445Sassar return ENOMEM; 172178825Sdfr 173127808Snectar (*modlist)[cMods] = (LDAPMod *)ber_memalloc(sizeof(LDAPMod)); 174178825Sdfr if ((*modlist)[cMods] == NULL) 17572445Sassar return ENOMEM; 176127808Snectar 177127808Snectar mod = (*modlist)[cMods]; 178127808Snectar mod->mod_op = modop; 179127808Snectar mod->mod_type = ber_strdup(attribute); 180127808Snectar if (mod->mod_type == NULL) { 181127808Snectar ber_memfree(mod); 182127808Snectar (*modlist)[cMods] = NULL; 18372445Sassar return ENOMEM; 18472445Sassar } 185127808Snectar 186127808Snectar if (modop & LDAP_MOD_BVALUES) { 187127808Snectar mod->mod_bvalues = NULL; 188127808Snectar } else { 189127808Snectar mod->mod_values = NULL; 190127808Snectar } 191127808Snectar 192127808Snectar (*modlist)[cMods + 1] = NULL; 19372445Sassar } 19472445Sassar 195127808Snectar return 0; 196127808Snectar} 197127808Snectar 198127808Snectarstatic krb5_error_code 199127808SnectarLDAP_addmod_len(LDAPMod *** modlist, int modop, const char *attribute, 200127808Snectar unsigned char *value, size_t len) 201127808Snectar{ 202127808Snectar krb5_error_code ret; 203178825Sdfr int cMods, i = 0; 204127808Snectar 205127808Snectar ret = LDAP__setmod(modlist, modop | LDAP_MOD_BVALUES, attribute, &cMods); 206178825Sdfr if (ret) 207127808Snectar return ret; 208127808Snectar 20972445Sassar if (value != NULL) { 210178825Sdfr struct berval **bv; 211127808Snectar 212178825Sdfr bv = (*modlist)[cMods]->mod_bvalues; 213178825Sdfr if (bv != NULL) { 214178825Sdfr for (i = 0; bv[i] != NULL; i++) 215127808Snectar ; 216178825Sdfr bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv)); 217178825Sdfr } else 218178825Sdfr bv = ber_memalloc(2 * sizeof(*bv)); 219178825Sdfr if (bv == NULL) 22072445Sassar return ENOMEM; 221178825Sdfr 222178825Sdfr (*modlist)[cMods]->mod_bvalues = bv; 223178825Sdfr 224233294Sstas bv[i] = ber_memalloc(sizeof(**bv));; 225178825Sdfr if (bv[i] == NULL) 22672445Sassar return ENOMEM; 227127808Snectar 228178825Sdfr bv[i]->bv_val = (void *)value; 229178825Sdfr bv[i]->bv_len = len; 230127808Snectar 231178825Sdfr bv[i + 1] = NULL; 23272445Sassar } 233127808Snectar 23472445Sassar return 0; 23572445Sassar} 23672445Sassar 23772445Sassarstatic krb5_error_code 23872445SassarLDAP_addmod(LDAPMod *** modlist, int modop, const char *attribute, 23972445Sassar const char *value) 24072445Sassar{ 241178825Sdfr int cMods, i = 0; 242127808Snectar krb5_error_code ret; 24372445Sassar 244127808Snectar ret = LDAP__setmod(modlist, modop, attribute, &cMods); 245178825Sdfr if (ret) 246127808Snectar return ret; 24772445Sassar 248127808Snectar if (value != NULL) { 249178825Sdfr char **bv; 25072445Sassar 251178825Sdfr bv = (*modlist)[cMods]->mod_values; 252178825Sdfr if (bv != NULL) { 253178825Sdfr for (i = 0; bv[i] != NULL; i++) 254127808Snectar ; 255178825Sdfr bv = ber_memrealloc(bv, (i + 2) * sizeof(*bv)); 256178825Sdfr } else 257178825Sdfr bv = ber_memalloc(2 * sizeof(*bv)); 258178825Sdfr if (bv == NULL) 25972445Sassar return ENOMEM; 260178825Sdfr 261178825Sdfr (*modlist)[cMods]->mod_values = bv; 262178825Sdfr 263178825Sdfr bv[i] = ber_strdup(value); 264178825Sdfr if (bv[i] == NULL) 26572445Sassar return ENOMEM; 266178825Sdfr 267178825Sdfr bv[i + 1] = NULL; 26872445Sassar } 26972445Sassar 27072445Sassar return 0; 27172445Sassar} 27272445Sassar 27372445Sassarstatic krb5_error_code 27472445SassarLDAP_addmod_generalized_time(LDAPMod *** mods, int modop, 27572445Sassar const char *attribute, KerberosTime * time) 27672445Sassar{ 27772445Sassar char buf[22]; 27872445Sassar struct tm *tm; 27972445Sassar 28072445Sassar /* XXX not threadsafe */ 28172445Sassar tm = gmtime(time); 28272445Sassar strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", tm); 28372445Sassar 28472445Sassar return LDAP_addmod(mods, modop, attribute, buf); 28572445Sassar} 28672445Sassar 28772445Sassarstatic krb5_error_code 288178825SdfrLDAP_addmod_integer(krb5_context context, 289178825Sdfr LDAPMod *** mods, int modop, 290178825Sdfr const char *attribute, unsigned long l) 291178825Sdfr{ 292178825Sdfr krb5_error_code ret; 293178825Sdfr char *buf; 294178825Sdfr 295178825Sdfr ret = asprintf(&buf, "%ld", l); 296178825Sdfr if (ret < 0) { 297233294Sstas krb5_set_error_message(context, ENOMEM, 298233294Sstas "asprintf: out of memory:"); 299233294Sstas return ENOMEM; 300178825Sdfr } 301178825Sdfr ret = LDAP_addmod(mods, modop, attribute, buf); 302178825Sdfr free (buf); 303178825Sdfr return ret; 304178825Sdfr} 305178825Sdfr 306178825Sdfrstatic krb5_error_code 30772445SassarLDAP_get_string_value(HDB * db, LDAPMessage * entry, 30872445Sassar const char *attribute, char **ptr) 30972445Sassar{ 310233294Sstas struct berval **vals; 31172445Sassar 312233294Sstas vals = ldap_get_values_len(HDB2LDAP(db), entry, attribute); 313233294Sstas if (vals == NULL || vals[0] == NULL) { 314178825Sdfr *ptr = NULL; 31572445Sassar return HDB_ERR_NOENTRY; 31672445Sassar } 317178825Sdfr 318233294Sstas *ptr = malloc(vals[0]->bv_len + 1); 319233294Sstas if (*ptr == NULL) { 320233294Sstas ldap_value_free_len(vals); 321233294Sstas return ENOMEM; 322233294Sstas } 32372445Sassar 324233294Sstas memcpy(*ptr, vals[0]->bv_val, vals[0]->bv_len); 325233294Sstas (*ptr)[vals[0]->bv_len] = 0; 32672445Sassar 327233294Sstas ldap_value_free_len(vals); 328233294Sstas 329233294Sstas return 0; 33072445Sassar} 33172445Sassar 33272445Sassarstatic krb5_error_code 33372445SassarLDAP_get_integer_value(HDB * db, LDAPMessage * entry, 33472445Sassar const char *attribute, int *ptr) 33572445Sassar{ 336233294Sstas krb5_error_code ret; 337233294Sstas char *val; 33872445Sassar 339233294Sstas ret = LDAP_get_string_value(db, entry, attribute, &val); 340233294Sstas if (ret) 341233294Sstas return ret; 342233294Sstas *ptr = atoi(val); 343233294Sstas free(val); 34472445Sassar return 0; 34572445Sassar} 34672445Sassar 34772445Sassarstatic krb5_error_code 34872445SassarLDAP_get_generalized_time_value(HDB * db, LDAPMessage * entry, 34972445Sassar const char *attribute, KerberosTime * kt) 35072445Sassar{ 35172445Sassar char *tmp, *gentime; 35272445Sassar struct tm tm; 35372445Sassar int ret; 35472445Sassar 35572445Sassar *kt = 0; 35672445Sassar 35772445Sassar ret = LDAP_get_string_value(db, entry, attribute, &gentime); 358178825Sdfr if (ret) 35972445Sassar return ret; 36072445Sassar 36172445Sassar tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm); 36272445Sassar if (tmp == NULL) { 36372445Sassar free(gentime); 36472445Sassar return HDB_ERR_NOENTRY; 36572445Sassar } 36672445Sassar 36772445Sassar free(gentime); 36872445Sassar 36972445Sassar *kt = timegm(&tm); 37072445Sassar 37172445Sassar return 0; 37272445Sassar} 37372445Sassar 374233294Sstasstatic int 375233294Sstasbervalstrcmp(struct berval *v, const char *str) 376233294Sstas{ 377233294Sstas size_t len = strlen(str); 378233294Sstas return (v->bv_len == len) && strncasecmp(str, (char *)v->bv_val, len) == 0; 379233294Sstas} 380233294Sstas 381233294Sstas 38272445Sassarstatic krb5_error_code 383178825SdfrLDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent, 38472445Sassar LDAPMessage * msg, LDAPMod *** pmods) 38572445Sassar{ 38672445Sassar krb5_error_code ret; 38772445Sassar krb5_boolean is_new_entry; 38872445Sassar char *tmp = NULL; 38972445Sassar LDAPMod **mods = NULL; 390178825Sdfr hdb_entry_ex orig; 39172445Sassar unsigned long oflags, nflags; 392178825Sdfr int i; 39372445Sassar 394178825Sdfr krb5_boolean is_samba_account = FALSE; 395178825Sdfr krb5_boolean is_account = FALSE; 396178825Sdfr krb5_boolean is_heimdal_entry = FALSE; 397178825Sdfr krb5_boolean is_heimdal_principal = FALSE; 398178825Sdfr 399233294Sstas struct berval **vals; 400178825Sdfr 401178825Sdfr *pmods = NULL; 402178825Sdfr 40372445Sassar if (msg != NULL) { 404178825Sdfr 405233294Sstas ret = LDAP_message2entry(context, db, msg, 0, &orig); 406178825Sdfr if (ret) 40772445Sassar goto out; 408178825Sdfr 409178825Sdfr is_new_entry = FALSE; 410233294Sstas 411233294Sstas vals = ldap_get_values_len(HDB2LDAP(db), msg, "objectClass"); 412233294Sstas if (vals) { 413233294Sstas int num_objectclasses = ldap_count_values_len(vals); 414178825Sdfr for (i=0; i < num_objectclasses; i++) { 415233294Sstas if (bervalstrcmp(vals[i], "sambaSamAccount")) 416178825Sdfr is_samba_account = TRUE; 417233294Sstas else if (bervalstrcmp(vals[i], structural_object)) 418178825Sdfr is_account = TRUE; 419233294Sstas else if (bervalstrcmp(vals[i], "krb5Principal")) 420178825Sdfr is_heimdal_principal = TRUE; 421233294Sstas else if (bervalstrcmp(vals[i], "krb5KDCEntry")) 422178825Sdfr is_heimdal_entry = TRUE; 423178825Sdfr } 424233294Sstas ldap_value_free_len(vals); 42572445Sassar } 426178825Sdfr 427178825Sdfr /* 428178825Sdfr * If this is just a "account" entry and no other objectclass 429178825Sdfr * is hanging on this entry, it's really a new entry. 430178825Sdfr */ 431233294Sstas if (is_samba_account == FALSE && is_heimdal_principal == FALSE && 432178825Sdfr is_heimdal_entry == FALSE) { 433178825Sdfr if (is_account == TRUE) { 434178825Sdfr is_new_entry = TRUE; 435178825Sdfr } else { 436178825Sdfr ret = HDB_ERR_NOENTRY; 437178825Sdfr goto out; 438178825Sdfr } 439178825Sdfr } 440178825Sdfr } else 441178825Sdfr is_new_entry = TRUE; 442178825Sdfr 443178825Sdfr if (is_new_entry) { 444178825Sdfr 44572445Sassar /* to make it perfectly obvious we're depending on 44672445Sassar * orig being intiialized to zero */ 44772445Sassar memset(&orig, 0, sizeof(orig)); 44872445Sassar 44972445Sassar ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "top"); 450178825Sdfr if (ret) 45172445Sassar goto out; 452233294Sstas 453178825Sdfr /* account is the structural object class */ 454178825Sdfr if (is_account == FALSE) { 455233294Sstas ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", 456178825Sdfr structural_object); 457178825Sdfr is_account = TRUE; 458178825Sdfr if (ret) 459178825Sdfr goto out; 46072445Sassar } 461178825Sdfr 462178825Sdfr ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5Principal"); 463178825Sdfr is_heimdal_principal = TRUE; 464178825Sdfr if (ret) 46572445Sassar goto out; 466178825Sdfr 467178825Sdfr ret = LDAP_addmod(&mods, LDAP_MOD_ADD, "objectClass", "krb5KDCEntry"); 468178825Sdfr is_heimdal_entry = TRUE; 469178825Sdfr if (ret) 47072445Sassar goto out; 47172445Sassar } 47272445Sassar 473233294Sstas if (is_new_entry || 474178825Sdfr krb5_principal_compare(context, ent->entry.principal, orig.entry.principal) 475178825Sdfr == FALSE) 476178825Sdfr { 477178825Sdfr if (is_heimdal_principal || is_heimdal_entry) { 478178825Sdfr 479178825Sdfr ret = krb5_unparse_name(context, ent->entry.principal, &tmp); 480178825Sdfr if (ret) 481178825Sdfr goto out; 482178825Sdfr 483178825Sdfr ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, 484178825Sdfr "krb5PrincipalName", tmp); 485178825Sdfr if (ret) { 486178825Sdfr free(tmp); 487178825Sdfr goto out; 488178825Sdfr } 489178825Sdfr free(tmp); 49072445Sassar } 491178825Sdfr 492178825Sdfr if (is_account || is_samba_account) { 493178825Sdfr ret = krb5_unparse_name_short(context, ent->entry.principal, &tmp); 494178825Sdfr if (ret) 495178825Sdfr goto out; 496178825Sdfr ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "uid", tmp); 497178825Sdfr if (ret) { 498178825Sdfr free(tmp); 499178825Sdfr goto out; 500178825Sdfr } 50172445Sassar free(tmp); 50272445Sassar } 50372445Sassar } 50472445Sassar 505178825Sdfr if (is_heimdal_entry && (ent->entry.kvno != orig.entry.kvno || is_new_entry)) { 506178825Sdfr ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, 507233294Sstas "krb5KeyVersionNumber", 508178825Sdfr ent->entry.kvno); 509178825Sdfr if (ret) 51072445Sassar goto out; 51172445Sassar } 51272445Sassar 513178825Sdfr if (is_heimdal_entry && ent->entry.valid_start) { 514178825Sdfr if (orig.entry.valid_end == NULL 515178825Sdfr || (*(ent->entry.valid_start) != *(orig.entry.valid_start))) { 516178825Sdfr ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, 517178825Sdfr "krb5ValidStart", 518178825Sdfr ent->entry.valid_start); 519178825Sdfr if (ret) 52072445Sassar goto out; 52172445Sassar } 52272445Sassar } 52372445Sassar 524178825Sdfr if (ent->entry.valid_end) { 525178825Sdfr if (orig.entry.valid_end == NULL || (*(ent->entry.valid_end) != *(orig.entry.valid_end))) { 526233294Sstas if (is_heimdal_entry) { 527178825Sdfr ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, 528178825Sdfr "krb5ValidEnd", 529178825Sdfr ent->entry.valid_end); 530178825Sdfr if (ret) 531178825Sdfr goto out; 532178825Sdfr } 533178825Sdfr if (is_samba_account) { 534178825Sdfr ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, 535233294Sstas "sambaKickoffTime", 536178825Sdfr *(ent->entry.valid_end)); 537178825Sdfr if (ret) 538178825Sdfr goto out; 53972445Sassar } 540178825Sdfr } 54172445Sassar } 54272445Sassar 543178825Sdfr if (ent->entry.pw_end) { 544178825Sdfr if (orig.entry.pw_end == NULL || (*(ent->entry.pw_end) != *(orig.entry.pw_end))) { 545178825Sdfr if (is_heimdal_entry) { 546178825Sdfr ret = LDAP_addmod_generalized_time(&mods, LDAP_MOD_REPLACE, 547178825Sdfr "krb5PasswordEnd", 548178825Sdfr ent->entry.pw_end); 549178825Sdfr if (ret) 550178825Sdfr goto out; 55172445Sassar } 552178825Sdfr 553178825Sdfr if (is_samba_account) { 554178825Sdfr ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, 555233294Sstas "sambaPwdMustChange", 556178825Sdfr *(ent->entry.pw_end)); 557178825Sdfr if (ret) 558178825Sdfr goto out; 559178825Sdfr } 56072445Sassar } 56172445Sassar } 56272445Sassar 563178825Sdfr 564178825Sdfr#if 0 /* we we have last_pw_change */ 565178825Sdfr if (is_samba_account && ent->entry.last_pw_change) { 566178825Sdfr if (orig.entry.last_pw_change == NULL || (*(ent->entry.last_pw_change) != *(orig.entry.last_pw_change))) { 567178825Sdfr ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, 568233294Sstas "sambaPwdLastSet", 569178825Sdfr *(ent->entry.last_pw_change)); 570178825Sdfr if (ret) 57172445Sassar goto out; 57272445Sassar } 57372445Sassar } 574178825Sdfr#endif 57572445Sassar 576178825Sdfr if (is_heimdal_entry && ent->entry.max_life) { 577178825Sdfr if (orig.entry.max_life == NULL 578178825Sdfr || (*(ent->entry.max_life) != *(orig.entry.max_life))) { 579178825Sdfr 580178825Sdfr ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, 581233294Sstas "krb5MaxLife", 582178825Sdfr *(ent->entry.max_life)); 583178825Sdfr if (ret) 58472445Sassar goto out; 58572445Sassar } 58672445Sassar } 58772445Sassar 588178825Sdfr if (is_heimdal_entry && ent->entry.max_renew) { 589178825Sdfr if (orig.entry.max_renew == NULL 590178825Sdfr || (*(ent->entry.max_renew) != *(orig.entry.max_renew))) { 59172445Sassar 592178825Sdfr ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, 593178825Sdfr "krb5MaxRenew", 594178825Sdfr *(ent->entry.max_renew)); 595178825Sdfr if (ret) 596178825Sdfr goto out; 59772445Sassar } 59872445Sassar } 59972445Sassar 600178825Sdfr oflags = HDBFlags2int(orig.entry.flags); 601178825Sdfr nflags = HDBFlags2int(ent->entry.flags); 602178825Sdfr 603178825Sdfr if (is_heimdal_entry && oflags != nflags) { 604178825Sdfr 605178825Sdfr ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, 606178825Sdfr "krb5KDCFlags", 607178825Sdfr nflags); 608178825Sdfr if (ret) 60972445Sassar goto out; 61072445Sassar } 61172445Sassar 612178825Sdfr /* Remove keys if they exists, and then replace keys. */ 613178825Sdfr if (!is_new_entry && orig.entry.keys.len > 0) { 614233294Sstas vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key"); 615233294Sstas if (vals) { 616233294Sstas ldap_value_free_len(vals); 61772445Sassar 618178825Sdfr ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5Key", NULL); 619178825Sdfr if (ret) 620178825Sdfr goto out; 62172445Sassar } 62272445Sassar } 62372445Sassar 624178825Sdfr for (i = 0; i < ent->entry.keys.len; i++) { 625178825Sdfr 626178825Sdfr if (is_samba_account 627178825Sdfr && ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) { 628178825Sdfr char *ntHexPassword; 629178825Sdfr char *nt; 630233294Sstas time_t now = time(NULL); 631233294Sstas 632178825Sdfr /* the key might have been 'sealed', but samba passwords 633178825Sdfr are clear in the directory */ 634178825Sdfr ret = hdb_unseal_key(context, db, &ent->entry.keys.val[i]); 635178825Sdfr if (ret) 636178825Sdfr goto out; 637233294Sstas 638178825Sdfr nt = ent->entry.keys.val[i].key.keyvalue.data; 639178825Sdfr /* store in ntPassword, not krb5key */ 640178825Sdfr ret = hex_encode(nt, 16, &ntHexPassword); 641178825Sdfr if (ret < 0) { 642233294Sstas ret = ENOMEM; 643233294Sstas krb5_set_error_message(context, ret, "hdb-ldap: failed to " 644178825Sdfr "hex encode key"); 64572445Sassar goto out; 64672445Sassar } 647233294Sstas ret = LDAP_addmod(&mods, LDAP_MOD_REPLACE, "sambaNTPassword", 648178825Sdfr ntHexPassword); 649178825Sdfr free(ntHexPassword); 650178825Sdfr if (ret) 65172445Sassar goto out; 652233294Sstas ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_REPLACE, 653233294Sstas "sambaPwdLastSet", now); 654233294Sstas if (ret) 655233294Sstas goto out; 656233294Sstas 657178825Sdfr /* have to kill the LM passwod if it exists */ 658233294Sstas vals = ldap_get_values_len(HDB2LDAP(db), msg, "sambaLMPassword"); 659233294Sstas if (vals) { 660233294Sstas ldap_value_free_len(vals); 661178825Sdfr ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, 662178825Sdfr "sambaLMPassword", NULL); 663178825Sdfr if (ret) 664178825Sdfr goto out; 66572445Sassar } 666233294Sstas 667178825Sdfr } else if (is_heimdal_entry) { 668178825Sdfr unsigned char *buf; 669178825Sdfr size_t len, buf_size; 670178825Sdfr 671178825Sdfr ASN1_MALLOC_ENCODE(Key, buf, buf_size, &ent->entry.keys.val[i], &len, ret); 672178825Sdfr if (ret) 673178825Sdfr goto out; 674178825Sdfr if(buf_size != len) 675178825Sdfr krb5_abortx(context, "internal error in ASN.1 encoder"); 676178825Sdfr 677178825Sdfr /* addmod_len _owns_ the key, doesn't need to copy it */ 678178825Sdfr ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5Key", buf, len); 679178825Sdfr if (ret) 680178825Sdfr goto out; 68172445Sassar } 68272445Sassar } 68372445Sassar 684178825Sdfr if (ent->entry.etypes) { 685178825Sdfr int add_krb5EncryptionType = 0; 686178825Sdfr 687233294Sstas /* 688178825Sdfr * Only add/modify krb5EncryptionType if it's a new heimdal 689178825Sdfr * entry or krb5EncryptionType already exists on the entry. 690178825Sdfr */ 691178825Sdfr 692178825Sdfr if (!is_new_entry) { 693233294Sstas vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5EncryptionType"); 694233294Sstas if (vals) { 695233294Sstas ldap_value_free_len(vals); 696178825Sdfr ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5EncryptionType", 697178825Sdfr NULL); 698178825Sdfr if (ret) 699178825Sdfr goto out; 700178825Sdfr add_krb5EncryptionType = 1; 701178825Sdfr } 702178825Sdfr } else if (is_heimdal_entry) 703178825Sdfr add_krb5EncryptionType = 1; 704178825Sdfr 705178825Sdfr if (add_krb5EncryptionType) { 706178825Sdfr for (i = 0; i < ent->entry.etypes->len; i++) { 707233294Sstas if (is_samba_account && 708178825Sdfr ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) 709178825Sdfr { 710178825Sdfr ; 711178825Sdfr } else if (is_heimdal_entry) { 712178825Sdfr ret = LDAP_addmod_integer(context, &mods, LDAP_MOD_ADD, 713178825Sdfr "krb5EncryptionType", 714178825Sdfr ent->entry.etypes->val[i]); 715178825Sdfr if (ret) 716178825Sdfr goto out; 717178825Sdfr } 718178825Sdfr } 719178825Sdfr } 720178825Sdfr } 721178825Sdfr 72272445Sassar /* for clarity */ 72372445Sassar ret = 0; 72472445Sassar 725178825Sdfr out: 72672445Sassar 727178825Sdfr if (ret == 0) 72872445Sassar *pmods = mods; 729178825Sdfr else if (mods != NULL) { 73072445Sassar ldap_mods_free(mods, 1); 73172445Sassar *pmods = NULL; 73272445Sassar } 73372445Sassar 734178825Sdfr if (msg) 73572445Sassar hdb_free_entry(context, &orig); 73672445Sassar 73772445Sassar return ret; 73872445Sassar} 73972445Sassar 74072445Sassarstatic krb5_error_code 74172445SassarLDAP_dn2principal(krb5_context context, HDB * db, const char *dn, 74272445Sassar krb5_principal * principal) 74372445Sassar{ 74472445Sassar krb5_error_code ret; 745178825Sdfr int rc; 746178825Sdfr const char *filter = "(objectClass=krb5Principal)"; 74772445Sassar LDAPMessage *res = NULL, *e; 748233294Sstas char *p; 74972445Sassar 750178825Sdfr ret = LDAP_no_size_limit(context, HDB2LDAP(db)); 751178825Sdfr if (ret) 75290926Snectar goto out; 75390926Snectar 754233294Sstas rc = ldap_search_ext_s(HDB2LDAP(db), dn, LDAP_SCOPE_SUBTREE, 755233294Sstas filter, krb5principal_attrs, 0, 756233294Sstas NULL, NULL, NULL, 757233294Sstas 0, &res); 758178825Sdfr if (check_ldap(context, db, rc)) { 75972445Sassar ret = HDB_ERR_NOENTRY; 760233294Sstas krb5_set_error_message(context, ret, "ldap_search_ext_s: " 761233294Sstas "filter: %s error: %s", 762233294Sstas filter, ldap_err2string(rc)); 76372445Sassar goto out; 76472445Sassar } 76572445Sassar 766178825Sdfr e = ldap_first_entry(HDB2LDAP(db), res); 76772445Sassar if (e == NULL) { 76872445Sassar ret = HDB_ERR_NOENTRY; 76972445Sassar goto out; 77072445Sassar } 77172445Sassar 772233294Sstas ret = LDAP_get_string_value(db, e, "krb5PrincipalName", &p); 773233294Sstas if (ret) { 77472445Sassar ret = HDB_ERR_NOENTRY; 77572445Sassar goto out; 77672445Sassar } 77772445Sassar 778233294Sstas ret = krb5_parse_name(context, p, principal); 779233294Sstas free(p); 78072445Sassar 78172445Sassar out: 782178825Sdfr if (res) 78372445Sassar ldap_msgfree(res); 784178825Sdfr 78572445Sassar return ret; 78672445Sassar} 78772445Sassar 788233294Sstasstatic int 789233294Sstasneed_quote(unsigned char c) 790233294Sstas{ 791233294Sstas return (c & 0x80) || 792233294Sstas (c < 32) || 793233294Sstas (c == '(') || 794233294Sstas (c == ')') || 795233294Sstas (c == '*') || 796233294Sstas (c == '\\') || 797233294Sstas (c == 0x7f); 798233294Sstas} 799233294Sstas 800233294Sstasconst static char hexchar[] = "0123456789ABCDEF"; 801233294Sstas 80272445Sassarstatic krb5_error_code 803233294Sstasescape_value(krb5_context context, const unsigned char *unquoted, char **quoted) 804233294Sstas{ 805233294Sstas size_t i, len; 806233294Sstas 807233294Sstas for (i = 0, len = 0; unquoted[i] != '\0'; i++, len++) { 808233294Sstas if (need_quote((unsigned char)unquoted[i])) 809233294Sstas len += 2; 810233294Sstas } 811233294Sstas 812233294Sstas *quoted = malloc(len + 1); 813233294Sstas if (*quoted == NULL) { 814233294Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 815233294Sstas return ENOMEM; 816233294Sstas } 817233294Sstas 818233294Sstas for (i = 0; unquoted[0] ; unquoted++) { 819233294Sstas if (need_quote((unsigned char *)unquoted[0])) { 820233294Sstas (*quoted)[i++] = '\\'; 821233294Sstas (*quoted)[i++] = hexchar[(unquoted[0] >> 4) & 0xf]; 822233294Sstas (*quoted)[i++] = hexchar[(unquoted[0] ) & 0xf]; 823233294Sstas } else 824233294Sstas (*quoted)[i++] = (char)unquoted[0]; 825233294Sstas } 826233294Sstas (*quoted)[i] = '\0'; 827233294Sstas return 0; 828233294Sstas} 829233294Sstas 830233294Sstas 831233294Sstasstatic krb5_error_code 832178825SdfrLDAP__lookup_princ(krb5_context context, 833178825Sdfr HDB *db, 834178825Sdfr const char *princname, 835178825Sdfr const char *userid, 836178825Sdfr LDAPMessage **msg) 83772445Sassar{ 83872445Sassar krb5_error_code ret; 839178825Sdfr int rc; 840233294Sstas char *quote, *filter = NULL; 84172445Sassar 842178825Sdfr ret = LDAP__connect(context, db); 843178825Sdfr if (ret) 844178825Sdfr return ret; 84572445Sassar 846233294Sstas /* 847233294Sstas * Quote searches that contain filter language, this quote 848233294Sstas * searches for *@REALM, which takes very long time. 849233294Sstas */ 850233294Sstas 851233294Sstas ret = escape_value(context, princname, "e); 852233294Sstas if (ret) 853233294Sstas goto out; 854233294Sstas 855178825Sdfr rc = asprintf(&filter, 856178825Sdfr "(&(objectClass=krb5Principal)(krb5PrincipalName=%s))", 857233294Sstas quote); 858233294Sstas free(quote); 859233294Sstas 86072445Sassar if (rc < 0) { 86172445Sassar ret = ENOMEM; 862233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 86372445Sassar goto out; 86472445Sassar } 86572445Sassar 866178825Sdfr ret = LDAP_no_size_limit(context, HDB2LDAP(db)); 867178825Sdfr if (ret) 86890926Snectar goto out; 86972445Sassar 870233294Sstas rc = ldap_search_ext_s(HDB2LDAP(db), HDB2BASE(db), 871233294Sstas LDAP_SCOPE_SUBTREE, filter, 872233294Sstas krb5kdcentry_attrs, 0, 873233294Sstas NULL, NULL, NULL, 874233294Sstas 0, msg); 875178825Sdfr if (check_ldap(context, db, rc)) { 876233294Sstas ret = HDB_ERR_NOENTRY; 877233294Sstas krb5_set_error_message(context, ret, "ldap_search_ext_s: " 878233294Sstas "filter: %s - error: %s", 879178825Sdfr filter, ldap_err2string(rc)); 88072445Sassar goto out; 88172445Sassar } 88272445Sassar 883178825Sdfr if (userid && ldap_count_entries(HDB2LDAP(db), *msg) == 0) { 884178825Sdfr free(filter); 885178825Sdfr filter = NULL; 886178825Sdfr ldap_msgfree(*msg); 887178825Sdfr *msg = NULL; 888233294Sstas 889233294Sstas ret = escape_value(context, userid, "e); 890233294Sstas if (ret) 891233294Sstas goto out; 892233294Sstas 893178825Sdfr rc = asprintf(&filter, 894178825Sdfr "(&(|(objectClass=sambaSamAccount)(objectClass=%s))(uid=%s))", 895233294Sstas structural_object, quote); 896233294Sstas free(quote); 897178825Sdfr if (rc < 0) { 898178825Sdfr ret = ENOMEM; 899233294Sstas krb5_set_error_message(context, ret, "asprintf: out of memory"); 900178825Sdfr goto out; 901178825Sdfr } 902233294Sstas 903178825Sdfr ret = LDAP_no_size_limit(context, HDB2LDAP(db)); 904178825Sdfr if (ret) 905178825Sdfr goto out; 906178825Sdfr 907233294Sstas rc = ldap_search_ext_s(HDB2LDAP(db), HDB2BASE(db), LDAP_SCOPE_SUBTREE, 908233294Sstas filter, krb5kdcentry_attrs, 0, 909233294Sstas NULL, NULL, NULL, 910233294Sstas 0, msg); 911178825Sdfr if (check_ldap(context, db, rc)) { 912178825Sdfr ret = HDB_ERR_NOENTRY; 913233294Sstas krb5_set_error_message(context, ret, 914233294Sstas "ldap_search_ext_s: filter: %s error: %s", 915233294Sstas filter, ldap_err2string(rc)); 916178825Sdfr goto out; 917178825Sdfr } 918178825Sdfr } 919178825Sdfr 92072445Sassar ret = 0; 92172445Sassar 92272445Sassar out: 923178825Sdfr if (filter) 92472445Sassar free(filter); 925178825Sdfr 92672445Sassar return ret; 92772445Sassar} 92872445Sassar 92972445Sassarstatic krb5_error_code 93072445SassarLDAP_principal2message(krb5_context context, HDB * db, 931178825Sdfr krb5_const_principal princ, LDAPMessage ** msg) 93272445Sassar{ 933178825Sdfr char *name, *name_short = NULL; 93472445Sassar krb5_error_code ret; 935178825Sdfr krb5_realm *r, *r0; 93672445Sassar 937178825Sdfr *msg = NULL; 938178825Sdfr 939178825Sdfr ret = krb5_unparse_name(context, princ, &name); 940178825Sdfr if (ret) 94172445Sassar return ret; 942178825Sdfr 943178825Sdfr ret = krb5_get_default_realms(context, &r0); 944178825Sdfr if(ret) { 945178825Sdfr free(name); 946178825Sdfr return ret; 94772445Sassar } 948178825Sdfr for (r = r0; *r != NULL; r++) { 949178825Sdfr if(strcmp(krb5_principal_get_realm(context, princ), *r) == 0) { 950178825Sdfr ret = krb5_unparse_name_short(context, princ, &name_short); 951178825Sdfr if (ret) { 952178825Sdfr krb5_free_host_realm(context, r0); 953178825Sdfr free(name); 954178825Sdfr return ret; 955178825Sdfr } 956178825Sdfr break; 957178825Sdfr } 958178825Sdfr } 959178825Sdfr krb5_free_host_realm(context, r0); 96072445Sassar 961178825Sdfr ret = LDAP__lookup_princ(context, db, name, name_short, msg); 962178825Sdfr free(name); 963178825Sdfr free(name_short); 96472445Sassar 96572445Sassar return ret; 96672445Sassar} 96772445Sassar 96872445Sassar/* 96972445Sassar * Construct an hdb_entry from a directory entry. 97072445Sassar */ 97172445Sassarstatic krb5_error_code 97272445SassarLDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg, 973233294Sstas int flags, hdb_entry_ex * ent) 97472445Sassar{ 975178825Sdfr char *unparsed_name = NULL, *dn = NULL, *ntPasswordIN = NULL; 976178825Sdfr char *samba_acct_flags = NULL; 97772445Sassar struct berval **keys; 978233294Sstas struct berval **vals; 979233294Sstas int tmp, tmp_time, i, ret, have_arcfour = 0; 98072445Sassar 98172445Sassar memset(ent, 0, sizeof(*ent)); 982178825Sdfr ent->entry.flags = int2HDBFlags(0); 98372445Sassar 984178825Sdfr ret = LDAP_get_string_value(db, msg, "krb5PrincipalName", &unparsed_name); 985178825Sdfr if (ret == 0) { 986178825Sdfr ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal); 987178825Sdfr if (ret) 988178825Sdfr goto out; 989178825Sdfr } else { 990178825Sdfr ret = LDAP_get_string_value(db, msg, "uid", 991178825Sdfr &unparsed_name); 992178825Sdfr if (ret == 0) { 993178825Sdfr ret = krb5_parse_name(context, unparsed_name, &ent->entry.principal); 994178825Sdfr if (ret) 995178825Sdfr goto out; 996178825Sdfr } else { 997233294Sstas krb5_set_error_message(context, HDB_ERR_NOENTRY, 998233294Sstas "hdb-ldap: ldap entry missing" 999178825Sdfr "principal name"); 1000178825Sdfr return HDB_ERR_NOENTRY; 1001178825Sdfr } 100272445Sassar } 100372445Sassar 1004178825Sdfr { 1005178825Sdfr int integer; 1006178825Sdfr ret = LDAP_get_integer_value(db, msg, "krb5KeyVersionNumber", 1007178825Sdfr &integer); 1008178825Sdfr if (ret) 1009178825Sdfr ent->entry.kvno = 0; 1010178825Sdfr else 1011178825Sdfr ent->entry.kvno = integer; 101272445Sassar } 101372445Sassar 1014178825Sdfr keys = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key"); 101572445Sassar if (keys != NULL) { 101672445Sassar int i; 101772445Sassar size_t l; 101872445Sassar 1019178825Sdfr ent->entry.keys.len = ldap_count_values_len(keys); 1020178825Sdfr ent->entry.keys.val = (Key *) calloc(ent->entry.keys.len, sizeof(Key)); 1021178825Sdfr if (ent->entry.keys.val == NULL) { 102290926Snectar ret = ENOMEM; 1023233294Sstas krb5_set_error_message(context, ret, "calloc: out of memory"); 102490926Snectar goto out; 102590926Snectar } 1026178825Sdfr for (i = 0; i < ent->entry.keys.len; i++) { 102772445Sassar decode_Key((unsigned char *) keys[i]->bv_val, 1028178825Sdfr (size_t) keys[i]->bv_len, &ent->entry.keys.val[i], &l); 102972445Sassar } 103072445Sassar ber_bvecfree(keys); 103172445Sassar } else { 103272445Sassar#if 1 103372445Sassar /* 103472445Sassar * This violates the ASN1 but it allows a principal to 103572445Sassar * be related to a general directory entry without creating 103672445Sassar * the keys. Hopefully it's OK. 103772445Sassar */ 1038178825Sdfr ent->entry.keys.len = 0; 1039178825Sdfr ent->entry.keys.val = NULL; 104072445Sassar#else 104172445Sassar ret = HDB_ERR_NOENTRY; 104272445Sassar goto out; 104372445Sassar#endif 104472445Sassar } 104572445Sassar 1046233294Sstas vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5EncryptionType"); 1047233294Sstas if (vals != NULL) { 1048178825Sdfr int i; 1049178825Sdfr 1050178825Sdfr ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes))); 1051178825Sdfr if (ent->entry.etypes == NULL) { 1052178825Sdfr ret = ENOMEM; 1053233294Sstas krb5_set_error_message(context, ret,"malloc: out of memory"); 1054178825Sdfr goto out; 1055178825Sdfr } 1056233294Sstas ent->entry.etypes->len = ldap_count_values_len(vals); 1057178825Sdfr ent->entry.etypes->val = calloc(ent->entry.etypes->len, sizeof(int)); 1058178825Sdfr if (ent->entry.etypes->val == NULL) { 1059178825Sdfr ret = ENOMEM; 1060233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 1061233294Sstas ent->entry.etypes->len = 0; 1062178825Sdfr goto out; 1063178825Sdfr } 1064178825Sdfr for (i = 0; i < ent->entry.etypes->len; i++) { 1065233294Sstas char *buf; 1066233294Sstas 1067233294Sstas buf = malloc(vals[i]->bv_len + 1); 1068233294Sstas if (buf == NULL) { 1069233294Sstas ret = ENOMEM; 1070233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 1071233294Sstas goto out; 1072233294Sstas } 1073233294Sstas memcpy(buf, vals[i]->bv_val, vals[i]->bv_len); 1074233294Sstas buf[vals[i]->bv_len] = '\0'; 1075233294Sstas ent->entry.etypes->val[i] = atoi(buf); 1076233294Sstas free(buf); 1077178825Sdfr } 1078233294Sstas ldap_value_free_len(vals); 107972445Sassar } 108072445Sassar 1081178825Sdfr for (i = 0; i < ent->entry.keys.len; i++) { 1082178825Sdfr if (ent->entry.keys.val[i].key.keytype == ETYPE_ARCFOUR_HMAC_MD5) { 1083178825Sdfr have_arcfour = 1; 1084178825Sdfr break; 1085178825Sdfr } 1086178825Sdfr } 108772445Sassar 1088178825Sdfr /* manually construct the NT (type 23) key */ 1089178825Sdfr ret = LDAP_get_string_value(db, msg, "sambaNTPassword", &ntPasswordIN); 1090178825Sdfr if (ret == 0 && have_arcfour == 0) { 1091178825Sdfr unsigned *etypes; 1092178825Sdfr Key *keys; 1093178825Sdfr int i; 1094178825Sdfr 1095178825Sdfr keys = realloc(ent->entry.keys.val, 1096178825Sdfr (ent->entry.keys.len + 1) * sizeof(ent->entry.keys.val[0])); 1097178825Sdfr if (keys == NULL) { 1098178825Sdfr free(ntPasswordIN); 1099178825Sdfr ret = ENOMEM; 1100233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 1101178825Sdfr goto out; 1102178825Sdfr } 1103178825Sdfr ent->entry.keys.val = keys; 1104178825Sdfr memset(&ent->entry.keys.val[ent->entry.keys.len], 0, sizeof(Key)); 1105178825Sdfr ent->entry.keys.val[ent->entry.keys.len].key.keytype = ETYPE_ARCFOUR_HMAC_MD5; 1106178825Sdfr ret = krb5_data_alloc (&ent->entry.keys.val[ent->entry.keys.len].key.keyvalue, 16); 1107178825Sdfr if (ret) { 1108233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 1109178825Sdfr free(ntPasswordIN); 1110178825Sdfr ret = ENOMEM; 1111178825Sdfr goto out; 1112178825Sdfr } 1113178825Sdfr ret = hex_decode(ntPasswordIN, 1114178825Sdfr ent->entry.keys.val[ent->entry.keys.len].key.keyvalue.data, 16); 1115178825Sdfr ent->entry.keys.len++; 1116178825Sdfr 1117178825Sdfr if (ent->entry.etypes == NULL) { 1118178825Sdfr ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes))); 1119178825Sdfr if (ent->entry.etypes == NULL) { 1120178825Sdfr ret = ENOMEM; 1121233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 1122178825Sdfr goto out; 1123178825Sdfr } 1124178825Sdfr ent->entry.etypes->val = NULL; 1125178825Sdfr ent->entry.etypes->len = 0; 1126178825Sdfr } 1127178825Sdfr 1128178825Sdfr for (i = 0; i < ent->entry.etypes->len; i++) 1129178825Sdfr if (ent->entry.etypes->val[i] == ETYPE_ARCFOUR_HMAC_MD5) 1130178825Sdfr break; 1131178825Sdfr /* If there is no ARCFOUR enctype, add one */ 1132178825Sdfr if (i == ent->entry.etypes->len) { 1133233294Sstas etypes = realloc(ent->entry.etypes->val, 1134233294Sstas (ent->entry.etypes->len + 1) * 1135178825Sdfr sizeof(ent->entry.etypes->val[0])); 1136178825Sdfr if (etypes == NULL) { 1137178825Sdfr ret = ENOMEM; 1138233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 1139233294Sstas goto out; 1140178825Sdfr } 1141178825Sdfr ent->entry.etypes->val = etypes; 1142233294Sstas ent->entry.etypes->val[ent->entry.etypes->len] = 1143178825Sdfr ETYPE_ARCFOUR_HMAC_MD5; 1144178825Sdfr ent->entry.etypes->len++; 1145178825Sdfr } 1146178825Sdfr } 1147178825Sdfr 1148178825Sdfr ret = LDAP_get_generalized_time_value(db, msg, "createTimestamp", 1149178825Sdfr &ent->entry.created_by.time); 1150178825Sdfr if (ret) 1151178825Sdfr ent->entry.created_by.time = time(NULL); 1152178825Sdfr 1153178825Sdfr ent->entry.created_by.principal = NULL; 1154178825Sdfr 1155233294Sstas if (flags & HDB_F_ADMIN_DATA) { 1156233294Sstas ret = LDAP_get_string_value(db, msg, "creatorsName", &dn); 1157233294Sstas if (ret == 0) { 1158233294Sstas LDAP_dn2principal(context, db, dn, &ent->entry.created_by.principal); 1159233294Sstas free(dn); 116072445Sassar } 116172445Sassar 1162233294Sstas ent->entry.modified_by = calloc(1, sizeof(*ent->entry.modified_by)); 1163233294Sstas if (ent->entry.modified_by == NULL) { 1164233294Sstas ret = ENOMEM; 1165233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 1166233294Sstas goto out; 1167233294Sstas } 1168233294Sstas 1169233294Sstas ret = LDAP_get_generalized_time_value(db, msg, "modifyTimestamp", 1170233294Sstas &ent->entry.modified_by->time); 1171233294Sstas if (ret == 0) { 1172233294Sstas ret = LDAP_get_string_value(db, msg, "modifiersName", &dn); 1173233294Sstas if (ret == 0) { 1174233294Sstas LDAP_dn2principal(context, db, dn, &ent->entry.modified_by->principal); 1175233294Sstas free(dn); 1176233294Sstas } else { 1177233294Sstas free(ent->entry.modified_by); 1178233294Sstas ent->entry.modified_by = NULL; 1179233294Sstas } 1180233294Sstas } 118172445Sassar } 118272445Sassar 1183178825Sdfr ent->entry.valid_start = malloc(sizeof(*ent->entry.valid_start)); 1184178825Sdfr if (ent->entry.valid_start == NULL) { 118572445Sassar ret = ENOMEM; 1186233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 118772445Sassar goto out; 118872445Sassar } 1189178825Sdfr ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidStart", 1190178825Sdfr ent->entry.valid_start); 1191178825Sdfr if (ret) { 119272445Sassar /* OPTIONAL */ 1193178825Sdfr free(ent->entry.valid_start); 1194178825Sdfr ent->entry.valid_start = NULL; 119572445Sassar } 1196233294Sstas 1197178825Sdfr ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end)); 1198178825Sdfr if (ent->entry.valid_end == NULL) { 119990926Snectar ret = ENOMEM; 1200233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 120172445Sassar goto out; 120272445Sassar } 1203178825Sdfr ret = LDAP_get_generalized_time_value(db, msg, "krb5ValidEnd", 1204178825Sdfr ent->entry.valid_end); 1205178825Sdfr if (ret) { 120672445Sassar /* OPTIONAL */ 1207178825Sdfr free(ent->entry.valid_end); 1208178825Sdfr ent->entry.valid_end = NULL; 120972445Sassar } 121072445Sassar 1211178825Sdfr ret = LDAP_get_integer_value(db, msg, "sambaKickoffTime", &tmp_time); 1212178825Sdfr if (ret == 0) { 1213178825Sdfr if (ent->entry.valid_end == NULL) { 1214178825Sdfr ent->entry.valid_end = malloc(sizeof(*ent->entry.valid_end)); 1215178825Sdfr if (ent->entry.valid_end == NULL) { 1216178825Sdfr ret = ENOMEM; 1217233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 1218178825Sdfr goto out; 1219178825Sdfr } 1220178825Sdfr } 1221178825Sdfr *ent->entry.valid_end = tmp_time; 1222178825Sdfr } 1223178825Sdfr 1224178825Sdfr ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end)); 1225178825Sdfr if (ent->entry.pw_end == NULL) { 122690926Snectar ret = ENOMEM; 1227233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 122872445Sassar goto out; 122972445Sassar } 1230178825Sdfr ret = LDAP_get_generalized_time_value(db, msg, "krb5PasswordEnd", 1231178825Sdfr ent->entry.pw_end); 1232178825Sdfr if (ret) { 123372445Sassar /* OPTIONAL */ 1234178825Sdfr free(ent->entry.pw_end); 1235178825Sdfr ent->entry.pw_end = NULL; 123672445Sassar } 123772445Sassar 1238233294Sstas ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time); 1239233294Sstas if (ret == 0) { 1240233294Sstas time_t delta; 1241233294Sstas 1242233294Sstas if (ent->entry.pw_end == NULL) { 1243233294Sstas ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end)); 1244233294Sstas if (ent->entry.pw_end == NULL) { 1245233294Sstas ret = ENOMEM; 1246233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 1247233294Sstas goto out; 1248233294Sstas } 1249233294Sstas } 1250233294Sstas 1251233294Sstas delta = krb5_config_get_time_default(context, NULL, 1252233294Sstas 365 * 24 * 60 * 60, 1253233294Sstas "kadmin", 1254233294Sstas "password_lifetime", 1255233294Sstas NULL); 1256233294Sstas *ent->entry.pw_end = tmp_time + delta; 1257233294Sstas } 1258233294Sstas 1259178825Sdfr ret = LDAP_get_integer_value(db, msg, "sambaPwdMustChange", &tmp_time); 1260178825Sdfr if (ret == 0) { 1261178825Sdfr if (ent->entry.pw_end == NULL) { 1262178825Sdfr ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end)); 1263178825Sdfr if (ent->entry.pw_end == NULL) { 1264178825Sdfr ret = ENOMEM; 1265233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 1266178825Sdfr goto out; 1267178825Sdfr } 1268178825Sdfr } 1269178825Sdfr *ent->entry.pw_end = tmp_time; 127072445Sassar } 1271178825Sdfr 1272178825Sdfr /* OPTIONAL */ 1273178825Sdfr ret = LDAP_get_integer_value(db, msg, "sambaPwdLastSet", &tmp_time); 1274178825Sdfr if (ret == 0) 1275178825Sdfr hdb_entry_set_pw_change_time(context, &ent->entry, tmp_time); 1276178825Sdfr 1277178825Sdfr { 1278178825Sdfr int max_life; 1279178825Sdfr 1280178825Sdfr ent->entry.max_life = malloc(sizeof(*ent->entry.max_life)); 1281178825Sdfr if (ent->entry.max_life == NULL) { 1282178825Sdfr ret = ENOMEM; 1283233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 1284178825Sdfr goto out; 1285178825Sdfr } 1286178825Sdfr ret = LDAP_get_integer_value(db, msg, "krb5MaxLife", &max_life); 1287178825Sdfr if (ret) { 1288178825Sdfr free(ent->entry.max_life); 1289178825Sdfr ent->entry.max_life = NULL; 1290178825Sdfr } else 1291178825Sdfr *ent->entry.max_life = max_life; 129272445Sassar } 129372445Sassar 1294178825Sdfr { 1295178825Sdfr int max_renew; 1296178825Sdfr 1297178825Sdfr ent->entry.max_renew = malloc(sizeof(*ent->entry.max_renew)); 1298178825Sdfr if (ent->entry.max_renew == NULL) { 1299178825Sdfr ret = ENOMEM; 1300233294Sstas krb5_set_error_message(context, ret, "malloc: out of memory"); 1301178825Sdfr goto out; 1302178825Sdfr } 1303178825Sdfr ret = LDAP_get_integer_value(db, msg, "krb5MaxRenew", &max_renew); 1304178825Sdfr if (ret) { 1305178825Sdfr free(ent->entry.max_renew); 1306178825Sdfr ent->entry.max_renew = NULL; 1307178825Sdfr } else 1308178825Sdfr *ent->entry.max_renew = max_renew; 130972445Sassar } 131072445Sassar 1311233294Sstas ret = LDAP_get_integer_value(db, msg, "krb5KDCFlags", &tmp); 1312233294Sstas if (ret) 131372445Sassar tmp = 0; 131472445Sassar 1315178825Sdfr ent->entry.flags = int2HDBFlags(tmp); 1316178825Sdfr 1317178825Sdfr /* Try and find Samba flags to put into the mix */ 1318178825Sdfr ret = LDAP_get_string_value(db, msg, "sambaAcctFlags", &samba_acct_flags); 1319178825Sdfr if (ret == 0) { 1320178825Sdfr /* parse the [UXW...] string: 1321233294Sstas 1322233294Sstas 'N' No password 1323233294Sstas 'D' Disabled 1324233294Sstas 'H' Homedir required 1325233294Sstas 'T' Temp account. 1326233294Sstas 'U' User account (normal) 1327233294Sstas 'M' MNS logon user account - what is this ? 1328233294Sstas 'W' Workstation account 1329233294Sstas 'S' Server account 1330233294Sstas 'L' Locked account 1331233294Sstas 'X' No Xpiry on password 1332233294Sstas 'I' Interdomain trust account 1333233294Sstas 1334233294Sstas */ 1335233294Sstas 133672445Sassar int i; 1337178825Sdfr int flags_len = strlen(samba_acct_flags); 133872445Sassar 1339178825Sdfr if (flags_len < 2) 1340178825Sdfr goto out2; 1341178825Sdfr 1342233294Sstas if (samba_acct_flags[0] != '[' 1343233294Sstas || samba_acct_flags[flags_len - 1] != ']') 1344178825Sdfr goto out2; 1345178825Sdfr 1346178825Sdfr /* Allow forwarding */ 1347178825Sdfr if (samba_forwardable) 1348178825Sdfr ent->entry.flags.forwardable = TRUE; 1349178825Sdfr 1350178825Sdfr for (i=0; i < flags_len; i++) { 1351178825Sdfr switch (samba_acct_flags[i]) { 1352178825Sdfr case ' ': 1353178825Sdfr case '[': 1354178825Sdfr case ']': 1355178825Sdfr break; 1356178825Sdfr case 'N': 1357178825Sdfr /* how to handle no password in kerberos? */ 1358178825Sdfr break; 1359178825Sdfr case 'D': 1360178825Sdfr ent->entry.flags.invalid = TRUE; 1361178825Sdfr break; 1362178825Sdfr case 'H': 1363178825Sdfr break; 1364178825Sdfr case 'T': 1365178825Sdfr /* temp duplicate */ 1366178825Sdfr ent->entry.flags.invalid = TRUE; 1367178825Sdfr break; 1368178825Sdfr case 'U': 1369178825Sdfr ent->entry.flags.client = TRUE; 1370178825Sdfr break; 1371178825Sdfr case 'M': 1372178825Sdfr break; 1373178825Sdfr case 'W': 1374178825Sdfr case 'S': 1375178825Sdfr ent->entry.flags.server = TRUE; 1376178825Sdfr ent->entry.flags.client = TRUE; 1377178825Sdfr break; 1378178825Sdfr case 'L': 1379178825Sdfr ent->entry.flags.invalid = TRUE; 1380178825Sdfr break; 1381178825Sdfr case 'X': 1382178825Sdfr if (ent->entry.pw_end) { 1383178825Sdfr free(ent->entry.pw_end); 1384178825Sdfr ent->entry.pw_end = NULL; 1385178825Sdfr } 1386178825Sdfr break; 1387178825Sdfr case 'I': 1388178825Sdfr ent->entry.flags.server = TRUE; 1389178825Sdfr ent->entry.flags.client = TRUE; 1390178825Sdfr break; 1391178825Sdfr } 139272445Sassar } 1393178825Sdfr out2: 1394178825Sdfr free(samba_acct_flags); 139572445Sassar } 139672445Sassar 139772445Sassar ret = 0; 139872445Sassar 1399178825Sdfrout: 1400178825Sdfr if (unparsed_name) 140172445Sassar free(unparsed_name); 140272445Sassar 1403178825Sdfr if (ret) 140472445Sassar hdb_free_entry(context, ent); 140572445Sassar 140672445Sassar return ret; 140772445Sassar} 140872445Sassar 1409178825Sdfrstatic krb5_error_code 1410178825SdfrLDAP_close(krb5_context context, HDB * db) 141172445Sassar{ 1412178825Sdfr if (HDB2LDAP(db)) { 1413178825Sdfr ldap_unbind_ext(HDB2LDAP(db), NULL, NULL); 1414178825Sdfr ((struct hdbldapdb *)db->hdb_db)->h_lp = NULL; 1415178825Sdfr } 1416233294Sstas 141772445Sassar return 0; 141872445Sassar} 141972445Sassar 142072445Sassarstatic krb5_error_code 142172445SassarLDAP_lock(krb5_context context, HDB * db, int operation) 142272445Sassar{ 142372445Sassar return 0; 142472445Sassar} 142572445Sassar 1426178825Sdfrstatic krb5_error_code 1427178825SdfrLDAP_unlock(krb5_context context, HDB * db) 142872445Sassar{ 142972445Sassar return 0; 143072445Sassar} 143172445Sassar 143272445Sassarstatic krb5_error_code 1433178825SdfrLDAP_seq(krb5_context context, HDB * db, unsigned flags, hdb_entry_ex * entry) 143472445Sassar{ 143572445Sassar int msgid, rc, parserc; 143672445Sassar krb5_error_code ret; 143772445Sassar LDAPMessage *e; 143872445Sassar 1439178825Sdfr msgid = HDB2MSGID(db); 1440178825Sdfr if (msgid < 0) 144172445Sassar return HDB_ERR_NOENTRY; 144272445Sassar 144372445Sassar do { 1444178825Sdfr rc = ldap_result(HDB2LDAP(db), msgid, LDAP_MSG_ONE, NULL, &e); 144572445Sassar switch (rc) { 1446178825Sdfr case LDAP_RES_SEARCH_REFERENCE: 1447178825Sdfr ldap_msgfree(e); 1448178825Sdfr ret = 0; 1449178825Sdfr break; 145072445Sassar case LDAP_RES_SEARCH_ENTRY: 145172445Sassar /* We have an entry. Parse it. */ 1452233294Sstas ret = LDAP_message2entry(context, db, e, flags, entry); 145372445Sassar ldap_msgfree(e); 145472445Sassar break; 145572445Sassar case LDAP_RES_SEARCH_RESULT: 145672445Sassar /* We're probably at the end of the results. If not, abandon. */ 145772445Sassar parserc = 1458178825Sdfr ldap_parse_result(HDB2LDAP(db), e, NULL, NULL, NULL, 145972445Sassar NULL, NULL, 1); 1460233294Sstas ret = HDB_ERR_NOENTRY; 146172445Sassar if (parserc != LDAP_SUCCESS 146272445Sassar && parserc != LDAP_MORE_RESULTS_TO_RETURN) { 1463233294Sstas krb5_set_error_message(context, ret, "ldap_parse_result: %s", 1464233294Sstas ldap_err2string(parserc)); 1465233294Sstas ldap_abandon_ext(HDB2LDAP(db), msgid, NULL, NULL); 146672445Sassar } 1467178825Sdfr HDBSETMSGID(db, -1); 146872445Sassar break; 1469178825Sdfr case LDAP_SERVER_DOWN: 1470178825Sdfr ldap_msgfree(e); 1471178825Sdfr LDAP_close(context, db); 1472178825Sdfr HDBSETMSGID(db, -1); 1473178825Sdfr ret = ENETDOWN; 1474178825Sdfr break; 147572445Sassar default: 147672445Sassar /* Some unspecified error (timeout?). Abandon. */ 147772445Sassar ldap_msgfree(e); 1478233294Sstas ldap_abandon_ext(HDB2LDAP(db), msgid, NULL, NULL); 147972445Sassar ret = HDB_ERR_NOENTRY; 1480178825Sdfr HDBSETMSGID(db, -1); 148172445Sassar break; 148272445Sassar } 148372445Sassar } while (rc == LDAP_RES_SEARCH_REFERENCE); 148472445Sassar 148572445Sassar if (ret == 0) { 1486178825Sdfr if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { 1487178825Sdfr ret = hdb_unseal_keys(context, db, &entry->entry); 148872445Sassar if (ret) 1489178825Sdfr hdb_free_entry(context, entry); 149072445Sassar } 149172445Sassar } 149272445Sassar 149372445Sassar return ret; 149472445Sassar} 149572445Sassar 149672445Sassarstatic krb5_error_code 1497178825SdfrLDAP_firstkey(krb5_context context, HDB *db, unsigned flags, 1498178825Sdfr hdb_entry_ex *entry) 149972445Sassar{ 1500178825Sdfr krb5_error_code ret; 1501178825Sdfr int msgid; 150272445Sassar 1503178825Sdfr ret = LDAP__connect(context, db); 1504178825Sdfr if (ret) 1505178825Sdfr return ret; 150672445Sassar 1507178825Sdfr ret = LDAP_no_size_limit(context, HDB2LDAP(db)); 1508178825Sdfr if (ret) 1509178825Sdfr return ret; 151072445Sassar 1511233294Sstas ret = ldap_search_ext(HDB2LDAP(db), HDB2BASE(db), 1512178825Sdfr LDAP_SCOPE_SUBTREE, 1513178825Sdfr "(|(objectClass=krb5Principal)(objectClass=sambaSamAccount))", 1514233294Sstas krb5kdcentry_attrs, 0, 1515233294Sstas NULL, NULL, NULL, 0, &msgid); 1516178825Sdfr if (msgid < 0) 151772445Sassar return HDB_ERR_NOENTRY; 151872445Sassar 1519178825Sdfr HDBSETMSGID(db, msgid); 152072445Sassar 152172445Sassar return LDAP_seq(context, db, flags, entry); 152272445Sassar} 152372445Sassar 152472445Sassarstatic krb5_error_code 152572445SassarLDAP_nextkey(krb5_context context, HDB * db, unsigned flags, 1526178825Sdfr hdb_entry_ex * entry) 152772445Sassar{ 152872445Sassar return LDAP_seq(context, db, flags, entry); 152972445Sassar} 153072445Sassar 153172445Sassarstatic krb5_error_code 1532178825SdfrLDAP__connect(krb5_context context, HDB * db) 153372445Sassar{ 153490926Snectar int rc, version = LDAP_VERSION3; 1535127808Snectar /* 1536127808Snectar * Empty credentials to do a SASL bind with LDAP. Note that empty 1537127808Snectar * different from NULL credentials. If you provide NULL 1538127808Snectar * credentials instead of empty credentials you will get a SASL 1539127808Snectar * bind in progress message. 1540127808Snectar */ 1541127808Snectar struct berval bv = { 0, "" }; 154272445Sassar 1543178825Sdfr if (HDB2LDAP(db)) { 154472445Sassar /* connection has been opened. ping server. */ 154572445Sassar struct sockaddr_un addr; 1546178825Sdfr socklen_t len = sizeof(addr); 154772445Sassar int sd; 154872445Sassar 1549178825Sdfr if (ldap_get_option(HDB2LDAP(db), LDAP_OPT_DESC, &sd) == 0 && 155072445Sassar getpeername(sd, (struct sockaddr *) &addr, &len) < 0) { 155172445Sassar /* the other end has died. reopen. */ 155272445Sassar LDAP_close(context, db); 155372445Sassar } 155472445Sassar } 155572445Sassar 1556178825Sdfr if (HDB2LDAP(db) != NULL) /* server is UP */ 155772445Sassar return 0; 155872445Sassar 1559178825Sdfr rc = ldap_initialize(&((struct hdbldapdb *)db->hdb_db)->h_lp, HDB2URL(db)); 156072445Sassar if (rc != LDAP_SUCCESS) { 1561233294Sstas krb5_set_error_message(context, HDB_ERR_NOENTRY, "ldap_initialize: %s", 1562233294Sstas ldap_err2string(rc)); 156372445Sassar return HDB_ERR_NOENTRY; 156472445Sassar } 156572445Sassar 1566178825Sdfr rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_PROTOCOL_VERSION, 1567178825Sdfr (const void *)&version); 156872445Sassar if (rc != LDAP_SUCCESS) { 1569233294Sstas krb5_set_error_message(context, HDB_ERR_BADVERSION, 1570233294Sstas "ldap_set_option: %s", ldap_err2string(rc)); 1571178825Sdfr LDAP_close(context, db); 157290926Snectar return HDB_ERR_BADVERSION; 157372445Sassar } 157472445Sassar 1575178825Sdfr rc = ldap_sasl_bind_s(HDB2LDAP(db), NULL, "EXTERNAL", &bv, 1576178825Sdfr NULL, NULL, NULL); 1577127808Snectar if (rc != LDAP_SUCCESS) { 1578233294Sstas krb5_set_error_message(context, HDB_ERR_BADVERSION, 1579233294Sstas "ldap_sasl_bind_s: %s", ldap_err2string(rc)); 1580178825Sdfr LDAP_close(context, db); 1581127808Snectar return HDB_ERR_BADVERSION; 1582127808Snectar } 1583127808Snectar 158490926Snectar return 0; 158572445Sassar} 158672445Sassar 158772445Sassarstatic krb5_error_code 158872445SassarLDAP_open(krb5_context context, HDB * db, int flags, mode_t mode) 158972445Sassar{ 159072445Sassar /* Not the right place for this. */ 159172445Sassar#ifdef HAVE_SIGACTION 159290926Snectar struct sigaction sa; 159372445Sassar 159490926Snectar sa.sa_flags = 0; 159590926Snectar sa.sa_handler = SIG_IGN; 159690926Snectar sigemptyset(&sa.sa_mask); 159772445Sassar 159890926Snectar sigaction(SIGPIPE, &sa, NULL); 159972445Sassar#else 160072445Sassar signal(SIGPIPE, SIG_IGN); 160190926Snectar#endif /* HAVE_SIGACTION */ 160272445Sassar 160390926Snectar return LDAP__connect(context, db); 160472445Sassar} 160572445Sassar 160672445Sassarstatic krb5_error_code 1607233294SstasLDAP_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal, 1608233294Sstas unsigned flags, krb5_kvno kvno, hdb_entry_ex * entry) 160972445Sassar{ 161072445Sassar LDAPMessage *msg, *e; 161172445Sassar krb5_error_code ret; 161272445Sassar 1613178825Sdfr ret = LDAP_principal2message(context, db, principal, &msg); 1614178825Sdfr if (ret) 161572445Sassar return ret; 161672445Sassar 1617178825Sdfr e = ldap_first_entry(HDB2LDAP(db), msg); 161872445Sassar if (e == NULL) { 161972445Sassar ret = HDB_ERR_NOENTRY; 162072445Sassar goto out; 162172445Sassar } 162272445Sassar 1623233294Sstas ret = LDAP_message2entry(context, db, e, flags, entry); 162472445Sassar if (ret == 0) { 1625178825Sdfr if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) { 1626178825Sdfr ret = hdb_unseal_keys(context, db, &entry->entry); 162772445Sassar if (ret) 1628178825Sdfr hdb_free_entry(context, entry); 162972445Sassar } 163072445Sassar } 163172445Sassar 163272445Sassar out: 163372445Sassar ldap_msgfree(msg); 163472445Sassar 163572445Sassar return ret; 163672445Sassar} 163772445Sassar 163872445Sassarstatic krb5_error_code 1639233294SstasLDAP_fetch(krb5_context context, HDB * db, krb5_const_principal principal, 1640233294Sstas unsigned flags, hdb_entry_ex * entry) 1641233294Sstas{ 1642233294Sstas return LDAP_fetch_kvno(context, db, principal, 1643233294Sstas flags & (~HDB_F_KVNO_SPECIFIED), 0, entry); 1644233294Sstas} 1645233294Sstas 1646233294Sstasstatic krb5_error_code 164772445SassarLDAP_store(krb5_context context, HDB * db, unsigned flags, 1648178825Sdfr hdb_entry_ex * entry) 164972445Sassar{ 165072445Sassar LDAPMod **mods = NULL; 165172445Sassar krb5_error_code ret; 165290926Snectar const char *errfn; 165390926Snectar int rc; 165472445Sassar LDAPMessage *msg = NULL, *e = NULL; 165572445Sassar char *dn = NULL, *name = NULL; 165672445Sassar 1657178825Sdfr ret = LDAP_principal2message(context, db, entry->entry.principal, &msg); 1658178825Sdfr if (ret == 0) 1659178825Sdfr e = ldap_first_entry(HDB2LDAP(db), msg); 166072445Sassar 1661178825Sdfr ret = krb5_unparse_name(context, entry->entry.principal, &name); 1662178825Sdfr if (ret) { 1663178825Sdfr free(name); 1664178825Sdfr return ret; 166572445Sassar } 166672445Sassar 1667178825Sdfr ret = hdb_seal_keys(context, db, &entry->entry); 1668178825Sdfr if (ret) 166972445Sassar goto out; 167072445Sassar 167172445Sassar /* turn new entry into LDAPMod array */ 167272445Sassar ret = LDAP_entry2mods(context, db, entry, e, &mods); 1673178825Sdfr if (ret) 167472445Sassar goto out; 167572445Sassar 167672445Sassar if (e == NULL) { 1677178825Sdfr ret = asprintf(&dn, "krb5PrincipalName=%s,%s", name, HDB2CREATE(db)); 167872445Sassar if (ret < 0) { 167972445Sassar ret = ENOMEM; 1680233294Sstas krb5_set_error_message(context, ret, "asprintf: out of memory"); 168172445Sassar goto out; 168272445Sassar } 168372445Sassar } else if (flags & HDB_F_REPLACE) { 168472445Sassar /* Entry exists, and we're allowed to replace it. */ 1685178825Sdfr dn = ldap_get_dn(HDB2LDAP(db), e); 168672445Sassar } else { 168772445Sassar /* Entry exists, but we're not allowed to replace it. Bail. */ 168872445Sassar ret = HDB_ERR_EXISTS; 168972445Sassar goto out; 169072445Sassar } 169172445Sassar 169272445Sassar /* write entry into directory */ 169372445Sassar if (e == NULL) { 169472445Sassar /* didn't exist before */ 1695233294Sstas rc = ldap_add_ext_s(HDB2LDAP(db), dn, mods, NULL, NULL ); 1696233294Sstas errfn = "ldap_add_ext_s"; 169772445Sassar } else { 169872445Sassar /* already existed, send deltas only */ 1699233294Sstas rc = ldap_modify_ext_s(HDB2LDAP(db), dn, mods, NULL, NULL ); 1700233294Sstas errfn = "ldap_modify_ext_s"; 170172445Sassar } 170272445Sassar 1703178825Sdfr if (check_ldap(context, db, rc)) { 1704178825Sdfr char *ld_error = NULL; 1705178825Sdfr ldap_get_option(HDB2LDAP(db), LDAP_OPT_ERROR_STRING, 1706178825Sdfr &ld_error); 1707233294Sstas ret = HDB_ERR_CANT_LOCK_DB; 1708233294Sstas krb5_set_error_message(context, ret, "%s: %s (DN=%s) %s: %s", 1709178825Sdfr errfn, name, dn, ldap_err2string(rc), ld_error); 1710178825Sdfr } else 171172445Sassar ret = 0; 171272445Sassar 171372445Sassar out: 171472445Sassar /* free stuff */ 1715178825Sdfr if (dn) 171672445Sassar free(dn); 1717178825Sdfr if (msg) 171872445Sassar ldap_msgfree(msg); 1719178825Sdfr if (mods) 172072445Sassar ldap_mods_free(mods, 1); 1721178825Sdfr if (name) 172272445Sassar free(name); 172372445Sassar 172472445Sassar return ret; 172572445Sassar} 172672445Sassar 172772445Sassarstatic krb5_error_code 1728178825SdfrLDAP_remove(krb5_context context, HDB *db, krb5_const_principal principal) 172972445Sassar{ 173072445Sassar krb5_error_code ret; 173172445Sassar LDAPMessage *msg, *e; 173272445Sassar char *dn = NULL; 173390926Snectar int rc, limit = LDAP_NO_LIMIT; 173472445Sassar 1735178825Sdfr ret = LDAP_principal2message(context, db, principal, &msg); 1736178825Sdfr if (ret) 173772445Sassar goto out; 173872445Sassar 1739178825Sdfr e = ldap_first_entry(HDB2LDAP(db), msg); 174072445Sassar if (e == NULL) { 174172445Sassar ret = HDB_ERR_NOENTRY; 174272445Sassar goto out; 174372445Sassar } 174472445Sassar 1745178825Sdfr dn = ldap_get_dn(HDB2LDAP(db), e); 174672445Sassar if (dn == NULL) { 174772445Sassar ret = HDB_ERR_NOENTRY; 174872445Sassar goto out; 174972445Sassar } 175072445Sassar 1751178825Sdfr rc = ldap_set_option(HDB2LDAP(db), LDAP_OPT_SIZELIMIT, (const void *)&limit); 175290926Snectar if (rc != LDAP_SUCCESS) { 1753233294Sstas ret = HDB_ERR_BADVERSION; 1754233294Sstas krb5_set_error_message(context, ret, "ldap_set_option: %s", 1755178825Sdfr ldap_err2string(rc)); 175690926Snectar goto out; 175790926Snectar } 175872445Sassar 1759233294Sstas rc = ldap_delete_ext_s(HDB2LDAP(db), dn, NULL, NULL ); 1760178825Sdfr if (check_ldap(context, db, rc)) { 1761178825Sdfr ret = HDB_ERR_CANT_LOCK_DB; 1762233294Sstas krb5_set_error_message(context, ret, "ldap_delete_ext_s: %s", 1763233294Sstas ldap_err2string(rc)); 1764178825Sdfr } else 176572445Sassar ret = 0; 176672445Sassar 176772445Sassar out: 1768178825Sdfr if (dn != NULL) 176972445Sassar free(dn); 1770178825Sdfr if (msg != NULL) 177172445Sassar ldap_msgfree(msg); 177272445Sassar 177372445Sassar return ret; 177472445Sassar} 177572445Sassar 177672445Sassarstatic krb5_error_code 1777178825SdfrLDAP_destroy(krb5_context context, HDB * db) 177872445Sassar{ 1779178825Sdfr krb5_error_code ret; 178072445Sassar 1781178825Sdfr LDAP_close(context, db); 178272445Sassar 178372445Sassar ret = hdb_clear_master_key(context, db); 1784178825Sdfr if (HDB2BASE(db)) 1785178825Sdfr free(HDB2BASE(db)); 1786178825Sdfr if (HDB2CREATE(db)) 1787178825Sdfr free(HDB2CREATE(db)); 1788178825Sdfr if (HDB2URL(db)) 1789178825Sdfr free(HDB2URL(db)); 1790178825Sdfr if (db->hdb_name) 1791178825Sdfr free(db->hdb_name); 1792178825Sdfr free(db->hdb_db); 179372445Sassar free(db); 179472445Sassar 179572445Sassar return ret; 179672445Sassar} 179772445Sassar 1798233294Sstasstatic krb5_error_code 1799178825Sdfrhdb_ldap_common(krb5_context context, 1800178825Sdfr HDB ** db, 1801178825Sdfr const char *search_base, 1802178825Sdfr const char *url) 180372445Sassar{ 1804178825Sdfr struct hdbldapdb *h; 1805178825Sdfr const char *create_base = NULL; 1806178825Sdfr 1807178825Sdfr if (search_base == NULL && search_base[0] == '\0') { 1808233294Sstas krb5_set_error_message(context, ENOMEM, "ldap search base not configured"); 1809178825Sdfr return ENOMEM; /* XXX */ 1810178825Sdfr } 1811178825Sdfr 1812178825Sdfr if (structural_object == NULL) { 1813178825Sdfr const char *p; 1814178825Sdfr 1815233294Sstas p = krb5_config_get_string(context, NULL, "kdc", 1816178825Sdfr "hdb-ldap-structural-object", NULL); 1817178825Sdfr if (p == NULL) 1818178825Sdfr p = default_structural_object; 1819178825Sdfr structural_object = strdup(p); 1820178825Sdfr if (structural_object == NULL) { 1821233294Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 1822178825Sdfr return ENOMEM; 1823178825Sdfr } 1824178825Sdfr } 1825178825Sdfr 1826233294Sstas samba_forwardable = 1827178825Sdfr krb5_config_get_bool_default(context, NULL, TRUE, 1828178825Sdfr "kdc", "hdb-samba-forwardable", NULL); 1829178825Sdfr 1830178825Sdfr *db = calloc(1, sizeof(**db)); 183190926Snectar if (*db == NULL) { 1832233294Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 183372445Sassar return ENOMEM; 183490926Snectar } 1835178825Sdfr memset(*db, 0, sizeof(**db)); 183672445Sassar 1837178825Sdfr h = calloc(1, sizeof(*h)); 1838178825Sdfr if (h == NULL) { 1839178825Sdfr free(*db); 1840178825Sdfr *db = NULL; 1841233294Sstas krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); 1842178825Sdfr return ENOMEM; 1843178825Sdfr } 1844178825Sdfr (*db)->hdb_db = h; 184590926Snectar 1846178825Sdfr /* XXX */ 1847178825Sdfr if (asprintf(&(*db)->hdb_name, "ldap:%s", search_base) == -1) { 1848178825Sdfr LDAP_destroy(context, *db); 1849178825Sdfr *db = NULL; 1850233294Sstas krb5_set_error_message(context, ENOMEM, "strdup: out of memory"); 1851178825Sdfr return ENOMEM; 185290926Snectar } 185390926Snectar 1854178825Sdfr h->h_url = strdup(url); 1855178825Sdfr h->h_base = strdup(search_base); 1856178825Sdfr if (h->h_url == NULL || h->h_base == NULL) { 1857178825Sdfr LDAP_destroy(context, *db); 1858178825Sdfr *db = NULL; 1859233294Sstas krb5_set_error_message(context, ENOMEM, "strdup: out of memory"); 1860178825Sdfr return ENOMEM; 1861178825Sdfr } 186272445Sassar 1863233294Sstas create_base = krb5_config_get_string(context, NULL, "kdc", 1864178825Sdfr "hdb-ldap-create-base", NULL); 1865178825Sdfr if (create_base == NULL) 1866178825Sdfr create_base = h->h_base; 1867178825Sdfr 1868178825Sdfr h->h_createbase = strdup(create_base); 1869178825Sdfr if (h->h_createbase == NULL) { 1870178825Sdfr LDAP_destroy(context, *db); 1871178825Sdfr *db = NULL; 1872233294Sstas krb5_set_error_message(context, ENOMEM, "strdup: out of memory"); 1873178825Sdfr return ENOMEM; 1874178825Sdfr } 1875178825Sdfr 1876178825Sdfr (*db)->hdb_master_key_set = 0; 1877178825Sdfr (*db)->hdb_openp = 0; 1878233294Sstas (*db)->hdb_capability_flags = 0; 1879178825Sdfr (*db)->hdb_open = LDAP_open; 1880178825Sdfr (*db)->hdb_close = LDAP_close; 1881233294Sstas (*db)->hdb_fetch_kvno = LDAP_fetch_kvno; 1882178825Sdfr (*db)->hdb_store = LDAP_store; 1883178825Sdfr (*db)->hdb_remove = LDAP_remove; 1884178825Sdfr (*db)->hdb_firstkey = LDAP_firstkey; 1885178825Sdfr (*db)->hdb_nextkey = LDAP_nextkey; 1886178825Sdfr (*db)->hdb_lock = LDAP_lock; 1887178825Sdfr (*db)->hdb_unlock = LDAP_unlock; 1888178825Sdfr (*db)->hdb_rename = NULL; 1889178825Sdfr (*db)->hdb__get = NULL; 1890178825Sdfr (*db)->hdb__put = NULL; 1891178825Sdfr (*db)->hdb__del = NULL; 1892178825Sdfr (*db)->hdb_destroy = LDAP_destroy; 1893178825Sdfr 189472445Sassar return 0; 189572445Sassar} 189672445Sassar 1897178825Sdfrkrb5_error_code 1898178825Sdfrhdb_ldap_create(krb5_context context, HDB ** db, const char *arg) 1899178825Sdfr{ 1900178825Sdfr return hdb_ldap_common(context, db, arg, "ldapi:///"); 1901178825Sdfr} 1902178825Sdfr 1903178825Sdfrkrb5_error_code 1904178825Sdfrhdb_ldapi_create(krb5_context context, HDB ** db, const char *arg) 1905178825Sdfr{ 1906178825Sdfr krb5_error_code ret; 1907178825Sdfr char *search_base, *p; 1908178825Sdfr 1909178825Sdfr asprintf(&p, "ldapi:%s", arg); 1910178825Sdfr if (p == NULL) { 1911178825Sdfr *db = NULL; 1912233294Sstas krb5_set_error_message(context, ENOMEM, "out of memory"); 1913178825Sdfr return ENOMEM; 1914178825Sdfr } 1915178825Sdfr search_base = strchr(p + strlen("ldapi://"), ':'); 1916178825Sdfr if (search_base == NULL) { 1917178825Sdfr *db = NULL; 1918233294Sstas krb5_set_error_message(context, HDB_ERR_BADVERSION, 1919233294Sstas "search base missing"); 1920178825Sdfr return HDB_ERR_BADVERSION; 1921178825Sdfr } 1922178825Sdfr *search_base = '\0'; 1923178825Sdfr search_base++; 1924178825Sdfr 1925178825Sdfr ret = hdb_ldap_common(context, db, search_base, p); 1926178825Sdfr free(p); 1927178825Sdfr return ret; 1928178825Sdfr} 1929178825Sdfr 1930178825Sdfr#ifdef OPENLDAP_MODULE 1931178825Sdfr 1932178825Sdfrstruct hdb_so_method hdb_ldap_interface = { 1933178825Sdfr HDB_INTERFACE_VERSION, 1934178825Sdfr "ldap", 1935178825Sdfr hdb_ldap_create 1936178825Sdfr}; 1937178825Sdfr 1938178825Sdfrstruct hdb_so_method hdb_ldapi_interface = { 1939178825Sdfr HDB_INTERFACE_VERSION, 1940178825Sdfr "ldapi", 1941178825Sdfr hdb_ldapi_create 1942178825Sdfr}; 1943178825Sdfr 1944178825Sdfr#endif 1945178825Sdfr 194672445Sassar#endif /* OPENLDAP */ 1947