]> arthur.barton.de Git - netatalk.git/blob - libatalk/acl/ldap.c
more intuitive parameter
[netatalk.git] / libatalk / acl / ldap.c
1 /*
2   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
3
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.
8
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.
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif /* HAVE_CONFIG_H */
18
19 #ifdef HAVE_LDAP
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/time.h>
24 #include <string.h>
25 #include <errno.h>
26 #define LDAP_DEPRECATED 1
27 #include <ldap.h>
28
29 #include <atalk/logger.h>
30 #include <atalk/afp.h>
31 #include <atalk/uuid.h>
32 #include <atalk/ldapconfig.h>   /* For struct ldap_pref */
33
34 typedef enum {
35     KEEPALIVE = 1
36 } ldapcon_t;
37
38 /********************************************************
39  * LDAP config stuff. Filled by libatalk/acl/ldap_config.c
40  ********************************************************/
41 int ldap_config_valid;
42
43 char *ldap_server;
44 int  ldap_auth_method;
45 char *ldap_auth_dn;
46 char *ldap_auth_pw;
47 char *ldap_userbase;
48 int  ldap_userscope;
49 char *ldap_groupbase;
50 int  ldap_groupscope;
51 char *ldap_uuid_attr;
52 char *ldap_uuid_string;
53 char *ldap_name_attr;
54 char *ldap_group_attr;
55 char *ldap_uid_attr;
56
57 struct ldap_pref ldap_prefs[] = {
58     {&ldap_server,     "ldap server",      0, 0, -1},
59     {&ldap_auth_method,"ldap auth method", 1, 1, -1},
60     {&ldap_auth_dn,    "ldap auth dn",     0, 0,  0},
61     {&ldap_auth_pw,    "ldap auth pw",     0, 0,  0},
62     {&ldap_userbase,   "ldap userbase",    0, 0, -1},
63     {&ldap_userscope,  "ldap userscope",   1 ,1, -1},
64     {&ldap_groupbase,  "ldap groupbase",   0, 0, -1},
65     {&ldap_groupscope, "ldap groupscope",  1 ,1, -1},
66     {&ldap_uuid_attr,  "ldap uuid attr",   0, 0, -1},
67     {&ldap_uuid_string,"ldap uuid string", 0, 0,  0},
68     {&ldap_name_attr,  "ldap name attr",   0, 0, -1},
69     {&ldap_group_attr, "ldap group attr",  0, 0, -1},
70     {&ldap_uid_attr,   "ldap uid attr",    0, 0,  0},
71     {NULL,             NULL,               0, 0, -1}
72 };
73
74 struct pref_array prefs_array[] = {
75     {"ldap auth method", "none",   LDAP_AUTH_NONE},
76     {"ldap auth method", "simple", LDAP_AUTH_SIMPLE},
77     {"ldap auth method", "sasl",   LDAP_AUTH_SASL},
78     {"ldap userscope",   "base",   LDAP_SCOPE_BASE},
79     {"ldap userscope",   "one",    LDAP_SCOPE_ONELEVEL},
80     {"ldap userscope",   "sub",    LDAP_SCOPE_SUBTREE},
81     {"ldap groupscope",  "base",   LDAP_SCOPE_BASE},
82     {"ldap groupscope",  "one",    LDAP_SCOPE_ONELEVEL},
83     {"ldap groupscope",  "sub",    LDAP_SCOPE_SUBTREE},
84     {NULL,               NULL,     0}
85 };
86
87 /********************************************************
88  * Static helper function
89  ********************************************************/
90
91 /*
92  * ldap_getattr_fromfilter_withbase_scope():
93  *   conflags: KEEPALIVE
94  *   scope: LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
95  *   result: return unique search result here, allocated here, caller must free
96  *
97  * returns: -1 on error
98  *           0 nothing found
99  *           1 successfull search, result int 'result'
100  *
101  * All connection managment to the LDAP server is done here. Just set KEEPALIVE if you know
102  * you will be dispatching more than one search in a row, then don't set it with the last search.
103  * You MUST dispatch the queries timely, otherwise the LDAP handle might timeout.
104  */
105 static int ldap_getattr_fromfilter_withbase_scope( const char *searchbase,
106                                                    const char *filter,
107                                                    char *attributes[],
108                                                    int scope,
109                                                    ldapcon_t conflags,
110                                                    char **result) {
111     int ret;
112     int ldaperr;
113     int retrycount = 0;
114     int desired_version  = LDAP_VERSION3;
115     static int ldapconnected = 0;
116     static LDAP *ld     = NULL;
117     LDAPMessage* msg    = NULL;
118     LDAPMessage* entry  = NULL;
119     char **attribute_values = NULL;
120     struct timeval timeout;
121
122     LOG(log_maxdebug, logtype_afpd,"ldap: BEGIN");
123
124     timeout.tv_sec = 3;
125     timeout.tv_usec = 0;
126
127     /* init LDAP if necessary */
128 retry:
129     ret = 0;
130
131     if (ld == NULL) {
132         LOG(log_maxdebug, logtype_default, "ldap: server: \"%s\"",
133             ldap_server);
134         if ((ld = ldap_init(ldap_server, LDAP_PORT)) == NULL ) {
135             LOG(log_error, logtype_default, "ldap: ldap_init error: %s",
136                 strerror(errno));
137             return -1;
138         }
139         if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &desired_version) != 0) {
140             /* LDAP_OPT_SUCCESS is not in the proposed standard, so we check for 0
141                http://tools.ietf.org/id/draft-ietf-ldapext-ldap-c-api-05.txt */
142             LOG(log_error, logtype_default, "ldap: ldap_set_option failed!");
143             free(ld);
144             ld = NULL;
145             return -1;
146         }
147     }
148
149     /* connect */
150     if (!ldapconnected) {
151         if (LDAP_AUTH_NONE == ldap_auth_method) {
152             if (ldap_bind_s(ld, "", "", LDAP_AUTH_SIMPLE) != LDAP_SUCCESS ) {
153                 LOG(log_error, logtype_default, "ldap: ldap_bind failed, auth_method: \'%d\'",
154                     ldap_auth_method);
155                 free(ld);
156                 ld = NULL;
157                 return -1;
158             }
159             ldapconnected = 1;
160
161         } else if (LDAP_AUTH_SIMPLE == ldap_auth_method) {
162             if (ldap_bind_s(ld, ldap_auth_dn, ldap_auth_pw, ldap_auth_method) != LDAP_SUCCESS ) {
163                 LOG(log_error, logtype_default,
164                     "ldap: ldap_bind failed: ldap_auth_dn: \'%s\', ldap_auth_pw: \'%s\', ldap_auth_method: \'%d\'",
165                     ldap_auth_dn, ldap_auth_pw, ldap_auth_method);
166                 free(ld);
167                 ld = NULL;
168                 return -1;
169             }
170             ldapconnected = 1;
171         }
172     }
173
174     LOG(log_maxdebug, logtype_afpd, "ldap: start search: base: %s, filter: %s, attr: %s",
175         searchbase, filter, attributes[0]);
176
177     /* start LDAP search */
178     ldaperr = ldap_search_st(ld, searchbase, scope, filter, attributes, 0, &timeout, &msg);
179     LOG(log_maxdebug, logtype_default, "ldap: ldap_search_st returned: %s",
180         ldap_err2string(ldaperr));
181     if (ldaperr != LDAP_SUCCESS) {
182         LOG(log_error, logtype_default, "ldap: ldap_search_st failed: %s, retrycount: %i",
183             ldap_err2string(ldaperr), retrycount);
184         ret = -1;
185         goto cleanup;
186     }
187
188     /* parse search result */
189     LOG(log_maxdebug, logtype_default, "ldap: got %d entries from ldap search",
190         ldap_count_entries(ld, msg));
191     if ((ret = ldap_count_entries(ld, msg)) != 1) {
192         ret = 0;
193         goto cleanup;
194     }
195
196     entry = ldap_first_entry(ld, msg);
197     if (entry == NULL) {
198         LOG(log_error, logtype_default, "ldap: ldap_first_entry error");
199         ret = -1;
200         goto cleanup;
201     }
202     attribute_values = ldap_get_values(ld, entry, attributes[0]);
203     if (attribute_values == NULL) {
204         LOG(log_error, logtype_default, "ldap: ldap_get_values error");
205         ret = -1;
206         goto cleanup;
207     }
208
209     LOG(log_maxdebug, logtype_afpd,"ldap: search result: %s: %s",
210         attributes[0], attribute_values[0]);
211
212     /* allocate result */
213     *result = strdup(attribute_values[0]);
214     if (*result == NULL) {
215         LOG(log_error, logtype_default, "ldap: strdup error: %s",strerror(errno));
216         ret = -1;
217         goto cleanup;
218     }
219
220 cleanup:
221     if (attribute_values)
222         ldap_value_free(attribute_values);
223     /* FIXME: is there another way to free entry ? */
224     while (entry != NULL)
225         entry = ldap_next_entry(ld, entry);
226     if (msg)
227         ldap_msgfree(msg);
228
229     if (ldapconnected) {
230         if ((ret == -1) || !(conflags & KEEPALIVE)) {
231             LOG(log_maxdebug, logtype_default,"ldap: unbind");
232             if (ldap_unbind_s(ld) != 0) {
233                 LOG(log_error, logtype_default, "ldap: unbind: %s\n", ldap_err2string(ldaperr));
234                 return -1;
235             }
236             ld = NULL;
237             ldapconnected = 0;
238
239             /* In case of error we try twice */
240             if (ret == -1) {
241                 retrycount++;
242                 if (retrycount < 2)
243                     goto retry;
244             }
245         }
246     }
247     return ret;
248 }
249
250 /********************************************************
251  * Interface
252  ********************************************************/
253
254 /*! 
255  * Search UUID for name in LDAP
256  *
257  * Caller must free uuid_string when done with it
258  *
259  * @param name        (r) name to search
260  * @param type        (r) type of USER or GROUP
261  * @param uuid_string (w) result as pointer to allocated UUID-string
262  *
263  * @returns 0 on success, -1 on error or not found
264  */
265 int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string) {
266     int ret;
267     int len;
268     char filter[256];           /* this should really be enough. we dont want to malloc everything! */
269     char *attributes[]  = { ldap_uuid_attr, NULL};
270     char *ldap_attr;
271
272     if (!ldap_config_valid)
273         return -1;
274
275     /* make filter */
276     if (type == UUID_GROUP)
277         ldap_attr = ldap_group_attr;
278     else /* type hopefully == UUID_USER */
279         ldap_attr = ldap_name_attr;
280     len = snprintf( filter, 256, "%s=%s", ldap_attr, name);
281     if (len >= 256 || len == -1) {
282         LOG(log_error, logtype_default, "ldap_getnamefromuuid: filter error:%d, \"%s\"", len, filter);
283         return -1;
284     }
285
286     if (type == UUID_GROUP) {
287         ret = ldap_getattr_fromfilter_withbase_scope( ldap_groupbase, filter, attributes, ldap_groupscope, KEEPALIVE, uuid_string);
288     } else  { /* type hopefully == UUID_USER */
289         ret = ldap_getattr_fromfilter_withbase_scope( ldap_userbase, filter, attributes, ldap_userscope, KEEPALIVE, uuid_string);
290     }
291     if (ret != 1)
292         return -1;
293     return 0;
294 }
295
296 /*
297  * LDAP search wrapper
298  * returns allocated storage in name, caller must free it
299  * returns 0 on success, -1 on error or not found
300  * 
301  * @param uuidstr  (r) uuid to search as ascii string
302  * @param name     (w) return pointer to name as allocated string
303  * @param type     (w) return type: USER or GROUP
304  *
305  * returns 0 on success, -1 on errror
306  */
307 int ldap_getnamefromuuid( const char *uuidstr, char **name, uuidtype_t *type) {
308     int ret;
309     int len;
310     char filter[256];       /* this should really be enough. we dont want to malloc everything! */
311     char *attributes[]  = { NULL, NULL};
312
313     if (!ldap_config_valid)
314         return -1;
315
316     /* make filter */
317     len = snprintf( filter, 256, "%s=%s", ldap_uuid_attr, uuidstr);
318     if (len >= 256 || len == -1) {
319         LOG(log_error, logtype_default, "ldap_getnamefromuuid: filter overflow:%d, \"%s\"", len, filter);
320         return -1;
321     }
322     /* search groups first. group acls are probably used more often */
323     attributes[0] = ldap_group_attr;
324     ret = ldap_getattr_fromfilter_withbase_scope( ldap_groupbase, filter, attributes, ldap_groupscope, KEEPALIVE, name);
325     if (ret == -1)
326         return -1;
327     if (ret == 1) {
328         *type = UUID_GROUP;
329         return 0;
330     }
331
332     attributes[0] = ldap_name_attr;
333     ret = ldap_getattr_fromfilter_withbase_scope( ldap_userbase, filter, attributes, ldap_userscope, KEEPALIVE, name);
334     if (ret == 1) {
335         *type = UUID_USER;
336         return 0;
337     }
338
339     return -1;
340 }
341 #endif  /* HAVE_LDAP */