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 lprintf(CTDL_INFO, "LDAP: Unbinding from directory server\n");
67 ldap_unbind(dirserver);
70 ldap_time_disconnect=0;
77 int CtdlConnectToLdap(void) {
81 if (ldap_time_disconnect && dirserver)
82 { // 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) {
92 lprintf(CTDL_CRIT, "LDAP: Could not connect to %s:%d : %s\n",
96 aide_message(strerror(errno), "LDAP: Could not connect to server.");
100 ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
102 lprintf(CTDL_INFO, "LDAP: Binding to %s\n", config.c_ldap_bind_dn);
104 i = ldap_simple_bind_s(dirserver,
105 config.c_ldap_bind_dn,
106 config.c_ldap_bind_pw
108 if (i != LDAP_SUCCESS) {
109 lprintf(CTDL_CRIT, "LDAP: Cannot bind: %s (%d)\n", ldap_err2string(i), i);
110 dirserver = NULL; /* FIXME disconnect from ldap */
111 aide_message(ldap_err2string(i), "LDAP: Cannot bind to server");
114 ldap_time_disconnect=5;
121 * Create the root node. If it's already there, so what?
123 void CtdlCreateLdapRoot(void) {
125 char *objectClass_values[3];
126 LDAPMod dc, objectClass;
131 /* We just want the top-level dc, not the whole hierarchy */
132 strcpy(topdc, config.c_ldap_base_dn);
133 for (i=0; topdc[i]; ++i) {
134 if (topdc[i] == ',') {
139 for (i=0; topdc[i]; ++i) {
140 if (topdc[i] == '=') strcpy(topdc, &topdc[i+1]);
143 /* Set up the transaction */
144 dc.mod_op = LDAP_MOD_ADD;
146 dc_values[0] = topdc;
148 dc.mod_values = dc_values;
149 objectClass.mod_op = LDAP_MOD_ADD;
150 objectClass.mod_type = "objectClass";
151 objectClass_values[0] = "top";
152 objectClass_values[1] = "domain";
153 objectClass_values[2] = NULL;
154 objectClass.mod_values = objectClass_values;
156 mods[1] = &objectClass;
159 /* Perform the transaction */
160 lprintf(CTDL_DEBUG, "LDAP: Setting up Base DN node...\n");
162 begin_critical_section(S_LDAP);
163 if (CtdlConnectToLdap())
165 end_critical_section(S_LDAP);
168 i = ldap_add_s(dirserver, config.c_ldap_base_dn, mods);
169 end_critical_section(S_LDAP);
171 if (i == LDAP_ALREADY_EXISTS) {
172 lprintf(CTDL_INFO, "LDAP: Base DN is already present in the directory; no need to add it again.\n");
174 else if (i != LDAP_SUCCESS) {
175 lprintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
176 ldap_err2string(i), i);
182 * Create an OU node representing a Citadel host.
183 * parameter cn is not used, its just there to keep the hook interface consistant
184 * parameter object not used here, present for interface compatability
186 int CtdlCreateLdapHostOU(char *cn, char *host, void **object) {
188 char *objectClass_values[3];
189 LDAPMod dc, objectClass;
194 /* The DN is this OU plus the base. */
195 snprintf(dn, sizeof dn, "ou=%s,%s", host, config.c_ldap_base_dn);
197 /* Set up the transaction */
198 dc.mod_op = LDAP_MOD_ADD;
202 dc.mod_values = dc_values;
203 objectClass.mod_op = LDAP_MOD_ADD;
204 objectClass.mod_type = "objectClass";
205 objectClass_values[0] = "top";
206 objectClass_values[1] = "organizationalUnit";
207 objectClass_values[2] = NULL;
208 objectClass.mod_values = objectClass_values;
210 mods[1] = &objectClass;
213 /* Perform the transaction */
214 lprintf(CTDL_DEBUG, "LDAP: Setting up Host OU node...\n");
216 begin_critical_section(S_LDAP);
217 if (CtdlConnectToLdap())
219 end_critical_section(S_LDAP);
222 i = ldap_add_s(dirserver, dn, mods);
223 end_critical_section(S_LDAP);
225 if (i == LDAP_ALREADY_EXISTS) {
226 lprintf(CTDL_INFO, "LDAP: Host OU is already present in the directory; no need to add it again.\n");
228 else if (i != LDAP_SUCCESS) {
229 lprintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
230 ldap_err2string(i), i);
242 * Create a base LDAP object for the interface
245 int CtdlCreateLdapObject(char *cn, char *ou, void **object)
247 // We do nothing here, this just gets the base structure created by the interface.
248 lprintf (CTDL_DEBUG, "LDAP: Created ldap object\n");
254 * Add an attribute to the ldap object
257 int CtdlAddLdapAttr(char *cn, char *ou, void **object)
265 lprintf (CTDL_DEBUG, "LDAP: Adding ldap attribute name:\"%s\" vlaue:\"%s\"\n", cn, ou);
270 while (attrs[num_attrs])
274 for (cur_attr = 0; cur_attr < num_attrs ; cur_attr++)
276 if (!strcmp(attrs[cur_attr]->mod_type, cn))
277 { // Adding a value to the attribute
278 if (attrs[cur_attr]->mod_values)
280 while (attrs[cur_attr]->mod_values[num_values])
283 attrs[cur_attr]->mod_values = realloc(attrs[cur_attr]->mod_values, (num_values + 2) * (sizeof(char *)));
284 attrs[cur_attr]->mod_values[num_values] = strdup(ou);
285 attrs[cur_attr]->mod_values[num_values+1] = NULL;
290 attrs = realloc(attrs, (sizeof(LDAPMod *)) * (num_attrs + 2));
292 attrs = malloc((sizeof(LDAPMod *)) * (num_attrs + 2));
293 attrs[num_attrs] = malloc(sizeof(LDAPMod));
294 memset(attrs[num_attrs], 0, sizeof(LDAPMod));
295 attrs[num_attrs+1] = NULL;
296 attrs[num_attrs]->mod_op = LDAP_MOD_ADD;
297 attrs[num_attrs]->mod_type = strdup(cn);
298 attrs[num_attrs]->mod_values = malloc(2 * sizeof(char *));
299 attrs[num_attrs]->mod_values[0] = strdup(ou);
300 attrs[num_attrs]->mod_values[1] = NULL;
307 * SAve the object to the LDAP server
309 int CtdlSaveLdapObject(char *cn, char *ou, void **object)
318 if (dirserver == NULL) return -1;
319 if (ou == NULL) return -1;
320 if (cn == NULL) return -1;
322 sprintf(this_dn, "euid=%s,ou=%s,%s", cn, ou, config.c_ldap_base_dn);
324 lprintf(CTDL_INFO, "LDAP: Calling ldap_add_s() for dn of '%s'\n", this_dn);
326 /* The last attribute must be a NULL one. */
327 attrs = (LDAPMod **)*object;
330 while (attrs[num_attrs])
333 while (attrs[num_attrs]->mod_values[count])
335 lprintf (CTDL_DEBUG, "LDAP: attribute %d, value %d = \'%s=%s\'\n", num_attrs, count, attrs[num_attrs]->mod_type, attrs[num_attrs]->mod_values[count]);
343 lprintf(CTDL_ERR, "LDAP: no attributes in CtdlSaveLdapObject\n");
347 begin_critical_section(S_LDAP);
348 if (CtdlConnectToLdap())
350 end_critical_section(S_LDAP);
354 i = ldap_add_s(dirserver, this_dn, attrs);
356 if (i == LDAP_SERVER_DOWN)
358 aide_message("The LDAP server appears to be down.\nThe save to LDAP did not occurr.\n", "LDAP: save failed");
359 end_critical_section(S_LDAP);
363 /* If the entry already exists, repopulate it instead */
364 if (i == LDAP_ALREADY_EXISTS) {
365 for (j=0; j<(num_attrs); ++j) {
366 attrs[j]->mod_op = LDAP_MOD_REPLACE;
368 lprintf(CTDL_INFO, "LDAP: Calling ldap_modify_s() for dn of '%s'\n", this_dn);
369 i = ldap_modify_s(dirserver, this_dn, attrs);
372 if (i != LDAP_SUCCESS) {
373 lprintf(CTDL_ERR, "LDAP: ldap_add_s() failed: %s (%d)\n",
374 ldap_err2string(i), i);
375 aide_message("The LDAP server refused the save command.\nDid you update the schema?\n", "LDAP: save failed (schema?)");
376 end_critical_section(S_LDAP);
379 end_critical_section(S_LDAP);
387 int CtdlFreeLdapObject(char *cn, char *ou, void **object)
394 attrs = (LDAPMod **)*object;
397 while (attrs[num_attrs])
401 lprintf(CTDL_DEBUG, "LDAP: Freeing attributes\n");
402 /* Free the attributes */
403 for (i=0; i<num_attrs; ++i) {
404 if (attrs[i] != NULL) {
406 /* First, free the value strings */
407 if (attrs[i]->mod_values != NULL) {
408 for (j=0; attrs[i]->mod_values[j] != NULL; ++j) {
409 free(attrs[i]->mod_values[j]);
413 /* Free the value strings pointer list */
414 if (attrs[i]->mod_values != NULL) {
415 free(attrs[i]->mod_values);
418 /* Now free the LDAPMod struct itself. */
430 * Delete a record from the LDAP
432 * parameter object not used here, present for hook interface compatability
434 int CtdlDeleteFromLdap(char *cn, char *ou, void **object)
440 if (dirserver == NULL) return -1;
441 if (ou == NULL) return -1;
442 if (cn == NULL) return -1;
444 sprintf(this_dn, "euid=%s,ou=%s,%s", cn, ou, config.c_ldap_base_dn);
446 lprintf(CTDL_DEBUG, "LDAP: Calling ldap_delete_s()\n");
448 begin_critical_section(S_LDAP);
449 if (CtdlConnectToLdap())
451 end_critical_section(S_LDAP);
455 i = ldap_delete_s(dirserver, this_dn);
457 if (i == LDAP_SERVER_DOWN)
459 end_critical_section(S_LDAP);
460 aide_message("The LDAP server appears to be down.\nThe delete from LDAP did not occurr.\n", "LDAP: delete failed");
464 if (i != LDAP_SUCCESS) {
465 lprintf(CTDL_ERR, "LDAP: ldap_delete_s() failed: %s (%d)\n",
466 ldap_err2string(i), i);
467 end_critical_section(S_LDAP);
468 aide_message(ldap_err2string(i), "LDAP: delete failed");
471 end_critical_section(S_LDAP);
478 void ldap_disconnect_timer(void)
480 begin_critical_section(S_LDAP);
481 if(ldap_time_disconnect)
483 ldap_time_disconnect--;
484 end_critical_section(S_LDAP);
488 end_critical_section(S_LDAP);
492 #endif /* HAVE_LDAP */
496 * Initialize the LDAP connector module ... or don't, if we don't have LDAP.
498 CTDL_MODULE_INIT(ldap)
501 if (!IsEmptyStr(config.c_ldap_base_dn))
503 CtdlRegisterCleanupHook(serv_ldap_cleanup);
504 CtdlRegisterSessionHook(ldap_disconnect_timer, EVT_TIMER);
505 CtdlRegisterDirectoryServiceFunc(CtdlDeleteFromLdap, DIRECTORY_USER_DEL, "ldap");
506 CtdlRegisterDirectoryServiceFunc(CtdlCreateLdapHostOU, DIRECTORY_CREATE_HOST, "ldap");
507 CtdlRegisterDirectoryServiceFunc(CtdlCreateLdapObject, DIRECTORY_CREATE_OBJECT, "ldap");
508 CtdlRegisterDirectoryServiceFunc(CtdlAddLdapAttr, DIRECTORY_ATTRIB_ADD, "ldap");
509 CtdlRegisterDirectoryServiceFunc(CtdlSaveLdapObject, DIRECTORY_SAVE_OBJECT, "ldap");
510 CtdlRegisterDirectoryServiceFunc(CtdlFreeLdapObject, DIRECTORY_FREE_OBJECT, "ldap");
511 CtdlCreateLdapRoot();
514 #endif /* HAVE_LDAP */
516 /* return our Subversion id for the Log */