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 "sysdep_decls.h"
35 #include "citserver.h"
38 #include "serv_extensions.h"
43 #include "serv_ldap.h"
51 LDAP *dirserver = NULL;
54 * LDAP connector cleanup function
56 void serv_ldap_cleanup(void)
58 if (!dirserver) return;
60 lprintf(7, "Unbinding from directory server\n");
61 ldap_unbind(dirserver);
68 * Create the root node. If it's already there, so what?
70 void CtdlCreateLdapRoot(void) {
72 char *objectClass_values[3];
73 LDAPMod dc, objectClass;
78 /* We just want the top-level dc, not the whole hierarchy */
79 strcpy(topdc, config.c_ldap_base_dn);
80 for (i=0; i<strlen(topdc); ++i) {
81 if (topdc[i] == ',') topdc[i] = 0;
83 for (i=0; i<strlen(topdc); ++i) {
84 if (topdc[i] == '=') strcpy(topdc, &topdc[i+1]);
87 /* Set up the transaction */
88 dc.mod_op = LDAP_MOD_ADD;
92 dc.mod_values = dc_values;
93 objectClass.mod_op = LDAP_MOD_ADD;
94 objectClass.mod_type = "objectClass";
95 objectClass_values[0] = "top";
96 objectClass_values[1] = "domain";
97 objectClass_values[2] = NULL;
98 objectClass.mod_values = objectClass_values;
100 mods[1] = &objectClass;
103 /* Perform the transaction */
104 lprintf(9, "Setting up Base DN node...\n");
105 begin_critical_section(S_LDAP);
106 i = ldap_add_s(dirserver, config.c_ldap_base_dn, mods);
107 end_critical_section(S_LDAP);
109 if (i != LDAP_SUCCESS) {
110 lprintf(3, "ldap_add_s() failed: %s (%d)\n",
111 ldap_err2string(i), i);
117 * Create an OU node representing a Citadel host.
119 void CtdlCreateHostOU(char *host) {
121 char *objectClass_values[3];
122 LDAPMod dc, objectClass;
127 /* The DN is this OU plus the base. */
128 snprintf(dn, sizeof dn, "ou=%s,%s", host, config.c_ldap_base_dn);
130 /* Set up the transaction */
131 dc.mod_op = LDAP_MOD_ADD;
135 dc.mod_values = dc_values;
136 objectClass.mod_op = LDAP_MOD_ADD;
137 objectClass.mod_type = "objectClass";
138 objectClass_values[0] = "top";
139 objectClass_values[1] = "organizationalUnit";
140 objectClass_values[2] = NULL;
141 objectClass.mod_values = objectClass_values;
143 mods[1] = &objectClass;
146 /* Perform the transaction */
147 lprintf(9, "Setting up Host OU node...\n");
148 begin_critical_section(S_LDAP);
149 i = ldap_add_s(dirserver, dn, mods);
150 end_critical_section(S_LDAP);
152 /* ignore the error -- it's ok if it already exists
153 if (i != LDAP_SUCCESS) {
154 lprintf(3, "ldap_add_s() failed: %s (%d)\n",
155 ldap_err2string(i), i);
167 void CtdlConnectToLdap(void) {
169 int ldap_version = 3;
171 lprintf(7, "Connecting to LDAP server %s:%d...\n",
172 config.c_ldap_host, config.c_ldap_port);
174 dirserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
175 if (dirserver == NULL) {
176 lprintf(3, "Could not connect to %s:%d : %s\n",
183 ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
185 lprintf(7, "Binding to %s\n", config.c_ldap_bind_dn);
187 i = ldap_simple_bind_s(dirserver,
188 config.c_ldap_bind_dn,
189 config.c_ldap_bind_pw
191 if (i != LDAP_SUCCESS) {
192 lprintf(3, "Cannot bind: %s (%d)\n", ldap_err2string(i), i);
193 dirserver = NULL; /* FIXME disconnect from ldap */
197 CtdlCreateLdapRoot();
203 * Write (add, or change if already exists) a directory entry to the
204 * LDAP server, based on the information supplied in a vCard.
206 void ctdl_vcard_to_ldap(struct CtdlMessage *msg) {
207 struct vCard *v = NULL;
210 LDAPMod **attrs = NULL;
216 if (dirserver == NULL) return;
217 if (msg == NULL) return;
218 if (msg->cm_fields['M'] == NULL) return;
219 if (msg->cm_fields['A'] == NULL) return;
220 if (msg->cm_fields['N'] == NULL) return;
222 /* First make sure the OU for the user's home Citadel host is created */
223 CtdlCreateHostOU(msg->cm_fields['N']);
225 /* Initialize variables */
226 strcpy(givenname, "_");
229 sprintf(this_dn, "cn=%s,ou=%s,%s",
232 config.c_ldap_base_dn
235 /* The first LDAP attribute will be an 'objectclass' list. Citadel
236 * doesn't do anything with this. It's just there for compatibility
240 attrs = mallok( (sizeof(LDAPMod *) * num_attrs) );
241 attrs[0] = mallok(sizeof(LDAPMod));
242 memset(attrs[0], 0, sizeof(LDAPMod));
243 attrs[0]->mod_op = LDAP_MOD_ADD;
244 attrs[0]->mod_type = "objectclass";
245 attrs[0]->mod_values = mallok(5 * sizeof(char *));
246 attrs[0]->mod_values[0] = strdoop("inetOrgPerson");
247 attrs[0]->mod_values[1] = strdoop("organizationalPerson");
248 attrs[0]->mod_values[2] = strdoop("person");
249 attrs[0]->mod_values[3] = strdoop("Top");
250 attrs[0]->mod_values[4] = NULL;
252 /* Add a "cn" (Common Name) attribute based on the user's screen name */
253 attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
254 attrs[num_attrs-1] = mallok(sizeof(LDAPMod));
255 memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
256 attrs[num_attrs-1]->mod_op = LDAP_MOD_ADD;
257 attrs[num_attrs-1]->mod_type = "cn";
258 attrs[num_attrs-1]->mod_values = mallok(2 * sizeof(char *));
259 attrs[num_attrs-1]->mod_values[0] = strdoop(msg->cm_fields['A']);
260 attrs[num_attrs-1]->mod_values[1] = NULL;
262 /* Convert the vCard fields to LDAP properties */
263 v = vcard_load(msg->cm_fields['M']);
264 if (v->numprops) for (i=0; i<(v->numprops); ++i) {
266 if (!strcasecmp(v->prop[i].name, "n")) {
267 extract_token(sn, v->prop[i].value, 0, ';');
268 extract_token(givenname, v->prop[i].value, 1, ';');
272 vcard_free(v); /* Don't need this anymore. */
274 /* "sn" (surname) based on info in vCard */
275 attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
276 attrs[num_attrs-1] = mallok(sizeof(LDAPMod));
277 memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
278 attrs[num_attrs-1]->mod_op = LDAP_MOD_ADD;
279 attrs[num_attrs-1]->mod_type = "sn";
280 attrs[num_attrs-1]->mod_values = mallok(2 * sizeof(char *));
281 attrs[num_attrs-1]->mod_values[0] = strdoop(sn);
282 attrs[num_attrs-1]->mod_values[1] = NULL;
284 /* "givenname" (first name) based on info in vCard */
285 attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
286 attrs[num_attrs-1] = mallok(sizeof(LDAPMod));
287 memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
288 attrs[num_attrs-1]->mod_op = LDAP_MOD_ADD;
289 attrs[num_attrs-1]->mod_type = "givenname";
290 attrs[num_attrs-1]->mod_values = mallok(2 * sizeof(char *));
291 attrs[num_attrs-1]->mod_values[0] = strdoop(givenname);
292 attrs[num_attrs-1]->mod_values[1] = NULL;
294 /* The last attribute must be a NULL one. */
295 attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
296 attrs[num_attrs - 1] = NULL;
298 lprintf(9, "this_dn: <%s>\n", this_dn);
300 lprintf(9, "Calling ldap_add_s()\n");
301 begin_critical_section(S_LDAP);
302 i = ldap_add_s(dirserver, this_dn, attrs);
303 end_critical_section(S_LDAP);
305 /* If the entry already exists, repopulate it instead */
306 if (i == LDAP_ALREADY_EXISTS) {
307 for (j=0; j<(num_attrs-1); ++j) {
308 attrs[j]->mod_op = LDAP_MOD_REPLACE;
310 lprintf(9, "Calling ldap_modify_s()\n");
311 begin_critical_section(S_LDAP);
312 i = ldap_modify_s(dirserver, this_dn, attrs);
313 end_critical_section(S_LDAP);
316 if (i != LDAP_SUCCESS) {
317 lprintf(3, "ldap_add_s() failed: %s (%d)\n",
318 ldap_err2string(i), i);
321 lprintf(9, "Freeing attributes\n");
322 /* Free the attributes */
323 for (i=0; i<num_attrs; ++i) {
324 if (attrs[i] != NULL) {
326 /* First, free the value strings */
327 if (attrs[i]->mod_values != NULL) {
328 for (j=0; attrs[i]->mod_values[j] != NULL; ++j) {
329 phree(attrs[i]->mod_values[j]);
333 /* Free the value strings pointer list */
334 if (attrs[i]->mod_values != NULL) {
335 phree(attrs[i]->mod_values);
338 /* Now free the LDAPMod struct itself. */
343 lprintf(9, "LDAP operation complete.\n");
347 #endif /* HAVE_LDAP */
351 * Initialize the LDAP connector module ... or don't, if we don't have LDAP.
353 char *serv_ldap_init(void)
356 CtdlRegisterCleanupHook(serv_ldap_cleanup);
358 if (strlen(config.c_ldap_host) > 0) {
362 #endif /* HAVE_LDAP */