lib: Make sid_parse return the parsed length
[samba.git] / source3 / lib / smbldap.c
index 5a1ba09ff13afd57664eae6d420033910dfb5646..cdd350fffa1e23051594b7efbedc197456472085 100644 (file)
@@ -24,9 +24,9 @@
 
 #include "includes.h"
 #include "smbldap.h"
-#include "secrets.h"
 #include "../libcli/security/security.h"
 #include <tevent.h>
+#include "lib/param/loadparm.h"
 
 /* Try not to hit the up or down server forever */
 
 
 #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;
 
+       /* credentials */
+       bool anonymous;
+       char *bind_dn;
+       char *bind_secret;
+       smbldap_bind_callback_fn bind_callback;
+       void *bind_callback_data;
+
+       bool paged_results;
+
+       unsigned int num_failures;
+
+       time_t last_use; /* monotonic */
+       struct tevent_context *tevent_context;
+       struct tevent_timer *idle_event;
+
+       struct timeval last_rebind; /* monotonic */
+};
+
+LDAP *smbldap_get_ldap(struct smbldap_state *state)
+{
+       return state->ldap_struct;
+}
+
+bool smbldap_get_paged_results(struct smbldap_state *state)
+{
+       return state->paged_results;
+}
+
+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.
 ******************************************************************/
                       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) {
        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;
 
        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;
 
@@ -301,7 +348,7 @@ static void smbldap_set_mod_internal(LDAPMod *** modlist, int modop, const char
                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;
@@ -353,7 +400,7 @@ static void smbldap_set_mod_internal(LDAPMod *** modlist, int modop, const char
                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);
+               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;
 
@@ -412,12 +459,6 @@ static void smbldap_make_mod_internal(LDAP *ldap_struct, LDAPMessage *existing,
        bool existed;
        DATA_BLOB oldblob = data_blob_null;
 
-       if (attribute == NULL) {
-               /* This can actually happen for ldapsam_compat where we for
-                * example don't have a password history */
-               return;
-       }
-
        if (existing != NULL) {
                if (op & LDAP_MOD_BVALUES) {
                        existed = smbldap_talloc_single_blob(talloc_tos(), ldap_struct, existing, attribute, &oldblob);
@@ -551,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;
 }
@@ -560,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) {
@@ -571,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;
@@ -731,7 +778,7 @@ static 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;
 
@@ -745,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;
        }
@@ -782,7 +829,7 @@ 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;
        }
@@ -876,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
@@ -949,28 +996,12 @@ static int rebindproc_connect (LDAP * ld, LDAP_CONST char *url, int request,
 ******************************************************************/
 static int smbldap_connect_system(struct smbldap_state *ldap_state)
 {
-       LDAP *ldap_struct = ldap_state->ldap_struct;
+       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"));
-                       rc = LDAP_INVALID_CREDENTIALS;
-                       goto done;
-               }
-               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));
@@ -993,11 +1024,25 @@ static int smbldap_connect_system(struct smbldap_state *ldap_state)
 #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",
@@ -1013,9 +1058,11 @@ static int smbldap_connect_system(struct smbldap_state *ldap_state)
        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;
        }
 
@@ -1031,7 +1078,7 @@ done:
 }
 
 static void smbldap_idle_fn(struct tevent_context *tevent_ctx,
-                           struct timed_event *te,
+                           struct tevent_timer *te,
                            struct timeval now_abs,
                            void *private_data);
 
@@ -1044,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_mono(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;
 
@@ -1064,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_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;
        }
@@ -1087,7 +1137,7 @@ static int smbldap_open(struct smbldap_state *ldap_state)
 
 
        ldap_state->last_ping = time_mono(NULL);
-       ldap_state->pid = sys_getpid();
+       ldap_state->pid = getpid();
 
        TALLOC_FREE(ldap_state->idle_event);
 
@@ -1111,8 +1161,8 @@ 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;
        }
 
@@ -1174,17 +1224,17 @@ static void setup_ldap_local_alarm(struct smbldap_state *ldap_state, time_t abso
                alarm(absolute_endtime - now);
        }
 
