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;
77 * Make sure this function is only called from within a begin_critical_section(S_LDAP)
78 * If you don't things will break!!!!!.
82 int connect_to_ldap(void)
87 if (ldap_time_disconnect && dirserver) { // Already connected
88 ldap_time_disconnect = 5; // reset the timer.
92 CtdlLogPrintf(CTDL_INFO, "LDAP: Connecting to LDAP server %s:%d...\n",
93 config.c_ldap_host, config.c_ldap_port);
95 dirserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
96 if (dirserver == NULL) {
97 CtdlLogPrintf(CTDL_CRIT,
98 "LDAP: Could not connect to %s:%d : %s\n",
99 config.c_ldap_host, config.c_ldap_port,
101 CtdlAideMessage(strerror(errno),
102 "LDAP: Could not connect to server.");
106 ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION,
109 CtdlLogPrintf(CTDL_INFO, "LDAP: Binding to %s\n", config.c_ldap_bind_dn);
111 i = ldap_simple_bind_s(dirserver,
112 config.c_ldap_bind_dn,
113 config.c_ldap_bind_pw);
114 if (i != LDAP_SUCCESS) {
115 CtdlLogPrintf(CTDL_CRIT, "LDAP: Cannot bind: %s (%d)\n",
116 ldap_err2string(i), i);
117 dirserver = NULL; /* FIXME disconnect from ldap */
118 CtdlAideMessage(ldap_err2string(i),
119 "LDAP: Cannot bind to server");
122 ldap_time_disconnect = 5;
129 * Create the root node. If it's already there, so what?
131 void create_ldap_root(void)
134 char *objectClass_values[3];
135 LDAPMod dc, objectClass;
140 /* We just want the top-level dc, not the whole hierarchy */
141 strcpy(topdc, config.c_ldap_base_dn);
142 for (i = 0; topdc[i]; ++i) {
143 if (topdc[i] == ',') {
148 for (i = 0; topdc[i]; ++i) {
150 strcpy(topdc, &topdc[i + 1]);
153 /* Set up the transaction */
154 dc.mod_op = LDAP_MOD_ADD;
156 dc_values[0] = topdc;
158 dc.mod_values = dc_values;
159 objectClass.mod_op = LDAP_MOD_ADD;
160 objectClass.mod_type = "objectClass";
161 objectClass_values[0] = "top";
162 objectClass_values[1] = "domain";
163 objectClass_values[2] = NULL;
164 objectClass.mod_values = objectClass_values;
166 mods[1] = &objectClass;
169 /* Perform the transaction */
170 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Setting up Base DN node...\n");
172 begin_critical_section(S_LDAP);
173 if (connect_to_ldap()) {
174 end_critical_section(S_LDAP);
177 i = ldap_add_s(dirserver, config.c_ldap_base_dn, mods);
178 end_critical_section(S_LDAP);
180 if (i == LDAP_ALREADY_EXISTS) {
181 CtdlLogPrintf(CTDL_INFO,
182 "LDAP: Base DN is already present in the directory; no need to add it again.\n");
183 } else if (i != LDAP_SUCCESS) {
184 CtdlLogPrintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
185 ldap_err2string(i), i);
191 * Create an OU node representing a Citadel host.
192 * parameter cn is not used, its just there to keep the hook interface consistant
193 * parameter object not used here, present for interface compatability
195 int create_ldap_host_OU(char *cn, char *host, void **object)
198 char *objectClass_values[3];
199 LDAPMod dc, objectClass;
204 /* The DN is this OU plus the base. */
205 snprintf(dn, sizeof dn, "ou=%s,%s", host, config.c_ldap_base_dn);
207 /* Set up the transaction */
208 dc.mod_op = LDAP_MOD_ADD;
212 dc.mod_values = dc_values;
213 objectClass.mod_op = LDAP_MOD_ADD;
214 objectClass.mod_type = "objectClass";
215 objectClass_values[0] = "top";
216 objectClass_values[1] = "organizationalUnit";
217 objectClass_values[2] = NULL;
218 objectClass.mod_values = objectClass_values;
220 mods[1] = &objectClass;
223 /* Perform the transaction */
224 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Setting up Host OU node...\n");
226 begin_critical_section(S_LDAP);
227 if (connect_to_ldap()) {
228 end_critical_section(S_LDAP);
231 i = ldap_add_s(dirserver, dn, mods);
232 end_critical_section(S_LDAP);
234 if (i == LDAP_ALREADY_EXISTS) {
235 CtdlLogPrintf(CTDL_INFO,
236 "LDAP: Host OU is already present in the directory; no need to add it again.\n");
237 } else if (i != LDAP_SUCCESS) {
238 CtdlLogPrintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
239 ldap_err2string(i), i);
251 * Create a base LDAP object for the interface
254 int create_ldap_object(char *cn, char *ou, void **object)
256 // We do nothing here, this just gets the base structure created by the interface.
257 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Created ldap object\n");
263 * Add an attribute to the ldap object
266 int add_ldap_object(char *cn, char *ou, void **object)
274 CtdlLogPrintf(CTDL_DEBUG,
275 "LDAP: Adding ldap attribute name:\"%s\" value:\"%s\"\n",
280 while (attrs[num_attrs])
284 for (cur_attr = 0; cur_attr < num_attrs; cur_attr++) {
285 if (!strcmp(attrs[cur_attr]->mod_type, cn)) { // Adding a value to the attribute
286 if (attrs[cur_attr]->mod_values) {
287 while (attrs[cur_attr]->
288 mod_values[num_values]) {
292 mod_values[num_values])) {
293 CtdlLogPrintf(CTDL_DEBUG,
294 "LDAP: Ignoring duplicate attribute/value pair\n");
300 attrs[cur_attr]->mod_values =
301 realloc(attrs[cur_attr]->mod_values,
302 (num_values + 2) * (sizeof(char *)));
303 attrs[cur_attr]->mod_values[num_values] =
305 attrs[cur_attr]->mod_values[num_values + 1] = NULL;
311 realloc(attrs, (sizeof(LDAPMod *)) * (num_attrs + 2));
313 attrs = malloc((sizeof(LDAPMod *)) * (num_attrs + 2));
314 attrs[num_attrs] = malloc(sizeof(LDAPMod));
315 memset(attrs[num_attrs], 0, sizeof(LDAPMod));
316 attrs[num_attrs + 1] = NULL;
317 attrs[num_attrs]->mod_op = LDAP_MOD_ADD;
318 attrs[num_attrs]->mod_type = strdup(cn);
319 attrs[num_attrs]->mod_values = malloc(2 * sizeof(char *));
320 attrs[num_attrs]->mod_values[0] = strdup(ou);
321 attrs[num_attrs]->mod_values[1] = NULL;
328 * SAve the object to the LDAP server
330 int save_ldap_object(char *cn, char *ou, void **object)
339 if (dirserver == NULL)
344 sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
346 CtdlLogPrintf(CTDL_INFO, "LDAP: Calling ldap_add_s() for dn of '%s'\n",
349 /* The last attribute must be a NULL one. */
350 attrs = (LDAPMod **) * object;
352 while (attrs[num_attrs]) {
354 while (attrs[num_attrs]->mod_values[count]) {
355 CtdlLogPrintf(CTDL_DEBUG,
356 "LDAP: attribute %d, value %d = \'%s=%s\'\n",
358 attrs[num_attrs]->mod_type,
366 CtdlLogPrintf(CTDL_ERR,
367 "LDAP: no attributes in save_ldap_object\n");
371 begin_critical_section(S_LDAP);
372 if (connect_to_ldap()) {
373 end_critical_section(S_LDAP);
377 i = ldap_add_s(dirserver, this_dn, attrs);
379 if (i == LDAP_SERVER_DOWN) {
381 ("The LDAP server appears to be down.\nThe save to LDAP did not occurr.\n",
382 "LDAP: save failed");
383 end_critical_section(S_LDAP);
387 /* If the entry already exists, repopulate it instead */
388 if (i == LDAP_ALREADY_EXISTS) {
389 for (j = 0; j < (num_attrs); ++j) {
390 attrs[j]->mod_op = LDAP_MOD_REPLACE;
392 CtdlLogPrintf(CTDL_INFO,
393 "LDAP: Calling ldap_modify_s() for dn of '%s'\n",
395 i = ldap_modify_s(dirserver, this_dn, attrs);
398 if (i != LDAP_SUCCESS) {
399 CtdlLogPrintf(CTDL_ERR, "LDAP: ldap_add_s() failed: %s (%d)\n",
400 ldap_err2string(i), i);
402 ("The LDAP server refused the save command.\nDid you update the schema?\n",
403 "LDAP: save failed (schema?)");
404 end_critical_section(S_LDAP);
407 end_critical_section(S_LDAP);
415 int free_ldap_object(char *cn, char *ou, void **object)
422 attrs = (LDAPMod **) * object;
424 while (attrs[num_attrs])
428 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Freeing attributes\n");
429 /* Free the attributes */
430 for (i = 0; i < num_attrs; ++i) {
431 if (attrs[i] != NULL) {
433 /* First, free the value strings */
434 if (attrs[i]->mod_values != NULL) {
436 attrs[i]->mod_values[j] != NULL;
438 free(attrs[i]->mod_values[j]);
442 /* Free the value strings pointer list */
443 if (attrs[i]->mod_values != NULL) {
444 free(attrs[i]->mod_values);
447 /* Now free the LDAPMod struct itself. */
459 * Delete a record from the LDAP
461 * parameter object not used here, present for hook interface compatability
463 int delete_from_ldap(char *cn, char *ou, void **object)
469 if (dirserver == NULL)
476 sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
478 CtdlLogPrintf(CTDL_DEBUG, "LDAP: Calling ldap_delete_s()\n");
480 begin_critical_section(S_LDAP);
481 if (connect_to_ldap()) {
482 end_critical_section(S_LDAP);
486 i = ldap_delete_s(dirserver, this_dn);
488 if (i == LDAP_SERVER_DOWN) {
489 end_critical_section(S_LDAP);
491 ("The LDAP server appears to be down.\nThe delete from LDAP did not occurr.\n",
492 "LDAP: delete failed");
496 if (i != LDAP_SUCCESS) {
497 CtdlLogPrintf(CTDL_ERR,
498 "LDAP: ldap_delete_s() failed: %s (%d)\n",
499 ldap_err2string(i), i);
500 end_critical_section(S_LDAP);
501 CtdlAideMessage(ldap_err2string(i), "LDAP: delete failed");
504 end_critical_section(S_LDAP);
511 void ldap_disconnect_timer(void)
513 begin_critical_section(S_LDAP);
514 if (ldap_time_disconnect) {
515 ldap_time_disconnect--;
516 end_critical_section(S_LDAP);
520 end_critical_section(S_LDAP);
524 #endif /* HAVE_LDAP */
528 * Initialize the LDAP connector module ... or don't, if we don't have LDAP.
530 CTDL_MODULE_INIT(ldap)
533 if (!IsEmptyStr(config.c_ldap_base_dn)) {
534 CtdlRegisterCleanupHook(serv_ldap_cleanup);
535 CtdlRegisterSessionHook(ldap_disconnect_timer, EVT_TIMER);
536 CtdlRegisterDirectoryServiceFunc(delete_from_ldap,
539 CtdlRegisterDirectoryServiceFunc(create_ldap_host_OU,
540 DIRECTORY_CREATE_HOST,
542 CtdlRegisterDirectoryServiceFunc(create_ldap_object,
543 DIRECTORY_CREATE_OBJECT,
545 CtdlRegisterDirectoryServiceFunc(add_ldap_object,
546 DIRECTORY_ATTRIB_ADD,
548 CtdlRegisterDirectoryServiceFunc(save_ldap_object,
549 DIRECTORY_SAVE_OBJECT,
551 CtdlRegisterDirectoryServiceFunc(free_ldap_object,
552 DIRECTORY_FREE_OBJECT,
556 #endif /* HAVE_LDAP */
558 /* return our Subversion id for the Log */