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