Active Directory LDAP queries for ACL support
authorRalph Boehme <sloowfranklin@gmail.com>
Tue, 10 Sep 2013 16:17:34 +0000 (18:17 +0200)
committerFrank Lahm <franklahm@gmail.com>
Thu, 12 Sep 2013 14:37:54 +0000 (16:37 +0200)
AD stores users and groups under the same subtree which broke our naive
search strategy. Add LDAP options "ldap user filter" and "ldap group filter".

Fixes Bug #526.

NEWS
doc/manpages/man5/afp.conf.5.xml
include/atalk/ldapconfig.h
libatalk/acl/ldap.c
man/man5/afp.conf.5.in

diff --git a/NEWS b/NEWS
index 839add0dca3bcb27496b381f96359c51565031df..0d2b299f06d8efaa8b90bf6f6a501834f2a13577 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,8 @@ Changes in 3.0.6
 * FIX: charset conversion failed when copying from Mac OS 9. Bug #523.
 * UPD: Don't force S_ISGID for directories on FreeBSD. Bug #525.
 * NEW: Add support for ZFS ACLs on FreeBSD with libsunacl. From FR#83.
+* FIX: Active Directory LDAP queries for ACL support with new options
+       "ldap user filter" and "ldap group filter". Bug #526.
 
 Changes in 3.0.5
 ================
index 27e8b14e81d3aff738e6a25aeeeea572392e252c..0cf62529c638d49305b91d84a14e209192d4e077 100644 (file)
           </listitem>
         </varlistentry>
 
+        <varlistentry>
+          <term>ldap group attr = <parameter>dn</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Name of the LDAP attribute with the groups short
+            name.</para>
+          </listitem>
+        </varlistentry>
+
         <varlistentry>
           <term>ldap uuid string = <parameter>STRING</parameter>
           <type>(G)</type></term>
             internal UUID representation is converted to and from the binary
             format used in the objectGUID attribute found on objects in Active
             Directory when interacting with the server.</para>
-
+            <para>See also the options <option>ldap user filter</option> and
+            <option>ldap group filter</option>.</para>
             <para><variablelist>
                 <varlistentry>
                   <term>string</term>
         </varlistentry>
 
         <varlistentry>
-          <term>ldap group attr = <parameter>dn</parameter>
+          <term>ldap user filter = <parameter>STRING (default: unused)</parameter>
           <type>(G)</type></term>
 
           <listitem>
-            <para>Name of the LDAP attribute with the groups short
-            name.</para>
+            <para>Optional LDAP filter that matches user objects. This is necessary for Active Directory
+            environments where users and groups are stored in the same directory subtree.</para>
+            <para>Recommended setting for Active Directory: <parameter>objectClass=user</parameter>.</para>
           </listitem>
         </varlistentry>
+
+        <varlistentry>
+          <term>ldap group filter = <parameter>STRING (default: unused)</parameter>
+          <type>(G)</type></term>
+
+          <listitem>
+            <para>Optional LDAP filter that matches group objects. This is necessary for Active Directory
+            environments where users and groups are stored in the same directory subtree.</para>
+            <para>Recommended setting for Active Directory: <parameter>objectClass=group</parameter>.</para>
+          </listitem>
+        </varlistentry>
+
       </variablelist>
     </refsect2>
   </refsect1>
index adc2a99d441ad31414b6ea392800db191a790c50..f01e0a064cce17e3216358a1bd2ad05e2eca4134 100644 (file)
@@ -21,6 +21,8 @@ extern char *ldap_uuid_string;
 extern char *ldap_name_attr;
 extern char *ldap_group_attr;
 extern char *ldap_uid_attr;
