Updated the copyright declaration in several modules, removing any language which...
[citadel.git] / citadel / ldap.c
1 /*
2  * These functions implement the portions of AUTHMODE_LDAP and AUTHMODE_LDAP_AD which
3  * actually speak to the LDAP server.
4  *
5  * Copyright (c) 2011 by Art Cancro and the citadel.org development team.
6  *
7  * This program is open source software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License, version 3.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  */
15
16 int ctdl_require_ldap_version = 3;
17
18 #include "sysdep.h"
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <stdio.h>
23 #include <fcntl.h>
24 #include <signal.h>
25 #include <pwd.h>
26 #include <ctype.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #ifdef HAVE_SYS_STAT_H
30 #include <sys/stat.h>
31 #endif
32
33 #if TIME_WITH_SYS_TIME
34 # include <sys/time.h>
35 # include <time.h>
36 #else
37 # if HAVE_SYS_TIME_H
38 #  include <sys/time.h>
39 # else
40 #  include <time.h>
41 # endif
42 #endif
43
44 #include <string.h>
45 #include <limits.h>
46 #include <libcitadel.h>
47 #include "auth.h"
48 #include "citadel.h"
49 #include "server.h"
50 #include "database.h"
51 #include "sysdep_decls.h"
52 #include "support.h"
53 #include "room_ops.h"
54 #include "file_ops.h"
55 #include "control.h"
56 #include "msgbase.h"
57 #include "config.h"
58 #include "citserver.h"
59 #include "citadel_dirs.h"
60 #include "genstamp.h"
61 #include "threads.h"
62 #include "citadel_ldap.h"
63 #include "ctdl_module.h"
64 #include "user_ops.h"
65
66 #ifdef HAVE_LDAP
67 #define LDAP_DEPRECATED 1       /* Suppress libldap's warning that we are using deprecated API calls */
68 #include <ldap.h>
69
70 int CtdlTryUserLDAP(char *username,
71                 char *found_dn, int found_dn_size,
72                 char *fullname, int fullname_size,
73                 uid_t *uid)
74 {
75         LDAP *ldserver = NULL;
76         int i;
77         LDAPMessage *search_result = NULL;
78         LDAPMessage *entry = NULL;
79         char searchstring[1024];
80         struct timeval tv;
81         char **values;
82         char *user_dn = NULL;
83
84         if (fullname) safestrncpy(fullname, username, fullname_size);
85
86         ldserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
87         if (ldserver == NULL) {
88                 syslog(LOG_ALERT, "LDAP: Could not connect to %s:%d : %s\n",
89                         config.c_ldap_host, config.c_ldap_port,
90                         strerror(errno)
91                 );
92                 return(errno);
93         }
94
95         ldap_set_option(ldserver, LDAP_OPT_PROTOCOL_VERSION, &ctdl_require_ldap_version);
96         ldap_set_option(ldserver, LDAP_OPT_REFERRALS, (void *)LDAP_OPT_OFF);
97
98         striplt(config.c_ldap_bind_dn);
99         striplt(config.c_ldap_bind_pw);
100         syslog(LOG_DEBUG, "LDAP bind DN: %s\n", config.c_ldap_bind_dn);
101         i = ldap_simple_bind_s(ldserver,
102                 (!IsEmptyStr(config.c_ldap_bind_dn) ? config.c_ldap_bind_dn : NULL),
103                 (!IsEmptyStr(config.c_ldap_bind_pw) ? config.c_ldap_bind_pw : NULL)
104         );
105         if (i != LDAP_SUCCESS) {
106                 syslog(LOG_ALERT, "LDAP: Cannot bind: %s (%d)\n", ldap_err2string(i), i);
107                 return(i);
108         }
109
110         tv.tv_sec = 10;
111         tv.tv_usec = 0;
112
113         if (config.c_auth_mode == AUTHMODE_LDAP_AD) {
114                 sprintf(searchstring, "(sAMAccountName=%s)", username);
115         }
116         else {
117                 sprintf(searchstring, "(&(objectclass=posixAccount)(uid=%s))", username);
118         }
119
120         syslog(LOG_DEBUG, "LDAP search: %s\n", searchstring);
121         (void) ldap_search_ext_s(
122                 ldserver,                                       /* ld                           */
123                 config.c_ldap_base_dn,                          /* base                         */
124                 LDAP_SCOPE_SUBTREE,                             /* scope                        */
125                 searchstring,                                   /* filter                       */
126                 NULL,                                           /* attrs (all attributes)       */
127                 0,                                              /* attrsonly (attrs + values)   */
128                 NULL,                                           /* serverctrls (none)           */
129                 NULL,                                           /* clientctrls (none)           */
130                 &tv,                                            /* timeout                      */
131                 1,                                              /* sizelimit (1 result max)     */
132                 &search_result                                  /* res                          */
133         );
134
135         /* Ignore the return value of ldap_search_ext_s().  Sometimes it returns an error even when
136          * the search succeeds.  Instead, we check to see whether search_result is still NULL.
137          */
138         if (search_result == NULL) {
139                 syslog(LOG_DEBUG, "LDAP search: zero results were returned\n");
140                 ldap_unbind(ldserver);
141                 return(2);
142         }
143
144         /* At this point we've got at least one result from our query.  If there are multiple
145          * results, we still only look at the first one.
146          */
147         entry = ldap_first_entry(ldserver, search_result);
148         if (entry) {
149
150                 user_dn = ldap_get_dn(ldserver, entry);
151                 if (user_dn) {
152                         syslog(LOG_DEBUG, "dn = %s\n", user_dn);
153                 }
154
155                 if (config.c_auth_mode == AUTHMODE_LDAP_AD) {
156                         values = ldap_get_values(ldserver, search_result, "displayName");
157                         if (values) {
158                                 if (values[0]) {
159                                         if (fullname) safestrncpy(fullname, values[0], fullname_size);
160                                         syslog(LOG_DEBUG, "displayName = %s\n", values[0]);
161                                 }
162                                 ldap_value_free(values);
163                         }
164                 }
165                 else {
166                         values = ldap_get_values(ldserver, search_result, "cn");
167                         if (values) {
168                                 if (values[0]) {
169                                         if (fullname) safestrncpy(fullname, values[0], fullname_size);
170                                         syslog(LOG_DEBUG, "cn = %s\n", values[0]);
171                                 }
172                                 ldap_value_free(values);
173                         }
174                 }
175
176                 if (config.c_auth_mode == AUTHMODE_LDAP_AD) {
177                         values = ldap_get_values(ldserver, search_result, "objectGUID");
178                         if (values) {
179                                 if (values[0]) {
180                                         if (uid != NULL) {
181                                                 *uid = abs(HashLittle(values[0], strlen(values[0])));
182                                                 syslog(LOG_DEBUG, "uid hashed from objectGUID = %d\n", *uid);
183                                         }
184                                 }
185                                 ldap_value_free(values);
186                         }
187                 }
188                 else {
189                         values = ldap_get_values(ldserver, search_result, "uidNumber");
190                         if (values) {
191                                 if (values[0]) {
192                                         syslog(LOG_DEBUG, "uidNumber = %s\n", values[0]);
193                                         if (uid != NULL) {
194                                                 *uid = atoi(values[0]);
195                                         }
196                                 }
197                                 ldap_value_free(values);
198                         }
199                 }
200
201         }
202
203         /* free the results */
204         ldap_msgfree(search_result);
205
206         /* unbind so we can go back in as the authenticating user */
207         ldap_unbind(ldserver);
208
209         if (!user_dn) {
210                 syslog(LOG_DEBUG, "No such user was found.\n");
211                 return(4);
212         }
213
214         if (found_dn) safestrncpy(found_dn, user_dn, found_dn_size);
215         ldap_memfree(user_dn);
216         return(0);
217 }
218
219
220 int CtdlTryPasswordLDAP(char *user_dn, const char *password)
221 {
222         LDAP *ldserver = NULL;
223         int i = (-1);
224
225         if (IsEmptyStr(password)) {
226                 syslog(LOG_DEBUG, "LDAP: empty passwords are not permitted\n");
227                 return(1);
228         }
229
230         syslog(LOG_DEBUG, "LDAP: trying to bind as %s\n", user_dn);
231         ldserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
232         if (ldserver) {
233                 ldap_set_option(ldserver, LDAP_OPT_PROTOCOL_VERSION, &ctdl_require_ldap_version);
234                 i = ldap_simple_bind_s(ldserver, user_dn, password);
235                 if (i == LDAP_SUCCESS) {
236                         syslog(LOG_DEBUG, "LDAP: bind succeeded\n");
237                 }
238                 else {
239                         syslog(LOG_DEBUG, "LDAP: Cannot bind: %s (%d)\n", ldap_err2string(i), i);
240                 }
241                 ldap_set_option(ldserver, LDAP_OPT_REFERRALS, (void *)LDAP_OPT_OFF);
242                 ldap_unbind(ldserver);
243         }
244
245         if (i == LDAP_SUCCESS) {
246                 return(0);
247         }
248
249         return(1);
250 }
251
252
253 /*
254  * Learn LDAP attributes and stuff them into the vCard.
255  * Returns nonzero if we changed anything.
256  */
257 int Ctdl_LDAP_to_vCard(char *ldap_dn, struct vCard *v)
258 {
259         int changed_something = 0;
260
261         if (!ldap_dn) return(0);
262         if (!v) return(0);
263
264         /*
265          * FIXME this is a stub function
266          *
267          * ldap_dn will contain the DN of the user, and v will contain a pointer to
268          * the vCard that needs to be (re-)populated.  Put the requisite LDAP code here.
269          *
270         vcard_set_prop(v, "email;internet", xxx, 0);
271          *
272          * return nonzero to tell the caller that we made changes that need to be saved
273         changed_something = 1;
274          *
275          */
276
277         return(changed_something);      /* tell the caller whether we made any changes */
278 }
279
280 #endif /* HAVE_LDAP */