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