* amd/info_ldap.c (and others): backport of LDAP support from 6.1
authorErez Zadok <ezk@cs.sunysb.edu>
Tue, 15 Mar 2005 04:14:57 +0000 (04:14 +0000)
committerErez Zadok <ezk@cs.sunysb.edu>
Tue, 15 Mar 2005 04:14:57 +0000 (04:14 +0000)
to 6.0, suggestion and patch from Jim Zajkowski
<jim.zajkowski@gmail.com>.

AUTHORS
ChangeLog
amd/amd.c
amd/amd.h
amd/conf.c
amd/info_ldap.c

diff --git a/AUTHORS b/AUTHORS
index 708cc23a6ddabcaa07fafb8a0839ed01d5c7b162..715b619ec2c0f903947ab8083eccd5baced53e02 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -352,4 +352,5 @@ to "/tmp/mtab" to avoid security problem.  Bug fixed to ensure that Amd
 terminates properly even mtab file doesn't exist.
 
 * Jim Zajkowski <jim.zajkowski@gmail.com>
-March 14, 2005: small patch to amd2ldif.
+March 14, 2005: small patch to amd2ldif, plus backport of LDAP support from
+6.1 branch.
index 0742a6551d1c395ba457f77b1fd5efc5a7f40d1d..587ba60a5654ddde1f139b6ef5e74fe36f0de98e 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2005-03-14  Erez Zadok  <ezk@cs.sunysb.edu>
 
+       * amd/info_ldap.c (and others): backport of LDAP support from 6.1
+       to 6.0, suggestion and patch from Jim Zajkowski
+       <jim.zajkowski@gmail.com>.
+
        * scripts/amd2ldif.in: patch to add the amdMapName attribute to
        the amdMapTimestamp object when amd2ldif converts it.  This is
        necessary if you are using type:=auto mounts and want those
index 81a0528b84d3bf94c80da83b10ecb29f1b722c56..6b41eb8fac32813c9c9e2cc8d48189a7c08824b8 100644 (file)
--- a/amd/amd.c
+++ b/amd/amd.c
@@ -38,7 +38,7 @@
  *
  *      %W% (Berkeley) %G%
  *
- * $Id: amd.c,v 1.8.2.7 2005/01/03 20:56:11 ezk Exp $
+ * $Id: amd.c,v 1.8.2.8 2005/03/15 04:14:57 ezk Exp $
  *
  */
 
@@ -293,6 +293,8 @@ init_global_options(void)
   /* LDAP cache */
   gopt.ldap_cache_seconds = 0;
   gopt.ldap_cache_maxmem = 131072;
+
+  gopt.ldap_proto_version = 2;
 #endif /* HAVE_MAP_LDAP */
 
 #ifdef HAVE_MAP_NIS
index 7e7e5ff0929d247861835a6aaadd62fa1af827d8..1a39c760be5fa306e45ea8f15da32b4aa01d3768 100644 (file)
--- a/amd/amd.h
+++ b/amd/amd.h
@@ -38,7 +38,7 @@
  *
  *      %W% (Berkeley) %G%
  *
- * $Id: amd.h,v 1.8.2.11 2005/03/04 18:42:04 ezk Exp $
+ * $Id: amd.h,v 1.8.2.12 2005/03/15 04:14:57 ezk Exp $
  *
  */
 
