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;
59 * LDAP connector cleanup function
61 void serv_ldap_cleanup(void)
64 CtdlLogPrintf(CTDL_INFO,
65 "LDAP: Unbinding from directory server\n");
66 ldap_unbind(dirserver);
69 ldap_time_disconnect = 0;
76 int connect_to_ldap(void)
81 if (ldap_time_disconnect && dirserver) { // Already connected
82 ldap_time_disconnect = 5; // reset the timer.
86 CtdlLogPrintf(CTDL_INFO, "LDAP: Connecting to LDAP server %s:%d...\n",
87 config.c_ldap_host, config.c_ldap_port);
89 dirserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
90 if (dirserver == NULL) {
91 CtdlLogPrintf(CTDL_CRIT,
92 "LDAP: Could not connect to %s:%d : %s\n",
93 config.c_ldap_host, config.c_ldap_port,
95 CtdlAideMessage(strerror(errno),
96 "LDAP: Could not connect to server.");
100 ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION,
103 CtdlLogPrintf(CTDL_INFO, "LDAP: Binding to %s\n", config.c_ldap_bind_dn);
105 i = ldap_simple_bind_s(dirserver,
106 config.c_ldap_bind_dn,
107 config.c_ldap_bind_pw);
108 if (i != LDAP_SUCCESS) {
109 CtdlLogPrintf(CTDL_CRIT, "LDAP: Cannot bind: %s (%d)\n",
110 ldap_err2string(i), i);
111 dirserver = NULL; /* FIXME disconnect from ldap */
112 CtdlAideMessage(ldap_err2string(i),
113 "LDAP: Cannot bind to server");
116 ldap_time_disconnect = 5;
123 * Create the root node. If it's already there, so what?
125 void create_ldap_root(void)
128 char *objectClass_values[3];
129 LDAPMod dc, objectClass;
134 /* We just want the top-level dc, not the whole hierarchy */
135 strcpy(topdc, config.c_ldap_base_dn);
136 for (i = 0; topdc[i]; ++i) {
137 if (topdc[i] == ',') {
142 for (i = 0; topdc[i]; ++i) {
144 strcpy(topdc, &topdc[i + 1]);
147 /* Set up the transaction */
148 dc.mod_op = LDAP_MOD_ADD;
150 dc_values[0] = topdc;
152 dc.mod_values = dc_values;
153 objectClass.mod_op = LDAP_MOD_ADD;
154 objectClass.mod_type = "objectClass";
155 objectClass_values[0] = "top";
156 objectClass_values[1] = "domain";
157 objectClass_values[2] = NULL;
158 objectClass.mod_values = objectClass_values;
160 mods[1] = &objectClass;
163 /* Perform the transaction */
164 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Setting up Base DN node...\n");
166 begin_critical_section(S_LDAP);
167 if (connect_to_ldap()) {
168 end_critical_section(S_LDAP);
171 i = ldap_add_s(dirserver, config.c_ldap_base_dn, mods);
172 end_critical_section(S_LDAP);
174 if (i == LDAP_ALREADY_EXISTS) {
175 CtdlLogPrintf(CTDL_INFO,
176 "LDAP: Base DN is already present in the directory; no need to add it again.\n");
177 } else if (i != LDAP_SUCCESS) {
178 CtdlLogPrintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
179 ldap_err2string(i), i);
185 * Create an OU node representing a Citadel host.
186 * parameter cn is not used, its just there to keep the hook interface consistant
187 * parameter object not used here, present for interface compatability
189 int create_ldap_host_OU(char *cn, char *host, void **object)
192 char *objectClass_values[3];
193 LDAPMod dc, objectClass;
198 /* The DN is this OU plus the base. */
199 snprintf(dn, sizeof dn, "ou=%s,%s", host, config.c_ldap_base_dn);
201 /* Set up the transaction */
202 dc.mod_op = LDAP_MOD_ADD;
206 dc.mod_values = dc_values;
207 objectClass.mod_op = LDAP_MOD_ADD;
208 objectClass.mod_type = "objectClass";
209 objectClass_values[0] = "top";
210 objectClass_values[1] = "organizationalUnit";
211 objectClass_values[2] = NULL;
212 objectClass.mod_values = objectClass_values;
214 mods[1] = &objectClass;
217 /* Perform the transaction */
218 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Setting up Host OU node...\n");
220 begin_critical_section(S_LDAP);
221 if (connect_to_ldap()) {
222 end_critical_section(S_LDAP);
225 i = ldap_add_s(dirserver, dn, mods);
226 end_critical_section(S_LDAP);
228 if (i == LDAP_ALREADY_EXISTS) {
229 CtdlLogPrintf(CTDL_INFO,
230 "LDAP: Host OU is already present in the directory; no need to add it again.\n");
231 } else if (i != LDAP_SUCCESS) {
232 CtdlLogPrintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
233 ldap_err2string(i), i);
245 * Create a base LDAP object for the interface
248 int create_ldap_object(char *cn, char *ou, void **object)
250 // We do nothing here, this just gets the base structure created by the interface.
251 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Created ldap object\n");
257 * Add an attribute to the ldap object
260 int add_ldap_object(char *cn, char *ou, void **object)
268 CtdlLogPrintf(CTDL_DEBUG,
269 "LDAP: Adding ldap attribute name:\"%s\" value:\"%s\"\n",
274 while (attrs[num_attrs])
278 for (cur_attr = 0; cur_attr < num_attrs; cur_attr++) {
279 if (!strcmp(attrs[cur_attr]->mod_type, cn)) { // Adding a value to the attribute
280 if (attrs[cur_attr]->mod_values) {
281 while (attrs[cur_attr]->
282 mod_values[num_values]) {
286 mod_values[num_values])) {
287 CtdlLogPrintf(CTDL_DEBUG,
288 "LDAP: Ignoring duplicate attribute/value pair\n");
294 attrs[cur_attr]->mod_values =
295 realloc(attrs[cur_attr]->mod_values,
296 (num_values + 2) * (sizeof(char *)));
297 attrs[cur_attr]->mod_values[num_values] =
299 attrs[cur_attr]->mod_values[num_values + 1] = NULL;
305 realloc(attrs, (sizeof(LDAPMod *)) * (num_attrs + 2));
307 attrs = malloc((sizeof(LDAPMod *)) * (num_attrs + 2));
308 attrs[num_attrs] = malloc(sizeof(LDAPMod));
309 memset(attrs[num_attrs], 0, sizeof(LDAPMod));
310 attrs[num_attrs + 1] = NULL;
311 attrs[num_attrs]->mod_op = LDAP_MOD_ADD;
312 attrs[num_attrs]->mod_type = strdup(cn);
313 attrs[num_attrs]->mod_values = malloc(2 * sizeof(char *));
314 attrs[num_attrs]->mod_values[0] = strdup(ou);
315 attrs[num_attrs]->mod_values[1] = NULL;
322 * SAve the object to the LDAP server
324 int save_ldap_object(char *cn, char *ou, void **object)
333 if (dirserver == NULL)
338 sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
340 CtdlLogPrintf(CTDL_INFO, "LDAP: Calling ldap_add_s() for dn of '%s'\n",
343 /* The last attribute must be a NULL one. */
344 attrs = (LDAPMod **) * object;
346 while (attrs[num_attrs]) {
348 while (attrs[num_attrs]->mod_values[count]) {
349 CtdlLogPrintf(CTDL_DEBUG,
350 "LDAP: attribute %d, value %d = \'%s=%s\'\n",
352 attrs[num_attrs]->mod_type,
360 CtdlLogPrintf(CTDL_ERR,
361 "LDAP: no attributes in save_ldap_object\n");
365 begin_critical_section(S_LDAP);
366 if (connect_to_ldap()) {
367 end_critical_section(S_LDAP);
371 i = ldap_add_s(dirserver, this_dn, attrs);
373 if (i == LDAP_SERVER_DOWN) {
375 ("The LDAP server appears to be down.\nThe save to LDAP did not occurr.\n",
376 "LDAP: save failed");
377 end_critical_section(S_LDAP);
381 /* If the entry already exists, repopulate it instead */
382 if (i == LDAP_ALREADY_EXISTS) {
383 for (j = 0; j < (num_attrs); ++j) {
384 attrs[j]->mod_op = LDAP_MOD_REPLACE;
386 CtdlLogPrintf(CTDL_INFO,
387 "LDAP: Calling ldap_modify_s() for dn of '%s'\n",
389 i = ldap_modify_s(dirserver, this_dn, attrs);
392 if (i != LDAP_SUCCESS) {
393 CtdlLogPrintf(CTDL_ERR, "LDAP: ldap_add_s() failed: %s (%d)\n",
394 ldap_err2string(i), i);
396 ("The LDAP server refused the save command.\nDid you update the schema?\n",
397 "LDAP: save failed (schema?)");
398 end_critical_section(S_LDAP);
401 end_critical_section(S_LDAP);
409 int free_ldap_object(char *cn, char *ou, void **object)
416 attrs = (LDAPMod **) * object;
418 while (attrs[num_attrs])
422 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Freeing attributes\n");
423 /* Free the attributes */
424 for (i = 0; i < num_attrs; ++i) {
425 if (attrs[i] != NULL) {
427 /* First, free the value strings */
428 if (attrs[i]->mod_values != NULL) {
430 attrs[i]->mod_values[j] != NULL;
432 free(attrs[i]->mod_values[j]);
436 /* Free the value strings pointer list */
437 if (attrs[i]->mod_values != NULL) {
438 free(attrs[i]->mod_values);
441 /* Now free the LDAPMod struct itself. */
453 * Delete a record from the LDAP
455 * parameter object not used here, present for hook interface compatability
457 int delete_from_ldap(char *cn, char *ou, void **object)
463 if (dirserver == NULL)
470 sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
472 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Calling ldap_delete_s()\n");
474 begin_critical_section(S_LDAP);
475 if (connect_to_ldap()) {
476 end_critical_section(S_LDAP);
480 i = ldap_delete_s(dirserver, this_dn);
482 if (i == LDAP_SERVER_DOWN) {
483 end_critical_section(S_LDAP);
485 ("The LDAP server appears to be down.\nThe delete from LDAP did not occurr.\n",
486 "LDAP: delete failed");
490 if (i != LDAP_SUCCESS) {
491 CtdlLogPrintf(CTDL_ERR,
492 "LDAP: ldap_delete_s() failed: %s (%d)\n",
493 ldap_err2string(i), i);
494 end_critical_section(S_LDAP);
495 CtdlAideMessage(ldap_err2string(i), "LDAP: delete failed");
498 end_critical_section(S_LDAP);
505 void ldap_disconnect_timer(void)
507 begin_critical_section(S_LDAP);
508 if (ldap_time_disconnect) {
509 ldap_time_disconnect--;
510 end_critical_section(S_LDAP);
514 end_critical_section(S_LDAP);
518 #endif /* HAVE_LDAP */
522 * Initialize the LDAP connector module ... or don't, if we don't have LDAP.
524 CTDL_MODULE_INIT(ldap)
527 if (!IsEmptyStr(config.c_ldap_base_dn)) {
528 CtdlRegisterCleanupHook(serv_ldap_cleanup);
529 CtdlRegisterSessionHook(ldap_disconnect_timer, EVT_TIMER);
530 CtdlRegisterDirectoryServiceFunc(delete_from_ldap,
533 CtdlRegisterDirectoryServiceFunc(create_ldap_host_OU,
534 DIRECTORY_CREATE_HOST,
536 CtdlRegisterDirectoryServiceFunc(create_ldap_object,
537 DIRECTORY_CREATE_OBJECT,
539 CtdlRegisterDirectoryServiceFunc(add_ldap_object,
540 DIRECTORY_ATTRIB_ADD,
542 CtdlRegisterDirectoryServiceFunc(save_ldap_object,
543 DIRECTORY_SAVE_OBJECT,
545 CtdlRegisterDirectoryServiceFunc(free_ldap_object,
546 DIRECTORY_FREE_OBJECT,
550 #endif /* HAVE_LDAP */
552 /* return our Subversion id for the Log */