+extern char *ldap_userfilter;
+extern char *ldap_groupfilter;
 extern int  ldap_uuid_encoding;
 
 typedef enum {
index f04c5aef1ed106df95247757e97581c08ff86ef5..e805a990542deb3c883a4419e1757c101977867c 100644 (file)
@@ -31,6 +31,7 @@
 #include <atalk/afp.h>
 #include <atalk/uuid.h>
 #include <atalk/ldapconfig.h>   /* For struct ldap_pref */
+#include <atalk/errchk.h>
 
 typedef enum {
     KEEPALIVE = 1
@@ -54,6 +55,8 @@ char *ldap_uuid_string;
 char *ldap_name_attr;
 char *ldap_group_attr;
 char *ldap_uid_attr;
+char *ldap_userfilter;
+char *ldap_groupfilter;
 int  ldap_uuid_encoding;
 
 struct ldap_pref ldap_prefs[] = {
@@ -72,6 +75,9 @@ struct ldap_pref ldap_prefs[] = {
     {&ldap_group_attr,     "ldap group attr",     0, 0, -1, -1},
     {&ldap_uid_attr,       "ldap uid attr",       0, 0,  0,  0},
     {&ldap_uuid_encoding,  "ldap uuid encoding",  1, 1,  0,  0},
+    {&ldap_userfilter,     "ldap user filter",    0, 0,  0,  0},
+    {&ldap_groupfilter,    "ldap group filter",   0, 0,  0,  0},
+    {&ldap_auth_pw,        "ldap auth pw",        0, 0,  0,  0},
     {NULL,                 NULL,                  0, 0,  0,  0}
 };
 
@@ -256,6 +262,69 @@ cleanup:
     return ret;
 }
 
+/*!
+ * Generate LDAP filter string for UUID query
+
+ * @param[in] uuidstr      the UUID as string
+ * @param[in] attr_filter  optional attribute
+ * @returns   pointer to static filter string
+ */
+static char *gen_uuid_filter(const char *uuidstr_in, const char *attr_filter)
+{
+    EC_INIT;
+    int len;
+    const char *uuidstr = uuidstr_in;
+
+#define MAX_FILTER_SIZE 512
+    static char filter[MAX_FILTER_SIZE];
+    char stripped[MAX_FILTER_SIZE];
+
+#define LDAP_BIN_UUID_LEN 49 /* LDAP Binary Notation is \XX * 16 bytes of UUID + terminator = 49 */
+    char ldap_bytes[LDAP_BIN_UUID_LEN];
+
+    if (ldap_uuid_encoding == LDAP_UUID_ENCODING_MSGUID) {
+        /* Convert to LDAP-safe binary encoding for direct query of AD objectGUID attribute */
+        int i = 0, s = 0;
+        char c;
+        while ((c = uuidstr[i])) {
+            if((c >='a' && c <= 'f')
+                || (c >= 'A' && c <= 'F')
+                || (c >= '0' && c <= '9')) {
+                stripped[s++] = toupper(c);
+            }
+            i++;
+        }
+
+        snprintf(ldap_bytes, LDAP_BIN_UUID_LEN,
+                 "\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c"
+                 "\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c",
+                 /* Data1 (uint32) */
+                 stripped[6], stripped[7], stripped[4], stripped[5],
+                 stripped[2], stripped[3], stripped[0], stripped[1],
+                 /* Data2 (uint16) */
+                 stripped[10], stripped[11], stripped[8], stripped[9],
+                 /* Data3 (uint16) */
+                 stripped[14], stripped[15], stripped[12], stripped[13],
+                 /* Data4 (uint64) */
+                 stripped[16], stripped[17], stripped[18], stripped[19],
+                 stripped[20], stripped[21], stripped[22], stripped[23],
+                 stripped[24], stripped[25], stripped[26], stripped[27],
+                 stripped[28], stripped[29], stripped[30], stripped[31]);
+        uuidstr = ldap_bytes;
+    }
+
+    if (attr_filter) {
+        len = snprintf(filter, 256, "(&(%s=%s)(%s))", ldap_uuid_attr, uuidstr, attr_filter);
+    } else {
+        len = snprintf(filter, 256, "%s=%s", ldap_uuid_attr, uuidstr);
+    }
+
+EC_CLEANUP:
+    if (ret != 0)
+        return NULL;
+    return filter;
+}
+
 /********************************************************
  * Interface
  ********************************************************/
@@ -332,76 +401,50 @@ int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string)
  * returns 0 on success, -1 on errror
  */
 int ldap_getnamefromuuid( const char *uuidstr, char **name, uuidtype_t *type) {
-    int ret;
-    int len;
-    char filter[256];       /* this should really be enough. we dont want to malloc everything! */
+    EC_INIT;
+    char *filter;
     char *attributes[]  = { NULL, NULL};
 
     if (!ldap_config_valid)
-        return -1;
-
-    if(ldap_uuid_encoding == LDAP_UUID_ENCODING_MSGUID) {
-        /* Convert to LDAP-safe binary encoding for direct query of AD objectGUID attribute */
-        char* stripped = malloc(strlen(uuidstr));
-
-        int i = 0;
-        int s = 0;
-        char c;
-        while ((c = uuidstr[i])) {
-            if((c >='a' && c <= 'f')
-                || (c >= 'A' && c <= 'F')
-                || (c >= '0' && c <= '9')) {
-                stripped[s++] = toupper(c);
-            }
-            i++;
-        }
+        EC_FAIL;
 
-        /* LDAP Binary Notation is \XX * 16 bytes of UUID + terminator = 49 */
-        char* ldap_bytes = malloc(49);
-        snprintf(ldap_bytes, 49,
-            "\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c"
-            "\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c",
-            /* Data1 (uint32) */
-            stripped[6], stripped[7], stripped[4], stripped[5],
-            stripped[2], stripped[3], stripped[0], stripped[1],
-            /* Data2 (uint16) */
-            stripped[10], stripped[11], stripped[8], stripped[9],
-            /* Data3 (uint16) */
-            stripped[14], stripped[15], stripped[12], stripped[13],
-            /* Data4 (uint64) */
-            stripped[16], stripped[17], stripped[18], stripped[19],
-            stripped[20], stripped[21], stripped[22], stripped[23],
-            stripped[24], stripped[25], stripped[26], stripped[27],
-            stripped[28], stripped[29], stripped[30], stripped[31]);
-        len = snprintf( filter, 256, "%s=%s", ldap_uuid_attr, ldap_bytes);
-
-        free(ldap_bytes);
-        free(stripped);
-    } else {
-        len = snprintf( filter, 256, "%s=%s", ldap_uuid_attr, uuidstr);
-    }
+    /*
+     * Search groups first as group acls are probably used more often.
+     * Note the special case of AD where users and groups are stored
+     * under the same subtree.
+     */
 
-    if (len >= 256 || len == -1) {
-        LOG(log_error, logtype_default, "ldap_getnamefromuuid: filter overflow:%d, \"%s\"", len, filter);
-        return -1;
-    }
-    /* search groups first. group acls are probably used more often */
     attributes[0] = ldap_group_attr;
-    ret = ldap_getattr_fromfilter_withbase_scope( ldap_groupbase, filter, attributes, ldap_groupscope, KEEPALIVE, name);
-    if (ret == -1)
-        return -1;
+    EC_NULL( filter = gen_uuid_filter(uuidstr, ldap_groupfilter) );
+    EC_NEG1( ret = ldap_getattr_fromfilter_withbase_scope(
+                 ldap_groupbase,
+                 filter,
+                 attributes,
+                 ldap_groupscope,
+                 KEEPALIVE,
+                 name) );
     if (ret == 1) {
         *type = UUID_GROUP;
-        return 0;
+        EC_EXIT_STATUS(0);
     }
 
     attributes[0] = ldap_name_attr;
-    ret = ldap_getattr_fromfilter_withbase_scope( ldap_userbase, filter, attributes, ldap_userscope, KEEPALIVE, name);
+    EC_NULL( filter = gen_uuid_filter(uuidstr, ldap_userfilter) );
+    EC_NEG1( ret = ldap_getattr_fromfilter_withbase_scope(
+                 ldap_userbase,
+                 filter,
+                 attributes,
+                 ldap_userscope,
+                 KEEPALIVE,
+                 name) );
     if (ret == 1) {
         *type = UUID_USER;
-        return 0;
+        EC_EXIT_STATUS(0);
     }
 
-    return -1;
+    EC_FAIL;
+
+EC_CLEANUP:
+    EC_EXIT;
 }
 #endif  /* HAVE_LDAP */
index 7a1f56d5af83129550089c83264f302815e8022a..9389328676df6d91d353f2a30d065bad8f7b98d7 100644 (file)
@@ -821,6 +821,11 @@ ldap name attr = \fIdn\fR \fB(G)\fR
 Name of the LDAP attribute with the users short name\&.
 .RE
 .PP
+ldap group attr = \fIdn\fR \fB(G)\fR
+.RS 4
+Name of the LDAP attribute with the groups short name\&.
+.RE
+.PP
 ldap uuid string = \fISTRING\fR \fB(G)\fR
 .RS 4
 Format of the uuid string in the directory\&. A series of x and \-, where every x denotes a value 0\-9a\-f and every \- is a separator\&.
@@ -831,6 +836,11 @@ Default: xxxxxxxx\-xxxx\-xxxx\-xxxx\-xxxxxxxxxxxx
 ldap uuid encoding = \fIstring | ms\-guid (default: string)\fR \fB(G)\fR
 .RS 4
 Format of the UUID of the LDAP attribute, allows usage of the binary objectGUID fields from Active Directory\&. If left unspecified, string is the default, which passes through the ASCII UUID returned by most other LDAP stores\&. If set to ms\-guid, the internal UUID representation is converted to and from the binary format used in the objectGUID attribute found on objects in Active Directory when interacting with the server\&.
+.sp
+See also the options
+\fBldap user filter\fR
+and
+\fBldap group filter\fR\&.
 .PP
 string
 .RS 4
@@ -843,9 +853,20 @@ Binary objectGUID from Active Directory
 .RE
 .RE
 .PP
-ldap group attr = \fIdn\fR \fB(G)\fR
+ldap user filter = \fISTRING (default: unused)\fR \fB(G)\fR
 .RS 4
-Name of the LDAP attribute with the groups short name\&.
+Optional LDAP filter that matches user objects\&. This is necessary for Active Directory environments where users and groups are stored in the same directory subtree\&.
+.sp
+Recommended setting for Active Directory:
+\fIobjectClass=user\fR\&.
+.RE
+.PP
+ldap group filter = \fISTRING (default: unused)\fR \fB(G)\fR
+.RS 4
+Optional LDAP filter that matches group objects\&. This is necessary for Active Directory environments where users and groups are stored in the same directory subtree\&.
+.sp
+Recommended setting for Active Directory:
+\fIobjectClass=group\fR\&.
 .RE
 .SH "EXPLANATION OF VOLUME PARAMETERS"
 .SS "Parameters"