@@ -140,6 +140,7 @@ struct amu_global_options {
   char *ldap_hostports;                /* LDAP host ports */
   long ldap_cache_seconds;     /* LDAP internal cache - keep seconds */
   long ldap_cache_maxmem;      /* LDAP internal cache - max memory (bytes) */
+  long ldap_proto_version;
 #endif /* HAVE_MAP_LDAP */
 #ifdef HAVE_MAP_NIS
   char *nis_domain;            /* YP domain name */
index 72b3b19d666143112e3f4a6fef5c0ff800525652..f0f1b948049409fe7205a99cc6c14565c3780375 100644 (file)
@@ -38,7 +38,7 @@
  *
  *      %W% (Berkeley) %G%
  *
- * $Id: conf.c,v 1.7.2.10 2005/03/04 18:42:04 ezk Exp $
+ * $Id: conf.c,v 1.7.2.11 2005/03/15 04:14:57 ezk Exp $
  *
  */
 
@@ -91,6 +91,7 @@ static int gopt_ldap_base(const char *val);
 static int gopt_ldap_cache_maxmem(const char *val);
 static int gopt_ldap_cache_seconds(const char *val);
 static int gopt_ldap_hostports(const char *val);
+static int gopt_ldap_proto_version(const char *val);
 static int gopt_local_domain(const char *val);
 static int gopt_log_file(const char *val);
 static int gopt_log_options(const char *val);
@@ -151,6 +152,7 @@ static struct _func_map glob_functable[] = {
   {"ldap_cache_maxmem",                gopt_ldap_cache_maxmem},
   {"ldap_cache_seconds",       gopt_ldap_cache_seconds},
   {"ldap_hostports",           gopt_ldap_hostports},
+  {"ldap_proto_version",        gopt_ldap_proto_version},
   {"local_domain",             gopt_local_domain},
   {"log_file",                 gopt_log_file},
   {"log_options",              gopt_log_options},
@@ -535,6 +537,44 @@ gopt_ldap_hostports(const char *val)
 
 }
 
+static int
+gopt_ldap_proto_version(const char *val)
+{
+#ifdef HAVE_MAP_LDAP
+  char *end;
+
+  gopt.ldap_proto_version = strtol((char *)val, &end, 10);
+  if (end == val) {
+    fprintf(stderr, "conf: bad ldap_proto_version option: %s\n",val);
+    return 1;
+  }
+
+  if (gopt.ldap_proto_version < 0 || gopt.ldap_proto_version > LDAP_VERSION_MAX) {
+    fprintf(stderr, "conf: bad ldap_proto_version option value: %s\n",val);
+    return 1;
+  }
+  switch (gopt.ldap_proto_version) {
+    /* XXX: what about LDAP_VERSION1? */
+  case LDAP_VERSION2:
+#ifdef LDAP_VERSION3
+  case LDAP_VERSION3:
+#endif /* LDAP_VERSION3 */
+#ifdef LDAP_VERSION4
+  case LDAP_VERSION4:
+#endif /* LDAP_VERSION4 */
+    break;
+  default:
+    fprintf(stderr, "conf: unsupported ldap_proto_version option value: %s\n",val);
+    return 1;
+  }
+  return 0;
+#else /* not HAVE_MAP_LDAP */
+  fprintf(stderr, "conf: ldap_proto_version option ignored.  No LDAP support available.\n");
+  return 1;
+#endif /* not HAVE_MAP_LDAP */
+}
+
+
 
 static int
 gopt_log_file(const char *val)
index 7cd800ab8af9bc5191b695450172ce45dafceb2c..23095eabae857f6c9246bcfe8846ce836fdd5502 100644 (file)
@@ -36,9 +36,8 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *      %W% (Berkeley) %G%
  *
- * $Id: info_ldap.c,v 1.9.2.11 2005/01/16 23:56:32 ezk Exp $
+ * $Id: info_ldap.c,v 1.9.2.12 2005/03/15 04:14:57 ezk Exp $
  *
  */
 
@@ -109,7 +108,7 @@ struct he_ent {
  * FORWARD DECLARATIONS:
  */
 static int amu_ldap_rebind(ALD *a);
-static int get_ldap_timestamp(LDAP *ld, char *map, time_t *ts);
+static int get_ldap_timestamp(ALD *a, char *map, time_t *ts);
 
 
 /*
@@ -167,13 +166,54 @@ cr_free(CR *c)
 }
 
 
+/*
+ * Special ldap_unbind function to handle SIGPIPE.
+ * We first ignore SIGPIPE, in case a remote LDAP server was
+ * restarted, then we reinstall the handler.
+ */
+static int
+amu_ldap_unbind(LDAP *ld)
+{
+  int e;
+#ifdef HAVE_SIGACTION
+  struct sigaction sa;
+#else /* not HAVE_SIGACTION */
+  void (*handler)(int);
+#endif /* not HAVE_SIGACTION */
+
+  dlog("amu_ldap_unbind()\n");
+
+#ifdef HAVE_SIGACTION
+  sa.sa_handler = SIG_IGN;
+  sa.sa_flags = 0;
+  sigemptyset(&(sa.sa_mask));
+  sigaddset(&(sa.sa_mask), SIGPIPE);
+  sigaction(SIGPIPE, &sa, &sa);        /* set IGNORE, and get old action */
+#else /* not HAVE_SIGACTION */
+  handler = signal(SIGPIPE, SIG_IGN);
+#endif /* not HAVE_SIGACTION */
+
+  e = ldap_unbind(ld);
+
+#ifdef HAVE_SIGACTION
+  sigemptyset(&(sa.sa_mask));
+  sigaddset(&(sa.sa_mask), SIGPIPE);
+  sigaction(SIGPIPE, &sa, NULL);
+#else /* not HAVE_SIGACTION */
+  (void) signal(SIGPIPE, handler);
+#endif /* not HAVE_SIGACTION */
+
+  return e;
+}
+
+
 static void
 ald_free(ALD *a)
 {
   he_free(a->hostent);
   cr_free(a->credentials);
   if (a->ldap != NULL)
-    ldap_unbind(a->ldap);
+    amu_ldap_unbind(a->ldap);
   XFREE(a);
 }
 
@@ -184,23 +224,23 @@ amu_ldap_init(mnt_map *m, char *map, time_t *ts)
   ALD *aldh;
   CR *creds;
 
+  dlog("-> amu_ldap_init: map <%s>\n", map);
+
   /*
    * XXX: by checking that map_type must be defined, aren't we
    * excluding the possibility of automatic searches through all
    * map types?
    */
   if (!gopt.map_type || !STREQ(gopt.map_type, AMD_LDAP_TYPE)) {
-    return (ENOENT);
-  }
-#ifdef DEBUG
-  else {
+    plog(XLOG_WARNING, "amu_ldap_init called with map_type <%s>\n",
+        (gopt.map_type ? gopt.map_type : "null"));
+  } else {
     dlog("Map %s is ldap\n", map);
   }
-#endif /* DEBUG */
 
   aldh = ALLOC(ALD);
   creds = ALLOC(CR);
-  aldh->ldap = NULL ;
+  aldh->ldap = NULL;
   aldh->hostent = string2he(gopt.ldap_hostports);
   if (aldh->hostent == NULL) {
     plog(XLOG_USER, "Unable to parse hostport %s for ldap map %s",
@@ -212,22 +252,17 @@ amu_ldap_init(mnt_map *m, char *map, time_t *ts)
   creds->method = LDAP_AUTH_SIMPLE;
   aldh->credentials = creds;
   aldh->timestamp = 0;
-#ifdef DEBUG
+  aldh->ldap = NULL;
   dlog("Trying for %s:%d\n", aldh->hostent->host, aldh->hostent->port);
-#endif /* DEBUG */
   if (amu_ldap_rebind(aldh)) {
     ald_free(aldh);
     return (ENOENT);
   }
   m->map_data = (void *) aldh;
-#ifdef DEBUG
   dlog("Bound to %s:%d\n", aldh->hostent->host, aldh->hostent->port);
-#endif /* DEBUG */
-  if (get_ldap_timestamp(aldh->ldap, map, ts))
+  if (get_ldap_timestamp(aldh, map, ts))
     return (ENOENT);
-#ifdef DEBUG
   dlog("Got timestamp for map %s: %ld\n", map, *ts);
-#endif /* DEBUG */
 
   return (0);
 }
@@ -242,13 +277,18 @@ amu_ldap_rebind(ALD *a)
   time_t now = clocktime();
   int try;
 
+  dlog("-> amu_ldap_rebind\n");
+
   if (a->ldap != NULL) {
     if ((a->timestamp - now) > AMD_LDAP_TTL) {
-#ifdef DEBUG
-      dlog("Reestablishing ldap connection\n");
-#endif /* DEBUG */
-      ldap_unbind(a->ldap);
+      dlog("Re-establishing ldap connection\n");
+      amu_ldap_unbind(a->ldap);
       a->timestamp = now;
+      a->ldap = NULL;
+    } else {
+      /* Assume all is OK.  If it wasn't we'll be back! */
+      dlog("amu_ldap_rebind: timestamp OK\n");
+      return (0);
     }
   }
 
@@ -258,6 +298,19 @@ amu_ldap_rebind(ALD *a)
        plog(XLOG_WARNING, "Unable to ldap_open to %s:%d\n", h->host, h->port);
        break;
       }
+#if LDAP_VERSION_MAX > LDAP_VERSION2
+      /* handle LDAPv3 and heigher, if available and amd.conf-igured */
+      if (gopt.ldap_proto_version > LDAP_VERSION2) {
+        if (!ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &gopt.ldap_proto_version)) {
+          dlog("amu_ldap_rebind: LDAP protocol version set to %ld\n",
+              gopt.ldap_proto_version);
+        } else {
+          plog(XLOG_WARNING, "Unable to set ldap protocol version to %ld\n",
+              gopt.ldap_proto_version);
+         break;
+        }
+      }
+#endif /* LDAP_VERSION_MAX > LDAP_VERSION2 */
       if (ldap_bind_s(ld, c->who, c->pw, c->method) != LDAP_SUCCESS) {
        plog(XLOG_WARNING, "Unable to ldap_bind to %s:%d as %s\n",
             h->host, h->port, c->who);
@@ -269,10 +322,10 @@ amu_ldap_rebind(ALD *a)
 #else /* not defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) */
        plog(XLOG_WARNING, "ldap_enable_cache(%ld) is not available on this system!\n", gopt.ldap_cache_seconds);
 #endif /* not defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) */
-       a->ldap = ld;
-       a->timestamp = now;
-       return (0);
       }
+      a->ldap = ld;
+      a->timestamp = now;
+      return (0);
     }
     plog(XLOG_WARNING, "Exhausted list of ldap servers, looping.\n");
   }
