Removed the remaining code where LDAP was optional.
[citadel.git] / citadel / ldap.c
1 // These functions implement the portions of AUTHMODE_LDAP and AUTHMODE_LDAP_AD which
2 // actually speak to the LDAP server.
3 //
4 // Copyright (c) 2011-2021 by the citadel.org development team.
5 //
6 // This program is open source software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License, version 3.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13
14 int ctdl_require_ldap_version = 3;
15
16 #define _GNU_SOURCE             // Needed to suppress warning about vasprintf() when running on Linux/Linux
17 #include <stdio.h>
18 #include <libcitadel.h>
19 #include "citserver.h"
20 #include "citadel_ldap.h"
21 #include "ctdl_module.h"
22 #include "user_ops.h"
23 #include "internet_addressing.h"
24 #include "config.h"
25 #include <ldap.h>
26
27 #define LDAP_DEPRECATED 1       // Suppress libldap's warning that we are using deprecated API calls
28
29
30 /*
31  * Utility function, supply a search result and get back the fullname (display name, common name, etc) from the first result
32  */
33 void derive_fullname_from_ldap_result(char *fullname, int fullname_size, LDAP *ldserver, LDAPMessage *search_result)
34 {
35         char **values;
36
37         if (fullname == NULL) return;
38
39         if (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_LDAP_AD) {
40                 values = ldap_get_values(ldserver, search_result, "displayName");
41                 if (values) {
42                         if (values[0]) {
43                                 if (fullname) safestrncpy(fullname, values[0], fullname_size);
44                                 syslog(LOG_DEBUG, "ldap: displayName = %s", values[0]);
45                         }
46                         ldap_value_free(values);
47                 }
48         }
49         else {
50                 values = ldap_get_values(ldserver, search_result, "cn");
51                 if (values) {
52                         if (values[0]) {
53                                 if (fullname) safestrncpy(fullname, values[0], fullname_size);
54                                 syslog(LOG_DEBUG, "ldap: cn = %s", values[0]);
55                         }
56                         ldap_value_free(values);
57                 }
58         }
59 }
60
61
62 /*
63  * Utility function, supply a search result and get back the uid from the first result
64  */
65 uid_t derive_uid_from_ldap(LDAP *ldserver, LDAPMessage *entry)
66 {
67         char **values;
68         uid_t uid = (-1);
69
70         if (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_LDAP_AD) {
71                 values = ldap_get_values(ldserver, entry, "objectGUID");        // AD schema: uid hashed from objectGUID
72                 if (values) {
73                         if (values[0]) {
74                                 uid = abs(HashLittle(values[0], strlen(values[0])));
75                         }
76                         ldap_value_free(values);
77                 }
78         }
79         else {
80                 values = ldap_get_values(ldserver, entry, "uidNumber");         // POSIX schema: uid = uidNumber
81                 if (values) {
82                         if (values[0]) {
83                                 uid = atoi(values[0]);
84                         }
85                         ldap_value_free(values);
86                 }
87         }
88
89         return(uid);
90 }
91
92
93 /*
94  * Wrapper function for ldap_initialize() that consistently fills in the correct fields
95  */
96 int ctdl_ldap_initialize(LDAP **ld) {
97
98         char server_url[256];
99         int ret;
100
101         snprintf(server_url, sizeof server_url, "ldap://%s:%d", CtdlGetConfigStr("c_ldap_host"), CtdlGetConfigInt("c_ldap_port"));
102         ret = ldap_initialize(ld, server_url);
103         if (ret != LDAP_SUCCESS) {
104                 syslog(LOG_ERR, "ldap: could not connect to %s : %m", server_url);
105                 *ld = NULL;
106                 return(errno);
107         }
108
109         return(ret);
110 }
111
112
113 /*
114  * Bind to the LDAP server and return a working handle
115  */
116 LDAP *ctdl_ldap_bind(void) {
117         LDAP *ldserver = NULL;
118         int i;
119
120         if (ctdl_ldap_initialize(&ldserver) != LDAP_SUCCESS) {
121                 return(NULL);
122         }
123
124         ldap_set_option(ldserver, LDAP_OPT_PROTOCOL_VERSION, &ctdl_require_ldap_version);
125         ldap_set_option(ldserver, LDAP_OPT_REFERRALS, (void *)LDAP_OPT_OFF);
126
127         striplt(CtdlGetConfigStr("c_ldap_bind_dn"));
128         striplt(CtdlGetConfigStr("c_ldap_bind_pw"));
129         i = ldap_simple_bind_s(ldserver,
130                 (!IsEmptyStr(CtdlGetConfigStr("c_ldap_bind_dn")) ? CtdlGetConfigStr("c_ldap_bind_dn") : NULL),
131                 (!IsEmptyStr(CtdlGetConfigStr("c_ldap_bind_pw")) ? CtdlGetConfigStr("c_ldap_bind_pw") : NULL)
132         );
133         if (i != LDAP_SUCCESS) {
134                 syslog(LOG_ERR, "ldap: Cannot bind: %s (%d)", ldap_err2string(i), i);
135                 return(NULL);
136         }
137
138         return(ldserver);
139 }
140
141
142 /*
143  * Look up a user in the directory to see if this is an account that can be authenticated
144  */
145 int CtdlTryUserLDAP(char *username,
146                 char *found_dn, int found_dn_size,
147                 char *fullname, int fullname_size,
148                 uid_t *uid)
149 {
150         LDAP *ldserver = NULL;
151         LDAPMessage *search_result = NULL;
152         LDAPMessage *entry = NULL;
153         char searchstring[1024];
154         struct timeval tv;
155         char *user_dn = NULL;
156
157         ldserver = ctdl_ldap_bind();
158         if (!ldserver) return(-1);
159
160         if (fullname) safestrncpy(fullname, username, fullname_size);
161         tv.tv_sec = 10;
162         tv.tv_usec = 0;
163
164         if (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_LDAP_AD) {
165                 snprintf(searchstring, sizeof(searchstring), "(sAMAccountName=%s)", username);
166         }
167         else {
168                 snprintf(searchstring, sizeof(searchstring), "(&(objectclass=posixAccount)(cn=%s))", username);
169                 // snprintf(searchstring, sizeof(searchstring), "(&(objectclass=posixAccount)(uid=%s))", username);
170         }
171
172         syslog(LOG_DEBUG, "ldap: search: %s", searchstring);
173         (void) ldap_search_ext_s(
174                 ldserver,                                       /* ld                           */
175                 CtdlGetConfigStr("c_ldap_base_dn"),             /* base                         */
176                 LDAP_SCOPE_SUBTREE,                             /* scope                        */
177                 searchstring,                                   /* filter                       */
178                 NULL,                                           /* attrs (all attributes)       */
179                 0,                                              /* attrsonly (attrs + values)   */
180                 NULL,                                           /* serverctrls (none)           */
181                 NULL,                                           /* clientctrls (none)           */
182                 &tv,                                            /* timeout                      */
183                 1,                                              /* sizelimit (1 result max)     */
184                 &search_result                                  /* res                          */
185         );
186
187         /* Ignore the return value of ldap_search_ext_s().  Sometimes it returns an error even when
188          * the search succeeds.  Instead, we check to see whether search_result is still NULL.
189          */
190         if (search_result == NULL) {
191                 syslog(LOG_DEBUG, "ldap: zero search results were returned");
192                 ldap_unbind(ldserver);
193                 return(2);
194         }
195
196         /* At this point we've got at least one result from our query.  If there are multiple
197          * results, we still only look at the first one.
198          */
199         entry = ldap_first_entry(ldserver, search_result);
200         if (entry) {
201
202                 user_dn = ldap_get_dn(ldserver, entry);
203                 if (user_dn) {
204                         syslog(LOG_DEBUG, "ldap: dn = %s", user_dn);
205                 }
206
207                 derive_fullname_from_ldap_result(fullname, fullname_size, ldserver, search_result);
208                 *uid = derive_uid_from_ldap(ldserver, search_result);
209         }
210
211         /* free the results */
212         ldap_msgfree(search_result);
213
214         /* unbind so we can go back in as the authenticating user */
215         ldap_unbind(ldserver);
216
217         if (!user_dn) {
218                 syslog(LOG_DEBUG, "ldap: No such user was found.");
219                 return(4);
220         }
221
222         if (found_dn) safestrncpy(found_dn, user_dn, found_dn_size);
223         ldap_memfree(user_dn);
224         return(0);
225 }
226
227
228 int CtdlTryPasswordLDAP(char *user_dn, const char *password)
229 {
230         LDAP *ldserver = NULL;
231         int i = (-1);
232
233         if (IsEmptyStr(password)) {
234                 syslog(LOG_DEBUG, "ldap: empty passwords are not permitted");
235                 return(1);
236         }
237
238         syslog(LOG_DEBUG, "ldap: trying to bind as %s", user_dn);
239         i = ctdl_ldap_initialize(&ldserver);
240         if (i == LDAP_SUCCESS) {
241                 ldap_set_option(ldserver, LDAP_OPT_PROTOCOL_VERSION, &ctdl_require_ldap_version);
242                 i = ldap_simple_bind_s(ldserver, user_dn, password);
243                 if (i == LDAP_SUCCESS) {
244                         syslog(LOG_DEBUG, "ldap: bind succeeded");
245                 }
246                 else {
247                         syslog(LOG_DEBUG, "ldap: Cannot bind: %s (%d)", ldap_err2string(i), i);
248                 }
249                 ldap_set_option(ldserver, LDAP_OPT_REFERRALS, (void *)LDAP_OPT_OFF);
250                 ldap_unbind(ldserver);
251         }
252
253         if (i == LDAP_SUCCESS) {
254                 return(0);
255         }
256
257         return(1);
258 }
259
260
261 //return !0 iff property changed.
262 int vcard_set_props_iff_different(struct vCard *v,char *propname,int numvals, char **vals) {
263         int i;
264         char *oldval = "";
265         for(i=0;i<numvals;i++) {
266           oldval = vcard_get_prop(v,propname,0,i,0);
267           if (oldval == NULL) break;
268           if (strcmp(vals[i],oldval)) break;
269         }
270         if (i!=numvals) {
271                 syslog(LOG_DEBUG, "ldap: vcard property %s, element %d of %d changed from %s to %s\n", propname, i, numvals, oldval, vals[i]);
272                 for(i=0;i<numvals;i++) vcard_set_prop(v,propname,vals[i],(i==0) ? 0 : 1);
273                 return 1;
274         }
275         return 0;
276 }
277
278
279 //return !0 iff property changed.
280 int vcard_set_one_prop_iff_different(struct vCard *v,char *propname, char *newfmt, ...) {
281         va_list args;
282         char *newvalue;
283         int changed_something;
284         va_start(args,newfmt);
285         if (-1==vasprintf(&newvalue,newfmt,args)) {
286                 syslog(LOG_ERR, "ldap: out of memory!");
287                 return 0;
288         }
289         changed_something = vcard_set_props_iff_different(v,propname,1,&newvalue);
290         va_end(args);
291         free(newvalue);
292         return changed_something;
293 }
294
295
296 /*
297  * Learn LDAP attributes and stuff them into the vCard.
298  * Returns nonzero if we changed anything.
299  */
300 int Ctdl_LDAP_to_vCard(char *ldap_dn, struct vCard *v)
301 {
302         int changed_something = 0;
303         LDAP *ldserver = NULL;
304         struct timeval tv;
305         LDAPMessage *search_result = NULL;
306         LDAPMessage *entry = NULL;
307         char **givenName;
308         char **sn;
309         char **cn;
310         char **initials;
311         char **o;
312         char **street;
313         char **l;
314         char **st;
315         char **postalCode;
316         char **telephoneNumber;
317         char **mobile;
318         char **homePhone;
319         char **facsimileTelephoneNumber;
320         char **mail;
321         char **uid;
322         char **homeDirectory;
323         char **uidNumber;
324         char **loginShell;
325         char **gidNumber;
326         char **c;
327         char **title;
328         char **uuid;
329         char *attrs[] = { "*","+",NULL};
330
331         if (!ldap_dn) return(0);
332         if (!v) return(0);
333
334         ldserver = ctdl_ldap_bind();
335         if (!ldserver) return(-1);
336
337         tv.tv_sec = 10;
338         tv.tv_usec = 0;
339
340         syslog(LOG_DEBUG, "ldap: search: %s", ldap_dn);
341         (void) ldap_search_ext_s(
342                 ldserver,                               // ld
343                 ldap_dn,                                // base
344                 LDAP_SCOPE_SUBTREE,                     // scope
345                 NULL,                                   // filter
346                 attrs,                                  // attrs (all attributes)
347                 0,                                      // attrsonly (attrs + values)
348                 NULL,                                   // serverctrls (none)
349                 NULL,                                   // clientctrls (none)
350                 &tv,                                    // timeout
351                 1,                                      // sizelimit (1 result max)
352                 &search_result                          // res
353         );
354         
355         /* Ignore the return value of ldap_search_ext_s().  Sometimes it returns an error even when
356          * the search succeeds.  Instead, we check to see whether search_result is still NULL.
357          */
358         if (search_result == NULL) {
359                 syslog(LOG_DEBUG, "ldap: zero search results were returned");
360                 ldap_unbind(ldserver);
361                 return(0);
362         }
363
364         /* At this point we've got at least one result from our query.  If there are multiple
365          * results, we still only look at the first one.
366          */
367         entry = ldap_first_entry(ldserver, search_result);
368         if (entry) {
369                 syslog(LOG_DEBUG, "ldap: search got user details for vcard.");
370                 givenName=ldap_get_values(ldserver, search_result, "givenName");
371                 sn=ldap_get_values(ldserver, search_result, "sn");
372                 cn=ldap_get_values(ldserver, search_result, "cn");
373                 initials=ldap_get_values(ldserver, search_result, "initials");
374                 title=ldap_get_values(ldserver, search_result, "title");
375                 o=ldap_get_values(ldserver, search_result, "o");
376                 street=ldap_get_values(ldserver, search_result, "street");
377                 l=ldap_get_values(ldserver, search_result, "l");
378                 st=ldap_get_values(ldserver, search_result, "st");
379                 postalCode=ldap_get_values(ldserver, search_result, "postalCode");
380                 telephoneNumber=ldap_get_values(ldserver, search_result, "telephoneNumber");
381                 mobile=ldap_get_values(ldserver, search_result, "mobile");
382                 homePhone=ldap_get_values(ldserver, search_result, "homePhone");
383                 facsimileTelephoneNumber=ldap_get_values(ldserver, search_result, "facsimileTelephoneNumber");
384                 mail=ldap_get_values(ldserver, search_result, "mail");
385                 uid=ldap_get_values(ldserver, search_result, "uid");
386                 homeDirectory=ldap_get_values(ldserver, search_result, "homeDirectory");
387                 uidNumber=ldap_get_values(ldserver, search_result, "uidNumber");
388                 loginShell=ldap_get_values(ldserver, search_result, "loginShell");
389                 gidNumber=ldap_get_values(ldserver, search_result, "gidNumber");
390                 c=ldap_get_values(ldserver, search_result, "c");
391                 uuid=ldap_get_values(ldserver, search_result, "entryUUID");
392
393                 if (street && l && st && postalCode && c) changed_something |= vcard_set_one_prop_iff_different(v,"adr",";;%s;%s;%s;%s;%s",street[0],l[0],st[0],postalCode[0],c[0]);
394                 if (telephoneNumber) changed_something |= vcard_set_one_prop_iff_different(v,"tel;work","%s",telephoneNumber[0]);
395                 if (facsimileTelephoneNumber) changed_something |= vcard_set_one_prop_iff_different(v,"tel;fax","%s",facsimileTelephoneNumber[0]);
396                 if (mobile) changed_something |= vcard_set_one_prop_iff_different(v,"tel;cell","%s",mobile[0]);
397                 if (homePhone) changed_something |= vcard_set_one_prop_iff_different(v,"tel;home","%s",homePhone[0]);
398                 if (givenName && sn) {
399                         if (initials) {
400                                 changed_something |= vcard_set_one_prop_iff_different(v,"n","%s;%s;%s",sn[0],givenName[0],initials[0]);
401                         }
402                         else {
403                                 changed_something |= vcard_set_one_prop_iff_different(v,"n","%s;%s",sn[0],givenName[0]);
404                         }
405                 }
406                 if (mail) {
407                         changed_something |= vcard_set_props_iff_different(v,"email;internet",ldap_count_values(mail),mail);
408                 }
409                 if (uuid) changed_something |= vcard_set_one_prop_iff_different(v,"X-uuid","%s",uuid[0]);
410                 if (o) changed_something |= vcard_set_one_prop_iff_different(v,"org","%s",o[0]);
411                 if (cn) changed_something |= vcard_set_one_prop_iff_different(v,"fn","%s",cn[0]);
412                 if (title) changed_something |= vcard_set_one_prop_iff_different(v,"title","%s",title[0]);
413                 
414                 if (givenName) ldap_value_free(givenName);
415                 if (initials) ldap_value_free(initials);
416                 if (sn) ldap_value_free(sn);
417                 if (cn) ldap_value_free(cn);
418                 if (o) ldap_value_free(o);
419                 if (street) ldap_value_free(street);
420                 if (l) ldap_value_free(l);
421                 if (st) ldap_value_free(st);
422                 if (postalCode) ldap_value_free(postalCode);
423                 if (telephoneNumber) ldap_value_free(telephoneNumber);
424                 if (mobile) ldap_value_free(mobile);
425                 if (homePhone) ldap_value_free(homePhone);
426                 if (facsimileTelephoneNumber) ldap_value_free(facsimileTelephoneNumber);
427                 if (mail) ldap_value_free(mail);
428                 if (uid) ldap_value_free(uid);
429                 if (homeDirectory) ldap_value_free(homeDirectory);
430                 if (uidNumber) ldap_value_free(uidNumber);
431                 if (loginShell) ldap_value_free(loginShell);
432                 if (gidNumber) ldap_value_free(gidNumber);
433                 if (c) ldap_value_free(c);
434                 if (title) ldap_value_free(title);
435                 if (uuid) ldap_value_free(uuid);
436         }
437         /* free the results */
438         ldap_msgfree(search_result);
439
440         /* unbind so we can go back in as the authenticating user */
441         ldap_unbind(ldserver);
442         return(changed_something);      /* tell the caller whether we made any changes */
443 }
444
445
446 /*
447  * Extract a user's Internet email addresses from LDAP.
448  * Returns zero if we got a valid set of addresses; nonzero for error.
449  */
450 int extract_email_addresses_from_ldap(char *ldap_dn, char *emailaddrs)
451 {
452         LDAP *ldserver = NULL;
453         struct timeval tv;
454         LDAPMessage *search_result = NULL;
455         LDAPMessage *entry = NULL;
456         char **mail;
457         char *attrs[] = { "*","+",NULL};
458
459         if (!ldap_dn) return(1);
460         if (!emailaddrs) return(1);
461
462         ldserver = ctdl_ldap_bind();
463         if (!ldserver) return(-1);
464
465         tv.tv_sec = 10;
466         tv.tv_usec = 0;
467
468         syslog(LOG_DEBUG, "ldap: search: %s", ldap_dn);
469         (void) ldap_search_ext_s(
470                 ldserver,                               // ld
471                 ldap_dn,                                // base
472                 LDAP_SCOPE_SUBTREE,                     // scope
473                 NULL,                                   // filter
474                 attrs,                                  // attrs (all attributes)
475                 0,                                      // attrsonly (attrs + values)
476                 NULL,                                   // serverctrls (none)
477                 NULL,                                   // clientctrls (none)
478                 &tv,                                    // timeout
479                 1,                                      // sizelimit (1 result max)
480                 &search_result                          // res
481         );
482         
483         /* Ignore the return value of ldap_search_ext_s().  Sometimes it returns an error even when
484          * the search succeeds.  Instead, we check to see whether search_result is still NULL.
485          */
486         if (search_result == NULL) {
487                 syslog(LOG_DEBUG, "ldap: zero search results were returned");
488                 ldap_unbind(ldserver);
489                 return(4);
490         }
491
492         /* At this point we've got at least one result from our query.  If there are multiple
493          * results, we still only look at the first one.
494          */
495         emailaddrs[0] = 0;      /* clear out any previous results */
496         entry = ldap_first_entry(ldserver, search_result);
497         if (entry) {
498                 syslog(LOG_DEBUG, "ldap: search got user details");
499                 mail = ldap_get_values(ldserver, search_result, "mail");
500
501                 if (mail) {
502                         int q;
503                         for (q=0; q<ldap_count_values(mail); ++q) {
504                                 if (IsDirectory(mail[q], 0)) {
505                                         if ((strlen(emailaddrs) + strlen(mail[q]) + 2) > 512) {
506                                                 syslog(LOG_ERR, "ldap: can't fit all email addresses into user record");
507                                         }
508                                         else {
509                                                 if (!IsEmptyStr(emailaddrs)) {
510                                                         strcat(emailaddrs, "|");
511                                                 }
512                                                 strcat(emailaddrs, mail[q]);
513                                         }
514                                 }
515                         }
516                 }
517         }
518
519         /* free the results */
520         ldap_msgfree(search_result);
521
522         /* unbind so we can go back in as the authenticating user */
523         ldap_unbind(ldserver);
524         return(0);
525 }
526
527
528 /*
529  * Scan LDAP for users and populate Citadel's user database with everyone
530  */
531 void CtdlSynchronizeUsersFromLDAP(void)
532 {
533         LDAP *ldserver = NULL;
534         LDAPMessage *search_result = NULL;
535         LDAPMessage *entry = NULL;
536         char *user_dn = NULL;
537         char searchstring[1024];
538         struct timeval tv;
539
540         if ((CtdlGetConfigInt("c_auth_mode") != AUTHMODE_LDAP) && (CtdlGetConfigInt("c_auth_mode") != AUTHMODE_LDAP_AD)) {
541                 return;         // not running LDAP
542         }
543
544         syslog(LOG_INFO, "ldap: synchronizing Citadel user database from LDAP");
545
546         ldserver = ctdl_ldap_bind();
547         if (!ldserver) return;
548
549         tv.tv_sec = 10;
550         tv.tv_usec = 0;
551
552         if (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_LDAP_AD) {
553                         snprintf(searchstring, sizeof(searchstring), "(&(objectClass=user)(objectClass=person)(!(objectClass=computer)))");
554         } else {
555                         snprintf(searchstring, sizeof(searchstring), "(objectClass=inetOrgPerson)");
556         }
557
558         syslog(LOG_DEBUG, "ldap: search: %s", searchstring);
559         (void) ldap_search_ext_s(
560                 ldserver,                                       // ld
561                 CtdlGetConfigStr("c_ldap_base_dn"),             // base
562                 LDAP_SCOPE_SUBTREE,                             // scope
563                 searchstring,                                   // filter
564                 NULL,                                           // attrs (all attributes)
565                 0,                                              // attrsonly (attrs + values)
566                 NULL,                                           // serverctrls (none)
567                 NULL,                                           // clientctrls (none)
568                 &tv,                                            // timeout
569                 INT_MAX,                                        // sizelimit (max)
570                 &search_result                                  // result
571         );
572
573         /* Ignore the return value of ldap_search_ext_s().  Sometimes it returns an error even when
574          * the search succeeds.  Instead, we check to see whether search_result is still NULL.
575          */
576         if (search_result == NULL) {
577                 syslog(LOG_DEBUG, "ldap: zero search results were returned");
578                 ldap_unbind(ldserver);
579                 return;
580         }
581
582         syslog(LOG_DEBUG, "ldap: %d entries returned", ldap_count_entries(ldserver, search_result));
583         entry = ldap_first_entry(ldserver, search_result);
584         while (entry) {
585
586                 user_dn = ldap_get_dn(ldserver, entry);
587                 if (user_dn) {
588                         syslog(LOG_DEBUG, "ldap: found %s", user_dn);
589
590                         int fullname_size = 256;
591                         char fullname[256] = { 0 } ;
592                         uid_t uid = (-1);
593                         char new_emailaddrs[512] = { 0 } ;
594
595                         derive_fullname_from_ldap_result(fullname, fullname_size, ldserver, entry);
596                         uid = derive_uid_from_ldap(ldserver, entry);
597                         syslog(LOG_DEBUG, "ldap: display name: <%s> , uid = <%d>", fullname, uid);
598
599                         // now create or update the user
600                         int found_user;
601                         struct ctdluser usbuf;
602
603                         found_user = getuserbyuid(&usbuf, uid);
604                         if (found_user != 0) {
605                                 create_user(fullname, CREATE_USER_DO_NOT_BECOME_USER, uid);
606                                 found_user = getuserbyuid(&usbuf, uid);
607                                 strcpy(fullname, usbuf.fullname);
608                         }
609
610                         if (found_user == 0) {          // user record exists
611                                 // now update the account email addresses if necessary
612                                 if (CtdlGetConfigInt("c_ldap_sync_email_addrs") > 0) {
613                                         if (extract_email_addresses_from_ldap(user_dn, new_emailaddrs) == 0) {
614                                                 if (strcmp(usbuf.emailaddrs, new_emailaddrs)) {                         // update only if changed
615                                                         CtdlSetEmailAddressesForUser(usbuf.fullname, new_emailaddrs);
616                                                 }
617                                         }
618                                 }
619                         }
620                         ldap_memfree(user_dn);
621                 }
622
623                 entry = ldap_next_entry(ldserver, entry);
624         }
625
626         /* free the results */
627         ldap_msgfree(search_result);
628
629         /* unbind so we can go back in as the authenticating user */
630         ldap_unbind(ldserver);
631 }