]> code.citadel.org Git - citadel.git/blob - citadel/modules/ldap/serv_ldap.c
LDAP module no longer allows duplicate values for the same attribute,
[citadel.git] / citadel / modules / ldap / 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 "citserver.h"
35 #include "support.h"
36 #include "config.h"
37 #include "room_ops.h"
38 #include "policy.h"
39 #include "database.h"
40 #include "msgbase.h"
41 #include "serv_ldap.h"
42 #include "tools.h"
43
44
45 #include "ctdl_module.h"
46
47
48
49 #ifdef HAVE_LDAP
50
51 #define LDAP_DEPRECATED 1       /* to stop warnings with newer libraries */
52
53 #include <ldap.h>
54
55 LDAP *dirserver = NULL;
56 int ldap_time_disconnect = 0;
57
58
59 /*
60  * LDAP connector cleanup function
61  */
62 void serv_ldap_cleanup(void)
63 {
64         if (dirserver)
65         {
66                 lprintf(CTDL_INFO, "LDAP: Unbinding from directory server\n");
67                 ldap_unbind(dirserver);
68         }
69         dirserver = NULL;
70         ldap_time_disconnect=0;
71 }
72
73
74
75
76
77 int CtdlConnectToLdap(void) {
78         int i;
79         int ldap_version = 3;
80
81         if (ldap_time_disconnect && dirserver)
82         {       // Already connected
83                 ldap_time_disconnect=5; // reset the timer.
84                 return 0;
85         }
86                 
87         lprintf(CTDL_INFO, "LDAP: Connecting to LDAP server %s:%d...\n",
88         config.c_ldap_host, config.c_ldap_port);
89
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",
93                         config.c_ldap_host,
94                         config.c_ldap_port,
95                         strerror(errno));
96                 aide_message(strerror(errno), "LDAP: Could not connect to server.");
97                 return -1;
98         }
99
100         ldap_set_option(dirserver, LDAP_OPT_PROTOCOL_VERSION, &ldap_version);
101
102         lprintf(CTDL_INFO, "LDAP: Binding to %s\n", config.c_ldap_bind_dn);
103
104         i = ldap_simple_bind_s(dirserver,
105                                 config.c_ldap_bind_dn,
106                                 config.c_ldap_bind_pw
107         );
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");
112                 return -1;
113         }
114         ldap_time_disconnect=5;
115         return 0;
116 }
117
118
119
120 /*
121  * Create the root node.  If it's already there, so what?
122  */
123 void CtdlCreateLdapRoot(void) {
124         char *dc_values[2];
125         char *objectClass_values[3];
126         LDAPMod dc, objectClass;
127         LDAPMod *mods[3];
128         char topdc[SIZ];
129         int i;
130
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] == ',') {
135                         topdc[i] = 0;
136                         break;
137                 }
138         }
139         for (i=0; topdc[i]; ++i) {
140                 if (topdc[i] == '=') strcpy(topdc, &topdc[i+1]);
141         }
142
143         /* Set up the transaction */
144         dc.mod_op               = LDAP_MOD_ADD;
145         dc.mod_type             = "dc";
146         dc_values[0]            = topdc;
147         dc_values[1]            = NULL;
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;
155         mods[0] = &dc;
156         mods[1] = &objectClass;
157         mods[2] = NULL;
158
159         /* Perform the transaction */
160         lprintf(CTDL_DEBUG, "LDAP: Setting up Base DN node...\n");
161
162         begin_critical_section(S_LDAP);
163         if (CtdlConnectToLdap())
164         {
165                 end_critical_section(S_LDAP);
166                 return;
167         }
168         i = ldap_add_s(dirserver, config.c_ldap_base_dn, mods);
169         end_critical_section(S_LDAP);
170
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");
173         }
174         else if (i != LDAP_SUCCESS) {
175                 lprintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
176                         ldap_err2string(i), i);
177         }
178 }
179
180
181 /*
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
185  */
186 int CtdlCreateLdapHostOU(char *cn, char *host, void **object) {
187         char *dc_values[2];
188         char *objectClass_values[3];
189         LDAPMod dc, objectClass;
190         LDAPMod *mods[3];
191         int i;
192         char dn[SIZ];
193
194         /* The DN is this OU plus the base. */
195         snprintf(dn, sizeof dn, "ou=%s,%s", host, config.c_ldap_base_dn);
196
197         /* Set up the transaction */
198         dc.mod_op               = LDAP_MOD_ADD;
199         dc.mod_type             = "ou";
200         dc_values[0]            = host;
201         dc_values[1]            = NULL;
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;
209         mods[0] = &dc;
210         mods[1] = &objectClass;
211         mods[2] = NULL;
212
213         /* Perform the transaction */
214         lprintf(CTDL_DEBUG, "LDAP: Setting up Host OU node...\n");
215         
216         begin_critical_section(S_LDAP);
217         if (CtdlConnectToLdap())
218         {
219                 end_critical_section(S_LDAP);
220                 return -1;
221         }
222         i = ldap_add_s(dirserver, dn, mods);
223         end_critical_section(S_LDAP);
224
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");
227         }
228         else if (i != LDAP_SUCCESS) {
229                 lprintf(CTDL_CRIT, "LDAP: ldap_add_s() failed: %s (%d)\n",
230                         ldap_err2string(i), i);
231                 return -1;
232         }
233         return 0;
234 }
235
236
237
238
239
240
241 /*
242  * Create a base LDAP object for the interface
243  */
244  
245 int CtdlCreateLdapObject(char *cn, char *ou, void **object)
246 {
247         // We do nothing here, this just gets the base structure created by the interface.
248         lprintf (CTDL_DEBUG, "LDAP: Created ldap object\n");
249         return 0;
250 }
251
252
253 /*
254  * Add an attribute to the ldap object
255  */
256  
257 int CtdlAddLdapAttr(char *cn, char *ou, void **object)
258 {
259         LDAPMod **attrs ;
260         int num_attrs = 0;
261         int num_values = 0;
262         int cur_attr;
263         
264         
265         lprintf (CTDL_DEBUG, "LDAP: Adding ldap attribute name:\"%s\" value:\"%s\"\n", cn, ou);
266         
267         attrs = *object;
268         if (attrs)
269         {
270                 while (attrs[num_attrs])
271                         num_attrs++;
272         }
273         
274         for (cur_attr = 0; cur_attr < num_attrs ; cur_attr++)
275         {
276                 if (!strcmp(attrs[cur_attr]->mod_type, cn))
277                 {       // Adding a value to the attribute
278                         if (attrs[cur_attr]->mod_values)
279                         {
280                                 while (attrs[cur_attr]->mod_values[num_values])
281                                 {
282                                         if (!strcmp(ou, attrs[cur_attr]->mod_values[num_values]))
283                                         {
284                                                 lprintf(CTDL_DEBUG, "LDAP: Ignoring duplicate attribute/value pair\n");
285                                                 return 0;
286                                         }
287                                         num_values++;
288                                 }
289                         }
290                         attrs[cur_attr]->mod_values = realloc(attrs[cur_attr]->mod_values, (num_values + 2) * (sizeof(char *)));
291                         attrs[cur_attr]->mod_values[num_values] = strdup(ou);
292                         attrs[cur_attr]->mod_values[num_values+1] = NULL;
293                         return 0;
294                 }
295         }
296         if (num_attrs)
297                 attrs = realloc(attrs, (sizeof(LDAPMod *)) * (num_attrs + 2));
298         else
299                 attrs = malloc((sizeof(LDAPMod *)) * (num_attrs + 2));
300         attrs[num_attrs] = malloc(sizeof(LDAPMod));
301         memset(attrs[num_attrs], 0, sizeof(LDAPMod));
302         attrs[num_attrs+1] = NULL;
303         attrs[num_attrs]->mod_op        = LDAP_MOD_ADD;
304         attrs[num_attrs]->mod_type      = strdup(cn);
305         attrs[num_attrs]->mod_values    = malloc(2 * sizeof(char *));
306         attrs[num_attrs]->mod_values[0] = strdup(ou);
307         attrs[num_attrs]->mod_values[1] = NULL;
308         *object = attrs;
309         return 0;
310 }
311
312
313 /*
314  * SAve the object to the LDAP server
315  */
316 int CtdlSaveLdapObject(char *cn, char *ou, void **object)
317 {
318         int i, j;
319         
320         char this_dn[SIZ];
321         LDAPMod **attrs ;
322         int num_attrs = 0;
323         int count = 0;
324         
325         if (dirserver == NULL) return -1;
326         if (cn == NULL) return -1;
327         
328         sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
329         
330         lprintf(CTDL_INFO, "LDAP: Calling ldap_add_s() for dn of '%s'\n", this_dn);
331
332         /* The last attribute must be a NULL one. */
333         attrs = (LDAPMod **)*object;
334         if (attrs)
335         {
336                 while (attrs[num_attrs])
337                 {
338                         count = 0;
339                         while (attrs[num_attrs]->mod_values[count])
340                         {
341                                 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]);
342                                 count++;
343                         }
344                         num_attrs++;
345                 }
346         }
347         else
348         {
349                 lprintf(CTDL_ERR, "LDAP: no attributes in CtdlSaveLdapObject\n");
350                 return -1;
351         }
352         
353         begin_critical_section(S_LDAP);
354         if (CtdlConnectToLdap())
355         {
356                 end_critical_section(S_LDAP);
357                 return -1;
358         }
359         
360         i = ldap_add_s(dirserver, this_dn, attrs);
361         
362         if (i == LDAP_SERVER_DOWN)
363         {
364                 aide_message("The LDAP server appears to be down.\nThe save to LDAP did not occurr.\n", "LDAP: save failed");
365                 end_critical_section(S_LDAP);
366                 return -1;
367         }
368
369         /* If the entry already exists, repopulate it instead */
370         if (i == LDAP_ALREADY_EXISTS) {
371                 for (j=0; j<(num_attrs); ++j) {
372                         attrs[j]->mod_op = LDAP_MOD_REPLACE;
373                 }
374                 lprintf(CTDL_INFO, "LDAP: Calling ldap_modify_s() for dn of '%s'\n", this_dn);
375                 i = ldap_modify_s(dirserver, this_dn, attrs);
376         }
377
378         if (i != LDAP_SUCCESS) {
379                 lprintf(CTDL_ERR, "LDAP: ldap_add_s() failed: %s (%d)\n",
380                         ldap_err2string(i), i);
381                 aide_message("The LDAP server refused the save command.\nDid you update the schema?\n", "LDAP: save failed (schema?)");
382                 end_critical_section(S_LDAP);
383                 return -1;
384         }
385         end_critical_section(S_LDAP);
386         return 0;
387 }
388
389
390 /*
391  * Free the object
392  */
393 int CtdlFreeLdapObject(char *cn, char *ou, void **object)
394 {
395         int i, j;
396         
397         LDAPMod **attrs ;
398         int num_attrs = 0;
399
400         attrs = (LDAPMod **)*object;
401         if (attrs)
402         {
403                 while (attrs[num_attrs])
404                         num_attrs++;
405         }
406
407         lprintf(CTDL_DEBUG, "LDAP: Freeing attributes\n");
408         /* Free the attributes */
409         for (i=0; i<num_attrs; ++i) {
410                 if (attrs[i] != NULL) {
411
412                         /* First, free the value strings */
413                         if (attrs[i]->mod_values != NULL) {
414                                 for (j=0; attrs[i]->mod_values[j] != NULL; ++j) {
415                                         free(attrs[i]->mod_values[j]);
416                                 }
417                         }
418
419                         /* Free the value strings pointer list */       
420                         if (attrs[i]->mod_values != NULL) {
421                                 free(attrs[i]->mod_values);
422                         }
423
424                         /* Now free the LDAPMod struct itself. */
425                         free(attrs[i]);
426                 }
427         }
428         free(attrs[i]);
429         free(attrs);
430         *object = NULL;
431         return 0;
432 }
433
434
435 /*
436  * Delete a record from the LDAP
437  *
438  * parameter object not used here, present for hook interface compatability
439  */
440 int CtdlDeleteFromLdap(char *cn, char *ou, void **object)
441 {
442         int i;
443         
444         char this_dn[SIZ];
445         
446         if (dirserver == NULL) return -1;
447         if (ou == NULL) return -1;
448         if (cn == NULL) return -1;
449         
450         sprintf(this_dn, "%s,%s", cn, config.c_ldap_base_dn);
451         
452         lprintf(CTDL_DEBUG, "LDAP: Calling ldap_delete_s()\n");
453         
454         begin_critical_section(S_LDAP);
455         if (CtdlConnectToLdap())
456         {
457                 end_critical_section(S_LDAP);
458                 return -1;
459         }
460         
461         i = ldap_delete_s(dirserver, this_dn);
462         
463         if (i == LDAP_SERVER_DOWN)
464         {
465                 end_critical_section(S_LDAP);
466                 aide_message("The LDAP server appears to be down.\nThe delete from LDAP did not occurr.\n", "LDAP: delete failed");
467                 return -1;
468         }
469
470         if (i != LDAP_SUCCESS) {
471                 lprintf(CTDL_ERR, "LDAP: ldap_delete_s() failed: %s (%d)\n",
472                         ldap_err2string(i), i);
473                 end_critical_section(S_LDAP);
474                 aide_message(ldap_err2string(i), "LDAP: delete failed");
475                 return -1;
476         }
477         end_critical_section(S_LDAP);
478         return 0;
479 }
480
481
482
483
484 void ldap_disconnect_timer(void)
485 {
486         begin_critical_section(S_LDAP);
487         if(ldap_time_disconnect)
488         {
489                 ldap_time_disconnect--;
490                 end_critical_section(S_LDAP);
491                 return;
492         }
493         serv_ldap_cleanup();
494         end_critical_section(S_LDAP);
495 }
496
497
498 #endif                          /* HAVE_LDAP */
499
500
501 /*
502  * Initialize the LDAP connector module ... or don't, if we don't have LDAP.
503  */
504 CTDL_MODULE_INIT(ldap)
505 {
506 #ifdef HAVE_LDAP
507         if (!IsEmptyStr(config.c_ldap_base_dn))
508         {
509                 CtdlRegisterCleanupHook(serv_ldap_cleanup);
510                 CtdlRegisterSessionHook(ldap_disconnect_timer, EVT_TIMER);
511                 CtdlRegisterDirectoryServiceFunc(CtdlDeleteFromLdap, DIRECTORY_USER_DEL, "ldap");
512                 CtdlRegisterDirectoryServiceFunc(CtdlCreateLdapHostOU, DIRECTORY_CREATE_HOST, "ldap");
513                 CtdlRegisterDirectoryServiceFunc(CtdlCreateLdapObject, DIRECTORY_CREATE_OBJECT, "ldap");
514                 CtdlRegisterDirectoryServiceFunc(CtdlAddLdapAttr, DIRECTORY_ATTRIB_ADD, "ldap");
515                 CtdlRegisterDirectoryServiceFunc(CtdlSaveLdapObject, DIRECTORY_SAVE_OBJECT, "ldap");
516                 CtdlRegisterDirectoryServiceFunc(CtdlFreeLdapObject, DIRECTORY_FREE_OBJECT, "ldap");
517                 CtdlCreateLdapRoot();
518         }
519
520 #endif                          /* HAVE_LDAP */
521
522         /* return our Subversion id for the Log */
523         return "$Id$";
524 }