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