Back to index

citadel  8.12
ldap.c
Go to the documentation of this file.
00001 /*
00002  * These functions implement the portions of AUTHMODE_LDAP and AUTHMODE_LDAP_AD which
00003  * actually speak to the LDAP server.
00004  *
00005  * Copyright (c) 2011 by Art Cancro and the citadel.org development team.
00006  *
00007  * This program is open source software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation; either version 3 of the License, or
00010  * (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020  */
00021 
00022 int ctdl_require_ldap_version = 3;
00023 
00024 #include "sysdep.h"
00025 #include <errno.h>
00026 #include <stdlib.h>
00027 #include <unistd.h>
00028 #include <stdio.h>
00029 #include <fcntl.h>
00030 #include <signal.h>
00031 #include <pwd.h>
00032 #include <ctype.h>
00033 #include <sys/types.h>
00034 #include <sys/wait.h>
00035 #ifdef HAVE_SYS_STAT_H
00036 #include <sys/stat.h>
00037 #endif
00038 
00039 #if TIME_WITH_SYS_TIME
00040 # include <sys/time.h>
00041 # include <time.h>
00042 #else
00043 # if HAVE_SYS_TIME_H
00044 #  include <sys/time.h>
00045 # else
00046 #  include <time.h>
00047 # endif
00048 #endif
00049 
00050 #include <string.h>
00051 #include <limits.h>
00052 #include <libcitadel.h>
00053 #include "auth.h"
00054 #include "citadel.h"
00055 #include "server.h"
00056 #include "database.h"
00057 #include "sysdep_decls.h"
00058 #include "support.h"
00059 #include "room_ops.h"
00060 #include "file_ops.h"
00061 #include "control.h"
00062 #include "msgbase.h"
00063 #include "config.h"
00064 #include "citserver.h"
00065 #include "citadel_dirs.h"
00066 #include "genstamp.h"
00067 #include "threads.h"
00068 #include "citadel_ldap.h"
00069 #include "ctdl_module.h"
00070 #include "user_ops.h"
00071 
00072 #ifdef HAVE_LDAP
00073 #define LDAP_DEPRECATED 1   /* Suppress libldap's warning that we are using deprecated API calls */
00074 #include <ldap.h>
00075 
00076 int CtdlTryUserLDAP(char *username,
00077               char *found_dn, int found_dn_size,
00078               char *fullname, int fullname_size,
00079               uid_t *uid)
00080 {
00081        LDAP *ldserver = NULL;
00082        int i;
00083        LDAPMessage *search_result = NULL;
00084        LDAPMessage *entry = NULL;
00085        char searchstring[1024];
00086        struct timeval tv;
00087        char **values;
00088        char *user_dn = NULL;
00089 
00090        if (fullname) safestrncpy(fullname, username, fullname_size);
00091 
00092        ldserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
00093        if (ldserver == NULL) {
00094               syslog(LOG_ALERT, "LDAP: Could not connect to %s:%d : %s\n",
00095                      config.c_ldap_host, config.c_ldap_port,
00096                      strerror(errno)
00097               );
00098               return(errno);
00099        }
00100 
00101        ldap_set_option(ldserver, LDAP_OPT_PROTOCOL_VERSION, &ctdl_require_ldap_version);
00102        ldap_set_option(ldserver, LDAP_OPT_REFERRALS, (void *)LDAP_OPT_OFF);
00103 
00104        striplt(config.c_ldap_bind_dn);
00105        striplt(config.c_ldap_bind_pw);
00106        syslog(LOG_DEBUG, "LDAP bind DN: %s\n", config.c_ldap_bind_dn);
00107        i = ldap_simple_bind_s(ldserver,
00108               (!IsEmptyStr(config.c_ldap_bind_dn) ? config.c_ldap_bind_dn : NULL),
00109               (!IsEmptyStr(config.c_ldap_bind_pw) ? config.c_ldap_bind_pw : NULL)
00110        );
00111        if (i != LDAP_SUCCESS) {
00112               syslog(LOG_ALERT, "LDAP: Cannot bind: %s (%d)\n", ldap_err2string(i), i);
00113               return(i);
00114        }
00115 
00116        tv.tv_sec = 10;
00117        tv.tv_usec = 0;
00118 
00119        if (config.c_auth_mode == AUTHMODE_LDAP_AD) {
00120               sprintf(searchstring, "(sAMAccountName=%s)", username);
00121        }
00122        else {
00123               sprintf(searchstring, "(&(objectclass=posixAccount)(uid=%s))", username);
00124        }
00125 
00126        syslog(LOG_DEBUG, "LDAP search: %s\n", searchstring);
00127        (void) ldap_search_ext_s(
00128               ldserver,                                 /* ld                       */
00129               config.c_ldap_base_dn,                           /* base                            */
00130               LDAP_SCOPE_SUBTREE,                       /* scope                    */
00131               searchstring,                             /* filter                   */
00132               NULL,                                     /* attrs (all attributes)   */
00133               0,                                        /* attrsonly (attrs + values)      */
00134               NULL,                                     /* serverctrls (none)              */
00135               NULL,                                     /* clientctrls (none)              */
00136               &tv,                                      /* timeout                  */
00137               1,                                        /* sizelimit (1 result max) */
00138               &search_result                                   /* res                      */
00139        );
00140 
00141        /* Ignore the return value of ldap_search_ext_s().  Sometimes it returns an error even when
00142         * the search succeeds.  Instead, we check to see whether search_result is still NULL.
00143         */
00144        if (search_result == NULL) {
00145               syslog(LOG_DEBUG, "LDAP search: zero results were returned\n");
00146               ldap_unbind(ldserver);
00147               return(2);
00148        }
00149 
00150        /* At this point we've got at least one result from our query.  If there are multiple
00151         * results, we still only look at the first one.
00152         */
00153        entry = ldap_first_entry(ldserver, search_result);
00154        if (entry) {
00155 
00156               user_dn = ldap_get_dn(ldserver, entry);
00157               if (user_dn) {
00158                      syslog(LOG_DEBUG, "dn = %s\n", user_dn);
00159               }
00160 
00161               if (config.c_auth_mode == AUTHMODE_LDAP_AD) {
00162                      values = ldap_get_values(ldserver, search_result, "displayName");
00163                      if (values) {
00164                             if (values[0]) {
00165                                    if (fullname) safestrncpy(fullname, values[0], fullname_size);
00166                                    syslog(LOG_DEBUG, "displayName = %s\n", values[0]);
00167                             }
00168                             ldap_value_free(values);
00169                      }
00170               }
00171               else {
00172                      values = ldap_get_values(ldserver, search_result, "cn");
00173                      if (values) {
00174                             if (values[0]) {
00175                                    if (fullname) safestrncpy(fullname, values[0], fullname_size);
00176                                    syslog(LOG_DEBUG, "cn = %s\n", values[0]);
00177                             }
00178                             ldap_value_free(values);
00179                      }
00180               }
00181 
00182               if (config.c_auth_mode == AUTHMODE_LDAP_AD) {
00183                      values = ldap_get_values(ldserver, search_result, "objectGUID");
00184                      if (values) {
00185                             if (values[0]) {
00186                                    if (uid != NULL) {
00187                                           *uid = abs(HashLittle(values[0], strlen(values[0])));
00188                                           syslog(LOG_DEBUG, "uid hashed from objectGUID = %d\n", *uid);
00189                                    }
00190                             }
00191                             ldap_value_free(values);
00192                      }
00193               }
00194               else {
00195                      values = ldap_get_values(ldserver, search_result, "uidNumber");
00196                      if (values) {
00197                             if (values[0]) {
00198                                    syslog(LOG_DEBUG, "uidNumber = %s\n", values[0]);
00199                                    if (uid != NULL) {
00200                                           *uid = atoi(values[0]);
00201                                    }
00202                             }
00203                             ldap_value_free(values);
00204                      }
00205               }
00206 
00207        }
00208 
00209        /* free the results */
00210        ldap_msgfree(search_result);
00211 
00212        /* unbind so we can go back in as the authenticating user */
00213        ldap_unbind(ldserver);
00214 
00215        if (!user_dn) {
00216               syslog(LOG_DEBUG, "No such user was found.\n");
00217               return(4);
00218        }
00219 
00220        if (found_dn) safestrncpy(found_dn, user_dn, found_dn_size);
00221        ldap_memfree(user_dn);
00222        return(0);
00223 }
00224 
00225 
00226 int CtdlTryPasswordLDAP(char *user_dn, const char *password)
00227 {
00228        LDAP *ldserver = NULL;
00229        int i = (-1);
00230 
00231        if (IsEmptyStr(password)) {
00232               syslog(LOG_DEBUG, "LDAP: empty passwords are not permitted\n");
00233               return(1);
00234        }
00235 
00236        syslog(LOG_DEBUG, "LDAP: trying to bind as %s\n", user_dn);
00237        ldserver = ldap_init(config.c_ldap_host, config.c_ldap_port);
00238        if (ldserver) {
00239               ldap_set_option(ldserver, LDAP_OPT_PROTOCOL_VERSION, &ctdl_require_ldap_version);
00240               i = ldap_simple_bind_s(ldserver, user_dn, password);
00241               if (i == LDAP_SUCCESS) {
00242                      syslog(LOG_DEBUG, "LDAP: bind succeeded\n");
00243               }
00244               else {
00245                      syslog(LOG_DEBUG, "LDAP: Cannot bind: %s (%d)\n", ldap_err2string(i), i);
00246               }
00247               ldap_set_option(ldserver, LDAP_OPT_REFERRALS, (void *)LDAP_OPT_OFF);
00248               ldap_unbind(ldserver);
00249        }
00250 
00251        if (i == LDAP_SUCCESS) {
00252               return(0);
00253        }
00254 
00255        return(1);
00256 }
00257 
00258 
00259 /*
00260  * Learn LDAP attributes and stuff them into the vCard.
00261  * Returns nonzero if we changed anything.
00262  */
00263 int Ctdl_LDAP_to_vCard(char *ldap_dn, struct vCard *v)
00264 {
00265        int changed_something = 0;
00266 
00267        if (!ldap_dn) return(0);
00268        if (!v) return(0);
00269 
00270        /*
00271         * FIXME this is a stub function
00272         *
00273         * ldap_dn will contain the DN of the user, and v will contain a pointer to
00274         * the vCard that needs to be (re-)populated.  Put the requisite LDAP code here.
00275         *
00276        vcard_set_prop(v, "email;internet", xxx, 0);
00277         *
00278         * return nonzero to tell the caller that we made changes that need to be saved
00279        changed_something = 1;
00280         *
00281         */
00282 
00283        return(changed_something);  /* tell the caller whether we made any changes */
00284 }
00285 
00286 #endif /* HAVE_LDAP */