move file_ops to modules/ctdlsrv/serv_file.c
[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 "control.h"
55 #include "msgbase.h"
56 #include "config.h"
57 #include "citserver.h"
58 #include "citadel_dirs.h"
59 #include "genstamp.h"
60 #include "threads.h"
61 #include "citadel_ldap.h"
62 #include "ctdl_module.h"
63 #include "user_ops.h"
64
65 #ifdef HAVE_LDAP
66 #define LDAP_DEPRECATED 1       /* Suppress libldap's warning that we are using deprecated API calls */
67 #include <ldap.h>
68
69 int CtdlTryUserLDAP(char *username,
70                 char *found_dn, int found_dn_size,
71                 char *fullname, int fullname_size,
72                 uid_t *uid)
73 {
74         LDAP *ldserver = NULL;
75         int i;
76         LDAPMessage *search_result = NULL;
77         LDAPMessage *entry = NULL;
78         char searchstring[1024];
79         struct timeval tv;
80         char **values;
81         char *user_dn = NULL;
82
83         if (fullname) safestrncpy(fullname, username, fullname_size);
84
85         ldserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
86         if (ldserver == NULL) {
87                 syslog(LOG_ALERT, "LDAP: Could not connect to %s:%d : %s\n",
88                         config.c_ldap_host, config.c_ldap_port,
89                         strerror(errno)
90                 );
91                 return(errno);
92         }
93
94         ldap_set_option(ldserver, LDAP_OPT_PROTOCOL_VERSION, &ctdl_require_ldap_version);
95         ldap_set_option(ldserver, LDAP_OPT_REFERRALS, (void *)LDAP_OPT_OFF);
96
97         striplt(config.c_ldap_bind_dn);
98         striplt(config.c_ldap_bind_pw);
99         syslog(LOG_DEBUG, "LDAP bind DN: %s\n", config.c_ldap_bind_dn);
100         i = ldap_simple_bind_s(ldserver,
101                 (!IsEmptyStr(config.c_ldap_bind_dn) ? config.c_ldap_bind_dn : NULL),
102                 (!IsEmptyStr(config.c_ldap_bind_pw) ? config.c_ldap_bind_pw : NULL)
103         );
104         if (i != LDAP_SUCCESS) {
105                 syslog(LOG_ALERT, "LDAP: Cannot bind: %s (%d)\n", ldap_err2string(i), i);
106                 return(i);
107         }
108
109         tv.tv_sec = 10;
110         tv.tv_usec = 0;
111
112         if (config.c_auth_mode == AUTHMODE_LDAP_AD) {
113                 sprintf(searchstring, "(sAMAccountName=%s)", username);
114         }
115         else {
116                 sprintf(searchstring, "(&(objectclass=posixAccount)(uid=%s))", username);
117         }
118
119         syslog(LOG_DEBUG, "LDAP search: %s\n", searchstring);
120         (void) ldap_search_ext_s(
121                 ldserver,                                       /* ld                           */
122                 config.c_ldap_base_dn,                          /* base                         */
123                 LDAP_SCOPE_SUBTREE,                             /* scope                        */
124                 searchstring,                                   /* filter                       */
125                 NULL,                                           /* attrs (all attributes)       */
126                 0,                                              /* attrsonly (attrs + values)   */
127                 NULL,                                           /* serverctrls (none)           */
128                 NULL,                                           /* clientctrls (none)           */
129                 &tv,                                            /* timeout                      */
130                 1,                                              /* sizelimit (1 result max)     */
131                 &search_result                                  /* res                          */
132         );
133
134         /* Ignore the return value of ldap_search_ext_s().  Sometimes it returns an error even when
135          * the search succeeds.  Instead, we check to see whether search_result is still NULL.
136          */
137         if (search_result == NULL) {
138                 syslog(LOG_DEBUG, "LDAP search: zero results were returned\n");
139                 ldap_unbind(ldserver);
140                 return(2);
141         }
142
143         /* At this point we've got at least one result from our query.  If there are multiple
144          * results, we still only look at the first one.
145          */
146         entry = ldap_first_entry(ldserver, search_result);
147         if (entry) {
148
149                 user_dn = ldap_get_dn(ldserver, entry);
150                 if (user_dn) {
151                         syslog(LOG_DEBUG, "dn = %s\n", user_dn);
152                 }
153
154                 if (config.c_auth_mode == AUTHMODE_LDAP_AD) {
155                         values = ldap_get_values(ldserver, search_result, "displayName");
156                         if (values) {
157                                 if (values[0]) {
158                                         if (fullname) safestrncpy(fullname, values[0], fullname_size);
159                                         syslog(LOG_DEBUG, "displayName = %s\n", values[0]);
160                                 }
161                                 ldap_value_free(values);
162                         }
163                 }
164                 else {
165                         values = ldap_get_values(ldserver, search_result, "cn");
166                         if (values) {
167                                 if (values[0]) {
168                                         if (fullname) safestrncpy(fullname, values[0], fullname_size);
169                                         syslog(LOG_DEBUG, "cn = %s\n", values[0]);
170                                 }
171                                 ldap_value_free(values);
172                         }
173                 }
174
175                 if (config.c_auth_mode == AUTHMODE_LDAP_AD) {
176                         values = ldap_get_values(ldserver, search_result, "objectGUID");
177                         if (values) {
178                                 if (values[0]) {
179                                         if (uid != NULL) {
180                                                 *uid = abs(HashLittle(values[0], strlen(values[0])));
181                                                 syslog(LOG_DEBUG, "uid hashed from objectGUID = %d\n", *uid);
182                                         }
183                                 }
184                                 ldap_value_free(values);
185                         }
186                 }
187                 else {
188                         values = ldap_get_values(ldserver, search_result, "uidNumber");
189                         if (values) {
190                                 if (values[0]) {
191                                         syslog(LOG_DEBUG, "uidNumber = %s\n", values[0]);
192                                         if (uid != NULL) {
193                                                 *uid = atoi(values[0]);
194                                         }
195                                 }
196                                 ldap_value_free(values);
197                         }
198                 }
199
200         }
201
202         /* free the results */
203         ldap_msgfree(search_result);
204
205         /* unbind so we can go back in as the authenticating user */
206         ldap_unbind(ldserver);
207
208         if (!user_dn) {
209                 syslog(LOG_DEBUG, "No such user was found.\n");
210                 return(4);
211         }
212
213         if (found_dn) safestrncpy(found_dn, user_dn, found_dn_size);
214         ldap_memfree(user_dn);
215         return(0);
216 }
217
218
219 int CtdlTryPasswordLDAP(char *user_dn, const char *password)
220 {
221         LDAP *ldserver = NULL;
222         int i = (-1);
223
224         if (IsEmptyStr(password)) {
225                 syslog(LOG_DEBUG, "LDAP: empty passwords are not permitted\n");
226                 return(1);
227         }
228
229         syslog(LOG_DEBUG, "LDAP: trying to bind as %s\n", user_dn);
230         ldserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
231         if (ldserver) {
232                 ldap_set_option(ldserver, LDAP_OPT_PROTOCOL_VERSION, &ctdl_require_ldap_version);
233                 i = ldap_simple_bind_s(ldserver, user_dn, password);
234                 if (i == LDAP_SUCCESS) {
235                         syslog(LOG_DEBUG, "LDAP: bind succeeded\n");
236                 }
237                 else {
238                         syslog(LOG_DEBUG, "LDAP: Cannot bind: %s (%d)\n", ldap_err2string(i), i);
239                 }
240                 ldap_set_option(ldserver, LDAP_OPT_REFERRALS, (void *)LDAP_OPT_OFF);
241                 ldap_unbind(ldserver);
242         }
243
244         if (i == LDAP_SUCCESS) {
245                 return(0);
246         }
247
248         return(1);
249 }
250
251
252 /*
253  * Learn LDAP attributes and stuff them into the vCard.
254  * Returns nonzero if we changed anything.
255  */
256 int Ctdl_LDAP_to_vCard(char *ldap_dn, struct vCard *v)
257 {
258         int changed_something = 0;
259
260         if (!ldap_dn) return(0);
261         if (!v) return(0);
262
263         /*
264          * FIXME this is a stub function
265          *
266          * ldap_dn will contain the DN of the user, and v will contain a pointer to
267          * the vCard that needs to be (re-)populated.  Put the requisite LDAP code here.
268          *
269         vcard_set_prop(v, "email;internet", xxx, 0);
270          *
271          * return nonzero to tell the caller that we made changes that need to be saved
272         changed_something = 1;
273          *
274          */
275
276         return(changed_something);      /* tell the caller whether we made any changes */
277 }
278
279 #endif /* HAVE_LDAP */