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>
34 #include "citserver.h"
40 #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;
58 * LDAP connector cleanup function
60 void serv_ldap_cleanup(void)
63 CtdlLogPrintf(CTDL_INFO,
64 "LDAP: Unbinding from directory server\n");
65 ldap_unbind(dirserver);
68 ldap_time_disconnect = 0;
76 * Make sure this function is only called from within a begin_critical_section(S_LDAP)
77 * If you don't things will break!!!!!.
81 int connect_to_ldap(void)
86 if (dirserver) { // Already connected
87 ldap_time_disconnect = 1 ; // reset the timer.
91 CtdlLogPrintf(CTDL_INFO, "LDAP: Connecting to LDAP server %s:%d...\n",
92 config.c_ldap_host, config.c_ldap_port);
94 dirserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
95 if (dirserver == NULL) {
96 CtdlLogPrintf(CTDL_CRIT,
97 "LDAP: Could not connect to %s:%d : %s\n",
98 config.c_ldap_host, config.c_ldap_port,
100 CtdlAideMessage(strerror(errno),
101 "LDAP: Could not connect to server.");
105 ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION,
108 CtdlLogPrintf(CTDL_INFO, "LDAP: Binding to %s\n", config.c_ldap_bind_dn);
110 i = ldap_simple_bind_s(dirserver,
111 config.c_ldap_bind_dn,
112 config.c_ldap_bind_pw);
113 if (i != LDAP_SUCCESS) {
114 CtdlLogPrintf(CTDL_CRIT, "LDAP: Cannot bind: %s (%d)\n",
115 ldap_err2string(i), i);
116 dirserver = NULL; /* FIXME disconnect from ldap */
117 CtdlAideMessage(ldap_err2string(i),
118 "LDAP: Cannot bind to server");
121 ldap_time_disconnect = 1;
128 * Create the root node. If it's already there, so what?
130 void create_ldap_root(void)
133 char *objectClass_values[3];
134 LDAPMod dc, objectClass;
139 /* We just want the top-level dc, not the whole hierarchy */
140 strcpy(topdc, config.c_ldap_base_dn);
141 for (i = 0; topdc[i]; ++i) {
142 if (topdc[i] == ',') {
147 for (i = 0; topdc[i]; ++i) {
149 strcpy(topdc, &topdc[i + 1]);
152 /* Set up the transaction */
153 dc.mod_op = LDAP_MOD_ADD;
155 dc_values[0] = topdc;
157 dc.mod_values = dc_values;
158 objectClass.mod_op = LDAP_MOD_ADD;
159 objectClass.mod_type = "objectClass";
160 objectClass_values[0] = "top";
161 objectClass_values[1] = "domain";
162 objectClass_values[2] = NULL;
163 objectClass.mod_values = objectClass_values;
165 mods[1] = &objectClass;
168 /* Perform the transaction */
169 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Setting up Base DN node...\n");
171 begin_critical_section(S_LDAP);
172 if (connect_to_ldap()) {
173 end_critical_section(S_LDAP);
176 i = ldap_add_s(dirserver, config.c_ldap_base_dn, mods);
177 end_critical_section(S_LDAP);
179 if (i == LDAP_ALREADY_EXISTS) {
180 CtdlLogPrintf(CTDL_INFO,
181 "LDAP: Base DN is already present in the directory; no need to add it again.\n");
182 } else if (i != LDAP_SUCCESS) {
183 CtdlLogPrintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
184 ldap_err2string(i), i);
190 * Create an OU node representing a Citadel host.
191 * parameter cn is not used, its just there to keep the hook interface consistant
192 * parameter object not used here, present for interface compatability
194 int create_ldap_host_OU(char *cn, char *host, void **object)
197 char *objectClass_values[3];
198 LDAPMod dc, objectClass;
203 /* The DN is this OU plus the base. */
204 snprintf(dn, sizeof dn, "ou=%s,%s", host, config.c_ldap_base_dn);
206 /* Set up the transaction */
207 dc.mod_op = LDAP_MOD_ADD;
211 dc.mod_values = dc_values;
212 objectClass.mod_op = LDAP_MOD_ADD;
213 objectClass.mod_type = "objectClass";
214 objectClass_values[0] = "top";
215 objectClass_values[1] = "organizationalUnit";
216 objectClass_values[2] = NULL;
217 objectClass.mod_values = objectClass_values;
219 mods[1] = &objectClass;
222 /* Perform the transaction */
223 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Setting up Host OU node...\n");
225 begin_critical_section(S_LDAP);
226 if (connect_to_ldap()) {
227 end_critical_section(S_LDAP);
230 i = ldap_add_s(dirserver, dn, mods);
231 end_critical_section(S_LDAP);
233 if (i == LDAP_ALREADY_EXISTS) {
234 CtdlLogPrintf(CTDL_INFO,
235 "LDAP: Host OU is already present in the directory; no need to add it again.\n");
236 } else if (i != LDAP_SUCCESS) {
237 CtdlLogPrintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
238 ldap_err2string(i), i);
250 * Create a base LDAP object for the interface
253 int create_ldap_object(char *cn, char *ou, void **object)
255 // We do nothing here, this just gets the base structure created by the interface.
256 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Created ldap object\n");
262 * Add an attribute to the ldap object
265 int add_ldap_object(char *cn, char *ou, void **object)
273 CtdlLogPrintf(CTDL_DEBUG,
274 "LDAP: Adding ldap attribute name:\"%s\" value:\"%s\"\n",
279 while (attrs[num_attrs])
283 for (cur_attr = 0; cur_attr < num_attrs; cur_attr++) {
284 if (!strcmp(attrs[cur_attr]->mod_type, cn)) { // Adding a value to the attribute
285 if (attrs[cur_attr]->mod_values) {
286 while (attrs[cur_attr]->
287 mod_values[num_values]) {
291 mod_values[num_values])) {
292 CtdlLogPrintf(CTDL_DEBUG,
293 "LDAP: Ignoring duplicate attribute/value pair\n");
299 attrs[cur_attr]->mod_values =
300 realloc(attrs[cur_attr]->mod_values,
301 (num_values + 2) * (sizeof(char *)));
302 attrs[cur_attr]->mod_values[num_values] =
304 attrs[cur_attr]->mod_values[num_values + 1] = NULL;
310 realloc(attrs, (sizeof(LDAPMod *)) * (num_attrs + 2));
312 attrs = malloc((sizeof(LDAPMod *)) * (num_attrs + 2));
313 attrs[num_attrs] = malloc(sizeof(LDAPMod));
314 memset(attrs[num_attrs], 0, sizeof(LDAPMod));
315 attrs[num_attrs + 1] = NULL;
316 attrs[num_attrs]->mod_op = LDAP_MOD_ADD;
317 attrs[num_attrs]->mod_type = strdup(cn);
318 attrs[num_attrs]->mod_values = malloc(2 * sizeof(char *));
319 attrs[num_attrs]->mod_values[0] = strdup(ou);
320 attrs[num_attrs]->mod_values[1] = NULL;
327 * SAve the object to the LDAP server
329 int save_ldap_object(char *cn, char *ou, void **object)
338 if (dirserver == NULL)
343 sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
345 CtdlLogPrintf(CTDL_INFO, "LDAP: Calling ldap_add_s() for dn of '%s'\n",
348 /* The last attribute must be a NULL one. */
349 attrs = (LDAPMod **) * object;
351 while (attrs[num_attrs]) {
353 while (attrs[num_attrs]->mod_values[count]) {
354 CtdlLogPrintf(CTDL_DEBUG,
355 "LDAP: attribute %d, value %d = \'%s=%s\'\n",
357 attrs[num_attrs]->mod_type,
365 CtdlLogPrintf(CTDL_ERR,
366 "LDAP: no attributes in save_ldap_object\n");
370 begin_critical_section(S_LDAP);
371 if (connect_to_ldap()) {
372 end_critical_section(S_LDAP);
376 i = ldap_add_s(dirserver, this_dn, attrs);
378 if (i == LDAP_SERVER_DOWN) {
380 ("The LDAP server appears to be down.\nThe save to LDAP did not occurr.\n",
381 "LDAP: save failed");
382 end_critical_section(S_LDAP);
386 /* If the entry already exists, repopulate it instead */
387 if (i == LDAP_ALREADY_EXISTS) {
388 for (j = 0; j < (num_attrs); ++j) {
389 attrs[j]->mod_op = LDAP_MOD_REPLACE;
391 CtdlLogPrintf(CTDL_INFO,
392 "LDAP: Calling ldap_modify_s() for dn of '%s'\n",
394 i = ldap_modify_s(dirserver, this_dn, attrs);
397 if (i != LDAP_SUCCESS) {
398 CtdlLogPrintf(CTDL_ERR, "LDAP: ldap_add_s() failed: %s (%d)\n",
399 ldap_err2string(i), i);
401 ("The LDAP server refused the save command.\nDid you update the schema?\n",
402 "LDAP: save failed (schema?)");
403 end_critical_section(S_LDAP);
406 end_critical_section(S_LDAP);
414 int free_ldap_object(char *cn, char *ou, void **object)
421 attrs = (LDAPMod **) * object;
423 while (attrs[num_attrs])
427 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Freeing attributes\n");
428 /* Free the attributes */
429 for (i = 0; i < num_attrs; ++i) {
430 if (attrs[i] != NULL) {
432 /* First, free the value strings */
433 if (attrs[i]->mod_values != NULL) {
435 attrs[i]->mod_values[j] != NULL;
437 free(attrs[i]->mod_values[j]);
441 /* Free the value strings pointer list */
442 if (attrs[i]->mod_values != NULL) {
443 free(attrs[i]->mod_values);
446 /* Now free the LDAPMod struct itself. */
458 * Delete a record from the LDAP
460 * parameter object not used here, present for hook interface compatability
462 int delete_from_ldap(char *cn, char *ou, void **object)
468 if (dirserver == NULL)
475 sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
477 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Calling ldap_delete_s()\n");
479 begin_critical_section(S_LDAP);
480 if (connect_to_ldap()) {
481 end_critical_section(S_LDAP);
485 i = ldap_delete_s(dirserver, this_dn);
487 if (i == LDAP_SERVER_DOWN) {
488 end_critical_section(S_LDAP);
490 ("The LDAP server appears to be down.\nThe delete from LDAP did not occurr.\n",
491 "LDAP: delete failed");
495 if (i != LDAP_SUCCESS) {
496 CtdlLogPrintf(CTDL_ERR,
497 "LDAP: ldap_delete_s() failed: %s (%d)\n",
498 ldap_err2string(i), i);
499 end_critical_section(S_LDAP);
500 CtdlAideMessage(ldap_err2string(i), "LDAP: delete failed");
503 end_critical_section(S_LDAP);
510 void ldap_disconnect_timer(void)
512 begin_critical_section(S_LDAP);
513 if (ldap_time_disconnect) {
514 ldap_time_disconnect--;
515 end_critical_section(S_LDAP);
519 end_critical_section(S_LDAP);
523 #endif /* HAVE_LDAP */
527 * Initialize the LDAP connector module ... or don't, if we don't have LDAP.
529 CTDL_MODULE_INIT(ldap)
532 if (!IsEmptyStr(config.c_ldap_base_dn)) {
533 CtdlRegisterCleanupHook(serv_ldap_cleanup);
534 CtdlRegisterSessionHook(ldap_disconnect_timer, EVT_TIMER);
535 CtdlRegisterDirectoryServiceFunc(delete_from_ldap,
538 CtdlRegisterDirectoryServiceFunc(create_ldap_host_OU,
539 DIRECTORY_CREATE_HOST,
541 CtdlRegisterDirectoryServiceFunc(create_ldap_object,
542 DIRECTORY_CREATE_OBJECT,
544 CtdlRegisterDirectoryServiceFunc(add_ldap_object,
545 DIRECTORY_ATTRIB_ADD,
547 CtdlRegisterDirectoryServiceFunc(save_ldap_object,
548 DIRECTORY_SAVE_OBJECT,
550 CtdlRegisterDirectoryServiceFunc(free_ldap_object,
551 DIRECTORY_FREE_OBJECT,
555 #endif /* HAVE_LDAP */
557 /* return our Subversion id for the Log */