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;
58 * LDAP connector cleanup function
60 void serv_ldap_cleanup(void)
62 if (!dirserver) return;
64 lprintf(CTDL_INFO, "Unbinding from directory server\n");
65 ldap_unbind(dirserver);
72 * Create the root node. If it's already there, so what?
74 void CtdlCreateLdapRoot(void) {
76 char *objectClass_values[3];
77 LDAPMod dc, objectClass;
82 /* We just want the top-level dc, not the whole hierarchy */
83 strcpy(topdc, config.c_ldap_base_dn);
84 for (i=0; topdc[i]; ++i) {
85 if (topdc[i] == ',') {
90 for (i=0; topdc[i]; ++i) {
91 if (topdc[i] == '=') strcpy(topdc, &topdc[i+1]);
94 /* Set up the transaction */
95 dc.mod_op = LDAP_MOD_ADD;
99 dc.mod_values = dc_values;
100 objectClass.mod_op = LDAP_MOD_ADD;
101 objectClass.mod_type = "objectClass";
102 objectClass_values[0] = "top";
103 objectClass_values[1] = "domain";
104 objectClass_values[2] = NULL;
105 objectClass.mod_values = objectClass_values;
107 mods[1] = &objectClass;
110 /* Perform the transaction */
111 lprintf(CTDL_DEBUG, "Setting up Base DN node...\n");
112 begin_critical_section(S_LDAP);
113 i = ldap_add_s(dirserver, config.c_ldap_base_dn, mods);
114 end_critical_section(S_LDAP);
116 if (i == LDAP_ALREADY_EXISTS) {
117 lprintf(CTDL_INFO, "Base DN is already present in the directory; no need to add it again.\n");
119 else if (i != LDAP_SUCCESS) {
120 lprintf(CTDL_CRIT, "ldap_add_s() failed: %s (%d)\n",
121 ldap_err2string(i), i);
127 * Create an OU node representing a Citadel host.
128 * parameter cn is not used, its just there to keep the hook interface consistant
129 * parameter object not used here, present for interface compatability
131 int CtdlCreateLdapHostOU(char *cn, char *host, void **object) {
133 char *objectClass_values[3];
134 LDAPMod dc, objectClass;
139 /* The DN is this OU plus the base. */
140 snprintf(dn, sizeof dn, "ou=%s,%s", host, config.c_ldap_base_dn);
142 /* Set up the transaction */
143 dc.mod_op = LDAP_MOD_ADD;
147 dc.mod_values = dc_values;
148 objectClass.mod_op = LDAP_MOD_ADD;
149 objectClass.mod_type = "objectClass";
150 objectClass_values[0] = "top";
151 objectClass_values[1] = "organizationalUnit";
152 objectClass_values[2] = NULL;
153 objectClass.mod_values = objectClass_values;
155 mods[1] = &objectClass;
158 /* Perform the transaction */
159 lprintf(CTDL_DEBUG, "Setting up Host OU node...\n");
160 begin_critical_section(S_LDAP);
161 i = ldap_add_s(dirserver, dn, mods);
162 end_critical_section(S_LDAP);
164 if (i == LDAP_ALREADY_EXISTS) {
165 lprintf(CTDL_INFO, "Host OU is already present in the directory; no need to add it again.\n");
167 else if (i != LDAP_SUCCESS) {
168 lprintf(CTDL_CRIT, "ldap_add_s() failed: %s (%d)\n",
169 ldap_err2string(i), i);
182 void CtdlConnectToLdap(void) {
184 int ldap_version = 3;
186 lprintf(CTDL_INFO, "Connecting to LDAP server %s:%d...\n",
187 config.c_ldap_host, config.c_ldap_port);
189 dirserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
190 if (dirserver == NULL) {
191 lprintf(CTDL_CRIT, "Could not connect to %s:%d : %s\n",
198 ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
200 lprintf(CTDL_INFO, "Binding to %s\n", config.c_ldap_bind_dn);
202 i = ldap_simple_bind_s(dirserver,
203 config.c_ldap_bind_dn,
204 config.c_ldap_bind_pw
206 if (i != LDAP_SUCCESS) {
207 lprintf(CTDL_CRIT, "Cannot bind: %s (%d)\n", ldap_err2string(i), i);
208 dirserver = NULL; /* FIXME disconnect from ldap */
212 CtdlCreateLdapRoot();
218 * Create a base LDAP object for the interface
221 int CtdlCreateLdapObject(char *cn, char *ou, void **object)
223 // We do nothing here, this just gets the base structure created by the interface.
224 lprintf (CTDL_DEBUG, "Created ldap object\n");
230 * Add an attribute to the ldap object
233 int CtdlAddLdapAttr(char *cn, char *ou, void **object)
241 lprintf (CTDL_DEBUG, "Adding ldap attribute\n");
246 while (attrs[num_attrs])
250 for (cur_attr = 0; cur_attr < num_attrs ; cur_attr++)
252 if (!strcmp(attrs[cur_attr]->mod_type, cn))
253 { // Adding a value to the attribute
254 if (attrs[cur_attr]->mod_values)
256 while (attrs[cur_attr]->mod_values[num_values])
259 attrs[cur_attr]->mod_values = realloc(attrs[cur_attr]->mod_values, (num_values + 2) * (sizeof(char *)));
260 attrs[cur_attr]->mod_values[num_values] = strdup(ou);
261 attrs[cur_attr]->mod_values[num_values+1] = NULL;
266 attrs = realloc(attrs, (sizeof(LDAPMod *)) * (num_attrs + 2));
268 attrs = malloc((sizeof(LDAPMod *)) * (num_attrs + 2));
269 attrs[num_attrs] = malloc(sizeof(LDAPMod));
270 memset(attrs[num_attrs], 0, sizeof(LDAPMod));
271 attrs[num_attrs+1] = NULL;
272 attrs[num_attrs]->mod_op = LDAP_MOD_ADD;
273 attrs[num_attrs]->mod_type = strdup(cn);
274 attrs[num_attrs]->mod_values = malloc(2 * sizeof(char *));
275 attrs[num_attrs]->mod_values[0] = strdup(ou);
276 attrs[num_attrs]->mod_values[1] = NULL;
283 * SAve the object to the LDAP server
285 int CtdlSaveLdapObject(char *cn, char *ou, void **object)
294 if (dirserver == NULL) return -1;
295 if (ou == NULL) return -1;
296 if (cn == NULL) return -1;
298 sprintf(this_dn, "cn=%s,ou=%s,%s", cn, ou, config.c_ldap_base_dn);
300 /* The last attribute must be a NULL one. */
301 attrs = (LDAPMod **)*object;
304 while (attrs[num_attrs])
308 lprintf(CTDL_DEBUG, "Calling ldap_add_s() for '%s'\n", this_dn);
310 begin_critical_section(S_LDAP);
311 i = ldap_add_s(dirserver, this_dn, attrs);
312 end_critical_section(S_LDAP);
314 /* If the entry already exists, repopulate it instead */
315 if (i == LDAP_ALREADY_EXISTS) {
316 for (j=0; j<(num_attrs); ++j) {
317 attrs[j]->mod_op = LDAP_MOD_REPLACE;
319 lprintf(CTDL_DEBUG, "Calling ldap_modify_s() for '%s'\n", this_dn);
320 begin_critical_section(S_LDAP);
321 i = ldap_modify_s(dirserver, this_dn, attrs);
322 end_critical_section(S_LDAP);
325 if (i != LDAP_SUCCESS) {
326 lprintf(CTDL_ERR, "ldap_add_s() failed: %s (%d)\n",
327 ldap_err2string(i), i);
337 int CtdlFreeLdapObject(char *cn, char *ou, void **object)
344 attrs = (LDAPMod **)*object;
347 while (attrs[num_attrs])
351 lprintf(CTDL_DEBUG, "Freeing attributes\n");
352 /* Free the attributes */
353 for (i=0; i<num_attrs; ++i) {
354 if (attrs[i] != NULL) {
356 /* First, free the value strings */
357 if (attrs[i]->mod_values != NULL) {
358 for (j=0; attrs[i]->mod_values[j] != NULL; ++j) {
359 free(attrs[i]->mod_values[j]);
363 /* Free the value strings pointer list */
364 if (attrs[i]->mod_values != NULL) {
365 free(attrs[i]->mod_values);
368 /* Now free the LDAPMod struct itself. */
380 * Delete a record from the LDAP
382 * parameter object not used here, present for hook interface compatability
384 int CtdlDeleteFromLdap(char *cn, char *ou, void **object)
390 if (dirserver == NULL) return -1;
391 if (ou == NULL) return -1;
392 if (cn == NULL) return -1;
394 sprintf(this_dn, "cn=%s,ou=%s,%s", cn, ou, config.c_ldap_base_dn);
396 lprintf(CTDL_DEBUG, "Calling ldap_delete_s()\n");
398 begin_critical_section(S_LDAP);
399 i = ldap_delete_s(dirserver, this_dn);
400 end_critical_section(S_LDAP);
402 if (i != LDAP_SUCCESS) {
403 lprintf(CTDL_ERR, "ldap_delete_s() failed: %s (%d)\n",
404 ldap_err2string(i), i);
411 #endif /* HAVE_LDAP */
415 * Initialize the LDAP connector module ... or don't, if we don't have LDAP.
417 CTDL_MODULE_INIT(ldap)
420 CtdlRegisterCleanupHook(serv_ldap_cleanup);
421 CtdlRegisterDirectoryServiceFunc(CtdlDeleteFromLdap, DIRECTORY_USER_DEL, "ldap");
422 CtdlRegisterDirectoryServiceFunc(CtdlCreateLdapHostOU, DIRECTORY_CREATE_HOST, "ldap");
423 CtdlRegisterDirectoryServiceFunc(CtdlCreateLdapObject, DIRECTORY_CREATE_OBJECT, "ldap");
424 CtdlRegisterDirectoryServiceFunc(CtdlAddLdapAttr, DIRECTORY_ATTRIB_ADD, "ldap");
425 CtdlRegisterDirectoryServiceFunc(CtdlSaveLdapObject, DIRECTORY_SAVE_OBJECT, "ldap");
426 CtdlRegisterDirectoryServiceFunc(CtdlFreeLdapObject, DIRECTORY_FREE_OBJECT, "ldap");
429 if (!IsEmptyStr(config.c_ldap_host)) {
433 #endif /* HAVE_LDAP */
435 /* return our Subversion id for the Log */