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_uuid_string; 53char *ldap_name_attr; 54char *ldap_group_attr; 55char *ldap_uid_attr; 56 57struct 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 74struct 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 */ 105static 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 */ 128retry: 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 220cleanup: 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 */ 265int 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 */ 307int 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 */ 342