X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Flib%2Fsmbldap.c;h=cdd350fffa1e23051594b7efbedc197456472085;hb=e18610a197aab80a32cae8c1e09b96496679bbad;hp=f7ca1c167bdc82623bef3c05e548b93e75f040e2;hpb=f381faa8d6fc2c4205e7d59c91f00324f70ca8b9;p=samba.git diff --git a/source3/lib/smbldap.c b/source3/lib/smbldap.c index f7ca1c167bd..cdd350fffa1 100644 --- a/source3/lib/smbldap.c +++ b/source3/lib/smbldap.c @@ -24,10 +24,9 @@ #include "includes.h" #include "smbldap.h" - -#ifndef LDAP_OPT_SUCCESS -#define LDAP_OPT_SUCCESS 0 -#endif +#include "../libcli/security/security.h" +#include +#include "lib/param/loadparm.h" /* Try not to hit the up or down server forever */ @@ -36,227 +35,54 @@ #define SMBLDAP_IDLE_TIME 150 /* After 2.5 minutes disconnect */ +struct smbldap_state { + LDAP *ldap_struct; + pid_t pid; + time_t last_ping; /* monotonic */ + /* retrieve-once info */ + const char *uri; -/* attributes used by Samba 2.2 */ - -ATTRIB_MAP_ENTRY attrib_map_v22[] = { - { LDAP_ATTR_UID, "uid" }, - { LDAP_ATTR_UIDNUMBER, LDAP_ATTRIBUTE_UIDNUMBER}, - { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER}, - { LDAP_ATTR_UNIX_HOME, "homeDirectory" }, - { LDAP_ATTR_PWD_LAST_SET, "pwdLastSet" }, - { LDAP_ATTR_PWD_CAN_CHANGE, "pwdCanChange" }, - { LDAP_ATTR_PWD_MUST_CHANGE, "pwdMustChange" }, - { LDAP_ATTR_LOGON_TIME, "logonTime" }, - { LDAP_ATTR_LOGOFF_TIME, "logoffTime" }, - { LDAP_ATTR_KICKOFF_TIME, "kickoffTime" }, - { LDAP_ATTR_CN, "cn" }, - { LDAP_ATTR_SN, "sn" }, - { LDAP_ATTR_DISPLAY_NAME, "displayName" }, - { LDAP_ATTR_HOME_PATH, "smbHome" }, - { LDAP_ATTR_HOME_DRIVE, "homeDrive" }, - { LDAP_ATTR_LOGON_SCRIPT, "scriptPath" }, - { LDAP_ATTR_PROFILE_PATH, "profilePath" }, - { LDAP_ATTR_DESC, "description" }, - { LDAP_ATTR_USER_WKS, "userWorkstations"}, - { LDAP_ATTR_USER_RID, "rid" }, - { LDAP_ATTR_PRIMARY_GROUP_RID, "primaryGroupID"}, - { LDAP_ATTR_LMPW, "lmPassword" }, - { LDAP_ATTR_NTPW, "ntPassword" }, - { LDAP_ATTR_DOMAIN, "domain" }, - { LDAP_ATTR_OBJCLASS, "objectClass" }, - { LDAP_ATTR_ACB_INFO, "acctFlags" }, - { LDAP_ATTR_MOD_TIMESTAMP, "modifyTimestamp" }, - { LDAP_ATTR_LIST_END, NULL } -}; + /* credentials */ + bool anonymous; + char *bind_dn; + char *bind_secret; + smbldap_bind_callback_fn bind_callback; + void *bind_callback_data; -ATTRIB_MAP_ENTRY attrib_map_to_delete_v22[] = { - { LDAP_ATTR_PWD_LAST_SET, "pwdLastSet" }, - { LDAP_ATTR_PWD_CAN_CHANGE, "pwdCanChange" }, - { LDAP_ATTR_PWD_MUST_CHANGE, "pwdMustChange" }, - { LDAP_ATTR_LOGON_TIME, "logonTime" }, - { LDAP_ATTR_LOGOFF_TIME, "logoffTime" }, - { LDAP_ATTR_KICKOFF_TIME, "kickoffTime" }, - { LDAP_ATTR_DISPLAY_NAME, "displayName" }, - { LDAP_ATTR_HOME_PATH, "smbHome" }, - { LDAP_ATTR_HOME_DRIVE, "homeDrives" }, - { LDAP_ATTR_LOGON_SCRIPT, "scriptPath" }, - { LDAP_ATTR_PROFILE_PATH, "profilePath" }, - { LDAP_ATTR_USER_WKS, "userWorkstations"}, - { LDAP_ATTR_USER_RID, "rid" }, - { LDAP_ATTR_PRIMARY_GROUP_RID, "primaryGroupID"}, - { LDAP_ATTR_LMPW, "lmPassword" }, - { LDAP_ATTR_NTPW, "ntPassword" }, - { LDAP_ATTR_DOMAIN, "domain" }, - { LDAP_ATTR_ACB_INFO, "acctFlags" }, - { LDAP_ATTR_LIST_END, NULL } -}; + bool paged_results; -/* attributes used by Samba 3.0's sambaSamAccount */ - -ATTRIB_MAP_ENTRY attrib_map_v30[] = { - { LDAP_ATTR_UID, "uid" }, - { LDAP_ATTR_UIDNUMBER, LDAP_ATTRIBUTE_UIDNUMBER}, - { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER}, - { LDAP_ATTR_UNIX_HOME, "homeDirectory" }, - { LDAP_ATTR_PWD_LAST_SET, "sambaPwdLastSet" }, - { LDAP_ATTR_PWD_CAN_CHANGE, "sambaPwdCanChange" }, - { LDAP_ATTR_PWD_MUST_CHANGE, "sambaPwdMustChange" }, - { LDAP_ATTR_LOGON_TIME, "sambaLogonTime" }, - { LDAP_ATTR_LOGOFF_TIME, "sambaLogoffTime" }, - { LDAP_ATTR_KICKOFF_TIME, "sambaKickoffTime" }, - { LDAP_ATTR_CN, "cn" }, - { LDAP_ATTR_SN, "sn" }, - { LDAP_ATTR_DISPLAY_NAME, "displayName" }, - { LDAP_ATTR_HOME_DRIVE, "sambaHomeDrive" }, - { LDAP_ATTR_HOME_PATH, "sambaHomePath" }, - { LDAP_ATTR_LOGON_SCRIPT, "sambaLogonScript" }, - { LDAP_ATTR_PROFILE_PATH, "sambaProfilePath" }, - { LDAP_ATTR_DESC, "description" }, - { LDAP_ATTR_USER_WKS, "sambaUserWorkstations" }, - { LDAP_ATTR_USER_SID, LDAP_ATTRIBUTE_SID }, - { LDAP_ATTR_PRIMARY_GROUP_SID, "sambaPrimaryGroupSID" }, - { LDAP_ATTR_LMPW, "sambaLMPassword" }, - { LDAP_ATTR_NTPW, "sambaNTPassword" }, - { LDAP_ATTR_DOMAIN, "sambaDomainName" }, - { LDAP_ATTR_OBJCLASS, "objectClass" }, - { LDAP_ATTR_ACB_INFO, "sambaAcctFlags" }, - { LDAP_ATTR_MUNGED_DIAL, "sambaMungedDial" }, - { LDAP_ATTR_BAD_PASSWORD_COUNT, "sambaBadPasswordCount" }, - { LDAP_ATTR_BAD_PASSWORD_TIME, "sambaBadPasswordTime" }, - { LDAP_ATTR_PWD_HISTORY, "sambaPasswordHistory" }, - { LDAP_ATTR_MOD_TIMESTAMP, "modifyTimestamp" }, - { LDAP_ATTR_LOGON_HOURS, "sambaLogonHours" }, - { LDAP_ATTR_LIST_END, NULL } -}; + unsigned int num_failures; -ATTRIB_MAP_ENTRY attrib_map_to_delete_v30[] = { - { LDAP_ATTR_PWD_LAST_SET, "sambaPwdLastSet" }, - { LDAP_ATTR_PWD_CAN_CHANGE, "sambaPwdCanChange" }, - { LDAP_ATTR_PWD_MUST_CHANGE, "sambaPwdMustChange" }, - { LDAP_ATTR_LOGON_TIME, "sambaLogonTime" }, - { LDAP_ATTR_LOGOFF_TIME, "sambaLogoffTime" }, - { LDAP_ATTR_KICKOFF_TIME, "sambaKickoffTime" }, - { LDAP_ATTR_DISPLAY_NAME, "displayName" }, - { LDAP_ATTR_HOME_DRIVE, "sambaHomeDrive" }, - { LDAP_ATTR_HOME_PATH, "sambaHomePath" }, - { LDAP_ATTR_LOGON_SCRIPT, "sambaLogonScript" }, - { LDAP_ATTR_PROFILE_PATH, "sambaProfilePath" }, - { LDAP_ATTR_USER_WKS, "sambaUserWorkstations" }, - { LDAP_ATTR_USER_SID, LDAP_ATTRIBUTE_SID }, - { LDAP_ATTR_PRIMARY_GROUP_SID, "sambaPrimaryGroupSID" }, - { LDAP_ATTR_LMPW, "sambaLMPassword" }, - { LDAP_ATTR_NTPW, "sambaNTPassword" }, - { LDAP_ATTR_DOMAIN, "sambaDomainName" }, - { LDAP_ATTR_ACB_INFO, "sambaAcctFlags" }, - { LDAP_ATTR_MUNGED_DIAL, "sambaMungedDial" }, - { LDAP_ATTR_BAD_PASSWORD_COUNT, "sambaBadPasswordCount" }, - { LDAP_ATTR_BAD_PASSWORD_TIME, "sambaBadPasswordTime" }, - { LDAP_ATTR_PWD_HISTORY, "sambaPasswordHistory" }, - { LDAP_ATTR_LOGON_HOURS, "sambaLogonHours" }, - { LDAP_ATTR_LIST_END, NULL } -}; - -/* attributes used for allocating RIDs */ - -ATTRIB_MAP_ENTRY dominfo_attr_list[] = { - { LDAP_ATTR_DOMAIN, "sambaDomainName" }, - { LDAP_ATTR_NEXT_RID, "sambaNextRid" }, - { LDAP_ATTR_NEXT_USERRID, "sambaNextUserRid" }, - { LDAP_ATTR_NEXT_GROUPRID, "sambaNextGroupRid" }, - { LDAP_ATTR_DOM_SID, LDAP_ATTRIBUTE_SID }, - { LDAP_ATTR_ALGORITHMIC_RID_BASE,"sambaAlgorithmicRidBase"}, - { LDAP_ATTR_OBJCLASS, "objectClass" }, - { LDAP_ATTR_LIST_END, NULL }, -}; + time_t last_use; /* monotonic */ + struct tevent_context *tevent_context; + struct tevent_timer *idle_event; -/* Samba 3.0 group mapping attributes */ - -ATTRIB_MAP_ENTRY groupmap_attr_list[] = { - { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER}, - { LDAP_ATTR_GROUP_SID, LDAP_ATTRIBUTE_SID }, - { LDAP_ATTR_GROUP_TYPE, "sambaGroupType" }, - { LDAP_ATTR_SID_LIST, "sambaSIDList" }, - { LDAP_ATTR_DESC, "description" }, - { LDAP_ATTR_DISPLAY_NAME, "displayName" }, - { LDAP_ATTR_CN, "cn" }, - { LDAP_ATTR_OBJCLASS, "objectClass" }, - { LDAP_ATTR_LIST_END, NULL } + struct timeval last_rebind; /* monotonic */ }; -ATTRIB_MAP_ENTRY groupmap_attr_list_to_delete[] = { - { LDAP_ATTR_GROUP_SID, LDAP_ATTRIBUTE_SID }, - { LDAP_ATTR_GROUP_TYPE, "sambaGroupType" }, - { LDAP_ATTR_DESC, "description" }, - { LDAP_ATTR_DISPLAY_NAME, "displayName" }, - { LDAP_ATTR_SID_LIST, "sambaSIDList" }, - { LDAP_ATTR_LIST_END, NULL } -}; - -/* idmap_ldap sambaUnixIdPool */ - -ATTRIB_MAP_ENTRY idpool_attr_list[] = { - { LDAP_ATTR_UIDNUMBER, LDAP_ATTRIBUTE_UIDNUMBER}, - { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER}, - { LDAP_ATTR_OBJCLASS, "objectClass" }, - { LDAP_ATTR_LIST_END, NULL } -}; - -ATTRIB_MAP_ENTRY sidmap_attr_list[] = { - { LDAP_ATTR_SID, LDAP_ATTRIBUTE_SID }, - { LDAP_ATTR_UIDNUMBER, LDAP_ATTRIBUTE_UIDNUMBER}, - { LDAP_ATTR_GIDNUMBER, LDAP_ATTRIBUTE_GIDNUMBER}, - { LDAP_ATTR_OBJCLASS, "objectClass" }, - { LDAP_ATTR_LIST_END, NULL } -}; - -/********************************************************************** - perform a simple table lookup and return the attribute name - **********************************************************************/ - - const char* get_attr_key2string( ATTRIB_MAP_ENTRY table[], int key ) +LDAP *smbldap_get_ldap(struct smbldap_state *state) { - int i = 0; - - while ( table[i].attrib != LDAP_ATTR_LIST_END ) { - if ( table[i].attrib == key ) - return table[i].name; - i++; - } - - return NULL; + return state->ldap_struct; } - -/********************************************************************** - Return the list of attribute names from a mapping table - **********************************************************************/ - - const char** get_attr_list( TALLOC_CTX *mem_ctx, ATTRIB_MAP_ENTRY table[] ) +bool smbldap_get_paged_results(struct smbldap_state *state) { - const char **names; - int i = 0; - - while ( table[i].attrib != LDAP_ATTR_LIST_END ) - i++; - i++; - - names = TALLOC_ARRAY( mem_ctx, const char*, i ); - if ( !names ) { - DEBUG(0,("get_attr_list: out of memory\n")); - return NULL; - } - - i = 0; - while ( table[i].attrib != LDAP_ATTR_LIST_END ) { - names[i] = talloc_strdup( names, table[i].name ); - i++; - } - names[i] = NULL; + return state->paged_results; +} - return names; +void smbldap_set_paged_results(struct smbldap_state *state, + bool paged_results) +{ + state->paged_results = paged_results; } +void smbldap_set_bind_callback(struct smbldap_state *state, + smbldap_bind_callback_fn callback, + void *callback_data) +{ + state->bind_callback = callback; + state->bind_callback_data = callback_data; +} /******************************************************************* Search an attribute and return the first value found. ******************************************************************/ @@ -266,6 +92,7 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = { int max_len) { char **values; + size_t size = 0; if ( !attribute ) return False; @@ -278,7 +105,7 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = { return False; } - if (convert_string(CH_UTF8, CH_UNIX,values[0], -1, value, max_len, False) == (size_t)-1) { + if (!convert_string(CH_UTF8, CH_UNIX,values[0], -1, value, max_len, &size)) { DEBUG(1, ("smbldap_get_single_attribute: string conversion of [%s] = [%s] failed!\n", attribute, values[0])); ldap_value_free(values); @@ -333,6 +160,40 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = { return result; } + char * smbldap_talloc_first_attribute(LDAP *ldap_struct, LDAPMessage *entry, + const char *attribute, + TALLOC_CTX *mem_ctx) +{ + char **values; + char *result; + size_t converted_size; + + if (attribute == NULL) { + return NULL; + } + + values = ldap_get_values(ldap_struct, entry, attribute); + + if (values == NULL) { + DEBUG(10, ("attribute %s does not exist\n", attribute)); + return NULL; + } + + if (!pull_utf8_talloc(mem_ctx, &result, values[0], &converted_size)) { + DEBUG(10, ("pull_utf8_talloc failed\n")); + ldap_value_free(values); + return NULL; + } + + ldap_value_free(values); + +#ifdef DEBUG_PASSWORDS + DEBUG (100, ("smbldap_get_first_attribute: [%s] = [%s]\n", + attribute, result)); +#endif + return result; +} + char * smbldap_talloc_smallest_attribute(LDAP *ldap_struct, LDAPMessage *entry, const char *attribute, TALLOC_CTX *mem_ctx) @@ -372,7 +233,7 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = { return NULL; } - if (StrCaseCmp(tmp, result) < 0) { + if (strcasecmp_m(tmp, result) < 0) { TALLOC_FREE(result); result = tmp; } else { @@ -417,15 +278,15 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = { struct dom_sid *sid) { DATA_BLOB blob; - bool ret; + struct sid_parse_ret ret; if (!smbldap_talloc_single_blob(talloc_tos(), ld, msg, attrib, &blob)) { return false; } - ret = sid_parse((char *)blob.data, blob.length, sid); + ret = sid_parse(blob.data, blob.length, sid); TALLOC_FREE(blob.data); - return ret; + return (ret.len != -1); } static int ldapmsg_destructor(LDAPMessage **result) { @@ -433,7 +294,7 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = { return 0; } - void talloc_autofree_ldapmsg(TALLOC_CTX *mem_ctx, LDAPMessage *result) + void smbldap_talloc_autofree_ldapmsg(TALLOC_CTX *mem_ctx, LDAPMessage *result) { LDAPMessage **handle; @@ -441,7 +302,7 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = { return; } - handle = TALLOC_P(mem_ctx, LDAPMessage *); + handle = talloc(mem_ctx, LDAPMessage *); SMB_ASSERT(handle != NULL); *handle = result; @@ -453,7 +314,7 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = { return 0; } - void talloc_autofree_ldapmod(TALLOC_CTX *mem_ctx, LDAPMod **mod) + void smbldap_talloc_autofree_ldapmod(TALLOC_CTX *mem_ctx, LDAPMod **mod) { LDAPMod ***handle; @@ -461,7 +322,7 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = { return; } - handle = TALLOC_P(mem_ctx, LDAPMod **); + handle = talloc(mem_ctx, LDAPMod **); SMB_ASSERT(handle != NULL); *handle = mod; @@ -473,7 +334,7 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = { manage memory used by the array, by each struct, and values ***********************************************************************/ - void smbldap_set_mod (LDAPMod *** modlist, int modop, const char *attribute, const char *value) +static void smbldap_set_mod_internal(LDAPMod *** modlist, int modop, const char *attribute, const char *value, const DATA_BLOB *blob) { LDAPMod **mods; int i; @@ -487,7 +348,7 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = { return; } -#if 0 /* commented out after discussion with abartlet. Do not reenable. +#if 0 /* commented out after discussion with abartlet. Do not re-enable. left here so other do not re-add similar code --jerry */ if (value == NULL || *value == '\0') return; @@ -524,7 +385,27 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = { mods[i + 1] = NULL; } - if (value != NULL) { + if (blob && (modop & LDAP_MOD_BVALUES)) { + j = 0; + if (mods[i]->mod_bvalues != NULL) { + for (; mods[i]->mod_bvalues[j] != NULL; j++); + } + mods[i]->mod_bvalues = SMB_REALLOC_ARRAY(mods[i]->mod_bvalues, struct berval *, j + 2); + + if (mods[i]->mod_bvalues == NULL) { + smb_panic("smbldap_set_mod: out of memory!"); + /* notreached. */ + } + + mods[i]->mod_bvalues[j] = SMB_MALLOC_P(struct berval); + SMB_ASSERT(mods[i]->mod_bvalues[j] != NULL); + + mods[i]->mod_bvalues[j]->bv_val = (char *)smb_memdup(blob->data, blob->length); + SMB_ASSERT(mods[i]->mod_bvalues[j]->bv_val != NULL); + mods[i]->mod_bvalues[j]->bv_len = blob->length; + + mods[i]->mod_bvalues[j + 1] = NULL; + } else if (value != NULL) { char *utf8_value = NULL; size_t converted_size; @@ -553,43 +434,59 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = { *modlist = mods; } + void smbldap_set_mod (LDAPMod *** modlist, int modop, const char *attribute, const char *value) +{ + smbldap_set_mod_internal(modlist, modop, attribute, value, NULL); +} + + void smbldap_set_mod_blob(LDAPMod *** modlist, int modop, const char *attribute, const DATA_BLOB *value) +{ + smbldap_set_mod_internal(modlist, modop | LDAP_MOD_BVALUES, attribute, NULL, value); +} + /********************************************************************** Set attribute to newval in LDAP, regardless of what value the attribute had in LDAP before. *********************************************************************/ - void smbldap_make_mod(LDAP *ldap_struct, LDAPMessage *existing, - LDAPMod ***mods, - const char *attribute, const char *newval) +static void smbldap_make_mod_internal(LDAP *ldap_struct, LDAPMessage *existing, + LDAPMod ***mods, + const char *attribute, int op, + const char *newval, + const DATA_BLOB *newblob) { char oldval[2048]; /* current largest allowed value is mungeddial */ bool existed; - - if (attribute == NULL) { - /* This can actually happen for ldapsam_compat where we for - * example don't have a password history */ - return; - } + DATA_BLOB oldblob = data_blob_null; if (existing != NULL) { - existed = smbldap_get_single_attribute(ldap_struct, existing, attribute, oldval, sizeof(oldval)); + if (op & LDAP_MOD_BVALUES) { + existed = smbldap_talloc_single_blob(talloc_tos(), ldap_struct, existing, attribute, &oldblob); + } else { + existed = smbldap_get_single_attribute(ldap_struct, existing, attribute, oldval, sizeof(oldval)); + } } else { existed = False; *oldval = '\0'; } - /* all of our string attributes are case insensitive */ - - if (existed && newval && (StrCaseCmp(oldval, newval) == 0)) { + if (existed) { + bool equal = false; + if (op & LDAP_MOD_BVALUES) { + equal = (newblob && (data_blob_cmp(&oldblob, newblob) == 0)); + } else { + /* all of our string attributes are case insensitive */ + equal = (newval && (strcasecmp_m(oldval, newval) == 0)); + } - /* Believe it or not, but LDAP will deny a delete and - an add at the same time if the values are the - same... */ - DEBUG(10,("smbldap_make_mod: attribute |%s| not changed.\n", attribute)); - return; - } + if (equal) { + /* Believe it or not, but LDAP will deny a delete and + an add at the same time if the values are the + same... */ + DEBUG(10,("smbldap_make_mod: attribute |%s| not changed.\n", attribute)); + return; + } - if (existed) { /* There has been no value before, so don't delete it. * Here's a possible race: We might end up with * duplicate attributes */ @@ -601,20 +498,48 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = { * in Novell NDS. In NDS you have to first remove attribute and then * you could add new value */ - DEBUG(10,("smbldap_make_mod: deleting attribute |%s| values |%s|\n", attribute, oldval)); - smbldap_set_mod(mods, LDAP_MOD_DELETE, attribute, oldval); + if (op & LDAP_MOD_BVALUES) { + DEBUG(10,("smbldap_make_mod: deleting attribute |%s| blob\n", attribute)); + smbldap_set_mod_blob(mods, LDAP_MOD_DELETE, attribute, &oldblob); + } else { + DEBUG(10,("smbldap_make_mod: deleting attribute |%s| values |%s|\n", attribute, oldval)); + smbldap_set_mod(mods, LDAP_MOD_DELETE, attribute, oldval); + } } /* Regardless of the real operation (add or modify) we add the new value here. We rely on deleting the old value, should it exist. */ - if ((newval != NULL) && (strlen(newval) > 0)) { - DEBUG(10,("smbldap_make_mod: adding attribute |%s| value |%s|\n", attribute, newval)); - smbldap_set_mod(mods, LDAP_MOD_ADD, attribute, newval); + if (op & LDAP_MOD_BVALUES) { + if (newblob && newblob->length) { + DEBUG(10,("smbldap_make_mod: adding attribute |%s| blob\n", attribute)); + smbldap_set_mod_blob(mods, LDAP_MOD_ADD, attribute, newblob); + } + } else { + if ((newval != NULL) && (strlen(newval) > 0)) { + DEBUG(10,("smbldap_make_mod: adding attribute |%s| value |%s|\n", attribute, newval)); + smbldap_set_mod(mods, LDAP_MOD_ADD, attribute, newval); + } } } + void smbldap_make_mod(LDAP *ldap_struct, LDAPMessage *existing, + LDAPMod ***mods, + const char *attribute, const char *newval) +{ + smbldap_make_mod_internal(ldap_struct, existing, mods, attribute, + 0, newval, NULL); +} + + void smbldap_make_mod_blob(LDAP *ldap_struct, LDAPMessage *existing, + LDAPMod ***mods, + const char *attribute, const DATA_BLOB *newblob) +{ + smbldap_make_mod_internal(ldap_struct, existing, mods, attribute, + LDAP_MOD_BVALUES, NULL, newblob); +} + /********************************************************************** Some varients of the LDAP rebind code do not pass in the third 'arg' pointer to a void*, so we try and work around it by assuming that the @@ -667,7 +592,7 @@ static void smbldap_store_state(LDAP *ld, struct smbldap_state *smbldap_state) t = SMB_XMALLOC_P(struct smbldap_state_lookup); ZERO_STRUCTP(t); - DLIST_ADD_END(smbldap_state_lookup_list, t, struct smbldap_state_lookup *); + DLIST_ADD_END(smbldap_state_lookup_list, t); t->ld = ld; t->smbldap_state = smbldap_state; } @@ -676,10 +601,10 @@ static void smbldap_store_state(LDAP *ld, struct smbldap_state *smbldap_state) start TLS on an existing LDAP connection *******************************************************************/ -int smb_ldap_start_tls(LDAP *ldap_struct, int version) +int smbldap_start_tls(LDAP *ldap_struct, int version) { #ifdef LDAP_OPT_X_TLS - int rc; + int rc,tls; #endif if (lp_ldap_ssl() != LDAP_SSL_START_TLS) { @@ -687,6 +612,12 @@ int smb_ldap_start_tls(LDAP *ldap_struct, int version) } #ifdef LDAP_OPT_X_TLS + /* check if we use ldaps already */ + ldap_get_option(ldap_struct, LDAP_OPT_X_TLS, &tls); + if (tls == LDAP_OPT_X_TLS_HARD) { + return LDAP_SUCCESS; + } + if (version != LDAP_VERSION3) { DEBUG(0, ("Need LDAPv3 for Start TLS\n")); return LDAP_OPERATIONS_ERROR; @@ -710,7 +641,7 @@ int smb_ldap_start_tls(LDAP *ldap_struct, int version) setup a connection to the LDAP server based on a uri *******************************************************************/ -int smb_ldap_setup_conn(LDAP **ldap_struct, const char *uri) +static int smb_ldap_setup_conn(LDAP **ldap_struct, const char *uri) { int rc; @@ -814,7 +745,7 @@ int smb_ldap_setup_conn(LDAP **ldap_struct, const char *uri) version *******************************************************************/ -int smb_ldap_upgrade_conn(LDAP *ldap_struct, int *new_version) +static int smb_ldap_upgrade_conn(LDAP *ldap_struct, int *new_version) { int version; int rc; @@ -847,7 +778,7 @@ int smb_ldap_upgrade_conn(LDAP *ldap_struct, int *new_version) open a connection to the ldap server (just until the bind) ******************************************************************/ -int smb_ldap_setup_full_conn(LDAP **ldap_struct, const char *uri) +int smbldap_setup_full_conn(LDAP **ldap_struct, const char *uri) { int rc, version; @@ -861,7 +792,7 @@ int smb_ldap_setup_full_conn(LDAP **ldap_struct, const char *uri) return rc; } - rc = smb_ldap_start_tls(*ldap_struct, version); + rc = smbldap_start_tls(*ldap_struct, version); if (rc) { return rc; } @@ -877,6 +808,7 @@ static int smbldap_open_connection (struct smbldap_state *ldap_state) { int rc = LDAP_SUCCESS; int version; + int deref; LDAP **ldap_struct = &ldap_state->ldap_struct; rc = smb_ldap_setup_conn(ldap_struct, ldap_state->uri); @@ -897,11 +829,21 @@ static int smbldap_open_connection (struct smbldap_state *ldap_state) /* Start TLS if required */ - rc = smb_ldap_start_tls(*ldap_struct, version); + rc = smbldap_start_tls(*ldap_struct, version); if (rc) { return rc; } + /* Set alias dereferencing method */ + deref = lp_ldap_deref(); + if (deref != -1) { + if (ldap_set_option (*ldap_struct, LDAP_OPT_DEREF, &deref) != LDAP_OPT_SUCCESS) { + DEBUG(1,("smbldap_open_connection: Failed to set dereferencing method: %d\n", deref)); + } else { + DEBUG(5,("Set dereferencing method: %d\n", deref)); + } + } + DEBUG(2, ("smbldap_open_connection: connection opened\n")); return rc; } @@ -916,6 +858,7 @@ static int rebindproc_with_state (LDAP * ld, char **whop, char **credp, int *methodp, int freeit, void *arg) { struct smbldap_state *ldap_state = arg; + struct timespec ts; /** @TODO Should we be doing something to check what servers we rebind to? Could we get a referral to a machine that we don't want to give our @@ -948,7 +891,8 @@ static int rebindproc_with_state (LDAP * ld, char **whop, char **credp, *methodp = LDAP_AUTH_SIMPLE; } - GetTimeOfDay(&ldap_state->last_rebind); + clock_gettime_mono(&ts); + ldap_state->last_rebind = convert_timespec_to_timeval(ts); return 0; } @@ -968,6 +912,7 @@ static int rebindproc_connect_with_state (LDAP *ldap_struct, struct smbldap_state *ldap_state = (struct smbldap_state *)arg; int rc; + struct timespec ts; int version; DEBUG(5,("rebindproc_connect_with_state: Rebinding to %s as \"%s\"\n", @@ -978,7 +923,7 @@ static int rebindproc_connect_with_state (LDAP *ldap_struct, * our credentials. At least *try* to secure the connection - Guenther */ smb_ldap_upgrade_conn(ldap_struct, &version); - smb_ldap_start_tls(ldap_struct, version); + smbldap_start_tls(ldap_struct, version); /** @TODO Should we be doing something to check what servers we rebind to? Could we get a referral to a machine that we don't want to give our @@ -1000,7 +945,8 @@ static int rebindproc_connect_with_state (LDAP *ldap_struct, DEBUG(10,("rebindproc_connect_with_state: " "setting last_rebind timestamp " "(req: 0x%02x)\n", (unsigned int)request)); - GetTimeOfDay(&ldap_state->last_rebind); + clock_gettime_mono(&ts); + ldap_state->last_rebind = convert_timespec_to_timeval(ts); break; default: ZERO_STRUCT(ldap_state->last_rebind); @@ -1048,28 +994,14 @@ static int rebindproc_connect (LDAP * ld, LDAP_CONST char *url, int request, /******************************************************************* connect to the ldap server under system privilege. ******************************************************************/ -static int smbldap_connect_system(struct smbldap_state *ldap_state, LDAP * ldap_struct) +static int smbldap_connect_system(struct smbldap_state *ldap_state) { + LDAP *ldap_struct = smbldap_get_ldap(ldap_state); int rc; int version; - if (!ldap_state->anonymous && !ldap_state->bind_dn) { - char *bind_dn = NULL; - char *bind_secret = NULL; - - /* get the default dn and password only if they are not set already */ - if (!fetch_ldap_pw(&bind_dn, &bind_secret)) { - DEBUG(0, ("ldap_connect_system: Failed to retrieve password from secrets.tdb\n")); - return LDAP_INVALID_CREDENTIALS; - } - smbldap_set_creds(ldap_state, false, bind_dn, bind_secret); - SAFE_FREE(bind_dn); - memset(bind_secret, '\0', strlen(bind_secret)); - SAFE_FREE(bind_secret); - } - /* removed the sasl_bind_s "EXTERNAL" stuff, as my testsuite - (OpenLDAP) doesnt' seem to support it */ + (OpenLDAP) doesn't seem to support it */ DEBUG(10,("ldap_connect_system: Binding to ldap server %s as \"%s\"\n", ldap_state->uri, ldap_state->bind_dn)); @@ -1092,11 +1024,25 @@ static int smbldap_connect_system(struct smbldap_state *ldap_state, LDAP * ldap_ #endif /*defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000)*/ #endif - rc = ldap_simple_bind_s(ldap_struct, ldap_state->bind_dn, ldap_state->bind_secret); + /* When there is an alternative bind callback is set, + attempt to use it to perform the bind */ + if (ldap_state->bind_callback != NULL) { + /* We have to allow bind callback to be run under become_root/unbecome_root + to make sure within smbd the callback has proper write access to its resources, + like credential cache. This is similar to passdb case where this callback is supposed + to be used. When used outside smbd, become_root()/unbecome_root() are no-op. + */ + become_root(); + rc = ldap_state->bind_callback(ldap_struct, ldap_state, ldap_state->bind_callback_data); + unbecome_root(); + } else { + rc = ldap_simple_bind_s(ldap_struct, ldap_state->bind_dn, ldap_state->bind_secret); + } if (rc != LDAP_SUCCESS) { char *ld_error = NULL; - ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_ERROR_STRING, + ldap_get_option(smbldap_get_ldap(ldap_state), + LDAP_OPT_ERROR_STRING, &ld_error); DEBUG(ldap_state->num_failures ? 2 : 0, ("failed to bind to server %s with dn=\"%s\" Error: %s\n\t%s\n", @@ -1106,27 +1052,34 @@ static int smbldap_connect_system(struct smbldap_state *ldap_state, LDAP * ldap_ ld_error ? ld_error : "(unknown)")); SAFE_FREE(ld_error); ldap_state->num_failures++; - return rc; + goto done; } ldap_state->num_failures = 0; ldap_state->paged_results = False; - ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_PROTOCOL_VERSION, &version); + ldap_get_option(smbldap_get_ldap(ldap_state), + LDAP_OPT_PROTOCOL_VERSION, &version); - if (smbldap_has_control(ldap_state->ldap_struct, ADS_PAGE_CTL_OID) && version == 3) { + if (smbldap_has_control(smbldap_get_ldap(ldap_state), ADS_PAGE_CTL_OID) + && version == 3) { ldap_state->paged_results = True; } DEBUG(3, ("ldap_connect_system: successful connection to the LDAP server\n")); DEBUGADD(10, ("ldap_connect_system: LDAP server %s support paged results\n", ldap_state->paged_results ? "does" : "does not")); +done: + if (rc != 0) { + ldap_unbind(ldap_struct); + ldap_state->ldap_struct = NULL; + } return rc; } -static void smbldap_idle_fn(struct event_context *event_ctx, - struct timed_event *te, - struct timeval now, +static void smbldap_idle_fn(struct tevent_context *tevent_ctx, + struct tevent_timer *te, + struct timeval now_abs, void *private_data); /********************************************************************** @@ -1138,17 +1091,20 @@ static int smbldap_open(struct smbldap_state *ldap_state) bool reopen = False; SMB_ASSERT(ldap_state); - if ((ldap_state->ldap_struct != NULL) && ((ldap_state->last_ping + SMBLDAP_DONT_PING_TIME) < time(NULL))) { + if ((smbldap_get_ldap(ldap_state) != NULL) && + ((ldap_state->last_ping + SMBLDAP_DONT_PING_TIME) < + time_mono(NULL))) { #ifdef HAVE_UNIXSOCKET struct sockaddr_un addr; #else - struct sockaddr addr; + struct sockaddr_storage addr; #endif socklen_t len = sizeof(addr); int sd; - opt_rc = ldap_get_option(ldap_state->ldap_struct, LDAP_OPT_DESC, &sd); + opt_rc = ldap_get_option(smbldap_get_ldap(ldap_state), + LDAP_OPT_DESC, &sd); if (opt_rc == 0 && (getpeername(sd, (struct sockaddr *) &addr, &len)) < 0 ) reopen = True; @@ -1158,15 +1114,15 @@ static int smbldap_open(struct smbldap_state *ldap_state) #endif if (reopen) { /* the other end has died. reopen. */ - ldap_unbind(ldap_state->ldap_struct); - ldap_state->ldap_struct = NULL; + ldap_unbind(smbldap_get_ldap(ldap_state)); + ldap_state->ldap_struct = NULL; ldap_state->last_ping = (time_t)0; } else { - ldap_state->last_ping = time(NULL); + ldap_state->last_ping = time_mono(NULL); } } - if (ldap_state->ldap_struct != NULL) { + if (smbldap_get_ldap(ldap_state) != NULL) { DEBUG(11,("smbldap_open: already connected to the LDAP server\n")); return LDAP_SUCCESS; } @@ -1175,21 +1131,19 @@ static int smbldap_open(struct smbldap_state *ldap_state) return rc; } - if ((rc = smbldap_connect_system(ldap_state, ldap_state->ldap_struct))) { - ldap_unbind(ldap_state->ldap_struct); - ldap_state->ldap_struct = NULL; + if ((rc = smbldap_connect_system(ldap_state))) { return rc; } - ldap_state->last_ping = time(NULL); - ldap_state->pid = sys_getpid(); + ldap_state->last_ping = time_mono(NULL); + ldap_state->pid = getpid(); TALLOC_FREE(ldap_state->idle_event); - if (ldap_state->event_context != NULL) { - ldap_state->idle_event = event_add_timed( - ldap_state->event_context, NULL, + if (ldap_state->tevent_context != NULL) { + ldap_state->idle_event = tevent_add_timer( + ldap_state->tevent_context, ldap_state, timeval_current_ofs(SMBLDAP_IDLE_TIME, 0), smbldap_idle_fn, ldap_state); } @@ -1207,90 +1161,128 @@ static NTSTATUS smbldap_close(struct smbldap_state *ldap_state) if (!ldap_state) return NT_STATUS_INVALID_PARAMETER; - if (ldap_state->ldap_struct != NULL) { - ldap_unbind(ldap_state->ldap_struct); + if (smbldap_get_ldap(ldap_state) != NULL) { + ldap_unbind(smbldap_get_ldap(ldap_state)); ldap_state->ldap_struct = NULL; } smbldap_delete_state(ldap_state); + TALLOC_FREE(ldap_state->idle_event); + DEBUG(5,("The connection to the LDAP server was closed\n")); /* maybe free the results here --metze */ return NT_STATUS_OK; } -static bool got_alarm; - -static void (*old_handler)(int); +static SIG_ATOMIC_T got_alarm; static void gotalarm_sig(int dummy) { - got_alarm = True; + got_alarm = 1; } -static int another_ldap_try(struct smbldap_state *ldap_state, int *rc, - int *attempts, time_t endtime) +static time_t calc_ldap_abs_endtime(int ldap_to) { - time_t now = time(NULL); - int open_rc = LDAP_SERVER_DOWN; + if (ldap_to == 0) { + /* No timeout - don't + return a value for + the alarm. */ + return (time_t)0; + } - if (*rc != LDAP_SERVER_DOWN) - goto no_next; + /* Make the alarm time one second beyond + the timout we're setting for the + remote search timeout, to allow that + to fire in preference. */ - if (now >= endtime) { - smbldap_close(ldap_state); - *rc = LDAP_TIMEOUT; - goto no_next; + return time_mono(NULL)+ldap_to+1; +} + +static int end_ldap_local_alarm(time_t absolute_endtime, int rc) +{ + if (absolute_endtime) { + alarm(0); + CatchSignal(SIGALRM, SIG_IGN); + if (got_alarm) { + /* Client timeout error code. */ + got_alarm = 0; + return LDAP_TIMEOUT; + } } + return rc; +} - if (*attempts == 0) { - got_alarm = False; - old_handler = CatchSignal(SIGALRM, gotalarm_sig); - alarm(endtime - now); +static void setup_ldap_local_alarm(struct smbldap_state *ldap_state, time_t absolute_endtime) +{ + time_t now = time_mono(NULL); - if (ldap_state->pid != sys_getpid()) - smbldap_close(ldap_state); + if (absolute_endtime) { + got_alarm = 0; + CatchSignal(SIGALRM, gotalarm_sig); + alarm(absolute_endtime - now); + } + + if (ldap_state->pid != getpid()) { + smbldap_close(ldap_state); } +} + +static void get_ldap_errs(struct smbldap_state *ldap_state, char **pp_ld_error, int *p_ld_errno) +{ + ldap_get_option(smbldap_get_ldap(ldap_state), + LDAP_OPT_ERROR_NUMBER, p_ld_errno); + + ldap_get_option(smbldap_get_ldap(ldap_state), + LDAP_OPT_ERROR_STRING, pp_ld_error); +} + +static int get_cached_ldap_connect(struct smbldap_state *ldap_state, time_t abs_endtime) +{ + int attempts = 0; while (1) { + int rc; + time_t now; - if (*attempts != 0) - smb_msleep(1000); + now = time_mono(NULL); + ldap_state->last_use = now; - *attempts += 1; + if (abs_endtime && now > abs_endtime) { + smbldap_close(ldap_state); + return LDAP_TIMEOUT; + } - open_rc = smbldap_open(ldap_state); + rc = smbldap_open(ldap_state); - if (open_rc == LDAP_SUCCESS) { - ldap_state->last_use = now; - return True; + if (rc == LDAP_SUCCESS) { + return LDAP_SUCCESS; } - if (open_rc == LDAP_INSUFFICIENT_ACCESS) { + attempts++; + DEBUG(1, ("Connection to LDAP server failed for the " + "%d try!\n", attempts)); + + if (rc == LDAP_INSUFFICIENT_ACCESS) { /* The fact that we are non-root or any other * access-denied condition will not change in the next * round of trying */ - *rc = open_rc; - break; + return rc; } if (got_alarm) { - *rc = LDAP_TIMEOUT; - break; + smbldap_close(ldap_state); + return LDAP_TIMEOUT; } - if (open_rc != LDAP_SUCCESS) { - DEBUG(1, ("Connection to LDAP server failed for the " - "%d try!\n", *attempts)); + smb_msleep(1000); + + if (got_alarm) { + smbldap_close(ldap_state); + return LDAP_TIMEOUT; } } - - no_next: - CatchSignal(SIGALRM, old_handler); - alarm(0); - ldap_state->last_use = now; - return False; } /********************************************************************* @@ -1303,10 +1295,11 @@ static int smbldap_search_ext(struct smbldap_state *ldap_state, int sizelimit, LDAPMessage **res) { int rc = LDAP_SERVER_DOWN; - int attempts = 0; char *utf8_filter; - time_t endtime = time(NULL)+lp_ldap_timeout(); + int to = lp_ldap_timeout(); + time_t abs_endtime = calc_ldap_abs_endtime(to); struct timeval timeout; + struct timeval *timeout_ptr = NULL; size_t converted_size; SMB_ASSERT(ldap_state); @@ -1316,11 +1309,12 @@ static int smbldap_search_ext(struct smbldap_state *ldap_state, if (ldap_state->last_rebind.tv_sec > 0) { struct timeval tval; + struct timespec ts; int64_t tdiff = 0; int sleep_time = 0; - ZERO_STRUCT(tval); - GetTimeOfDay(&tval); + clock_gettime_mono(&ts); + tval = convert_timespec_to_timeval(ts); tdiff = usec_time_diff(&tval, &ldap_state->last_rebind); tdiff /= 1000; /* Convert to milliseconds. */ @@ -1342,60 +1336,51 @@ static int smbldap_search_ext(struct smbldap_state *ldap_state, return LDAP_NO_MEMORY; } - /* Setup timeout for the ldap_search_ext_s call - local and remote. */ - timeout.tv_sec = lp_ldap_timeout(); - timeout.tv_usec = 0; + /* Setup remote timeout for the ldap_search_ext_s call. */ + if (to) { + timeout.tv_sec = to; + timeout.tv_usec = 0; + timeout_ptr = &timeout; + } - /* Setup alarm timeout.... Do we need both of these ? JRA. - * Yes, I think we do need both of these. The server timeout only - * covers the case where the server's operation takes too long. It - * does not cover the case where the request hangs on its way to the - * server. The server side timeout is not strictly necessary, it's - * just a bit more kind to the server. VL. */ + setup_ldap_local_alarm(ldap_state, abs_endtime); - got_alarm = 0; - CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig); - alarm(lp_ldap_timeout()); - /* End setup timeout. */ + while (1) { + char *ld_error = NULL; + int ld_errno; + + rc = get_cached_ldap_connect(ldap_state, abs_endtime); + if (rc != LDAP_SUCCESS) { + break; + } - while (another_ldap_try(ldap_state, &rc, &attempts, endtime)) { - rc = ldap_search_ext_s(ldap_state->ldap_struct, base, scope, + rc = ldap_search_ext_s(smbldap_get_ldap(ldap_state), + base, scope, utf8_filter, - CONST_DISCARD(char **, attrs), - attrsonly, sctrls, cctrls, &timeout, + discard_const_p(char *, attrs), + attrsonly, sctrls, cctrls, timeout_ptr, sizelimit, res); - if (rc != LDAP_SUCCESS) { - char *ld_error = NULL; - int ld_errno; - - ldap_get_option(ldap_state->ldap_struct, - LDAP_OPT_ERROR_NUMBER, &ld_errno); - - ldap_get_option(ldap_state->ldap_struct, - LDAP_OPT_ERROR_STRING, &ld_error); - DEBUG(10, ("Failed search for base: %s, error: %d (%s) " - "(%s)\n", base, ld_errno, - ldap_err2string(rc), - ld_error ? ld_error : "unknown")); - SAFE_FREE(ld_error); - - if (ld_errno == LDAP_SERVER_DOWN) { - ldap_unbind(ldap_state->ldap_struct); - ldap_state->ldap_struct = NULL; - } + if (rc == LDAP_SUCCESS) { + break; } - } - TALLOC_FREE(utf8_filter); + get_ldap_errs(ldap_state, &ld_error, &ld_errno); - /* Teardown timeout. */ - CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN); - alarm(0); + DEBUG(10, ("Failed search for base: %s, error: %d (%s) " + "(%s)\n", base, ld_errno, + ldap_err2string(rc), + ld_error ? ld_error : "unknown")); + SAFE_FREE(ld_error); - if (got_alarm != 0) - return LDAP_TIMELIMIT_EXCEEDED; + if (ld_errno != LDAP_SERVER_DOWN) { + break; + } + ldap_unbind(smbldap_get_ldap(ldap_state)); + ldap_state->ldap_struct = NULL; + } - return rc; + TALLOC_FREE(utf8_filter); + return end_ldap_local_alarm(abs_endtime, rc); } int smbldap_search(struct smbldap_state *ldap_state, @@ -1443,7 +1428,7 @@ int smbldap_search_paged(struct smbldap_state *ldap_state, } ber_flatten(cookie_be, &cookie_bv); - pr.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID); + pr.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID); pr.ldctl_iscritical = (char) critical; pr.ldctl_value.bv_len = cookie_bv->bv_len; pr.ldctl_value.bv_val = cookie_bv->bv_val; @@ -1465,7 +1450,7 @@ int smbldap_search_paged(struct smbldap_state *ldap_state, DEBUG(3,("smbldap_search_paged: search was successful\n")); - rc = ldap_parse_result(ldap_state->ldap_struct, *res, NULL, NULL, + rc = ldap_parse_result(smbldap_get_ldap(ldap_state), *res, NULL, NULL, NULL, NULL, &rcontrols, 0); if (rc != 0) { DEBUG(3,("smbldap_search_paged: ldap_parse_result failed " \ @@ -1501,9 +1486,8 @@ done: int smbldap_modify(struct smbldap_state *ldap_state, const char *dn, LDAPMod *attrs[]) { int rc = LDAP_SERVER_DOWN; - int attempts = 0; char *utf8_dn; - time_t endtime = time(NULL)+lp_ldap_timeout(); + time_t abs_endtime = calc_ldap_abs_endtime(lp_ldap_timeout()); size_t converted_size; SMB_ASSERT(ldap_state); @@ -1514,40 +1498,47 @@ int smbldap_modify(struct smbldap_state *ldap_state, const char *dn, LDAPMod *at return LDAP_NO_MEMORY; } - while (another_ldap_try(ldap_state, &rc, &attempts, endtime)) { - rc = ldap_modify_s(ldap_state->ldap_struct, utf8_dn, attrs); + setup_ldap_local_alarm(ldap_state, abs_endtime); + + while (1) { + char *ld_error = NULL; + int ld_errno; + + rc = get_cached_ldap_connect(ldap_state, abs_endtime); if (rc != LDAP_SUCCESS) { - char *ld_error = NULL; - int ld_errno; - - ldap_get_option(ldap_state->ldap_struct, - LDAP_OPT_ERROR_NUMBER, &ld_errno); - - ldap_get_option(ldap_state->ldap_struct, - LDAP_OPT_ERROR_STRING, &ld_error); - DEBUG(10, ("Failed to modify dn: %s, error: %d (%s) " - "(%s)\n", dn, ld_errno, - ldap_err2string(rc), - ld_error ? ld_error : "unknown")); - SAFE_FREE(ld_error); - - if (ld_errno == LDAP_SERVER_DOWN) { - ldap_unbind(ldap_state->ldap_struct); - ldap_state->ldap_struct = NULL; - } + break; + } + + rc = ldap_modify_s(smbldap_get_ldap(ldap_state), utf8_dn, + attrs); + if (rc == LDAP_SUCCESS) { + break; } + + get_ldap_errs(ldap_state, &ld_error, &ld_errno); + + DEBUG(10, ("Failed to modify dn: %s, error: %d (%s) " + "(%s)\n", dn, ld_errno, + ldap_err2string(rc), + ld_error ? ld_error : "unknown")); + SAFE_FREE(ld_error); + + if (ld_errno != LDAP_SERVER_DOWN) { + break; + } + ldap_unbind(smbldap_get_ldap(ldap_state)); + ldap_state->ldap_struct = NULL; } TALLOC_FREE(utf8_dn); - return rc; + return end_ldap_local_alarm(abs_endtime, rc); } int smbldap_add(struct smbldap_state *ldap_state, const char *dn, LDAPMod *attrs[]) { int rc = LDAP_SERVER_DOWN; - int attempts = 0; char *utf8_dn; - time_t endtime = time(NULL)+lp_ldap_timeout(); + time_t abs_endtime = calc_ldap_abs_endtime(lp_ldap_timeout()); size_t converted_size; SMB_ASSERT(ldap_state); @@ -1558,40 +1549,46 @@ int smbldap_add(struct smbldap_state *ldap_state, const char *dn, LDAPMod *attrs return LDAP_NO_MEMORY; } - while (another_ldap_try(ldap_state, &rc, &attempts, endtime)) { - rc = ldap_add_s(ldap_state->ldap_struct, utf8_dn, attrs); + setup_ldap_local_alarm(ldap_state, abs_endtime); + + while (1) { + char *ld_error = NULL; + int ld_errno; + + rc = get_cached_ldap_connect(ldap_state, abs_endtime); if (rc != LDAP_SUCCESS) { - char *ld_error = NULL; - int ld_errno; - - ldap_get_option(ldap_state->ldap_struct, - LDAP_OPT_ERROR_NUMBER, &ld_errno); - - ldap_get_option(ldap_state->ldap_struct, - LDAP_OPT_ERROR_STRING, &ld_error); - DEBUG(10, ("Failed to add dn: %s, error: %d (%s) " - "(%s)\n", dn, ld_errno, - ldap_err2string(rc), - ld_error ? ld_error : "unknown")); - SAFE_FREE(ld_error); - - if (ld_errno == LDAP_SERVER_DOWN) { - ldap_unbind(ldap_state->ldap_struct); - ldap_state->ldap_struct = NULL; - } + break; + } + + rc = ldap_add_s(smbldap_get_ldap(ldap_state), utf8_dn, attrs); + if (rc == LDAP_SUCCESS) { + break; } + + get_ldap_errs(ldap_state, &ld_error, &ld_errno); + + DEBUG(10, ("Failed to add dn: %s, error: %d (%s) " + "(%s)\n", dn, ld_errno, + ldap_err2string(rc), + ld_error ? ld_error : "unknown")); + SAFE_FREE(ld_error); + + if (ld_errno != LDAP_SERVER_DOWN) { + break; + } + ldap_unbind(smbldap_get_ldap(ldap_state)); + ldap_state->ldap_struct = NULL; } TALLOC_FREE(utf8_dn); - return rc; + return end_ldap_local_alarm(abs_endtime, rc); } int smbldap_delete(struct smbldap_state *ldap_state, const char *dn) { int rc = LDAP_SERVER_DOWN; - int attempts = 0; char *utf8_dn; - time_t endtime = time(NULL)+lp_ldap_timeout(); + time_t abs_endtime = calc_ldap_abs_endtime(lp_ldap_timeout()); size_t converted_size; SMB_ASSERT(ldap_state); @@ -1602,32 +1599,39 @@ int smbldap_delete(struct smbldap_state *ldap_state, const char *dn) return LDAP_NO_MEMORY; } - while (another_ldap_try(ldap_state, &rc, &attempts, endtime)) { - rc = ldap_delete_s(ldap_state->ldap_struct, utf8_dn); + setup_ldap_local_alarm(ldap_state, abs_endtime); + + while (1) { + char *ld_error = NULL; + int ld_errno; + + rc = get_cached_ldap_connect(ldap_state, abs_endtime); if (rc != LDAP_SUCCESS) { - char *ld_error = NULL; - int ld_errno; - - ldap_get_option(ldap_state->ldap_struct, - LDAP_OPT_ERROR_NUMBER, &ld_errno); - - ldap_get_option(ldap_state->ldap_struct, - LDAP_OPT_ERROR_STRING, &ld_error); - DEBUG(10, ("Failed to delete dn: %s, error: %d (%s) " - "(%s)\n", dn, ld_errno, - ldap_err2string(rc), - ld_error ? ld_error : "unknown")); - SAFE_FREE(ld_error); - - if (ld_errno == LDAP_SERVER_DOWN) { - ldap_unbind(ldap_state->ldap_struct); - ldap_state->ldap_struct = NULL; - } + break; + } + + rc = ldap_delete_s(smbldap_get_ldap(ldap_state), utf8_dn); + if (rc == LDAP_SUCCESS) { + break; + } + + get_ldap_errs(ldap_state, &ld_error, &ld_errno); + + DEBUG(10, ("Failed to delete dn: %s, error: %d (%s) " + "(%s)\n", dn, ld_errno, + ldap_err2string(rc), + ld_error ? ld_error : "unknown")); + SAFE_FREE(ld_error); + + if (ld_errno != LDAP_SERVER_DOWN) { + break; } + ldap_unbind(smbldap_get_ldap(ldap_state)); + ldap_state->ldap_struct = NULL; } TALLOC_FREE(utf8_dn); - return rc; + return end_ldap_local_alarm(abs_endtime, rc); } int smbldap_extended_operation(struct smbldap_state *ldap_state, @@ -1636,39 +1640,46 @@ int smbldap_extended_operation(struct smbldap_state *ldap_state, char **retoidp, struct berval **retdatap) { int rc = LDAP_SERVER_DOWN; - int attempts = 0; - time_t endtime = time(NULL)+lp_ldap_timeout(); + time_t abs_endtime = calc_ldap_abs_endtime(lp_ldap_timeout()); if (!ldap_state) return (-1); - while (another_ldap_try(ldap_state, &rc, &attempts, endtime)) { - rc = ldap_extended_operation_s(ldap_state->ldap_struct, reqoid, + setup_ldap_local_alarm(ldap_state, abs_endtime); + + while (1) { + char *ld_error = NULL; + int ld_errno; + + rc = get_cached_ldap_connect(ldap_state, abs_endtime); + if (rc != LDAP_SUCCESS) { + break; + } + + rc = ldap_extended_operation_s(smbldap_get_ldap(ldap_state), + reqoid, reqdata, serverctrls, clientctrls, retoidp, retdatap); - if (rc != LDAP_SUCCESS) { - char *ld_error = NULL; - int ld_errno; - - ldap_get_option(ldap_state->ldap_struct, - LDAP_OPT_ERROR_NUMBER, &ld_errno); - - ldap_get_option(ldap_state->ldap_struct, - LDAP_OPT_ERROR_STRING, &ld_error); - DEBUG(10, ("Extended operation failed with error: " - "%d (%s) (%s)\n", ld_errno, - ldap_err2string(rc), - ld_error ? ld_error : "unknown")); - SAFE_FREE(ld_error); - - if (ld_errno == LDAP_SERVER_DOWN) { - ldap_unbind(ldap_state->ldap_struct); - ldap_state->ldap_struct = NULL; - } + if (rc == LDAP_SUCCESS) { + break; } + + get_ldap_errs(ldap_state, &ld_error, &ld_errno); + + DEBUG(10, ("Extended operation failed with error: " + "%d (%s) (%s)\n", ld_errno, + ldap_err2string(rc), + ld_error ? ld_error : "unknown")); + SAFE_FREE(ld_error); + + if (ld_errno != LDAP_SERVER_DOWN) { + break; + } + ldap_unbind(smbldap_get_ldap(ldap_state)); + ldap_state->ldap_struct = NULL; } - return rc; + return end_ldap_local_alarm(abs_endtime, rc); } /******************************************************************* @@ -1678,30 +1689,32 @@ int smbldap_search_suffix (struct smbldap_state *ldap_state, const char *filter, const char **search_attr, LDAPMessage ** result) { - return smbldap_search(ldap_state, lp_ldap_suffix(), LDAP_SCOPE_SUBTREE, + return smbldap_search(ldap_state, lp_ldap_suffix(talloc_tos()), + LDAP_SCOPE_SUBTREE, filter, search_attr, 0, result); } -static void smbldap_idle_fn(struct event_context *event_ctx, - struct timed_event *te, - struct timeval now, +static void smbldap_idle_fn(struct tevent_context *tevent_ctx, + struct tevent_timer *te, + struct timeval now_abs, void *private_data) { struct smbldap_state *state = (struct smbldap_state *)private_data; TALLOC_FREE(state->idle_event); - if (state->ldap_struct == NULL) { + if (smbldap_get_ldap(state) == NULL) { DEBUG(10,("ldap connection not connected...\n")); return; } - if ((state->last_use+SMBLDAP_IDLE_TIME) > now.tv_sec) { + if ((state->last_use+SMBLDAP_IDLE_TIME) > time_mono(NULL)) { DEBUG(10,("ldap connection not idle...\n")); - state->idle_event = event_add_timed( - event_ctx, NULL, - timeval_add(&now, SMBLDAP_IDLE_TIME, 0), + /* this needs to be made monotonic clock aware inside tevent: */ + state->idle_event = tevent_add_timer( + tevent_ctx, state, + timeval_add(&now_abs, SMBLDAP_IDLE_TIME, 0), smbldap_idle_fn, private_data); return; @@ -1725,24 +1738,32 @@ void smbldap_free_struct(struct smbldap_state **ldap_state) SAFE_FREE((*ldap_state)->bind_dn); SAFE_FREE((*ldap_state)->bind_secret); + smbldap_set_bind_callback(*ldap_state, NULL, NULL); - TALLOC_FREE((*ldap_state)->idle_event); - - *ldap_state = NULL; + TALLOC_FREE(*ldap_state); /* No need to free any further, as it is talloc()ed */ } +static int smbldap_state_destructor(struct smbldap_state *state) +{ + smbldap_free_struct(&state); + return 0; +} + /********************************************************************** Intitalise the 'general' ldap structures, on which ldap operations may be conducted *********************************************************************/ -NTSTATUS smbldap_init(TALLOC_CTX *mem_ctx, struct event_context *event_ctx, +NTSTATUS smbldap_init(TALLOC_CTX *mem_ctx, struct tevent_context *tevent_ctx, const char *location, + bool anon, + const char *bind_dn, + const char *bind_secret, struct smbldap_state **smbldap_state) { - *smbldap_state = TALLOC_ZERO_P(mem_ctx, struct smbldap_state); + *smbldap_state = talloc_zero(mem_ctx, struct smbldap_state); if (!*smbldap_state) { DEBUG(0, ("talloc() failed for ldapsam private_data!\n")); return NT_STATUS_NO_MEMORY; @@ -1754,8 +1775,13 @@ NTSTATUS smbldap_init(TALLOC_CTX *mem_ctx, struct event_context *event_ctx, (*smbldap_state)->uri = "ldap://localhost"; } - (*smbldap_state)->event_context = event_ctx; + (*smbldap_state)->tevent_context = tevent_ctx; + + if (bind_dn && bind_secret) { + smbldap_set_creds(*smbldap_state, anon, bind_dn, bind_secret); + } + talloc_set_destructor(*smbldap_state, smbldap_state_destructor); return NT_STATUS_OK; } @@ -1804,7 +1830,7 @@ static bool smbldap_check_root_dse(LDAP *ld, const char **attrs, const char *val } rc = ldap_search_s(ld, "", LDAP_SCOPE_BASE, - "(objectclass=*)", CONST_DISCARD(char **, attrs), 0 , &msg); + "(objectclass=*)", discard_const_p(char *, attrs), 0 , &msg); if (rc != LDAP_SUCCESS) { DEBUG(3,("smbldap_check_root_dse: Could not search rootDSE\n")); @@ -1892,6 +1918,8 @@ bool smbldap_set_creds(struct smbldap_state *ldap_state, bool anon, const char * /* free any previously set credential */ SAFE_FREE(ldap_state->bind_dn); + smbldap_set_bind_callback(ldap_state, NULL, NULL); + if (ldap_state->bind_secret) { /* make sure secrets are zeroed out of memory */ memset(ldap_state->bind_secret, '\0', strlen(ldap_state->bind_secret));