s3-smbldap: make octet_strings/DATA_BLOBs const.
[nivanova/samba-autobuild/.git] / source3 / lib / smbldap.c
index 7287e3d9986a7698a0d40350a1eac71788edac70..fe43237464161095b86e327429047a54a8c63160 100644 (file)
 
 #include "includes.h"
 #include "smbldap.h"
-
-#ifndef LDAP_OPT_SUCCESS
-#define LDAP_OPT_SUCCESS 0
-#endif
+#include "secrets.h"
+#include "../libcli/security/security.h"
 
 /* Try not to hit the up or down server forever */
 
@@ -266,6 +264,7 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = {
                                    int max_len)
 {
        char **values;
+       size_t size = 0;
 
        if ( !attribute )
                return False;
@@ -278,7 +277,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 +332,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)
@@ -473,7 +506,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;
@@ -524,7 +557,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 *)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,17 +606,30 @@ 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;
+       DATA_BLOB oldblob = data_blob_null;
 
        if (attribute == NULL) {
                /* This can actually happen for ldapsam_compat where we for
@@ -572,24 +638,33 @@ ATTRIB_MAP_ENTRY sidmap_attr_list[] = {
        }
 
        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(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 +676,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 
@@ -710,7 +813,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;
 
@@ -724,9 +827,9 @@ int smb_ldap_setup_conn(LDAP **ldap_struct, const char *uri)
                return rc;
        }
 
-       if (lp_ldap_ref_follow() != Auto) {
+       if (lp_ldap_follow_referral() != Auto) {
                rc = ldap_set_option(*ldap_struct, LDAP_OPT_REFERRALS,
-                    lp_ldap_ref_follow() ? LDAP_OPT_ON : LDAP_OPT_OFF);
+                    lp_ldap_follow_referral() ? LDAP_OPT_ON : LDAP_OPT_OFF);
                if (rc != LDAP_SUCCESS)
                        DEBUG(0, ("Failed to set LDAP_OPT_REFERRALS: %s\n",
                                ldap_err2string(rc)));
@@ -814,7 +917,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;
@@ -877,6 +980,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);
@@ -902,6 +1006,16 @@ static int smbldap_open_connection (struct smbldap_state *ldap_state)
                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 +1030,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 +1063,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 +1084,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", 
@@ -1000,7 +1117,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);
@@ -1126,7 +1244,7 @@ static int smbldap_connect_system(struct smbldap_state *ldap_state, LDAP * ldap_
 
 static void smbldap_idle_fn(struct event_context *event_ctx,
                            struct timed_event *te,
-                           struct timeval now,
+                           struct timeval now_abs,
                            void *private_data);
 
 /**********************************************************************
@@ -1138,7 +1256,7 @@ 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 ((ldap_state->ldap_struct != NULL) && ((ldap_state->last_ping + SMBLDAP_DONT_PING_TIME) < time_mono(NULL))) {
 
 #ifdef HAVE_UNIXSOCKET
                struct sockaddr_un addr;
@@ -1162,7 +1280,7 @@ static int smbldap_open(struct smbldap_state *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);
                } 
        }
 
@@ -1182,7 +1300,7 @@ static int smbldap_open(struct smbldap_state *ldap_state)
        }
 
 
-       ldap_state->last_ping = time(NULL);
+       ldap_state->last_ping = time_mono(NULL);
        ldap_state->pid = sys_getpid();
 
        TALLOC_FREE(ldap_state->idle_event);
@@ -1232,7 +1350,7 @@ static void gotalarm_sig(int dummy)
 static int another_ldap_try(struct smbldap_state *ldap_state, int *rc,
                            int *attempts, time_t endtime)
 {
-       time_t now = time(NULL);
+       time_t now = time_mono(NULL);
        int open_rc = LDAP_SERVER_DOWN;
 
        if (*rc != LDAP_SERVER_DOWN)
@@ -1305,7 +1423,7 @@ static int smbldap_search_ext(struct smbldap_state *ldap_state,
        int             rc = LDAP_SERVER_DOWN;
        int             attempts = 0;
        char           *utf8_filter;
-       time_t          endtime = time(NULL)+lp_ldap_timeout();
+       time_t          endtime = time_mono(NULL)+lp_ldap_timeout();
        struct          timeval timeout;
        size_t          converted_size;
 
@@ -1316,11 +1434,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. */
@@ -1354,7 +1473,7 @@ static int smbldap_search_ext(struct smbldap_state *ldap_state,
         * just a bit more kind to the server. VL. */
 
        got_alarm = 0;
-       CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
+       CatchSignal(SIGALRM, gotalarm_sig);
        alarm(lp_ldap_timeout());
        /* End setup timeout. */
 
@@ -1389,7 +1508,7 @@ static int smbldap_search_ext(struct smbldap_state *ldap_state,
        TALLOC_FREE(utf8_filter);
 
        /* Teardown timeout. */
-       CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
+       CatchSignal(SIGALRM, SIG_IGN);
        alarm(0);
 
        if (got_alarm != 0)
@@ -1503,7 +1622,7 @@ int smbldap_modify(struct smbldap_state *ldap_state, const char *dn, LDAPMod *at
        int             rc = LDAP_SERVER_DOWN;
        int             attempts = 0;
        char           *utf8_dn;
-       time_t          endtime = time(NULL)+lp_ldap_timeout();
+       time_t          endtime = time_mono(NULL)+lp_ldap_timeout();
        size_t          converted_size;
 
        SMB_ASSERT(ldap_state);
@@ -1547,7 +1666,7 @@ 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          endtime = time_mono(NULL)+lp_ldap_timeout();
        size_t          converted_size;
 
        SMB_ASSERT(ldap_state);
@@ -1591,7 +1710,7 @@ 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          endtime = time_mono(NULL)+lp_ldap_timeout();
        size_t          converted_size;
 
        SMB_ASSERT(ldap_state);
@@ -1637,7 +1756,7 @@ int smbldap_extended_operation(struct smbldap_state *ldap_state,
 {
        int             rc = LDAP_SERVER_DOWN;
        int             attempts = 0;
-       time_t          endtime = time(NULL)+lp_ldap_timeout();
+       time_t          endtime = time_mono(NULL)+lp_ldap_timeout();
 
        if (!ldap_state)
                return (-1);
@@ -1684,7 +1803,7 @@ int smbldap_search_suffix (struct smbldap_state *ldap_state,
 
 static void smbldap_idle_fn(struct event_context *event_ctx,
                            struct timed_event *te,
-                           struct timeval now,
+                           struct timeval now_abs,
                            void *private_data)
 {
        struct smbldap_state *state = (struct smbldap_state *)private_data;
@@ -1696,12 +1815,13 @@ static void smbldap_idle_fn(struct event_context *event_ctx,
                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"));
 
+               /* this needs to be made monotonic clock aware inside tevent: */
                state->idle_event = event_add_timed(
                        event_ctx, NULL,
-                       timeval_add(&now, SMBLDAP_IDLE_TIME, 0),
+                       timeval_add(&now_abs, SMBLDAP_IDLE_TIME, 0),
                        smbldap_idle_fn,
                        private_data);
                return;