-       if (ldap_state->pid != sys_getpid()) {
+       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(ldap_state->ldap_struct,
+       ldap_get_option(smbldap_get_ldap(ldap_state),
                        LDAP_OPT_ERROR_NUMBER, p_ld_errno);
 
-       ldap_get_option(ldap_state->ldap_struct,
+       ldap_get_option(smbldap_get_ldap(ldap_state),
                        LDAP_OPT_ERROR_STRING, pp_ld_error);
 }
 
@@ -1304,7 +1354,8 @@ static int smbldap_search_ext(struct smbldap_state *ldap_state,
                        break;
                }
 
-               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,
                                       discard_const_p(char *, attrs),
                                       attrsonly, sctrls, cctrls, timeout_ptr,
@@ -1324,7 +1375,7 @@ static int smbldap_search_ext(struct smbldap_state *ldap_state,
                if (ld_errno != LDAP_SERVER_DOWN) {
                        break;
                }
-               ldap_unbind(ldap_state->ldap_struct);
+               ldap_unbind(smbldap_get_ldap(ldap_state));
                ldap_state->ldap_struct = NULL;
        }
 
@@ -1399,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 " \
@@ -1458,7 +1509,8 @@ int smbldap_modify(struct smbldap_state *ldap_state, const char *dn, LDAPMod *at
                        break;
                }
 
-               rc = ldap_modify_s(ldap_state->ldap_struct, utf8_dn, attrs);
+               rc = ldap_modify_s(smbldap_get_ldap(ldap_state), utf8_dn,
+                                  attrs);
                if (rc == LDAP_SUCCESS) {
                        break;
                }
@@ -1474,7 +1526,7 @@ int smbldap_modify(struct smbldap_state *ldap_state, const char *dn, LDAPMod *at
                if (ld_errno != LDAP_SERVER_DOWN) {
                        break;
                }
-               ldap_unbind(ldap_state->ldap_struct);
+               ldap_unbind(smbldap_get_ldap(ldap_state));
                ldap_state->ldap_struct = NULL;
        }
 
@@ -1508,7 +1560,7 @@ int smbldap_add(struct smbldap_state *ldap_state, const char *dn, LDAPMod *attrs
                        break;
                }
 
-               rc = ldap_add_s(ldap_state->ldap_struct, utf8_dn, attrs);
+               rc = ldap_add_s(smbldap_get_ldap(ldap_state), utf8_dn, attrs);
                if (rc == LDAP_SUCCESS) {
                        break;
                }
@@ -1524,7 +1576,7 @@ int smbldap_add(struct smbldap_state *ldap_state, const char *dn, LDAPMod *attrs
                if (ld_errno != LDAP_SERVER_DOWN) {
                        break;
                }
-               ldap_unbind(ldap_state->ldap_struct);
+               ldap_unbind(smbldap_get_ldap(ldap_state));
                ldap_state->ldap_struct = NULL;
        }
 
@@ -1558,7 +1610,7 @@ int smbldap_delete(struct smbldap_state *ldap_state, const char *dn)
                        break;
                }
 
-               rc = ldap_delete_s(ldap_state->ldap_struct, utf8_dn);
+               rc = ldap_delete_s(smbldap_get_ldap(ldap_state), utf8_dn);
                if (rc == LDAP_SUCCESS) {
                        break;
                }
@@ -1574,7 +1626,7 @@ int smbldap_delete(struct smbldap_state *ldap_state, const char *dn)
                if (ld_errno != LDAP_SERVER_DOWN) {
                        break;
                }
-               ldap_unbind(ldap_state->ldap_struct);
+               ldap_unbind(smbldap_get_ldap(ldap_state));
                ldap_state->ldap_struct = NULL;
        }
 
@@ -1604,7 +1656,8 @@ int smbldap_extended_operation(struct smbldap_state *ldap_state,
                        break;
                }
 
-               rc = ldap_extended_operation_s(ldap_state->ldap_struct, reqoid,
+               rc = ldap_extended_operation_s(smbldap_get_ldap(ldap_state),
+                                              reqoid,
                                               reqdata, serverctrls,
                                               clientctrls, retoidp, retdatap);
                if (rc == LDAP_SUCCESS) {
@@ -1622,7 +1675,7 @@ int smbldap_extended_operation(struct smbldap_state *ldap_state,
                if (ld_errno != LDAP_SERVER_DOWN) {
                        break;
                }
-               ldap_unbind(ldap_state->ldap_struct);
+               ldap_unbind(smbldap_get_ldap(ldap_state));
                ldap_state->ldap_struct = NULL;
        }
 
@@ -1636,12 +1689,13 @@ 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 tevent_context *tevent_ctx,
-                           struct timed_event *te,
+                           struct tevent_timer *te,
                            struct timeval now_abs,
                            void *private_data)
 {
@@ -1649,7 +1703,7 @@ static void smbldap_idle_fn(struct tevent_context *tevent_ctx,
 
        TALLOC_FREE(state->idle_event);
 
-       if (state->ldap_struct == NULL) {
+       if (smbldap_get_ldap(state) == NULL) {
                DEBUG(10,("ldap connection not connected...\n"));
                return;
        }
@@ -1684,6 +1738,7 @@ 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);
 
@@ -1722,6 +1777,10 @@ NTSTATUS smbldap_init(TALLOC_CTX *mem_ctx, struct tevent_context *tevent_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;
 }
@@ -1859,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));