all.mk
+libfreeradius-ldap.mk
-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
*/
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;
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) {
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,
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;
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) {
* 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;
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;
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);
}
docdir
oldincludedir
includedir
-runstatedir
localstatedir
sharedstatedir
sysconfdir
sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var'
-runstatedir='${localstatedir}/run'
includedir='${prefix}/include'
oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE}'
| -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=* \
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.
--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]
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'
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;}
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
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
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 2>/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
+' <conf$$subs.awk | sed '
+/^[^""]/{
+ N
+ s/\n//
+}
+' >>$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 = "\a"
+
+}
+{
+ 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
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ for (key in D) D_is_set[key] = 1
+ FS = "\a"
+}
+/^[\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
+
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=
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)
--- /dev/null
+/*
+ * 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 <a.cudbardb@freeradius.org>
+ *
+ * @copyright 2017 The FreeRADIUS Server Project.
+ */
+RCSID("$Id$")
+
+#define LOG_PREFIX "%s - "
+#define LOG_PREFIX_ARGS handle_config->name
+
+#include <freeradius-devel/rad_assert.h>
+
+#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;
+}
/**
* $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 <a.cudbardb@freeradius.org>
* @copyright 2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
*/
-#define LOG_PREFIX "rlm_ldap (%s) - "
+#define LOG_PREFIX "fr_ldap (%s) - "
#define LOG_PREFIX_ARGS inst->name
#include "rlm_ldap.h"
* 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;
* - 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;
* - 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;
*
* @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;
* @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
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--;
* @copyright 2016 The FreeRADIUS Server Project.
* @copyright 2016 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
*/
-#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 },
*
* @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",
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;
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);
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);
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++;
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;
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++;
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;
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:
* @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.
*/
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) {
/*
* 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]);
}
}
} 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) {
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;
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);
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();
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();
*/
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");
}
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:
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:
* 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");
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();
#include <freeradius-devel/rad_assert.h>
-#include <stdarg.h>
-#include <ctype.h>
-
-#define LOG_PREFIX "rlm_ldap (%s) - "
-#define LOG_PREFIX_ARGS inst->name
+#define LOG_PREFIX "%s - "
+#define LOG_PREFIX_ARGS handle_config->name
#include <freeradius-devel/radiusd.h>
-#include <freeradius-devel/modules.h>
-#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 },
* 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) {
free(client);
}
-/** Converts "bad" strings into ones which are safe for LDAP
- *
- * @note RFC 4515 says filter strings can only use the @verbatim \<hex><hex> @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 \<hex><hex> @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 \<hex><hex> @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 \<hex><hex> @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 <attr>=<val>,<attr>=<val>
- */
- 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 \<hex><hex> @endverbatim or @verbatim \<special> @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 \<special> @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);
* 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.
* (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;
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;
}
/*
* 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 : "");
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:
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:
*
* 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
* @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__);
}
/*
*/
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 */
*
* 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__);
}
/*
* 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);
* 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);
*
* 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;
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;
}
(*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
* @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;
* 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;
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;
}
} 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);
}
#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
*/
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);
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.
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;\
}
/*
* 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 {
#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
}
# 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;
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
}
--- /dev/null
+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@
--- /dev/null
+/**
+ * $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 <a.cudbardb@freeradius.org>
+ *
+ * @copyright 2017 The FreeRADIUS Server Project.
+ * @copyright 2017 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+#ifndef LIBFREERADIUS_LDAP_H
+#define LIBFREERADIUS_LDAP_H
+
+#include <freeradius-devel/radiusd.h>
+#include <lber.h>
+#include <ldap.h>
+#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
#include <freeradius-devel/rad_assert.h>
-#include <stdarg.h>
-#include <ctype.h>
-
#include "rlm_ldap.h"
#include <freeradius-devel/map_proc.h>
};
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
};
/*
* 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
};
/*
* 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
};
/*
* 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" */
{ 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>[,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.
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]);
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;
}
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]);
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) {
/*
* 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)) {
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;
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;
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;
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;
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;
* 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;
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) {
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;
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)) {
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);
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 */
}
/*
* 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;
}
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;
/*
* 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;
/*
*/
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;
/*
* 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
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
* 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
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
}
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;
}
}
* 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));
}
#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
/*
* 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;
/*
}
}
+ fr_ldap_global_config(inst->ldap_debug, inst->tls_random_file);
+
return 0;
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 */
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
+#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 <lber.h>
-#include <ldap.h>
-#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.
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.
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
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.
#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
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.
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.
*/
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.
*/
*/
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
*/
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
--- /dev/null
+# 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
*
*/
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.
{
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;
/** 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.
* @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,
if (error) *error = NULL;
if (extra) *extra = NULL;
- sasl_ctx.inst = inst;
sasl_ctx.request = request;
sasl_ctx.identity = identity;
sasl_ctx.password = password;
* 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 */
}
* 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:
--- /dev/null
+/*
+ * 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 <a.cudbardb@freeradius.org>
+ *
+ * @copyright 2013 Network RADIUS SARL <info@networkradius.com>
+ * @copyright 2013-2015 The FreeRADIUS Server Project.
+ */
+#include <freeradius-devel/rad_assert.h>
+#include <ctype.h>
+
+#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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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 <a.cudbardb@freeradius.org>
+ * @copyright 2017 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2017 The FreeRADIUS Server Project.
+ */
+#include "rlm_ldap.h"
+
+#include <stdarg.h>
+#include <ctype.h>
+
+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 \<hex><hex> @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 \<hex><hex> @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 \<hex><hex> @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 \<hex><hex> @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 <attr>=<val>,<attr>=<val>
+ */
+ 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>[,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 \<hex><hex> @endverbatim or @verbatim \<special> @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 \<special> @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;
+}