@@ -283,24 +336,24 @@ amu_ldap_rebind(ALD *a)
 
 
 static int
-get_ldap_timestamp(LDAP *ld, char *map, time_t *ts)
+get_ldap_timestamp(ALD *a, char *map, time_t *ts)
 {
   struct timeval tv;
   char **vals, *end;
   char filter[MAXPATHLEN];
   int i, err = 0, nentries = 0;
-  LDAPMessage *res, *entry;
+  LDAPMessage *res = NULL, *entry;
+
+  dlog("-> get_ldap_timestamp: map <%s>\n", map);
 
   tv.tv_sec = 3;
   tv.tv_usec = 0;
   sprintf(filter, AMD_LDAP_TSFILTER, map);
-#ifdef DEBUG
   dlog("Getting timestamp for map %s\n", map);
   dlog("Filter is: %s\n", filter);
   dlog("Base is: %s\n", gopt.ldap_base);
-#endif /* DEBUG */
   for (i = 0; i < AMD_LDAP_RETRIES; i++) {
-    err = ldap_search_st(ld,
+    err = ldap_search_st(a->ldap,
                         gopt.ldap_base,
                         LDAP_SCOPE_SUBTREE,
                         filter,
@@ -310,19 +363,32 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts)
                         &res);
     if (err == LDAP_SUCCESS)
       break;
-#ifdef DEBUG
-    dlog("Timestamp search timed out, trying again...\n");
-#endif /* DEBUG */
+    if (res) {
+      ldap_msgfree(res);
+      res = NULL;
+    }
+    plog(XLOG_USER, "Timestamp LDAP search attempt %d failed: %s\n",
+        i + 1, ldap_err2string(err));
+    if (err != LDAP_TIMEOUT) {
+      dlog("get_ldap_timestamp: unbinding...\n");
+      amu_ldap_unbind(a->ldap);
+      a->ldap = NULL;
+      if (amu_ldap_rebind(a))
+        return (ENOENT);
+    }
+    dlog("Timestamp search failed, trying again...\n");
   }
 
   if (err != LDAP_SUCCESS) {
     *ts = 0;
     plog(XLOG_USER, "LDAP timestamp search failed: %s\n",
         ldap_err2string(err));
+    if (res)
+      ldap_msgfree(res);
     return (ENOENT);
   }
 
-  nentries = ldap_count_entries(ld, res);
+  nentries = ldap_count_entries(a->ldap, res);
   if (nentries == 0) {
     plog(XLOG_USER, "No timestamp entry for map %s\n", map);
     *ts = 0;
@@ -330,8 +396,8 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts)
     return (ENOENT);
   }
 
