]> arthur.barton.de Git - netatalk.git/blob - libatalk/acl/ldap.c
a78f2a5fc50b49e7817c9076b51bd3cdb8fcebf9
[netatalk.git] / libatalk / acl / ldap.c
1 /*
2    $Id: ldap.c,v 1.1 2009-02-02 11:55:01 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 etc/afpd/acl_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 char *ldap_groupbase;
47 char *ldap_uuid_attr;
48 char *ldap_name_attr;
49 char *ldap_group_attr;
50 char *ldap_uid_attr;
51
52 struct ldap_pref ldap_prefs[] = {
53     {&ldap_server,     "ldap_server",      0, 0, -1},
54     {&ldap_auth_method,"ldap_auth_method", 1, 1, -1},
55     {&ldap_auth_dn,    "ldap_auth_dn",     0, 0,  0},
56     {&ldap_auth_pw,    "ldap_auth_pw",     0, 0,  0},
57     {&ldap_userbase,   "ldap_userbase",    0, 0, -1},
58     {&ldap_groupbase,  "ldap_groupbase",   0, 0, -1},
59     {&ldap_uuid_attr,  "ldap_uuid_attr",   0, 0, -1},
60     {&ldap_name_attr,  "ldap_name_attr",   0, 0, -1},
61     {&ldap_group_attr, "ldap_group_attr",  0, 0, -1},
62     {&ldap_uid_attr,   "ldap_uid_attr",    0, 0,  0},
63     {NULL,             NULL,               0, 0, -1}
64 };
65
66 struct pref_array prefs_array[] = {
67     {"ldap_auth_method", "none",   LDAP_AUTH_NONE},
68     {"ldap_auth_method", "simple", LDAP_AUTH_SIMPLE},
69     {"ldap_auth_method", "sasl",   LDAP_AUTH_SASL},
70     {NULL,               NULL,     0}
71 };
72
73 /********************************************************
74  * Static helper function
75  ********************************************************/
76
77 /* 
78  * ldap_getattr_fromfilter_withbase_scope():
79  *   conflags: KEEPALIVE
80  *   scope: LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
81  *   result: return unique search result here, allocated here, caller must free
82  *
83  * All connection managment to the LDAP server is done here. Just set KEEPALIVE if you know
84  * you will be dispatching more than one search in a row, then don't set it with the last search.
85  * You MUST dispatch the queries timely, otherwise the LDAP handle might timeout.
86  */
87 static int ldap_getattr_fromfilter_withbase_scope( const char *searchbase, 
88                                                    const char *filter, 
89                                                    char *attributes[], 
90                                                    int scope,
91                                                    ldapcon_t conflags,
92                                                    char **result) {
93     int ret = 0;
94     int ldaperr;
95     int desired_version  = LDAP_VERSION3;
96     static int ldapconnected = 0;
97     static LDAP *ld     = NULL;
98     LDAPMessage* msg    = NULL;
99     LDAPMessage* entry  = NULL;
100
101     char **attribute_values;
102     struct timeval timeout;
103
104 //    LOG(log_debug, logtype_afpd,"ldap_getattr_fromfilter_withbase_scope: BEGIN");
105
106     timeout.tv_sec = 3;
107     timeout.tv_usec = 0;
108
109     /* init LDAP if necessary */
110     if (!ldapconnected) {
111 //      LOG(log_debug, logtype_default, "ldap_getattr_fromfilter_withbase_scope: LDAP server: \'%s\'", ldap_server);
112         if ((ld = ldap_init(ldap_server, LDAP_PORT)) == NULL ) {
113             LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_init error");
114             return -1;
115         }
116         if (ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &desired_version) != 0) {
117             /* LDAP_OPT_SUCCESS is not in the proposed standard, so we check for 0
118                http://tools.ietf.org/id/draft-ietf-ldapext-ldap-c-api-05.txt */
119             LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_set_option failed!");
120             ret = -1;
121             goto cleanup;
122         }
123     }
124
125     /* connect */
126     if (!ldapconnected) {
127         if (LDAP_AUTH_NONE == ldap_auth_method) {
128             if (ldap_bind_s(ld, "", "", LDAP_AUTH_SIMPLE) != LDAP_SUCCESS ) {
129                 LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_bind failed!");
130                 LOG(log_error, logtype_default, "ldap_auth_method: \'%d\'", ldap_auth_method);
131                 return -1;
132             }
133             ldapconnected = 1;
134
135         } else if (LDAP_AUTH_SIMPLE == ldap_auth_method) {
136             if (ldap_bind_s(ld, ldap_auth_dn, ldap_auth_pw, ldap_auth_method) != LDAP_SUCCESS ) {
137                 LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_bind failed!");
138                 LOG(log_error, logtype_default, "ldap_auth_dn: \'%s\', ldap_auth_pw: \'%s\', ldap_auth_method: \'%d\'",
139                     ldap_auth_dn, ldap_auth_pw, ldap_auth_method);
140                 return -1;
141             }
142             ldapconnected = 1;
143         }
144     }
145
146 //    LOG(log_debug, logtype_afpd,"LDAP start search: base: %s, filter: %s, attr: %s", searchbase, filter, attributes[0]);
147
148     /* start LDAP search */
149     ldaperr = ldap_search_st(ld, searchbase, scope, filter, attributes, 0, &timeout, &msg);
150     if (ldaperr != LDAP_SUCCESS) {
151         LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: ldap_search_st failed: %s", ldap_err2string(ldaperr));
152         ret = -1;
153         goto cleanup;
154     }
155
156     /* parse search result */
157 //    LOG(log_debug, logtype_default, "ldap_getuuidfromname: got %d entries from ldap search", ldap_count_entries(ld, msg));
158     if (ldap_count_entries(ld, msg) != 1) {
159         ret = -1;
160         goto cleanup;
161     }
162     entry = ldap_first_entry(ld, msg);
163     if (entry == NULL) {
164         LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: error in ldap_first_entry");
165         ret = -1;
166         goto cleanup;
167     }
168     attribute_values = ldap_get_values(ld, entry, attributes[0]);
169     if (attribute_values == NULL) {
170         LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: error in ldap_get_values");
171         ret = -1;
172         goto cleanup;
173     }
174
175 //    LOG(log_debug, logtype_afpd,"LDAP Search result: %s: %s", attributes[0], attribute_values[0]);
176
177     /* allocate place for uuid as string */
178     *result = calloc( 1, strlen(attribute_values[0]) + 1);
179     if (*result == NULL) {
180         LOG(log_error, logtype_default, "ldap_getattr_fromfilter_withbase_scope: %s: error calloc'ing",strerror(errno));
181         ret = -1;
182         goto cleanup;
183     }
184     /* get value */
185     strcpy( *result, attribute_values[0]);
186     ldap_value_free(attribute_values);
187
188     /* FIXME: is there another way to free entry ? */
189     while (entry != NULL)
190         entry = ldap_next_entry(ld, entry);
191
192 cleanup:
193     if(msg)
194         ldap_msgfree(msg);
195     if(ld) {
196         if ((ldapconnected && !(conflags & KEEPALIVE)) || (*result != NULL)) {
197             ldapconnected = 0;  /* regardless of unbind errors */
198 //          LOG(log_debug, logtype_default,"LDAP unbind!");
199             if (ldap_unbind_s(ld) != 0) {
200                 LOG(log_error, logtype_default, "ldap_unbind_s: %s\n", ldap_err2string(ldaperr));
201                 return -1;
202             }
203         }
204     }
205     return ret;
206 }
207
208 /******************************************************** 
209  * Interface
210  ********************************************************/
211
212 int ldap_getuuidfromname( const char *name, uuidtype_t type, char **uuid_string) {
213     int ret;
214     int len;
215     char filter[256];           /* this should really be enough. we dont want to malloc everything! */
216     char *attributes[]  = { ldap_uuid_attr, NULL};
217     char *ldap_attr;
218
219     /* make filter */
220     if (type == UUID_GROUP)
221         ldap_attr = ldap_group_attr;
222     else /* type hopefully == UUID_USER */
223         ldap_attr = ldap_name_attr;
224     len = snprintf( filter, 256, "%s=%s", ldap_attr, name);
225     if (len >= 256 || len == -1) {
226         LOG(log_error, logtype_default, "ldap_getnamefromuuid: filter error:%d, \"%s\"", len, filter);
227         return -1;
228     }    
229
230
231     if (type == UUID_GROUP) {
232         ret = ldap_getattr_fromfilter_withbase_scope( ldap_groupbase, filter, attributes, LDAP_SCOPE_ONELEVEL, KEEPALIVE, uuid_string);
233     } else  { /* type hopefully == UUID_USER */
234         ret = ldap_getattr_fromfilter_withbase_scope( ldap_userbase, filter, attributes, LDAP_SCOPE_ONELEVEL, 0, uuid_string);
235     }
236     return ret;
237 }
238
239 int ldap_getnamefromuuid( char *uuidstr, char **name, uuidtype_t *type) {
240     int ret;
241     int len;
242     char filter[256];           /* this should really be enough. we dont want to malloc everything! */
243     char *attributes[]  = { NULL, NULL};
244
245     /* make filter */
246     len = snprintf( filter, 256, "%s=%s", ldap_uuid_attr, uuidstr);
247     if (len >= 256 || len == -1) {
248         LOG(log_error, logtype_default, "ldap_getnamefromuuid: filter overflow:%d, \"%s\"", len, filter);
249         return -1;
250     }    
251     /* search groups first. group acls are probably used more often */
252     attributes[0] = ldap_group_attr;
253     ret = ldap_getattr_fromfilter_withbase_scope( ldap_groupbase, filter, attributes, LDAP_SCOPE_ONELEVEL, KEEPALIVE, name);
254     if (ret == 0) {
255         *type = UUID_GROUP;
256         return 0;
257     }
258     attributes[0] = ldap_name_attr;
259     ret = ldap_getattr_fromfilter_withbase_scope( ldap_userbase, filter, attributes, LDAP_SCOPE_ONELEVEL, 0, name);
260     if (ret == 0) {
261         *type = UUID_USER;
262         return 0;
263     }
264
265     return ret;
266 }
267