From 6821c9c1b67181b020f375bab7ac8061b0dcbec2 Mon Sep 17 00:00:00 2001 From: Ralph Boehme Date: Tue, 10 Sep 2013 18:17:34 +0200 Subject: [PATCH] Active Directory LDAP queries for ACL support 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 | 2 + doc/manpages/man5/afp.conf.5.xml | 32 ++++++- include/atalk/ldapconfig.h | 2 + libatalk/acl/ldap.c | 155 ++++++++++++++++++++----------- man/man5/afp.conf.5.in | 25 ++++- 5 files changed, 154 insertions(+), 62 deletions(-) diff --git a/NEWS b/NEWS index 839add0d..0d2b299f 100644 --- 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 ================ diff --git a/doc/manpages/man5/afp.conf.5.xml b/doc/manpages/man5/afp.conf.5.xml index 27e8b14e..0cf62529 100644 --- a/doc/manpages/man5/afp.conf.5.xml +++ b/doc/manpages/man5/afp.conf.5.xml @@ -1314,6 +1314,16 @@ + + ldap group attr = dn + (G) + + + Name of the LDAP attribute with the groups short + name. + + + ldap uuid string = STRING (G) @@ -1339,7 +1349,8 @@ 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. - + See also the options and + . string @@ -1361,14 +1372,27 @@ - ldap group attr = dn + ldap user filter = STRING (default: unused) (G) - 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. + Recommended setting for Active Directory: objectClass=user. + + + ldap group filter = STRING (default: unused) + (G) + + + 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. + Recommended setting for Active Directory: objectClass=group. + + + diff --git a/include/atalk/ldapconfig.h b/include/atalk/ldapconfig.h index adc2a99d..f01e0a06 100644 --- a/include/atalk/ldapconfig.h +++ b/include/atalk/ldapconfig.h @@ -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 { diff --git a/libatalk/acl/ldap.c b/libatalk/acl/ldap.c index f04c5aef..e805a990 100644 --- a/libatalk/acl/ldap.c +++ b/libatalk/acl/ldap.c @@ -31,6 +31,7 @@ #include #include #include /* For struct ldap_pref */ +#include 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 */ diff --git a/man/man5/afp.conf.5.in b/man/man5/afp.conf.5.in index 7a1f56d5..93893286 100644 --- a/man/man5/afp.conf.5.in +++ b/man/man5/afp.conf.5.in @@ -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" -- 2.39.2