From: Arran Cudbard-Bell Date: Thu, 6 Apr 2017 21:40:44 +0000 (-0400) Subject: Breakout libldap functions into a utility library X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b562dc3ba07a3a0b66047e4a9d6a7e65c21edd04;p=thirdparty%2Ffreeradius-server.git Breakout libldap functions into a utility library --- diff --git a/src/modules/rlm_ldap/.gitignore b/src/modules/rlm_ldap/.gitignore index 01a5daa3cc5..8e5e021c7f8 100644 --- a/src/modules/rlm_ldap/.gitignore +++ b/src/modules/rlm_ldap/.gitignore @@ -1 +1,2 @@ all.mk +libfreeradius-ldap.mk diff --git a/src/modules/rlm_ldap/all.mk.in b/src/modules/rlm_ldap/all.mk.in index 75bd1d819b4..fa7a27fa46c 100644 --- a/src/modules/rlm_ldap/all.mk.in +++ b/src/modules/rlm_ldap/all.mk.in @@ -1,10 +1 @@ -TARGETNAME := @targetname@ - -ifneq "$(TARGETNAME)" "" -TARGET := $(TARGETNAME).a -endif - -SOURCES := $(TARGETNAME).c attrmap.c ldap.c clients.c groups.c edir.c control.c directory.c @SASL@ - -SRC_CFLAGS := @mod_cflags@ -TGT_LDLIBS := @mod_ldflags@ +SUBMAKEFILES := libfreeradius-ldap.mk rlm_ldap.mk diff --git a/src/modules/rlm_ldap/attrmap.c b/src/modules/rlm_ldap/attrmap.c index c9332af9e8c..45bd3948995 100644 --- a/src/modules/rlm_ldap/attrmap.c +++ b/src/modules/rlm_ldap/attrmap.c @@ -38,7 +38,7 @@ */ int rlm_ldap_map_getvalue(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx) { - rlm_ldap_result_t *self = uctx; + fr_ldap_result_t *self = uctx; VALUE_PAIR *head = NULL, *vp; vp_cursor_t cursor; int i; @@ -309,7 +309,7 @@ int rlm_ldap_map_do(const rlm_ldap_t *inst, REQUEST *request, LDAP *handle, unsigned int total = 0; int applied = 0; /* How many maps have been applied to the current request */ - rlm_ldap_result_t result; + fr_ldap_result_t result; char const *name; for (map = expanded->maps; map != NULL; map = map->next) { @@ -366,7 +366,7 @@ int rlm_ldap_map_do(const rlm_ldap_t *inst, REQUEST *request, LDAP *handle, vp_map_t *attr; char *value; - value = rlm_ldap_berval_to_string(request, values[i]); + value = fr_ldap_berval_to_string(request, values[i]); RDEBUG3("Parsing attribute string '%s'", value); if (map_afrom_attr_str(request, &attr, value, REQUEST_CURRENT, PAIR_LIST_REPLY, diff --git a/src/modules/rlm_ldap/clients.c b/src/modules/rlm_ldap/clients.c index 123dd2a517d..3b91f56baeb 100644 --- a/src/modules/rlm_ldap/clients.c +++ b/src/modules/rlm_ldap/clients.c @@ -86,7 +86,7 @@ static int _get_client_value(char **out, CONF_PAIR const *cp, void *data) return 0; } - *out = rlm_ldap_berval_to_string(NULL, values[0]); + *out = fr_ldap_berval_to_string(NULL, values[0]); ldap_value_free_len(values); if (!*out) return -1; @@ -127,7 +127,7 @@ int rlm_ldap_client_load(rlm_ldap_t const *inst, CONF_SECTION *tmpl, CONF_SECTIO count++; /* - * Create an array of LDAP attributes to feed to rlm_ldap_search. + * Create an array of LDAP attributes to feed to fr_ldap_search. */ attrs = talloc_array(inst, char const *, count); if (rlm_ldap_client_get_attrs(attrs, &idx, map) < 0) { @@ -145,9 +145,11 @@ int rlm_ldap_client_load(rlm_ldap_t const *inst, CONF_SECTION *tmpl, CONF_SECTIO * Perform all searches as the admin user. */ if (conn->rebound) { - status = rlm_ldap_bind(inst, NULL, &conn, - conn->pool_inst->admin_identity, conn->pool_inst->admin_password, - &(conn->pool_inst->admin_sasl), true, NULL, NULL, NULL); + status = fr_ldap_bind(NULL, &conn, + conn->config->admin_identity, conn->config->admin_password, + &(conn->config->admin_sasl), + NULL, + NULL, NULL); if (status != LDAP_PROC_SUCCESS) { ret = -1; goto finish; @@ -158,8 +160,8 @@ int rlm_ldap_client_load(rlm_ldap_t const *inst, CONF_SECTION *tmpl, CONF_SECTIO conn->rebound = false; } - status = rlm_ldap_search(&result, inst, NULL, &conn, inst->clientobj_base_dn, inst->clientobj_scope, - inst->clientobj_filter, attrs, NULL, NULL); + status = fr_ldap_search(&result, NULL, &conn, inst->clientobj_base_dn, inst->clientobj_scope, + inst->clientobj_filter, attrs, NULL, NULL); switch (status) { case LDAP_PROC_SUCCESS: break; @@ -203,12 +205,12 @@ int rlm_ldap_client_load(rlm_ldap_t const *inst, CONF_SECTION *tmpl, CONF_SECTIO goto finish; } - rlm_ldap_normalise_dn(dn, dn); + fr_ldap_util_normalise_dn(dn, dn); cp = cf_pair_find(map, "identifier"); if (cp) { values = ldap_get_values_len(conn->handle, entry, cf_pair_value(cp)); - if (values) id = rlm_ldap_berval_to_string(NULL, values[0]); + if (values) id = fr_ldap_berval_to_string(NULL, values[0]); ldap_value_free_len(values); } diff --git a/src/modules/rlm_ldap/configure b/src/modules/rlm_ldap/configure index bba0a878fae..acb62af22bc 100755 --- a/src/modules/rlm_ldap/configure +++ b/src/modules/rlm_ldap/configure @@ -615,7 +615,6 @@ infodir docdir oldincludedir includedir -runstatedir localstatedir sharedstatedir sysconfdir @@ -687,7 +686,6 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' -runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE}' @@ -940,15 +938,6 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; - -runstatedir | --runstatedir | --runstatedi | --runstated \ - | --runstate | --runstat | --runsta | --runst | --runs \ - | --run | --ru | --r) - ac_prev=runstatedir ;; - -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ - | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ - | --run=* | --ru=* | --r=*) - runstatedir=$ac_optarg ;; - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1086,7 +1075,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir runstatedir + libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -1239,7 +1228,6 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -1913,7 +1901,7 @@ fail= SMART_LIBS= SMART_CLFAGS= SASL= -if test x$with_rlm_ldap != xno; then +if test x$with_libfreeradius-ldap != xno; then ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -3462,20 +3450,20 @@ $as_echo "#define WITH_SASL 1" >>confdefs.h fi fi - targetname=rlm_ldap + targetname=libfreeradius-ldap else targetname= - echo \*\*\* module rlm_ldap is disabled. + echo \*\*\* module libfreeradius-ldap is disabled. fi if test x"$fail" != x""; then if test x"${enable_strict_dependencies}" = x"yes"; then - as_fn_error $? "set --without-rlm_ldap to disable it explicitly." "$LINENO" 5 + as_fn_error $? "set --without-libfreeradius-ldap to disable it explicitly." "$LINENO" 5 else - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: silently not building rlm_ldap." >&5 -$as_echo "$as_me: WARNING: silently not building rlm_ldap." >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: FAILURE: rlm_ldap requires: $fail." >&5 -$as_echo "$as_me: WARNING: FAILURE: rlm_ldap requires: $fail." >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: silently not building libfreeradius-ldap." >&5 +$as_echo "$as_me: WARNING: silently not building libfreeradius-ldap." >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: FAILURE: libfreeradius-ldap requires: $fail." >&5 +$as_echo "$as_me: WARNING: FAILURE: libfreeradius-ldap requires: $fail." >&2;} if test x"$headersuggestion" != x; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $headersuggestion" >&5 $as_echo "$as_me: WARNING: $headersuggestion" >&2;} @@ -3507,10 +3495,11 @@ mod_cflags="$SMART_CPPFLAGS" ac_config_headers="$ac_config_headers config.h" + unset ac_cv_env_LIBS_set unset ac_cv_env_LIBS_value -ac_config_files="$ac_config_files all.mk" +ac_config_files="$ac_config_files libfreeradius-ldap.mk" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -4202,7 +4191,7 @@ for ac_config_target in $ac_config_targets do case $ac_config_target in "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; - "all.mk") CONFIG_FILES="$CONFIG_FILES all.mk" ;; + "libfreeradius-ldap.mk") CONFIG_FILES="$CONFIG_FILES libfreeradius-ldap.mk" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac @@ -4785,3 +4774,1279 @@ $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi + +ac_config_files="$ac_config_files all.mk" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to the package provider." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "libfreeradius-ldap.mk") CONFIG_FILES="$CONFIG_FILES libfreeradius-ldap.mk" ;; + "all.mk") CONFIG_FILES="$CONFIG_FILES all.mk" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi + ;; + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/src/modules/rlm_ldap/configure.ac b/src/modules/rlm_ldap/configure.ac index 107eaba8a82..35c0e43894f 100644 --- a/src/modules/rlm_ldap/configure.ac +++ b/src/modules/rlm_ldap/configure.ac @@ -1,7 +1,7 @@ AC_PREREQ([2.53]) AC_INIT(rlm_ldap.c) AC_REVISION($Revision$) -AC_DEFUN(modname,[rlm_ldap]) +AC_DEFUN(modname,[libfreeradius-ldap]) fail= SMART_LIBS= @@ -161,4 +161,10 @@ AC_SUBST(mod_cflags) AC_SUBST(SASL) AC_SUBST(targetname) AC_CONFIG_HEADER(config.h) + +AC_OUTPUT(libfreeradius-ldap.mk) + +dnl # This is needed so that all.mk is not created unless configure is run +dnl # so that it doesn't attempt to include libfreeradius-redis.mk which +dnl # is also dependent on configure being run. AC_OUTPUT(all.mk) diff --git a/src/modules/rlm_ldap/conn.c b/src/modules/rlm_ldap/conn.c new file mode 100644 index 00000000000..86b2abc719d --- /dev/null +++ b/src/modules/rlm_ldap/conn.c @@ -0,0 +1,150 @@ +/* + * This program is is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * @file rlm_ldap.c + * @brief Connection wrappers + * + * @author Arran Cudbard-Bell + * + * @copyright 2017 The FreeRADIUS Server Project. + */ +RCSID("$Id$") + +#define LOG_PREFIX "%s - " +#define LOG_PREFIX_ARGS handle_config->name + +#include + +#include "rlm_ldap.h" + +/** Gets an LDAP socket from the connection pool + * + * Retrieve a socket from the connection pool, or NULL on error (of if no sockets are available). + * + * @param inst rlm_ldap configuration. + * @param request Current request (may be NULL). + */ +ldap_handle_t *mod_conn_get(rlm_ldap_t const *inst, REQUEST *request) +{ + ldap_handle_t *conn; + + conn = fr_connection_get(inst->pool, request); + + rad_assert(!conn || conn->config); + +#ifdef LDAP_CONTROL_X_SESSION_TRACKING + /* + * Add optional session tracking controls, + * that contain values of some attributes + * in the request. + */ + if ((conn != NULL) && (request != NULL) && inst->session_tracking) { + if (rlm_ldap_control_add_session_tracking(conn, request) < 0) { + fr_connection_release(inst->pool, request, conn); + return NULL; + } + } +#endif + return conn; +} + +/** Releases an LDAP socket back to the connection pool + * + * If the socket was rebound chasing a referral onto another server then we destroy it. + * If the socket was rebound to another user on the same server, we let the next caller rebind it. + * + * @param inst rlm_ldap configuration. + * @param request The current request. + * @param conn to release. + */ +void mod_conn_release(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t *conn) +{ + /* + * Could have already been free'd due to a previous error. + */ + if (!conn) return; + + /* + * Clear any client/server controls associated with the connection. + */ + fr_ldap_control_clear(conn); + + /* + * We chased a referral to another server. + * + * This connection is no longer part of the pool which is + * connected to and bound to the configured server. + * Close it. + * + * Note that we do NOT close it if it was bound to another user. + * Instead, we let the next caller do the rebind. + */ + if (conn->referred) { + fr_connection_close(inst->pool, request, conn); + return; + } + + fr_connection_release(inst->pool, request, conn); + return; +} + +/** Create and return a new connection + * + * Create a new ldap connection and allocate memory for a new rlm_handle_t + */ +void *mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *timeout) +{ + ldap_rcode_t status; + ldap_handle_t *conn; + ldap_handle_config_t const *handle_config = instance; /* Not talloced */ + + conn = fr_ldap_conn_alloc(ctx, handle_config); + if (!conn) return NULL; + + fr_ldap_conn_timeout_set(conn, timeout); + if (handle_config->start_tls) { + if (ldap_start_tls_s(conn->handle, NULL, NULL) != LDAP_SUCCESS) { + int ldap_errno; + + ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno); + + ERROR("Could not start TLS: %s", ldap_err2string(ldap_errno)); + + error: + talloc_free(conn); + + return NULL; + } + } + + status = fr_ldap_bind(NULL, + &conn, + conn->config->admin_identity, conn->config->admin_password, + &(conn->config->admin_sasl), + timeout, + NULL, NULL); + if (status != LDAP_PROC_SUCCESS) goto error; + fr_ldap_conn_timeout_reset(conn); + + /* + * Only error out on memory allocation errors + */ + if (fr_ldap_directory_alloc(conn, &conn->directory, &conn) < 0) goto error; + + return conn; +} diff --git a/src/modules/rlm_ldap/control.c b/src/modules/rlm_ldap/control.c index 8b88578ff75..e6b73f87ea3 100644 --- a/src/modules/rlm_ldap/control.c +++ b/src/modules/rlm_ldap/control.c @@ -16,13 +16,13 @@ /** * $Id$ - * @file src/modules/rlm_ldap/control.c + * @file src/modules/fr_ldap/control.c * @brief Functions for managing server/client side sort controls. * * @author Arran Cudbard-Bell * @copyright 2015 Arran Cudbard-Bell */ -#define LOG_PREFIX "rlm_ldap (%s) - " +#define LOG_PREFIX "fr_ldap (%s) - " #define LOG_PREFIX_ARGS inst->name #include "rlm_ldap.h" @@ -33,21 +33,21 @@ * because they're overriden in their entirety if any call specific * controls are specified. * - * @param[out] serverctrls_out Where to write serverctrls. - * @param[out] clientctrls_out Where to write clientctrls. - * @param[in] serverctrls_len length of serverctrls array. - * @param[in] clientctrls_len length of clientctrls array. - * @param[in] conn to get controls from. - * @param[in] serverctrls_in from arguments. - * @param[in] clientctrls_in from_arguments. + * @param[out] serverctrls_out Where to write serverctrls. + * @param[out] clientctrls_out Where to write clientctrls. + * @param[in] serverctrls_len length of serverctrls array. + * @param[in] clientctrls_len length of clientctrls array. + * @param[in] conn to get controls from. + * @param[in] serverctrls_in from arguments. + * @param[in] clientctrls_in from_arguments. */ - void rlm_ldap_control_merge(LDAPControl *serverctrls_out[], - LDAPControl *clientctrls_out[], - size_t serverctrls_len, - size_t clientctrls_len, - ldap_handle_t *conn, - LDAPControl *serverctrls_in[], - LDAPControl *clientctrls_in[]) + void fr_ldap_control_merge(LDAPControl *serverctrls_out[], + LDAPControl *clientctrls_out[], + size_t serverctrls_len, + size_t clientctrls_len, + ldap_handle_t *conn, + LDAPControl *serverctrls_in[], + LDAPControl *clientctrls_in[]) { size_t i, num_serverctrls = 0, num_clientctrls = 0; @@ -86,7 +86,7 @@ * - 0 on success. * - -1 on failure (exceeded maximum controls). */ -int rlm_ldap_control_add_server(ldap_handle_t *conn, LDAPControl *ctrl, bool freeit) +int fr_ldap_control_add_server(ldap_handle_t *conn, LDAPControl *ctrl, bool freeit) { if ((size_t)conn->serverctrls_cnt >= ((sizeof(conn->serverctrls) / sizeof(conn->serverctrls[0])) - 1)) { return -1; @@ -110,7 +110,7 @@ int rlm_ldap_control_add_server(ldap_handle_t *conn, LDAPControl *ctrl, bool fre * - 0 on success. * - -1 on failure (exceeded maximum controls). */ -int rlm_ldap_control_add_client(ldap_handle_t *conn, LDAPControl *ctrl, bool freeit) +int fr_ldap_control_add_client(ldap_handle_t *conn, LDAPControl *ctrl, bool freeit) { if ((size_t)conn->clientctrls_cnt >= ((sizeof(conn->clientctrls) / sizeof(conn->clientctrls[0])) - 1)) { return -1; @@ -127,7 +127,7 @@ int rlm_ldap_control_add_client(ldap_handle_t *conn, LDAPControl *ctrl, bool fre * * @param conn to clear controls from. */ -void rlm_ldap_control_clear(ldap_handle_t *conn) +void fr_ldap_control_clear(ldap_handle_t *conn) { int i; @@ -161,7 +161,7 @@ void rlm_ldap_control_clear(ldap_handle_t *conn) * @param conn to add controls to. * @param request to draw attributes from. */ -int rlm_ldap_control_add_session_tracking(ldap_handle_t *conn, REQUEST *request) +int fr_ldap_control_add_session_tracking(ldap_handle_t *conn, REQUEST *request) { /* * The OpenLDAP guys didn't declare the formatOID parameter to @@ -267,15 +267,15 @@ int rlm_ldap_control_add_session_tracking(ldap_handle_t *conn, REQUEST *request) goto error; } - if (username_control && (rlm_ldap_control_add_server(conn, username_control, true) < 0)) goto error; + if (username_control && (fr_ldap_control_add_server(conn, username_control, true) < 0)) goto error; - if (acctsessionid_control && (rlm_ldap_control_add_server(conn, acctsessionid_control, true) < 0)) { + if (acctsessionid_control && (fr_ldap_control_add_server(conn, acctsessionid_control, true) < 0)) { conn->serverctrls_cnt--; conn->serverctrls[conn->serverctrls_cnt].control = NULL; goto error; } - if (acctmultisessionid_control && (rlm_ldap_control_add_server(conn, acctmultisessionid_control, true) < 0)) { + if (acctmultisessionid_control && (fr_ldap_control_add_server(conn, acctmultisessionid_control, true) < 0)) { conn->serverctrls_cnt--; conn->serverctrls[conn->serverctrls_cnt].control = NULL; conn->serverctrls_cnt--; diff --git a/src/modules/rlm_ldap/directory.c b/src/modules/rlm_ldap/directory.c index 34b0075bacf..4514b4d2bd6 100644 --- a/src/modules/rlm_ldap/directory.c +++ b/src/modules/rlm_ldap/directory.c @@ -24,10 +24,10 @@ * @copyright 2016 The FreeRADIUS Server Project. * @copyright 2016 Arran Cudbard-Bell */ -#define LOG_PREFIX "rlm_ldap (%s) - " -#define LOG_PREFIX_ARGS inst->name +#define LOG_PREFIX "%s - " +#define LOG_PREFIX_ARGS (*pconn)->config->name -#include "rlm_ldap.h" +#include "libldap.h" static FR_NAME_NUMBER const ldap_directory_type_table[] = { { "Unknown", LDAP_DIRECTORY_UNKNOWN }, @@ -49,14 +49,13 @@ static FR_NAME_NUMBER const ldap_directory_type_table[] = { * * @param[in] ctx to allocate ldap_directory_t in. * @param[out] out where to write pointer to new ldap_directory_t struct. - * @param[in] inst rlm_ldap configuration. * @param[in,out] pconn connection for querying the directory. * @return * - 0 on success. * - 1 if we failed identifying the directory server. * - -1 on error. */ -int rlm_ldap_directory_alloc(TALLOC_CTX *ctx, ldap_directory_t **out, rlm_ldap_t const *inst, ldap_handle_t **pconn) +int fr_ldap_directory_alloc(TALLOC_CTX *ctx, ldap_directory_t **out, ldap_handle_t **pconn) { static char const *attrs[] = { "vendorname", "vendorversion", @@ -82,7 +81,8 @@ int rlm_ldap_directory_alloc(TALLOC_CTX *ctx, ldap_directory_t **out, rlm_ldap_t directory->type = LDAP_DIRECTORY_UNKNOWN; - status = rlm_ldap_search(&result, inst, NULL, pconn, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, NULL, NULL); + status = fr_ldap_search(&result, NULL, pconn, "", LDAP_SCOPE_BASE, "(objectclass=*)", + attrs, NULL, NULL); switch (status) { case LDAP_PROC_SUCCESS: break; @@ -115,7 +115,7 @@ int rlm_ldap_directory_alloc(TALLOC_CTX *ctx, ldap_directory_t **out, rlm_ldap_t values = ldap_get_values_len((*pconn)->handle, entry, "vendorname"); if (values) { - directory->vendor_str = rlm_ldap_berval_to_string(directory, values[0]); + directory->vendor_str = fr_ldap_berval_to_string(directory, values[0]); INFO("Directory vendor: %s", directory->vendor_str); ldap_value_free_len(values); @@ -123,7 +123,7 @@ int rlm_ldap_directory_alloc(TALLOC_CTX *ctx, ldap_directory_t **out, rlm_ldap_t values = ldap_get_values_len((*pconn)->handle, entry, "vendorversion"); if (values) { - directory->version_str = rlm_ldap_berval_to_string(directory, values[0]); + directory->version_str = fr_ldap_berval_to_string(directory, values[0]); INFO("Directory version: %s", directory->version_str); ldap_value_free_len(values); diff --git a/src/modules/rlm_ldap/groups.c b/src/modules/rlm_ldap/groups.c index 28561b097d2..13a474e6ed5 100644 --- a/src/modules/rlm_ldap/groups.c +++ b/src/modules/rlm_ldap/groups.c @@ -87,7 +87,7 @@ static rlm_rcode_t rlm_ldap_group_name2dn(rlm_ldap_t const *inst, REQUEST *reque inst->groupobj_filter ? inst->groupobj_filter : "", names[0] && names[1] ? "(|" : ""); while (*name) { - rlm_ldap_escape_func(request, buffer, sizeof(buffer), *name++, NULL); + fr_ldap_escape_func(request, buffer, sizeof(buffer), *name++, NULL); filter = talloc_asprintf_append_buffer(filter, "(%s=%s)", inst->groupobj_name_attr, buffer); name_cnt++; @@ -97,14 +97,14 @@ static rlm_rcode_t rlm_ldap_group_name2dn(rlm_ldap_t const *inst, REQUEST *reque names[0] && names[1] ? ")" : ""); if (tmpl_expand(&base_dn, base_dn_buff, sizeof(base_dn_buff), request, - inst->groupobj_base_dn, rlm_ldap_escape_func, NULL) < 0) { + inst->groupobj_base_dn, fr_ldap_escape_func, NULL) < 0) { REDEBUG("Failed creating base_dn"); return RLM_MODULE_INVALID; } - status = rlm_ldap_search(&result, inst, request, pconn, base_dn, inst->groupobj_scope, - filter, attrs, NULL, NULL); + status = fr_ldap_search(&result, request, pconn, base_dn, inst->groupobj_scope, + filter, attrs, NULL, NULL); switch (status) { case LDAP_PROC_SUCCESS: break; @@ -156,7 +156,7 @@ static rlm_rcode_t rlm_ldap_group_name2dn(rlm_ldap_t const *inst, REQUEST *reque rcode = RLM_MODULE_FAIL; goto finish; } - rlm_ldap_normalise_dn(*dn, *dn); + fr_ldap_util_normalise_dn(*dn, *dn); RDEBUG("Got group DN \"%s\"", *dn); dn++; @@ -213,7 +213,7 @@ static rlm_rcode_t rlm_ldap_group_dn2name(rlm_ldap_t const *inst, REQUEST *reque RDEBUG("Resolving group DN \"%s\" to group name", dn); - status = rlm_ldap_search(&result, inst, request, pconn, dn, LDAP_SCOPE_BASE, NULL, attrs, NULL, NULL); + status = fr_ldap_search(&result, request, pconn, dn, LDAP_SCOPE_BASE, NULL, attrs, NULL, NULL); switch (status) { case LDAP_PROC_SUCCESS: break; @@ -244,7 +244,7 @@ static rlm_rcode_t rlm_ldap_group_dn2name(rlm_ldap_t const *inst, REQUEST *reque goto finish; } - *out = rlm_ldap_berval_to_string(request, values[0]); + *out = fr_ldap_berval_to_string(request, values[0]); RDEBUG("Group DN \"%s\" resolves to name \"%s\"", dn, *out); finish: @@ -259,7 +259,7 @@ finish: * @param[in] inst rlm_ldap configuration. * @param[in] request Current request. * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. - * @param[in] entry retrieved by rlm_ldap_find_user or rlm_ldap_search. + * @param[in] entry retrieved by rlm_ldap_find_user or fr_ldap_search. * @param[in] attr membership attribute to look for in the entry. * @return One of the RLM_MODULE_* values. */ @@ -314,7 +314,7 @@ rlm_rcode_t rlm_ldap_cacheable_userobj(rlm_ldap_t const *inst, REQUEST *request, fr_pair_cursor_init(&groups_cursor, &groups); for (i = 0; (i < LDAP_MAX_CACHEABLE) && (i < count); i++) { - is_dn = rlm_ldap_is_dn(values[i]->bv_val, values[i]->bv_len); + is_dn = fr_ldap_util_is_dn(values[i]->bv_val, values[i]->bv_len); if (inst->cacheable_group_dn) { /* @@ -329,7 +329,7 @@ rlm_rcode_t rlm_ldap_cacheable_userobj(rlm_ldap_t const *inst, REQUEST *request, * this to a DN. Store all the group names in an array so we can do one query. */ } else { - *name_p++ = rlm_ldap_berval_to_string(value_ctx, values[i]); + *name_p++ = fr_ldap_berval_to_string(value_ctx, values[i]); } } @@ -350,7 +350,7 @@ rlm_rcode_t rlm_ldap_cacheable_userobj(rlm_ldap_t const *inst, REQUEST *request, } else { char *dn; - dn = rlm_ldap_berval_to_string(value_ctx, values[i]); + dn = fr_ldap_berval_to_string(value_ctx, values[i]); rcode = rlm_ldap_group_dn2name(inst, request, pconn, dn, &name); talloc_free(dn); if (rcode != RLM_MODULE_OK) { @@ -439,21 +439,21 @@ rlm_rcode_t rlm_ldap_cacheable_groupobj(rlm_ldap_t const *inst, REQUEST *request return RLM_MODULE_OK; } - if (rlm_ldap_xlat_filter(request, + if (fr_ldap_xlat_filter(request, filters, sizeof(filters) / sizeof(*filters), filter, sizeof(filter)) < 0) { return RLM_MODULE_INVALID; } if (tmpl_expand(&base_dn, base_dn_buff, sizeof(base_dn_buff), request, - inst->groupobj_base_dn, rlm_ldap_escape_func, NULL) < 0) { + inst->groupobj_base_dn, fr_ldap_escape_func, NULL) < 0) { REDEBUG("Failed creating base_dn"); return RLM_MODULE_INVALID; } - status = rlm_ldap_search(&result, inst, request, pconn, base_dn, - inst->groupobj_scope, filter, attrs, NULL, NULL); + status = fr_ldap_search(&result, request, pconn, base_dn, + inst->groupobj_scope, filter, attrs, NULL, NULL); switch (status) { case LDAP_PROC_SUCCESS: break; @@ -485,7 +485,7 @@ rlm_rcode_t rlm_ldap_cacheable_groupobj(rlm_ldap_t const *inst, REQUEST *request goto finish; } - rlm_ldap_normalise_dn(dn, dn); + fr_ldap_util_normalise_dn(dn, dn); MEM(vp = pair_make_config(inst->cache_da->name, NULL, T_OP_ADD)); fr_pair_value_strcpy(vp, dn); @@ -557,11 +557,11 @@ rlm_rcode_t rlm_ldap_check_groupobj_dynamic(rlm_ldap_t const *inst, REQUEST *req RDEBUG2("Checking for user in group objects"); - if (rlm_ldap_is_dn(check->vp_strvalue, check->vp_length)) { + if (fr_ldap_util_is_dn(check->vp_strvalue, check->vp_length)) { char const *filters[] = { inst->groupobj_filter, inst->groupobj_membership_filter }; RINDENT(); - ret = rlm_ldap_xlat_filter(request, + ret = fr_ldap_xlat_filter(request, filters, sizeof(filters) / sizeof(*filters), filter, sizeof(filter)); REXDENT(); @@ -582,7 +582,7 @@ rlm_rcode_t rlm_ldap_check_groupobj_dynamic(rlm_ldap_t const *inst, REQUEST *req snprintf(name_filter, sizeof(name_filter), "(%s=%s)", inst->groupobj_name_attr, check->vp_strvalue); RINDENT(); - ret = rlm_ldap_xlat_filter(request, + ret = fr_ldap_xlat_filter(request, filters, sizeof(filters) / sizeof(*filters), filter, sizeof(filter)); REXDENT(); @@ -594,7 +594,7 @@ rlm_rcode_t rlm_ldap_check_groupobj_dynamic(rlm_ldap_t const *inst, REQUEST *req */ RINDENT(); ret = tmpl_expand(&base_dn, base_dn_buff, sizeof(base_dn_buff), request, inst->groupobj_base_dn, - rlm_ldap_escape_func, NULL); + fr_ldap_escape_func, NULL); REXDENT(); if (ret < 0) { REDEBUG("Failed creating base_dn"); @@ -604,7 +604,7 @@ rlm_rcode_t rlm_ldap_check_groupobj_dynamic(rlm_ldap_t const *inst, REQUEST *req } RINDENT(); - status = rlm_ldap_search(NULL, inst, request, pconn, base_dn, inst->groupobj_scope, filter, NULL, NULL, NULL); + status = fr_ldap_search(NULL, request, pconn, base_dn, inst->groupobj_scope, filter, NULL, NULL, NULL); REXDENT(); switch (status) { case LDAP_PROC_SUCCESS: @@ -646,7 +646,7 @@ rlm_rcode_t rlm_ldap_check_userobj_dynamic(rlm_ldap_t const *inst, REQUEST *requ RDEBUG2("Checking user object's %s attributes", inst->userobj_membership_attr); RINDENT(); - status = rlm_ldap_search(&result, inst, request, pconn, dn, LDAP_SCOPE_BASE, NULL, attrs, NULL, NULL); + status = fr_ldap_search(&result, request, pconn, dn, LDAP_SCOPE_BASE, NULL, attrs, NULL, NULL); REXDENT(); switch (status) { case LDAP_PROC_SUCCESS: @@ -683,10 +683,10 @@ rlm_rcode_t rlm_ldap_check_userobj_dynamic(rlm_ldap_t const *inst, REQUEST *requ * Loop over the list of groups the user is a member of, * looking for a match. */ - name_is_dn = rlm_ldap_is_dn(check->vp_strvalue, check->vp_length); + name_is_dn = fr_ldap_util_is_dn(check->vp_strvalue, check->vp_length); count = ldap_count_values_len(values); for (i = 0; i < count; i++) { - value_is_dn = rlm_ldap_is_dn(values[i]->bv_val, values[i]->bv_len); + value_is_dn = fr_ldap_util_is_dn(values[i]->bv_val, values[i]->bv_len); RDEBUG2("Processing %s value \"%.*s\" as a %s", inst->userobj_membership_attr, (int)values[i]->bv_len, values[i]->bv_val, value_is_dn ? "DN" : "group name"); @@ -769,7 +769,7 @@ rlm_rcode_t rlm_ldap_check_userobj_dynamic(rlm_ldap_t const *inst, REQUEST *requ char *value; bool eq = false; - value = rlm_ldap_berval_to_string(request, values[i]); + value = fr_ldap_berval_to_string(request, values[i]); RINDENT(); ret = rlm_ldap_group_dn2name(inst, request, pconn, value, &resolved); REXDENT(); diff --git a/src/modules/rlm_ldap/ldap.c b/src/modules/rlm_ldap/ldap.c index 22a92208b40..8ad5eb79e11 100644 --- a/src/modules/rlm_ldap/ldap.c +++ b/src/modules/rlm_ldap/ldap.c @@ -27,18 +27,28 @@ #include -#include -#include - -#define LOG_PREFIX "rlm_ldap (%s) - " -#define LOG_PREFIX_ARGS inst->name +#define LOG_PREFIX "%s - " +#define LOG_PREFIX_ARGS handle_config->name #include -#include -#include "rlm_ldap.h" +#include "libldap.h" + +LDAP *ldap_global_handle; //!< Hack for OpenLDAP libldap global initialisation. +static int instance_count = 0; + +/** Used to set the global log prefix for functions which don't operate on connections + * + */ +static ldap_handle_config_t ldap_global_handle_config = { + .name = "global" +}; -static const char specials[] = ",+\"\\<>;*=()"; -static const char hextab[] = "0123456789abcdef"; +/* + * Some functions may be called with a NULL request structure, this + * simplifies switching certain messages from the request log to + * the main log. + */ +#define LDAP_EXTRA_DEBUG() do { if (extra) { if (request) REDEBUG("%s", extra); else ERROR("%s", extra); }} while (0) FR_NAME_NUMBER const ldap_supported_extensions[] = { { "bindname", LDAP_DEREF_NEVER }, @@ -52,11 +62,13 @@ FR_NAME_NUMBER const ldap_supported_extensions[] = { * There are so many different timers in LDAP it's often hard to debug * issues with them, hence the need for this function. */ -void rlm_ldap_timeout_debug(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t const *conn, - struct timeval const *timeout, char const *prefix) +void fr_ldap_timeout_debug(REQUEST *request, ldap_handle_t const *conn, + struct timeval const *timeout, char const *prefix) { - struct timeval *net = NULL, *client = NULL; - int server = 0; + struct timeval *net = NULL, *client = NULL; + int server = 0; + ldap_handle_config_t const *handle_config = conn->config; + #ifdef LDAP_OPT_NETWORK_TIMEOUT if (ldap_get_option(conn->handle, LDAP_OPT_NETWORK_TIMEOUT, &net) != LDAP_OPT_SUCCESS) { @@ -109,437 +121,12 @@ void rlm_ldap_timeout_debug(rlm_ldap_t const *inst, REQUEST *request, ldap_handl free(client); } -/** Converts "bad" strings into ones which are safe for LDAP - * - * @note RFC 4515 says filter strings can only use the @verbatim \ @endverbatim - * format, whereas RFC 4514 indicates that some chars in DNs, may be escaped simply - * with a backslash. For simplicity, we always use the hex escape sequences. - * In other areas where we're doing DN comparison, the DNs need to be normalised first - * so that they both use only hex escape sequences. - * - * @note This is a callback for xlat operations. - * - * Will escape any characters in input strings that would cause the string to be interpreted - * as part of a DN and or filter. Escape sequence is @verbatim \ @endverbatim. - * - * @param request The current request. - * @param out Pointer to output buffer. - * @param outlen Size of the output buffer. - * @param in Raw unescaped string. - * @param arg Any additional arguments (unused). - */ -size_t rlm_ldap_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg) -{ - - size_t left = outlen; - - if (*in && ((*in == ' ') || (*in == '#'))) goto encode; - - while (*in) { - /* - * Encode unsafe characters. - */ - if (memchr(specials, *in, sizeof(specials) - 1)) { - encode: - /* - * Only 3 or less bytes available. - */ - if (left <= 3) break; - - *out++ = '\\'; - *out++ = hextab[(*in >> 4) & 0x0f]; - *out++ = hextab[*in & 0x0f]; - in++; - left -= 3; - - continue; - } - - if (left <= 1) break; - - /* - * Doesn't need encoding - */ - *out++ = *in++; - left--; - } - - *out = '\0'; - - return outlen - left; -} - -/** Converts escaped DNs and filter strings into normal - * - * @note RFC 4515 says filter strings can only use the @verbatim \ @endverbatim - * format, whereas RFC 4514 indicates that some chars in DNs, may be escaped simply - * with a backslash.. - * - * Will unescape any special characters in strings, or @verbatim \ @endverbatim - * sequences. - * - * @param request The current request. - * @param out Pointer to output buffer. - * @param outlen Size of the output buffer. - * @param in Escaped string string. - * @param arg Any additional arguments (unused). - */ -size_t rlm_ldap_unescape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg) -{ - char const *p; - char *c1, *c2, c3; - size_t freespace = outlen; - - if (outlen <= 1) return 0; - - p = in; - while (*p && (--freespace > 0)) { - if (*p != '\\') { - next: - *out++ = *p++; - continue; - } - - p++; - - /* It's an escaped special, just remove the slash */ - if (memchr(specials, *in, sizeof(specials) - 1)) { - *out++ = *p++; - continue; - } - - /* Is a hex sequence */ - if (!(c1 = memchr(hextab, tolower(p[0]), 16)) || - !(c2 = memchr(hextab, tolower(p[1]), 16))) goto next; - c3 = ((c1 - hextab) << 4) + (c2 - hextab); - - *out++ = c3; - p += 2; - } - - *out = '\0'; - - return outlen - freespace; -} - - -/** Check whether a string looks like a DN - * - * @param[in] in Str to check. - * @param[in] inlen Length of string to check. - * @return - * - true if string looks like a DN. - * - false if string does not look like DN. - */ -bool rlm_ldap_is_dn(char const *in, size_t inlen) -{ - char const *p; - - char want = '='; - bool too_soon = true; - int comp = 1; - - for (p = in; inlen > 0; p++, inlen--) { - if (p[0] == '\\') { - char c; - - too_soon = false; - - /* - * Invalid escape sequence, not a DN - */ - if (inlen < 2) return false; - - /* - * Double backslash, consume two chars - */ - if (p[1] == '\\') { - inlen--; - p++; - continue; - } - - /* - * Special, consume two chars - */ - switch (p[1]) { - case ' ': - case '#': - case '=': - case '"': - case '+': - case ',': - case ';': - case '<': - case '>': - case '\'': - inlen -= 1; - p += 1; - continue; - - default: - break; - } - - /* - * Invalid escape sequence, not a DN - */ - if (inlen < 3) return false; - - /* - * Hex encoding, consume three chars - */ - if (fr_hex2bin((uint8_t *) &c, 1, p + 1, 2) == 1) { - inlen -= 2; - p += 2; - continue; - } - - /* - * Invalid escape sequence, not a DN - */ - return false; - } - - switch (*p) { - case '=': - if (too_soon || (*p != want)) return false; /* Too soon after last , or = */ - want = ','; - too_soon = true; - break; - - case ',': - if (too_soon || (*p != want)) return false; /* Too soon after last , or = */ - want = '='; - too_soon = true; - comp++; - break; - - default: - too_soon = false; - break; - } - } - - /* - * If the string ended with , or =, or the number - * of components was less than 2 - * - * i.e. we don't have =,= - */ - if (too_soon || (comp < 2)) return false; - - return true; -} - -/** Convert a berval to a talloced string - * - * The ldap_get_values function is deprecated, and ldap_get_values_len - * does not guarantee the berval buffers it returns are \0 terminated. - * - * For some cases this is fine, for others we require a \0 terminated - * buffer (feeding DNs back into libldap for example). - * - * @param ctx to allocate in. - * @param in Berval to copy. - * @return \0 terminated buffer containing in->bv_val. - */ -char *rlm_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in) -{ - char *out; - - out = talloc_array(ctx, char, in->bv_len + 1); - if (!out) return NULL; - - memcpy(out, in->bv_val, in->bv_len); - out[in->bv_len] = '\0'; - - return out; -} - -/** Normalise escape sequences in a DN - * - * Characters in a DN can either be escaped as - * @verbatim \ @endverbatim or @verbatim \ @endverbatim - * - * The LDAP directory chooses how characters are escaped, which can make - * local comparisons of DNs difficult. - * - * Here we search for hex sequences that match special chars, and convert - * them to the @verbatim \ @endverbatim form. - * - * @note the resulting output string will only ever be shorter than the - * input, so it's fine to use the same buffer for both out and in. - * - * @param out Where to write the normalised DN. - * @param in The input DN. - * @return The number of bytes written to out. - */ -size_t rlm_ldap_normalise_dn(char *out, char const *in) -{ - char const *p; - char *o = out; - - for (p = in; *p != '\0'; p++) { - if (p[0] == '\\') { - char c; - - /* - * Double backslashes get processed specially - */ - if (p[1] == '\\') { - p += 1; - *o++ = p[0]; - *o++ = p[1]; - continue; - } - - /* - * Hex encodings that have an alternative - * special encoding, get rewritten to the - * special encoding. - */ - if (fr_hex2bin((uint8_t *) &c, 1, p + 1, 2) == 1) { - switch (c) { - case ' ': - case '#': - case '=': - case '"': - case '+': - case ',': - case ';': - case '<': - case '>': - case '\'': - *o++ = '\\'; - *o++ = c; - p += 2; - continue; - - default: - break; - } - } - } - *o++ = *p; - } - *o = '\0'; - - return o - out; -} - -/** Find the place at which the two DN strings diverge - * - * Returns the length of the non matching string in full. - * - * @param full DN. - * @param part Partial DN as returned by ldap_parse_result. - * @return - * - Length of the portion of full which wasn't matched - * - -1 on failure. - */ -static size_t rlm_ldap_common_dn(char const *full, char const *part) -{ - size_t f_len, p_len, i; - - if (!full) return -1; - - f_len = strlen(full); - - if (!part) return -1; - - p_len = strlen(part); - if (!p_len) return f_len; - - if ((f_len < p_len) || !f_len) return -1; - - for (i = 0; i < p_len; i++) if (part[p_len - i] != full[f_len - i]) return -1; - - return f_len - p_len; -} - -/** Combine and expand filters - * - * @param request Current request. - * @param out Where to write the expanded string. - * @param outlen Length of output buffer. - * @param sub Array of subfilters (may contain NULLs). - * @param sublen Number of potential subfilters in array. - * @return length of expanded data. - */ -ssize_t rlm_ldap_xlat_filter(REQUEST *request, char const **sub, size_t sublen, char *out, size_t outlen) -{ - char buffer[LDAP_MAX_FILTER_STR_LEN + 1]; - char const *in = NULL; - char *p = buffer; - - ssize_t len = 0; - - unsigned int i; - int cnt = 0; - - /* - * Figure out how many filter elements we need to integrate - */ - for (i = 0; i < sublen; i++) { - if (sub[i] && *sub[i]) { - in = sub[i]; - cnt++; - } - } - - if (!cnt) { - out[0] = '\0'; - return 0; - } - - if (cnt > 1) { - if (outlen < 3) { - goto oob; - } - - p[len++] = '('; - p[len++] = '&'; - - for (i = 0; i < sublen; i++) { - if (sub[i] && (*sub[i] != '\0')) { - len += strlcpy(p + len, sub[i], outlen - len); - - if ((size_t) len >= outlen) { - oob: - REDEBUG("Out of buffer space creating filter"); - - return -1; - } - } - } - - if ((outlen - len) < 2) { - goto oob; - } - - p[len++] = ')'; - p[len] = '\0'; - - in = buffer; - } - - len = xlat_eval(out, outlen, request, in, rlm_ldap_escape_func, NULL); - if (len < 0) { - REDEBUG("Failed creating filter"); - - return -1; - } - - return len; -} - /** Return the error string associated with a handle * * @param conn to retrieve error from. * @return error string. */ -char const *rlm_ldap_error_str(ldap_handle_t const *conn) +char const *fr_ldap_error_str(ldap_handle_t const *conn) { int lib_errno; ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER, &lib_errno); @@ -558,7 +145,6 @@ char const *rlm_ldap_error_str(ldap_handle_t const *conn) * Will also produce extended error output including any messages the server * sent, and information about partial DN matches. * - * @param[in] inst of LDAP module. * @param[in] conn Current connection. * @param[in] msgid returned from last operation. May be -1 if no result * processing is required. @@ -571,13 +157,12 @@ char const *rlm_ldap_error_str(ldap_handle_t const *conn) * (faster) or must be freed (with talloc_free). * @return One of the LDAP_PROC_* (#ldap_rcode_t) values. */ -ldap_rcode_t rlm_ldap_result(rlm_ldap_t const *inst, - ldap_handle_t const *conn, - int msgid, - char const *dn, - struct timeval const *timeout, - LDAPMessage **result, - char const **error, char **extra) +ldap_rcode_t fr_ldap_result(ldap_handle_t const *conn, + int msgid, + char const *dn, + struct timeval const *timeout, + LDAPMessage **result, + char const **error, char **extra) { ldap_rcode_t status = LDAP_PROC_SUCCESS; @@ -620,8 +205,7 @@ ldap_rcode_t rlm_ldap_result(rlm_ldap_t const *inst, if (msgid < 0) return LDAP_SUCCESS; /* No msgid and no error, return now */ if (!timeout) { - tv.tv_sec = inst->res_timeout; - tv.tv_usec = 0; + tv = conn->config->res_timeout; } else { tv = *timeout; } @@ -684,7 +268,7 @@ process_error: /* * Build our own internal diagnostic string */ - len = rlm_ldap_common_dn(dn, part_dn); + len = fr_ldap_common_dn(dn, part_dn); if (len < 0) break; our_err = talloc_typed_asprintf(conn, "Match stopped here: [%.*s]%s", len, dn, part_dn ? part_dn : ""); @@ -707,18 +291,18 @@ process_error: case LDAP_TIMEOUT: *error = "Timed out while waiting for server to respond"; - goto timeout; + status = LDAP_PROC_TIMEOUT; + break; case LDAP_TIMELIMIT_EXCEEDED: *error = "Time limit exceeded"; - timeout: - trigger_exec(NULL, inst->cs, "modules.ldap.timeout", true, NULL); - /* FALL-THROUGH */ + status = LDAP_PROC_TIMEOUT; + break; case LDAP_BUSY: case LDAP_UNAVAILABLE: case LDAP_SERVER_DOWN: - status = LDAP_PROC_RETRY; + status = LDAP_PROC_BAD_CONN; goto error_string; case LDAP_INVALID_CREDENTIALS: @@ -728,7 +312,7 @@ process_error: case LDAP_OPERATIONS_ERROR: *error = "Please set 'chase_referrals=yes' and 'rebind=yes'. See the ldap module configuration " - "for details."; + "for details"; /* FALL-THROUGH */ default: @@ -802,7 +386,6 @@ process_error: * * Performs a simple bind to the LDAP directory, and handles any errors that occur. * - * @param[in] inst rlm_ldap configuration. * @param[in] request Current request, this may be NULL, in which case all * debug logging is done with radlog. * @param[in,out] pconn to use. May change as this function calls functions @@ -810,46 +393,39 @@ process_error: * @param[in] dn of the user, may be NULL to bind anonymously. * @param[in] password of the user, may be NULL if no password is specified. * @param[in] sasl mechanism to use for bind, and additional parameters. - * @param[in] retry whether we're rebinding an existing connection. * @param[in] timeout Maximum time bind is allowed to take. * @param[in] serverctrls Only used for SASL binds. May be NULL. * @param[in] clientctrls Search controls for sasl_bind. * Only used for SASL binds. May be NULL. * @return One of the LDAP_PROC_* (#ldap_rcode_t) values. */ -ldap_rcode_t rlm_ldap_bind(rlm_ldap_t const *inst, - REQUEST *request, - ldap_handle_t **pconn, - char const *dn, char const *password, +ldap_rcode_t fr_ldap_bind(REQUEST *request, + ldap_handle_t **pconn, + char const *dn, char const *password, #ifdef WITH_SASL - ldap_sasl const *sasl, + ldap_sasl const *sasl, #else - NDEBUG_UNUSED ldap_sasl const *sasl, + NDEBUG_UNUSED ldap_sasl const *sasl, #endif - bool retry, - struct timeval const *timeout, - LDAPControl **serverctrls, LDAPControl **clientctrls) + struct timeval const *timeout, + LDAPControl **serverctrls, LDAPControl **clientctrls) { - ldap_rcode_t status = LDAP_PROC_ERROR; - ldap_pool_inst_t const *pool_inst = (*pconn)->pool_inst; - - int msgid = -1; + ldap_rcode_t status = LDAP_PROC_ERROR; + ldap_handle_config_t const *handle_config = (*pconn)->config; - char const *error = NULL; - char *extra = NULL; + int msgid = -1; - int i, num; - int conn_available; + char const *error = NULL; + char *extra = NULL; rad_assert(*pconn && (*pconn)->handle); - rad_assert(!retry || inst->pool); #ifndef WITH_SASL rad_assert(!sasl || !sasl->mech); #endif if (DEBUG_ENABLED4 || (request && RDEBUG_ENABLED4)) { - rlm_ldap_timeout_debug(inst, request, *pconn, timeout, __FUNCTION__); + fr_ldap_timeout_debug(request, *pconn, timeout, __FUNCTION__); } /* @@ -857,102 +433,57 @@ ldap_rcode_t rlm_ldap_bind(rlm_ldap_t const *inst, */ if (!dn) dn = ""; - /* - * Pool isn't available during module instantiation - */ - conn_available = inst->pool ? fr_connection_pool_state(inst->pool)->num : 0; - - /* - * For sanity, for when no connections are viable, - * and we can't make a new one. - */ - num = retry ? conn_available : 0; - for (i = num; i >= 0; i--) { #ifdef WITH_SASL - if (sasl && sasl->mech) { - status = rlm_ldap_sasl_interactive(inst, request, *pconn, dn, password, sasl, - serverctrls, clientctrls, timeout, &error, &extra); - } else + if (sasl && sasl->mech) { + status = fr_ldap_sasl_interactive(request, *pconn, dn, password, sasl, + serverctrls, clientctrls, timeout, &error, &extra); + } else #endif - { - int ret; - struct berval cred; - - if (password) { - memcpy(&cred.bv_val, &password, sizeof(cred.bv_val)); - cred.bv_len = talloc_array_length(password) - 1; - } else { - cred.bv_val = NULL; - cred.bv_len = 0; - } - - /* - * Yes, confusingly named. This is the simple version - * of the SASL bind function that should always be - * available. - */ - ret = ldap_sasl_bind((*pconn)->handle, dn, LDAP_SASL_SIMPLE, &cred, - serverctrls, clientctrls, &msgid); - - /* We got a valid message ID */ - if ((ret == 0) && (msgid >= 0)) ROPTIONAL(RDEBUG2, DEBUG2, "Waiting for bind result..."); + { + int ret; + struct berval cred; - status = rlm_ldap_result(inst, *pconn, msgid, dn, timeout, NULL, &error, &extra); + if (password) { + memcpy(&cred.bv_val, &password, sizeof(cred.bv_val)); + cred.bv_len = talloc_array_length(password) - 1; + } else { + cred.bv_val = NULL; + cred.bv_len = 0; } - switch (status) { - case LDAP_PROC_SUCCESS: - ROPTIONAL(RDEBUG, DEBUG, "Bind successful"); - break; - - case LDAP_PROC_NOT_PERMITTED: - ROPTIONAL(REDEBUG, ERROR, "Bind was not permitted: %s", error); - LDAP_EXTRA_DEBUG(); - - break; - - case LDAP_PROC_REJECT: - ROPTIONAL(REDEBUG, ERROR, "Bind credentials incorrect: %s", error); - LDAP_EXTRA_DEBUG(); - - break; + /* + * Yes, confusingly named. This is the simple version + * of the SASL bind function that should always be + * available. + */ + ret = ldap_sasl_bind((*pconn)->handle, dn, LDAP_SASL_SIMPLE, &cred, + serverctrls, clientctrls, &msgid); - case LDAP_PROC_RETRY: - if (retry) { - *pconn = fr_connection_reconnect(inst->pool, request, *pconn); - if (*pconn) { - ROPTIONAL(RWDEBUG, WARN, "Bind with %s to %s failed: %s. Got new socket, " - "retrying...", *dn ? dn : "(anonymous)", - pool_inst->server, error); + /* We got a valid message ID */ + if ((ret == 0) && (msgid >= 0)) ROPTIONAL(RDEBUG2, DEBUG2, "Waiting for bind result..."); - talloc_free(extra); /* don't leak debug info */ + status = fr_ldap_result(*pconn, msgid, dn, NULL, NULL, &error, &extra); + } - continue; - } - }; - status = LDAP_PROC_ERROR; + switch (status) { + case LDAP_PROC_SUCCESS: + ROPTIONAL(RDEBUG, DEBUG, "Bind successful"); + break; - /* - * Were not allowed to retry, or there are no more - * sockets, treat this as a hard failure. - */ - /* FALL-THROUGH */ - default: - ROPTIONAL(REDEBUG, ERROR, "Bind with %s to %s failed: %s", *dn ? dn : "(anonymous)", - pool_inst->server, error); - LDAP_EXTRA_DEBUG(); + case LDAP_PROC_NOT_PERMITTED: + ROPTIONAL(REDEBUG, ERROR, "Bind as \"%s\" to \"%s\" not permitted: %s", *dn ? dn : "(anonymous)", + handle_config->server, error); + LDAP_EXTRA_DEBUG(); + break; - break; - } + default: + ROPTIONAL(REDEBUG, ERROR, "Bind as \"%s\" to \"%s\" failed: %s", *dn ? dn : "(anonymous)", + handle_config->server, error); + LDAP_EXTRA_DEBUG(); break; } - if (retry && (i < 0)) { - ROPTIONAL(REDEBUG, ERROR, "Hit reconnection limit"); - status = LDAP_PROC_ERROR; - } - talloc_free(extra); return status; /* caller closes the connection */ @@ -962,53 +493,51 @@ ldap_rcode_t rlm_ldap_bind(rlm_ldap_t const *inst, * * Binds as the administrative user and performs a search, dealing with any errors. * - * @param[out] result Where to store the result. Must be freed with ldap_msgfree if LDAP_PROC_SUCCESS is returned. - * May be NULL in which case result will be automatically freed after use. - * @param[in] inst rlm_ldap configuration. - * @param[in] request Current request. - * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. - * @param[in] dn to use as base for the search. - * @param[in] scope to use (LDAP_SCOPE_BASE, LDAP_SCOPE_ONE, LDAP_SCOPE_SUB). - * @param[in] filter to use, should be pre-escaped. - * @param[in] attrs to retrieve. - * @param[in] serverctrls Search controls to pass to the server. May be NULL. - * @param[in] clientctrls Search controls for ldap_search. May be NULL. + * @param[out] result Where to store the result. Must be freed with ldap_msgfree + * if LDAP_PROC_SUCCESS is returned. + * May be NULL in which case result will be automatically freed after use. + * @param[in] request Current request. + * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. + * @param[in] dn to use as base for the search. + * @param[in] scope to use (LDAP_SCOPE_BASE, LDAP_SCOPE_ONE, LDAP_SCOPE_SUB). + * @param[in] filter to use, should be pre-escaped. + * @param[in] attrs to retrieve. + * @param[in] serverctrls Search controls to pass to the server. May be NULL. + * @param[in] clientctrls Search controls for ldap_search. May be NULL. * @return One of the LDAP_PROC_* (#ldap_rcode_t) values. */ -ldap_rcode_t rlm_ldap_search(LDAPMessage **result, rlm_ldap_t const *inst, REQUEST *request, - ldap_handle_t **pconn, - char const *dn, int scope, char const *filter, char const * const *attrs, - LDAPControl **serverctrls, LDAPControl **clientctrls) +ldap_rcode_t fr_ldap_search(LDAPMessage **result, REQUEST *request, + ldap_handle_t **pconn, + char const *dn, int scope, char const *filter, char const * const *attrs, + LDAPControl **serverctrls, LDAPControl **clientctrls) { - ldap_rcode_t status = LDAP_PROC_ERROR; - LDAPMessage *our_result = NULL; - - int msgid; // Message id returned by - // ldap_search_ext. + ldap_rcode_t status = LDAP_PROC_ERROR; + LDAPMessage *our_result = NULL; - int count = 0; // Number of results we got. + ldap_handle_config_t const *handle_config = (*pconn)->config; - struct timeval tv; // Holds timeout values. + int msgid; // Message id returned by + // ldap_search_ext. - char const *error = NULL; - char *extra = NULL; + int count = 0; // Number of results we got. - int i; + struct timeval tv; // Holds timeout values. - int conn_available; + char const *error = NULL; + char *extra = NULL; - LDAPControl *our_serverctrls[LDAP_MAX_CONTROLS]; - LDAPControl *our_clientctrls[LDAP_MAX_CONTROLS]; + LDAPControl *our_serverctrls[LDAP_MAX_CONTROLS]; + LDAPControl *our_clientctrls[LDAP_MAX_CONTROLS]; - rlm_ldap_control_merge(our_serverctrls, our_clientctrls, - sizeof(our_serverctrls) / sizeof(*our_serverctrls), - sizeof(our_clientctrls) / sizeof(*our_clientctrls), - *pconn, serverctrls, clientctrls); + fr_ldap_control_merge(our_serverctrls, our_clientctrls, + sizeof(our_serverctrls) / sizeof(*our_serverctrls), + sizeof(our_clientctrls) / sizeof(*our_clientctrls), + *pconn, serverctrls, clientctrls); rad_assert(*pconn && (*pconn)->handle); if (DEBUG_ENABLED4 || (request && RDEBUG_ENABLED4)) { - rlm_ldap_timeout_debug(inst, request, *pconn, NULL, __FUNCTION__); + fr_ldap_timeout_debug(request, *pconn, NULL, __FUNCTION__); } /* @@ -1022,12 +551,11 @@ ldap_rcode_t rlm_ldap_search(LDAPMessage **result, rlm_ldap_t const *inst, REQUE * Do all searches as the admin user. */ if ((*pconn)->rebound) { - status = rlm_ldap_bind(inst, request, pconn, (*pconn)->pool_inst->admin_identity, - (*pconn)->pool_inst->admin_password, &(*pconn)->pool_inst->admin_sasl, true, - NULL, NULL, NULL); - if (status != LDAP_PROC_SUCCESS) { - return LDAP_PROC_ERROR; - } + status = fr_ldap_bind(request, pconn, + (*pconn)->config->admin_identity, (*pconn)->config->admin_password, + &(*pconn)->config->admin_sasl, NULL, + NULL, NULL); + if (status != LDAP_PROC_SUCCESS) return LDAP_PROC_ERROR; rad_assert(*pconn); @@ -1047,71 +575,42 @@ ldap_rcode_t rlm_ldap_search(LDAPMessage **result, rlm_ldap_t const *inst, REQUE * having to pass it explicitly. */ memset(&tv, 0, sizeof(tv)); - tv.tv_sec = inst->res_timeout; - /* - * Pool isn't available during module instantiation - */ - conn_available = inst->pool ? fr_connection_pool_state(inst->pool)->num : 0; + (void) ldap_search_ext((*pconn)->handle, dn, scope, filter, search_attrs, + 0, our_serverctrls, our_clientctrls, NULL, 0, &msgid); + + ROPTIONAL(RDEBUG, DEBUG, "Waiting for search result..."); + status = fr_ldap_result(*pconn, msgid, dn, NULL, &our_result, &error, &extra); + switch (status) { + case LDAP_PROC_SUCCESS: + break; /* - * For sanity, for when no connections are viable, - * and we can't make a new one. + * Invalid DN isn't a failure when searching. + * The DN may be xlat expanded so may point directly + * to an LDAP object. If that can't be located, it's + * the same as notfound. */ - for (i = conn_available; i >= 0; i--) { - (void) ldap_search_ext((*pconn)->handle, dn, scope, filter, search_attrs, - 0, our_serverctrls, our_clientctrls, &tv, 0, &msgid); - - ROPTIONAL(RDEBUG, DEBUG, "Waiting for search result..."); - status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &our_result, &error, &extra); - switch (status) { - case LDAP_PROC_SUCCESS: - break; - - /* - * Invalid DN isn't a failure when searching. - * The DN may be xlat expanded so may point directly - * to an LDAP object. If that can't be located, it's - * the same as notfound. - */ - case LDAP_PROC_BAD_DN: - ROPTIONAL(RDEBUG, DEBUG, "%s", error); - if (extra) ROPTIONAL(RDEBUG, DEBUG, "%s", extra); - break; - - case LDAP_PROC_RETRY: - *pconn = fr_connection_reconnect(inst->pool, request, *pconn); - if (*pconn) { - ROPTIONAL(RWDEBUG, WARN, "Search failed: %s. Got new socket, retrying...", error); - - talloc_free(extra); /* don't leak debug info */ - - continue; - } - - status = LDAP_PROC_ERROR; - - /* FALL-THROUGH */ - default: - ROPTIONAL(REDEBUG, ERROR, "Failed performing search: %s", error); - if (extra) ROPTIONAL(REDEBUG, ERROR, "%s", extra); - - goto finish; - } - + case LDAP_PROC_BAD_DN: + ROPTIONAL(RDEBUG, DEBUG, "%s", error); + if (extra) ROPTIONAL(RDEBUG, DEBUG, "%s", extra); break; - } - if (i < 0) { - ROPTIONAL(REDEBUG, ERROR, "Hit reconnection limit"); + case LDAP_PROC_BAD_CONN: status = LDAP_PROC_ERROR; + goto finish; + + /* FALL-THROUGH */ + default: + ROPTIONAL(REDEBUG, ERROR, "Failed performing search: %s", error); + if (extra) ROPTIONAL(REDEBUG, ERROR, "%s", extra); goto finish; } count = ldap_count_entries((*pconn)->handle, our_result); if (count < 0) { - ROPTIONAL(REDEBUG, ERROR, "Error counting results: %s", rlm_ldap_error_str(*pconn)); + ROPTIONAL(REDEBUG, ERROR, "Error counting results: %s", fr_ldap_error_str(*pconn)); status = LDAP_PROC_ERROR; ldap_msgfree(our_result); @@ -1145,18 +644,17 @@ finish: * * Binds as the administrative user and attempts to modify an LDAP object. * - * @param[in] inst rlm_ldap configuration. - * @param[in] request Current request. - * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. - * @param[in] dn of the object to modify. - * @param[in] mods to make, see 'man ldap_modify' for more information. - * @param[in] serverctrls Search controls to pass to the server. May be NULL. - * @param[in] clientctrls Search controls for ldap_modify. May be NULL. + * @param[in] request Current request. + * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. + * @param[in] dn of the object to modify. + * @param[in] mods to make, see 'man ldap_modify' for more information. + * @param[in] serverctrls Search controls to pass to the server. May be NULL. + * @param[in] clientctrls Search controls for ldap_modify. May be NULL. * @return One of the LDAP_PROC_* (#ldap_rcode_t) values. */ -ldap_rcode_t rlm_ldap_modify(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, - char const *dn, LDAPMod *mods[], - LDAPControl **serverctrls, LDAPControl **clientctrls) +ldap_rcode_t fr_ldap_modify(REQUEST *request, ldap_handle_t **pconn, + char const *dn, LDAPMod *mods[], + LDAPControl **serverctrls, LDAPControl **clientctrls) { ldap_rcode_t status = LDAP_PROC_ERROR; @@ -1165,29 +663,26 @@ ldap_rcode_t rlm_ldap_modify(rlm_ldap_t const *inst, REQUEST *request, ldap_hand char const *error = NULL; char *extra = NULL; - int i; - LDAPControl *our_serverctrls[LDAP_MAX_CONTROLS]; LDAPControl *our_clientctrls[LDAP_MAX_CONTROLS]; - int conn_available = 0; - - rlm_ldap_control_merge(our_serverctrls, our_clientctrls, - sizeof(our_serverctrls) / sizeof(*our_serverctrls), - sizeof(our_clientctrls) / sizeof(*our_clientctrls), - *pconn, serverctrls, clientctrls); + fr_ldap_control_merge(our_serverctrls, our_clientctrls, + sizeof(our_serverctrls) / sizeof(*our_serverctrls), + sizeof(our_clientctrls) / sizeof(*our_clientctrls), + *pconn, serverctrls, clientctrls); rad_assert(*pconn && (*pconn)->handle); - if (RDEBUG_ENABLED4) rlm_ldap_timeout_debug(inst, request, *pconn, NULL, __FUNCTION__); + if (RDEBUG_ENABLED4) fr_ldap_timeout_debug(request, *pconn, NULL, __FUNCTION__); /* * Perform all modifications as the admin user. */ if ((*pconn)->rebound) { - status = rlm_ldap_bind(inst, request, pconn, (*pconn)->pool_inst->admin_identity, - (*pconn)->pool_inst->admin_password, &(*pconn)->pool_inst->admin_sasl, true, - NULL, NULL, NULL); + status = fr_ldap_bind(request, pconn, + (*pconn)->config->admin_identity, (*pconn)->config->admin_password, + &(*pconn)->config->admin_sasl, + NULL, NULL, NULL); if (status != LDAP_PROC_SUCCESS) { return LDAP_PROC_ERROR; } @@ -1197,361 +692,30 @@ ldap_rcode_t rlm_ldap_modify(rlm_ldap_t const *inst, REQUEST *request, ldap_hand (*pconn)->rebound = false; } - /* - * Pool isn't available during module instantiation - */ - conn_available = inst->pool ? fr_connection_pool_state(inst->pool)->num : 0; - - /* - * For sanity, for when no connections are viable, - * and we can't make a new one. - */ - for (i = conn_available; i >= 0; i--) { - RDEBUG2("Modifying object with DN \"%s\"", dn); - (void) ldap_modify_ext((*pconn)->handle, dn, mods, our_serverctrls, our_clientctrls, &msgid); - - RDEBUG2("Waiting for modify result..."); - status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, NULL, &error, &extra); - switch (status) { - case LDAP_PROC_SUCCESS: - break; - - case LDAP_PROC_RETRY: - *pconn = fr_connection_reconnect(inst->pool, request, *pconn); - if (*pconn) { - RWDEBUG("Modify failed: %s. Got new socket, retrying...", error); - - talloc_free(extra); /* don't leak debug info */ - continue; - } - - status = LDAP_PROC_ERROR; - - /* FALL-THROUGH */ - default: - REDEBUG("Failed modifying object: %s", error); - REDEBUG("%s", extra); - - goto finish; - } - - break; - } - - if (i < 0) { - ROPTIONAL(REDEBUG, ERROR, "Hit reconnection limit"); - status = LDAP_PROC_ERROR; - } - -finish: - talloc_free(extra); - - return status; -} - -/** Retrieve the DN of a user object - * - * Retrieves the DN of a user and adds it to the control list as LDAP-UserDN. Will also retrieve any - * attributes passed and return the result in *result. - * - * This potentially allows for all authorization and authentication checks to be performed in one - * ldap search operation, which is a big bonus given the number of crappy, slow *cough*AD*cough* - * LDAP directory servers out there. - * - * @param[in] inst rlm_ldap configuration. - * @param[in] request Current request. - * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. - * @param[in] attrs Additional attributes to retrieve, may be NULL. - * @param[in] force Query even if the User-DN already exists. - * @param[out] result Where to write the result, may be NULL in which case result is discarded. - * @param[out] rcode The status of the operation, one of the RLM_MODULE_* codes. - * @return The user's DN or NULL on error. - */ -char const *rlm_ldap_find_user(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, - char const *attrs[], bool force, LDAPMessage **result, rlm_rcode_t *rcode) -{ - static char const *tmp_attrs[] = { NULL }; - - ldap_rcode_t status; - VALUE_PAIR *vp = NULL; - LDAPMessage *tmp_msg = NULL, *entry = NULL; - int ldap_errno; - int cnt; - char *dn = NULL; - char const *filter = NULL; - char filter_buff[LDAP_MAX_FILTER_STR_LEN]; - char const *base_dn; - char base_dn_buff[LDAP_MAX_DN_STR_LEN]; - LDAPControl *serverctrls[] = { inst->userobj_sort_ctrl, NULL }; - - bool freeit = false; //!< Whether the message should - //!< be freed after being processed. - - *rcode = RLM_MODULE_FAIL; - - if (!result) { - result = &tmp_msg; - freeit = true; - } - *result = NULL; - - if (!attrs) { - memset(&attrs, 0, sizeof(tmp_attrs)); - } - - /* - * If the caller isn't looking for the result we can just return the current userdn value. - */ - if (!force) { - vp = fr_pair_find_by_num(request->control, 0, PW_LDAP_USERDN, TAG_ANY); - if (vp) { - RDEBUG("Using user DN from request \"%s\"", vp->vp_strvalue); - *rcode = RLM_MODULE_OK; - return vp->vp_strvalue; - } - } - - /* - * Perform all searches as the admin user. - */ - if ((*pconn)->rebound) { - status = rlm_ldap_bind(inst, request, pconn, (*pconn)->pool_inst->admin_identity, - (*pconn)->pool_inst->admin_password, &(*pconn)->pool_inst->admin_sasl, true, - NULL, NULL, NULL); - if (status != LDAP_PROC_SUCCESS) { - *rcode = RLM_MODULE_FAIL; - return NULL; - } - - rad_assert(*pconn); - - (*pconn)->rebound = false; - } - - if (inst->userobj_filter) { - if (tmpl_expand(&filter, filter_buff, sizeof(filter_buff), request, inst->userobj_filter, - rlm_ldap_escape_func, NULL) < 0) { - REDEBUG("Unable to create filter"); - *rcode = RLM_MODULE_INVALID; - - return NULL; - } - } - - if (tmpl_expand(&base_dn, base_dn_buff, sizeof(base_dn_buff), request, - inst->userobj_base_dn, rlm_ldap_escape_func, NULL) < 0) { - REDEBUG("Unable to create base_dn"); - *rcode = RLM_MODULE_INVALID; - - return NULL; - } + RDEBUG2("Modifying object with DN \"%s\"", dn); + (void) ldap_modify_ext((*pconn)->handle, dn, mods, our_serverctrls, our_clientctrls, &msgid); - status = rlm_ldap_search(result, inst, request, pconn, base_dn, - inst->userobj_scope, filter, attrs, serverctrls, NULL); + RDEBUG2("Waiting for modify result..."); + status = fr_ldap_result(*pconn, msgid, dn, NULL, NULL, &error, &extra); switch (status) { case LDAP_PROC_SUCCESS: break; - case LDAP_PROC_BAD_DN: - case LDAP_PROC_NO_RESULT: - *rcode = RLM_MODULE_NOTFOUND; - return NULL; + case LDAP_PROC_BAD_CONN: + break; + /* FALL-THROUGH */ default: - *rcode = RLM_MODULE_FAIL; - return NULL; - } - - rad_assert(*pconn); - - /* - * Forbid the use of unsorted search results that - * contain multiple entries, as it's a potential - * security issue, and likely non deterministic. - */ - if (!inst->userobj_sort_ctrl) { - cnt = ldap_count_entries((*pconn)->handle, *result); - if (cnt > 1) { - REDEBUG("Ambiguous search result, returned %i unsorted entries (should return 1 or 0). " - "Enable sorting, or specify a more restrictive base_dn, filter or scope", cnt); - REDEBUG("The following entries were returned:"); - RINDENT(); - for (entry = ldap_first_entry((*pconn)->handle, *result); - entry; - entry = ldap_next_entry((*pconn)->handle, entry)) { - dn = ldap_get_dn((*pconn)->handle, entry); - REDEBUG("%s", dn); - ldap_memfree(dn); - } - REXDENT(); - *rcode = RLM_MODULE_INVALID; - goto finish; - } - } - - entry = ldap_first_entry((*pconn)->handle, *result); - if (!entry) { - ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno); - REDEBUG("Failed retrieving entry: %s", - ldap_err2string(ldap_errno)); - - goto finish; - } - - dn = ldap_get_dn((*pconn)->handle, entry); - if (!dn) { - ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno); - REDEBUG("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno)); + REDEBUG("Failed modifying object: %s", error); + REDEBUG("%s", extra); goto finish; } - rlm_ldap_normalise_dn(dn, dn); - - /* - * We can't use fr_pair_make here to copy the value into the - * attribute, as the dn must be copied into the attribute - * verbatim (without de-escaping). - * - * Special chars are pre-escaped by libldap, and because - * we pass the string back to libldap we must not alter it. - */ - RDEBUG("User object found at DN \"%s\"", dn); - vp = fr_pair_make(request, &request->control, "LDAP-UserDN", NULL, T_OP_EQ); - if (vp) { - fr_pair_value_strcpy(vp, dn); - *rcode = RLM_MODULE_OK; - } - ldap_memfree(dn); finish: - if ((freeit || (*rcode != RLM_MODULE_OK)) && *result) { - ldap_msgfree(*result); - *result = NULL; - } - - return vp ? vp->vp_strvalue : NULL; -} - -/** Check for presence of access attribute in result - * - * @param[in] inst rlm_ldap configuration. - * @param[in] request Current request. - * @param[in] conn used to retrieve access attributes. - * @param[in] entry retrieved by rlm_ldap_find_user or rlm_ldap_search. - * @return - * - #RLM_MODULE_USERLOCK if the user was denied access. - * - #RLM_MODULE_OK otherwise. - */ -rlm_rcode_t rlm_ldap_check_access(rlm_ldap_t const *inst, REQUEST *request, - ldap_handle_t const *conn, LDAPMessage *entry) -{ - rlm_rcode_t rcode = RLM_MODULE_OK; - struct berval **values = NULL; - - values = ldap_get_values_len(conn->handle, entry, inst->userobj_access_attr); - if (values) { - if (inst->access_positive) { - if ((values[0]->bv_len >= 5) && (strncasecmp(values[0]->bv_val, "false", 5) == 0)) { - RDEBUG("\"%s\" attribute exists but is set to 'false' - user locked out", - inst->userobj_access_attr); - rcode = RLM_MODULE_USERLOCK; - } - /* RLM_MODULE_OK set above... */ - } else if ((values[0]->bv_len < 5) || (strncasecmp(values[0]->bv_val, "false", 5) != 0)) { - RDEBUG("\"%s\" attribute exists - user locked out", inst->userobj_access_attr); - rcode = RLM_MODULE_USERLOCK; - } - ldap_value_free_len(values); - } else if (inst->access_positive) { - RDEBUG("No \"%s\" attribute - user locked out", inst->userobj_access_attr); - rcode = RLM_MODULE_USERLOCK; - } - - return rcode; -} + talloc_free(extra); -/** Verify we got a password from the search - * - * Checks to see if after the LDAP to RADIUS mapping has been completed that a reference password. - * - * @param inst rlm_ldap configuration. - * @param request Current request. - * @param conn the connection handle - */ -void rlm_ldap_check_reply(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t const *conn) -{ - /* - * More warning messages for people who can't be bothered to read the documentation. - * - * Expect_password is set when we process the mapping, and is only true if there was a mapping between - * an LDAP attribute and a password reference attribute in the control list. - */ - if (!inst->expect_password || (rad_debug_lvl < L_DBG_LVL_2)) return; - - if (!fr_pair_find_by_num(request->control, 0, PW_CLEARTEXT_PASSWORD, TAG_ANY) && - !fr_pair_find_by_num(request->control, 0, PW_NT_PASSWORD, TAG_ANY) && - !fr_pair_find_by_num(request->control, 0, PW_USER_PASSWORD, TAG_ANY) && - !fr_pair_find_by_num(request->control, 0, PW_PASSWORD_WITH_HEADER, TAG_ANY) && - !fr_pair_find_by_num(request->control, 0, PW_CRYPT_PASSWORD, TAG_ANY)) { - switch (conn->directory->type) { - case LDAP_DIRECTORY_ACTIVE_DIRECTORY: - RWDEBUG("!!! Found map between LDAP attribute and a FreeRADIUS password attribute"); - RWDEBUG("!!! Active Directory does not allow passwords to be read via LDAP"); - RWDEBUG("!!! Remove the password map and either:"); - RWDEBUG("!!! - Configure authentication via ntlm_auth (mschapv2 only)"); - RWDEBUG("!!! - Configure authentication via wbclient (mschapv2 only)"); - RWDEBUG("!!! that password attribute"); - RWDEBUG("!!! - Bind as the user by listing %s in the authenticate section, and", - inst->name); - RWDEBUG("!!! setting attribute &control:Auth-Type := '%s' in the authorize section", - inst->name); - RWDEBUG("!!! (pap only)"); - - break; - - case LDAP_DIRECTORY_EDIRECTORY: - RWDEBUG("!!! Found map between LDAP attribute and a FreeRADIUS password attribute"); - RWDEBUG("!!! eDirectory does not allow passwords to be retrieved via LDAP search"); - RWDEBUG("!!! Remove the password map and either:"); - RWDEBUG("!!! - Set 'edir = yes' and enable the universal password feature on your "); - RWDEBUG("!!! eDir server (recommended)"); - RWDEBUG("!!! that password attribute"); - RWDEBUG("!!! - Bind as the user by listing %s in the authenticate section, and", - inst->name); - RWDEBUG("!!! setting attribute &control:Auth-Type := '%s' in the authorize section", - inst->name); - RWDEBUG("!!! (pap only)"); - break; - - default: - if (!conn->pool_inst->admin_identity) { - RWDEBUG("!!! Found map between LDAP attribute and a FreeRADIUS password attribute"); - RWDEBUG("!!! but no password attribute found in search result"); - RWDEBUG("!!! Either:"); - RWDEBUG("!!! - Ensure the user object contains a password attribute, and that "); - RWDEBUG("!!! \"%s\" has permission to read that password attribute (recommended)", - conn->pool_inst->admin_identity); - RWDEBUG("!!! - Bind as the user by listing %s in the authenticate section, and", - inst->name); - RWDEBUG("!!! setting attribute &control:Auth-Type := '%s' in the authorize section", - inst->name); - RWDEBUG("!!! (pap only)"); - } else { - RWDEBUG("!!! No \"known good\" password added"); - RWDEBUG("!!! but no password attribute found in search result"); - RWDEBUG("!!! Either:"); - RWDEBUG("!!! - Ensure the user object contains a password attribute, and that "); - RWDEBUG("!!! 'identity' is set to the DN of an account that has permission to read"); - RWDEBUG("!!! that password attribute"); - RWDEBUG("!!! - Bind as the user by listing %s in the authenticate section, and", - inst->name); - RWDEBUG("!!! setting attribute &control:Auth-Type := '%s' in the authorize section", - inst->name); - RWDEBUG("!!! (pap only)"); - } - break; - } - } + return status; } #if LDAP_SET_REBIND_PROC_ARGS == 3 @@ -1563,15 +727,14 @@ void rlm_ldap_check_reply(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_ * @param url to bind to. * @param request that triggered the rebind. * @param msgid that triggered the rebind. - * @param ctx rlm_ldap configuration. + * @param ctx fr_ldap configuration. */ -static int rlm_ldap_rebind(LDAP *handle, LDAP_CONST char *url, UNUSED ber_tag_t request, UNUSED ber_int_t msgid, - void *ctx) +static int fr_ldap_rebind(LDAP *handle, LDAP_CONST char *url, + UNUSED ber_tag_t request, UNUSED ber_int_t msgid, void *ctx) { ldap_rcode_t status; ldap_handle_t *conn = talloc_get_type_abort(ctx, ldap_handle_t); - ldap_pool_inst_t const *pool_inst = conn->pool_inst; - rlm_ldap_t const *inst = conn->inst; + ldap_handle_config_t const *handle_config = conn->config; char const *admin_identity = NULL; char const *admin_password = NULL; @@ -1590,7 +753,7 @@ static int rlm_ldap_rebind(LDAP *handle, LDAP_CONST char *url, UNUSED ber_tag_t * SASL mech is inherited from the module that defined the connection * pool. */ - if (pool_inst->use_referral_credentials) { + if (handle_config->use_referral_credentials) { LDAPURLDesc *ldap_url; int ret; char **ext; @@ -1641,7 +804,7 @@ static int rlm_ldap_rebind(LDAP *handle, LDAP_CONST char *url, UNUSED ber_tag_t default: if (critical) { ERROR("Failed parsing critical extension \"%s\": " - "Not supported by rlm_ldap", *ext); + "Not supported by FreeRADIUS", *ext); ldap_free_urldesc(ldap_url); return LDAP_OTHER; } @@ -1653,12 +816,12 @@ static int rlm_ldap_rebind(LDAP *handle, LDAP_CONST char *url, UNUSED ber_tag_t } else # endif { - admin_identity = pool_inst->admin_identity; - admin_password = pool_inst->admin_password; + admin_identity = handle_config->admin_identity; + admin_password = handle_config->admin_password; } - status = rlm_ldap_bind(inst, NULL, &conn, admin_identity, admin_password, - &conn->pool_inst->admin_sasl, false, NULL, NULL, NULL); + status = fr_ldap_bind(NULL, &conn, admin_identity, admin_password, + &conn->config->admin_sasl, NULL, NULL, NULL); if (status != LDAP_PROC_SUCCESS) { ldap_get_option(handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno); @@ -1669,41 +832,6 @@ static int rlm_ldap_rebind(LDAP *handle, LDAP_CONST char *url, UNUSED ber_tag_t } #endif -int rlm_ldap_global_init(rlm_ldap_t *inst) -{ - int ldap_errno; - - rad_assert(inst); /* clang scan */ - -#define do_ldap_global_option(_option, _name, _value) \ - if (ldap_set_option(NULL, _option, _value) != LDAP_OPT_SUCCESS) { \ - ldap_get_option(NULL, LDAP_OPT_ERROR_NUMBER, &ldap_errno); \ - ERROR("Failed setting global option %s: %s", _name, \ - (ldap_errno != LDAP_SUCCESS) ? ldap_err2string(ldap_errno) : "Unknown error"); \ - return -1;\ - } - -#define maybe_ldap_global_option(_option, _name, _value) \ - if (_value) do_ldap_global_option(_option, _name, _value) - -#ifdef LDAP_OPT_DEBUG_LEVEL - /* - * Can't use do_ldap_global_option - */ - if (inst->ldap_debug) do_ldap_global_option(LDAP_OPT_DEBUG_LEVEL, "ldap_debug", &(inst->ldap_debug)); -#endif - -#ifdef LDAP_OPT_X_TLS_RANDOM_FILE - /* - * OpenLDAP will error out if we attempt to set - * this on a handle. Presumably it's global in - * OpenSSL too. - */ - maybe_ldap_global_option(LDAP_OPT_X_TLS_RANDOM_FILE, "random_file", inst->tls_random_file); -#endif - return 0; -} - /** Close and delete a connection * * Unbinds the LDAP connection, informing the server and freeing any memory, then releases the memory used by the @@ -1714,20 +842,20 @@ int rlm_ldap_global_init(rlm_ldap_t *inst) */ static int _mod_conn_free(ldap_handle_t *conn) { - rlm_ldap_t const *inst = conn->inst; + ldap_handle_config_t const *handle_config = conn->config; rad_assert(conn->handle); - rlm_ldap_control_clear(conn); + fr_ldap_control_clear(conn); #ifdef HAVE_LDAP_UNBIND_EXT_S LDAPControl *our_serverctrls[LDAP_MAX_CONTROLS]; LDAPControl *our_clientctrls[LDAP_MAX_CONTROLS]; - rlm_ldap_control_merge(our_serverctrls, our_clientctrls, - sizeof(our_serverctrls) / sizeof(*our_serverctrls), - sizeof(our_clientctrls) / sizeof(*our_clientctrls), - conn, NULL, NULL); + fr_ldap_control_merge(our_serverctrls, our_clientctrls, + sizeof(our_serverctrls) / sizeof(*our_serverctrls), + sizeof(our_clientctrls) / sizeof(*our_clientctrls), + conn, NULL, NULL); DEBUG3("Closing libldap handle %p", conn->handle); ldap_unbind_ext_s(conn->handle, our_serverctrls, our_clientctrls); @@ -1740,50 +868,51 @@ static int _mod_conn_free(ldap_handle_t *conn) return 0; } -/** Create and return a new connection +/** Allocate and configure a new connection * - * Create a new ldap connection and allocate memory for a new rlm_handle_t + * Allocates and configures both our ldap handle, and libldap's handle. + * + * @param[in] ctx to allocate handle in. + * @param[in] handle_config Connection configuration. + * @return + * - A new handle on success. + * - NULL on error. */ -void *mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *timeout) +ldap_handle_t *fr_ldap_conn_alloc(TALLOC_CTX *ctx, ldap_handle_config_t const *handle_config) { - ldap_rcode_t status; + ldap_handle_t *conn; + LDAP *handle = NULL; - int ldap_errno, ldap_version; + int ldap_errno, ldap_version; - rlm_ldap_t const *inst = talloc_get_type_abort(instance, rlm_ldap_t); - ldap_pool_inst_t const *pool_inst = &inst->pool_inst; - ldap_handle_t *conn; - LDAP *handle = NULL; - - DEBUG("Connecting to %s", pool_inst->server); #ifdef HAVE_LDAP_INITIALIZE - ldap_errno = ldap_initialize(&handle, pool_inst->server); + ldap_errno = ldap_initialize(&handle, handle_config->server); if (ldap_errno != LDAP_SUCCESS) { ERROR("ldap_initialize failed: %s", ldap_err2string(ldap_errno)); return NULL; } #else - handle = ldap_init(pool_inst->server, pool_inst->port); + handle = ldap_init(handle_config->server, handle_config->port); if (!handle) { ERROR("ldap_init failed"); return NULL; } #endif + DEBUG3("New libldap handle %p", handle); + /* * Allocate memory for the handle. */ conn = talloc_zero(ctx, ldap_handle_t); if (!conn) return NULL; - conn->inst = inst; - conn->pool_inst = pool_inst; + conn->config = handle_config; conn->handle = handle; conn->rebound = false; conn->referred = false; - talloc_set_destructor(conn, _mod_conn_free); - DEBUG3("New libldap handle %p", conn->handle); + talloc_set_destructor(conn, _mod_conn_free); /* * We now have a connection structure, but no actual connection. @@ -1794,7 +923,7 @@ void *mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *tim if (ldap_set_option(conn->handle, _option, _value) != LDAP_OPT_SUCCESS) { \ ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno); \ ERROR("Failed setting connection option %s: %s", _name, \ - (ldap_errno != LDAP_SUCCESS) ? ldap_err2string(ldap_errno) : "Unknown error"); \ + (ldap_errno != LDAP_SUCCESS) ? ldap_err2string(ldap_errno) : "Unknown error"); \ goto error;\ } @@ -1804,20 +933,20 @@ void *mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *tim /* * Leave "dereference" unset to use the OpenLDAP default. */ - if (pool_inst->dereference_str) { - do_ldap_option(LDAP_OPT_DEREF, "dereference", &(pool_inst->dereference)); + if (handle_config->dereference_str) { + do_ldap_option(LDAP_OPT_DEREF, "dereference", &(handle_config->dereference)); } /* * Leave "chase_referrals" unset to use the OpenLDAP default. */ - if (!pool_inst->chase_referrals_unset) { - if (pool_inst->chase_referrals) { + if (!handle_config->chase_referrals_unset) { + if (handle_config->chase_referrals) { do_ldap_option(LDAP_OPT_REFERRALS, "chase_referrals", LDAP_OPT_ON); - if (pool_inst->rebind == true) { + if (handle_config->rebind == true) { #if LDAP_SET_REBIND_PROC_ARGS == 3 - ldap_set_rebind_proc(conn->handle, rlm_ldap_rebind, conn); + ldap_set_rebind_proc(conn->handle, fr_ldap_rebind, conn); #endif } } else { @@ -1827,56 +956,56 @@ void *mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *tim #ifdef LDAP_OPT_NETWORK_TIMEOUT /* - * A value of zero results in an pool_instant failure. + * A value of zero results in an handle configuration failure. * * When most people specify zero they mean infinite. * * libldap requires tv_sec to be -1 to mean that. */ { - struct timeval ldap_timeout = *timeout; + struct timeval ldap_timeout = handle_config->net_timeout; if ((ldap_timeout.tv_usec == 0) && (ldap_timeout.tv_sec == 0)) ldap_timeout.tv_sec = -1; - do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "pool.connect_timeout", &ldap_timeout); + do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout", &ldap_timeout); } #endif - do_ldap_option(LDAP_OPT_TIMELIMIT, "srv_timelimit", &(pool_inst->srv_timelimit)); + do_ldap_option(LDAP_OPT_TIMELIMIT, "srv_timelimit", &(handle_config->srv_timelimit)); ldap_version = LDAP_VERSION3; do_ldap_option(LDAP_OPT_PROTOCOL_VERSION, "ldap_version", &ldap_version); #ifdef LDAP_OPT_X_KEEPALIVE_IDLE - do_ldap_option(LDAP_OPT_X_KEEPALIVE_IDLE, "keepalive_idle", &(pool_inst->keepalive_idle)); + do_ldap_option(LDAP_OPT_X_KEEPALIVE_IDLE, "keepalive_idle", &(handle_config->keepalive_idle)); #endif #ifdef LDAP_OPT_X_KEEPALIVE_PROBES - do_ldap_option(LDAP_OPT_X_KEEPALIVE_PROBES, "keepalive_probes", &(pool_inst->keepalive_probes)); + do_ldap_option(LDAP_OPT_X_KEEPALIVE_PROBES, "keepalive_probes", &(handle_config->keepalive_probes)); #endif #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL - do_ldap_option(LDAP_OPT_X_KEEPALIVE_INTERVAL, "keepalive_interval", &(pool_inst->keepalive_interval)); + do_ldap_option(LDAP_OPT_X_KEEPALIVE_INTERVAL, "keepalive_interval", &(handle_config->keepalive_interval)); #endif #ifdef HAVE_LDAP_START_TLS_S /* * Set all of the TLS options */ - if (pool_inst->tls_mode) do_ldap_option(LDAP_OPT_X_TLS, "tls_mode", &(pool_inst->tls_mode)); + if (handle_config->tls_mode) do_ldap_option(LDAP_OPT_X_TLS, "tls_mode", &(handle_config->tls_mode)); - maybe_ldap_option(LDAP_OPT_X_TLS_CACERTFILE, "ca_file", pool_inst->tls_ca_file); - maybe_ldap_option(LDAP_OPT_X_TLS_CACERTDIR, "ca_path", pool_inst->tls_ca_path); + maybe_ldap_option(LDAP_OPT_X_TLS_CACERTFILE, "ca_file", handle_config->tls_ca_file); + maybe_ldap_option(LDAP_OPT_X_TLS_CACERTDIR, "ca_path", handle_config->tls_ca_path); /* * Set certificate options */ - maybe_ldap_option(LDAP_OPT_X_TLS_CERTFILE, "certificate_file", pool_inst->tls_certificate_file); - maybe_ldap_option(LDAP_OPT_X_TLS_KEYFILE, "private_key_file", pool_inst->tls_private_key_file); + maybe_ldap_option(LDAP_OPT_X_TLS_CERTFILE, "certificate_file", handle_config->tls_certificate_file); + maybe_ldap_option(LDAP_OPT_X_TLS_KEYFILE, "private_key_file", handle_config->tls_private_key_file); # ifdef LDAP_OPT_X_TLS_NEVER - if (pool_inst->tls_require_cert_str) { - do_ldap_option(LDAP_OPT_X_TLS_REQUIRE_CERT, "require_cert", &pool_inst->tls_require_cert); + if (handle_config->tls_require_cert_str) { + do_ldap_option(LDAP_OPT_X_TLS_REQUIRE_CERT, "require_cert", &handle_config->tls_require_cert); } # endif @@ -1892,32 +1021,15 @@ void *mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *tim } # endif - /* - * And finally start the TLS code. - */ - if (pool_inst->start_tls) { - if (pool_inst->port == 636) { + if (handle_config->start_tls) { + if (handle_config->port == 636) { WARN("Told to Start TLS on LDAPS port this will probably fail, please correct the " "configuration"); } - - if (ldap_start_tls_s(conn->handle, NULL, NULL) != LDAP_SUCCESS) { - ldap_get_option(conn->handle, LDAP_OPT_ERROR_NUMBER, &ldap_errno); - - ERROR("Could not start TLS: %s", ldap_err2string(ldap_errno)); - goto error; - } } #endif /* HAVE_LDAP_START_TLS_S */ - status = rlm_ldap_bind(inst, NULL, &conn, conn->pool_inst->admin_identity, conn->pool_inst->admin_password, - &(conn->pool_inst->admin_sasl), false, timeout, NULL, NULL); - if (status != LDAP_PROC_SUCCESS) goto error; - - /* - * Only error out on memory allocation errors - */ - if (rlm_ldap_directory_alloc(conn, &conn->directory, inst, &conn) < 0) goto error; + conn->config = handle_config; return conn; @@ -1927,74 +1039,194 @@ error: return NULL; } -/** Gets an LDAP socket from the connection pool - * - * Retrieve a socket from the connection pool, or NULL on error (of if no sockets are available). - * - * @param inst rlm_ldap configuration. - * @param request Current request (may be NULL). - */ - -ldap_handle_t *mod_conn_get(rlm_ldap_t const *inst, REQUEST *request) +int fr_ldap_conn_timeout_set(ldap_handle_t const *conn, struct timeval const *timeout) { - ldap_handle_t *conn; + int ldap_errno; + ldap_handle_config_t const *handle_config = conn->config; + +#ifdef LDAP_OPT_NETWORK_TIMEOUT + /* + * A value of zero results in an handle configuration failure. + * + * When most people specify zero they mean infinite. + * + * libldap requires tv_sec to be -1 to mean that. + */ + { + struct timeval ldap_timeout = *timeout; + + if ((ldap_timeout.tv_usec == 0) && (ldap_timeout.tv_sec == 0)) ldap_timeout.tv_sec = -1; - conn = fr_connection_get(inst->pool, request); + do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout", &ldap_timeout); + } +#endif + + return 0; - rad_assert(!conn || conn->pool_inst); +error: + return -1; +} + +int fr_ldap_conn_timeout_reset(ldap_handle_t const *conn) +{ + int ldap_errno; + ldap_handle_config_t const *handle_config = conn->config; -#ifdef LDAP_CONTROL_X_SESSION_TRACKING +#ifdef LDAP_OPT_NETWORK_TIMEOUT /* - * Add optional session tracking controls, - * that contain values of some attributes - * in the request. + * A value of zero results in an handle configuration failure. + * + * When most people specify zero they mean infinite. + * + * libldap requires tv_sec to be -1 to mean that. */ - if ((conn != NULL) && (request != NULL) && conn->pool_inst->session_tracking) { - if (rlm_ldap_control_add_session_tracking(conn, request) < 0) { - fr_connection_release(inst->pool, request, conn); - return NULL; - } + { + struct timeval ldap_timeout = handle_config->net_timeout; + + if ((ldap_timeout.tv_usec == 0) && (ldap_timeout.tv_sec == 0)) ldap_timeout.tv_sec = -1; + + do_ldap_option(LDAP_OPT_NETWORK_TIMEOUT, "net_timeout", &ldap_timeout); } #endif - return conn; + + return 0; + +error: + return -1; } -/** Frees an LDAP socket back to the connection pool +/** Change settings global to libldap * - * If the socket was rebound chasing a referral onto another server then we destroy it. - * If the socket was rebound to another user on the same server, we let the next caller rebind it. + * May only be called once. Subsequent calls will be ignored. * - * @param inst rlm_ldap configuration. - * @param request The current request. - * @param conn to release. + * @param[in] debug_level to enable in libldap. + * @param[in] tls_random_file Where OpenSSL gets its randomness. */ -void mod_conn_release(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t *conn) +int fr_ldap_global_config(int debug_level, char const *tls_random_file) { - /* - * Could have already been free'd due to a previous error. - */ - if (!conn) return; + static bool done_config; + ldap_handle_config_t *handle_config = &ldap_global_handle_config; + + if (done_config) return 0; + +#define do_ldap_global_option(_option, _name, _value) \ + if (ldap_set_option(NULL, _option, _value) != LDAP_OPT_SUCCESS) { \ + int _ldap_errno; \ + ldap_get_option(NULL, LDAP_OPT_ERROR_NUMBER, &_ldap_errno); \ + ERROR("Failed setting global option %s: %s", _name, \ + (_ldap_errno != LDAP_SUCCESS) ? ldap_err2string(_ldap_errno) : "Unknown error"); \ + return -1;\ + } + +#define maybe_ldap_global_option(_option, _name, _value) \ + if (_value) do_ldap_global_option(_option, _name, _value) + +#ifdef LDAP_OPT_DEBUG_LEVEL + if (debug_level) do_ldap_global_option(LDAP_OPT_DEBUG_LEVEL, "ldap_debug", &debug_level); +#else + if (debug_level) WARN("ldap_debug not honoured as LDAP_OPT_DEBUG_LEVEL is not available"); +#endif +#ifdef LDAP_OPT_X_TLS_RANDOM_FILE /* - * Clear any client/server controls associated with the connection. + * OpenLDAP will error out if we attempt to set + * this on a handle. Presumably it's global in + * OpenSSL too. */ - rlm_ldap_control_clear(conn); + maybe_ldap_global_option(LDAP_OPT_X_TLS_RANDOM_FILE, "random_file", tls_random_file); +#endif + + done_config = true; + + return 0; +} + +/** Initialise libldap and check library versions + * + * @return + * - 0 on success. + * - -1 on failure. + */ +int fr_ldap_global_init(void) +{ + int ldap_errno; + static LDAPAPIInfo info = { .ldapai_info_version = LDAP_API_INFO_VERSION }; /* static to quiet valgrind about this being uninitialised */ + ldap_handle_config_t *handle_config = &ldap_global_handle_config; + + if (instance_count > 0) { + instance_count++; + + return 0; + } /* - * We chased a referral to another server. - * - * This connection is no longer part of the pool which is - * connected to and bound to the configured server. - * Close it. + * Only needs to be done once, prevents races in environment + * initialisation within libldap. * - * Note that we do NOT close it if it was bound to another user. - * Instead, we let the next caller do the rebind. + * See: https://github.com/arr2036/ldapperf/issues/2 */ - if (conn->referred) { - fr_connection_close(inst->pool, request, conn); - return; +#ifdef HAVE_LDAP_INITIALIZE + ldap_initialize(&ldap_global_handle, ""); +#else + ldap_global_handle = ldap_init("", 0); +#endif + if (!ldap_global_handle) { + ERROR("Failed initialising global LDAP handle"); + return -1; } - fr_connection_release(inst->pool, request, conn); - return; + ldap_errno = ldap_get_option(NULL, LDAP_OPT_API_INFO, &info); + if (ldap_errno == LDAP_OPT_SUCCESS) { + /* + * Don't generate warnings if the compile type vendor name + * is found within the link time vendor name. + * + * This allows the server to be built against OpenLDAP but + * run with Symas OpenLDAP. + */ + if (strcasestr(info.ldapai_vendor_name, LDAP_VENDOR_NAME) == NULL) { + WARN("ldap - libldap vendor changed since the server was built"); + WARN("ldap - linked: %s, built: %s", info.ldapai_vendor_name, LDAP_VENDOR_NAME); + } + + if (info.ldapai_vendor_version < LDAP_VENDOR_VERSION) { + WARN("ldap - libldap older than the version the server was built against"); + WARN("ldap - linked: %i, built: %i", + info.ldapai_vendor_version, LDAP_VENDOR_VERSION); + } + + INFO("ldap - libldap vendor: %s, version: %i", info.ldapai_vendor_name, + info.ldapai_vendor_version); + + ldap_memfree(info.ldapai_vendor_name); + ldap_memfree(info.ldapai_extensions); + } else { + DEBUG("ldap - Falling back to build time libldap version info. Query for LDAP_OPT_API_INFO " + "returned: %i", ldap_errno); + INFO("ldap - libldap vendor: %s, version: %i.%i.%i", LDAP_VENDOR_NAME, + LDAP_VENDOR_VERSION_MAJOR, LDAP_VENDOR_VERSION_MINOR, LDAP_VENDOR_VERSION_PATCH); + } + + instance_count++; + + return 0; +} + +/** Free any global libldap resources + * + */ +void fr_ldap_global_free(void) +{ + if (--instance_count > 0) return; + + /* + * Keeping the dummy ld around for the lifetime + * of the module should always work, + * irrespective of what changes happen in libldap. + */ +#ifdef HAVE_LDAP_UNBIND_EXT_S + ldap_unbind_ext_s(ldap_global_handle, NULL, NULL); +#else + ldap_unbind_s(ldap_global_handle); +#endif } diff --git a/src/modules/rlm_ldap/libfreeradius-ldap.mk.in b/src/modules/rlm_ldap/libfreeradius-ldap.mk.in new file mode 100644 index 00000000000..cf38b7b52ce --- /dev/null +++ b/src/modules/rlm_ldap/libfreeradius-ldap.mk.in @@ -0,0 +1,10 @@ +TARGETNAME := @targetname@ + +ifneq "$(TARGETNAME)" "" +TARGET := $(TARGETNAME).a +endif + +SOURCES := control.c directory.c edir.c ldap.c @SASL@ + +SRC_CFLAGS := @mod_cflags@ +TGT_LDLIBS := @mod_ldflags@ diff --git a/src/modules/rlm_ldap/libldap.h b/src/modules/rlm_ldap/libldap.h new file mode 100644 index 00000000000..90bbd934207 --- /dev/null +++ b/src/modules/rlm_ldap/libldap.h @@ -0,0 +1,421 @@ +/** + * $Id$ + * @file libldap.h + * @brief Common utility functions for interacting with LDAP directories + * + * @note Do not rename to ldap.h. This causes configure checks to break + * in stupid ways, where the configure script will use the local ldap.h + * file, instead of the one from libldap. + * + * @author Arran Cudbard-Bell + * + * @copyright 2017 The FreeRADIUS Server Project. + * @copyright 2017 Arran Cudbard-Bell + */ +#ifndef LIBFREERADIUS_LDAP_H +#define LIBFREERADIUS_LDAP_H + +#include +#include +#include +#include "config.h" + +extern LDAP *ldap_global_handle; + +/* + * Framework on OSX doesn't export the symbols but leaves + * the macro defined *sigh*. + */ +#ifndef HAVE_LDAP_CREATE_SESSION_TRACKING_CONTROL +# undef LDAP_CONTROL_X_SESSION_TRACKING +#endif + +/* + * There's a typo in libldap's ldap.h which was fixed by + * Howard Chu in 19aeb1cd. This typo had the function defined + * as ldap_create_session_tracking_control but declared as + * ldap_create_session_tracking. + * + * We fix this, by adding the correct declaration here. + */ +#ifdef LDAP_CONTROL_X_SESSION_TRACKING +# if !defined(HAVE_DECL_LDAP_CREATE_SESSION_TRACKING_CONTROL) || (HAVE_DECL_LDAP_CREATE_SESSION_TRACKING_CONTROL == 0) +LDAP_F( int ) +ldap_create_session_tracking_control LDAP_P(( + LDAP *ld, + char *sessionSourceIp, + char *sessionSourceName, + char *formatOID, + struct berval *sessionTrackingIdentifier, + LDAPControl **ctrlp )); +# endif +#endif + +/* + * Ensure the have the ldap_create_sort_keylist() + * function too, else we can't use ldap_create_sort_control() + */ +#if !defined(HAVE_LDAP_CREATE_SORT_KEYLIST) || !defined(HAVE_LDAP_FREE_SORT_KEYLIST) +# undef HAVE_LDAP_CREATE_SORT_CONTROL +#endif + +/* + * Because the LTB people define LDAP_VENDOR_VERSION_PATCH + * as X, which precludes its use in printf statements *sigh* + * + * Identifiers that are not macros, all evaluate to 0, + * which is why this works. + */ +#if !defined(LDAP_VENDOR_VERSION_PATCH) || LDAP_VENDOR_VERSION_PATCH == 0 +# undef LDAP_VENDOR_VERSION_PATCH +# define LDAP_VENDOR_VERSION_PATCH 0 +#endif + +/* + * For compatibility with other LDAP libraries + */ +#if !defined(LDAP_SCOPE_BASE) && defined(LDAP_SCOPE_BASEOBJECT) +# define LDAP_SCOPE_BASE LDAP_SCOPE_BASEOBJECT +#endif + +#if !defined(LDAP_SCOPE_ONE) && defined(LDAP_SCOPE_ONELEVEL) +# define LDAP_SCOPE_ONE LDAP_SCOPE_ONELEVEL +#endif + +#if !defined(LDAP_SCOPE_SUB) && defined(LDAP_SCOPE_SUBTREE) +# define LDAP_SCOPE_SUB LDAP_SCOPE_SUBTREE +#endif + +#if !defined(LDAP_OPT_RESULT_CODE) && defined(LDAP_OPT_ERROR_NUMBER) +# define LDAP_OPT_RESULT_CODE LDAP_OPT_ERROR_NUMBER +#endif + +#ifndef LDAP_CONST +# define LDAP_CONST +#endif + +#if defined(HAVE_LDAP_URL_PARSE) && defined(HAVE_LDAP_IS_LDAP_URL) && defined(HAVE_LDAP_URL_DESC2STR) +# define LDAP_CAN_PARSE_URLS +#endif + +#define LDAP_MAX_CONTROLS 10 //!< Maximum number of client/server controls. + //!< Used to allocate static arrays of control pointers. +#define LDAP_MAX_ATTRMAP 128 //!< Maximum number of mappings between LDAP and + //!< FreeRADIUS attributes. +#define LDAP_MAP_RESERVED 4 //!< Number of additional items to allocate in expanded + //!< attribute name arrays. Currently for enable attribute, + //!< group membership attribute, valuepair attribute, + //!< and profile attribute. + +#define LDAP_MAX_CACHEABLE 64 //!< Maximum number of groups we retrieve from the server for + //!< a given user. If more than this number are retrieve the + //!< module returns invalid. + +#define LDAP_MAX_GROUP_NAME_LEN 128 //!< Maximum name of a group name. +#define LDAP_MAX_ATTR_STR_LEN 256 //!< Maximum length of an xlat expanded LDAP attribute. +#define LDAP_MAX_FILTER_STR_LEN 1024 //!< Maximum length of an xlat expanded filter. +#define LDAP_MAX_DN_STR_LEN 1024 //!< Maximum length of an xlat expanded DN. + +#define LDAP_VIRTUAL_DN_ATTR "dn" //!< 'Virtual' attribute which maps to the DN of the object. + + +typedef enum { + LDAP_EXT_UNSUPPORTED, //!< Unsupported extension. + LDAP_EXT_BINDNAME, //!< Specifies the user DN or name for an LDAP bind. + LDAP_EXT_BINDPW, //!< Specifies the password for an LDAP bind. +} ldap_supported_extension_t; + +typedef struct ldap_sasl { + char const *mech; //!< SASL mech(s) to try. + char const *proxy; //!< Identity to proxy. + char const *realm; //!< Kerberos realm. +} ldap_sasl; + +typedef struct fr_ldap_control { + LDAPControl *control; //!< LDAP control. + bool freeit; //!< Whether the control should be freed after + //!< we've finished using it. +} fr_ldap_control_t; + +typedef enum { + LDAP_DIRECTORY_UNKNOWN = 0, //!< We can't determine the directory server. + + LDAP_DIRECTORY_ACTIVE_DIRECTORY, //!< Directory server is Active Directory. + LDAP_DIRECTORY_EDIRECTORY, //!< Directory server is eDir. + LDAP_DIRECTORY_IBM, //!< Directory server is IBM. + LDAP_DIRECTORY_NETSCAPE, //!< Directory server is Netscape. + LDAP_DIRECTORY_OPENLDAP, //!< Directory server is OpenLDAP. + LDAP_DIRECTORY_ORACLE_INTERNET_DIRECTORY, //!< Directory server is Oracle Internet Directory. + LDAP_DIRECTORY_ORACLE_UNIFIED_DIRECTORY, //!< Directory server is Oracle Unified Directory. + LDAP_DIRECTORY_ORACLE_VIRTUAL_DIRECTORY, //!< Directory server is Oracle Virtual Directory. + LDAP_DIRECTORY_SUN_ONE_DIRECTORY, //!< Directory server is Sun One Directory. + LDAP_DIRECTORY_SIEMENS_AG, //!< Directory server is Siemens AG. + LDAP_DIRECTORY_UNBOUND_ID //!< Directory server is Unbound ID +} ldap_directory_type_t; + +typedef struct ldap_directory { + char const *vendor_str; //!< As returned from the vendorName attribute in the + //!< rootDSE. + char const *version_str; //!< As returned from the vendorVersion attribute in the + //!< rootDSE. + ldap_directory_type_t type; //!< Cannonical server implementation. + + bool cleartext_password; //!< Whether the server will return the user's plaintext + //!< password. +} ldap_directory_t; + +/** Connection configuration + * + * Must not be passed into functions except via the connection handle + * this avoids problems with not using the connection pool configuration. + */ +typedef struct { + char const *name; //!< Name of the module that created this connection. + + char *server; //!< Initial server to bind to. + char const **server_str; //!< Server set in the config. + + uint16_t port; //!< Port to use when binding to the server. + + char const *admin_identity; //!< Identity we bind as when we need to query the LDAP + //!< directory. + char const *admin_password; //!< Password used in administrative bind. + + ldap_sasl admin_sasl; //!< SASL parameters used when binding as the admin. + + int dereference; //!< libldap value specifying dereferencing behaviour. + char const *dereference_str; //!< When to dereference (never, searching, finding, always) + + bool chase_referrals; //!< If the LDAP server returns a referral to another server + //!< or point in the tree, follow it, establishing new + //!< connections and binding where necessary. + bool chase_referrals_unset; //!< If true, use the OpenLDAP defaults for chase_referrals. + + bool use_referral_credentials; //!< If true use credentials from the referral URL. + + bool rebind; //!< Controls whether we set an ldad_rebind_proc function + //!< and so determines if we can bind to other servers whilst + //!< chasing referrals. If this is false, we will still chase + //!< referrals on the same server, but won't bind to other + //!< servers. + + /* + * TLS items. + */ + int tls_mode; + + bool start_tls; //!< Send the Start TLS message to the LDAP directory + //!< to start encrypted communications using the standard + //!< LDAP port. + + char const *tls_ca_file; //!< Sets the full path to a CA certificate (used to validate + //!< the certificate the server presents). + + char const *tls_ca_path; //!< Sets the path to a directory containing CA certificates. + + char const *tls_certificate_file; //!< Sets the path to the public certificate file we present + //!< to the servers. + + char const *tls_private_key_file; //!< Sets the path to the private key for our public + //!< certificate. + + char const *tls_require_cert_str; //!< Sets requirements for validating the certificate the + //!< server presents. + + int tls_require_cert; //!< OpenLDAP constant representing the require cert string. + + + /* + * For keep-alives. + */ +#ifdef LDAP_OPT_X_KEEPALIVE_IDLE + uint32_t keepalive_idle; //!< Number of seconds a connections needs to remain idle + //!< before TCP starts sending keepalive probes. +#endif +#ifdef LDAP_OPT_X_KEEPALIVE_PROBES + uint32_t keepalive_probes; //!< Number of missed timeouts before the connection is + //!< dropped. +#endif +#ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL + uint32_t keepalive_interval; //!< Interval between keepalive probes. +#endif + + /* + * Search timelimits + */ + uint32_t srv_timelimit; //!< How long the server should spent on a single request + //!< (also bounded by value on the server). + + struct timeval net_timeout; //!< How long we wait in blocking network calls. + //!< We set this in the LDAP API, even though with + //!< async calls, we control this using our event loop. + //!< This is just in case there are blocking calls which + //!< happen internally which we can't work around. + + struct timeval res_timeout; //!< How long we wait for results. +} ldap_handle_config_t; + +/** Tracks the state of a libldap connection handle + * + */ +typedef struct ldap_handle { + LDAP *handle; //!< libldap handle. + bool rebound; //!< Whether the connection has been rebound to something + //!< other than the admin user. + bool referred; //!< Whether the connection is now established a server + //!< other than the configured one. + + fr_ldap_control_t serverctrls[LDAP_MAX_CONTROLS + 1]; //!< Server controls to use for all operations with + //!< this handle. + fr_ldap_control_t clientctrls[LDAP_MAX_CONTROLS + 1]; //!< Client controls to use for all operations with + //!< this handle. + int serverctrls_cnt; //!< Number of server controls associated with the handle. + int clientctrls_cnt; //!< Number of client controls associated with the handle. + + ldap_directory_t *directory; //!< The type of directory we're connected to. + + ldap_handle_config_t const *config; //!< rlm_ldap connection configuration. +} ldap_handle_t; + +/** Contains a collection of values + * + */ +typedef struct fr_ldap_result { + struct berval **values; //!< libldap struct containing bv_val (char *) + //!< and length bv_len. + int count; //!< Number of values. +} fr_ldap_result_t; + +/** Codes returned by fr_ldap internal functions + * + */ +typedef enum { + LDAP_PROC_CONTINUE = 1, //!< Operation is in progress. + LDAP_PROC_SUCCESS = 0, //!< Operation was successfull. + + LDAP_PROC_ERROR = -1, //!< Unrecoverable library/server error. + + LDAP_PROC_BAD_CONN = -2, //!< Transitory error, caller should retry the operation + //!< with a new connection. + + LDAP_PROC_NOT_PERMITTED = -3, //!< Operation was not permitted, either current user was + //!< locked out in the case of binds, or has insufficient + //!< access. + + LDAP_PROC_REJECT = -4, //!< Bind failed, user was rejected. + + LDAP_PROC_BAD_DN = -5, //!< Specified an invalid object in a bind or search DN. + + LDAP_PROC_NO_RESULT = -6, //!< Got no results. + + LDAP_PROC_TIMEOUT = -7 //!< Operation timed out. +} ldap_rcode_t; + + +extern FR_NAME_NUMBER const ldap_supported_extensions[]; + +extern FR_NAME_NUMBER const ldap_scope[]; +extern FR_NAME_NUMBER const ldap_tls_require_cert[]; + +/* + * ldap.c - Wrappers arounds OpenLDAP functions. + */ +void fr_ldap_timeout_debug(REQUEST *request, ldap_handle_t const *conn, + struct timeval const *timeout, char const *prefix); + +size_t fr_ldap_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg); + +size_t fr_ldap_unescape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg); + +ssize_t fr_ldap_xlat_filter(REQUEST *request, char const **sub, size_t sublen, char *out, size_t outlen); + +ldap_rcode_t fr_ldap_bind(REQUEST *request, + ldap_handle_t **pconn, + char const *dn, char const *password, +#ifdef WITH_SASL + ldap_sasl const *sasl, +#else + NDEBUG_UNUSED ldap_sasl const *sasl, +#endif + struct timeval const *timeout, + LDAPControl **serverctrls, LDAPControl **clientctrls); + +char const *fr_ldap_error_str(ldap_handle_t const *conn); + +ldap_rcode_t fr_ldap_search(LDAPMessage **result, REQUEST *request, + ldap_handle_t **pconn, + char const *dn, int scope, char const *filter, char const * const * attrs, + LDAPControl **serverctrls, LDAPControl **clientctrls); + +ldap_rcode_t fr_ldap_modify(REQUEST *request, ldap_handle_t **pconn, + char const *dn, LDAPMod *mods[], + LDAPControl **serverctrls, LDAPControl **clientctrls); + + +ldap_rcode_t fr_ldap_result(ldap_handle_t const *conn, int msgid, char const *dn, + struct timeval const *timeout, + LDAPMessage **result, char const **error, char **extra); + +ldap_handle_t *fr_ldap_conn_alloc(TALLOC_CTX *ctx, ldap_handle_config_t const *handle_config); + +int fr_ldap_conn_timeout_set(ldap_handle_t const *conn, struct timeval const *timeout); + +int fr_ldap_conn_timeout_reset(ldap_handle_t const *conn); + +int fr_ldap_global_config(int debug_level, char const *tls_random_file); + +int fr_ldap_global_init(void); + +void fr_ldap_global_free(void); + +/* + * control.c - Connection based client/server controls + */ +void fr_ldap_control_merge(LDAPControl *serverctrls_out[], + LDAPControl *clientctrls_out[], + size_t serverctrls_len, + size_t clientctrls_len, + ldap_handle_t *conn, + LDAPControl *serverctrls_in[], + LDAPControl *clientctrls_in[]); + +int fr_ldap_control_add_server(ldap_handle_t *conn, LDAPControl *ctrl, bool freeit); + +int fr_ldap_control_add_client(ldap_handle_t *conn, LDAPControl *ctrl, bool freeit); + +void fr_ldap_control_clear(ldap_handle_t *conn); + +int fr_ldap_control_add_session_tracking(ldap_handle_t *conn, REQUEST *request); + +/* + * directory.c - Get directory capabilities from the remote server + */ +int fr_ldap_directory_alloc(TALLOC_CTX *ctx, ldap_directory_t **out, ldap_handle_t **pconn); + + +/* + * sasl.c - SASL bind functions + */ +ldap_rcode_t fr_ldap_sasl_interactive(REQUEST *request, + ldap_handle_t *pconn, char const *dn, + char const *password, ldap_sasl const *sasl, + LDAPControl **serverctrls, LDAPControl **clientctrls, + struct timeval const *timeout, + char const **error, char **error_extra); + +/* + * uti.c - Utility functions + */ +size_t fr_ldap_common_dn(char const *full, char const *part); + +bool fr_ldap_util_is_dn(char const *in, size_t inlen); + +size_t fr_ldap_util_normalise_dn(char *out, char const *in); + +char *fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in); + +int fr_ldap_parse_url_extensions(LDAPControl **sss, REQUEST *request, + ldap_handle_t *conn, char **extensions); + + +#endif diff --git a/src/modules/rlm_ldap/rlm_ldap.c b/src/modules/rlm_ldap/rlm_ldap.c index ba05c4cab85..b117595bb76 100644 --- a/src/modules/rlm_ldap/rlm_ldap.c +++ b/src/modules/rlm_ldap/rlm_ldap.c @@ -31,9 +31,6 @@ RCSID("$Id$") #include -#include -#include - #include "rlm_ldap.h" #include @@ -73,9 +70,9 @@ static FR_NAME_NUMBER const ldap_dereference[] = { }; static CONF_PARSER sasl_mech_dynamic[] = { - { FR_CONF_OFFSET("mech", PW_TYPE_TMPL | PW_TYPE_NOT_EMPTY, ldap_sasl_dynamic, mech) }, - { FR_CONF_OFFSET("proxy", PW_TYPE_TMPL, ldap_sasl_dynamic, proxy) }, - { FR_CONF_OFFSET("realm", PW_TYPE_TMPL, ldap_sasl_dynamic, realm) }, + { FR_CONF_OFFSET("mech", PW_TYPE_TMPL | PW_TYPE_NOT_EMPTY, ldap_sasl_dynamic_t, mech) }, + { FR_CONF_OFFSET("proxy", PW_TYPE_TMPL, ldap_sasl_dynamic_t, proxy) }, + { FR_CONF_OFFSET("realm", PW_TYPE_TMPL, ldap_sasl_dynamic_t, realm) }, CONF_PARSER_TERMINATOR }; @@ -93,20 +90,20 @@ static CONF_PARSER tls_config[] = { /* * Deprecated attributes */ - { FR_CONF_OFFSET("ca_file", PW_TYPE_FILE_INPUT, ldap_pool_inst_t, tls_ca_file) }, + { FR_CONF_OFFSET("ca_file", PW_TYPE_FILE_INPUT, ldap_handle_config_t, tls_ca_file) }, - { FR_CONF_OFFSET("ca_path", PW_TYPE_FILE_INPUT, ldap_pool_inst_t, tls_ca_path) }, + { FR_CONF_OFFSET("ca_path", PW_TYPE_FILE_INPUT, ldap_handle_config_t, tls_ca_path) }, - { FR_CONF_OFFSET("certificate_file", PW_TYPE_FILE_INPUT, ldap_pool_inst_t, tls_certificate_file) }, + { FR_CONF_OFFSET("certificate_file", PW_TYPE_FILE_INPUT, ldap_handle_config_t, tls_certificate_file) }, - { FR_CONF_OFFSET("private_key_file", PW_TYPE_FILE_INPUT, ldap_pool_inst_t, tls_private_key_file) }, + { FR_CONF_OFFSET("private_key_file", PW_TYPE_FILE_INPUT, ldap_handle_config_t, tls_private_key_file) }, /* * LDAP Specific TLS attributes */ - { FR_CONF_OFFSET("start_tls", PW_TYPE_BOOLEAN, ldap_pool_inst_t, start_tls), .dflt = "no" }, + { FR_CONF_OFFSET("start_tls", PW_TYPE_BOOLEAN, ldap_handle_config_t, start_tls), .dflt = "no" }, - { FR_CONF_OFFSET("require_cert", PW_TYPE_STRING, ldap_pool_inst_t, tls_require_cert_str) }, + { FR_CONF_OFFSET("require_cert", PW_TYPE_STRING, ldap_handle_config_t, tls_require_cert_str) }, CONF_PARSER_TERMINATOR }; @@ -178,41 +175,37 @@ static CONF_PARSER option_config[] = { /* * Pool config items */ - { FR_CONF_OFFSET("chase_referrals", PW_TYPE_BOOLEAN, rlm_ldap_t, pool_inst.chase_referrals) }, + { FR_CONF_OFFSET("chase_referrals", PW_TYPE_BOOLEAN, rlm_ldap_t, handle_config.chase_referrals) }, - { FR_CONF_OFFSET("use_referral_credentials", PW_TYPE_BOOLEAN, rlm_ldap_t, pool_inst.use_referral_credentials), .dflt = "no" }, + { FR_CONF_OFFSET("use_referral_credentials", PW_TYPE_BOOLEAN, rlm_ldap_t, handle_config.use_referral_credentials), .dflt = "no" }, - { FR_CONF_OFFSET("rebind", PW_TYPE_BOOLEAN, rlm_ldap_t, pool_inst.rebind) }, - -#ifdef LDAP_CONTROL_X_SESSION_TRACKING - { FR_CONF_OFFSET("session_tracking", PW_TYPE_BOOLEAN, rlm_ldap_t, pool_inst.session_tracking), .dflt = "no" }, -#endif + { FR_CONF_OFFSET("rebind", PW_TYPE_BOOLEAN, rlm_ldap_t, handle_config.rebind) }, #ifdef LDAP_OPT_NETWORK_TIMEOUT /* timeout on network activity */ - { FR_CONF_DEPRECATED("net_timeout", PW_TYPE_INTEGER, rlm_ldap_t, pool_inst.net_timeout), .dflt = "10" }, + { FR_CONF_DEPRECATED("net_timeout", PW_TYPE_INTEGER, rlm_ldap_t, handle_config.net_timeout), .dflt = "10" }, #endif #ifdef LDAP_OPT_X_KEEPALIVE_IDLE - { FR_CONF_OFFSET("idle", PW_TYPE_INTEGER, rlm_ldap_t, pool_inst.keepalive_idle), .dflt = "60" }, + { FR_CONF_OFFSET("idle", PW_TYPE_INTEGER, rlm_ldap_t, handle_config.keepalive_idle), .dflt = "60" }, #endif #ifdef LDAP_OPT_X_KEEPALIVE_PROBES - { FR_CONF_OFFSET("probes", PW_TYPE_INTEGER, rlm_ldap_t, pool_inst.keepalive_probes), .dflt = "3" }, + { FR_CONF_OFFSET("probes", PW_TYPE_INTEGER, rlm_ldap_t, handle_config.keepalive_probes), .dflt = "3" }, #endif #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL - { FR_CONF_OFFSET("interval", PW_TYPE_INTEGER, rlm_ldap_t, pool_inst.keepalive_interval), .dflt = "30" }, + { FR_CONF_OFFSET("interval", PW_TYPE_INTEGER, rlm_ldap_t, handle_config.keepalive_interval), .dflt = "30" }, #endif - { FR_CONF_OFFSET("dereference", PW_TYPE_STRING, rlm_ldap_t, pool_inst.dereference_str) }, + { FR_CONF_OFFSET("dereference", PW_TYPE_STRING, rlm_ldap_t, handle_config.dereference_str) }, /* allow server unlimited time for search (server-side limit) */ - { FR_CONF_OFFSET("srv_timelimit", PW_TYPE_INTEGER, rlm_ldap_t, pool_inst.srv_timelimit), .dflt = "20" }, + { FR_CONF_OFFSET("srv_timelimit", PW_TYPE_INTEGER, rlm_ldap_t, handle_config.srv_timelimit), .dflt = "20" }, /* * Instance config items */ /* timeout for search results */ - { FR_CONF_OFFSET("res_timeout", PW_TYPE_INTEGER, rlm_ldap_t, res_timeout), .dflt = "20" }, + { FR_CONF_OFFSET("res_timeout", PW_TYPE_TIMEVAL, rlm_ldap_t, handle_config.res_timeout), .dflt = "20" }, CONF_PARSER_TERMINATOR }; @@ -229,17 +222,21 @@ static const CONF_PARSER module_config[] = { /* * Pool config items */ - { FR_CONF_OFFSET("server", PW_TYPE_STRING | PW_TYPE_MULTI, rlm_ldap_t, pool_inst.server_str) }, /* Do not set to required */ + { FR_CONF_OFFSET("server", PW_TYPE_STRING | PW_TYPE_MULTI, rlm_ldap_t, handle_config.server_str) }, /* Do not set to required */ - { FR_CONF_OFFSET("port", PW_TYPE_SHORT, rlm_ldap_t, pool_inst.port) }, + { FR_CONF_OFFSET("port", PW_TYPE_SHORT, rlm_ldap_t, handle_config.port) }, - { FR_CONF_OFFSET("identity", PW_TYPE_STRING, rlm_ldap_t, pool_inst.admin_identity) }, - { FR_CONF_OFFSET("password", PW_TYPE_STRING | PW_TYPE_SECRET, rlm_ldap_t, pool_inst.admin_password) }, + { FR_CONF_OFFSET("identity", PW_TYPE_STRING, rlm_ldap_t, handle_config.admin_identity) }, + { FR_CONF_OFFSET("password", PW_TYPE_STRING | PW_TYPE_SECRET, rlm_ldap_t, handle_config.admin_password) }, - { FR_CONF_OFFSET("sasl", PW_TYPE_SUBSECTION, rlm_ldap_t, pool_inst.admin_sasl), .subcs = (void const *) sasl_mech_static }, + { FR_CONF_OFFSET("sasl", PW_TYPE_SUBSECTION, rlm_ldap_t, handle_config.admin_sasl), .subcs = (void const *) sasl_mech_static }, { FR_CONF_OFFSET("valuepair_attribute", PW_TYPE_STRING, rlm_ldap_t, valuepair_attr) }, +#ifdef LDAP_CONTROL_X_SESSION_TRACKING + { FR_CONF_OFFSET("session_tracking", PW_TYPE_BOOLEAN, rlm_ldap_t, session_tracking), .dflt = "no" }, +#endif + #ifdef WITH_EDIR /* support for eDirectory Universal Password */ { FR_CONF_OFFSET("edir", PW_TYPE_BOOLEAN, rlm_ldap_t, edir) }, /* NULL defaults to "no" */ @@ -265,98 +262,22 @@ static const CONF_PARSER module_config[] = { { FR_CONF_POINTER("global", PW_TYPE_SUBSECTION, NULL), .subcs = (void const *) global_config }, - { FR_CONF_OFFSET("tls", PW_TYPE_SUBSECTION, rlm_ldap_t, pool_inst), .subcs = (void const *) tls_config }, + { FR_CONF_OFFSET("tls", PW_TYPE_SUBSECTION, rlm_ldap_t, handle_config), .subcs = (void const *) tls_config }, CONF_PARSER_TERMINATOR }; - -static LDAP *global_handle; //!< Hack for OpenLDAP libldap global initialisation. - static ssize_t ldap_escape_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen, UNUSED void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt) { - return rlm_ldap_escape_func(request, *out, outlen, fmt, NULL); + return fr_ldap_escape_func(request, *out, outlen, fmt, NULL); } static ssize_t ldap_unescape_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen, UNUSED void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt) { - return rlm_ldap_unescape_func(request, *out, outlen, fmt, NULL); -} - -/** Parse a subset (just server side sort for now) of LDAP URL extensions - * - * @param[out] sss Where to write a pointer to the server side sort control - * we created. - * @param[in] request The current request. - * @param[in] conn Handle to allocate controls under. - * @param[in] extensions A NULL terminated array of extensions. - * @return - * - 0 on success. - * - -1 on failure. - */ -static int ldap_parse_url_extensions(LDAPControl **sss, REQUEST *request, ldap_handle_t *conn, char **extensions) -{ - int i; - - *sss = NULL; - - if (!extensions) return 0; - - /* - * Parse extensions in the LDAP URL - */ - for (i = 0; extensions[i]; i++) { - char *p; - bool is_critical = false; - - p = extensions[i]; - if (*p == '!') { - is_critical = true; - p++; - } - -#ifdef HAVE_LDAP_CREATE_SORT_CONTROL - /* - * Server side sort control - */ - if (strncmp(p, "sss", 3) == 0) { - LDAPSortKey **keys; - int ret; - - p += 3; - p = strchr(p, '='); - if (!p) { - REDEBUG("Server side sort extension must be in the format \"[!]sss=[,key]\""); - return -1; - } - p++; - - ret = ldap_create_sort_keylist(&keys, p); - if (ret != LDAP_SUCCESS) { - REDEBUG("Invalid server side sort value \"%s\": %s", p, ldap_err2string(ret)); - return -1; - } - - if (*sss) ldap_control_free(*sss); - - ret = ldap_create_sort_control(conn->handle, keys, is_critical ? 1 : 0, sss); - ldap_free_sort_keylist(keys); - if (ret != LDAP_SUCCESS) { - ERROR("Failed creating server sort control: %s", ldap_err2string(ret)); - return -1; - } - - continue; - } -#endif - - RWDEBUG("URL extension \"%s\" ignored", p); - } - - return 0; + return fr_ldap_unescape_func(request, *out, outlen, fmt, NULL); } /** Expand an LDAP URL into a query, and return a string result from that query. @@ -413,10 +334,10 @@ static ssize_t ldap_xlat(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen, memcpy(&attrs, &ldap_url->lud_attrs, sizeof(attrs)); - if (ldap_parse_url_extensions(&server_ctrls[0], request, conn, ldap_url->lud_exts) < 0) goto free_socket; + if (fr_ldap_parse_url_extensions(&server_ctrls[0], request, conn, ldap_url->lud_exts) < 0) goto free_socket; - status = rlm_ldap_search(&result, inst, request, &conn, ldap_url->lud_dn, ldap_url->lud_scope, - ldap_url->lud_filter, attrs, server_ctrls, NULL); + status = fr_ldap_search(&result, request, &conn, ldap_url->lud_dn, ldap_url->lud_scope, + ldap_url->lud_filter, attrs, server_ctrls, NULL); #ifdef HAVE_LDAP_CREATE_SORT_CONTROL if (server_ctrls[0]) ldap_control_free(server_ctrls[0]); @@ -516,7 +437,7 @@ static rlm_rcode_t mod_map_proc(void *mod_inst, UNUSED void *proc_inst, REQUEST rlm_ldap_map_exp_t expanded; /* faster than allocing every time */ - if (tmpl_aexpand(request, &url_str, request, url, rlm_ldap_escape_func, NULL) < 0) { + if (tmpl_aexpand(request, &url_str, request, url, fr_ldap_escape_func, NULL) < 0) { return RLM_MODULE_FAIL; } @@ -541,10 +462,10 @@ static rlm_rcode_t mod_map_proc(void *mod_inst, UNUSED void *proc_inst, REQUEST conn = mod_conn_get(inst, request); if (!conn) goto free_expanded; - if (ldap_parse_url_extensions(&server_ctrls[0], request, conn, ldap_url->lud_exts) < 0) goto free_socket; + if (fr_ldap_parse_url_extensions(&server_ctrls[0], request, conn, ldap_url->lud_exts) < 0) goto free_socket; - status = rlm_ldap_search(&result, inst, request, &conn, ldap_url->lud_dn, ldap_url->lud_scope, - ldap_url->lud_filter, expanded.attrs, server_ctrls, NULL); + status = fr_ldap_search(&result, request, &conn, ldap_url->lud_dn, ldap_url->lud_scope, + ldap_url->lud_filter, expanded.attrs, server_ctrls, NULL); #ifdef HAVE_LDAP_CREATE_SORT_CONTROL if (server_ctrls[0]) ldap_control_free(server_ctrls[0]); @@ -583,7 +504,7 @@ static rlm_rcode_t mod_map_proc(void *mod_inst, UNUSED void *proc_inst, REQUEST map != NULL; map = map->next, i++) { int ret; - rlm_ldap_result_t attr; + fr_ldap_result_t attr; attr.values = ldap_get_values_len(conn->handle, entry, expanded.attrs[i]); if (!attr.values) { @@ -682,12 +603,12 @@ static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR /* * Check if we can do cached membership verification */ - check_is_dn = rlm_ldap_is_dn(check->vp_strvalue, check->vp_length); + check_is_dn = fr_ldap_util_is_dn(check->vp_strvalue, check->vp_length); if (check_is_dn) { char *norm; MEM(norm = talloc_memdup(check, check->vp_strvalue, talloc_array_length(check->vp_strvalue))); - rlm_ldap_normalise_dn(norm, check->vp_strvalue); + fr_ldap_util_normalise_dn(norm, check->vp_strvalue); fr_pair_value_strsteal(check, norm); } if ((check_is_dn && inst->cacheable_group_dn) || (!check_is_dn && inst->cacheable_group_name)) { @@ -825,7 +746,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, UNUSED void memset(&sasl, 0, sizeof(sasl)); if (tmpl_expand(&sasl.mech, sasl_mech_buff, sizeof(sasl_mech_buff), request, - inst->user_sasl.mech, rlm_ldap_escape_func, inst) < 0) { + inst->user_sasl.mech, fr_ldap_escape_func, inst) < 0) { REDEBUG("Failed expanding user.sasl.mech: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto finish; @@ -833,7 +754,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, UNUSED void if (inst->user_sasl.proxy) { if (tmpl_expand(&sasl.proxy, sasl_proxy_buff, sizeof(sasl_proxy_buff), request, - inst->user_sasl.proxy, rlm_ldap_escape_func, inst) < 0) { + inst->user_sasl.proxy, fr_ldap_escape_func, inst) < 0) { REDEBUG("Failed expanding user.sasl.proxy: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto finish; @@ -842,7 +763,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, UNUSED void if (inst->user_sasl.realm) { if (tmpl_expand(&sasl.realm, sasl_realm_buff, sizeof(sasl_realm_buff), request, - inst->user_sasl.realm, rlm_ldap_escape_func, inst) < 0) { + inst->user_sasl.realm, fr_ldap_escape_func, inst) < 0) { REDEBUG("Failed expanding user.sasl.realm: %s", fr_strerror()); rcode = RLM_MODULE_FAIL; goto finish; @@ -862,8 +783,12 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, UNUSED void return rcode; } conn->rebound = true; - status = rlm_ldap_bind(inst, request, &conn, dn, request->password->vp_strvalue, - inst->user_sasl.mech ? &sasl : NULL, true, NULL, NULL, NULL); + status = fr_ldap_bind(request, + &conn, + dn, request->password->vp_strvalue, + inst->user_sasl.mech ? &sasl : NULL, + NULL, + NULL, NULL); switch (status) { case LDAP_PROC_SUCCESS: rcode = RLM_MODULE_OK; @@ -926,14 +851,14 @@ static rlm_rcode_t rlm_ldap_map_profile(rlm_ldap_t const *inst, REQUEST *request if (!dn || !*dn) return RLM_MODULE_OK; if (tmpl_expand(&filter, filter_buff, sizeof(filter_buff), request, - inst->profile_filter, rlm_ldap_escape_func, NULL) < 0) { + inst->profile_filter, fr_ldap_escape_func, NULL) < 0) { REDEBUG("Failed creating profile filter"); return RLM_MODULE_INVALID; } - status = rlm_ldap_search(&result, inst, request, pconn, dn, - LDAP_SCOPE_BASE, filter, expanded->attrs, NULL, NULL); + status = fr_ldap_search(&result, request, pconn, dn, + LDAP_SCOPE_BASE, filter, expanded->attrs, NULL, NULL); switch (status) { case LDAP_PROC_SUCCESS: break; @@ -1104,7 +1029,7 @@ static rlm_rcode_t mod_authorize(void *instance, UNUSED void *thread, REQUEST *r * Bind as the user */ conn->rebound = true; - status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue, NULL, true, NULL, NULL, NULL); + status = fr_ldap_bind(request, &conn, dn, vp->vp_strvalue, NULL, NULL, NULL, NULL); switch (status) { case LDAP_PROC_SUCCESS: rcode = RLM_MODULE_OK; @@ -1179,7 +1104,7 @@ skip_edir: rlm_rcode_t ret; char *value; - value = rlm_ldap_berval_to_string(request, values[i]); + value = fr_ldap_berval_to_string(request, values[i]); ret = rlm_ldap_map_profile(inst, request, &conn, value, &expanded); talloc_free(value); if (ret == RLM_MODULE_FAIL) { @@ -1412,7 +1337,7 @@ static rlm_rcode_t user_modify(rlm_ldap_t const *inst, REQUEST *request, ldap_ac goto error; } - status = rlm_ldap_modify(inst, request, &conn, dn, modify, NULL, NULL); + status = fr_ldap_modify(request, &conn, dn, modify, NULL, NULL); switch (status) { case LDAP_PROC_SUCCESS: break; @@ -1537,6 +1462,8 @@ static int mod_bootstrap(CONF_SECTION *conf, void *instance) inst->name = cf_section_name2(conf); if (!inst->name) inst->name = cf_section_name1(conf); + inst->handle_config.name = talloc_asprintf(inst, "rlm_ldap (%s)", inst->name); + if (inst->group_attribute) { group_attribute = inst->group_attribute; } else if (cf_section_name2(conf)) { @@ -1573,7 +1500,7 @@ static int mod_bootstrap(CONF_SECTION *conf, void *instance) inst->cache_da = inst->group_da; /* Default to the group_da */ } - xlat_register(inst, inst->name, ldap_xlat, rlm_ldap_escape_func, NULL, 0, XLAT_DEFAULT_BUF_LEN); + xlat_register(inst, inst->name, ldap_xlat, fr_ldap_escape_func, NULL, 0, XLAT_DEFAULT_BUF_LEN); xlat_register(inst, "ldap_escape", ldap_escape_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN); xlat_register(inst, "ldap_unescape", ldap_unescape_xlat, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN); map_proc_register(inst, inst->name, mod_map_proc, ldap_map_verify, 0); @@ -1602,7 +1529,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) options = cf_section_sub_find(conf, "options"); if (!options || !cf_pair_find(options, "chase_referrals")) { - inst->pool_inst.chase_referrals_unset = true; /* use OpenLDAP defaults */ + inst->handle_config.chase_referrals_unset = true; /* use OpenLDAP defaults */ } /* @@ -1633,7 +1560,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) * connection pool. */ if (!cf_pair_find(conf, "pool")) { - if (!inst->pool_inst.server_str) { + if (!inst->handle_config.server_str) { cf_log_err_cs(conf, "Configuration item 'server' must have a value"); goto error; } @@ -1646,7 +1573,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) goto error; } - if (inst->pool_inst.admin_sasl.mech) { + if (inst->handle_config.admin_sasl.mech) { cf_log_err_cs(conf, "Configuration item 'sasl.mech' not supported. " "Linked libldap does not provide ldap_sasl_interactive_bind function"); goto error; @@ -1672,8 +1599,8 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) /* * Now iterate over all the 'server' config items */ - for (i = 0; i < talloc_array_length(inst->pool_inst.server_str); i++) { - char const *value = inst->pool_inst.server_str[i]; + for (i = 0; i < talloc_array_length(inst->handle_config.server_str); i++) { + char const *value = inst->handle_config.server_str[i]; size_t j; /* @@ -1754,7 +1681,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) */ if (ldap_url->lud_scheme) { if (strcmp(ldap_url->lud_scheme, "ldaps") == 0) { - if (inst->pool_inst.start_tls == true) { + if (inst->handle_config.start_tls == true) { cf_log_err_cs(conf, "ldaps:// scheme is not compatible " "with 'start_tls'"); goto ldap_url_error; @@ -1770,7 +1697,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) /* * URL port overrides configured port. */ - ldap_url->lud_port = inst->pool_inst.port; + ldap_url->lud_port = inst->handle_config.port; /* * If there's no URL port, then set it to the default @@ -1785,7 +1712,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) cf_log_err_cs(conf, "Failed recombining URL components"); goto ldap_url_error; } - inst->pool_inst.server = talloc_asprintf_append(inst->pool_inst.server, "%s ", url); + inst->handle_config.server = talloc_asprintf_append(inst->handle_config.server, "%s ", url); free(url); } # else @@ -1807,11 +1734,11 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) * port, we use the hard-coded default. */ if (set_port_maybe) { - ldap_url->lud_port = inst->pool_inst.port; + ldap_url->lud_port = inst->handle_config.port; if (!ldap_url->lud_port) ldap_url->lud_port = default_port; } - inst->pool_inst.server = talloc_asprintf_append(inst->pool_inst.server, "%s:%i ", + inst->handle_config.server = talloc_asprintf_append(inst->handle_config.server, "%s:%i ", ldap_url->lud_host ? ldap_url->lud_host : "localhost", ldap_url->lud_port); # endif @@ -1838,7 +1765,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) int port = 0; size_t len; - port = inst->pool_inst.port; + port = inst->handle_config.port; /* * We don't support URLs if the library didn't provide @@ -1865,44 +1792,44 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) } if (port == 0) port = LDAP_PORT; - inst->pool_inst.server = talloc_asprintf_append(inst->pool_inst.server, "ldap://%.*s:%i ", (int) len, value, port); + inst->handle_config.server = talloc_asprintf_append(inst->handle_config.server, "ldap://%.*s:%i ", (int) len, value, port); #else /* * ldap_init takes port, which can be overridden by :port so * we don't need to do any parsing here. */ - inst->pool_inst.server = talloc_asprintf_append(inst->pool_inst.server, "%s ", value); + inst->handle_config.server = talloc_asprintf_append(inst->handle_config.server, "%s ", value); #endif } } /* - * inst->pool_inst.server be unset if connection pool sharing is used. + * inst->handle_config.server be unset if connection pool sharing is used. */ - if (inst->pool_inst.server) { - inst->pool_inst.server[talloc_array_length(inst->pool_inst.server) - 2] = '\0'; - DEBUG4("rlm_ldap (%s) - LDAP server string: %s", inst->name, inst->pool_inst.server); + if (inst->handle_config.server) { + inst->handle_config.server[talloc_array_length(inst->handle_config.server) - 2] = '\0'; + DEBUG4("rlm_ldap (%s) - LDAP server string: %s", inst->name, inst->handle_config.server); } #ifdef LDAP_OPT_X_TLS_NEVER /* * Workaround for servers which support LDAPS but not START TLS */ - if (inst->pool_inst.port == LDAPS_PORT || inst->pool_inst.tls_mode) { - inst->pool_inst.tls_mode = LDAP_OPT_X_TLS_HARD; + if (inst->handle_config.port == LDAPS_PORT || inst->handle_config.tls_mode) { + inst->handle_config.tls_mode = LDAP_OPT_X_TLS_HARD; } else { - inst->pool_inst.tls_mode = 0; + inst->handle_config.tls_mode = 0; } #endif /* * Convert dereference strings to enumerated constants */ - if (inst->pool_inst.dereference_str) { - inst->pool_inst.dereference = fr_str2int(ldap_dereference, inst->pool_inst.dereference_str, -1); - if (inst->pool_inst.dereference < 0) { + if (inst->handle_config.dereference_str) { + inst->handle_config.dereference = fr_str2int(ldap_dereference, inst->handle_config.dereference_str, -1); + if (inst->handle_config.dereference < 0) { cf_log_err_cs(conf, "Invalid 'dereference' value \"%s\", expected 'never', 'searching', " - "'finding' or 'always'", inst->pool_inst.dereference_str); + "'finding' or 'always'", inst->handle_config.dereference_str); goto error; } } @@ -1981,7 +1908,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) * Always set the control as critical, if it's not needed * the user can comment it out... */ - ret = ldap_create_sort_control(global_handle, keys, 1, &inst->userobj_sort_ctrl); + ret = ldap_create_sort_control(ldap_global_handle, keys, 1, &inst->userobj_sort_ctrl); ldap_free_sort_keylist(keys); if (ret != LDAP_SUCCESS) { ERROR("Failed creating server sort control: %s", ldap_err2string(ret)); @@ -1990,16 +1917,16 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) } #endif - if (inst->pool_inst.tls_require_cert_str) { + if (inst->handle_config.tls_require_cert_str) { #ifdef LDAP_OPT_X_TLS_NEVER /* * Convert cert strictness to enumerated constants */ - inst->pool_inst.tls_require_cert = fr_str2int(ldap_tls_require_cert, - inst->pool_inst.tls_require_cert_str, -1); - if (inst->pool_inst.tls_require_cert < 0) { + inst->handle_config.tls_require_cert = fr_str2int(ldap_tls_require_cert, + inst->handle_config.tls_require_cert_str, -1); + if (inst->handle_config.tls_require_cert < 0) { cf_log_err_cs(conf, "Invalid 'tls.require_cert' value \"%s\", expected 'never', " - "'demand', 'allow', 'try' or 'hard'", inst->pool_inst.tls_require_cert_str); + "'demand', 'allow', 'try' or 'hard'", inst->handle_config.tls_require_cert_str); goto error; } #else @@ -2024,12 +1951,13 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) /* * Set global options */ - if (rlm_ldap_global_init(inst) < 0) goto error; + if (fr_ldap_global_init() < 0) goto error; /* * Initialize the socket pool. */ - inst->pool = module_connection_pool_init(inst->cs, inst, mod_conn_create, NULL, NULL, NULL, NULL); + inst->pool = module_connection_pool_init(inst->cs, &inst->handle_config, + mod_conn_create, NULL, NULL, NULL, NULL); if (!inst->pool) goto error; /* @@ -2059,6 +1987,8 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) } } + fr_ldap_global_config(inst->ldap_debug, inst->tls_random_file); + return 0; error: @@ -2067,68 +1997,14 @@ error: static int mod_load(void) { - static LDAPAPIInfo info = { .ldapai_info_version = LDAP_API_INFO_VERSION }; /* static to quiet valgrind about this being uninitialised */ - int ldap_errno; - - /* - * Only needs to be done once, prevents races in environment - * initialisation within libldap. - * - * See: https://github.com/arr2036/ldapperf/issues/2 - */ -#ifdef HAVE_LDAP_INITIALIZE - ldap_initialize(&global_handle, ""); -#else - global_handle = ldap_init("", 0); -#endif - - ldap_errno = ldap_get_option(NULL, LDAP_OPT_API_INFO, &info); - if (ldap_errno == LDAP_OPT_SUCCESS) { - /* - * Don't generate warnings if the compile type vendor name - * is found within the link time vendor name. - * - * This allows the server to be built against OpenLDAP but - * run with Symas OpenLDAP. - */ - if (strcasestr(info.ldapai_vendor_name, LDAP_VENDOR_NAME) == NULL) { - WARN("rlm_ldap - libldap vendor changed since the server was built"); - WARN("rlm_ldap - linked: %s, built: %s", info.ldapai_vendor_name, LDAP_VENDOR_NAME); - } - - if (info.ldapai_vendor_version < LDAP_VENDOR_VERSION) { - WARN("rlm_ldap - libldap older than the version the server was built against"); - WARN("rlm_ldap - linked: %i, built: %i", - info.ldapai_vendor_version, LDAP_VENDOR_VERSION); - } - - INFO("rlm_ldap - libldap vendor: %s, version: %i", info.ldapai_vendor_name, - info.ldapai_vendor_version); - - ldap_memfree(info.ldapai_vendor_name); - ldap_memfree(info.ldapai_extensions); - } else { - DEBUG("rlm_ldap - Falling back to build time libldap version info. Query for LDAP_OPT_API_INFO " - "returned: %i", ldap_errno); - INFO("rlm_ldap - libldap vendor: %s, version: %i.%i.%i", LDAP_VENDOR_NAME, - LDAP_VENDOR_VERSION_MAJOR, LDAP_VENDOR_VERSION_MINOR, LDAP_VENDOR_VERSION_PATCH); - } + fr_ldap_global_init(); return 0; } static void mod_unload(void) { - /* - * Keeping the dummy ld around for the lifetime - * of the module should always work, - * irrespective of what changes happen in libldap. - */ -#ifdef HAVE_LDAP_UNBIND_EXT_S - ldap_unbind_ext_s(global_handle, NULL, NULL); -#else - ldap_unbind_s(global_handle); -#endif + fr_ldap_global_free();; } /* globally exported name */ diff --git a/src/modules/rlm_ldap/rlm_ldap.h b/src/modules/rlm_ldap/rlm_ldap.h index 284c75d053a..43c2d0aab4d 100644 --- a/src/modules/rlm_ldap/rlm_ldap.h +++ b/src/modules/rlm_ldap/rlm_ldap.h @@ -17,123 +17,15 @@ #include #include +#include "libldap.h" -/* - * We're mostly using the new API now, but ldap_bind - * is in the list of deprecated functions, at we may - * always need to support that. - */ -#define LDAP_DEPRECATED 1 -#include -#include -#include "config.h" - -/* - * Framework on OSX doesn't export the symbols but leaves - * the macro defined *sigh*. - */ -#ifndef HAVE_LDAP_CREATE_SESSION_TRACKING_CONTROL -# undef LDAP_CONTROL_X_SESSION_TRACKING -#endif - -/* - * There's a typo in libldap's ldap.h which was fixed by - * Howard Chu in 19aeb1cd. This typo had the function defined - * as ldap_create_session_tracking_control but declared as - * ldap_create_session_tracking. - * - * We fix this, by adding the correct declaration here. - */ -#ifdef LDAP_CONTROL_X_SESSION_TRACKING -# if !defined(HAVE_DECL_LDAP_CREATE_SESSION_TRACKING_CONTROL) || (HAVE_DECL_LDAP_CREATE_SESSION_TRACKING_CONTROL == 0) -LDAP_F( int ) -ldap_create_session_tracking_control LDAP_P(( - LDAP *ld, - char *sessionSourceIp, - char *sessionSourceName, - char *formatOID, - struct berval *sessionTrackingIdentifier, - LDAPControl **ctrlp )); -# endif -#endif - -/* - * Ensure the have the ldap_create_sort_keylist() - * function too, else we can't use ldap_create_sort_control() - */ -#if !defined(HAVE_LDAP_CREATE_SORT_KEYLIST) || !defined(HAVE_LDAP_FREE_SORT_KEYLIST) -# undef HAVE_LDAP_CREATE_SORT_CONTROL -#endif - -/* - * Because the LTB people define LDAP_VENDOR_VERSION_PATCH - * as X, which precludes its use in printf statements *sigh* - * - * Identifiers that are not macros, all evaluate to 0, - * which is why this works. - */ -#if !defined(LDAP_VENDOR_VERSION_PATCH) || LDAP_VENDOR_VERSION_PATCH == 0 -# undef LDAP_VENDOR_VERSION_PATCH -# define LDAP_VENDOR_VERSION_PATCH 0 -#endif - -/* - * For compatibility with other LDAP libraries - */ -#if !defined(LDAP_SCOPE_BASE) && defined(LDAP_SCOPE_BASEOBJECT) -# define LDAP_SCOPE_BASE LDAP_SCOPE_BASEOBJECT -#endif - -#if !defined(LDAP_SCOPE_ONE) && defined(LDAP_SCOPE_ONELEVEL) -# define LDAP_SCOPE_ONE LDAP_SCOPE_ONELEVEL -#endif - -#if !defined(LDAP_SCOPE_SUB) && defined(LDAP_SCOPE_SUBTREE) -# define LDAP_SCOPE_SUB LDAP_SCOPE_SUBTREE -#endif - -#if !defined(LDAP_OPT_RESULT_CODE) && defined(LDAP_OPT_ERROR_NUMBER) -# define LDAP_OPT_RESULT_CODE LDAP_OPT_ERROR_NUMBER -#endif - -#ifndef LDAP_CONST -# define LDAP_CONST -#endif - -#if defined(HAVE_LDAP_URL_PARSE) && defined(HAVE_LDAP_IS_LDAP_URL) && defined(HAVE_LDAP_URL_DESC2STR) -# define LDAP_CAN_PARSE_URLS -#endif - -#define LDAP_MAX_CONTROLS 10 //!< Maximum number of client/server controls. - //!< Used to allocate static arrays of control pointers. -#define LDAP_MAX_ATTRMAP 128 //!< Maximum number of mappings between LDAP and - //!< FreeRADIUS attributes. -#define LDAP_MAP_RESERVED 4 //!< Number of additional items to allocate in expanded - //!< attribute name arrays. Currently for enable attribute, - //!< group membership attribute, valuepair attribute, - //!< and profile attribute. - -#define LDAP_MAX_CACHEABLE 64 //!< Maximum number of groups we retrieve from the server for - //!< a given user. If more than this number are retrieve the - //!< module returns invalid. +typedef struct ldap_inst_s rlm_ldap_t; -#define LDAP_MAX_GROUP_NAME_LEN 128 //!< Maximum name of a group name. -#define LDAP_MAX_ATTR_STR_LEN 256 //!< Maximum length of an xlat expanded LDAP attribute. -#define LDAP_MAX_FILTER_STR_LEN 1024 //!< Maximum length of an xlat expanded filter. -#define LDAP_MAX_DN_STR_LEN 1024 //!< Maximum length of an xlat expanded DN. - -#define LDAP_VIRTUAL_DN_ATTR "dn" //!< 'Virtual' attribute which maps to the DN of the object. - - -typedef enum { - LDAP_EXT_UNSUPPORTED, //!< Unsupported extension. - LDAP_EXT_BINDNAME, //!< Specifies the user DN or name for an LDAP bind. - LDAP_EXT_BINDPW, //!< Specifies the password for an LDAP bind. -} ldap_supported_extension; - -extern FR_NAME_NUMBER const ldap_supported_extensions[]; - -typedef struct rlm_ldap_s rlm_ldap_t; +typedef struct { + vp_tmpl_t *mech; //!< SASL mech(s) to try. + vp_tmpl_t *proxy; //!< Identity to proxy. + vp_tmpl_t *realm; //!< Kerberos realm. +} ldap_sasl_dynamic_t; typedef struct ldap_acct_section { CONF_SECTION *cs; //!< Section configuration. @@ -141,141 +33,7 @@ typedef struct ldap_acct_section { char const *reference; //!< Configuration reference string. } ldap_acct_section_t; -typedef struct ldap_sasl { - char const *mech; //!< SASL mech(s) to try. - char const *proxy; //!< Identity to proxy. - char const *realm; //!< Kerberos realm. -} ldap_sasl; - -typedef struct ldap_sasl_dynamic { - vp_tmpl_t *mech; //!< SASL mech(s) to try. - vp_tmpl_t *proxy; //!< Identity to proxy. - vp_tmpl_t *realm; //!< Kerberos realm. -} ldap_sasl_dynamic; - -typedef struct rlm_ldap_control { - LDAPControl *control; //!< LDAP control. - bool freeit; //!< Whether the control should be freed after - //!< we've finished using it. -} rlm_ldap_control_t; - - -typedef enum { - LDAP_DIRECTORY_UNKNOWN = 0, //!< We can't determine the directory server. - - LDAP_DIRECTORY_ACTIVE_DIRECTORY, //!< Directory server is Active Directory. - LDAP_DIRECTORY_EDIRECTORY, //!< Directory server is eDir. - LDAP_DIRECTORY_IBM, //!< Directory server is IBM. - LDAP_DIRECTORY_NETSCAPE, //!< Directory server is Netscape. - LDAP_DIRECTORY_OPENLDAP, //!< Directory server is OpenLDAP. - LDAP_DIRECTORY_ORACLE_INTERNET_DIRECTORY, //!< Directory server is Oracle Internet Directory. - LDAP_DIRECTORY_ORACLE_UNIFIED_DIRECTORY, //!< Directory server is Oracle Unified Directory. - LDAP_DIRECTORY_ORACLE_VIRTUAL_DIRECTORY, //!< Directory server is Oracle Virtual Directory. - LDAP_DIRECTORY_SUN_ONE_DIRECTORY, //!< Directory server is Sun One Directory. - LDAP_DIRECTORY_SIEMENS_AG, //!< Directory server is Siemens AG. - LDAP_DIRECTORY_UNBOUND_ID //!< Directory server is Unbound ID -} ldap_directory_type_t; - -typedef struct ldap_directory { - char const *vendor_str; //!< As returned from the vendorName attribute in the - //!< rootDSE. - char const *version_str; //!< As returned from the vendorVersion attribute in the - //!< rootDSE. - ldap_directory_type_t type; //!< Cannonical server implementation. - - bool cleartext_password; //!< Whether the server will return the user's plaintext - //!< password. -} ldap_directory_t; - -/** Pool configuration - * - * Must not be passed into functions except via the connection handle - * this avoids problems with not using the connection pool configuration. - */ -typedef struct { - char *server; //!< Initial server to bind to. - char const **server_str; //!< Server set in the config. - - uint16_t port; //!< Port to use when binding to the server. - - char const *admin_identity; //!< Identity we bind as when we need to query the LDAP - //!< directory. - char const *admin_password; //!< Password used in administrative bind. - - ldap_sasl admin_sasl; //!< SASL parameters used when binding as the admin. - - int dereference; //!< libldap value specifying dereferencing behaviour. - char const *dereference_str; //!< When to dereference (never, searching, finding, always) - - bool chase_referrals; //!< If the LDAP server returns a referral to another server - //!< or point in the tree, follow it, establishing new - //!< connections and binding where necessary. - bool chase_referrals_unset; //!< If true, use the OpenLDAP defaults for chase_referrals. - - bool use_referral_credentials; //!< If true use credentials from the referral URL. - - bool rebind; //!< Controls whether we set an ldad_rebind_proc function - //!< and so determines if we can bind to other servers whilst - //!< chasing referrals. If this is false, we will still chase - //!< referrals on the same server, but won't bind to other - //!< servers. - - /* - * TLS items. - */ - int tls_mode; - bool start_tls; //!< Send the Start TLS message to the LDAP directory - //!< to start encrypted communications using the standard - //!< LDAP port. - - char const *tls_ca_file; //!< Sets the full path to a CA certificate (used to validate - //!< the certificate the server presents). - - char const *tls_ca_path; //!< Sets the path to a directory containing CA certificates. - - char const *tls_certificate_file; //!< Sets the path to the public certificate file we present - //!< to the servers. - - char const *tls_private_key_file; //!< Sets the path to the private key for our public - //!< certificate. - - char const *tls_require_cert_str; //!< Sets requirements for validating the certificate the - //!< server presents. - - int tls_require_cert; //!< OpenLDAP constant representing the require cert string. - - /* - * Options - */ -#ifdef LDAP_CONTROL_X_SESSION_TRACKING - bool session_tracking; //!< Whether we add session tracking controls, which help - //!< identify the autz or acct session the commands were - //!< issued for. -#endif - - /* - * For keep-alives. - */ -#ifdef LDAP_OPT_X_KEEPALIVE_IDLE - uint32_t keepalive_idle; //!< Number of seconds a connections needs to remain idle - //!< before TCP starts sending keepalive probes. -#endif -#ifdef LDAP_OPT_X_KEEPALIVE_PROBES - uint32_t keepalive_probes; //!< Number of missed timeouts before the connection is - //!< dropped. -#endif -#ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL - uint32_t keepalive_interval; //!< Interval between keepalive probes. -#endif - - /* - * Search timelimits - */ - uint32_t srv_timelimit; //!< How long the server should spent on a single request - //!< (also bounded by value on the server). -} ldap_pool_inst_t; - -struct rlm_ldap_s { +struct ldap_inst_s { char const *name; //!< Instance name. CONF_SECTION *cs; //!< Main configuration section for this instance. @@ -289,9 +47,13 @@ struct rlm_ldap_s { vp_map_t *user_map; //!< Attribute map applied to users and profiles. /* - * Search time limits + * Options */ - uint32_t res_timeout; //!< How long we wait for a result from the server. +#ifdef LDAP_CONTROL_X_SESSION_TRACKING + bool session_tracking; //!< Whether we add session tracking controls, which help + //!< identify the autz or acct session the commands were + //!< issued for. +#endif /* * User object attributes and filters @@ -309,7 +71,7 @@ struct rlm_ldap_s { bool access_positive; //!< If true the presence of the attribute will allow access, //!< else it will deny access. - ldap_sasl_dynamic user_sasl; //!< SASL parameters used when binding as the user. + ldap_sasl_dynamic_t user_sasl; //!< SASL parameters used when binding as the user. char const *valuepair_attr; //!< Generic dynamic mapping attribute, contains a RADIUS //!< attribute and value. @@ -387,7 +149,7 @@ struct rlm_ldap_s { #endif fr_connection_pool_t *pool; //!< Connection pool instance. - ldap_pool_inst_t pool_inst; //!< Connection configuration instance. + ldap_handle_config_t handle_config; //!< Connection configuration instance. /* * Global config @@ -398,29 +160,6 @@ struct rlm_ldap_s { uint32_t ldap_debug; //!< Debug flag for the SDK. }; -/** Tracks the state of a libldap connection handle - * - */ -typedef struct ldap_handle { - LDAP *handle; //!< libldap handle. - bool rebound; //!< Whether the connection has been rebound to something - //!< other than the admin user. - bool referred; //!< Whether the connection is now established a server - //!< other than the configured one. - - rlm_ldap_control_t serverctrls[LDAP_MAX_CONTROLS + 1]; //!< Server controls to use for all operations with - //!< this handle. - rlm_ldap_control_t clientctrls[LDAP_MAX_CONTROLS + 1]; //!< Client controls to use for all operations with - //!< this handle. - int serverctrls_cnt; //!< Number of server controls associated with the handle. - int clientctrls_cnt; //!< Number of client controls associated with the handle. - - ldap_directory_t *directory; //!< The type of directory we're connected to. - - ldap_pool_inst_t const *pool_inst; //!< rlm_ldap connection configuration. - rlm_ldap_t const *inst; //!< rlm_ldap pool inst. -} ldap_handle_t; - /** Result of expanding the RHS of a set of maps * * Used to store the array of attributes we'll be querying for. @@ -433,104 +172,17 @@ typedef struct rlm_ldap_map_exp { int count; //!< Index on next free element. } rlm_ldap_map_exp_t; -/** Contains a collection of values - * - */ -typedef struct rlm_ldap_result { - struct berval **values; //!< libldap struct containing bv_val (char *) - //!< and length bv_len. - int count; //!< Number of values. -} rlm_ldap_result_t; - -/** Codes returned by rlm_ldap internal functions - * - */ -typedef enum { - LDAP_PROC_CONTINUE = 1, //!< Operation is in progress. - LDAP_PROC_SUCCESS = 0, //!< Operation was successfull. - - LDAP_PROC_ERROR = -1, //!< Unrecoverable library/server error. - - LDAP_PROC_RETRY = -2, //!< Transitory error, caller should retry the operation - //!< with a new connection. - - LDAP_PROC_NOT_PERMITTED = -3, //!< Operation was not permitted, either current user was - //!< locked out in the case of binds, or has insufficient - //!< access. - - LDAP_PROC_REJECT = -4, //!< Bind failed, user was rejected. - - LDAP_PROC_BAD_DN = -5, //!< Specified an invalid object in a bind or search DN. - - LDAP_PROC_NO_RESULT = -6 //!< Got no results. -} ldap_rcode_t; - -/* - * Some functions may be called with a NULL request structure, this - * simplifies switching certain messages from the request log to - * the main log. - */ -#define LDAP_EXTRA_DEBUG() do { if (extra) { if (request) REDEBUG("%s", extra); else ERROR("%s", extra); }} while (0) - -extern FR_NAME_NUMBER const ldap_scope[]; -extern FR_NAME_NUMBER const ldap_tls_require_cert[]; - /* - * ldap.c - Wrappers arounds OpenLDAP functions. + * user.c - User lookup functions */ -void rlm_ldap_timeout_debug(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t const *conn, - struct timeval const *timeout, char const *prefix); - -size_t rlm_ldap_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg); - -size_t rlm_ldap_unescape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg); - -bool rlm_ldap_is_dn(char const *in, size_t inlen); - -size_t rlm_ldap_normalise_dn(char *out, char const *in); - -ssize_t rlm_ldap_xlat_filter(REQUEST *request, char const **sub, size_t sublen, char *out, size_t outlen); - -ldap_rcode_t rlm_ldap_bind(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn, - char const *password, ldap_sasl const *sasl, bool retry, struct timeval const *timeout, - LDAPControl **serverctrls, LDAPControl **clientctrls); - -char const *rlm_ldap_error_str(ldap_handle_t const *conn); - -ldap_rcode_t rlm_ldap_search(LDAPMessage **result, rlm_ldap_t const *inst, REQUEST *request, - ldap_handle_t **pconn, - char const *dn, int scope, char const *filter, char const * const *attrs, - LDAPControl **serverctrls, LDAPControl **clientctrls); - -ldap_rcode_t rlm_ldap_modify(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, - char const *dn, LDAPMod *mods[], - LDAPControl **serverctrls, LDAPControl **clientctrls); - char const *rlm_ldap_find_user(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *attrs[], bool force, LDAPMessage **result, rlm_rcode_t *rcode); -rlm_rcode_t rlm_ldap_check_access(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t const *conn, - LDAPMessage *entry); +rlm_rcode_t rlm_ldap_check_access(rlm_ldap_t const *inst, REQUEST *request, + ldap_handle_t const *conn, LDAPMessage *entry); void rlm_ldap_check_reply(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t const *conn); -/* - * ldap.c - Callbacks for the connection pool API. - */ -ldap_rcode_t rlm_ldap_result(rlm_ldap_t const *inst, ldap_handle_t const *conn, int msgid, char const *dn, - struct timeval const *timeout, - LDAPMessage **result, char const **error, char **extra); - -char *rlm_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in); - -int rlm_ldap_global_init(rlm_ldap_t *inst) CC_HINT(nonnull); - -void *mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *timeout); - -ldap_handle_t *mod_conn_get(rlm_ldap_t const *inst, REQUEST *request); - -void mod_conn_release(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t *conn); - /* * groups.c - Group membership functions. */ @@ -547,6 +199,15 @@ rlm_rcode_t rlm_ldap_check_userobj_dynamic(rlm_ldap_t const *inst, REQUEST *requ rlm_rcode_t rlm_ldap_check_cached(rlm_ldap_t const *inst, REQUEST *request, VALUE_PAIR *check); +/* + * conn.c - Connection wrappers. + */ +ldap_handle_t *mod_conn_get(rlm_ldap_t const *inst, REQUEST *request); + +void mod_conn_release(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t *conn); + +void *mod_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *timeout); + /* * attrmap.c - Attribute mapping code. */ @@ -564,30 +225,6 @@ int rlm_ldap_map_do(rlm_ldap_t const *inst, REQUEST *request, LDAP *handle, */ int rlm_ldap_client_load(rlm_ldap_t const *inst, CONF_SECTION *tmpl, CONF_SECTION *cs); -/* - * control.c - Connection based client/server controls - */ -void rlm_ldap_control_merge(LDAPControl *serverctrls_out[], - LDAPControl *clientctrls_out[], - size_t serverctrls_len, - size_t clientctrls_len, - ldap_handle_t *conn, - LDAPControl *serverctrls_in[], - LDAPControl *clientctrls_in[]); - -int rlm_ldap_control_add_server(ldap_handle_t *conn, LDAPControl *ctrl, bool freeit); - -int rlm_ldap_control_add_client(ldap_handle_t *conn, LDAPControl *ctrl, bool freeit); - -void rlm_ldap_control_clear(ldap_handle_t *conn); - -int rlm_ldap_control_add_session_tracking(ldap_handle_t *conn, REQUEST *request); - -/* - * directory.c - Get directory capabilities from the remote server - */ -int rlm_ldap_directory_alloc(TALLOC_CTX *ctx, ldap_directory_t **out, rlm_ldap_t const *inst, ldap_handle_t **pconn); - /* * edir.c - Magic extensions for Novell */ @@ -595,13 +232,4 @@ int nmasldap_get_password(LDAP *ld, char const *dn, char *password, size_t *len) char const *edir_errstr(int code); -/* - * sasl.s - SASL bind functions - */ -ldap_rcode_t rlm_ldap_sasl_interactive(rlm_ldap_t const *inst, REQUEST *request, - ldap_handle_t *pconn, char const *dn, - char const *password, ldap_sasl const *sasl, - LDAPControl **serverctrls, LDAPControl **clientctrls, - struct timeval const *timeout, - char const **error, char **error_extra); #endif diff --git a/src/modules/rlm_ldap/rlm_ldap.mk b/src/modules/rlm_ldap/rlm_ldap.mk new file mode 100644 index 00000000000..84367d79d86 --- /dev/null +++ b/src/modules/rlm_ldap/rlm_ldap.mk @@ -0,0 +1,15 @@ +# This needs to be cleared explicitly, as the libfreeradius-ldap.mk +# might not always be available, and the TARGETNAME from the previous +# target may stick around. +TARGETNAME= +-include $(top_builddir)/src/modules/rlm_ldap/libfreeradius-ldap.mk + +ifneq "${TARGETNAME}" "" + TARGETNAME := rlm_ldap + TARGET := $(TARGETNAME).a +endif + +SOURCES := $(TARGETNAME).c attrmap.c clients.c groups.c user.c conn.c + +SRC_CFLAGS += -I$(top_builddir)/src/modules/rlm_ldap +TGT_PREREQS := libfreeradius-ldap.a diff --git a/src/modules/rlm_ldap/sasl.c b/src/modules/rlm_ldap/sasl.c index 00a0156be3a..55a3c0c9fbf 100644 --- a/src/modules/rlm_ldap/sasl.c +++ b/src/modules/rlm_ldap/sasl.c @@ -36,7 +36,6 @@ * */ typedef struct rlm_ldap_sasl_ctx { - rlm_ldap_t const *inst; //!< LDAP instance REQUEST *request; //!< The current request. char const *identity; //!< User's DN or identity. @@ -57,7 +56,6 @@ static int _sasl_interact(UNUSED LDAP *handle, UNUSED unsigned flags, void *ctx, { rlm_ldap_sasl_ctx_t *this = ctx; REQUEST *request = this->request; - rlm_ldap_t const *inst = this->inst; sasl_interact_t *cb = sasl_callbacks; sasl_interact_t *cb_p; @@ -92,7 +90,6 @@ static int _sasl_interact(UNUSED LDAP *handle, UNUSED unsigned flags, void *ctx, /** Initiate an LDAP interactive bind * - * @param[in] inst rlm_ldap configuration. * @param[in] request Current request, this may be NULL. * @param[in] conn to use. May change as this function calls functions * which auto re-connect. @@ -106,7 +103,7 @@ static int _sasl_interact(UNUSED LDAP *handle, UNUSED unsigned flags, void *ctx, * @param[out] extra information about the error. * @return One of the LDAP_PROC_* (#ldap_rcode_t) values. */ -ldap_rcode_t rlm_ldap_sasl_interactive(rlm_ldap_t const *inst, REQUEST *request, +ldap_rcode_t rlm_ldap_sasl_interactive(REQUEST *request, ldap_handle_t *conn, char const *identity, char const *password, ldap_sasl const *sasl, LDAPControl **serverctrls, LDAPControl **clientctrls, @@ -132,7 +129,6 @@ ldap_rcode_t rlm_ldap_sasl_interactive(rlm_ldap_t const *inst, REQUEST *request, if (error) *error = NULL; if (extra) *extra = NULL; - sasl_ctx.inst = inst; sasl_ctx.request = request; sasl_ctx.identity = identity; sasl_ctx.password = password; @@ -155,7 +151,7 @@ ldap_rcode_t rlm_ldap_sasl_interactive(rlm_ldap_t const *inst, REQUEST *request, * successful without the help of ldap_result. */ if (ret != LDAP_SASL_BIND_IN_PROGRESS) { - status = rlm_ldap_result(inst, conn, -1, identity, timeout, NULL, error, extra); + status = rlm_ldap_result(conn, -1, identity, timeout, NULL, error, extra); break; /* Old result gets freed on after exit */ } @@ -165,7 +161,7 @@ ldap_rcode_t rlm_ldap_sasl_interactive(rlm_ldap_t const *inst, REQUEST *request, * If LDAP parse result indicates there was an error * then we're done. */ - status = rlm_ldap_result(inst, conn, msgid, identity, timeout, &result, error, extra); + status = rlm_ldap_result(conn, msgid, identity, timeout, &result, error, extra); switch (status) { case LDAP_PROC_SUCCESS: /* ldap_sasl_interactive_bind should have indicated success */ case LDAP_PROC_CONTINUE: diff --git a/src/modules/rlm_ldap/user.c b/src/modules/rlm_ldap/user.c new file mode 100644 index 00000000000..384860ab446 --- /dev/null +++ b/src/modules/rlm_ldap/user.c @@ -0,0 +1,338 @@ +/* + * This program is is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * @file groups.c + * @brief LDAP module group functions. + * + * @author Arran Cudbard-Bell + * + * @copyright 2013 Network RADIUS SARL + * @copyright 2013-2015 The FreeRADIUS Server Project. + */ +#include +#include + +#define LOG_PREFIX "rlm_ldap (%s) - " +#define LOG_PREFIX_ARGS inst->name + +#include "rlm_ldap.h" + +/** Retrieve the DN of a user object + * + * Retrieves the DN of a user and adds it to the control list as LDAP-UserDN. Will also retrieve any + * attributes passed and return the result in *result. + * + * This potentially allows for all authorization and authentication checks to be performed in one + * ldap search operation, which is a big bonus given the number of crappy, slow *cough*AD*cough* + * LDAP directory servers out there. + * + * @param[in] inst rlm_ldap configuration. + * @param[in] request Current request. + * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. + * @param[in] attrs Additional attributes to retrieve, may be NULL. + * @param[in] force Query even if the User-DN already exists. + * @param[out] result Where to write the result, may be NULL in which case result is discarded. + * @param[out] rcode The status of the operation, one of the RLM_MODULE_* codes. + * @return The user's DN or NULL on error. + */ +char const *rlm_ldap_find_user(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t **pconn, + char const *attrs[], bool force, LDAPMessage **result, rlm_rcode_t *rcode) +{ + static char const *tmp_attrs[] = { NULL }; + + ldap_rcode_t status; + VALUE_PAIR *vp = NULL; + LDAPMessage *tmp_msg = NULL, *entry = NULL; + int ldap_errno; + int cnt; + char *dn = NULL; + char const *filter = NULL; + char filter_buff[LDAP_MAX_FILTER_STR_LEN]; + char const *base_dn; + char base_dn_buff[LDAP_MAX_DN_STR_LEN]; + LDAPControl *serverctrls[] = { inst->userobj_sort_ctrl, NULL }; + + bool freeit = false; //!< Whether the message should + //!< be freed after being processed. + + *rcode = RLM_MODULE_FAIL; + + if (!result) { + result = &tmp_msg; + freeit = true; + } + *result = NULL; + + if (!attrs) { + memset(&attrs, 0, sizeof(tmp_attrs)); + } + + /* + * If the caller isn't looking for the result we can just return the current userdn value. + */ + if (!force) { + vp = fr_pair_find_by_num(request->control, 0, PW_LDAP_USERDN, TAG_ANY); + if (vp) { + RDEBUG("Using user DN from request \"%s\"", vp->vp_strvalue); + *rcode = RLM_MODULE_OK; + return vp->vp_strvalue; + } + } + + /* + * Perform all searches as the admin user. + */ + if ((*pconn)->rebound) { + status = fr_ldap_bind(request, pconn, (*pconn)->config->admin_identity, + (*pconn)->config->admin_password, &(*pconn)->config->admin_sasl, + NULL, NULL, NULL); + if (status != LDAP_PROC_SUCCESS) { + *rcode = RLM_MODULE_FAIL; + return NULL; + } + + rad_assert(*pconn); + + (*pconn)->rebound = false; + } + + if (inst->userobj_filter) { + if (tmpl_expand(&filter, filter_buff, sizeof(filter_buff), request, inst->userobj_filter, + fr_ldap_escape_func, NULL) < 0) { + REDEBUG("Unable to create filter"); + *rcode = RLM_MODULE_INVALID; + + return NULL; + } + } + + if (tmpl_expand(&base_dn, base_dn_buff, sizeof(base_dn_buff), request, + inst->userobj_base_dn, fr_ldap_escape_func, NULL) < 0) { + REDEBUG("Unable to create base_dn"); + *rcode = RLM_MODULE_INVALID; + + return NULL; + } + + status = fr_ldap_search(result, request, pconn, base_dn, + inst->userobj_scope, filter, attrs, serverctrls, NULL); + switch (status) { + case LDAP_PROC_SUCCESS: + break; + + case LDAP_PROC_BAD_DN: + case LDAP_PROC_NO_RESULT: + *rcode = RLM_MODULE_NOTFOUND; + return NULL; + + default: + *rcode = RLM_MODULE_FAIL; + return NULL; + } + + rad_assert(*pconn); + + /* + * Forbid the use of unsorted search results that + * contain multiple entries, as it's a potential + * security issue, and likely non deterministic. + */ + if (!inst->userobj_sort_ctrl) { + cnt = ldap_count_entries((*pconn)->handle, *result); + if (cnt > 1) { + REDEBUG("Ambiguous search result, returned %i unsorted entries (should return 1 or 0). " + "Enable sorting, or specify a more restrictive base_dn, filter or scope", cnt); + REDEBUG("The following entries were returned:"); + RINDENT(); + for (entry = ldap_first_entry((*pconn)->handle, *result); + entry; + entry = ldap_next_entry((*pconn)->handle, entry)) { + dn = ldap_get_dn((*pconn)->handle, entry); + REDEBUG("%s", dn); + ldap_memfree(dn); + } + REXDENT(); + *rcode = RLM_MODULE_INVALID; + goto finish; + } + } + + entry = ldap_first_entry((*pconn)->handle, *result); + if (!entry) { + ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno); + REDEBUG("Failed retrieving entry: %s", + ldap_err2string(ldap_errno)); + + goto finish; + } + + dn = ldap_get_dn((*pconn)->handle, entry); + if (!dn) { + ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno); + REDEBUG("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno)); + + goto finish; + } + fr_ldap_util_normalise_dn(dn, dn); + + /* + * We can't use fr_pair_make here to copy the value into the + * attribute, as the dn must be copied into the attribute + * verbatim (without de-escaping). + * + * Special chars are pre-escaped by libldap, and because + * we pass the string back to libldap we must not alter it. + */ + RDEBUG("User object found at DN \"%s\"", dn); + vp = fr_pair_make(request, &request->control, "LDAP-UserDN", NULL, T_OP_EQ); + if (vp) { + fr_pair_value_strcpy(vp, dn); + *rcode = RLM_MODULE_OK; + } + ldap_memfree(dn); + +finish: + if ((freeit || (*rcode != RLM_MODULE_OK)) && *result) { + ldap_msgfree(*result); + *result = NULL; + } + + return vp ? vp->vp_strvalue : NULL; +} + +/** Check for presence of access attribute in result + * + * @param[in] inst rlm_ldap configuration. + * @param[in] request Current request. + * @param[in] conn used to retrieve access attributes. + * @param[in] entry retrieved by rlm_ldap_find_user or fr_ldap_search. + * @return + * - #RLM_MODULE_USERLOCK if the user was denied access. + * - #RLM_MODULE_OK otherwise. + */ +rlm_rcode_t rlm_ldap_check_access(rlm_ldap_t const *inst, REQUEST *request, + ldap_handle_t const *conn, LDAPMessage *entry) +{ + rlm_rcode_t rcode = RLM_MODULE_OK; + struct berval **values = NULL; + + values = ldap_get_values_len(conn->handle, entry, inst->userobj_access_attr); + if (values) { + if (inst->access_positive) { + if ((values[0]->bv_len >= 5) && (strncasecmp(values[0]->bv_val, "false", 5) == 0)) { + RDEBUG("\"%s\" attribute exists but is set to 'false' - user locked out", + inst->userobj_access_attr); + rcode = RLM_MODULE_USERLOCK; + } + /* RLM_MODULE_OK set above... */ + } else if ((values[0]->bv_len < 5) || (strncasecmp(values[0]->bv_val, "false", 5) != 0)) { + RDEBUG("\"%s\" attribute exists - user locked out", inst->userobj_access_attr); + rcode = RLM_MODULE_USERLOCK; + } + ldap_value_free_len(values); + } else if (inst->access_positive) { + RDEBUG("No \"%s\" attribute - user locked out", inst->userobj_access_attr); + rcode = RLM_MODULE_USERLOCK; + } + + return rcode; +} + +/** Verify we got a password from the search + * + * Checks to see if after the LDAP to RADIUS mapping has been completed that a reference password. + * + * @param inst rlm_ldap configuration. + * @param request Current request. + * @param conn the connection handle + */ +void rlm_ldap_check_reply(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t const *conn) +{ + /* + * More warning messages for people who can't be bothered to read the documentation. + * + * Expect_password is set when we process the mapping, and is only true if there was a mapping between + * an LDAP attribute and a password reference attribute in the control list. + */ + if (!inst->expect_password || (rad_debug_lvl < L_DBG_LVL_2)) return; + + if (!fr_pair_find_by_num(request->control, 0, PW_CLEARTEXT_PASSWORD, TAG_ANY) && + !fr_pair_find_by_num(request->control, 0, PW_NT_PASSWORD, TAG_ANY) && + !fr_pair_find_by_num(request->control, 0, PW_USER_PASSWORD, TAG_ANY) && + !fr_pair_find_by_num(request->control, 0, PW_PASSWORD_WITH_HEADER, TAG_ANY) && + !fr_pair_find_by_num(request->control, 0, PW_CRYPT_PASSWORD, TAG_ANY)) { + switch (conn->directory->type) { + case LDAP_DIRECTORY_ACTIVE_DIRECTORY: + RWDEBUG("!!! Found map between LDAP attribute and a FreeRADIUS password attribute"); + RWDEBUG("!!! Active Directory does not allow passwords to be read via LDAP"); + RWDEBUG("!!! Remove the password map and either:"); + RWDEBUG("!!! - Configure authentication via ntlm_auth (mschapv2 only)"); + RWDEBUG("!!! - Configure authentication via wbclient (mschapv2 only)"); + RWDEBUG("!!! that password attribute"); + RWDEBUG("!!! - Bind as the user by listing %s in the authenticate section, and", + inst->name); + RWDEBUG("!!! setting attribute &control:Auth-Type := '%s' in the authorize section", + inst->name); + RWDEBUG("!!! (pap only)"); + + break; + + case LDAP_DIRECTORY_EDIRECTORY: + RWDEBUG("!!! Found map between LDAP attribute and a FreeRADIUS password attribute"); + RWDEBUG("!!! eDirectory does not allow passwords to be retrieved via LDAP search"); + RWDEBUG("!!! Remove the password map and either:"); + RWDEBUG("!!! - Set 'edir = yes' and enable the universal password feature on your "); + RWDEBUG("!!! eDir server (recommended)"); + RWDEBUG("!!! that password attribute"); + RWDEBUG("!!! - Bind as the user by listing %s in the authenticate section, and", + inst->name); + RWDEBUG("!!! setting attribute &control:Auth-Type := '%s' in the authorize section", + inst->name); + RWDEBUG("!!! (pap only)"); + break; + + default: + if (!conn->config->admin_identity) { + RWDEBUG("!!! Found map between LDAP attribute and a FreeRADIUS password attribute"); + RWDEBUG("!!! but no password attribute found in search result"); + RWDEBUG("!!! Either:"); + RWDEBUG("!!! - Ensure the user object contains a password attribute, and that "); + RWDEBUG("!!! \"%s\" has permission to read that password attribute (recommended)", + conn->config->admin_identity); + RWDEBUG("!!! - Bind as the user by listing %s in the authenticate section, and", + inst->name); + RWDEBUG("!!! setting attribute &control:Auth-Type := '%s' in the authorize section", + inst->name); + RWDEBUG("!!! (pap only)"); + } else { + RWDEBUG("!!! No \"known good\" password added"); + RWDEBUG("!!! but no password attribute found in search result"); + RWDEBUG("!!! Either:"); + RWDEBUG("!!! - Ensure the user object contains a password attribute, and that "); + RWDEBUG("!!! 'identity' is set to the DN of an account that has permission to read"); + RWDEBUG("!!! that password attribute"); + RWDEBUG("!!! - Bind as the user by listing %s in the authenticate section, and", + inst->name); + RWDEBUG("!!! setting attribute &control:Auth-Type := '%s' in the authorize section", + inst->name); + RWDEBUG("!!! (pap only)"); + } + break; + } + } +} diff --git a/src/modules/rlm_ldap/util.c b/src/modules/rlm_ldap/util.c new file mode 100644 index 00000000000..ee88b584a4f --- /dev/null +++ b/src/modules/rlm_ldap/util.c @@ -0,0 +1,531 @@ +/* + * This program is is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * @file util.c + * @brief Utility functions to escape and parse DNs + * + * @author Arran Cudbard-Bell + * @copyright 2017 Arran Cudbard-Bell + * @copyright 2017 The FreeRADIUS Server Project. + */ +#include "rlm_ldap.h" + +#include +#include + +static const char specials[] = ",+\"\\<>;*=()"; +static const char hextab[] = "0123456789abcdef"; + +/** Converts "bad" strings into ones which are safe for LDAP + * + * @note RFC 4515 says filter strings can only use the @verbatim \ @endverbatim + * format, whereas RFC 4514 indicates that some chars in DNs, may be escaped simply + * with a backslash. For simplicity, we always use the hex escape sequences. + * In other areas where we're doing DN comparison, the DNs need to be normalised first + * so that they both use only hex escape sequences. + * + * @note This is a callback for xlat operations. + * + * Will escape any characters in input strings that would cause the string to be interpreted + * as part of a DN and or filter. Escape sequence is @verbatim \ @endverbatim. + * + * @param request The current request. + * @param out Pointer to output buffer. + * @param outlen Size of the output buffer. + * @param in Raw unescaped string. + * @param arg Any additional arguments (unused). + */ +size_t fr_ldap_escape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg) +{ + + size_t left = outlen; + + if (*in && ((*in == ' ') || (*in == '#'))) goto encode; + + while (*in) { + /* + * Encode unsafe characters. + */ + if (memchr(specials, *in, sizeof(specials) - 1)) { + encode: + /* + * Only 3 or less bytes available. + */ + if (left <= 3) break; + + *out++ = '\\'; + *out++ = hextab[(*in >> 4) & 0x0f]; + *out++ = hextab[*in & 0x0f]; + in++; + left -= 3; + + continue; + } + + if (left <= 1) break; + + /* + * Doesn't need encoding + */ + *out++ = *in++; + left--; + } + + *out = '\0'; + + return outlen - left; +} + +/** Converts escaped DNs and filter strings into normal + * + * @note RFC 4515 says filter strings can only use the @verbatim \ @endverbatim + * format, whereas RFC 4514 indicates that some chars in DNs, may be escaped simply + * with a backslash.. + * + * Will unescape any special characters in strings, or @verbatim \ @endverbatim + * sequences. + * + * @param request The current request. + * @param out Pointer to output buffer. + * @param outlen Size of the output buffer. + * @param in Escaped string string. + * @param arg Any additional arguments (unused). + */ +size_t fr_ldap_unescape_func(UNUSED REQUEST *request, char *out, size_t outlen, char const *in, UNUSED void *arg) +{ + char const *p; + char *c1, *c2, c3; + size_t freespace = outlen; + + if (outlen <= 1) return 0; + + p = in; + while (*p && (--freespace > 0)) { + if (*p != '\\') { + next: + *out++ = *p++; + continue; + } + + p++; + + /* It's an escaped special, just remove the slash */ + if (memchr(specials, *in, sizeof(specials) - 1)) { + *out++ = *p++; + continue; + } + + /* Is a hex sequence */ + if (!(c1 = memchr(hextab, tolower(p[0]), 16)) || + !(c2 = memchr(hextab, tolower(p[1]), 16))) goto next; + c3 = ((c1 - hextab) << 4) + (c2 - hextab); + + *out++ = c3; + p += 2; + } + + *out = '\0'; + + return outlen - freespace; +} + + +/** Check whether a string looks like a DN + * + * @param[in] in Str to check. + * @param[in] inlen Length of string to check. + * @return + * - true if string looks like a DN. + * - false if string does not look like DN. + */ +bool fr_ldap_util_is_dn(char const *in, size_t inlen) +{ + char const *p; + + char want = '='; + bool too_soon = true; + int comp = 1; + + for (p = in; inlen > 0; p++, inlen--) { + if (p[0] == '\\') { + char c; + + too_soon = false; + + /* + * Invalid escape sequence, not a DN + */ + if (inlen < 2) return false; + + /* + * Double backslash, consume two chars + */ + if (p[1] == '\\') { + inlen--; + p++; + continue; + } + + /* + * Special, consume two chars + */ + switch (p[1]) { + case ' ': + case '#': + case '=': + case '"': + case '+': + case ',': + case ';': + case '<': + case '>': + case '\'': + inlen -= 1; + p += 1; + continue; + + default: + break; + } + + /* + * Invalid escape sequence, not a DN + */ + if (inlen < 3) return false; + + /* + * Hex encoding, consume three chars + */ + if (fr_hex2bin((uint8_t *) &c, 1, p + 1, 2) == 1) { + inlen -= 2; + p += 2; + continue; + } + + /* + * Invalid escape sequence, not a DN + */ + return false; + } + + switch (*p) { + case '=': + if (too_soon || (*p != want)) return false; /* Too soon after last , or = */ + want = ','; + too_soon = true; + break; + + case ',': + if (too_soon || (*p != want)) return false; /* Too soon after last , or = */ + want = '='; + too_soon = true; + comp++; + break; + + default: + too_soon = false; + break; + } + } + + /* + * If the string ended with , or =, or the number + * of components was less than 2 + * + * i.e. we don't have =,= + */ + if (too_soon || (comp < 2)) return false; + + return true; +} + +/** Parse a subset (just server side sort for now) of LDAP URL extensions + * + * @param[out] sss Where to write a pointer to the server side sort control + * we created. + * @param[in] request The current request. + * @param[in] conn Handle to allocate controls under. + * @param[in] extensions A NULL terminated array of extensions. + * @return + * - 0 on success. + * - -1 on failure. + */ +int fr_ldap_parse_url_extensions(LDAPControl **sss, REQUEST *request, ldap_handle_t *conn, char **extensions) +{ + int i; + + *sss = NULL; + + if (!extensions) return 0; + + /* + * Parse extensions in the LDAP URL + */ + for (i = 0; extensions[i]; i++) { + char *p; + bool is_critical = false; + + p = extensions[i]; + if (*p == '!') { + is_critical = true; + p++; + } + +#ifdef HAVE_LDAP_CREATE_SORT_CONTROL + /* + * Server side sort control + */ + if (strncmp(p, "sss", 3) == 0) { + LDAPSortKey **keys; + int ret; + + p += 3; + p = strchr(p, '='); + if (!p) { + REDEBUG("Server side sort extension must be in the format \"[!]sss=[,key]\""); + return -1; + } + p++; + + ret = ldap_create_sort_keylist(&keys, p); + if (ret != LDAP_SUCCESS) { + REDEBUG("Invalid server side sort value \"%s\": %s", p, ldap_err2string(ret)); + return -1; + } + + if (*sss) ldap_control_free(*sss); + + ret = ldap_create_sort_control(conn->handle, keys, is_critical ? 1 : 0, sss); + ldap_free_sort_keylist(keys); + if (ret != LDAP_SUCCESS) { + ERROR("Failed creating server sort control: %s", ldap_err2string(ret)); + return -1; + } + + continue; + } +#endif + + RWDEBUG("URL extension \"%s\" ignored", p); + } + + return 0; +} + + +/** Convert a berval to a talloced string + * + * The ldap_get_values function is deprecated, and ldap_get_values_len + * does not guarantee the berval buffers it returns are \0 terminated. + * + * For some cases this is fine, for others we require a \0 terminated + * buffer (feeding DNs back into libldap for example). + * + * @param ctx to allocate in. + * @param in Berval to copy. + * @return \0 terminated buffer containing in->bv_val. + */ +char *fr_ldap_berval_to_string(TALLOC_CTX *ctx, struct berval const *in) +{ + char *out; + + out = talloc_array(ctx, char, in->bv_len + 1); + if (!out) return NULL; + + memcpy(out, in->bv_val, in->bv_len); + out[in->bv_len] = '\0'; + + return out; +} + +/** Normalise escape sequences in a DN + * + * Characters in a DN can either be escaped as + * @verbatim \ @endverbatim or @verbatim \ @endverbatim + * + * The LDAP directory chooses how characters are escaped, which can make + * local comparisons of DNs difficult. + * + * Here we search for hex sequences that match special chars, and convert + * them to the @verbatim \ @endverbatim form. + * + * @note the resulting output string will only ever be shorter than the + * input, so it's fine to use the same buffer for both out and in. + * + * @param out Where to write the normalised DN. + * @param in The input DN. + * @return The number of bytes written to out. + */ +size_t fr_ldap_util_normalise_dn(char *out, char const *in) +{ + char const *p; + char *o = out; + + for (p = in; *p != '\0'; p++) { + if (p[0] == '\\') { + char c; + + /* + * Double backslashes get processed specially + */ + if (p[1] == '\\') { + p += 1; + *o++ = p[0]; + *o++ = p[1]; + continue; + } + + /* + * Hex encodings that have an alternative + * special encoding, get rewritten to the + * special encoding. + */ + if (fr_hex2bin((uint8_t *) &c, 1, p + 1, 2) == 1) { + switch (c) { + case ' ': + case '#': + case '=': + case '"': + case '+': + case ',': + case ';': + case '<': + case '>': + case '\'': + *o++ = '\\'; + *o++ = c; + p += 2; + continue; + + default: + break; + } + } + } + *o++ = *p; + } + *o = '\0'; + + return o - out; +} + +/** Find the place at which the two DN strings diverge + * + * Returns the length of the non matching string in full. + * + * @param full DN. + * @param part Partial DN as returned by ldap_parse_result. + * @return + * - Length of the portion of full which wasn't matched + * - -1 on failure. + */ +size_t fr_ldap_common_dn(char const *full, char const *part) +{ + size_t f_len, p_len, i; + + if (!full) return -1; + + f_len = strlen(full); + + if (!part) return -1; + + p_len = strlen(part); + if (!p_len) return f_len; + + if ((f_len < p_len) || !f_len) return -1; + + for (i = 0; i < p_len; i++) if (part[p_len - i] != full[f_len - i]) return -1; + + return f_len - p_len; +} + +/** Combine and expand filters + * + * @param request Current request. + * @param out Where to write the expanded string. + * @param outlen Length of output buffer. + * @param sub Array of subfilters (may contain NULLs). + * @param sublen Number of potential subfilters in array. + * @return length of expanded data. + */ +ssize_t fr_ldap_xlat_filter(REQUEST *request, char const **sub, size_t sublen, char *out, size_t outlen) +{ + char buffer[LDAP_MAX_FILTER_STR_LEN + 1]; + char const *in = NULL; + char *p = buffer; + + ssize_t len = 0; + + unsigned int i; + int cnt = 0; + + /* + * Figure out how many filter elements we need to integrate + */ + for (i = 0; i < sublen; i++) { + if (sub[i] && *sub[i]) { + in = sub[i]; + cnt++; + } + } + + if (!cnt) { + out[0] = '\0'; + return 0; + } + + if (cnt > 1) { + if (outlen < 3) { + goto oob; + } + + p[len++] = '('; + p[len++] = '&'; + + for (i = 0; i < sublen; i++) { + if (sub[i] && (*sub[i] != '\0')) { + len += strlcpy(p + len, sub[i], outlen - len); + + if ((size_t) len >= outlen) { + oob: + REDEBUG("Out of buffer space creating filter"); + + return -1; + } + } + } + + if ((outlen - len) < 2) { + goto oob; + } + + p[len++] = ')'; + p[len] = '\0'; + + in = buffer; + } + + len = xlat_eval(out, outlen, request, in, fr_ldap_escape_func, NULL); + if (len < 0) { + REDEBUG("Failed creating filter"); + + return -1; + } + + return len; +}