-  entry = ldap_first_entry(ld, res);
-  vals = ldap_get_values(ld, entry, AMD_LDAP_TSATTR);
+  entry = ldap_first_entry(a->ldap, res);
+  vals = ldap_get_values(a->ldap, entry, AMD_LDAP_TSATTR);
   if (ldap_count_values(vals) == 0) {
     plog(XLOG_USER, "Missing timestamp value for map %s\n", map);
     *ts = 0;
@@ -339,9 +405,7 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts)
     ldap_msgfree(res);
     return (ENOENT);
   }
-#ifdef DEBUG
   dlog("TS value is:%s:\n", vals[0]);
-#endif /* DEBUG */
 
   if (vals[0]) {
     *ts = (time_t) strtol(vals[0], &end, 10);
@@ -363,9 +427,7 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts)
 
   ldap_value_free(vals);
   ldap_msgfree(res);
-#ifdef DEBUG
   dlog("The timestamp for %s is %ld (err=%d)\n", map, *ts, err);
-#endif /* DEBUG */
   return (err);
 }
 
@@ -373,12 +435,15 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts)
 int
 amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts)
 {
-  char **vals, filter[MAXPATHLEN];
+  char **vals, filter[MAXPATHLEN], filter2[2 * MAXPATHLEN];
+  char *f1, *f2;
   struct timeval tv;
   int i, err = 0, nvals = 0, nentries = 0;
-  LDAPMessage *entry, *res;
+  LDAPMessage *entry, *res = NULL;
   ALD *a = (ALD *) (m->map_data);
 
+  dlog("-> amu_ldap_search: map <%s>, key <%s>\n", map, key);
+
   tv.tv_sec = 2;
   tv.tv_usec = 0;
   if (a == NULL) {
@@ -389,42 +454,62 @@ amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts)
     return (ENOENT);
 
   sprintf(filter, AMD_LDAP_FILTER, map, key);
-#ifdef DEBUG
-  dlog("Search with filter: %s\n", filter);
-#endif /* DEBUG */
+  /* "*" is special to ldap_search(); run through the filter escaping it. */
+  f1 = filter; f2 = filter2;
+  while (*f1) {
+    if (*f1 == '*') {
+      *f2++ = '\\'; *f2++ = '2'; *f2++ = 'a';
+      f1++;
+    } else {
+      *f2++ = *f1++;
+    }
+  }
+  *f2 = '\0';
+  dlog("Search with filter: <%s>\n", filter2);
   for (i = 0; i < AMD_LDAP_RETRIES; i++) {
     err = ldap_search_st(a->ldap,
                         gopt.ldap_base,
                         LDAP_SCOPE_SUBTREE,
-                        filter,
+                        filter2,
                         0,
                         0,
                         &tv,
                         &res);
     if (err == LDAP_SUCCESS)
       break;
+    if (res) {
+      ldap_msgfree(res);
+      res = NULL;
+    }
+    plog(XLOG_USER, "LDAP search attempt %d failed: %s\n",
+        i + 1, ldap_err2string(err));
+    if (err != LDAP_TIMEOUT) {
+      dlog("amu_ldap_search: unbinding...\n");
+      amu_ldap_unbind(a->ldap);
+      a->ldap = NULL;
+      if (amu_ldap_rebind(a))
+        return (ENOENT);
+    }
   }
 
   switch (err) {
   case LDAP_SUCCESS:
     break;
   case LDAP_NO_SUCH_OBJECT:
-#ifdef DEBUG
     dlog("No object\n");
-#endif /* DEBUG */
-    ldap_msgfree(res);
+    if (res)
+      ldap_msgfree(res);
     return (ENOENT);
   default:
     plog(XLOG_USER, "LDAP search failed: %s\n",
         ldap_err2string(err));
-    ldap_msgfree(res);
+    if (res)
+      ldap_msgfree(res);
     return (EIO);
   }
 
   nentries = ldap_count_entries(a->ldap, res);
-#ifdef DEBUG
   dlog("Search found %d entries\n", nentries);
-#endif /* DEBUG */
   if (nentries == 0) {
     ldap_msgfree(res);
     return (ENOENT);
@@ -438,9 +523,7 @@ amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts)
     ldap_msgfree(res);
     return (EIO);
   }
-#ifdef DEBUG
   dlog("Map %s, %s => %s\n", map, key, vals[0]);
-#endif /* DEBUG */
   if (vals[0]) {
     *pval = strdup(vals[0]);
     err = 0;
@@ -461,15 +544,13 @@ amu_ldap_mtime(mnt_map *m, char *map, time_t *ts)
   ALD *aldh = (ALD *) (m->map_data);
 
   if (aldh == NULL) {
-#ifdef DEBUG
     dlog("LDAP panic: unable to find map data\n");
-#endif /* DEBUG */
     return (ENOENT);
   }
   if (amu_ldap_rebind(aldh)) {
     return (ENOENT);
   }
-  if (get_ldap_timestamp(aldh->ldap, map, ts)) {
+  if (get_ldap_timestamp(aldh, map, ts)) {
     return (ENOENT);
   }
   return (0);