2 Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
17 #endif /* HAVE_CONFIG_H */
27 #define LDAP_DEPRECATED 1
30 #include <atalk/logger.h>
31 #include <atalk/afp.h>
32 #include <atalk/uuid.h>
33 #include <atalk/ldapconfig.h> /* For struct ldap_pref */
34 #include <atalk/errchk.h>
40 /********************************************************
41 * LDAP config stuff. Filled by libatalk/acl/ldap_config.c
42 ********************************************************/
43 int ldap_config_valid;
54 char *ldap_uuid_string;
56 char *ldap_group_attr;
58 char *ldap_userfilter;
59 char *ldap_groupfilter;
60 int ldap_uuid_encoding;
62 struct ldap_pref ldap_prefs[] = {
63 /* pointer to pref, prefname, strorint, intfromarray, valid, valid_save */
64 {&ldap_server, "ldap server", 0, 0, -1, -1},
65 {&ldap_auth_method, "ldap auth method", 1, 1, -1, -1},
66 {&ldap_auth_dn, "ldap auth dn", 0, 0, 0, 0},
67 {&ldap_auth_pw, "ldap auth pw", 0, 0, 0, 0},
68 {&ldap_userbase, "ldap userbase", 0, 0, -1, -1},
69 {&ldap_userscope, "ldap userscope", 1 ,1, -1, -1},
70 {&ldap_groupbase, "ldap groupbase", 0, 0, -1, -1},
71 {&ldap_groupscope, "ldap groupscope", 1 ,1, -1, -1},
72 {&ldap_uuid_attr, "ldap uuid attr", 0, 0, -1, -1},
73 {&ldap_uuid_string, "ldap uuid string", 0, 0, 0, 0},
74 {&ldap_name_attr, "ldap name attr", 0, 0, -1, -1},
75 {&ldap_group_attr, "ldap group attr", 0, 0, -1, -1},
76 {&ldap_uid_attr, "ldap uid attr", 0, 0, 0, 0},
77 {&ldap_uuid_encoding, "ldap uuid encoding", 1, 1, 0, 0},
78 {&ldap_userfilter, "ldap user filter", 0, 0, 0, 0},
79 {&ldap_groupfilter, "ldap group filter", 0, 0, 0, 0},
80 {&ldap_auth_pw, "ldap auth pw", 0, 0, 0, 0},
81 {NULL, NULL, 0, 0, 0, 0}
84 struct pref_array prefs_array[] = {
85 {"ldap auth method", "none", LDAP_AUTH_NONE},
86 {"ldap auth method", "simple", LDAP_AUTH_SIMPLE},
87 {"ldap auth method", "sasl", LDAP_AUTH_SASL},
88 {"ldap userscope", "base", LDAP_SCOPE_BASE},
89 {"ldap userscope", "one", LDAP_SCOPE_ONELEVEL},
90 {"ldap userscope", "sub", LDAP_SCOPE_SUBTREE},
91 {"ldap groupscope", "base", LDAP_SCOPE_BASE},
92 {"ldap groupscope", "one", LDAP_SCOPE_ONELEVEL},
93 {"ldap groupscope", "sub", LDAP_SCOPE_SUBTREE},
94 {"ldap uuid encoding", "ms-guid", LDAP_UUID_ENCODING_MSGUID},
95 {"ldap uuid encoding", "string", LDAP_UUID_ENCODING_STRING},
99 /********************************************************
100 * Static helper function
101 ********************************************************/
104 * ldap_getattr_fromfilter_withbase_scope():
105 * conflags: KEEPALIVE
106 * scope: LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
107 * result: return unique search result here, allocated here, caller must free
109 * returns: -1 on error
111 * 1 successfull search, result int 'result'
113 * All connection managment to the LDAP server is done here. Just set KEEPALIVE if you know
114 * you will be dispatching more than one search in a row, then don't set it with the last search.
115 * You MUST dispatch the queries timely, otherwise the LDAP handle might timeout.
117 static int ldap_getattr_fromfilter_withbase_scope( const char *searchbase,
126 int desired_version = LDAP_VERSION3;
127 static int ldapconnected = 0;
128 static LDAP *ld = NULL;
129 LDAPMessage* msg = NULL;
130 LDAPMessage* entry = NULL;
131 struct berval **attribute_values = NULL;
132 struct timeval timeout;
134 LOG(log_maxdebug, logtype_afpd,"ldap: BEGIN");
139 /* init LDAP if necessary */
144 LOG(log_maxdebug, logtype_default, "ldap: server: \"%s\"",
146 if ((ld = ldap_init(ldap_server, LDAP_PORT)) == NULL ) {
147 LOG(log_error, logtype_default, "ldap: ldap_init error: %s",
151 if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &desired_version) != 0) {
152 /* LDAP_OPT_SUCCESS is not in the proposed standard, so we check for 0
153 http://tools.ietf.org/id/draft-ietf-ldapext-ldap-c-api-05.txt */
154 LOG(log_error, logtype_default, "ldap: ldap_set_option failed!");
162 if (!ldapconnected) {
163 if (LDAP_AUTH_NONE == ldap_auth_method) {
164 if (ldap_bind_s(ld, "", "", LDAP_AUTH_SIMPLE) != LDAP_SUCCESS ) {
165 LOG(log_error, logtype_default, "ldap: ldap_bind failed, auth_method: \'%d\'",
173 } else if (LDAP_AUTH_SIMPLE == ldap_auth_method) {
174 if (ldap_bind_s(ld, ldap_auth_dn, ldap_auth_pw, ldap_auth_method) != LDAP_SUCCESS ) {
175 LOG(log_error, logtype_default,
176 "ldap: ldap_bind failed: ldap_auth_dn: \'%s\', ldap_auth_pw: \'%s\', ldap_auth_method: \'%d\'",
177 ldap_auth_dn, ldap_auth_pw, ldap_auth_method);
186 LOG(log_maxdebug, logtype_afpd, "ldap: start search: base: %s, filter: %s, attr: %s",
187 searchbase, filter, attributes[0]);
189 /* start LDAP search */
190 ldaperr = ldap_search_st(ld, searchbase, scope, filter, attributes, 0, &timeout, &msg);
191 LOG(log_maxdebug, logtype_default, "ldap: ldap_search_st returned: %s",
192 ldap_err2string(ldaperr));
193 if (ldaperr != LDAP_SUCCESS) {
194 LOG(log_error, logtype_default, "ldap: ldap_search_st failed: %s, retrycount: %i",
195 ldap_err2string(ldaperr), retrycount);
200 /* parse search result */
201 LOG(log_maxdebug, logtype_default, "ldap: got %d entries from ldap search",
202 ldap_count_entries(ld, msg));
203 if ((ret = ldap_count_entries(ld, msg)) != 1) {
208 entry = ldap_first_entry(ld, msg);
210 LOG(log_error, logtype_default, "ldap: ldap_first_entry error");
215 attribute_values = ldap_get_values_len(ld, entry, attributes[0]);
216 if (attribute_values == NULL) {
217 LOG(log_error, logtype_default, "ldap: ldap_get_values_len error");
222 LOG(log_maxdebug, logtype_afpd,"ldap: search result: %s: %s",
223 attributes[0], attribute_values[0]->bv_val);
225 /* allocate and copy result */
226 *result = calloc(1, attribute_values[0]->bv_len + 1);
227 memcpy(*result, attribute_values[0]->bv_val, attribute_values[0]->bv_len + 1);
229 if (*result == NULL) {
230 LOG(log_error, logtype_default, "ldap: memcopy error: %s", strerror(errno));
236 if (attribute_values)
237 ldap_value_free_len(attribute_values);
238 /* FIXME: is there another way to free entry ? */
239 while (entry != NULL)
240 entry = ldap_next_entry(ld, entry);
245 if ((ret == -1) || !(conflags & KEEPALIVE)) {
246 LOG(log_maxdebug, logtype_default,"ldap: unbind");
247 if (ldap_unbind_s(ld) != 0) {
248 LOG(log_error, logtype_default, "ldap: unbind: %s\n", ldap_err2string(ldaperr));
254 /* In case of error we try twice */
266 * Generate LDAP filter string for UUID query
268 * @param[in] uuidstr the UUID as string
269 * @param[in] attr_filter optional attribute
270 * @returns pointer to static filter string
272 static char *gen_uuid_filter(const char *uuidstr_in, const char *attr_filter)
276 const char *uuidstr = uuidstr_in;
278 #define MAX_FILTER_SIZE 512
279 static char filter[MAX_FILTER_SIZE];
280 char stripped[MAX_FILTER_SIZE];
282 #define LDAP_BIN_UUID_LEN 49 /* LDAP Binary Notation is \XX * 16 bytes of UUID + terminator = 49 */
283 char ldap_bytes[LDAP_BIN_UUID_LEN];
285 if (ldap_uuid_encoding == LDAP_UUID_ENCODING_MSGUID) {
286 /* Convert to LDAP-safe binary encoding for direct query of AD objectGUID attribute */
289 while ((c = uuidstr[i])) {
290 if((c >='a' && c <= 'f')
291 || (c >= 'A' && c <= 'F')
292 || (c >= '0' && c <= '9')) {
293 stripped[s++] = toupper(c);
298 snprintf(ldap_bytes, LDAP_BIN_UUID_LEN,
299 "\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c"
300 "\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c\\%c%c",
302 stripped[6], stripped[7], stripped[4], stripped[5],
303 stripped[2], stripped[3], stripped[0], stripped[1],
305 stripped[10], stripped[11], stripped[8], stripped[9],
307 stripped[14], stripped[15], stripped[12], stripped[13],
309 stripped[16], stripped[17], stripped[18], stripped[19],
310 stripped[20], stripped[21], stripped[22], stripped[23],
311 stripped[24], stripped[25], stripped[26], stripped[27],
312 stripped[28], stripped[29], stripped[30], stripped[31]);
313 uuidstr = ldap_bytes;
317 len = snprintf(filter, 256, "(&(%s=%s)(%s))", ldap_uuid_attr, uuidstr, attr_filter);
319 len = snprintf(filter, 256, "%s=%s", ldap_uuid_attr, uuidstr);
328 /********************************************************
330 ********************************************************/
333 * Search UUID for name in LDAP
335 * Caller must free uuid_string when done with it
337 * @param name (r) name to search
338 * @param type (r) type of USER or GROUP
339 * @param uuid_string (w) result as pointer to allocated UUID-string
341 * @returns 0 on success, -1 on error or not found
343 int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string) {
346 char filter[256]; /* this should really be enough. we dont want to malloc everything! */
347 char *attributes[] = { ldap_uuid_attr, NULL};
350 if (!ldap_config_valid)
354 if (type == UUID_GROUP)
355 ldap_attr = ldap_group_attr;
356 else /* type hopefully == UUID_USER */
357 ldap_attr = ldap_name_attr;
358 len = snprintf( filter, 256, "%s=%s", ldap_attr, name);
359 if (len >= 256 || len == -1) {
360 LOG(log_error, logtype_default, "ldap_getnamefromuuid: filter error:%d, \"%s\"", len, filter);
364 if (type == UUID_GROUP) {
365 ret = ldap_getattr_fromfilter_withbase_scope( ldap_groupbase, filter, attributes, ldap_groupscope, KEEPALIVE, uuid_string);
366 } else { /* type hopefully == UUID_USER */
367 ret = ldap_getattr_fromfilter_withbase_scope( ldap_userbase, filter, attributes, ldap_userscope, KEEPALIVE, uuid_string);
373 if(ldap_uuid_encoding == LDAP_UUID_ENCODING_MSGUID) {
374 /* Convert byte array to UUID string (no dashes) */
375 unsigned char* uuid_bytes = (unsigned char*) *uuid_string;
376 *uuid_string = malloc(37);
377 snprintf(*uuid_string, 37,
378 "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
379 uuid_bytes[3], uuid_bytes[2], uuid_bytes[1], uuid_bytes[0], /* Data1 */
380 uuid_bytes[5], uuid_bytes[4], /* Data2 */
381 uuid_bytes[7], uuid_bytes[6], /* Data3 */
382 uuid_bytes[8], uuid_bytes[9], /* Data4 - High Bytes */
383 uuid_bytes[10], uuid_bytes[11], uuid_bytes[12], /* Data4 - Low Bytes */
384 uuid_bytes[13], uuid_bytes[14], uuid_bytes[15]);
386 LOG(log_error, logtype_default, "ldap_getnamefromuuid: uuid_string: %s", *uuid_string);
393 * LDAP search wrapper
394 * returns allocated storage in name, caller must free it
395 * returns 0 on success, -1 on error or not found
397 * @param uuidstr (r) uuid to search as ascii string
398 * @param name (w) return pointer to name as allocated string
399 * @param type (w) return type: USER or GROUP
401 * returns 0 on success, -1 on errror
403 int ldap_getnamefromuuid( const char *uuidstr, char **name, uuidtype_t *type) {
406 char *attributes[] = { NULL, NULL};
408 if (!ldap_config_valid)
412 * Search groups first as group acls are probably used more often.
413 * Note the special case of AD where users and groups are stored
414 * under the same subtree.
417 attributes[0] = ldap_group_attr;
418 EC_NULL( filter = gen_uuid_filter(uuidstr, ldap_groupfilter) );
419 EC_NEG1( ret = ldap_getattr_fromfilter_withbase_scope(
431 attributes[0] = ldap_name_attr;
432 EC_NULL( filter = gen_uuid_filter(uuidstr, ldap_userfilter) );
433 EC_NEG1( ret = ldap_getattr_fromfilter_withbase_scope(
450 #endif /* HAVE_LDAP */