]> arthur.barton.de Git - netatalk.git/blob - libatalk/acl/ldap.c
Merge dircache-rewrite
[netatalk.git] / libatalk / acl / ldap.c
1 /*
2   $Id: ldap.c,v 1.7 2010-04-23 11:37:06 franklahm Exp $
3   Copyright (c) 2008,2009 Frank Lahm <franklahm@gmail.com>
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 */
15
16 #ifdef HAVE_CONFIG_H
17 #include "config.h"
18 #endif /* HAVE_CONFIG_H */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <sys/time.h>
23 #include <string.h>
24 #include <errno.h>
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  * All connection managment to the LDAP server is done here. Just set KEEPALIVE if you know
94  * you will be dispatching more than one search in a row, then don't set it with the last search.
95  * You MUST dispatch the queries timely, otherwise the LDAP handle might timeout.
96  */
97 static int ldap_getattr_fromfilter_withbase_scope( const char *searchbase,
98                                                    const char *filter,
99                                                    char *attributes[],
100                                                    int scope,
101                                                    ldapcon_t conflags,
102                                                    char **result) {
103     int ret;
104     int ldaperr;
105     int retrycount = 0;
106     int desired_version  = LDAP_VERSION3;
107     static int ldapconnected = 0;
108     static LDAP *ld     = NULL;
109     LDAPMessage* msg    = NULL;
110     LDAPMessage* entry  = NULL;
111
112     char **attribute_values;
113     struct timeval timeout;
114
115     LOG(log_maxdebug, logtype_afpd,"ldap_getattr_fromfilter_withbase_scope: BEGIN");
116
117     timeout.tv_sec = 3;
118     timeout.tv_usec = 0;
119
120     /* init LDAP if necessary */
121 retry:
122     ret = 0;
123
124     if (!ldapconnected) {
125         LOG(log_maxdebug, logtype_default, "ldap_getattr_fromfilter_withbase_scope: LDAP server: \"%s\"",
126             ldap_server);
127         if ((ld = ldap_init(ldap_server, LDAP_PORT)) == NULL ) {
128             LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_init error");
129             return -1;
130         }
131         if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &desired_version) != 0) {
132             /* LDAP_OPT_SUCCESS is not in the proposed standard, so we check for 0
133                http://tools.ietf.org/id/draft-ietf-ldapext-ldap-c-api-05.txt */
134             LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_set_option failed!");
135             ret = -1;
136             goto cleanup;
137         }
138     }
139
140     /* connect */
141     if (!ldapconnected) {
142         if (LDAP_AUTH_NONE == ldap_auth_method) {
143             if (ldap_bind_s(ld, "", "", LDAP_AUTH_SIMPLE) != LDAP_SUCCESS ) {
144                 LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_bind failed!");
145                 LOG(log_error, logtype_default, "ldap_auth_method: \'%d\'", ldap_auth_method);
146                 return -1;
147             }
148             ldapconnected = 1;
149
150         } else if (LDAP_AUTH_SIMPLE == ldap_auth_method) {
151             if (ldap_bind_s(ld, ldap_auth_dn, ldap_auth_pw, ldap_auth_method) != LDAP_SUCCESS ) {
152                 LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_bind failed!");
153                 LOG(log_error, logtype_default, "ldap_auth_dn: \'%s\', ldap_auth_pw: \'%s\', ldap_auth_method: \'%d\'",
154                     ldap_auth_dn, ldap_auth_pw, ldap_auth_method);
155                 return -1;
156             }
157             ldapconnected = 1;
158         }
159     }
160
161     LOG(log_maxdebug, logtype_afpd, "LDAP start search: base: %s, filter: %s, attr: %s",
162         searchbase, filter, attributes[0]);
163
164     /* start LDAP search */
165     ldaperr = ldap_search_st(ld, searchbase, scope, filter, attributes, 0, &timeout, &msg);
166     LOG(log_maxdebug, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_search_st returned: %s, %u",
167         ldap_err2string(ldaperr), ldaperr);
168     if (ldaperr != LDAP_SUCCESS) {
169         if (retrycount ==1)
170             LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_search_st failed: %s", ldap_err2string(ldaperr));
171         ret = -1;
172         goto cleanup;
173     }
174
175     /* parse search result */
176     LOG(log_maxdebug, logtype_default, "ldap_getuuidfromname: got %d entries from ldap search",
177         ldap_count_entries(ld, msg));
178     if (ldap_count_entries(ld, msg) != 1) {
179         ret = -1;
180         goto cleanup;
181     }
182
183     entry = ldap_first_entry(ld, msg);
184     if (entry == NULL) {
185         LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: error in ldap_first_entry");
186         ret = -1;
187         goto cleanup;
188     }
189     attribute_values = ldap_get_values(ld, entry, attributes[0]);
190     if (attribute_values == NULL) {
191         LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: error in ldap_get_values");
192         ret = -1;
193         goto cleanup;
194     }
195
196     LOG(log_maxdebug, logtype_afpd,"LDAP Search result: %s: %s",
197         attributes[0], attribute_values[0]);
198
199     /* allocate place for uuid as string */
200     *result = calloc( 1, strlen(attribute_values[0]) + 1);
201     if (*result == NULL) {
202         LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: %s: error calloc'ing",strerror(errno));
203         ret = -1;
204         goto cleanup;
205     }
206     /* get value */
207     strcpy( *result, attribute_values[0]);
208     ldap_value_free(attribute_values);
209
210     /* FIXME: is there another way to free entry ? */
211     while (entry != NULL)
212         entry = ldap_next_entry(ld, entry);
213
214 cleanup:
215     if (msg)
216         ldap_msgfree(msg);
217     if (ld) {
218         if (ldapconnected
219             && ( !(conflags & KEEPALIVE)
220                  ||
221                  ((ret == -1) && (ldaperr != LDAP_SUCCESS))) /* ie ldapsearch got 0 results */
222             ) {
223
224             ldapconnected = 0;  /* regardless of unbind errors */
225             LOG(log_maxdebug, logtype_default,"LDAP unbind");
226             if (ldap_unbind_s(ld) != 0) {
227                 LOG(log_error, logtype_default, "ldap_unbind_s: %s\n", ldap_err2string(ldaperr));
228                 return -1;
229             }
230             retrycount++;
231             if (retrycount < 2)
232                 goto retry;
233         }
234     }
235     return ret;
236 }
237
238 /********************************************************
239  * Interface
240  ********************************************************/
241
242 int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string) {
243     int ret;
244     int len;
245     char filter[256];           /* this should really be enough. we dont want to malloc everything! */
246     char *attributes[]  = { ldap_uuid_attr, NULL};
247     char *ldap_attr;
248
249     /* make filter */
250     if (type == UUID_GROUP)
251         ldap_attr = ldap_group_attr;
252     else /* type hopefully == UUID_USER */
253         ldap_attr = ldap_name_attr;
254     len = snprintf( filter, 256, "%s=%s", ldap_attr, name);
255     if (len >= 256 || len == -1) {
256         LOG(log_error, logtype_default, "ldap_getnamefromuuid: filter error:%d, \"%s\"", len, filter);
257         return -1;
258     }
259
260     if (type == UUID_GROUP) {
261         ret = ldap_getattr_fromfilter_withbase_scope( ldap_groupbase, filter, attributes, ldap_groupscope, KEEPALIVE, uuid_string);
262     } else  { /* type hopefully == UUID_USER */
263         ret = ldap_getattr_fromfilter_withbase_scope( ldap_userbase, filter, attributes, ldap_userscope, KEEPALIVE, uuid_string);
264     }
265     return ret;
266 }
267
268 int ldap_getnamefromuuid( char *uuidstr, char **name, uuidtype_t *type) {
269     int ret;
270     int len;
271     char filter[256];       /* this should really be enough. we dont want to malloc everything! */
272     char *attributes[]  = { NULL, NULL};
273
274     /* make filter */
275     len = snprintf( filter, 256, "%s=%s", ldap_uuid_attr, uuidstr);
276     if (len >= 256 || len == -1) {
277         LOG(log_error, logtype_default, "ldap_getnamefromuuid: filter overflow:%d, \"%s\"", len, filter);
278         return -1;
279     }
280     /* search groups first. group acls are probably used more often */
281     attributes[0] = ldap_group_attr;
282     ret = ldap_getattr_fromfilter_withbase_scope( ldap_groupbase, filter, attributes, ldap_groupscope, KEEPALIVE, name);
283     if (ret == 0) {
284         *type = UUID_GROUP;
285         return 0;
286     }
287     attributes[0] = ldap_name_attr;
288     ret = ldap_getattr_fromfilter_withbase_scope( ldap_userbase, filter, attributes, ldap_userscope, KEEPALIVE, name);
289     if (ret == 0) {
290         *type = UUID_USER;
291         return 0;
292     }
293
294     return ret;
295 }