"Fossies" - the Fresh Open Source Software Archive

Member "citadel/ldap.c" (5 Jun 2021, 19418 Bytes) of package /linux/www/citadel.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ldap.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 9.01_vs_902.

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