]> code.citadel.org Git - citadel.git/blob - citadel/serv_ldap.c
* Got Citadel talking to LDAP. Still requires manual creation of schema
[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 #endif                          /* HAVE_LDAP */
66
67
68 void CtdlConnectToLdap(void) {
69         int i;
70         int ldap_version = 3;
71
72         lprintf(7, "Connecting to LDAP server %s:%d...\n",
73                 config.c_ldap_host, config.c_ldap_port);
74
75         dirserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
76         if (dirserver == NULL) {
77                 lprintf(3, "Could not connect to %s:%d : %s\n",
78                         config.c_ldap_host,
79                         config.c_ldap_port,
80                         strerror(errno));
81                 return;
82         }
83
84         ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
85
86         lprintf(7, "Binding to %s\n", config.c_ldap_bind_dn);
87
88         i = ldap_simple_bind_s(dirserver,
89                                 config.c_ldap_bind_dn,
90                                 config.c_ldap_bind_pw
91         );
92         if (i != LDAP_SUCCESS) {
93                 lprintf(3, "Cannot bind: %s (%d)\n", ldap_err2string(i), i);
94                 dirserver = NULL;       /* FIXME disconnect from ldap */
95         }
96 }
97
98
99
100 /*
101  * Write (add, or change if already exists) a directory entry to the
102  * LDAP server, based on the information supplied in a vCard.
103  */
104 void ctdl_vcard_to_ldap(struct CtdlMessage *msg) {
105         struct vCard *v = NULL;
106         int i, j;
107         char this_dn[SIZ];
108         LDAPMod **attrs = NULL;
109         int num_attrs = 0;
110
111         char givenname[SIZ];
112         char sn[SIZ];
113
114         if (dirserver == NULL) return;
115         if (msg == NULL) return;
116         if (msg->cm_fields['M'] == NULL) return;
117         if (msg->cm_fields['A'] == NULL) return;
118         if (msg->cm_fields['N'] == NULL) return;
119
120         /* Initialize variables */
121         strcpy(givenname, "_");
122         strcpy(sn, "_");
123
124         sprintf(this_dn, "cn=%s,ou=%s,%s",
125                 msg->cm_fields['A'],
126                 msg->cm_fields['N'],
127                 config.c_ldap_base_dn
128         );
129
130         /* The first LDAP attribute will be an 'objectclass' list.  Citadel
131          * doesn't do anything with this.  It's just there for compatibility
132          * with Kolab.
133          */
134         num_attrs = 1;
135         attrs = mallok( (sizeof(LDAPMod *) * num_attrs) );
136         attrs[0] = mallok(sizeof(LDAPMod));
137         memset(attrs[0], 0, sizeof(LDAPMod));
138         attrs[0]->mod_op        = LDAP_MOD_ADD;
139         attrs[0]->mod_type      = "objectclass";
140         attrs[0]->mod_values    = mallok(5 * sizeof(char *));
141         attrs[0]->mod_values[0] = strdoop("inetOrgPerson");
142         attrs[0]->mod_values[1] = strdoop("organizationalPerson");
143         attrs[0]->mod_values[2] = strdoop("person");
144         attrs[0]->mod_values[3] = strdoop("Top");
145         attrs[0]->mod_values[4] = NULL;
146
147         /* Add a "cn" (Common Name) attribute based on the user's screen name */
148         attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
149         attrs[num_attrs-1] = mallok(sizeof(LDAPMod));
150         memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
151         attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
152         attrs[num_attrs-1]->mod_type            = "cn";
153         attrs[num_attrs-1]->mod_values          = mallok(2 * sizeof(char *));
154         attrs[num_attrs-1]->mod_values[0]       = strdoop(msg->cm_fields['A']);
155         attrs[num_attrs-1]->mod_values[1]       = NULL;
156         
157         /* Convert the vCard fields to LDAP properties */
158         v = vcard_load(msg->cm_fields['M']);
159         if (v->numprops) for (i=0; i<(v->numprops); ++i) {
160
161                 if (!strcasecmp(v->prop[i].name, "n")) {
162                         extract_token(sn,               v->prop[i].value, 0, ';');
163                         extract_token(givenname,        v->prop[i].value, 1, ';');
164                 }
165
166         }
167         vcard_free(v);  /* Don't need this anymore. */
168
169         /* "sn" (surname) based on info in vCard */
170         attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
171         attrs[num_attrs-1] = mallok(sizeof(LDAPMod));
172         memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
173         attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
174         attrs[num_attrs-1]->mod_type            = "sn";
175         attrs[num_attrs-1]->mod_values          = mallok(2 * sizeof(char *));
176         attrs[num_attrs-1]->mod_values[0]       = strdoop(sn);
177         attrs[num_attrs-1]->mod_values[1]       = NULL;
178         
179         /* "givenname" (first name) based on info in vCard */
180         attrs = reallok(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
181         attrs[num_attrs-1] = mallok(sizeof(LDAPMod));
182         memset(attrs[num_attrs-1], 0, sizeof(LDAPMod));
183         attrs[num_attrs-1]->mod_op              = LDAP_MOD_ADD;
184         attrs[num_attrs-1]->mod_type            = "givenname";
185         attrs[num_attrs-1]->mod_values          = mallok(2 * sizeof(char *));
186         attrs[num_attrs-1]->mod_values[0]       = strdoop(givenname);
187         attrs[num_attrs-1]->mod_values[1]       = NULL;
188
189         /* The last attribute must be a NULL one. */
190         attrs = realloc(attrs, (sizeof(LDAPMod *) * ++num_attrs) );
191         attrs[num_attrs - 1] = NULL;
192         
193         lprintf(9, "this_dn: <%s>\n", this_dn);
194
195         lprintf(9, "Calling ldap_add_s()\n");
196         begin_critical_section(S_LDAP);
197         i = ldap_add_s(dirserver, this_dn, attrs);
198         end_critical_section(S_LDAP);
199
200         /* If the entry already exists, repopulate it instead */
201         if (i == LDAP_ALREADY_EXISTS) {
202                 for (j=0; j<(num_attrs-1); ++j) {
203                         attrs[j]->mod_op = LDAP_MOD_REPLACE;
204                 }
205                 lprintf(9, "Calling ldap_modify_s()\n");
206                 begin_critical_section(S_LDAP);
207                 i = ldap_modify_s(dirserver, this_dn, attrs);
208                 end_critical_section(S_LDAP);
209         }
210
211         if (i != LDAP_SUCCESS) {
212                 lprintf(3, "ldap_add_s() failed: %s (%d)\n",
213                         ldap_err2string(i), i);
214         }
215
216         lprintf(9, "Freeing attributes\n");
217         /* Free the attributes */
218         for (i=0; i<num_attrs; ++i) {
219                 if (attrs[i] != NULL) {
220
221                         /* First, free the value strings */
222                         if (attrs[i]->mod_values != NULL) {
223                                 for (j=0; attrs[i]->mod_values[j] != NULL; ++j) {
224                                         phree(attrs[i]->mod_values[j]);
225                                 }
226                         }
227
228                         /* Free the value strings pointer list */       
229                         if (attrs[i]->mod_values != NULL) {
230                                 phree(attrs[i]->mod_values);
231                         }
232
233                         /* Now free the LDAPMod struct itself. */
234                         phree(attrs[i]);
235                 }
236         }
237         phree(attrs);
238         lprintf(9, "LDAP operation complete.\n");
239 }
240
241
242
243
244 /*
245  * Initialize the LDAP connector module ... or don't, if we don't have LDAP.
246  */
247 char *serv_ldap_init(void)
248 {
249 #ifdef HAVE_LDAP
250         CtdlRegisterCleanupHook(serv_ldap_cleanup);
251
252         if (strlen(config.c_ldap_host) > 0) {
253                 CtdlConnectToLdap();
254         }
255
256 #endif                          /* HAVE_LDAP */
257         return "$Id$";
258 }