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