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