4 * A module which implements the LDAP connector for Citadel.
16 #include <sys/types.h>
18 #if TIME_WITH_SYS_TIME
19 # include <sys/time.h>
23 # include <sys/time.h>
32 #include <libcitadel.h>
35 #include "citserver.h"
41 #include "serv_ldap.h"
44 #include "ctdl_module.h"
50 #define LDAP_DEPRECATED 1 /* to stop warnings with newer libraries */
54 LDAP *dirserver = NULL;
55 int ldap_time_disconnect = 0;
59 /* There is a forward referance so.... */
60 int delete_from_ldap(char *cn, char *ou, void **object);
64 * LDAP connector cleanup function
66 void serv_ldap_cleanup(void)
69 CtdlLogPrintf(CTDL_INFO,
70 "LDAP: Unbinding from directory server\n");
71 ldap_unbind(dirserver);
74 ldap_time_disconnect = 0;
82 * Make sure this function is only called from within a begin_critical_section(S_LDAP)
83 * If you don't things will break!!!!!.
87 int connect_to_ldap(void)
92 if (dirserver) { // Already connected
93 ldap_time_disconnect = 1 ; // reset the timer.
97 CtdlLogPrintf(CTDL_INFO, "LDAP: Connecting to LDAP server %s:%d...\n",
98 config.c_ldap_host, config.c_ldap_port);
100 dirserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
101 if (dirserver == NULL) {
102 CtdlLogPrintf(CTDL_CRIT,
103 "LDAP: Could not connect to %s:%d : %s\n",
104 config.c_ldap_host, config.c_ldap_port,
106 CtdlAideMessage(strerror(errno), "LDAP: Could not connect to server.");
110 ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
112 CtdlLogPrintf(CTDL_INFO, "LDAP: Binding to %s\n", config.c_ldap_bind_dn);
114 i = ldap_simple_bind_s(dirserver, config.c_ldap_bind_dn, config.c_ldap_bind_pw);
115 if (i == LDAP_SUCCESS) {
117 "WARNING: populating an external LDAP address book is deprecated.\n"
118 "This function will be discontinued in a future release.\n"
119 "Please migrate to vCard-based address books as soon as possible.\n"
120 "Visit the Citadel support forum if you need further assistance.\n"
122 "More information about this change is available at:\n"
123 "http://www.citadel.org/doku.php/faq:systemadmin:ldap_deprecated\n"
125 "Warning: LDAP address book is deprecated"
129 CtdlLogPrintf(CTDL_CRIT, "LDAP: Cannot bind: %s (%d)\n", ldap_err2string(i), i);
130 dirserver = NULL; /* FIXME disconnect from ldap */
131 CtdlAideMessage(ldap_err2string(i), "LDAP: Cannot bind to server");
134 ldap_time_disconnect = 1;
141 * Create the root node. If it's already there, so what?
143 void create_ldap_root(void)
146 char *objectClass_values[3];
147 LDAPMod dc, objectClass;
152 /* We just want the top-level dc, not the whole hierarchy */
153 strcpy(topdc, config.c_ldap_base_dn);
154 for (i = 0; topdc[i]; ++i) {
155 if (topdc[i] == ',') {
160 for (i = 0; topdc[i]; ++i) {
162 strcpy(topdc, &topdc[i + 1]);
165 /* Set up the transaction */
166 dc.mod_op = LDAP_MOD_ADD;
168 dc_values[0] = topdc;
170 dc.mod_values = dc_values;
171 objectClass.mod_op = LDAP_MOD_ADD;
172 objectClass.mod_type = "objectClass";
173 objectClass_values[0] = "top";
174 objectClass_values[1] = "domain";
175 objectClass_values[2] = NULL;
176 objectClass.mod_values = objectClass_values;
178 mods[1] = &objectClass;
181 /* Perform the transaction */
182 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Setting up Base DN node...\n");
184 begin_critical_section(S_LDAP);
185 if (connect_to_ldap()) {
186 end_critical_section(S_LDAP);
189 i = ldap_add_s(dirserver, config.c_ldap_base_dn, mods);
190 end_critical_section(S_LDAP);
192 if (i == LDAP_ALREADY_EXISTS) {
193 CtdlLogPrintf(CTDL_INFO,
194 "LDAP: Base DN is already present in the directory; no need to add it again.\n");
195 } else if (i != LDAP_SUCCESS) {
196 CtdlLogPrintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
197 ldap_err2string(i), i);
203 * Create an OU node representing a Citadel host.
204 * parameter cn is not used, its just there to keep the hook interface consistant
205 * parameter object not used here, present for interface compatability
207 int create_ldap_host_OU(char *cn, char *host, void **object)
210 char *objectClass_values[3];
211 LDAPMod dc, objectClass;
216 /* The DN is this OU plus the base. */
217 snprintf(dn, sizeof dn, "ou=%s,%s", host, config.c_ldap_base_dn);
219 /* Set up the transaction */
220 dc.mod_op = LDAP_MOD_ADD;
224 dc.mod_values = dc_values;
225 objectClass.mod_op = LDAP_MOD_ADD;
226 objectClass.mod_type = "objectClass";
227 objectClass_values[0] = "top";
228 objectClass_values[1] = "organizationalUnit";
229 objectClass_values[2] = NULL;
230 objectClass.mod_values = objectClass_values;
232 mods[1] = &objectClass;
235 /* Perform the transaction */
236 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Setting up Host OU node...\n");
238 begin_critical_section(S_LDAP);
239 if (connect_to_ldap()) {
240 end_critical_section(S_LDAP);
243 i = ldap_add_s(dirserver, dn, mods);
244 end_critical_section(S_LDAP);
246 if (i == LDAP_ALREADY_EXISTS) {
247 CtdlLogPrintf(CTDL_INFO,
248 "LDAP: Host OU is already present in the directory; no need to add it again.\n");
249 } else if (i != LDAP_SUCCESS) {
250 CtdlLogPrintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
251 ldap_err2string(i), i);
263 * Create a base LDAP object for the interface
266 int create_ldap_object(char *cn, char *ou, void **object)
268 // We do nothing here, this just gets the base structure created by the interface.
269 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Created ldap object\n");
275 * Add an attribute to the ldap object
278 int add_ldap_object(char *cn, char *ou, void **object)
286 CtdlLogPrintf(CTDL_DEBUG,
287 "LDAP: Adding ldap attribute name:\"%s\" value:\"%s\"\n",
292 while (attrs[num_attrs])
296 for (cur_attr = 0; cur_attr < num_attrs; cur_attr++) {
297 if (!strcmp(attrs[cur_attr]->mod_type, cn)) { // Adding a value to the attribute
298 if (attrs[cur_attr]->mod_values) {
299 while (attrs[cur_attr]->
300 mod_values[num_values]) {
304 mod_values[num_values])) {
305 CtdlLogPrintf(CTDL_DEBUG,
306 "LDAP: Ignoring duplicate attribute/value pair\n");
312 attrs[cur_attr]->mod_values =
313 realloc(attrs[cur_attr]->mod_values,
314 (num_values + 2) * (sizeof(char *)));
315 attrs[cur_attr]->mod_values[num_values] =
317 attrs[cur_attr]->mod_values[num_values + 1] = NULL;
323 realloc(attrs, (sizeof(LDAPMod *)) * (num_attrs + 2));
325 attrs = malloc((sizeof(LDAPMod *)) * (num_attrs + 2));
326 attrs[num_attrs] = malloc(sizeof(LDAPMod));
327 memset(attrs[num_attrs], 0, sizeof(LDAPMod));
328 attrs[num_attrs + 1] = NULL;
329 attrs[num_attrs]->mod_op = LDAP_MOD_ADD;
330 attrs[num_attrs]->mod_type = strdup(cn);
331 attrs[num_attrs]->mod_values = malloc(2 * sizeof(char *));
332 attrs[num_attrs]->mod_values[0] = strdup(ou);
333 attrs[num_attrs]->mod_values[1] = NULL;
340 * SAve the object to the LDAP server
342 int save_ldap_object(char *cn, char *ou, void **object)
351 if (dirserver == NULL)
356 sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
358 CtdlLogPrintf(CTDL_INFO, "LDAP: Calling ldap_add_s() for dn of '%s'\n",
361 /* The last attribute must be a NULL one. */
362 attrs = (LDAPMod **) * object;
364 while (attrs[num_attrs]) {
366 while (attrs[num_attrs]->mod_values[count]) {
367 CtdlLogPrintf(CTDL_DEBUG,
368 "LDAP: attribute %d, value %d = \'%s=%s\'\n",
370 attrs[num_attrs]->mod_type,
378 CtdlLogPrintf(CTDL_ERR,
379 "LDAP: no attributes in save_ldap_object\n");
383 begin_critical_section(S_LDAP);
384 if (connect_to_ldap()) {
385 end_critical_section(S_LDAP);
389 i = ldap_add_s(dirserver, this_dn, attrs);
391 if (i == LDAP_SERVER_DOWN) {
393 ("The LDAP server appears to be down.\nThe save to LDAP did not occurr.\n",
394 "LDAP: save failed");
395 end_critical_section(S_LDAP);
399 /* If the entry already exists, repopulate it instead */
400 /* repopulating doesn't work as Citadel may want some attributes to be deleted.
401 * we have no way of knowing which attributes to delete and LDAP won't work it out for us
402 * so now we delete the old entry and create a new one.
404 if (i == LDAP_ALREADY_EXISTS) {
405 end_critical_section(S_LDAP);
406 CtdlLogPrintf(CTDL_INFO,
407 "LDAP: Create, already exists, deleteing first.\n");
408 if (delete_from_ldap(cn, ou, NULL))
410 begin_critical_section(S_LDAP);
411 CtdlLogPrintf(CTDL_INFO,
412 "LDAP: Calling ldap_add_s() to recreate for dn of '%s'\n",
414 i = ldap_add_s(dirserver, this_dn, attrs);
417 if (i != LDAP_SUCCESS) {
418 CtdlLogPrintf(CTDL_ERR, "LDAP: ldap_add_s() failed: %s (%d)\n",
419 ldap_err2string(i), i);
421 ("The LDAP server refused the save command.\nDid you update the schema?\n",
422 "LDAP: save failed (schema?)");
423 end_critical_section(S_LDAP);
426 end_critical_section(S_LDAP);
434 int free_ldap_object(char *cn, char *ou, void **object)
441 attrs = (LDAPMod **) * object;
443 while (attrs[num_attrs])
447 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Freeing attributes\n");
448 /* Free the attributes */
449 for (i = 0; i < num_attrs; ++i) {
450 if (attrs[i] != NULL) {
452 /* First, free the value strings */
453 if (attrs[i]->mod_values != NULL) {
455 attrs[i]->mod_values[j] != NULL;
457 free(attrs[i]->mod_values[j]);
461 /* Free the value strings pointer list */
462 if (attrs[i]->mod_values != NULL) {
463 free(attrs[i]->mod_values);
466 /* Now free the LDAPMod struct itself. */
478 * Delete a record from the LDAP
480 * parameter object not used here, present for hook interface compatability
482 int delete_from_ldap(char *cn, char *ou, void **object)
488 if (dirserver == NULL)
493 sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
495 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Calling ldap_delete_s()\n");
497 begin_critical_section(S_LDAP);
498 if (connect_to_ldap()) {
499 end_critical_section(S_LDAP);
503 i = ldap_delete_s(dirserver, this_dn);
505 if (i == LDAP_SERVER_DOWN) {
506 end_critical_section(S_LDAP);
508 ("The LDAP server appears to be down.\nThe delete from LDAP did not occurr.\n",
509 "LDAP: delete failed");
513 if (i != LDAP_SUCCESS) {
514 CtdlLogPrintf(CTDL_ERR,
515 "LDAP: ldap_delete_s() failed: %s (%d)\n",
516 ldap_err2string(i), i);
517 end_critical_section(S_LDAP);
518 CtdlAideMessage(ldap_err2string(i), "LDAP: delete failed");
521 end_critical_section(S_LDAP);
528 void ldap_disconnect_timer(void)
530 begin_critical_section(S_LDAP);
531 if (ldap_time_disconnect) {
532 ldap_time_disconnect--;
533 end_critical_section(S_LDAP);
537 end_critical_section(S_LDAP);
541 #endif /* HAVE_LDAP */
545 * Initialize the LDAP connector module ... or don't, if we don't have LDAP.
547 CTDL_MODULE_INIT(ldap)
552 if (!IsEmptyStr(config.c_ldap_base_dn)) {
553 CtdlRegisterCleanupHook(serv_ldap_cleanup);
554 CtdlRegisterSessionHook(ldap_disconnect_timer, EVT_TIMER);
555 CtdlRegisterDirectoryServiceFunc(delete_from_ldap,
558 CtdlRegisterDirectoryServiceFunc(create_ldap_host_OU,
559 DIRECTORY_CREATE_HOST,
561 CtdlRegisterDirectoryServiceFunc(create_ldap_object,
562 DIRECTORY_CREATE_OBJECT,
564 CtdlRegisterDirectoryServiceFunc(add_ldap_object,
565 DIRECTORY_ATTRIB_ADD,
567 CtdlRegisterDirectoryServiceFunc(save_ldap_object,
568 DIRECTORY_SAVE_OBJECT,
570 CtdlRegisterDirectoryServiceFunc(free_ldap_object,
571 DIRECTORY_FREE_OBJECT,
575 #endif /* HAVE_LDAP */
578 /* return our Subversion id for the Log */