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),
107 "LDAP: Could not connect to server.");
112 "WARNING: populating an external LDAP address book is deprecated.\n"
113 "This function will be discontinued in a future release.\n"
114 "Please migrate to vCard-based address books as soon as possible.\n"
115 "Visit the Citadel support forum if you need further assistance.\n"
117 "Warning: LDAP address book is deprecated"
121 ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION,
124 CtdlLogPrintf(CTDL_INFO, "LDAP: Binding to %s\n", config.c_ldap_bind_dn);
126 i = ldap_simple_bind_s(dirserver,
127 config.c_ldap_bind_dn,
128 config.c_ldap_bind_pw);
129 if (i != LDAP_SUCCESS) {
130 CtdlLogPrintf(CTDL_CRIT, "LDAP: Cannot bind: %s (%d)\n",
131 ldap_err2string(i), i);
132 dirserver = NULL; /* FIXME disconnect from ldap */
133 CtdlAideMessage(ldap_err2string(i),
134 "LDAP: Cannot bind to server");
137 ldap_time_disconnect = 1;
144 * Create the root node. If it's already there, so what?
146 void create_ldap_root(void)
149 char *objectClass_values[3];
150 LDAPMod dc, objectClass;
155 /* We just want the top-level dc, not the whole hierarchy */
156 strcpy(topdc, config.c_ldap_base_dn);
157 for (i = 0; topdc[i]; ++i) {
158 if (topdc[i] == ',') {
163 for (i = 0; topdc[i]; ++i) {
165 strcpy(topdc, &topdc[i + 1]);
168 /* Set up the transaction */
169 dc.mod_op = LDAP_MOD_ADD;
171 dc_values[0] = topdc;
173 dc.mod_values = dc_values;
174 objectClass.mod_op = LDAP_MOD_ADD;
175 objectClass.mod_type = "objectClass";
176 objectClass_values[0] = "top";
177 objectClass_values[1] = "domain";
178 objectClass_values[2] = NULL;
179 objectClass.mod_values = objectClass_values;
181 mods[1] = &objectClass;
184 /* Perform the transaction */
185 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Setting up Base DN node...\n");
187 begin_critical_section(S_LDAP);
188 if (connect_to_ldap()) {
189 end_critical_section(S_LDAP);
192 i = ldap_add_s(dirserver, config.c_ldap_base_dn, mods);
193 end_critical_section(S_LDAP);
195 if (i == LDAP_ALREADY_EXISTS) {
196 CtdlLogPrintf(CTDL_INFO,
197 "LDAP: Base DN is already present in the directory; no need to add it again.\n");
198 } else if (i != LDAP_SUCCESS) {
199 CtdlLogPrintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
200 ldap_err2string(i), i);
206 * Create an OU node representing a Citadel host.
207 * parameter cn is not used, its just there to keep the hook interface consistant
208 * parameter object not used here, present for interface compatability
210 int create_ldap_host_OU(char *cn, char *host, void **object)
213 char *objectClass_values[3];
214 LDAPMod dc, objectClass;
219 /* The DN is this OU plus the base. */
220 snprintf(dn, sizeof dn, "ou=%s,%s", host, config.c_ldap_base_dn);
222 /* Set up the transaction */
223 dc.mod_op = LDAP_MOD_ADD;
227 dc.mod_values = dc_values;
228 objectClass.mod_op = LDAP_MOD_ADD;
229 objectClass.mod_type = "objectClass";
230 objectClass_values[0] = "top";
231 objectClass_values[1] = "organizationalUnit";
232 objectClass_values[2] = NULL;
233 objectClass.mod_values = objectClass_values;
235 mods[1] = &objectClass;
238 /* Perform the transaction */
239 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Setting up Host OU node...\n");
241 begin_critical_section(S_LDAP);
242 if (connect_to_ldap()) {
243 end_critical_section(S_LDAP);
246 i = ldap_add_s(dirserver, dn, mods);
247 end_critical_section(S_LDAP);
249 if (i == LDAP_ALREADY_EXISTS) {
250 CtdlLogPrintf(CTDL_INFO,
251 "LDAP: Host OU is already present in the directory; no need to add it again.\n");
252 } else if (i != LDAP_SUCCESS) {
253 CtdlLogPrintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
254 ldap_err2string(i), i);
266 * Create a base LDAP object for the interface
269 int create_ldap_object(char *cn, char *ou, void **object)
271 // We do nothing here, this just gets the base structure created by the interface.
272 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Created ldap object\n");
278 * Add an attribute to the ldap object
281 int add_ldap_object(char *cn, char *ou, void **object)
289 CtdlLogPrintf(CTDL_DEBUG,
290 "LDAP: Adding ldap attribute name:\"%s\" value:\"%s\"\n",
295 while (attrs[num_attrs])
299 for (cur_attr = 0; cur_attr < num_attrs; cur_attr++) {
300 if (!strcmp(attrs[cur_attr]->mod_type, cn)) { // Adding a value to the attribute
301 if (attrs[cur_attr]->mod_values) {
302 while (attrs[cur_attr]->
303 mod_values[num_values]) {
307 mod_values[num_values])) {
308 CtdlLogPrintf(CTDL_DEBUG,
309 "LDAP: Ignoring duplicate attribute/value pair\n");
315 attrs[cur_attr]->mod_values =
316 realloc(attrs[cur_attr]->mod_values,
317 (num_values + 2) * (sizeof(char *)));
318 attrs[cur_attr]->mod_values[num_values] =
320 attrs[cur_attr]->mod_values[num_values + 1] = NULL;
326 realloc(attrs, (sizeof(LDAPMod *)) * (num_attrs + 2));
328 attrs = malloc((sizeof(LDAPMod *)) * (num_attrs + 2));
329 attrs[num_attrs] = malloc(sizeof(LDAPMod));
330 memset(attrs[num_attrs], 0, sizeof(LDAPMod));
331 attrs[num_attrs + 1] = NULL;
332 attrs[num_attrs]->mod_op = LDAP_MOD_ADD;
333 attrs[num_attrs]->mod_type = strdup(cn);
334 attrs[num_attrs]->mod_values = malloc(2 * sizeof(char *));
335 attrs[num_attrs]->mod_values[0] = strdup(ou);
336 attrs[num_attrs]->mod_values[1] = NULL;
343 * SAve the object to the LDAP server
345 int save_ldap_object(char *cn, char *ou, void **object)
354 if (dirserver == NULL)
359 sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
361 CtdlLogPrintf(CTDL_INFO, "LDAP: Calling ldap_add_s() for dn of '%s'\n",
364 /* The last attribute must be a NULL one. */
365 attrs = (LDAPMod **) * object;
367 while (attrs[num_attrs]) {
369 while (attrs[num_attrs]->mod_values[count]) {
370 CtdlLogPrintf(CTDL_DEBUG,
371 "LDAP: attribute %d, value %d = \'%s=%s\'\n",
373 attrs[num_attrs]->mod_type,
381 CtdlLogPrintf(CTDL_ERR,
382 "LDAP: no attributes in save_ldap_object\n");
386 begin_critical_section(S_LDAP);
387 if (connect_to_ldap()) {
388 end_critical_section(S_LDAP);
392 i = ldap_add_s(dirserver, this_dn, attrs);
394 if (i == LDAP_SERVER_DOWN) {
396 ("The LDAP server appears to be down.\nThe save to LDAP did not occurr.\n",
397 "LDAP: save failed");
398 end_critical_section(S_LDAP);
402 /* If the entry already exists, repopulate it instead */
403 /* repopulating doesn't work as Citadel may want some attributes to be deleted.
404 * we have no way of knowing which attributes to delete and LDAP won't work it out for us
405 * so now we delete the old entry and create a new one.
407 if (i == LDAP_ALREADY_EXISTS) {
408 end_critical_section(S_LDAP);
409 CtdlLogPrintf(CTDL_INFO,
410 "LDAP: Create, already exists, deleteing first.\n");
411 if (delete_from_ldap(cn, ou, NULL))
413 begin_critical_section(S_LDAP);
414 CtdlLogPrintf(CTDL_INFO,
415 "LDAP: Calling ldap_add_s() to recreate for dn of '%s'\n",
417 i = ldap_add_s(dirserver, this_dn, attrs);
420 if (i != LDAP_SUCCESS) {
421 CtdlLogPrintf(CTDL_ERR, "LDAP: ldap_add_s() failed: %s (%d)\n",
422 ldap_err2string(i), i);
424 ("The LDAP server refused the save command.\nDid you update the schema?\n",
425 "LDAP: save failed (schema?)");
426 end_critical_section(S_LDAP);
429 end_critical_section(S_LDAP);
437 int free_ldap_object(char *cn, char *ou, void **object)
444 attrs = (LDAPMod **) * object;
446 while (attrs[num_attrs])
450 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Freeing attributes\n");
451 /* Free the attributes */
452 for (i = 0; i < num_attrs; ++i) {
453 if (attrs[i] != NULL) {
455 /* First, free the value strings */
456 if (attrs[i]->mod_values != NULL) {
458 attrs[i]->mod_values[j] != NULL;
460 free(attrs[i]->mod_values[j]);
464 /* Free the value strings pointer list */
465 if (attrs[i]->mod_values != NULL) {
466 free(attrs[i]->mod_values);
469 /* Now free the LDAPMod struct itself. */
481 * Delete a record from the LDAP
483 * parameter object not used here, present for hook interface compatability
485 int delete_from_ldap(char *cn, char *ou, void **object)
491 if (dirserver == NULL)
496 sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
498 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Calling ldap_delete_s()\n");
500 begin_critical_section(S_LDAP);
501 if (connect_to_ldap()) {
502 end_critical_section(S_LDAP);
506 i = ldap_delete_s(dirserver, this_dn);
508 if (i == LDAP_SERVER_DOWN) {
509 end_critical_section(S_LDAP);
511 ("The LDAP server appears to be down.\nThe delete from LDAP did not occurr.\n",
512 "LDAP: delete failed");
516 if (i != LDAP_SUCCESS) {
517 CtdlLogPrintf(CTDL_ERR,
518 "LDAP: ldap_delete_s() failed: %s (%d)\n",
519 ldap_err2string(i), i);
520 end_critical_section(S_LDAP);
521 CtdlAideMessage(ldap_err2string(i), "LDAP: delete failed");
524 end_critical_section(S_LDAP);
531 void ldap_disconnect_timer(void)
533 begin_critical_section(S_LDAP);
534 if (ldap_time_disconnect) {
535 ldap_time_disconnect--;
536 end_critical_section(S_LDAP);
540 end_critical_section(S_LDAP);
544 #endif /* HAVE_LDAP */
548 * Initialize the LDAP connector module ... or don't, if we don't have LDAP.
550 CTDL_MODULE_INIT(ldap)
555 if (!IsEmptyStr(config.c_ldap_base_dn)) {
556 CtdlRegisterCleanupHook(serv_ldap_cleanup);
557 CtdlRegisterSessionHook(ldap_disconnect_timer, EVT_TIMER);
558 CtdlRegisterDirectoryServiceFunc(delete_from_ldap,
561 CtdlRegisterDirectoryServiceFunc(create_ldap_host_OU,
562 DIRECTORY_CREATE_HOST,
564 CtdlRegisterDirectoryServiceFunc(create_ldap_object,
565 DIRECTORY_CREATE_OBJECT,
567 CtdlRegisterDirectoryServiceFunc(add_ldap_object,
568 DIRECTORY_ATTRIB_ADD,
570 CtdlRegisterDirectoryServiceFunc(save_ldap_object,
571 DIRECTORY_SAVE_OBJECT,
573 CtdlRegisterDirectoryServiceFunc(free_ldap_object,
574 DIRECTORY_FREE_OBJECT,
578 #endif /* HAVE_LDAP */
581 /* return our Subversion id for the Log */