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