]> code.citadel.org Git - citadel.git/blob - citadel/serv_ldap.c
*** empty log message ***
[citadel.git] / citadel / serv_ldap.c
1 /*
2  * $Id$
3  *
4  * A module which implements the LDAP connector for Citadel.
5  *
6  */
7
8 #include "sysdep.h"
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <stdio.h>
12 #include <fcntl.h>
13 #include <signal.h>
14 #include <pwd.h>
15 #include <errno.h>
16 #include <sys/types.h>
17
18 #if TIME_WITH_SYS_TIME
19 # include <sys/time.h>
20 # include <time.h>
21 #else
22 # if HAVE_SYS_TIME_H
23 #  include <sys/time.h>
24 # else
25 #  include <time.h>
26 # endif
27 #endif
28
29 #include <sys/wait.h>
30 #include <string.h>
31 #include <limits.h>
32 #include "citadel.h"
33 #include "server.h"
34 #include "sysdep_decls.h"
35 #include "citserver.h"
36 #include "support.h"
37 #include "config.h"
38 #include "serv_extensions.h"
39 #include "room_ops.h"
40 #include "policy.h"
41 #include "database.h"
42 #include "msgbase.h"
43 #include "serv_ldap.h"
44 #include "vcard.h"
45 #include "tools.h"
46
47 #ifdef HAVE_LDAP
48
49 #include <ldap.h>
50
51 LDAP *dirserver = NULL;
52
53 /*
54  * LDAP connector cleanup function
55  */
56 void serv_ldap_cleanup(void)
57 {
58         if (!dirserver) return;
59
60         lprintf(7, "Unbinding from directory server\n");
61         ldap_unbind(dirserver);
62         dirserver = NULL;
63 }
64
65
66
67 /*
68  * Create the root node.  If it's already there, so what?
69  */
70 void CtdlCreateLdapRoot(void) {
71         char *dc_values[2];
72         char *objectClass_values[3];
73         LDAPMod dc, objectClass;
74         LDAPMod *mods[3];
75         char topdc[SIZ];
76         int i;
77
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;
82         }
83         for (i=0; i<strlen(topdc); ++i) {
84                 if (topdc[i] == '=') strcpy(topdc, &topdc[i+1]);
85         }
86
87         /* Set up the transaction */
88         dc.mod_op               = LDAP_MOD_ADD;
89         dc.mod_type             = "dc";
90         dc_values[0]            = topdc;
91         dc_values[1]            = NULL;
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;
99         mods[0] = &dc;
100         mods[1] = &objectClass;
101         mods[2] = NULL;
102
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);
108
109         if (i != LDAP_SUCCESS) {
110                 lprintf(3, "ldap_add_s() failed: %s (%d)\n",
111                         ldap_err2string(i), i);
112         }
113 }
114
115
116 /*
117  * Create an OU node representing a Citadel host.
118  */
119 void CtdlCreateHostOU(char *host) {
120         char *dc_values[2];
121         char *objectClass_values[3];
122         LDAPMod dc, objectClass;
123         LDAPMod *mods[3];
124         int i;
125         char dn[SIZ];
126
127         /* The DN is this OU plus the base. */
128         snprintf(dn, sizeof dn, "ou=%s,%s", host, config.c_ldap_base_dn);
129
130         /* Set up the transaction */
131         dc.mod_op               = LDAP_MOD_ADD;
132         dc.mod_type             = "ou";
133         dc_values[0]            = host;
134         dc_values[1]            = NULL;
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;
142         mods[0] = &dc;
143         mods[1] = &objectClass;
144         mods[2] = NULL;
145
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);
151
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);
156         }
157         */
158 }
159
160
161
162
163
164
165
166
167 void CtdlConnectToLdap(void) {
168         int i;
169         int ldap_version = 3;
170
171         lprintf(7, "Connecting to LDAP server %s:%d...\n",
172                 config.c_ldap_host, config.c_ldap_port);
173
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",
177                         config.c_ldap_host,
178                         config.c_ldap_port,
179                         strerror(errno));
180                 return;
181         }
182
183         ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
184
185         lprintf(7, "Binding to %s\n", config.c_ldap_bind_dn);
186
187         i = ldap_simple_bind_s(dirserver,
188                                 config.c_ldap_bind_dn,
189                                 config.c_ldap_bind_pw
190         );
191         if (i != LDAP_SUCCESS) {
192                 lprintf(3, "Cannot bind: %s (%d)\n", ldap_err2string(i), i);
193                 dirserver = NULL;       /* FIXME disconnect from ldap */
194                 return;
195         }
196
197         CtdlCreateLdapRoot();
198 }
199
200
201
202 /*
203  * Write (add, or change if already exists) a directory entry to the
204  * LDAP server, based on the information supplied in a vCard.
205  */
206 void ctdl_vcard_to_ldap(struct CtdlMessage *msg) {
207         struct vCard *v = NULL;
208         int i, j;
209         char this_dn[SIZ];
210         LDAPMod **attrs = NULL;
211         int num_attrs = 0;
212
213         char givenname[SIZ];
214         char sn[SIZ];
215
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;
221
222         /* First make sure the OU for the user's home Citadel host is created */
223         CtdlCreateHostOU(msg->cm_fields['N']);
224
225         /* Initialize variables */
226         strcpy(givenname, "_");
227         strcpy(sn, "_");
228
229         sprintf(this_dn, "cn=%s,ou=%s,%s",
230                 msg->cm_fields['A'],
231                 msg->cm_fields['N'],
232                 config.c_ldap_base_dn
233         );
234
235         /* The first LDAP attribute will be an 'objectclass' list.  Citadel
236          * doesn't do anything with this.  It's just there for compatibility
237          * with Kolab.
238          */
239         num_attrs = 1;
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;
251
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;
261         
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) {
265
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, ';');
269                 }
270
271         }
272         vcard_free(v);  /* Don't need this anymore. */
273
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;
283         
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;
293
294         /* The last attribute must be a NULL one. */
295         attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
296         attrs[num_attrs - 1] = NULL;
297         
298         lprintf(9, "this_dn: <%s>\n", this_dn);
299
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);
304
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;
309                 }
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);
314         }
315
316         if (i != LDAP_SUCCESS) {
317                 lprintf(3, "ldap_add_s() failed: %s (%d)\n",
318                         ldap_err2string(i), i);
319         }
320
321         lprintf(9, "Freeing attributes\n");
322         /* Free the attributes */
323         for (i=0; i<num_attrs; ++i) {
324                 if (attrs[i] != NULL) {
325
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]);
330                                 }
331                         }
332
333                         /* Free the value strings pointer list */       
334                         if (attrs[i]->mod_values != NULL) {
335                                 phree(attrs[i]->mod_values);
336                         }
337
338                         /* Now free the LDAPMod struct itself. */
339                         phree(attrs[i]);
340                 }
341         }
342         phree(attrs);
343         lprintf(9, "LDAP operation complete.\n");
344 }
345
346
347 #endif                          /* HAVE_LDAP */
348
349
350 /*
351  * Initialize the LDAP connector module ... or don't, if we don't have LDAP.
352  */
353 char *serv_ldap_init(void)
354 {
355 #ifdef HAVE_LDAP
356         CtdlRegisterCleanupHook(serv_ldap_cleanup);
357
358         if (strlen(config.c_ldap_host) > 0) {
359                 CtdlConnectToLdap();
360         }
361
362 #endif                          /* HAVE_LDAP */
363         return "$Id$";
364 }