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"
41 #include "serv_ldap.h"
45 #include "ctdl_module.h"
51 #define LDAP_DEPRECATED 1 /* to stop warnings with newer libraries */
55 LDAP *dirserver = NULL;
56 int ldap_time_disconnect = 0;
60 * LDAP connector cleanup function
62 void serv_ldap_cleanup(void)
66 "LDAP: Unbinding from directory server\n");
67 ldap_unbind(dirserver);
70 ldap_time_disconnect = 0;
77 int connect_to_ldap(void)
82 if (ldap_time_disconnect && dirserver) { // Already connected
83 ldap_time_disconnect = 5; // reset the timer.
87 lprintf(CTDL_INFO, "LDAP: Connecting to LDAP server %s:%d...\n",
88 config.c_ldap_host, config.c_ldap_port);
90 dirserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
91 if (dirserver == NULL) {
93 "LDAP: Could not connect to %s:%d : %s\n",
94 config.c_ldap_host, config.c_ldap_port,
96 aide_message(strerror(errno),
97 "LDAP: Could not connect to server.");
101 ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION,
104 lprintf(CTDL_INFO, "LDAP: Binding to %s\n", config.c_ldap_bind_dn);
106 i = ldap_simple_bind_s(dirserver,
107 config.c_ldap_bind_dn,
108 config.c_ldap_bind_pw);
109 if (i != LDAP_SUCCESS) {
110 lprintf(CTDL_CRIT, "LDAP: Cannot bind: %s (%d)\n",
111 ldap_err2string(i), i);
112 dirserver = NULL; /* FIXME disconnect from ldap */
113 aide_message(ldap_err2string(i),
114 "LDAP: Cannot bind to server");
117 ldap_time_disconnect = 5;
124 * Create the root node. If it's already there, so what?
126 void create_ldap_root(void)
129 char *objectClass_values[3];
130 LDAPMod dc, objectClass;
135 /* We just want the top-level dc, not the whole hierarchy */
136 strcpy(topdc, config.c_ldap_base_dn);
137 for (i = 0; topdc[i]; ++i) {
138 if (topdc[i] == ',') {
143 for (i = 0; topdc[i]; ++i) {
145 strcpy(topdc, &topdc[i + 1]);
148 /* Set up the transaction */
149 dc.mod_op = LDAP_MOD_ADD;
151 dc_values[0] = topdc;
153 dc.mod_values = dc_values;
154 objectClass.mod_op = LDAP_MOD_ADD;
155 objectClass.mod_type = "objectClass";
156 objectClass_values[0] = "top";
157 objectClass_values[1] = "domain";
158 objectClass_values[2] = NULL;
159 objectClass.mod_values = objectClass_values;
161 mods[1] = &objectClass;
164 /* Perform the transaction */
165 lprintf(CTDL_DEBUG, "LDAP: Setting up Base DN node...\n");
167 begin_critical_section(S_LDAP);
168 if (connect_to_ldap()) {
169 end_critical_section(S_LDAP);
172 i = ldap_add_s(dirserver, config.c_ldap_base_dn, mods);
173 end_critical_section(S_LDAP);
175 if (i == LDAP_ALREADY_EXISTS) {
177 "LDAP: Base DN is already present in the directory; no need to add it again.\n");
178 } else if (i != LDAP_SUCCESS) {
179 lprintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
180 ldap_err2string(i), i);
186 * Create an OU node representing a Citadel host.
187 * parameter cn is not used, its just there to keep the hook interface consistant
188 * parameter object not used here, present for interface compatability
190 int create_ldap_host_OU(char *cn, char *host, void **object)
193 char *objectClass_values[3];
194 LDAPMod dc, objectClass;
199 /* The DN is this OU plus the base. */
200 snprintf(dn, sizeof dn, "ou=%s,%s", host, config.c_ldap_base_dn);
202 /* Set up the transaction */
203 dc.mod_op = LDAP_MOD_ADD;
207 dc.mod_values = dc_values;
208 objectClass.mod_op = LDAP_MOD_ADD;
209 objectClass.mod_type = "objectClass";
210 objectClass_values[0] = "top";
211 objectClass_values[1] = "organizationalUnit";
212 objectClass_values[2] = NULL;
213 objectClass.mod_values = objectClass_values;
215 mods[1] = &objectClass;
218 /* Perform the transaction */
219 lprintf(CTDL_DEBUG, "LDAP: Setting up Host OU node...\n");
221 begin_critical_section(S_LDAP);
222 if (connect_to_ldap()) {
223 end_critical_section(S_LDAP);
226 i = ldap_add_s(dirserver, dn, mods);
227 end_critical_section(S_LDAP);
229 if (i == LDAP_ALREADY_EXISTS) {
231 "LDAP: Host OU is already present in the directory; no need to add it again.\n");
232 } else if (i != LDAP_SUCCESS) {
233 lprintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
234 ldap_err2string(i), i);
246 * Create a base LDAP object for the interface
249 int create_ldap_object(char *cn, char *ou, void **object)
251 // We do nothing here, this just gets the base structure created by the interface.
252 lprintf(CTDL_DEBUG, "LDAP: Created ldap object\n");
258 * Add an attribute to the ldap object
261 int add_ldap_object(char *cn, char *ou, void **object)
270 "LDAP: Adding ldap attribute name:\"%s\" value:\"%s\"\n",
275 while (attrs[num_attrs])
279 for (cur_attr = 0; cur_attr < num_attrs; cur_attr++) {
280 if (!strcmp(attrs[cur_attr]->mod_type, cn)) { // Adding a value to the attribute
281 if (attrs[cur_attr]->mod_values) {
282 while (attrs[cur_attr]->
283 mod_values[num_values]) {
287 mod_values[num_values])) {
289 "LDAP: Ignoring duplicate attribute/value pair\n");
295 attrs[cur_attr]->mod_values =
296 realloc(attrs[cur_attr]->mod_values,
297 (num_values + 2) * (sizeof(char *)));
298 attrs[cur_attr]->mod_values[num_values] =
300 attrs[cur_attr]->mod_values[num_values + 1] = NULL;
306 realloc(attrs, (sizeof(LDAPMod *)) * (num_attrs + 2));
308 attrs = malloc((sizeof(LDAPMod *)) * (num_attrs + 2));
309 attrs[num_attrs] = malloc(sizeof(LDAPMod));
310 memset(attrs[num_attrs], 0, sizeof(LDAPMod));
311 attrs[num_attrs + 1] = NULL;
312 attrs[num_attrs]->mod_op = LDAP_MOD_ADD;
313 attrs[num_attrs]->mod_type = strdup(cn);
314 attrs[num_attrs]->mod_values = malloc(2 * sizeof(char *));
315 attrs[num_attrs]->mod_values[0] = strdup(ou);
316 attrs[num_attrs]->mod_values[1] = NULL;
323 * SAve the object to the LDAP server
325 int save_ldap_object(char *cn, char *ou, void **object)
334 if (dirserver == NULL)
339 sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
341 lprintf(CTDL_INFO, "LDAP: Calling ldap_add_s() for dn of '%s'\n",
344 /* The last attribute must be a NULL one. */
345 attrs = (LDAPMod **) * object;
347 while (attrs[num_attrs]) {
349 while (attrs[num_attrs]->mod_values[count]) {
351 "LDAP: attribute %d, value %d = \'%s=%s\'\n",
353 attrs[num_attrs]->mod_type,
362 "LDAP: no attributes in save_ldap_object\n");
366 begin_critical_section(S_LDAP);
367 if (connect_to_ldap()) {
368 end_critical_section(S_LDAP);
372 i = ldap_add_s(dirserver, this_dn, attrs);
374 if (i == LDAP_SERVER_DOWN) {
376 ("The LDAP server appears to be down.\nThe save to LDAP did not occurr.\n",
377 "LDAP: save failed");
378 end_critical_section(S_LDAP);
382 /* If the entry already exists, repopulate it instead */
383 if (i == LDAP_ALREADY_EXISTS) {
384 for (j = 0; j < (num_attrs); ++j) {
385 attrs[j]->mod_op = LDAP_MOD_REPLACE;
388 "LDAP: Calling ldap_modify_s() for dn of '%s'\n",
390 i = ldap_modify_s(dirserver, this_dn, attrs);
393 if (i != LDAP_SUCCESS) {
394 lprintf(CTDL_ERR, "LDAP: ldap_add_s() failed: %s (%d)\n",
395 ldap_err2string(i), i);
397 ("The LDAP server refused the save command.\nDid you update the schema?\n",
398 "LDAP: save failed (schema?)");
399 end_critical_section(S_LDAP);
402 end_critical_section(S_LDAP);
410 int free_ldap_object(char *cn, char *ou, void **object)
417 attrs = (LDAPMod **) * object;
419 while (attrs[num_attrs])
423 lprintf(CTDL_DEBUG, "LDAP: Freeing attributes\n");
424 /* Free the attributes */
425 for (i = 0; i < num_attrs; ++i) {
426 if (attrs[i] != NULL) {
428 /* First, free the value strings */
429 if (attrs[i]->mod_values != NULL) {
431 attrs[i]->mod_values[j] != NULL;
433 free(attrs[i]->mod_values[j]);
437 /* Free the value strings pointer list */
438 if (attrs[i]->mod_values != NULL) {
439 free(attrs[i]->mod_values);
442 /* Now free the LDAPMod struct itself. */
454 * Delete a record from the LDAP
456 * parameter object not used here, present for hook interface compatability
458 int delete_from_ldap(char *cn, char *ou, void **object)
464 if (dirserver == NULL)
471 sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
473 lprintf(CTDL_DEBUG, "LDAP: Calling ldap_delete_s()\n");
475 begin_critical_section(S_LDAP);
476 if (connect_to_ldap()) {
477 end_critical_section(S_LDAP);
481 i = ldap_delete_s(dirserver, this_dn);
483 if (i == LDAP_SERVER_DOWN) {
484 end_critical_section(S_LDAP);
486 ("The LDAP server appears to be down.\nThe delete from LDAP did not occurr.\n",
487 "LDAP: delete failed");
491 if (i != LDAP_SUCCESS) {
493 "LDAP: ldap_delete_s() failed: %s (%d)\n",
494 ldap_err2string(i), i);
495 end_critical_section(S_LDAP);
496 aide_message(ldap_err2string(i), "LDAP: delete failed");
499 end_critical_section(S_LDAP);
506 void ldap_disconnect_timer(void)
508 begin_critical_section(S_LDAP);
509 if (ldap_time_disconnect) {
510 ldap_time_disconnect--;
511 end_critical_section(S_LDAP);
515 end_critical_section(S_LDAP);
519 #endif /* HAVE_LDAP */
523 * Initialize the LDAP connector module ... or don't, if we don't have LDAP.
525 CTDL_MODULE_INIT(ldap)
528 if (!IsEmptyStr(config.c_ldap_base_dn)) {
529 CtdlRegisterCleanupHook(serv_ldap_cleanup);
530 CtdlRegisterSessionHook(ldap_disconnect_timer, EVT_TIMER);
531 CtdlRegisterDirectoryServiceFunc(delete_from_ldap,
534 CtdlRegisterDirectoryServiceFunc(create_ldap_host_OU,
535 DIRECTORY_CREATE_HOST,
537 CtdlRegisterDirectoryServiceFunc(create_ldap_object,
538 DIRECTORY_CREATE_OBJECT,
540 CtdlRegisterDirectoryServiceFunc(add_ldap_object,
541 DIRECTORY_ATTRIB_ADD,
543 CtdlRegisterDirectoryServiceFunc(save_ldap_object,
544 DIRECTORY_SAVE_OBJECT,
546 CtdlRegisterDirectoryServiceFunc(free_ldap_object,
547 DIRECTORY_FREE_OBJECT,
551 #endif /* HAVE_LDAP */
553 /* return our Subversion id for the Log */