==============================================================================
+helpers/external_acl/kerberos_ldap_group/support_ldap.cc
+
+/* get_attributes is partly from OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2009 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
AC_DEFUN([SQUID_CHECK_WORKING_GSSAPI], [
AC_CACHE_CHECK([for working gssapi], squid_cv_working_gssapi, [
AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#ifdef HAVE_HEIMDAL_KERBEROS
#ifdef HAVE_GSSAPI_GSSAPI_H
#include <gssapi/gssapi.h>
-#elif HAVE_GSSAPI_H
+#elif defined(HAVE_GSSAPI_H)
#include <gssapi.h>
#endif
-
-#ifdef HAVE_GSSAPI_GSSAPI_EXT_H
-#include <gssapi/gssapi_ext.h>
+#else
+#ifdef HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#elif defined(HAVE_GSSAPI_H)
+#include <gssapi.h>
#endif
-
#ifdef HAVE_GSSAPI_GSSAPI_KRB5_H
#include <gssapi/gssapi_krb5.h>
#endif
-
#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
#include <gssapi/gssapi_generic.h>
#endif
+#endif
int
main(void)
{
AC_CHECK_LIB(krb5,krb5_get_error_message,
AC_DEFINE(HAVE_KRB5_GET_ERROR_MESSAGE,1,
[Define to 1 if you have krb5_get_error_message]),)
+ AC_CHECK_DECLS(krb5_kt_free_entry,,,[#include <krb5.h>])
AC_CHECK_LIB(krb5,krb5_kt_free_entry,
AC_DEFINE(HAVE_KRB5_KT_FREE_ENTRY,1,
[Define to 1 if you have krb5_kt_free_entry]),)
dir="$srcdir/helpers/external_acl/$helper"
if test -f "$dir/config.test" && sh "$dir/config.test" "$@"; then
EXTERNAL_ACL_HELPERS="$EXTERNAL_ACL_HELPERS $helper"
+ # special case
+ if test "x$helper" = "xkerberos_ldap_group" ; then
+ squid_require_sasl=yes
+ fi
else
AC_MSG_NOTICE([external acl helper $helper ... found but cannot be built])
fi
AC_MSG_ERROR(Neither SASL nor SASL2 found)
])
])
+ case "$host_os" in
+ Darwin)
+ if test "$ac_cv_lib_sasl2_sasl_errstring" = "yes" ; then
+ AC_DEFINE(HAVE_SASL_DARWIN,1,[Define to 1 if Mac Darwin without sasl.h])
+ echo "checking for MAC Darwin without sasl.h ... yes"
+ else
+ echo "checking for MAC Darwin without sasl.h ... no"
+ fi
+ ;;
+ esac
AC_SUBST(LIBSASL)
fi
dnl On MinGW OpenLDAP is not available, so LDAP helpers can be linked
dnl only with Windows LDAP libraries using -lwldap32
case "$host_os" in
-mingw|mingw32)
- LDAPLIB="-lwldap32"
- LBERLIB=""
- ;;
-*)
- LDAPLIB="-lldap"
- dnl LDAP helpers need to know if -llber is needed or not
- AC_CHECK_LIB(lber, main, [LBERLIB="-llber"])
- ;;
+ mingw|mingw32)
+ LDAPLIB="-lwldap32"
+ LBERLIB=""
+ ;;
+ *)
+ AC_CHECK_LIB(ldap, ldap_init, [LDAPLIB="-lldap"])
+ dnl LDAP helpers need to know if -llber is needed or not
+ AC_CHECK_LIB(lber, ber_init, [LBERLIB="-llber"])
+ dnl if no ldap lib found check for mozilla version
+ if test "x$ac_cv_lib_ldap_ldap_init" != x""yes; then
+ oLIBS=$LIBS
+ LIBS="$LIBPTHREADS"
+ AC_CHECK_LIB(ldap60, ldap_init, [LDAPLIB="-lldap60"])
+ LIBS="$LDAPLIB $LIBPTHREADS"
+ AC_CHECK_LIB(prldap60, prldap_init, [LDAPLIB="-lprldap60 $LDAPLIB"])
+ LIBS="$LDAPLIB $LIBPTHREADS"
+ AC_CHECK_LIB(ssldap60, ldapssl_init, [LDAPLIB="-lssldap60 $LDAPLIB"])
+ LIBS=$oLIBS
+ fi
+
+ AC_CHECK_HEADERS(ldap.h lber.h)
+ AC_CHECK_HEADERS(mozldap/ldap.h)
+
+ dnl
+ dnl Check for LDAP_OPT_DEBUG_LEVEL
+ dnl
+ AC_MSG_CHECKING([for LDAP_OPT_DEBUG_LEVEL])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+# include <ldap.h>
+ ]],[[
+ int i=LDAP_OPT_DEBUG_LEVEL
+ ]])],
+ [ AC_MSG_RESULT(yes) ],
+ [ AC_MSG_RESULT(no) ])
+
+ dnl
+ dnl Check for working ldap
+ dnl
+ oLIBS=$LIBS
+ LIBS="$LDAPLIB $LBERLIB $LIBPTHREADS"
+ AC_MSG_CHECKING([for working ldap])
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
+# define LDAP_DEPRECATED 1
+# if HAVE_LDAP_H
+# include <ldap.h>
+# elif HAVE_MOZLDAP_LDAP_H
+# include <mozldap/ldap.h>
+# endif
+ int
+ main(void)
+ {
+ char host[]="";
+ int port;
+
+ ldap_init((const char *)&host, port);
+
+ return 0;
+ }
+ ]])],
+ [ AC_DEFINE(HAVE_LDAP, 1, [LDAP support])
+ AC_MSG_RESULT(yes) ],
+ [ AC_MSG_RESULT(no) ])
+ LIBS=$oLIBS
+
+ dnl
+ dnl Check for ldap vendor
+ dnl
+ AC_MSG_CHECKING([for OpenLDAP])
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
+# if HAVE_LDAP_H
+# include <ldap.h>
+# endif
+# include <string.h>
+ int
+ main(void)
+ {
+ return strcmp(LDAP_VENDOR_NAME,"OpenLDAP");
+ }
+ ]])],
+ [ AC_DEFINE(HAVE_OPENLDAP, 1, [OpenLDAP support])
+ AC_MSG_RESULT(yes) ],
+ [ AC_MSG_RESULT(no) ])
+
+ AC_MSG_CHECKING([for Sun LDAP SDK])
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
+# if HAVE_LDAP_H
+# include <ldap.h>
+# endif
+# include <string.h>
+ int
+ main(void)
+ {
+ return strcmp(LDAP_VENDOR_NAME,"Sun Microsystems Inc.");
+ }
+ ]])],
+ [ AC_DEFINE(HAVE_SUN_LDAP_SDK, 1, [Sun LDAP SDK support])
+ AC_MSG_RESULT(yes) ],
+ [ AC_MSG_RESULT(no) ])
+
+ AC_MSG_CHECKING([for Mozilla LDAP SDK])
+ AC_RUN_IFELSE([AC_LANG_SOURCE([[
+# if HAVE_LDAP_H
+# include <ldap.h>
+# elif HAVE_MOZLDAP_LDAP_H
+# include <mozldap/ldap.h>
+# endif
+# include <string.h>
+ int
+ main(void)
+ {
+ return strcmp(LDAP_VENDOR_NAME,"mozilla.org");
+ }
+ ]])],
+ [ AC_DEFINE(HAVE_MOZILLA_LDAP_SDK, 1, [Mozilla LDAP SDK support])
+ AC_MSG_RESULT(yes) ],
+ [ AC_MSG_RESULT(no)])
+
+ dnl
+ dnl Check for LDAP_REBINDPROC_CALLBACK
+ dnl
+ AC_MSG_CHECKING([for LDAP_REBINDPROC_CALLBACK])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+# if HAVE_LDAP_H
+# include <ldap.h>
+# elif HAVE_MOZLDAP_LDAP_H
+# include <mozldap/ldap.h>
+# endif
+ ]],[[
+ LDAP_REBINDPROC_CALLBACK ldap_rebind;
+ ]])],
+ [ AC_DEFINE(HAVE_LDAP_REBINDPROC_CALLBACK,1,[Define to 1 if you have LDAP_REBINDPROC_CALLBACK])
+ AC_MSG_RESULT(yes) ],
+ [ AC_MSG_RESULT(no) ])
+
+ dnl
+ dnl Check for LDAP_REBIND_PROC
+ dnl
+ AC_MSG_CHECKING([for LDAP_REBIND_PROC])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+# if HAVE_LDAP_H
+# include <ldap.h>
+# elif HAVE_MOZLDAP_LDAP_H
+# include <mozldap/ldap.h>
+# endif
+ ]],[[
+ LDAP_REBIND_PROC ldap_rebind;
+ ]])],
+ [ AC_DEFINE(HAVE_LDAP_REBIND_PROC,1,[Define to 1 if you have LDAP_REBIND_PROC])
+ AC_MSG_RESULT(yes) ],
+ [ AC_MSG_RESULT(no) ])
+
+ dnl
+ dnl Check for LDAP_REBIND_FUNCTION
+ dnl
+ AC_MSG_CHECKING([for LDAP_REBIND_FUNCTION])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+# define LDAP_REFERRALS
+# if HAVE_LDAP_H
+# include <ldap.h>
+# elif HAVE_MOZLDAP_LDAP_H
+# include <mozldap/ldap.h>
+# endif
+ ]],[[
+ LDAP_REBIND_FUNCTION ldap_rebind;
+ ]])],
+ [ AC_DEFINE(HAVE_LDAP_REBIND_FUNCTION,1,[Define to 1 if you have LDAP_REBIND_FUNCTION])
+ AC_MSG_RESULT(yes) ],
+ [ AC_MSG_RESULT(no) ])
+
+ dnl
+ dnl Check for LDAP_SCOPE_DEFAULT
+ dnl
+ AC_MSG_CHECKING([for LDAP_SCOPE_DEFAULT])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+# if HAVE_LDAP_H
+# include <ldap.h>
+# elif HAVE_MOZLDAP_LDAP_H
+# include <mozldap/ldap.h>
+# endif
+ ]],[[
+ int i=LDAP_SCOPE_DEFAULT;
+ ]])],
+ [ AC_DEFINE(HAVE_LDAP_SCOPE_DEFAULT,1,[Define to 1 if you have LDAP_SCOPE_DEFAULT])
+ AC_MSG_RESULT(yes) ],
+ [ AC_MSG_RESULT(no) ])
+
+ dnl
+ dnl Check for ldap_url_desc.lud_scheme
+ dnl
+ AC_CHECK_MEMBER(struct ldap_url_desc.lud_scheme,
+ AC_DEFINE(HAVE_LDAP_URL_LUD_SCHEME,1,
+ [Define to 1 if you have LDAPURLDesc.lud_scheme]),,[#include <ldap.h>])
+
+ dnl
+ dnl Check for ldapssl_client_init
+ dnl
+ AC_CHECK_LIB(ldap,ldapssl_client_init,
+ AC_DEFINE(HAVE_LDAPSSL_CLIENT_INIT,1,[Define to 1 if you have ldapssl_client_init]),)
+
+ dnl
+ dnl Check for ldap_url_desc2str
+ dnl
+ AC_CHECK_LIB(ldap,ldap_url_desc2str,
+ AC_DEFINE(HAVE_LDAP_URL_DESC2STR,1,[Define to 1 if you have ldap_url_desc2str]),)
+
+ dnl
+ dnl Check for ldap_url_parse
+ dnl
+ AC_CHECK_LIB(ldap,ldap_url_parse,
+ AC_DEFINE(HAVE_LDAP_URL_PARSE,1,[Define to 1 if you have ldap_url_parse]),)
+ ;;
esac
+
AC_SUBST(LDAPLIB)
AC_SUBST(LBERLIB)
helpers/external_acl/AD_group/Makefile \
helpers/external_acl/eDirectory_userip/Makefile \
helpers/external_acl/file_userip/Makefile \
+ helpers/external_acl/kerberos_ldap_group/Makefile \
helpers/external_acl/LDAP_group/Makefile \
helpers/external_acl/LM_group/Makefile \
helpers/external_acl/session/Makefile \
--- /dev/null
+include $(top_srcdir)/src/Common.am
+
+EXTRA_DIST = README COPYING config.test
+SUBDIRS =
+
+AM_CPPFLAGS = $(INCLUDES) -I$(srcdir)
+
+libexec_PROGRAMS = ext_kerberos_ldap_group_acl
+
+ext_kerberos_ldap_group_acl_SOURCES = \
+ kerberos_ldap_group.cc \
+ support_group.cc \
+ support_netbios.cc \
+ support_member.cc \
+ support_krb5.cc \
+ support_ldap.cc \
+ support_sasl.cc \
+ support_resolv.cc \
+ support_log.cc
+
+ext_kerberos_ldap_group_acl_LDFLAGS =
+ext_kerberos_ldap_group_acl_LDADD = \
+ $(COMPAT_LIB) \
+ $(LDAPLIB) \
+ $(LBERLIB) \
+ $(LIBSASL) \
+ $(KRB5LIBS) \
+ $(XTRA_LIBS)
--- /dev/null
+--------------------------------------------------------------------------------
+readme.txt is the squid_kerb_ldap read-me file.
+
+Author: Markus Moeller (markus_moeller at compuserve.com)
+
+Copyright (C) 2007 Markus Moeller. All rights reserved.
+--------------------------------------------------------------------------------
+
+squid_kerb_ldap Read Me
+
+Markus Moeller
+June 2, 2007
+
+1 Introduction
+
+squid_kerb_ldap is a reference implementation that supports SASL/GSSAPI authentication
+to an ldap server. It is mainly intended to connect to Active Directory or Openldap based
+ldap servers.
+
+For AD it checks the memberof attribute to determine if a user is member of a group.
+For non AD it checks the memberuid attribute to determine if a user is member of a group and if not it checks if the primary group matches.
+Currently, squid_kerb_ldap supports Squid 2.6 on Linux.
+
+squid_auth_ldap requires either MIT or Heimdal Kerberos libraries and header files.
+
+2 Building and Installation
+
+./configure
+make
+
+Copy the helper squid_kerb_ldap to an apropriate directory.
+
+3 Configuration
+
+a) Configure IE or Firefox to point to the squid proxy by using the fqdn. IE and Firefox will use the
+fqdn to query for a HTTP/fqdn Kerberos service principal.
+
+b) Create a keytab which contains the HTTP/fqdn Kerberos service principal and place it into a directory
+where the squid run user can read the keytab.
+
+c) Add the following line to squid.conf
+
+auth_param negotiate program /usr/sbin/squid_kerb_auth
+auth_param negotiate children 10
+auth_param negotiate keep_alive on
+
+external_acl_type squid_kerb_ldap ttl=3600 negative_ttl=3600 %LOGIN /usr/sbin/squid_kerb_ldap -g GROUP@
+acl ldap_group_check external squid_kerb_ldap
+http_access allow ldap_group_check
+
+If a ntlm helper is used too add a -N option to map the netbios name to a Kerberos realm e.g.
+
+external_acl_type squid_kerb_ldap ttl=3600 negative_ttl=3600 %LOGIN /usr/sbin/squid_kerb_ldap -g GROUP@ -N NETBIOS@KERBEROS.REALM
+acl ldap_group_check external squid_kerb_ldap
+http_access allow ldap_group_check
+
+
+d) Modify squid startup file
+
+Add the following lines to the squid startup script to point squid to a keytab file which
+contains the HTTP/fqdn service principal for the default Kerberos domain. The fqdn must be
+the proxy name set in IE or firefox. You can not use an IP address.
+
+KRB5_KTNAME=/etc/squid/HTTP.keytab
+export KRB5_KTNAME
+
+If you use a different Kerberos domain than the machine itself is in you can point squid to
+the seperate Kerberos config file by setting the following environmnet variable in the startup
+script.
+
+KRB5_CONFIG=/etc/krb5-squid.conf
+export KRB5_CONFIG
+
+4 Miscellaneous
+
+The -i options creates informational messages whereas -d creates full debug output
+
+If squid_kerb_ldap doesn't determine for some reason the right service ldap server you can provide
+it with -u BIND_DN -p BIND_PW -b BIND_PATH -l LDAP_URL
+
+STARTTLS/SSL is supported and the -a options disables server certificate checks
+
+If you serve multiple Kerberos realms add a HTTP/fqdn@REALM service principal per realm to the
+HTTP.keytab file and use the -s GSS_C_NO_NAME option with squid_kerb_auth.
+
+squid_kerb_ldap will determine automagically the right ldap server. The following method is used:
+
+1) For user@REALM
+ 1) Query DNS for SRV record _ldap._tcp.REALM
+ 2) Query DNS for A record REALM
+ 3) Use LDAP_URL if given
+
+2) For user
+ 2) Use LDAP_URL if given
+
+The Groups to check against are determined as follows:
+
+1 For user@REALM
+ 1) Use values given by -g option which contain a @REALM e.g. -g GROUP1@REALM:GROUP2@REALM
+ 2) Use values given by -g option which contain a @ only e.g. -g GROUP1@:GROUP2@
+ 3) Use values given by -g option which do not contain a realm e.g. -g GROUP1:GROUP2
+
+1 For user
+ 3) Use values given by -g option which do not contain a realm e.g. -g GROUP1:GROUP2
+
+
+To support Non-ASCII character use -t <GROUP> or -t <GROUP>@<REALM> instead of -g where GROUP is the hex UTF-8 representation e.g.
+
+ -t 6d61726b7573 instead of -g markus
+
+The REALM must still be based on the ASCII character set. If REALM contains also non ASCII characters use -T <GROUP>@<REALM> where GROUP and REALM are hex UTF-8 representation e.g.
+
+ -T 6d61726b7573@57494e3230303352322e484f4d45 instead of -g markus@WIN2003R2.HOME
+
+For a translation of hex UTF-8 see for example http://www.utf8-chartable.de/unicode-utf8-table.pl
+
+
+
+
--- /dev/null
+#!/bin/ksh
+#
+# -----------------------------------------------------------------------------
+#
+# Author: Markus Moeller (markus_moeller at compuserve.com)
+#
+# Copyright (C) 2007 Markus Moeller. All rights reserved.
+#
+# This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+#
+# -----------------------------------------------------------------------------
+#
+#
+# creates the following files:
+# <server>.cert
+# secmod.db
+# key3.db
+# cert8.db
+#
+#
+if [ -z "$1" ]; then
+ echo "Usage: `basename $0` ldap-server port"
+ exit 0
+fi
+if [ -z "$2" ]; then
+ port=636
+else
+ port=$2
+fi
+
+server=$1
+
+#
+# Remove old files
+#
+rm ${server}_[0-9]*.cert 2>/dev/null
+#
+# Get certs and store in .cert file
+#
+( openssl s_client -showcerts -connect $server:$port 2>/dev/null <<!
+QUIT
+!
+) | awk 'BEGIN{start=0;ostart=0}{if ( $0 ~ /BEGIN CERTIFICATE/ ) { start=start+1 };
+ if ( start > ostart ) {print $0 >>"'$server'_"start".cert"};
+ if ( $0 ~ /END CERTIFICATE/) { ostart=start } }'
+
+#
+# from mozilla-nss-tools
+# /usr/sfw/bin on Solaris
+#
+#
+# Create database for Sun ldap and pem file for Openldap
+#
+rm ${server}_[0-9]*.pem 2>/dev/null
+let i=0
+ ls ${server}_[0-9]*.cert | while read file; do
+ let i=i+1
+ cat $file >> ${server}_$i.pem
+ CA=`openssl x509 -noout -text -in ${server}_$i.pem | grep -i "CA:.*true"`
+ if [ -n "$CA" ]; then
+ echo "CA is in ${server}_$i.pem"
+ certutil -A -a -n "${server}_$i" -i $file -t "C,," -d .
+ else
+ certutil -A -a -n "${server}_$i" -i $file -t "P,," -d .
+ fi
+ rm $file
+done
+echo "Certs:"
+certutil -d . -L
+echo "are in"
+ls *.db
--- /dev/null
+#!/bin/sh
+# Don't build without gssapi.h
+if test -f /usr/include/ldap.h || test -f /usr/local/include/ldap.h ; then
+ exit 0
+fi
+exit 1
--- /dev/null
+.if !'po4a'hide' .TH ext_kerberos_ldap_group_acl 8
+.
+.SH NAME
+.if !'po4a'hide' .B ext_kerberos_ldap_group_acl
+.if !'po4a'hide' \-
+Squid LDAP external acl group helper for Kerberos or NTLM credentials.
+.PP
+Version 1.2.2sq
+.
+.SH SYNOPSIS
+.if !'po4a'hide' .B ext_kerberos_ldap_group_acl
+.if !'po4a'hide' .B [\-h] [\-d] [\-i] [\-s] [\-a] [\-D Realm ] [\-N Netbios-Realm-List] [\-m Max-Depth] [\-u Ldap-User] [\-p Ldap-Password] [\-b Ldap-Bind-Path] [\-l Ldap-URL] \-g Group-Realm-List \-t Hex-Group-Realm-List \-T Hex-Group-Hex-Realm-List
+.
+.SH DESCRIPTION
+.B ext_kerberos_ldap_group_acl
+is an installed binary and allows Squid to connect to a LDAP directory to
+authorize users via LDAP groups. Options are specified as parameters on the
+command line, while the username (e.g. user, user@REALM, NDOMAIN\\user) to
+be checked against the LDAP directory are specified on subsequent lines of
+input to the helper, one username per line.
+.PP
+.B ext_kerberos_ldap_group_acl
+will determine the ldap server name from DNS SRV and/or
+A records or a local hosts file (e.g. for the Kerberos Realm SUSE.HOME it
+will look for an SRV record _ldap._tcp.SUSE.HOME and an A record SUSE.HOME
+or a SUSE.HOME hosts entry). If no domain information is available from the
+username the ldap server will be determined through the command line options.
+.PP
+.B ext_kerberos_ldap_group_acl
+requires as a minimum the \-g, \-t or \-T option which
+provides the ldap group name the user has to belong too. For Active Directory
+a recursive group lookup is implemented until a max depth specified by \-m depth.
+For other Ldap servers a RFC2307bis schema of groups is assumed.
+.PP
+Different group names can be specified for different domains using a
+group@domain syntax.
+As expected by the
+.B external_acl_type
+construct of Squid, after
+specifying a username and group followed by a new line, this
+helper will produce either
+.B OK
+or
+.B ERR
+on the following line
+to show if the user is a member of the specified group.
+.
+.SH OPTIONS
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-h
+Display the binary help and command line syntax info using stderr.
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-d
+Write debug messages to stderr.
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-i
+Write informational messages to stderr.
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-s
+Use SSL for the ldap connection.
+.IP
+The CA certificate file can be set via the environment variable TLS_CACERTFILE (default /etc/ssl/certs/cert.pem) (OpenLDAP).
+.IP
+The SSL certificate database can be set via the environment variable SSL_CERTDBPATH (default /etc/certs) (Sun and Mozilla LDAP SDK).
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-a
+Allow SSL without certificate verification.
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-D Realm
+Default Kerberos domain to use for usernames which do not contain domain
+information (e.g. for users using basic authentication).
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-N Netbios-Realm-List
+A list of Netbios name mappings to Kerberos domain names of the form
+Netbios-Name@Kerberos-Realm[:Netbios-Name@Kerberos-Realm] (e.g. for users
+using NTLM authentication).
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-m Max-Depth
+Maximal depth of recursive group search.
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-u Ldap-User
+Username for LDAP server.
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-u Ldap-Password
+Password for LDAP server.
+.IP
+As the password needs to be printed in plain text in your Squid configuration
+it is strongly recommended to use a account with minimal associated privileges.
+This to limit the damage in case someone could get hold of a copy of your Squid
+configuration file or extracts the password used from a process listing.
+.
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-b Ldap-Bind-Path
+LDAP server bind path.
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-u Ldap-URL
+LDAP server URL in form ldap[s]://server:port
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-g Group-Realm-List
+A list of group name per Kerberos domain of the form
+Group|Group@|Group@Realm[:Group@|Group@Realm]
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-t Hex-Group-Realm-List
+A list of group name per Kerberos domain of the
+form Group|Group@|Group@Realm[:Group@|Group@Realm] where group is in
+UTF-8 hex format
+.if !'po4a'hide' .TP 12
+.if !'po4a'hide' .B \-T Hex-Group-Hex-Realm-List
+A list of group name per Kerberos domain of the form
+Group|Group@|Group@Realm[:Group@|Group@Realm] where group and domain
+is in UTF-8 hex format
+.
+.SH CONFIGURATION
+.PP See FAQ wiki page for examples of how to write configuration snippets. (TBD)
+.PP
+This helper is intended to be used as an
+.B external_acl_type
+helper in
+.B squid.conf.
+.if !'po4a'hide' .P
+.if !'po4a'hide' .ft CR
+.if !'po4a'hide' .nf
+.if !'po4a'hide' external_acl_type kerberos_ldap_group1 ttl=3600 negative_ttl=3600 %LOGIN /path/to/ext_kerberos_ldap_group_acl -g GROUP1
+.if !'po4a'hide' .br
+.if !'po4a'hide' external_acl_type kerberos_ldap_group2 ttl=3600 negative_ttl=3600 %LOGIN /path/to/ext_kerberos_ldap_group_acl -g GROUP2
+.if !'po4a'hide' .br
+.if !'po4a'hide' acl group1 external kerberos_ldap_group1
+.if !'po4a'hide' .br
+.if !'po4a'hide' acl group2 external kerberos_ldap_group2
+.if !'po4a'hide' .fi
+.if !'po4a'hide' .ft
+.PP
+.B NOTE:
+The following squid startup file modification may be required:
+
+Add the following lines to the squid startup script to point squid to a keytab file which
+contains the HTTP/fqdn service principal for the default Kerberos domain. The fqdn must be
+the proxy name set in IE or firefox. You can not use an IP address.
+.if !'po4a'hide' .P
+.if !'po4a'hide' .ft CR
+.if !'po4a'hide' .nf
+.if !'po4a'hide' KRB5_KTNAME=/etc/squid/HTTP.keytab
+.if !'po4a'hide' export KRB5_KTNAME
+.if !'po4a'hide' .fi
+.if !'po4a'hide' .ft
+
+If you use a different Kerberos domain than the machine itself is in you can point squid to
+the seperate Kerberos config file by setting the following environmnet variable in the startup
+script.
+.if !'po4a'hide' .P
+.if !'po4a'hide' .ft CR
+.if !'po4a'hide' .nf
+.if !'po4a'hide' KRB5_CONFIG=/etc/krb5-squid.conf
+.if !'po4a'hide' export KRB5_CONFIG
+.if !'po4a'hide' .fi
+.if !'po4a'hide' .ft
+
+.B ext_kerberos_ldap_group_acl
+will determine automagically the right ldap server. The following method is used:
+
+1) For user@REALM
+ a) Query DNS for SRV record _ldap._tcp.REALM
+ b) Query DNS for A record REALM
+ c) Use LDAP_URL if given
+
+2) For user
+ a) Use domain -D REALM and follow step 1)
+ b) Use LDAP_URL if given
+
+The Groups to check against are determined as follows:
+
+1) For user@REALM
+ a) Use values given by -g option which contain a @REALM e.g. -g GROUP1@REALM:GROUP2@REALM
+ b) Use values given by -g option which contain a @ only e.g. -g GROUP1@:GROUP2@
+ c) Use values given by -g option which do not contain a realm e.g. -g GROUP1:GROUP2
+
+2) For user
+ a) Use values given by -g option which do not contain a realm e.g. -g GROUP1:GROUP2
+
+3) For NDOMAIN\\user
+ a) Use realm given by -N NDOMAIN@REALM and then use values given by -g option which contain a @REALM e.g. -g GROUP1@REALM:GROUP2@REALM
+
+To support Non-ASCII character use -t GROUP or -t GROUP@REALM instead of -g where GROUP is the hex UTF-8 representation e.g.
+
+ -t 6d61726b7573 instead of -g markus
+
+The REALM must still be based on the ASCII character set. If REALM contains also non ASCII characters use -T GROUP@REALM where GROUP and REALM are hex UTF-8 representation e.g.
+
+ -T 6d61726b7573@57494e3230303352322e484f4d45 instead of -g markus@WIN2003R2.HOME
+
+For a translation of hex UTF-8 see for example http://www.utf8-chartable.de/unicode-utf8-table.pl
+
+.
+.SH AUTHOR
+This program was written by
+.if !'po4a'hide' .I Markus Moeller <markus_moeller@compuserve.com>
+.PP
+This manual was written by
+.if !'po4a'hide' .I Markus Moeller <markus_moeller@compuserve.com>
+.
+.SH COPYRIGHT
+This program and documentation is copyright to the authors named above.
+.PP
+Distributed under the GNU General Public License (GNU GPL) version 2 or later (GPLv2+).
+.
+.SH QUESTIONS
+Questions on the usage of this program can be sent to the
+.I Squid Users mailing list
+.if !'po4a'hide' <squid-users@squid-cache.org>
+.
+.SH REPORTING BUGS
+Bug reports need to be made in English.
+See http://wiki.squid-cache.org/SquidFaq/BugReporting for details of what you need to include with your bug report.
+.PP
+Report bugs or bug fixes using http://bugs.squid-cache.org/
+.PP
+Report serious security bugs to
+.I Squid Bugs <squid-bugs@squid-cache.org>
+.PP
+Report ideas for new improvements to the
+.I Squid Developers mailing list
+.if !'po4a'hide' <squid-dev@squid-cache.org>
+.
+.SH SEE ALSO
+.if !'po4a'hide' .BR squid "(8) "
+.if !'po4a'hide' .BR negotiate_kerberos_auth "(8) "
+.br
+.BR RFC1035 " - Domain names - implementation and specification,"
+.br
+.BR RFC2782 " - A DNS RR for specifying the location of services (DNS SRV),"
+.br
+.BR RFC2254 " - The String Representation of LDAP Search Filters,"
+.br
+.BR RFC2307bis " - An Approach for Using LDAP as a Network Information Service
+http://www.padl.com/~lukeh/rfc2307bis.txt,"
+.br
+The Squid FAQ wiki
+.if !'po4a'hide' http://wiki.squid-cache.org/SquidFaq
+.br
+The Squid Configuration Manual
+.if !'po4a'hide' http://www.squid-cache.org/Doc/config/
--- /dev/null
+/*
+ * -----------------------------------------------------------------------------
+ *
+ * Author: Markus Moeller (markus_moeller at compuserve.com)
+ *
+ * Copyright (C) 2007 Markus Moeller. All rights reserved.
+ *
+ * This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * As a special exemption, M Moeller gives permission to link this program
+ * with MIT, Heimdal or other GSS/Kerberos libraries, and distribute
+ * the resulting executable, without including the source code for
+ * the Libraries in the source distribution.
+ *
+ * -----------------------------------------------------------------------------
+ */
+/*
+ * Hosted at http://sourceforge.net/projects/squidkerbauth
+ */
+#include "config.h"
+#include "helpers/defines.h"
+#include "util.h"
+
+#ifdef HAVE_LDAP
+
+#include "support.h"
+#ifdef HAVE_CTYPE_H
+#include <ctype.h>
+#endif
+
+void
+init_args(struct main_args *margs)
+{
+ margs->nlist = NULL;
+ margs->glist = NULL;
+ margs->ulist = NULL;
+ margs->tlist = NULL;
+ margs->luser = NULL;
+ margs->lpass = NULL;
+ margs->lbind = NULL;
+ margs->lurl = NULL;
+ margs->ssl = NULL;
+ margs->rc_allow = 0;
+ margs->AD = 0;
+ margs->mdepth = 5;
+ margs->ddomain = NULL;
+ margs->groups = NULL;
+ margs->ndoms = NULL;
+}
+
+void clean_gd(struct gdstruct *gdsp);
+void clean_nd(struct ndstruct *ndsp);
+
+void
+clean_gd(struct gdstruct *gdsp)
+{
+ struct gdstruct *p = NULL, *pp = NULL;
+
+ start:
+ p = gdsp;
+ if (!p)
+ return;
+ while (p->next) {
+ pp = p;
+ p = p->next;
+ }
+ if (p->group) {
+ xfree(p->group);
+ p->group = NULL;
+ }
+ if (p->domain) {
+ xfree(p->domain);
+ p->domain = NULL;
+ }
+ if (pp && pp->next) {
+ xfree(pp->next);
+ pp->next = NULL;
+ }
+ if (p == gdsp) {
+ xfree(gdsp);
+ gdsp = NULL;
+ }
+ goto start;
+}
+
+void
+clean_nd(struct ndstruct *ndsp)
+{
+ struct ndstruct *p = NULL, *pp = NULL;
+
+ start:
+ p = ndsp;
+ if (!p)
+ return;
+ while (p->next) {
+ pp = p;
+ p = p->next;
+ }
+ if (p->netbios) {
+ xfree(p->netbios);
+ p->netbios = NULL;
+ }
+ if (p->domain) {
+ xfree(p->domain);
+ p->domain = NULL;
+ }
+ if (pp && pp->next) {
+ xfree(pp->next);
+ pp->next = NULL;
+ }
+ if (p == ndsp) {
+ xfree(ndsp);
+ ndsp = NULL;
+ }
+ goto start;
+}
+
+void
+clean_args(struct main_args *margs)
+{
+ if (margs->glist) {
+ xfree(margs->glist);
+ margs->glist = NULL;
+ }
+ if (margs->ulist) {
+ xfree(margs->ulist);
+ margs->ulist = NULL;
+ }
+ if (margs->tlist) {
+ xfree(margs->tlist);
+ margs->tlist = NULL;
+ }
+ if (margs->nlist) {
+ xfree(margs->nlist);
+ margs->nlist = NULL;
+ }
+ if (margs->luser) {
+ xfree(margs->luser);
+ margs->luser = NULL;
+ }
+ if (margs->lpass) {
+ xfree(margs->lpass);
+ margs->lpass = NULL;
+ }
+ if (margs->lbind) {
+ xfree(margs->lbind);
+ margs->lbind = NULL;
+ }
+ if (margs->lurl) {
+ xfree(margs->lurl);
+ margs->lurl = NULL;
+ }
+ if (margs->ssl) {
+ xfree(margs->ssl);
+ margs->ssl = NULL;
+ }
+ if (margs->ddomain) {
+ xfree(margs->ddomain);
+ margs->ddomain = NULL;
+ }
+ if (margs->groups) {
+ clean_gd(margs->groups);
+ margs->groups = NULL;
+ }
+ if (margs->ndoms) {
+ clean_nd(margs->ndoms);
+ margs->ndoms = NULL;
+ }
+}
+
+void strup(char *s);
+
+int
+main(int argc, char *const argv[])
+{
+ char buf[6400];
+ char *user, *domain;
+ char *nuser, *nuser8 = NULL, *netbios;
+ char *c;
+ int opt;
+ int length;
+ struct main_args margs;
+
+ setbuf(stdout, NULL);
+ setbuf(stdin, NULL);
+
+ init_args(&margs);
+
+ while (-1 != (opt = getopt(argc, argv, "diasg:D:N:u:U:t:T:p:l:b:m:h"))) {
+ switch (opt) {
+ case 'd':
+ debug_enabled = 1;
+ break;
+ case 'i':
+ log_enabled = 1;
+ break;
+ case 'a':
+ margs.rc_allow = 1;
+ break;
+ case 's':
+ margs.ssl = (char *) "yes";
+ break;
+ case 'g':
+ margs.glist = xstrdup(optarg);
+ break;
+ case 'D':
+ margs.ddomain = xstrdup(optarg);
+ break;
+ case 'N':
+ margs.nlist = xstrdup(optarg);
+ break;
+ case 'u':
+ margs.luser = xstrdup(optarg);
+ break;
+ case 'U':
+ margs.ulist = xstrdup(optarg);
+ break;
+ case 't':
+ margs.ulist = xstrdup(optarg);
+ break;
+ case 'T':
+ margs.tlist = xstrdup(optarg);
+ break;
+ case 'p':
+ margs.lpass = xstrdup(optarg);
+ /* Hide Password */
+ memset(optarg, 'X', strlen(optarg));
+ break;
+ case 'l':
+ margs.lurl = xstrdup(optarg);
+ break;
+ case 'b':
+ margs.lbind = xstrdup(optarg);
+ break;
+ case 'm':
+ margs.mdepth = atoi(optarg);
+ break;
+ case 'h':
+ fprintf(stderr, "Usage: \n");
+ fprintf(stderr, "squid_kerb_ldap [-d] [-i] -g group list [-D domain] [-N netbios domain map] [-s] [-u ldap user] [-p ldap user password] [-l ldap url] [-b ldap bind path] [-a] [-m max depth] [-h]\n");
+ fprintf(stderr, "-d full debug\n");
+ fprintf(stderr, "-i informational messages\n");
+ fprintf(stderr, "-g group list\n");
+ fprintf(stderr, "-t group list (only group name hex UTF-8 format)\n");
+ fprintf(stderr, "-T group list (all in hex UTF-8 format - except seperator @)\n");
+ fprintf(stderr, "-D default domain\n");
+ fprintf(stderr, "-N netbios to dns domain map\n");
+ fprintf(stderr, "-u ldap user\n");
+ fprintf(stderr, "-p ldap user password\n");
+ fprintf(stderr, "-l ldap url\n");
+ fprintf(stderr, "-b ldap bind path\n");
+ fprintf(stderr, "-s use SSL encryption with Kerberos authentication\n");
+ fprintf(stderr, "-a allow SSL without cert verification\n");
+ fprintf(stderr, "-m maximal depth for recursive searches\n");
+ fprintf(stderr, "-h help\n");
+ fprintf(stderr, "The ldap url, ldap user and ldap user password details are only used if the kerberised\n");
+ fprintf(stderr, "access fails(e.g. unknown domain) or if the username does not contain a domain part\n");
+ fprintf(stderr, "and no default domain is provided.\n");
+ fprintf(stderr, "If the ldap url starts with ldaps:// it is either start_tls or simple SSL\n");
+ fprintf(stderr, "The group list can be:\n");
+ fprintf(stderr, "group - In this case group can be used for all keberised and non kerberised ldap servers\n");
+ fprintf(stderr, "group@ - In this case group can be used for all keberised ldap servers\n");
+ fprintf(stderr, "group@domain - In this case group can be used for ldap servers of domain domain\n");
+ fprintf(stderr, "group1@domain1:group2@domain2:group3@:group4 - A list is build with a colon as seperator\n");
+ fprintf(stderr, "Group membership is determined with AD servers through the users memberof attribute which\n");
+ fprintf(stderr, "is followed to the top (e.g. if the group is a member of a group)\n");
+ fprintf(stderr, "Group membership is determined with non AD servers through the users memberuid (assuming\n");
+ fprintf(stderr, "PosixGroup) or primary group membership (assuming PosixAccount)\n");
+ clean_args(&margs);
+ exit(0);
+ default:
+ warn((char *) "%s| %s: WARNING: unknown option: -%c.\n", LogTime(), PROGRAM, opt);
+ }
+ }
+
+ debug((char *) "%s| %s: INFO: Starting version %s\n", LogTime(), PROGRAM, KERBEROS_LDAP_GROUP_VERSION);
+ if (create_gd(&margs)) {
+ debug((char *) "%s| %s: FATAL: Error in group list: %s\n", LogTime(), PROGRAM, margs.glist ? margs.glist : "NULL");
+ SEND_ERR("");
+ clean_args(&margs);
+ exit(1);
+ }
+ if (create_nd(&margs)) {
+ debug((char *) "%s| %s: FATAL: Error in netbios list: %s\n", LogTime(), PROGRAM, margs.nlist ? margs.nlist : "NULL");
+ SEND_ERR("");
+ clean_args(&margs);
+ exit(1);
+ }
+ while (1) {
+ if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
+ if (ferror(stdin)) {
+ debug((char *) "%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n", LogTime(), PROGRAM, ferror(stdin),
+ strerror(ferror(stdin)));
+
+ SEND_ERR("");
+ clean_args(&margs);
+ exit(1); /* BIIG buffer */
+ }
+ SEND_ERR("");
+ clean_args(&margs);
+ exit(0);
+ }
+ c = (char *) memchr(buf, '\n', sizeof(buf) - 1);
+ if (c) {
+ *c = '\0';
+ length = c - buf;
+ } else {
+ SEND_ERR("");
+ debug((char *) "%s| %s: ERR\n", LogTime(), PROGRAM);
+ continue;
+ }
+
+ user = buf;
+ nuser = strchr(user, '\\');
+ if (!nuser)
+ nuser8 = strstr(user, "%5C");
+ if (!nuser && !nuser8)
+ nuser8 = strstr(user, "%5c");
+ domain = strrchr(user, '@');
+ if (nuser || nuser8) {
+ if (nuser) {
+ *nuser = '\0';
+ nuser++;
+ } else {
+ *nuser8 = '\0';
+ nuser = nuser8 + 3;
+ }
+ netbios = user;
+ if (debug_enabled)
+ debug((char *) "%s| %s: INFO: Got User: %s Netbios Name: %s\n", LogTime(), PROGRAM, nuser, netbios);
+ else
+ log((char *) "%s| %s: INFO: Got User: %s Netbios Name: %s\n", LogTime(), PROGRAM, nuser, netbios);
+ domain = get_netbios_name(&margs, netbios);
+ user = nuser;
+ } else if (domain) {
+ strup(domain);
+ *domain = '\0';
+ domain++;
+ }
+ if (!domain && margs.ddomain) {
+ domain = xstrdup(margs.ddomain);
+ if (debug_enabled)
+ debug((char *) "%s| %s: INFO: Got User: %s set default domain: %s\n", LogTime(), PROGRAM, user, domain);
+ else
+ log((char *) "%s| %s: INFO: Got User: %s set default domain: %s\n", LogTime(), PROGRAM, user, domain);
+ }
+ if (debug_enabled)
+ debug((char *) "%s| %s: INFO: Got User: %s Domain: %s\n", LogTime(), PROGRAM, user, domain ? domain : "NULL");
+ else
+ log((char *) "%s| %s: INFO: Got User: %s Domain: %s\n", LogTime(), PROGRAM, user, domain ? domain : "NULL");
+
+ if (!strcmp(user, "QQ") && domain && !strcmp(domain, "QQ")) {
+ clean_args(&margs);
+ exit(-1);
+ }
+ if (check_memberof(&margs, user, domain)) {
+ SEND_OK("");
+ debug((char *) "%s| %s: DEBUG: OK\n", LogTime(), PROGRAM);
+ } else {
+ SEND_ERR("");
+ debug((char *) "%s| %s: DEBUG: ERR\n", LogTime(), PROGRAM);
+ }
+ }
+
+
+}
+
+void
+strup(char *s)
+{
+ while (*s) {
+ *s = toupper((unsigned char) *s);
+ s++;
+ }
+}
+
+#else
+#include <stdio.h>
+#include <stdlib.h>
+int
+main(int argc, char *const argv[])
+{
+ setbuf(stdout, NULL);
+ setbuf(stdin, NULL);
+ char buf[6400];
+ while (1) {
+ if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
+ }
+ fprintf(stdout, "ERR\n");
+ fprintf(stderr, "LDAP group authorisation not supported\n");
+ }
+}
+#endif
--- /dev/null
+/*
+ * -----------------------------------------------------------------------------
+ *
+ * Author: Markus Moeller (markus_moeller at compuserve.com)
+ *
+ * Copyright (C) 2007 Markus Moeller. All rights reserved.
+ *
+ * This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#define KERBEROS_LDAP_GROUP_VERSION "1.2.2sq"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#ifdef HAVE_HEIMDAL_KERBEROS
+#ifdef HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#elif defined(HAVE_GSSAPI_H)
+#include <gssapi.h>
+#endif
+#ifdef HAVE_KRB5_H
+#include <krb5.h>
+#endif
+#ifdef HAVE_COM_ERR_H
+#include <com_err.h>
+#else
+#define error_message(code) krb5_get_err_text(kparam.context,code)
+#endif
+#else /*MIT */
+#ifdef HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#elif defined(HAVE_GSSAPI_H)
+#include <gssapi.h>
+#endif
+#ifdef HAVE_GSSAPI_GSSAPI_KRB5_H
+#include <gssapi/gssapi_krb5.h>
+#endif
+#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
+#include <gssapi/gssapi_generic.h>
+#endif
+#ifdef HAVE_GSSAPI_GSSAPI_EXT_H
+#include <gssapi/gssapi_ext.h>
+#endif
+#ifdef HAVE_KRB5_H
+#if HAVE_BROKEN_SOLARIS_KRB5_H
+#if defined(__cplusplus)
+#define KRB5INT_BEGIN_DECLS extern "C" {
+#define KRB5INT_END_DECLS
+KRB5INT_BEGIN_DECLS
+#endif
+#endif
+#include <krb5.h>
+#endif
+#ifdef HAVE_COM_ERR_H
+#include <com_err.h>
+#endif
+#endif
+#ifndef gss_nt_service_name
+#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
+#endif
+
+#define LDAP_DEPRECATED 1
+#ifdef HAVE_LDAP_REBIND_FUNCTION
+#define LDAP_REFERRALS
+#endif
+#ifdef HAVE_LBER_H
+#include <lber.h>
+#endif
+#ifdef HAVE_LDAP_H
+#include <ldap.h>
+#endif
+#ifdef HAVE_MOZLDAP_LDAP_H
+#include <mozldap/ldap.h>
+#endif
+
+struct gdstruct {
+ char *group;
+ char *domain;
+ struct gdstruct *next;
+};
+struct ndstruct {
+ char *netbios;
+ char *domain;
+ struct ndstruct *next;
+};
+
+struct main_args {
+ char *glist;
+ char *ulist;
+ char *tlist;
+ char *nlist;
+ char *luser;
+ char *lpass;
+ char *lbind;
+ char *lurl;
+ char *ssl;
+ int rc_allow;
+ int AD;
+ int mdepth;
+ char *ddomain;
+ struct gdstruct *groups;
+ struct ndstruct *ndoms;
+};
+
+SQUIDCEXTERN int log_enabled;
+
+/* the macro overload style is really a gcc-ism */
+#ifdef __GNUC__
+
+
+#define log(X...) \
+ if (log_enabled) { \
+ fprintf(stderr, "%s(%d): pid=%ld :", __FILE__, __LINE__, (long)getpid() ); \
+ fprintf(stderr,X); \
+ } else (void)0
+
+#define error(X...) \
+ fprintf(stderr, "%s(%d): pid=%ld :", __FILE__, __LINE__, (long)getpid() ); \
+ fprintf(stderr,X); \
+
+#define warn(X...) \
+ fprintf(stderr, "%s(%d): pid=%ld :", __FILE__, __LINE__, (long)getpid() ); \
+ fprintf(stderr,X); \
+
+#else /* __GNUC__ */
+
+/* non-GCC compilers can't do the above macro define yet. */
+void log(char *format,...);
+void error(char *format,...);
+void warn(char *format,...);
+#endif
+
+
+struct hstruct {
+ char *host;
+ int port;
+ int priority;
+ int weight;
+};
+
+struct ldap_creds {
+ char *dn;
+ char *pw;
+};
+
+
+void init_args(struct main_args *margs);
+void clean_args(struct main_args *margs);
+const char *LogTime(void);
+
+int check_memberof(struct main_args *margs, char *user, char *domain);
+int get_memberof(struct main_args *margs, char *user, char *domain, char *group);
+
+char *get_netbios_name(struct main_args *margs, char *netbios);
+
+int create_gd(struct main_args *margs);
+int create_nd(struct main_args *margs);
+
+int krb5_create_cache(struct main_args *margs, char *domain);
+void krb5_cleanup(void);
+
+int get_ldap_hostname_list(struct main_args *margs, struct hstruct **hlist, int nhosts, char *domain);
+int get_hostname_list(struct main_args *margs, struct hstruct **hlist, int nhosts, char *name);
+int free_hostname_list(struct hstruct **hlist, int nhosts);
+
+#if defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H) || defined(HAVE_SASL_DARWIN)
+int tool_sasl_bind(LDAP * ld, char *binddn, char *ssl);
+#endif
+
+#define PROGRAM "kerberos_ldap_group"
--- /dev/null
+
+/*
+ * -----------------------------------------------------------------------------
+ *
+ * Author: Markus Moeller (markus_moeller at compuserve.com)
+ *
+ * Copyright (C) 2007 Markus Moeller. All rights reserved.
+ *
+ * This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#include "config.h"
+#include "util.h"
+
+#ifdef HAVE_LDAP
+
+#include "support.h"
+
+struct gdstruct *init_gd(void);
+
+struct gdstruct *
+init_gd(void)
+{
+ struct gdstruct *gdsp;
+ gdsp = (struct gdstruct *) xmalloc(sizeof(struct gdstruct));
+ gdsp->group = NULL;
+ gdsp->domain = NULL;
+ gdsp->next = NULL;
+ return gdsp;
+}
+
+char *utf8dup(struct main_args *margs);
+
+char *
+utf8dup(struct main_args *margs)
+{
+ int c = 0, s;
+ size_t n;
+ char *src;
+ unsigned char *p, *dup;
+
+ src = margs->glist;
+ if (!src)
+ return NULL;
+ for (n = 0; n < strlen(src); n++)
+ if ((unsigned char) src[n] > 127)
+ c++;
+ if (c != 0) {
+ p = (unsigned char *) xmalloc(strlen(src) + c);
+ dup = p;
+ for (n = 0; n < strlen(src); n++) {
+ s = (unsigned char) src[n];
+ if (s > 127 && s < 192) {
+ *p = 194;
+ p++;
+ *p = s;
+ } else if (s > 191 && s < 256) {
+ *p = 195;
+ p++;
+ *p = s - 64;
+ } else
+ *p = s;
+ p++;
+ }
+ *p = '\0';
+ debug((char *) "%s| %s: INFO: Group %s as UTF-8: %s\n", LogTime(), PROGRAM, src, dup);
+ return (char *) dup;
+ } else
+ return xstrdup(src);
+}
+
+char *hex_utf_char(struct main_args *margs, int flag);
+/*
+ * UTF8 = UTF1 / UTFMB
+ * UTFMB = UTF2 / UTF3 / UTF4
+ *
+ * UTF0 = %x80-BF
+ * UTF1 = %x00-7F
+ * UTF2 = %xC2-DF UTF0
+ * UTF3 = %xE0 %xA0-BF UTF0 / %xE1-EC 2(UTF0) /
+ * %xED %x80-9F UTF0 / %xEE-EF 2(UTF0)
+ * UTF4 = %xF0 %x90-BF 2(UTF0) / %xF1-F3 3(UTF0) /
+ * %xF4 %x80-8F 2(UTF0)
+ *
+ * http://www.utf8-chartable.de/unicode-utf8-table.pl
+ */
+
+char *
+hex_utf_char(struct main_args *margs, int flag)
+{
+ char *up;
+ char *upd;
+ char *ul;
+ int a, n, nl, ival, ichar;
+ int iUTF2, iUTF3, iUTF4;
+
+ if (flag) {
+ up = margs->ulist;
+ } else {
+ up = margs->tlist;
+ }
+
+ if (!up)
+ return NULL;
+
+ upd = strrchr(up, '@');
+ if (upd)
+ a = upd - up;
+ else
+ a = strlen(up);
+
+ ul = (char *) xmalloc(strlen(up));
+ n = 0;
+ nl = 0;
+ iUTF2 = 0;
+ iUTF3 = 0;
+ iUTF4 = 0;
+
+ while (n < (int) strlen(up)) {
+ if (flag && n == a)
+ break;
+ if (up[n] == '@') {
+ ul[nl] = '@';
+ nl++;
+ n++;
+ continue;
+ }
+ ival = up[n];
+ if (ival > 64 && ival < 71)
+ ichar = (ival - 55) * 16;
+ else if (ival > 96 && ival < 103)
+ ichar = (ival - 87) * 16;
+ else if (ival > 47 && ival < 58)
+ ichar = (ival - 48) * 16;
+ else {
+ debug((char *) "%s| %s: WARNING: Invalid Hex value %c\n", LogTime(), PROGRAM, ival);
+ if (ul)
+ xfree(ul);
+ return NULL;
+ }
+
+
+ if (n == a - 1) {
+ debug((char *) "%s| %s: WARNING: Invalid Hex UTF-8 string %s\n", LogTime(), PROGRAM, up);
+ if (ul)
+ xfree(ul);
+ return NULL;
+ }
+ n++;
+ ival = up[n];
+ if (ival > 64 && ival < 71)
+ ichar = ichar + ival - 55;
+ else if (ival > 96 && ival < 103)
+ ichar = ichar + ival - 87;
+ else if (ival > 47 && ival < 58)
+ ichar = ichar + ival - 48;
+ else {
+ debug((char *) "%s| %s: WARNING: Invalid Hex value %c\n", LogTime(), PROGRAM, ival);
+ if (ul)
+ xfree(ul);
+ return NULL;
+ }
+
+ if (iUTF2) {
+ if (iUTF2 == 0xC2 && ichar > 0x7F && ichar < 0xC0) {
+ iUTF2 = 0;
+ ul[nl - 1] = ichar;
+ } else if (iUTF2 == 0xC3 && ichar > 0x7F && ichar < 0xC0) {
+ iUTF2 = 0;
+ ul[nl - 1] = ichar + 64;
+ } else if (iUTF2 > 0xC3 && iUTF2 < 0xE0 && ichar > 0x7F && ichar < 0xC0) {
+ iUTF2 = 0;
+ ul[nl] = ichar;
+ nl++;
+ } else {
+ iUTF2 = 0;
+ ul[nl] = ichar;
+ ul[nl + 1] = '\0';
+ debug((char *) "%s| %s: WARNING: Invalid UTF-8 sequence for Unicode %s\n", LogTime(), PROGRAM, ul);
+ if (ul)
+ xfree(ul);
+ return NULL;
+ }
+ } else if (iUTF3) {
+ if (iUTF3 == 0xE0 && ichar > 0x9F && ichar < 0xC0) {
+ iUTF3 = 1;
+ ul[nl] = ichar;
+ nl++;
+ } else if (iUTF3 > 0xE0 && iUTF3 < 0xED && ichar > 0x7F && ichar < 0xC0) {
+ iUTF3 = 2;
+ ul[nl] = ichar;
+ nl++;
+ } else if (iUTF3 == 0xED && ichar > 0x7F && ichar < 0xA0) {
+ iUTF3 = 3;
+ ul[nl] = ichar;
+ nl++;
+ } else if (iUTF3 > 0xED && iUTF3 < 0xF0 && ichar > 0x7F && ichar < 0xC0) {
+ iUTF3 = 4;
+ ul[nl] = ichar;
+ nl++;
+ } else if (iUTF3 > 0 && iUTF3 < 5 && ichar > 0x7F && ichar < 0xC0) {
+ iUTF3 = 0;
+ ul[nl] = ichar;
+ nl++;
+ } else {
+ iUTF3 = 0;
+ ul[nl] = ichar;
+ ul[nl + 1] = '\0';
+ debug((char *) "%s| %s: WARNING: Invalid UTF-8 sequence for Unicode %s\n", LogTime(), PROGRAM, ul);
+ if (ul)
+ xfree(ul);
+ return NULL;
+ }
+ } else if (iUTF4) {
+ if (iUTF4 == 0xF0 && ichar > 0x8F && ichar < 0xC0) {
+ iUTF4 = 1;
+ ul[nl] = ichar;
+ nl++;
+ } else if (iUTF4 > 0xF0 && iUTF3 < 0xF4 && ichar > 0x7F && ichar < 0xC0) {
+ iUTF4 = 2;
+ ul[nl] = ichar;
+ nl++;
+ } else if (iUTF4 == 0xF4 && ichar > 0x7F && ichar < 0x90) {
+ iUTF4 = 3;
+ ul[nl] = ichar;
+ nl++;
+ } else if (iUTF4 > 0 && iUTF4 < 5 && ichar > 0x7F && ichar < 0xC0) {
+ if (iUTF4 == 4)
+ iUTF4 = 0;
+ else
+ iUTF4 = 4;
+ ul[nl] = ichar;
+ nl++;
+ } else {
+ iUTF4 = 0;
+ ul[nl] = ichar;
+ ul[nl + 1] = '\0';
+ debug((char *) "%s| %s: WARNING: Invalid UTF-8 sequence for Unicode %s\n", LogTime(), PROGRAM, ul);
+ if (ul)
+ xfree(ul);
+ return NULL;
+ }
+ } else if (ichar < 0x80) {
+ /* UTF1 */
+ ul[nl] = ichar;
+ nl++;
+ } else if (ichar > 0xC1 && ichar < 0xE0) {
+ /* UTF2 (Latin) */
+ iUTF2 = ichar;
+ ul[nl] = ichar;
+ nl++;
+ } else if (ichar > 0xDF && ichar < 0xF0) {
+ /* UTF3 */
+ iUTF3 = ichar;
+ ul[nl] = ichar;
+ nl++;
+ } else if (ichar > 0xEF && ichar < 0xF5) {
+ /* UTF4 */
+ iUTF4 = ichar;
+ ul[nl] = ichar;
+ nl++;
+ } else {
+ ul[nl] = ichar;
+ ul[nl + 1] = '\0';
+ debug((char *) "%s| %s: WARNING: Invalid UTF-8 sequence for Unicode %s\n", LogTime(), PROGRAM, ul);
+ if (ul)
+ xfree(ul);
+ return NULL;
+ }
+ n++;
+ }
+
+ ul[nl] = '\0';
+ if (iUTF2 || iUTF3 || iUTF4) {
+ debug((char *) "%s| %s: INFO: iUTF2: %d iUTF3: %d iUTF4: %d\n", LogTime(), PROGRAM, iUTF2, iUTF3, iUTF4);
+ debug((char *) "%s| %s: WARNING: Invalid UTF-8 sequence for Unicode %s\n", LogTime(), PROGRAM, ul);
+ if (ul)
+ xfree(ul);
+ return NULL;
+ }
+ if (flag && upd)
+ ul = strcat(ul, upd);
+ return ul;
+}
+
+
+int
+create_gd(struct main_args *margs)
+{
+ char *gp, *dp;
+ char *hp1, *hp2, *up;
+ char *p;
+ struct gdstruct *gdsp = NULL, *gdspn = NULL;
+ /*
+ * Group list format:
+ *
+ * glist=Pattern1[:Pattern2]
+ *
+ * Pattern=Group Group for all domains(including non Kerberos domains using ldap url options) if no
+ * other group definition for domain exists or users without
+ * domain information.
+ * gdstruct.domain=NULL, gdstruct.group=Group
+ *
+ * or Pattern=Group@ Group for all Kerberos domains if no other group definition
+ * exists
+ * gdstruct.domain="", gdstruct.group=Group
+ *
+ * or Pattern=Group@Domain Group for a specific Kerberos domain
+ * gdstruct.domain=Domain, gdstruct.group=Group
+ *
+ *
+ */
+ hp1 = hex_utf_char(margs, 0);
+ hp2 = hex_utf_char(margs, 1);
+ up = utf8dup(margs);
+ p = up;
+ if (hp1) {
+ if (hp2) {
+ if (up) {
+ p = (char *) xmalloc(strlen(up) + strlen(hp1) + strlen(hp2) + 2);
+ strcpy(p, up);
+ strcat(p, ":");
+ strcat(p, hp1);
+ strcat(p, ":");
+ strcat(p, hp2);
+ } else {
+ p = (char *) xmalloc(strlen(hp1) + strlen(hp2) + 1);
+ strcpy(p, hp1);
+ strcat(p, ":");
+ strcat(p, hp2);
+ }
+ } else {
+ if (up) {
+ p = (char *) xmalloc(strlen(up) + strlen(hp1) + 1);
+ strcpy(p, up);
+ strcat(p, ":");
+ strcat(p, hp1);
+ } else
+ p = hp1;
+ }
+ } else {
+ if (hp2) {
+ if (up) {
+ p = (char *) xmalloc(strlen(up) + strlen(hp2) + 1);
+ strcpy(p, up);
+ strcat(p, ":");
+ strcat(p, hp2);
+ } else
+ p = hp2;
+ } else
+ p = up;
+ }
+ gp = p;
+ debug((char *) "%s| %s: INFO: Group list %s\n", LogTime(), PROGRAM, p ? p : "NULL");
+ dp = NULL;
+
+ if (!p) {
+ debug((char *) "%s| %s: ERROR: No groups defined.\n", LogTime(), PROGRAM);
+ return (1);
+ }
+ while (*p) { /* loop over group list */
+ if (*p == '\n' || *p == '\r') { /* Ignore CR and LF if exist */
+ p++;
+ continue;
+ }
+ if (*p == '@') { /* end of group name - start of domain name */
+ if (p == gp) { /* empty group name not allowed */
+ debug((char *) "%s| %s: ERROR: No group defined for domain %s\n", LogTime(), PROGRAM, p);
+ return (1);
+ }
+ *p = '\0';
+ p++;
+ gdsp = init_gd();
+ gdsp->group = gp;
+ if (gdspn) /* Have already an existing structure */
+ gdsp->next = gdspn;
+ dp = p; /* after @ starts new domain name */
+ } else if (*p == ':') { /* end of group name or end of domain name */
+ if (p == gp) { /* empty group name not allowed */
+ debug((char *) "%s| %s: ERROR: No group defined for domain %s\n", LogTime(), PROGRAM, p);
+ return (1);
+ }
+ *p = '\0';
+ p++;
+ if (dp) { /* end of domain name */
+ gdsp->domain = xstrdup(dp);
+ dp = NULL;
+ } else { /* end of group name and no domain name */
+ gdsp = init_gd();
+ gdsp->group = gp;
+ if (gdspn) /* Have already an existing structure */
+ gdsp->next = gdspn;
+ }
+ gdspn = gdsp;
+ gp = p; /* after : starts new group name */
+ debug((char *) "%s| %s: INFO: Group %s Domain %s\n", LogTime(), PROGRAM, gdsp->group, gdsp->domain ? gdsp->domain : "NULL");
+ } else
+ p++;
+ }
+ if (p == gp) { /* empty group name not allowed */
+ debug((char *) "%s| %s: ERROR: No group defined for domain %s\n", LogTime(), PROGRAM, p);
+ return (1);
+ }
+ if (dp) { /* end of domain name */
+ gdsp->domain = xstrdup(dp);
+ } else { /* end of group name and no domain name */
+ gdsp = init_gd();
+ gdsp->group = gp;
+ if (gdspn) /* Have already an existing structure */
+ gdsp->next = gdspn;
+ }
+ debug((char *) "%s| %s: INFO: Group %s Domain %s\n", LogTime(), PROGRAM, gdsp->group, gdsp->domain ? gdsp->domain : "NULL");
+
+ margs->groups = gdsp;
+ return (0);
+}
+#endif
--- /dev/null
+/*
+ * ----------------------------------------------------------------------------
+ *
+ * Author: Markus Moeller (markus_moeller at compuserve.com)
+ *
+ * Copyright (C) 2007 Markus Moeller. All rights reserved.
+ *
+ * This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#include "config.h"
+#include "util.h"
+
+#ifdef HAVE_LDAP
+
+#include "support.h"
+
+struct kstruct {
+ krb5_context context;
+ char *mem_cache_env;
+ krb5_ccache cc;
+} kparam;
+
+#define KT_PATH_MAX 256
+
+void
+krb5_cleanup()
+{
+ if (kparam.context) {
+ if (kparam.cc)
+ krb5_cc_destroy(kparam.context, kparam.cc);
+ krb5_free_context(kparam.context);
+ }
+}
+/*
+ * create Kerberos memory cache
+ */
+int
+krb5_create_cache(struct main_args *margs, char *domain)
+{
+
+ krb5_keytab keytab = 0;
+ krb5_keytab_entry entry;
+ krb5_kt_cursor cursor;
+ krb5_creds *creds = NULL;
+ krb5_creds *tgt_creds = NULL;
+ krb5_principal *principal_list = NULL;
+ krb5_principal principal = NULL;
+ char *service;
+ char *keytab_name = NULL, *principal_name = NULL, *mem_cache = NULL;
+ char buf[KT_PATH_MAX], *p;
+ int nprinc = 0;
+ int i;
+ int retval = 0;
+ int found = 0;
+ krb5_error_code code = 0;
+
+ kparam.context = NULL;
+
+ if (!domain || !strcmp(domain, ""))
+ return (1);
+
+ /*
+ * Initialise Kerberos
+ */
+
+ code = krb5_init_context(&kparam.context);
+ if (code) {
+ error((char *) "%s| %s: ERROR: Error while initialising Kerberos library : %s\n", LogTime(), PROGRAM, error_message(code));
+ retval = 1;
+ goto cleanup;
+ }
+ /*
+ * getting default keytab name
+ */
+
+ debug((char *) "%s| %s: DEBUG: Get default keytab file name\n", LogTime(), PROGRAM);
+ krb5_kt_default_name(kparam.context, buf, KT_PATH_MAX);
+ p = strchr(buf, ':'); /* Find the end if "FILE:" */
+ if (p)
+ p++; /* step past : */
+ keytab_name = xstrdup(p ? p : buf);
+ debug((char *) "%s| %s: DEBUG: Got default keytab file name %s\n", LogTime(), PROGRAM, keytab_name);
+
+ code = krb5_kt_resolve(kparam.context, keytab_name, &keytab);
+ if (code) {
+ error((char *) "%s| %s: ERROR: Error while resolving keytab %s : %s\n", LogTime(), PROGRAM, keytab_name, error_message(code));
+ retval = 1;
+ goto cleanup;
+ }
+ code = krb5_kt_start_seq_get(kparam.context, keytab, &cursor);
+ if (code) {
+ error((char *) "%s| %s: ERROR: Error while starting keytab scan : %s\n", LogTime(), PROGRAM, error_message(code));
+ retval = 1;
+ goto cleanup;
+ }
+ debug((char *) "%s| %s: DEBUG: Get principal name from keytab %s\n", LogTime(), PROGRAM, keytab_name);
+
+ nprinc = 0;
+ while ((code = krb5_kt_next_entry(kparam.context, keytab, &entry, &cursor)) == 0) {
+
+ principal_list = (krb5_principal *) xrealloc(principal_list, sizeof(krb5_principal) * (nprinc + 1));
+ krb5_copy_principal(kparam.context, entry.principal, &principal_list[nprinc++]);
+#ifdef HAVE_HEIMDAL_KERBEROS
+ debug((char *) "%s| %s: DEBUG: Keytab entry has realm name: %s\n", LogTime(), PROGRAM, entry.principal->realm);
+#else
+ debug((char *) "%s| %s: DEBUG: Keytab entry has realm name: %s\n", LogTime(), PROGRAM, krb5_princ_realm(kparam.context, entry.principal)->data);
+#endif
+#ifdef HAVE_HEIMDAL_KERBEROS
+ if (!strcasecmp(domain, entry.principal->realm))
+#else
+ if (!strcasecmp(domain, krb5_princ_realm(kparam.context, entry.principal)->data))
+#endif
+ {
+ code = krb5_unparse_name(kparam.context, entry.principal, &principal_name);
+ if (code) {
+ error((char *) "%s| %s: ERROR: Error while unparsing principal name : %s\n", LogTime(), PROGRAM, error_message(code));
+ } else {
+ debug((char *) "%s| %s: DEBUG: Found principal name: %s\n", LogTime(), PROGRAM, principal_name);
+ found = 1;
+ }
+ }
+#if defined(HAVE_HEIMDAL_KERBEROS) || ( defined(HAVE_KRB5_KT_FREE_ENTRY) && HAVE_DECL_KRB5_KT_FREE_ENTRY==1)
+ code = krb5_kt_free_entry(kparam.context, &entry);
+#else
+ code = krb5_free_keytab_entry_contents(kparam.context, &entry);
+#endif
+ if (code) {
+ error((char *) "%s| %s: ERROR: Error while freeing keytab entry : %s\n", LogTime(), PROGRAM, error_message(code));
+ retval = 1;
+ break;
+ }
+ if (found)
+ break;
+ }
+
+ if (code && code != KRB5_KT_END) {
+ error((char *) "%s| %s: ERROR: Error while scanning keytab : %s\n", LogTime(), PROGRAM, error_message(code));
+ retval = 1;
+ goto cleanup;
+ }
+ code = krb5_kt_end_seq_get(kparam.context, keytab, &cursor);
+ if (code) {
+ error((char *) "%s| %s: ERROR: Error while ending keytab scan : %s\n", LogTime(), PROGRAM, error_message(code));
+ retval = 1;
+ goto cleanup;
+ }
+ /*
+ * prepare memory credential cache
+ */
+#if !defined(HAVE_KRB5_MEMORY_CACHE) || defined(HAVE_SUN_LDAP_SDK)
+ mem_cache = (char *) xmalloc(strlen("FILE:/tmp/squid_ldap_") + 16);
+ snprintf(mem_cache, strlen("FILE:/tmp/squid_ldap_") + 16, "FILE:/tmp/squid_ldap_%d", (int) getpid());
+#else
+ mem_cache = (char *) xmalloc(strlen("MEMORY:squid_ldap_") + 16);
+ snprintf(mem_cache, strlen("MEMORY:squid_ldap_") + 16, "MEMORY:squid_ldap_%d", (int) getpid());
+#endif
+
+ setenv("KRB5CCNAME", mem_cache, 1);
+ debug((char *) "%s| %s: DEBUG: Set credential cache to %s\n", LogTime(), PROGRAM, mem_cache);
+ code = krb5_cc_resolve(kparam.context, mem_cache, &kparam.cc);
+ if (code) {
+ error((char *) "%s| %s: ERROR: Error while resolving memory ccache : %s\n", LogTime(), PROGRAM, error_message(code));
+ retval = 1;
+ goto cleanup;
+ }
+ /*
+ * if no principal name found in keytab for domain use the prinipal name which can get a TGT
+ */
+ if (!principal_name) {
+ debug((char *) "%s| %s: DEBUG: Did not find a principal in keytab for domain %s.\n", LogTime(), PROGRAM, domain);
+ debug((char *) "%s| %s: DEBUG: Try to get principal of trusted domain.\n", LogTime(), PROGRAM);
+ creds = (krb5_creds *) xmalloc(sizeof(*creds));
+ memset(creds, 0, sizeof(*creds));
+
+ for (i = 0; i < nprinc; i++) {
+ /*
+ * get credentials
+ */
+ code = krb5_unparse_name(kparam.context, principal_list[i], &principal_name);
+ if (code) {
+ debug((char *) "%s| %s: DEBUG: Error while unparsing principal name : %s\n", LogTime(), PROGRAM, error_message(code));
+ goto loop_end;
+ }
+ debug((char *) "%s| %s: DEBUG: Keytab entry has principal: %s\n", LogTime(), PROGRAM, principal_name);
+
+#if HAVE_GET_INIT_CREDS_KEYTAB
+ code = krb5_get_init_creds_keytab(kparam.context, creds, principal_list[i], keytab, 0, NULL, NULL);
+#else
+ service = (char *) xmalloc(strlen("krbtgt") + 2 * strlen(domain) + 3);
+ snprintf(service, strlen("krbtgt") + 2 * strlen(domain) + 3, "krbtgt/%s@%s", domain, domain);
+ creds->client = principal_list[i];
+ code = krb5_parse_name(kparam.context, service, &creds->server);
+ if (service)
+ xfree(service);
+ code = krb5_get_in_tkt_with_keytab(kparam.context, 0, NULL, NULL, NULL, keytab, NULL, creds, 0);
+#endif
+ if (code) {
+ debug((char *) "%s| %s: DEBUG: Error while initialising credentials from keytab : %s\n", LogTime(), PROGRAM, error_message(code));
+ goto loop_end;
+ }
+ code = krb5_cc_initialize(kparam.context, kparam.cc, principal_list[i]);
+ if (code) {
+ error((char *) "%s| %s: ERROR: Error while initializing memory caches : %s\n", LogTime(), PROGRAM, error_message(code));
+ goto loop_end;
+ }
+ code = krb5_cc_store_cred(kparam.context, kparam.cc, creds);
+ if (code) {
+ debug((char *) "%s| %s: DEBUG: Error while storing credentials : %s\n", LogTime(), PROGRAM, error_message(code));
+ goto loop_end;
+ }
+ if (creds->server)
+ krb5_free_principal(kparam.context, creds->server);
+#ifdef HAVE_HEIMDAL_KERBEROS
+ service = (char *) xmalloc(strlen("krbtgt") + strlen(domain) + strlen(principal_list[i]->realm) + 3);
+ snprintf(service, strlen("krbtgt") + strlen(domain) + strlen(principal_list[i]->realm) + 3, "krbtgt/%s@%s", domain, principal_list[i]->realm);
+#else
+ service = (char *) xmalloc(strlen("krbtgt") + strlen(domain) + strlen(krb5_princ_realm(kparam.context, principal_list[i])->data) + 3);
+ snprintf(service, strlen("krbtgt") + strlen(domain) + strlen(krb5_princ_realm(kparam.context, principal_list[i])->data) + 3, "krbtgt/%s@%s", domain, krb5_princ_realm(kparam.context, principal_list[i])->data);
+#endif
+ code = krb5_parse_name(kparam.context, service, &creds->server);
+ if (service)
+ xfree(service);
+ if (code) {
+ error((char *) "%s| %s: ERROR: Error while initialising TGT credentials : %s\n", LogTime(), PROGRAM, error_message(code));
+ goto loop_end;
+ }
+ code = krb5_get_credentials(kparam.context, 0, kparam.cc, creds, &tgt_creds);
+ if (code) {
+ debug((char *) "%s| %s: DEBUG: Error while getting tgt : %s\n", LogTime(), PROGRAM, error_message(code));
+ goto loop_end;
+ } else {
+ debug((char *) "%s| %s: DEBUG: Found trusted principal name: %s\n", LogTime(), PROGRAM, principal_name);
+ found = 1;
+ break;
+ }
+
+ loop_end:
+ if (principal_name)
+ xfree(principal_name);
+ principal_name = NULL;
+ }
+
+ if (tgt_creds)
+ krb5_free_creds(kparam.context, tgt_creds);
+ tgt_creds = NULL;
+ if (creds)
+ krb5_free_creds(kparam.context, creds);
+ creds = NULL;
+ }
+ if (principal_name) {
+
+ debug((char *) "%s| %s: DEBUG: Got principal name %s\n", LogTime(), PROGRAM, principal_name);
+ /*
+ * build principal
+ */
+ code = krb5_parse_name(kparam.context, principal_name, &principal);
+ if (code) {
+ error((char *) "%s| %s: ERROR: Error while parsing name %s : %s\n", LogTime(), PROGRAM, principal_name, error_message(code));
+ retval = 1;
+ goto cleanup;
+ }
+ creds = (krb5_creds *) xmalloc(sizeof(*creds));
+ memset(creds, 0, sizeof(*creds));
+
+ /*
+ * get credentials
+ */
+#if HAVE_GET_INIT_CREDS_KEYTAB
+ code = krb5_get_init_creds_keytab(kparam.context, creds, principal, keytab, 0, NULL, NULL);
+#else
+ service = (char *) xmalloc(strlen("krbtgt") + 2 * strlen(domain) + 3);
+ snprintf(service, strlen("krbtgt") + 2 * strlen(domain) + 3, "krbtgt/%s@%s", domain, domain);
+ creds->client = principal;
+ code = krb5_parse_name(kparam.context, service, &creds->server);
+ if (service)
+ xfree(service);
+ code = krb5_get_in_tkt_with_keytab(kparam.context, 0, NULL, NULL, NULL, keytab, NULL, creds, 0);
+#endif
+ if (code) {
+ error((char *) "%s| %s: ERROR: Error while initialising credentials from keytab : %s\n", LogTime(), PROGRAM, error_message(code));
+ retval = 1;
+ goto cleanup;
+ }
+ code = krb5_cc_initialize(kparam.context, kparam.cc, principal);
+ if (code) {
+ error((char *) "%s| %s: ERROR: Error while initializing memory caches : %s\n", LogTime(), PROGRAM, error_message(code));
+ retval = 1;
+ goto cleanup;
+ }
+ code = krb5_cc_store_cred(kparam.context, kparam.cc, creds);
+ if (code) {
+ error((char *) "%s| %s: ERROR: Error while storing credentials : %s\n", LogTime(), PROGRAM, error_message(code));
+ retval = 1;
+ goto cleanup;
+ }
+ debug((char *) "%s| %s: DEBUG: Stored credentials\n", LogTime(), PROGRAM);
+ } else {
+ debug((char *) "%s| %s: DEBUG: Got no principal name\n", LogTime(), PROGRAM);
+ retval = 1;
+ }
+ cleanup:
+ if (keytab)
+ krb5_kt_close(kparam.context, keytab);
+ if (keytab_name)
+ xfree(keytab_name);
+ if (principal_name)
+ xfree(principal_name);
+ if (mem_cache)
+ xfree(mem_cache);
+ if (principal)
+ krb5_free_principal(kparam.context, principal);
+ for (i = 0; i < nprinc; i++) {
+ if (principal_list[i])
+ krb5_free_principal(kparam.context, principal_list[i]);
+ }
+ if (principal_list)
+ xfree(principal_list);
+ if (creds)
+ krb5_free_creds(kparam.context, creds);
+
+ return (retval);
+}
+#endif
--- /dev/null
+/*
+ * -----------------------------------------------------------------------------
+ *
+ * Author: Markus Moeller (markus_moeller at compuserve.com)
+ *
+ * Copyright (C) 2007 Markus Moeller. All rights reserved.
+ *
+ * This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#include "config.h"
+#include "util.h"
+
+#ifdef HAVE_LDAP
+
+#include "support.h"
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+char *convert_domain_to_bind_path(char *domain);
+char *escape_filter(char *filter);
+int check_AD(struct main_args *margs, LDAP * ld);
+int ldap_set_defaults(struct main_args *margs, LDAP * ld);
+int ldap_set_ssl_defaults(struct main_args *margs);
+LDAP *tool_ldap_open(struct main_args *margs, char *host, int port, char *ssl);
+
+#define CONNECT_TIMEOUT 2
+#define SEARCH_TIMEOUT 30
+
+#define FILTER "(memberuid=%s)"
+#define ATTRIBUTE "cn"
+#define FILTER_UID "(uid=%s)"
+#define FILTER_GID "(&(gidNumber=%s)(objectclass=posixgroup))"
+#define ATTRIBUTE_GID "gidNumber"
+
+#define FILTER_AD "(samaccountname=%s)"
+#define ATTRIBUTE_AD "memberof"
+
+int get_attributes(struct main_args *margs, LDAP * ld, LDAPMessage * res, const char *attribute /* IN */ , char ***out_val /* OUT (caller frees) */ );
+int search_group_tree(struct main_args *margs, LDAP * ld, char *bindp, char *ldap_group, char *group, int depth);
+
+#if defined(HAVE_SUN_LDAP_SDK) || defined(HAVE_MOZILLA_LDAP_SDK)
+#ifdef HAVE_LDAP_REBINDPROC_CALLBACK
+
+#if defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H) || defined(HAVE_SASL_DARWIN)
+static LDAP_REBINDPROC_CALLBACK ldap_sasl_rebind;
+
+static int LDAP_CALL LDAP_CALLBACK
+ldap_sasl_rebind(
+ LDAP * ld,
+ char **whop,
+ char **credp,
+ int *methodp,
+ int freeit,
+ void *params)
+{
+ struct ldap_creds *cp = (struct ldap_creds *) params;
+ whop = whop;
+ credp = credp;
+ methodp = methodp;
+ freeit = freeit;
+ return tool_sasl_bind(ld, cp->dn, cp->pw);
+}
+#endif
+
+static LDAP_REBINDPROC_CALLBACK ldap_simple_rebind;
+
+static int LDAP_CALL LDAP_CALLBACK
+ldap_simple_rebind(
+ LDAP * ld,
+ char **whop,
+ char **credp,
+ int *methodp,
+ int freeit,
+ void *params)
+{
+ struct ldap_creds *cp = (struct ldap_creds *) params;
+ whop = whop;
+ credp = credp;
+ methodp = methodp;
+ freeit = freeit;
+ return ldap_bind_s(ld, cp->dn, cp->pw, LDAP_AUTH_SIMPLE);
+}
+#elif defined(HAVE_LDAP_REBIND_PROC)
+#if defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H) || defined(HAVE_SASL_DARWIN)
+static LDAP_REBIND_PROC ldap_sasl_rebind;
+
+static int
+ldap_sasl_rebind(
+ LDAP * ld,
+ LDAP_CONST char *url,
+ ber_tag_t request,
+ ber_int_t msgid,
+ void *params)
+{
+ struct ldap_creds *cp = (struct ldap_creds *) params;
+ url = url;
+ request = request;
+ msgid = msgid;
+ return tool_sasl_bind(ld, cp->dn, cp->pw);
+}
+#endif
+
+static LDAP_REBIND_PROC ldap_simple_rebind;
+
+static int
+ldap_simple_rebind(
+ LDAP * ld,
+ LDAP_CONST char *url,
+ ber_tag_t request,
+ ber_int_t msgid,
+ void *params)
+{
+ struct ldap_creds *cp = (struct ldap_creds *) params;
+ url = url;
+ request = request;
+ msgid = msgid;
+ return ldap_bind_s(ld, cp->dn, cp->pw, LDAP_AUTH_SIMPLE);
+}
+
+#elif defined(HAVE_LDAP_REBIND_FUNCTION)
+#ifndef LDAP_REFERRALS
+#define LDAP_REFERRALS
+#endif
+#if defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H) || defined(HAVE_SASL_DARWIN)
+static LDAP_REBIND_FUNCTION ldap_sasl_rebind;
+
+static int
+ldap_sasl_rebind(
+ LDAP * ld,
+ char **whop,
+ char **credp,
+ int *methodp,
+ int freeit,
+ void *params)
+{
+ struct ldap_creds *cp = (struct ldap_creds *) params;
+ whop = whop;
+ credp = credp;
+ methodp = methodp;
+ freeit = freeit;
+ return tool_sasl_bind(ld, cp->dn, cp->pw);
+}
+#endif
+
+static LDAP_REBIND_FUNCTION ldap_simple_rebind;
+
+static int
+ldap_simple_rebind(
+ LDAP * ld,
+ char **whop,
+ char **credp,
+ int *methodp,
+ int freeit,
+ void *params)
+{
+ struct ldap_creds *cp = (struct ldap_creds *) params;
+ whop = whop;
+ credp = credp;
+ methodp = methodp;
+ freeit = freeit;
+ return ldap_bind_s(ld, cp->dn, cp->pw, LDAP_AUTH_SIMPLE);
+}
+#else
+#error "No rebind functione defined"
+#endif
+#else /* HAVE_SUN_LDAP_SDK */
+#if defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H) || defined(HAVE_SASL_DARWIN)
+static LDAP_REBIND_PROC ldap_sasl_rebind;
+
+static int
+ldap_sasl_rebind(
+ LDAP * ld,
+ LDAP_CONST char *url,
+ ber_tag_t request,
+ ber_int_t msgid,
+ void *params)
+{
+ struct ldap_creds *cp = (struct ldap_creds *) params;
+ url = url;
+ request = request;
+ msgid = msgid;
+ return tool_sasl_bind(ld, cp->dn, cp->pw);
+}
+#endif
+
+static LDAP_REBIND_PROC ldap_simple_rebind;
+
+static int
+ldap_simple_rebind(
+ LDAP * ld,
+ LDAP_CONST char *url,
+ ber_tag_t request,
+ ber_int_t msgid,
+ void *params)
+{
+
+ struct ldap_creds *cp = (struct ldap_creds *) params;
+ url = url;
+ request = request;
+ msgid = msgid;
+ return ldap_bind_s(ld, cp->dn, cp->pw, LDAP_AUTH_SIMPLE);
+}
+
+#endif
+char *
+convert_domain_to_bind_path(char *domain)
+{
+ char *dp, *bindp = NULL, *bp = NULL;
+ int i = 0;
+
+ if (!domain)
+ return NULL;
+
+ for (dp = domain; *dp; dp++) {
+ if (*dp == '.')
+ i++;
+ }
+ /*
+ * add dc= and
+ * replace . with ,dc= => new length = old length + #dots * 3 + 3
+ */
+ bindp = (char *) xmalloc(strlen(domain) + 3 + i * 3 + 1);
+ bp = bindp;
+ strcpy(bp, "dc=");
+ bp += 3;
+ for (dp = domain; *dp; dp++) {
+ if (*dp == '.') {
+ strcpy(bp, ",dc=");
+ bp += 4;
+ } else
+ *bp++ = *dp;
+ }
+ *bp = '\0';
+ return bindp;
+}
+
+char *
+escape_filter(char *filter)
+{
+ int i;
+ char *ldap_filter_esc, *ldf;
+
+ i = 0;
+ for (ldap_filter_esc = filter; *ldap_filter_esc; ldap_filter_esc++) {
+ if ((*ldap_filter_esc == '*') ||
+ (*ldap_filter_esc == '(') ||
+ (*ldap_filter_esc == ')') ||
+ (*ldap_filter_esc == '\\'))
+ i = i + 3;
+ }
+
+ ldap_filter_esc = (char *) xcalloc(strlen(filter) + i + 1, sizeof(char));
+ ldf = ldap_filter_esc;
+ for (; *filter; filter++) {
+ if (*filter == '*') {
+ strcpy(ldf, "\\2a");
+ ldf = ldf + 3;
+ } else if (*filter == '(') {
+ strcpy(ldf, "\\28");
+ ldf = ldf + 3;
+ } else if (*filter == ')') {
+ strcpy(ldf, "\\29");
+ ldf = ldf + 3;
+ } else if (*filter == '\\') {
+ strcpy(ldf, "\\5c");
+ ldf = ldf + 3;
+ } else {
+ *ldf = *filter;
+ ldf++;
+ }
+ }
+ *ldf = '\0';
+
+ return ldap_filter_esc;
+};
+
+int
+check_AD(struct main_args *margs, LDAP * ld)
+{
+ LDAPMessage *res;
+ char **attr_value = NULL;
+ struct timeval searchtime;
+ int max_attr = 0;
+ int j, rc = 0;
+
+#define FILTER_SCHEMA "(objectclass=*)"
+#define ATTRIBUTE_SCHEMA "schemaNamingContext"
+#define FILTER_SAM "(ldapdisplayname=samaccountname)"
+
+ searchtime.tv_sec = SEARCH_TIMEOUT;
+ searchtime.tv_usec = 0;
+
+ debug((char *) "%s| %s: DEBUG: Search ldap server with bind path \"\" and filter: %s\n", LogTime(), PROGRAM, FILTER_SCHEMA);
+ rc = ldap_search_ext_s(ld, (char *) "", LDAP_SCOPE_BASE, (char *) FILTER_SCHEMA, NULL, 0,
+ NULL, NULL, &searchtime, 0, &res);
+
+ if (rc == LDAP_SUCCESS)
+ max_attr = get_attributes(margs, ld, res, ATTRIBUTE_SCHEMA, &attr_value);
+
+ if (max_attr == 1) {
+ ldap_msgfree(res);
+ debug((char *) "%s| %s: DEBUG: Search ldap server with bind path %s and filter: %s\n", LogTime(), PROGRAM, attr_value[0], FILTER_SAM);
+ rc = ldap_search_ext_s(ld, attr_value[0], LDAP_SCOPE_SUBTREE, (char *) FILTER_SAM, NULL, 0,
+ NULL, NULL, &searchtime, 0, &res);
+ debug((char *) "%s| %s: DEBUG: Found %d ldap entr%s\n", LogTime(), PROGRAM, ldap_count_entries(ld, res), ldap_count_entries(ld, res) > 1 || ldap_count_entries(ld, res) == 0 ? "ies" : "y");
+ if (ldap_count_entries(ld, res) > 0)
+ margs->AD = 1;
+ } else
+ debug((char *) "%s| %s: DEBUG: Did not find ldap entry for subschemasubentry\n", LogTime(), PROGRAM);
+ debug((char *) "%s| %s: DEBUG: Determined ldap server %sas an Active Directory server\n", LogTime(), PROGRAM, margs->AD ? "" : "not ");
+ /*
+ * Cleanup
+ */
+ if (attr_value) {
+ for (j = 0; j < max_attr; j++) {
+ xfree(attr_value[j]);
+ }
+ xfree(attr_value);
+ attr_value = NULL;
+ }
+ ldap_msgfree(res);
+ return rc;
+}
+int
+search_group_tree(struct main_args *margs, LDAP * ld, char *bindp, char *ldap_group, char *group, int depth)
+{
+ LDAPMessage *res = NULL;
+ char **attr_value = NULL;
+ int max_attr = 0;
+ char *filter = NULL;
+ char *search_exp = NULL;
+ int j, rc = 0, retval = 0;
+ char *av = NULL, *avp = NULL;
+ int ldepth;
+ char *ldap_filter_esc = NULL;
+ struct timeval searchtime;
+
+#define FILTER_GROUP_AD "(&(%s)(objectclass=group))"
+#define FILTER_GROUP "(&(memberuid=%s)(objectclass=posixgroup))"
+
+ searchtime.tv_sec = SEARCH_TIMEOUT;
+ searchtime.tv_usec = 0;
+
+ if (margs->AD)
+ filter = (char *) FILTER_GROUP_AD;
+ else
+ filter = (char *) FILTER_GROUP;
+
+ ldap_filter_esc = escape_filter(ldap_group);
+
+ search_exp = (char *) xmalloc(strlen(filter) + strlen(ldap_filter_esc) + 1);
+ snprintf(search_exp, strlen(filter) + strlen(ldap_filter_esc) + 1, filter, ldap_filter_esc);
+
+ if (ldap_filter_esc)
+ xfree(ldap_filter_esc);
+
+ if (depth > margs->mdepth) {
+ debug((char *) "%s| %s: DEBUG: Max search depth reached %d>%d\n", LogTime(), PROGRAM, depth, margs->mdepth);
+ return 0;
+ }
+ debug((char *) "%s| %s: DEBUG: Search ldap server with bind path %s and filter : %s\n", LogTime(), PROGRAM, bindp, search_exp);
+ rc = ldap_search_ext_s(ld, bindp, LDAP_SCOPE_SUBTREE,
+ search_exp, NULL, 0,
+ NULL, NULL, &searchtime, 0, &res);
+ if (search_exp)
+ xfree(search_exp);
+
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error searching ldap server: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ ldap_unbind_s(ld);
+ return 0;
+ }
+ debug((char *) "%s| %s: DEBUG: Found %d ldap entr%s\n", LogTime(), PROGRAM, ldap_count_entries(ld, res), ldap_count_entries(ld, res) > 1 || ldap_count_entries(ld, res) == 0 ? "ies" : "y");
+
+ if (margs->AD)
+ max_attr = get_attributes(margs, ld, res, ATTRIBUTE_AD, &attr_value);
+ else
+ max_attr = get_attributes(margs, ld, res, ATTRIBUTE, &attr_value);
+
+ /*
+ * Compare group names
+ */
+ retval = 0;
+ ldepth = depth + 1;
+ for (j = 0; j < max_attr; j++) {
+
+ /* Compare first CN= value assuming it is the same as the group name itself */
+ av = attr_value[j];
+ if (!strncasecmp("CN=", av, 3)) {
+ av += 3;
+ if ((avp = strchr(av, ','))) {
+ *avp = '\0';
+ }
+ }
+ if (debug_enabled) {
+ int n;
+ debug((char *) "%s| %s: DEBUG: Entry %d \"%s\" in hex UTF-8 is ", LogTime(), PROGRAM, j + 1, av);
+ for (n = 0; av[n] != '\0'; n++)
+ fprintf(stderr, "%02x", (unsigned char) av[n]);
+ fprintf(stderr, "\n");
+ }
+ if (!strcasecmp(group, av)) {
+ retval = 1;
+ debug((char *) "%s| %s: DEBUG: Entry %d \"%s\" matches group name \"%s\"\n", LogTime(), PROGRAM, j + 1, av, group);
+ break;
+ } else
+ debug((char *) "%s| %s: DEBUG: Entry %d \"%s\" does not match group name \"%s\"\n", LogTime(), PROGRAM, j + 1, av, group);
+ /*
+ * Do recursive group search
+ */
+ debug((char *) "%s| %s: DEBUG: Perform recursive group search for group \"%s\"\n", LogTime(), PROGRAM, av);
+ av = attr_value[j];
+ if (search_group_tree(margs, ld, bindp, av, group, ldepth)) {
+ retval = 1;
+ if (!strncasecmp("CN=", av, 3)) {
+ av += 3;
+ if ((avp = strchr(av, ','))) {
+ *avp = '\0';
+ }
+ }
+ if (debug_enabled)
+ debug((char *) "%s| %s: DEBUG: Entry %d \"%s\" is member of group named \"%s\"\n", LogTime(), PROGRAM, j + 1, av, group);
+ else
+ break;
+
+ }
+ }
+
+ /*
+ * Cleanup
+ */
+ if (attr_value) {
+ for (j = 0; j < max_attr; j++) {
+ xfree(attr_value[j]);
+ }
+ xfree(attr_value);
+ attr_value = NULL;
+ }
+ ldap_msgfree(res);
+
+ return retval;
+}
+
+int
+ldap_set_defaults(struct main_args *margs, LDAP * ld)
+{
+ int val, rc = 0;
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
+ struct timeval tv;
+#endif
+ val = LDAP_VERSION3;
+ rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &val);
+ if (rc != LDAP_SUCCESS) {
+ debug((char *) "%s| %s: DEBUG: Error while setting protocol version: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ return rc;
+ }
+ rc = ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+ if (rc != LDAP_SUCCESS) {
+ debug((char *) "%s| %s: DEBUG: Error while setting referrals off: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ return rc;
+ }
+#ifdef LDAP_OPT_NETWORK_TIMEOUT
+ tv.tv_sec = CONNECT_TIMEOUT;
+ tv.tv_usec = 0;
+ rc = ldap_set_option(ld, LDAP_OPT_NETWORK_TIMEOUT, &tv);
+ if (rc != LDAP_SUCCESS) {
+ debug((char *) "%s| %s: DEBUG: Error while setting network timeout: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ return rc;
+ }
+#endif /* LDAP_OPT_NETWORK_TIMEOUT */
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_set_ssl_defaults(struct main_args *margs)
+{
+#if defined(HAVE_OPENLDAP) || defined(HAVE_LDAPSSL_CLIENT_INIT)
+ int rc = 0;
+#endif
+#ifdef HAVE_OPENLDAP
+ int val;
+ char *ssl_cacertfile = NULL;
+ int free_path;
+#elif defined(HAVE_LDAPSSL_CLIENT_INIT)
+ char *ssl_certdbpath = NULL;
+#endif
+
+#ifdef HAVE_OPENLDAP
+ if (!margs->rc_allow) {
+ debug((char *) "%s| %s: DEBUG: Enable server certificate check for ldap server.\n", LogTime(), PROGRAM);
+ val = LDAP_OPT_X_TLS_DEMAND;
+ rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &val);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error while setting LDAP_OPT_X_TLS_REQUIRE_CERT DEMAND for ldap server: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ return rc;
+ }
+ ssl_cacertfile = getenv("TLS_CACERTFILE");
+ free_path = 0;
+ if (!ssl_cacertfile) {
+ ssl_cacertfile = xstrdup("/etc/ssl/certs/cert.pem");
+ free_path = 1;
+ }
+ debug((char *) "%s| %s: DEBUG: Set certificate file for ldap server to %s.(Changeable through setting environment variable TLS_CACERTFILE)\n", LogTime(), PROGRAM, ssl_cacertfile);
+ rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, ssl_cacertfile);
+ if (ssl_cacertfile && free_path) {
+ xfree(ssl_cacertfile);
+ ssl_cacertfile = NULL;
+ }
+ if (rc != LDAP_OPT_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error while setting LDAP_OPT_X_TLS_CACERTFILE for ldap server: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ return rc;
+ }
+ } else {
+ debug((char *) "%s| %s: DEBUG: Disable server certificate check for ldap server.\n", LogTime(), PROGRAM);
+ val = LDAP_OPT_X_TLS_ALLOW;
+ rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &val);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error while setting LDAP_OPT_X_TLS_REQUIRE_CERT ALLOW for ldap server: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ return rc;
+ }
+ }
+#elif defined(HAVE_LDAPSSL_CLIENT_INIT)
+ /*
+ * Solaris SSL ldap calls require path to certificate database
+ */
+/*
+ * rc = ldapssl_client_init( ssl_certdbpath, NULL );
+ * rc = ldapssl_advclientauth_init( ssl_certdbpath, NULL , 0 , NULL, NULL, 0, NULL, 2);
+ */
+ ssl_certdbpath = getenv("SSL_CERTDBPATH");
+ if (!ssl_certdbpath) {
+ ssl_certdbpath = xstrdup("/etc/certs");
+ }
+ debug((char *) "%s| %s: DEBUG: Set certificate database path for ldap server to %s.(Changeable through setting environment variable SSL_CERTDBPATH)\n", LogTime(), PROGRAM, ssl_certdbpath);
+ if (!margs->rc_allow) {
+ rc = ldapssl_advclientauth_init(ssl_certdbpath, NULL, 0, NULL, NULL, 0, NULL, 2);
+ } else {
+ rc = ldapssl_advclientauth_init(ssl_certdbpath, NULL, 0, NULL, NULL, 0, NULL, 0);
+ debug((char *) "%s| %s: DEBUG: Disable server certificate check for ldap server.\n", LogTime(), PROGRAM);
+ }
+ if (ssl_certdbpath) {
+ xfree(ssl_certdbpath);
+ ssl_certdbpath = NULL;
+ }
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error while setting SSL for ldap server: %s\n", LogTime(), PROGRAM, ldapssl_err2string(rc));
+ return rc;
+ }
+#else
+ error((char *) "%s| %s: ERROR: SSL not supported by ldap library\n", LogTime(), PROGRAM);
+#endif
+ return LDAP_SUCCESS;
+}
+
+int
+get_attributes(struct main_args *margs, LDAP * ld, LDAPMessage * res, const char *attribute, char ***ret_value)
+{
+
+ LDAPMessage *msg;
+ char **attr_value = NULL;
+ int max_attr = 0;
+
+ attr_value = *ret_value;
+ /*
+ * loop over attributes
+ */
+ debug((char *) "%s| %s: DEBUG: Search ldap entries for attribute : %s\n", LogTime(), PROGRAM, attribute);
+ for (msg = ldap_first_entry(ld, res); msg; msg = ldap_next_entry(ld, msg)) {
+
+ BerElement *b;
+ char *attr;
+
+ switch (ldap_msgtype(msg)) {
+
+ case LDAP_RES_SEARCH_ENTRY:
+
+ for (attr = ldap_first_attribute(ld, msg, &b); attr;
+ attr = ldap_next_attribute(ld, msg, b)) {
+ if (strcasecmp(attr, attribute) == 0) {
+ struct berval **values;
+ int il;
+
+ if ((values = ldap_get_values_len(ld, msg, attr)) != NULL) {
+ for (il = 0; values[il] != NULL; il++) {
+
+ attr_value = (char **) xrealloc(attr_value, (il + 1) * sizeof(char *));
+ if (!attr_value)
+ break;
+
+ attr_value[il] = (char *) xmalloc(values[il]->bv_len + 1);
+ memcpy(attr_value[il], values[il]->bv_val, values[il]->bv_len);
+ attr_value[il][values[il]->bv_len] = 0;
+ }
+ max_attr = il;
+ }
+ ber_bvecfree(values);
+ }
+ ldap_memfree(attr);
+ }
+ ber_free(b, 0);
+ break;
+ case LDAP_RES_SEARCH_REFERENCE:
+ debug((char *) "%s| %s: DEBUG: Received a search reference message\n", LogTime(), PROGRAM);
+ break;
+ case LDAP_RES_SEARCH_RESULT:
+ debug((char *) "%s| %s: DEBUG: Received a search result message\n", LogTime(), PROGRAM);
+ break;
+ default:
+ break;
+ }
+ }
+
+ debug((char *) "%s| %s: DEBUG: %d ldap entr%s found with attribute : %s\n", LogTime(), PROGRAM, max_attr, max_attr > 1 || max_attr == 0 ? "ies" : "y", attribute);
+
+ *ret_value = attr_value;
+ return max_attr;
+}
+
+/*
+ * call to open ldap server with or without SSL
+ */
+LDAP *
+tool_ldap_open(struct main_args * margs, char *host, int port, char *ssl)
+{
+ LDAP *ld;
+#ifdef HAVE_OPENLDAP
+ LDAPURLDesc *url = NULL;
+ char *ldapuri = NULL;
+#endif
+ int rc = 0;
+
+ /*
+ * Use ldap open here to check if TCP connection is possible. If possible use it.
+ * (Not sure if this is the best way)
+ */
+#ifdef HAVE_OPENLDAP
+ url = (LDAPURLDesc *) xmalloc(sizeof(*url));
+ memset(url, 0, sizeof(*url));
+#ifdef HAVE_LDAP_URL_LUD_SCHEME
+ if (ssl)
+ url->lud_scheme = (char *) "ldaps";
+ else
+ url->lud_scheme = (char *) "ldap";
+#endif
+ url->lud_host = host;
+ url->lud_port = port;
+#ifdef HAVE_LDAP_SCOPE_DEFAULT
+ url->lud_scope = LDAP_SCOPE_DEFAULT;
+#else
+ url->lud_scope = LDAP_SCOPE_SUBTREE;
+#endif
+#ifdef HAVE_LDAP_URL_DESC2STR
+ ldapuri = ldap_url_desc2str(url);
+#elif defined(HAVE_LDAP_URL_PARSE)
+ rc = ldap_url_parse(ldapuri, &url);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error while parsing url: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ if (ldapuri)
+ xfree(ldapuri);
+ if (url)
+ xfree(url);
+ return NULL;
+ }
+#else
+#error "No URL parsing function"
+#endif
+ if (url) {
+ xfree(url);
+ url = NULL;
+ }
+ rc = ldap_initialize(&ld, ldapuri);
+ if (ldapuri)
+ xfree(ldapuri);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error while initialising connection to ldap server: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ ldap_unbind(ld);
+ ld = NULL;
+ return NULL;
+ }
+#else
+ ld = ldap_init(host, port);
+#endif
+ rc = ldap_set_defaults(margs, ld);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error while setting default options for ldap server: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ ldap_unbind(ld);
+ ld = NULL;
+ return NULL;
+ }
+ if (ssl) {
+ /*
+ * Try Start TLS first
+ */
+ debug((char *) "%s| %s: DEBUG: Set SSL defaults\n", LogTime(), PROGRAM);
+ rc = ldap_set_ssl_defaults(margs);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error while setting SSL default options for ldap server: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ ldap_unbind(ld);
+ ld = NULL;
+ return NULL;
+ }
+#ifdef HAVE_OPENLDAP
+ /*
+ * Use tls if possible
+ */
+ rc = ldap_start_tls_s(ld, NULL, NULL);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error while setting start_tls for ldap server: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ ldap_unbind(ld);
+ ld = NULL;
+ url = (LDAPURLDesc *) xmalloc(sizeof(*url));
+ memset(url, 0, sizeof(*url));
+#ifdef HAVE_LDAP_URL_LUD_SCHEME
+ url->lud_scheme = (char *) "ldaps";
+#endif
+ url->lud_host = host;
+ url->lud_port = port;
+#ifdef HAVE_LDAP_SCOPE_DEFAULT
+ url->lud_scope = LDAP_SCOPE_DEFAULT;
+#else
+ url->lud_scope = LDAP_SCOPE_SUBTREE;
+#endif
+#ifdef HAVE_LDAP_URL_DESC2STR
+ ldapuri = ldap_url_desc2str(url);
+#elif defined(HAVE_LDAP_URL_PARSE)
+ rc = ldap_url_parse(ldapuri, &url);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error while parsing url: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ if (ldapuri)
+ xfree(ldapuri);
+ if (url)
+ xfree(url);
+ return NULL;
+ }
+#else
+#error "No URL parsing function"
+#endif
+ if (url) {
+ xfree(url);
+ url = NULL;
+ }
+ rc = ldap_initialize(&ld, ldapuri);
+ if (ldapuri)
+ xfree(ldapuri);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error while initialising connection to ldap server: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ ldap_unbind(ld);
+ ld = NULL;
+ return NULL;
+ }
+ rc = ldap_set_defaults(margs, ld);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error while setting default options for ldap server: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ ldap_unbind(ld);
+ ld = NULL;
+ return NULL;
+ }
+ }
+#elif defined(HAVE_LDAPSSL_CLIENT_INIT)
+ ld = ldapssl_init(host, port, 1);
+ if (!ld) {
+ error((char *) "%s| %s: ERROR: Error while setting SSL for ldap server: %s\n", LogTime(), PROGRAM, ldapssl_err2string(rc));
+ ldap_unbind(ld);
+ ld = NULL;
+ return NULL;
+ }
+ rc = ldap_set_defaults(margs, ld);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error while setting default options for ldap server: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ ldap_unbind(ld);
+ ld = NULL;
+ return NULL;
+ }
+#else
+ error((char *) "%s| %s: ERROR: SSL not supported by ldap library\n", LogTime(), PROGRAM);
+#endif
+ }
+ return ld;
+}
+
+/*
+ * ldap calls to get attribute from Ldap Directory Server
+ */
+int
+get_memberof(struct main_args *margs, char *user, char *domain, char *group)
+{
+ LDAP *ld = NULL;
+ LDAPMessage *res;
+#ifndef HAVE_SUN_LDAP_SDK
+ int ldap_debug = 0;
+#endif
+ struct ldap_creds *lcreds = NULL;
+ char *bindp = NULL;
+ char *filter = NULL;
+ char *search_exp;
+ struct timeval searchtime;
+ int i, j, rc = 0, kc = 1;
+ int retval;
+ char **attr_value = NULL;
+ char *av = NULL, *avp = NULL;
+ int max_attr = 0;
+ struct hstruct *hlist = NULL;
+ int nhosts = 0;
+ char *hostname;
+ char *host;
+ int port;
+ char *ssl = NULL;
+ char *p;
+ char *ldap_filter_esc = NULL;
+
+
+ searchtime.tv_sec = SEARCH_TIMEOUT;
+ searchtime.tv_usec = 0;
+ /*
+ * Fill Kerberos memory cache with credential from keytab for SASL/GSSAPI
+ */
+ if (domain) {
+ debug((char *) "%s| %s: DEBUG: Setup Kerberos credential cache\n", LogTime(), PROGRAM);
+
+ kc = krb5_create_cache(margs, domain);
+ if (kc) {
+ error((char *) "%s| %s: ERROR: Error during setup of Kerberos credential cache\n", LogTime(), PROGRAM);
+ }
+ }
+ if (kc && (!margs->lurl || !margs->luser | !margs->lpass)) {
+ /*
+ * If Kerberos fails and no url given exit here
+ */
+ retval = 0;
+ goto cleanup;
+ }
+#ifndef HAVE_SUN_LDAP_SDK
+ /*
+ * Initialise ldap
+ */
+ ldap_debug = 127 /* LDAP_DEBUG_TRACE */ ;
+ ldap_debug = -1 /* LDAP_DEBUG_ANY */ ;
+ ldap_debug = 0;
+ (void) ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, &ldap_debug);
+#endif
+ debug((char *) "%s| %s: DEBUG: Initialise ldap connection\n", LogTime(), PROGRAM);
+
+ if (domain && !kc) {
+ if (margs->ssl) {
+ debug((char *) "%s| %s: DEBUG: Enable SSL to ldap servers\n", LogTime(), PROGRAM);
+ }
+ debug((char *) "%s| %s: DEBUG: Canonicalise ldap server name for domain %s\n", LogTime(), PROGRAM, domain);
+ /*
+ * Loop over list of ldap servers of users domain
+ */
+ nhosts = get_ldap_hostname_list(margs, &hlist, 0, domain);
+ for (i = 0; i < nhosts; i++) {
+ port = 389;
+ if (hlist[i].port != -1)
+ port = hlist[i].port;
+ debug((char *) "%s| %s: DEBUG: Setting up connection to ldap server %s:%d\n", LogTime(), PROGRAM, hlist[i].host, port);
+
+ ld = tool_ldap_open(margs, hlist[i].host, port, margs->ssl);
+ if (!ld)
+ continue;
+
+ /*
+ * ldap bind with SASL/GSSAPI authentication (only possible if a domain was part of the username)
+ */
+
+#if defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H) || defined(HAVE_SASL_DARWIN)
+ debug((char *) "%s| %s: DEBUG: Bind to ldap server with SASL/GSSAPI\n", LogTime(), PROGRAM);
+
+ rc = tool_sasl_bind(ld, bindp, margs->ssl);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error while binding to ldap server with SASL/GSSAPI: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ ldap_unbind(ld);
+ ld = NULL;
+ continue;
+ }
+ lcreds = (ldap_creds *) xmalloc(sizeof(struct ldap_creds));
+ lcreds->dn = bindp ? xstrdup(bindp) : NULL;
+ lcreds->pw = margs->ssl ? xstrdup(margs->ssl) : NULL;
+ ldap_set_rebind_proc(ld, ldap_sasl_rebind, (char *) lcreds);
+ if (ld != NULL) {
+ debug((char *) "%s| %s: DEBUG: %s initialised %sconnection to ldap server %s:%d\n", LogTime(), PROGRAM, ld ? "Successfully" : "Failed to", margs->ssl ? "SSL protected " : "", hlist[i].host, port);
+ break;
+ }
+#else
+ ldap_unbind(ld);
+ ld = NULL;
+ error((char *) "%s| %s: ERROR: SASL not supported on system\n", LogTime(), PROGRAM);
+ continue;
+#endif
+ }
+ nhosts = free_hostname_list(&hlist, nhosts);
+ if (ld == NULL) {
+ debug((char *) "%s| %s: DEBUG: Error during initialisation of ldap connection: %s\n", LogTime(), PROGRAM, strerror(errno));
+ }
+ bindp = convert_domain_to_bind_path(domain);
+ }
+ if ((!domain || !ld) && margs->lurl && strstr(margs->lurl, "://")) {
+ /*
+ * If username does not contain a domain and a url was given then try it
+ */
+ hostname = strstr(margs->lurl, "://") + 3;
+ ssl = strstr(margs->lurl, "ldaps://");
+ if (ssl) {
+ debug((char *) "%s| %s: DEBUG: Enable SSL to ldap servers\n", LogTime(), PROGRAM);
+ }
+ debug((char *) "%s| %s: DEBUG: Canonicalise ldap server name %s\n", LogTime(), PROGRAM, hostname);
+ /*
+ * Loop over list of ldap servers
+ */
+ host = xstrdup(hostname);
+ port = 389;
+ if ((p = strchr(host, ':'))) {
+ *p = '\0';
+ p++;
+ port = atoi(p);
+ }
+ nhosts = get_hostname_list(margs, &hlist, 0, host);
+ if (host)
+ xfree(host);
+ host = NULL;
+ for (i = 0; i < nhosts; i++) {
+
+ ld = tool_ldap_open(margs, hlist[i].host, port, ssl);
+ if (!ld)
+ continue;
+ /*
+ * ldap bind with username/password authentication
+ */
+
+ debug((char *) "%s| %s: DEBUG: Bind to ldap server with Username/Password\n", LogTime(), PROGRAM);
+ rc = ldap_simple_bind_s(ld, margs->luser, margs->lpass);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error while binding to ldap server with Username/Password: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ ldap_unbind(ld);
+ ld = NULL;
+ continue;
+ }
+ lcreds = (ldap_creds *) xmalloc(sizeof(struct ldap_creds));
+ lcreds->dn = xstrdup(margs->luser);
+ lcreds->pw = xstrdup(margs->lpass);
+ ldap_set_rebind_proc(ld, ldap_simple_rebind, (char *) lcreds);
+ debug((char *) "%s| %s: DEBUG: %s set up %sconnection to ldap server %s:%d\n", LogTime(), PROGRAM, ld ? "Successfully" : "Failed to", ssl ? "SSL protected " : "", hlist[i].host, port);
+ break;
+
+ }
+ nhosts = free_hostname_list(&hlist, nhosts);
+ if (bindp)
+ xfree(bindp);
+ if (margs->lbind) {
+ bindp = xstrdup(margs->lbind);
+ } else {
+ bindp = convert_domain_to_bind_path(domain);
+ }
+ }
+ if (ld == NULL) {
+ debug((char *) "%s| %s: DEBUG: Error during initialisation of ldap connection: %s\n", LogTime(), PROGRAM, strerror(errno));
+ retval = 0;
+ goto cleanup;
+ }
+ /*
+ * ldap search for user
+ */
+ /*
+ * Check if server is AD by querying for attribute samaccountname
+ */
+ margs->AD = 0;
+ rc = check_AD(margs, ld);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error determining ldap server type: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ ldap_unbind(ld);
+ ld = NULL;
+ retval = 0;
+ goto cleanup;
+ }
+ if (margs->AD)
+ filter = (char *) FILTER_AD;
+ else
+ filter = (char *) FILTER;
+
+ ldap_filter_esc = escape_filter(user);
+
+ search_exp = (char *) xmalloc(strlen(filter) + strlen(ldap_filter_esc) + 1);
+ snprintf(search_exp, strlen(filter) + strlen(ldap_filter_esc) + 1, filter, ldap_filter_esc);
+
+ if (ldap_filter_esc)
+ xfree(ldap_filter_esc);
+
+ debug((char *) "%s| %s: DEBUG: Search ldap server with bind path %s and filter : %s\n", LogTime(), PROGRAM, bindp, search_exp);
+ rc = ldap_search_ext_s(ld, bindp, LDAP_SCOPE_SUBTREE,
+ search_exp, NULL, 0,
+ NULL, NULL, &searchtime, 0, &res);
+ if (search_exp)
+ xfree(search_exp);
+
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error searching ldap server: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ ldap_unbind(ld);
+ ld = NULL;
+ retval = 0;
+ goto cleanup;
+ }
+ debug((char *) "%s| %s: DEBUG: Found %d ldap entr%s\n", LogTime(), PROGRAM, ldap_count_entries(ld, res), ldap_count_entries(ld, res) > 1 || ldap_count_entries(ld, res) == 0 ? "ies" : "y");
+
+ if (ldap_count_entries(ld, res) != 0) {
+
+ if (margs->AD)
+ max_attr = get_attributes(margs, ld, res, ATTRIBUTE_AD, &attr_value);
+ else {
+ max_attr = get_attributes(margs, ld, res, ATTRIBUTE, &attr_value);
+ }
+
+ /*
+ * Compare group names
+ */
+ retval = 0;
+ for (j = 0; j < max_attr; j++) {
+
+ /* Compare first CN= value assuming it is the same as the group name itself */
+ av = attr_value[j];
+ if (!strncasecmp("CN=", av, 3)) {
+ av += 3;
+ if ((avp = strchr(av, ','))) {
+ *avp = '\0';
+ }
+ }
+ if (debug_enabled) {
+ int n;
+ debug((char *) "%s| %s: DEBUG: Entry %d \"%s\" in hex UTF-8 is ", LogTime(), PROGRAM, j + 1, av);
+ for (n = 0; av[n] != '\0'; n++)
+ fprintf(stderr, "%02x", (unsigned char) av[n]);
+ fprintf(stderr, "\n");
+ }
+ if (!strcasecmp(group, av)) {
+ retval = 1;
+ if (debug_enabled)
+ debug((char *) "%s| %s: DEBUG: Entry %d \"%s\" matches group name \"%s\"\n", LogTime(), PROGRAM, j + 1, av, group);
+ else
+ break;
+ } else
+ debug((char *) "%s| %s: DEBUG: Entry %d \"%s\" does not match group name \"%s\"\n", LogTime(), PROGRAM, j + 1, av, group);
+ }
+ /*
+ * Do recursive group search for AD only since posixgroups can not contain other groups
+ */
+ if (!retval && margs->AD) {
+ if (debug_enabled && max_attr > 0) {
+ debug((char *) "%s| %s: DEBUG: Perform recursive group search\n", LogTime(), PROGRAM);
+ }
+ for (j = 0; j < max_attr; j++) {
+
+ av = attr_value[j];
+ if (search_group_tree(margs, ld, bindp, av, group, 1)) {
+ retval = 1;
+ if (!strncasecmp("CN=", av, 3)) {
+ av += 3;
+ if ((avp = strchr(av, ','))) {
+ *avp = '\0';
+ }
+ }
+ if (debug_enabled)
+ debug((char *) "%s| %s: DEBUG: Entry %d group \"%s\" is (in)direct member of group \"%s\"\n", LogTime(), PROGRAM, j + 1, av, group);
+ else
+ break;
+ }
+ }
+ }
+ /*
+ * Cleanup
+ */
+ if (attr_value) {
+ for (j = 0; j < max_attr; j++) {
+ xfree(attr_value[j]);
+ }
+ xfree(attr_value);
+ attr_value = NULL;
+ }
+ ldap_msgfree(res);
+ } else if (ldap_count_entries(ld, res) == 0 && margs->AD) {
+ ldap_msgfree(res);
+ ldap_unbind(ld);
+ ld = NULL;
+ retval = 0;
+ goto cleanup;
+ } else {
+ ldap_msgfree(res);
+ retval = 0;
+ }
+
+ if (!margs->AD && retval == 0) {
+ /*
+ * Check for primary Group membership
+ */
+ debug((char *) "%s| %s: DEBUG: Search for primary group membership: \"%s\"\n", LogTime(), PROGRAM, group);
+ filter = (char *) FILTER_UID;
+
+ ldap_filter_esc = escape_filter(user);
+
+ search_exp = (char *) xmalloc(strlen(filter) + strlen(ldap_filter_esc) + 1);
+ snprintf(search_exp, strlen(filter) + strlen(ldap_filter_esc) + 1, filter, ldap_filter_esc);
+
+ if (ldap_filter_esc)
+ xfree(ldap_filter_esc);
+
+ debug((char *) "%s| %s: DEBUG: Search ldap server with bind path %s and filter: %s\n", LogTime(), PROGRAM, bindp, search_exp);
+ rc = ldap_search_ext_s(ld, bindp, LDAP_SCOPE_SUBTREE,
+ search_exp, NULL, 0,
+ NULL, NULL, &searchtime, 0, &res);
+ if (search_exp)
+ xfree(search_exp);
+
+ debug((char *) "%s| %s: DEBUG: Found %d ldap entr%s\n", LogTime(), PROGRAM, ldap_count_entries(ld, res), ldap_count_entries(ld, res) > 1 || ldap_count_entries(ld, res) == 0 ? "ies" : "y");
+
+ max_attr = get_attributes(margs, ld, res, ATTRIBUTE_GID, &attr_value);
+
+ if (max_attr == 1) {
+ char **attr_value_2 = NULL;
+ int max_attr_2 = 0;
+
+ ldap_msgfree(res);
+ filter = (char *) FILTER_GID;
+
+ ldap_filter_esc = escape_filter(attr_value[0]);
+
+ search_exp = (char *) xmalloc(strlen(filter) + strlen(ldap_filter_esc) + 1);
+ snprintf(search_exp, strlen(filter) + strlen(ldap_filter_esc) + 1, filter, ldap_filter_esc);
+
+ if (ldap_filter_esc)
+ xfree(ldap_filter_esc);
+
+ debug((char *) "%s| %s: DEBUG: Search ldap server with bind path %s and filter: %s\n", LogTime(), PROGRAM, bindp, search_exp);
+ rc = ldap_search_ext_s(ld, bindp, LDAP_SCOPE_SUBTREE,
+ search_exp, NULL, 0,
+ NULL, NULL, &searchtime, 0, &res);
+ if (search_exp)
+ xfree(search_exp);
+
+ max_attr_2 = get_attributes(margs, ld, res, ATTRIBUTE, &attr_value_2);
+ /*
+ * Compare group names
+ */
+ retval = 0;
+ if (max_attr_2 == 1) {
+
+ /* Compare first CN= value assuming it is the same as the group name itself */
+ av = attr_value_2[0];
+ if (!strcasecmp(group, av)) {
+ retval = 1;
+ debug((char *) "%s| %s: DEBUG: \"%s\" matches group name \"%s\"\n", LogTime(), PROGRAM, av, group);
+ } else
+ debug((char *) "%s| %s: DEBUG: \"%s\" does not match group name \"%s\"\n", LogTime(), PROGRAM, av, group);
+
+ }
+ /*
+ * Cleanup
+ */
+ if (attr_value_2) {
+ for (j = 0; j < max_attr_2; j++) {
+ xfree(attr_value_2[j]);
+ }
+ xfree(attr_value_2);
+ attr_value_2 = NULL;
+ }
+ ldap_msgfree(res);
+
+ debug((char *) "%s| %s: DEBUG: Users primary group %s %s\n", LogTime(), PROGRAM, retval ? "matches" : "does not match", group);
+
+ } else
+ debug((char *) "%s| %s: DEBUG: Did not find ldap entry for group %s\n", LogTime(), PROGRAM, group);
+ /*
+ * Cleanup
+ */
+ if (attr_value) {
+ for (j = 0; j < max_attr; j++) {
+ xfree(attr_value[j]);
+ }
+ xfree(attr_value);
+ attr_value = NULL;
+ }
+ }
+ rc = ldap_unbind(ld);
+ ld = NULL;
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Error unbind ldap server: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ }
+ debug((char *) "%s| %s: DEBUG: Unbind ldap server\n", LogTime(), PROGRAM);
+ cleanup:
+ if (domain)
+ krb5_cleanup();
+ if (lcreds) {
+ if (lcreds->dn)
+ xfree(lcreds->dn);
+ if (lcreds->pw)
+ xfree(lcreds->pw);
+ xfree(lcreds);
+ }
+ if (bindp)
+ xfree(bindp);
+ bindp = NULL;
+ return (retval);
+
+}
+#endif
--- /dev/null
+/*
+ * -----------------------------------------------------------------------------
+ *
+ * Author: Markus Moeller (markus_moeller at compuserve.com)
+ *
+ * Copyright (C) 2007 Markus Moeller. All rights reserved.
+ *
+ * This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#include "config.h"
+
+#ifdef HAVE_LDAP
+
+#include "support.h"
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+const char *
+LogTime()
+{
+ struct tm *tm;
+ struct timeval now;
+ static time_t last_t = 0;
+ static char buf[128];
+
+ gettimeofday(&now, NULL);
+ if (now.tv_sec != last_t) {
+ tm = localtime(&now.tv_sec);
+ strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
+ last_t = now.tv_sec;
+ }
+ return buf;
+}
+/* default off */
+int log_enabled = 0;
+
+#ifndef __GNUC__
+/* under gcc a macro define in compat/debug.h is used instead */
+
+void
+log(char *format,...)
+{
+ if (!log_enabled)
+ return;
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+}
+
+void
+error(char *format,...)
+{
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+}
+
+void
+warn(char *format,...)
+{
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+}
+
+#endif /* __GNUC__ */
+#endif
--- /dev/null
+/*
+ * -----------------------------------------------------------------------------
+ *
+ * Author: Markus Moeller (markus_moeller at compuserve.com)
+ *
+ * Copyright (C) 2007 Markus Moeller. All rights reserved.
+ *
+ * This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#include "config.h"
+#include "util.h"
+
+#ifdef HAVE_LDAP
+
+#include "support.h"
+
+int
+check_memberof(struct main_args *margs, char *user, char *domain)
+{
+
+ /*
+ * Check order:
+ *
+ * 1. Check domain against list of groups per domain
+ * 1a. If domain does not exist in list try default domain
+ * 1b. If default domain does not exist use default group against ldap url with user/password
+ * 1c. If default group does not exist exit with error.
+ * 2. Query ldap membership
+ * 2a. Use GSSAPI/SASL with HTTP/fqdn@DOMAIN credentials from keytab
+ * 2b. Use username/password with TLS
+ *
+ */
+ struct gdstruct *gr;
+ int found = 0;
+
+
+ /* Check users domain */
+
+ gr = margs->groups;
+ while (gr && domain) {
+ debug((char *) "%s| %s: DEBUG: User domain loop: group@domain %s@%s\n", LogTime(), PROGRAM, gr->group, gr->domain ? gr->domain : "NULL");
+ if (gr->domain && !strcasecmp(gr->domain, domain)) {
+ debug((char *) "%s| %s: DEBUG: Found group@domain %s@%s\n", LogTime(), PROGRAM, gr->group, gr->domain);
+ /* query ldap */
+ if (get_memberof(margs, user, domain, gr->group)) {
+ if (debug_enabled)
+ debug((char *) "%s| %s: INFO: User %s is member of group@domain %s@%s\n", LogTime(), PROGRAM, user, gr->group, gr->domain);
+ else
+ log((char *) "%s| %s: INFO: User %s is member of group@domain %s@%s\n", LogTime(), PROGRAM, user, gr->group, gr->domain);
+ found++;
+ break;
+ } else {
+ if (debug_enabled)
+ debug((char *) "%s| %s: INFO: User %s is not member of group@domain %s@%s\n", LogTime(), PROGRAM, user, gr->group, gr->domain);
+ else
+ log((char *) "%s| %s: INFO: User %s is not member of group@domain %s@%s\n", LogTime(), PROGRAM, user, gr->group, gr->domain);
+ }
+ }
+ gr = gr->next;
+ }
+
+ if (found)
+ return (1);
+
+ /* Check default domain */
+
+ gr = margs->groups;
+ while (gr && domain) {
+ debug((char *) "%s| %s: DEBUG: Default domain loop: group@domain %s@%s\n", LogTime(), PROGRAM, gr->group, gr->domain ? gr->domain : "NULL");
+ if (gr->domain && !strcasecmp(gr->domain, "")) {
+ debug((char *) "%s| %s: DEBUG: Found group@domain %s@%s\n", LogTime(), PROGRAM, gr->group, gr->domain);
+ /* query ldap */
+ if (get_memberof(margs, user, domain, gr->group)) {
+ if (debug_enabled)
+ debug((char *) "%s| %s: INFO: User %s is member of group@domain %s@%s\n", LogTime(), PROGRAM, user, gr->group, gr->domain);
+ else
+ log((char *) "%s| %s: INFO: User %s is member of group@domain %s@%s\n", LogTime(), PROGRAM, user, gr->group, gr->domain);
+ found++;
+ break;
+ } else {
+ if (debug_enabled)
+ debug((char *) "%s| %s: INFO: User %s is not member of group@domain %s@%s\n", LogTime(), PROGRAM, user, gr->group, gr->domain);
+ else
+ log((char *) "%s| %s: INFO: User %s is not member of group@domain %s@%s\n", LogTime(), PROGRAM, user, gr->group, gr->domain);
+ }
+ }
+ gr = gr->next;
+ }
+
+ if (found)
+ return (1);
+
+ /* Check default group with ldap url */
+
+ gr = margs->groups;
+ while (gr) {
+ debug((char *) "%s| %s: DEBUG: Default group loop: group@domain %s@%s\n", LogTime(), PROGRAM, gr->group, gr->domain ? gr->domain : "NULL");
+ if (!gr->domain) {
+ debug((char *) "%s| %s: DEBUG: Found group@domain %s@%s\n", LogTime(), PROGRAM, gr->group, gr->domain ? gr->domain : "NULL");
+ /* query ldap */
+ if (get_memberof(margs, user, domain, gr->group)) {
+ if (debug_enabled)
+ debug((char *) "%s| %s: INFO: User %s is member of group@domain %s@%s\n", LogTime(), PROGRAM, user, gr->group, gr->domain ? gr->domain : "NULL");
+ else
+ log((char *) "%s| %s: INFO: User %s is member of group@domain %s@%s\n", LogTime(), PROGRAM, user, gr->group, gr->domain ? gr->domain : "NULL");
+ found++;
+ break;
+ } else {
+ if (debug_enabled)
+ debug((char *) "%s| %s: INFO: User %s is not member of group@domain %s@%s\n", LogTime(), PROGRAM, user, gr->group, gr->domain ? gr->domain : "NULL");
+ else
+ log((char *) "%s| %s: INFO: User %s is not member of group@domain %s@%s\n", LogTime(), PROGRAM, user, gr->group, gr->domain ? gr->domain : "NULL");
+ }
+ }
+ gr = gr->next;
+ }
+
+ if (found)
+ return (1);
+
+ return (0);
+}
+#endif
--- /dev/null
+/*
+ * -----------------------------------------------------------------------------
+ *
+ * Author: Markus Moeller (markus_moeller at compuserve.com)
+ *
+ * Copyright (C) 2007 Markus Moeller. All rights reserved.
+ *
+ * This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#include "config.h"
+#include "util.h"
+
+#ifdef HAVE_LDAP
+
+#include "support.h"
+
+struct ndstruct *init_nd(void);
+
+struct ndstruct *
+init_nd(void)
+{
+ struct ndstruct *ndsp;
+ ndsp = (struct ndstruct *) xmalloc(sizeof(struct ndstruct));
+ ndsp->netbios = NULL;
+ ndsp->domain = NULL;
+ ndsp->next = NULL;
+ return ndsp;
+}
+
+int
+create_nd(struct main_args *margs)
+{
+ char *np, *dp;
+ char *p;
+ struct ndstruct *ndsp = NULL, *ndspn = NULL;
+ /*
+ * netbios list format:
+ *
+ * nlist=Pattern1[:Pattern2]
+ *
+ * Pattern=NetbiosName@Domain Netbios Name for a specific Kerberos domain
+ * ndstruct.domain=Domain, ndstruct.netbios=NetbiosName
+ *
+ *
+ */
+ p = margs->nlist;
+ np = margs->nlist;
+ debug((char *) "%s| %s: DEBUG: Netbios list %s\n", LogTime(), PROGRAM, margs->nlist ? margs->nlist : "NULL");
+ dp = NULL;
+
+ if (!p) {
+ debug((char *) "%s| %s: DEBUG: No netbios names defined.\n", LogTime(), PROGRAM);
+ return (0);
+ }
+ while (*p) { /* loop over group list */
+ if (*p == '\n' || *p == '\r') { /* Ignore CR and LF if exist */
+ p++;
+ continue;
+ }
+ if (*p == '@') { /* end of group name - start of domain name */
+ if (p == np) { /* empty group name not allowed */
+ debug((char *) "%s| %s: DEBUG: No netbios name defined for domain %s\n", LogTime(), PROGRAM, p);
+ return (1);
+ }
+ *p = '\0';
+ p++;
+ ndsp = init_nd();
+ ndsp->netbios = xstrdup(np);
+ if (ndspn) /* Have already an existing structure */
+ ndsp->next = ndspn;
+ dp = p; /* after @ starts new domain name */
+ } else if (*p == ':') { /* end of group name or end of domain name */
+ if (p == np) { /* empty group name not allowed */
+ debug((char *) "%s| %s: DEBUG: No netbios name defined for domain %s\n", LogTime(), PROGRAM, p);
+ return (1);
+ }
+ *p = '\0';
+ p++;
+ if (dp) { /* end of domain name */
+ ndsp->domain = xstrdup(dp);
+ dp = NULL;
+ } else { /* end of group name and no domain name */
+ ndsp = init_nd();
+ ndsp->netbios = xstrdup(np);
+ if (ndspn) /* Have already an existing structure */
+ ndsp->next = ndspn;
+ }
+ ndspn = ndsp;
+ np = p; /* after : starts new group name */
+ if (!ndsp->domain || !strcmp(ndsp->domain, "")) {
+ debug((char *) "%s| %s: DEBUG: No domain defined for netbios name %s\n", LogTime(), PROGRAM, ndsp->netbios);
+ return (1);
+ }
+ debug((char *) "%s| %s: DEBUG: Netbios name %s Domain %s\n", LogTime(), PROGRAM, ndsp->netbios, ndsp->domain);
+ } else
+ p++;
+ }
+ if (p == np) { /* empty group name not allowed */
+ debug((char *) "%s| %s: DEBUG: No netbios name defined for domain %s\n", LogTime(), PROGRAM, p);
+ return (1);
+ }
+ if (dp) { /* end of domain name */
+ ndsp->domain = xstrdup(dp);
+ } else { /* end of group name and no domain name */
+ ndsp = init_nd();
+ ndsp->netbios = xstrdup(np);
+ if (ndspn) /* Have already an existing structure */
+ ndsp->next = ndspn;
+ }
+ if (!ndsp->domain || !strcmp(ndsp->domain, "")) {
+ debug((char *) "%s| %s: DEBUG: No domain defined for netbios name %s\n", LogTime(), PROGRAM, ndsp->netbios);
+ return (1);
+ }
+ debug((char *) "%s| %s: DEBUG: Netbios name %s Domain %s\n", LogTime(), PROGRAM, ndsp->netbios, ndsp->domain);
+
+ margs->ndoms = ndsp;
+ return (0);
+}
+
+char *
+get_netbios_name(struct main_args *margs, char *netbios)
+{
+ struct ndstruct *nd;
+
+ nd = margs->ndoms;
+ while (nd && netbios) {
+ debug((char *) "%s| %s: DEBUG: Netbios domain loop: netbios@domain %s@%s\n", LogTime(), PROGRAM, nd->netbios, nd->domain);
+ if (nd->netbios && !strcasecmp(nd->netbios, netbios)) {
+ debug((char *) "%s| %s: DEBUG: Found netbios@domain %s@%s\n", LogTime(), PROGRAM, nd->netbios, nd->domain);
+ return (nd->domain);
+ }
+ nd = nd->next;
+ }
+
+ return NULL;
+}
+#endif
--- /dev/null
+/*
+ * -----------------------------------------------------------------------------
+ *
+ * Author: Markus Moeller (markus_moeller at compuserve.com)
+ *
+ * Copyright (C) 2007 Markus Moeller. All rights reserved.
+ *
+ * This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#include "config.h"
+#include "util.h"
+
+#ifdef HAVE_LDAP
+
+#include "support.h"
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+
+void nsError(int nserror, char *server);
+static int compare_hosts(struct hstruct *h1, struct hstruct *h2);
+static void swap(struct hstruct *a, struct hstruct *b);
+static void sort(struct hstruct *array, int nitems, int (*cmp) (struct hstruct *, struct hstruct *), int begin, int end);
+static void msort(struct hstruct *array, size_t nitems, int (*cmp) (struct hstruct *, struct hstruct *));
+
+/*
+ * See http://www.ietf.org/rfc/rfc1035.txt
+ */
+/*
+ * See http://www.ietf.org/rfc/rfc2782.txt
+ *
+ */
+void
+nsError(int nserror, char *service)
+{
+ switch (nserror) {
+ case HOST_NOT_FOUND:
+ error((char *) "%s| %s: ERROR: res_search: Unknown service record: %s\n", LogTime(), PROGRAM, service);
+ break;
+ case NO_DATA:
+ error((char *) "%s| %s: ERROR: res_search: No SRV record for %s\n", LogTime(), PROGRAM, service);
+ break;
+ case TRY_AGAIN:
+ error((char *) "%s| %s: ERROR: res_search: No response for SRV query\n", LogTime(), PROGRAM);
+ break;
+ default:
+ error((char *) "%s| %s: ERROR: res_search: Unexpected error: %s\n", LogTime(), PROGRAM, strerror(nserror));
+ }
+}
+
+static void
+swap(struct hstruct *a, struct hstruct *b)
+{
+ struct hstruct c;
+
+ c.host = a->host;
+ c.priority = a->priority;
+ c.weight = a->weight;
+ a->priority = b->priority;
+ a->weight = b->weight;
+ b->host = c.host;
+ b->priority = c.priority;
+ b->weight = c.weight;
+}
+
+static void
+sort(struct hstruct *array, int nitems, int (*cmp) (struct hstruct *, struct hstruct *), int begin, int end)
+{
+ if (end > begin) {
+ int pivot = begin;
+ int l = begin + 1;
+ int r = end;
+ while (l < r) {
+ if (cmp(&array[l], &array[pivot]) <= 0) {
+ l += 1;
+ } else {
+ r -= 1;
+ swap(&array[l], &array[r]);
+ }
+ }
+ l -= 1;
+ swap(&array[begin], &array[l]);
+ sort(array, nitems, cmp, begin, l);
+ sort(array, nitems, cmp, r, end);
+ }
+}
+
+static void
+msort(struct hstruct *array, size_t nitems, int (*cmp) (struct hstruct *, struct hstruct *))
+{
+ sort(array, nitems, cmp, 0, nitems - 1);
+}
+
+static int
+compare_hosts(struct hstruct *host1, struct hstruct *host2)
+{
+ /*
+ *
+ * The comparison function must return an integer less than, equal to,
+ * or greater than zero if the first argument is considered to be
+ * respectively less than, equal to, or greater than the second.
+ */
+ if ((host1->priority < host2->priority) && (host1->priority != -1))
+ return -1;
+ if ((host1->priority < host2->priority) && (host1->priority == -1))
+ return 1;
+ if ((host1->priority > host2->priority) && (host2->priority != -1))
+ return 1;
+ if ((host1->priority > host2->priority) && (host2->priority == -1))
+ return -1;
+ if (host1->priority == host2->priority) {
+ if (host1->weight > host2->weight)
+ return -1;
+ if (host1->weight < host2->weight)
+ return 1;
+ }
+ return 0;
+}
+
+int
+free_hostname_list(struct hstruct **hlist, int nhosts)
+{
+ struct hstruct *hp = NULL;
+ int i;
+
+ hp = *hlist;
+ for (i = 0; i < nhosts; i++) {
+ if (hp[i].host)
+ xfree(hp[i].host);
+ hp[i].host = NULL;
+ }
+
+
+ if (hp)
+ xfree(hp);
+ hp = NULL;
+ *hlist = hp;
+ return 0;
+}
+
+int
+get_hostname_list(struct main_args *margs, struct hstruct **hlist, int nhosts, char *name)
+{
+ /*
+ * char host[sysconf(_SC_HOST_NAME_MAX)];
+ */
+ char host[1024];
+ struct addrinfo *hres = NULL, *hres_list;
+ int rc, count;
+ struct hstruct *hp = NULL;
+
+ if (!name)
+ return (nhosts);
+
+ hp = *hlist;
+ rc = getaddrinfo((const char *) name, NULL, NULL, &hres);
+ if (rc != 0) {
+ error((char *) "%s| %s: ERROR: Error while resolving hostname with getaddrinfo: %s\n", LogTime(), PROGRAM, gai_strerror(rc));
+ return (nhosts);
+ }
+ hres_list = hres;
+ count = 0;
+ while (hres_list) {
+ count++;
+ hres_list = hres_list->ai_next;
+ }
+ hres_list = hres;
+ count = 0;
+ while (hres_list) {
+ rc = getnameinfo(hres_list->ai_addr, hres_list->ai_addrlen, host, sizeof(host), NULL, 0, 0);
+ if (rc != 0) {
+ error((char *) "%s| %s: ERROR: Error while resolving ip address with getnameinfo: %s\n", LogTime(), PROGRAM, gai_strerror(rc));
+ freeaddrinfo(hres);
+ *hlist = hp;
+ return (nhosts);
+ }
+ count++;
+ debug((char *) "%s| %s: DEBUG: Resolved address %d of %s to %s\n", LogTime(), PROGRAM, count, name, host);
+
+ hp = (struct hstruct *) xrealloc(hp, sizeof(struct hstruct) * (nhosts + 1));
+ hp[nhosts].host = xstrdup(host);
+ hp[nhosts].port = -1;
+ hp[nhosts].priority = -1;
+ hp[nhosts].weight = -1;
+ nhosts++;
+
+ hres_list = hres_list->ai_next;
+ }
+
+ freeaddrinfo(hres);
+ *hlist = hp;
+ return (nhosts);
+}
+
+int
+get_ldap_hostname_list(struct main_args *margs, struct hstruct **hlist, int nh, char *domain)
+{
+
+ /*
+ * char name[sysconf(_SC_HOST_NAME_MAX)];
+ */
+ char name[1024];
+ char host[NS_MAXDNAME];
+ char *service;
+ struct hstruct *hp = NULL;
+ int nhosts = 0;
+ int size;
+ int type, rdlength;
+ int priority, weight, port;
+ int len, olen;
+ int i, j, k;
+ u_char *buffer;
+ u_char *p;
+
+ if (margs->ssl) {
+ service = (char *) xmalloc(strlen("_ldaps._tcp.") + strlen(domain) + 1);
+ strcpy(service, "_ldaps._tcp.");
+ } else {
+ service = (char *) xmalloc(strlen("_ldap._tcp.") + strlen(domain) + 1);
+ strcpy(service, "_ldap._tcp.");
+ }
+ strcat(service, domain);
+
+#ifndef PACKETSZ_MULT
+/*
+ * It seems Solaris doesn't give back the real length back when res_search uses a to small buffer
+ * Set a bigger one here
+ */
+#define PACKETSZ_MULT 10
+#endif
+
+ hp = *hlist;
+ buffer = (u_char *) xmalloc(PACKETSZ_MULT * NS_PACKETSZ);
+ if ((len = res_search(service, ns_c_in, ns_t_srv, (u_char *) buffer, PACKETSZ_MULT * NS_PACKETSZ)) < 0) {
+ error((char *) "%s| %s: ERROR: Error while resolving service record %s with res_search\n", LogTime(), PROGRAM, service);
+ nsError(h_errno, service);
+ if (margs->ssl) {
+ xfree(service);
+ service = (char *) xmalloc(strlen("_ldap._tcp.") + strlen(domain) + 1);
+ strcpy(service, "_ldap._tcp.");
+ strcat(service, domain);
+ if ((len = res_search(service, ns_c_in, ns_t_srv, (u_char *) buffer, PACKETSZ_MULT * NS_PACKETSZ)) < 0) {
+ error((char *) "%s| %s: ERROR: Error while resolving service record %s with res_search\n", LogTime(), PROGRAM, service);
+ nsError(h_errno, service);
+ goto cleanup;
+ }
+ } else {
+ goto cleanup;
+ }
+ }
+ if (len > PACKETSZ_MULT * NS_PACKETSZ) {
+ olen = len;
+ buffer = (u_char *) xrealloc(buffer, len);
+ if ((len = res_search(service, ns_c_in, ns_t_srv, (u_char *) buffer, len)) < 0) {
+ error((char *) "%s| %s: ERROR: Error while resolving service record %s with res_search\n", LogTime(), PROGRAM, service);
+ nsError(h_errno, service);
+ goto cleanup;
+ }
+ if (len > olen) {
+ error((char *) "%s| %s: ERROR: Reply to big: buffer: %d reply length: %d\n", LogTime(), PROGRAM, olen, len);
+ goto cleanup;
+ }
+ }
+ p = buffer;
+ p += 6 * NS_INT16SZ; /* Header(6*16bit) = id + flags + 4*section count */
+ if (p > buffer + len) {
+ error((char *) "%s| %s: ERROR: Message to small: %d < header size\n", LogTime(), PROGRAM, len);
+ goto cleanup;
+ }
+ if ((size = dn_expand(buffer, buffer + len, p, name, sysconf(_SC_HOST_NAME_MAX))) < 0) {
+ error((char *) "%s| %s: ERROR: Error while expanding query name with dn_expand: %s\n", LogTime(), PROGRAM, strerror(errno));
+ goto cleanup;
+ }
+ p += size; /* Query name */
+ p += 2 * NS_INT16SZ; /* Query type + class (2*16bit) */
+ if (p > buffer + len) {
+ error((char *) "%s| %s: ERROR: Message to small: %d < header + query name,type,class \n", LogTime(), PROGRAM, len);
+ goto cleanup;
+ }
+ while (p < buffer + len) {
+ if ((size = dn_expand(buffer, buffer + len, p, name, sysconf(_SC_HOST_NAME_MAX))) < 0) {
+ error((char *) "%s| %s: ERROR: Error while expanding answer name with dn_expand: %s\n", LogTime(), PROGRAM, strerror(errno));
+ goto cleanup;
+ }
+ p += size; /* Resource Record name */
+ if (p > buffer + len) {
+ error((char *) "%s| %s: ERROR: Message to small: %d < header + query name,type,class + answer name\n", LogTime(), PROGRAM, len);
+ goto cleanup;
+ }
+ NS_GET16(type, p); /* RR type (16bit) */
+ p += NS_INT16SZ + NS_INT32SZ; /* RR class + ttl (16bit+32bit) */
+ if (p > buffer + len) {
+ error((char *) "%s| %s: ERROR: Message to small: %d < header + query name,type,class + answer name + RR type,class,ttl\n", LogTime(), PROGRAM, len);
+ goto cleanup;
+ }
+ NS_GET16(rdlength, p); /* RR data length (16bit) */
+
+ if (type == ns_t_srv) { /* SRV record */
+ if (p > buffer + len) {
+ error((char *) "%s| %s: ERROR: Message to small: %d < header + query name,type,class + answer name + RR type,class,ttl + RR data length\n", LogTime(), PROGRAM, len);
+ goto cleanup;
+ }
+ NS_GET16(priority, p); /* Priority (16bit) */
+ if (p > buffer + len) {
+ error((char *) "%s| %s: ERROR: Message to small: %d < SRV RR + priority\n", LogTime(), PROGRAM, len);
+ goto cleanup;
+ }
+ NS_GET16(weight, p); /* Weight (16bit) */
+ if (p > buffer + len) {
+ error((char *) "%s| %s: ERROR: Message to small: %d < SRV RR + priority + weight\n", LogTime(), PROGRAM, len);
+ goto cleanup;
+ }
+ NS_GET16(port, p); /* Port (16bit) */
+ if (p > buffer + len) {
+ error((char *) "%s| %s: ERROR: Message to small: %d < SRV RR + priority + weight + port\n", LogTime(), PROGRAM, len);
+ goto cleanup;
+ }
+ if ((size = dn_expand(buffer, buffer + len, p, host, NS_MAXDNAME)) < 0) {
+ error((char *) "%s| %s: ERROR: Error while expanding SRV RR name with dn_expand: %s\n", LogTime(), PROGRAM, strerror(errno));
+ goto cleanup;
+ }
+ debug((char *) "%s| %s: DEBUG: Resolved SRV %s record to %s\n", LogTime(), PROGRAM, service, host);
+ hp = (struct hstruct *) xrealloc(hp, sizeof(struct hstruct) * (nh + 1));
+ hp[nh].host = xstrdup(host);
+ hp[nh].port = port;
+ hp[nh].priority = priority;
+ hp[nh].weight = weight;
+ nh++;
+ p += size;
+ } else {
+ p += rdlength;
+ }
+ if (p > buffer + len) {
+ error((char *) "%s| %s: ERROR: Message to small: %d < SRV RR + priority + weight + port + name\n", LogTime(), PROGRAM, len);
+ goto cleanup;
+ }
+ }
+ if (p != buffer + len) {
+#if (SIZEOF_LONG == 8)
+ errror("%s| %s: ERROR: Inconsistence message length: %ld!=0\n", LogTime(), PROGRAM, buffer + len - p);
+#else
+ error((char *) "%s| %s: ERROR: Inconsistence message length: %d!=0\n", LogTime(), PROGRAM, buffer + len - p);
+#endif
+ goto cleanup;
+ }
+ nhosts = get_hostname_list(margs, &hp, nh, domain);
+
+ /* Remove duplicates */
+ for (i = 0; i < nhosts; i++) {
+ for (j = i + 1; j < nhosts; j++) {
+ if (!strcasecmp(hp[i].host, hp[j].host)) {
+ if (hp[i].port == hp[j].port ||
+ (hp[i].port == -1 && hp[j].port == 389) ||
+ (hp[i].port == 389 && hp[j].port == -1)) {
+ xfree(hp[j].host);
+ for (k = j + 1; k < nhosts; k++) {
+ hp[k - 1].host = hp[k].host;
+ hp[k - 1].port = hp[k].port;
+ hp[k - 1].priority = hp[k].priority;
+ hp[k - 1].weight = hp[k].weight;
+ }
+ j--;
+ nhosts--;
+ hp = (struct hstruct *) xrealloc(hp, sizeof(struct hstruct) * (nhosts + 1));
+ }
+ }
+ }
+ }
+
+ /* Sort by Priority / Weight */
+ msort(hp, nhosts, compare_hosts);
+
+ if (debug_enabled) {
+ debug((char *) "%s| %s: DEBUG: Sorted ldap server names for domain %s:\n", LogTime(), PROGRAM, domain);
+ for (i = 0; i < nhosts; i++) {
+ debug((char *) "%s| %s: DEBUG: Host: %s Port: %d Priority: %d Weight: %d\n", LogTime(), PROGRAM, hp[i].host, hp[i].port, hp[i].priority, hp[i].weight);
+ }
+ }
+ if (buffer)
+ xfree(buffer);
+ if (service)
+ xfree(service);
+ *hlist = hp;
+ return (nhosts);
+
+ cleanup:
+ if (buffer)
+ xfree(buffer);
+ if (service)
+ xfree(service);
+ *hlist = hp;
+ return (nhosts);
+}
+#endif
--- /dev/null
+/*
+ * -----------------------------------------------------------------------------
+ *
+ * Author: Markus Moeller (markus_moeller at compuserve.com)
+ *
+ * Copyright (C) 2007 Markus Moeller. All rights reserved.
+ *
+ * This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * -----------------------------------------------------------------------------
+ */
+
+#include "config.h"
+#include "util.h"
+
+#ifdef HAVE_LDAP
+
+#include "support.h"
+
+#ifdef HAVE_SASL_H
+#include <sasl.h>
+#elif defined(HAVE_SASL_SASL_H)
+#include <sasl/sasl.h>
+#elif defined(HAVE_SASL_DARWIN)
+typedef struct sasl_interact {
+ unsigned long id; /* same as client/user callback ID */
+ const char *challenge; /* presented to user (e.g. OTP challenge) */
+ const char *prompt; /* presented to user (e.g. "Username: ") */
+ const char *defresult; /* default result string */
+ const void *result; /* set to point to result */
+ unsigned len; /* set to length of result */
+} sasl_interact_t;
+
+#define SASL_CB_USER 0x4001 /* client user identity to login as */
+#define SASL_CB_AUTHNAME 0x4002 /* client authentication name */
+#define SASL_CB_PASS 0x4004 /* client passphrase-based secret */
+#define SASL_CB_ECHOPROMPT 0x4005 /* challenge and client enterred result */
+#define SASL_CB_NOECHOPROMPT 0x4006 /* challenge and client enterred result */
+#define SASL_CB_GETREALM 0x4008 /* realm to attempt authentication in */
+#define SASL_CB_LIST_END 0 /* end of list */
+#endif
+
+#if defined(HAVE_SASL_H) || defined(HAVE_SASL_SASL_H) || defined(HAVE_SASL_DARWIN)
+void *lutil_sasl_defaults(
+ LDAP * ld,
+ char *mech,
+ char *realm,
+ char *authcid,
+ char *passwd,
+ char *authzid);
+
+LDAP_SASL_INTERACT_PROC lutil_sasl_interact;
+
+int lutil_sasl_interact(
+ LDAP * ld,
+ unsigned flags,
+ void *defaults,
+ void *in);
+
+void lutil_sasl_freedefs(
+ void *defaults);
+
+
+/*
+ * SASL definitions for openldap support
+ */
+
+
+typedef struct lutil_sasl_defaults_s {
+ char *mech;
+ char *realm;
+ char *authcid;
+ char *passwd;
+ char *authzid;
+ char **resps;
+ int nresps;
+} lutilSASLdefaults;
+
+void *
+lutil_sasl_defaults(
+ LDAP * ld,
+ char *mech,
+ char *realm,
+ char *authcid,
+ char *passwd,
+ char *authzid)
+{
+ lutilSASLdefaults *defaults;
+
+ defaults = (lutilSASLdefaults *) xmalloc(sizeof(lutilSASLdefaults));
+
+ if (defaults == NULL)
+ return NULL;
+
+ defaults->mech = mech ? xstrdup(mech) : NULL;
+ defaults->realm = realm ? xstrdup(realm) : NULL;
+ defaults->authcid = authcid ? xstrdup(authcid) : NULL;
+ defaults->passwd = passwd ? xstrdup(passwd) : NULL;
+ defaults->authzid = authzid ? xstrdup(authzid) : NULL;
+
+ if (defaults->mech == NULL) {
+ ldap_get_option(ld, LDAP_OPT_X_SASL_MECH, &defaults->mech);
+ }
+ if (defaults->realm == NULL) {
+ ldap_get_option(ld, LDAP_OPT_X_SASL_REALM, &defaults->realm);
+ }
+ if (defaults->authcid == NULL) {
+ ldap_get_option(ld, LDAP_OPT_X_SASL_AUTHCID, &defaults->authcid);
+ }
+ if (defaults->authzid == NULL) {
+ ldap_get_option(ld, LDAP_OPT_X_SASL_AUTHZID, &defaults->authzid);
+ }
+ defaults->resps = NULL;
+ defaults->nresps = 0;
+
+ return defaults;
+}
+
+static int
+interaction(
+ unsigned flags,
+ sasl_interact_t * interact,
+ lutilSASLdefaults * defaults)
+{
+ const char *dflt = interact->defresult;
+
+ int noecho = 0;
+ int challenge = 0;
+
+ flags = flags;
+ switch (interact->id) {
+ case SASL_CB_GETREALM:
+ if (defaults)
+ dflt = defaults->realm;
+ break;
+ case SASL_CB_AUTHNAME:
+ if (defaults)
+ dflt = defaults->authcid;
+ break;
+ case SASL_CB_PASS:
+ if (defaults)
+ dflt = defaults->passwd;
+ noecho = 1;
+ break;
+ case SASL_CB_USER:
+ if (defaults)
+ dflt = defaults->authzid;
+ break;
+ case SASL_CB_NOECHOPROMPT:
+ noecho = 1;
+ challenge = 1;
+ break;
+ case SASL_CB_ECHOPROMPT:
+ challenge = 1;
+ break;
+ }
+
+ if (dflt && !*dflt)
+ dflt = NULL;
+
+ /* input must be empty */
+ interact->result = (dflt && *dflt) ? dflt : "";
+ interact->len = (unsigned) strlen((const char *) interact->result);
+
+ return LDAP_SUCCESS;
+}
+
+int
+lutil_sasl_interact(
+ LDAP * ld,
+ unsigned flags,
+ void *defaults,
+ void *in)
+{
+ sasl_interact_t *interact = (sasl_interact_t *) in;
+
+ if (ld == NULL)
+ return LDAP_PARAM_ERROR;
+
+ while (interact->id != SASL_CB_LIST_END) {
+ int rc = interaction(flags, interact, (lutilSASLdefaults *) defaults);
+
+ if (rc)
+ return rc;
+ interact++;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+void
+lutil_sasl_freedefs(
+ void *defaults)
+{
+ lutilSASLdefaults *defs = (lutilSASLdefaults *) defaults;
+
+ if (defs->mech)
+ xfree(defs->mech);
+ if (defs->realm)
+ xfree(defs->realm);
+ if (defs->authcid)
+ xfree(defs->authcid);
+ if (defs->passwd)
+ xfree(defs->passwd);
+ if (defs->authzid)
+ xfree(defs->authzid);
+ if (defs->resps)
+ xfree(defs->resps);
+
+ xfree(defs);
+}
+
+int
+tool_sasl_bind(LDAP * ld, char *binddn, char *ssl)
+{
+ /*
+ * unsigned sasl_flags = LDAP_SASL_AUTOMATIC;
+ * unsigned sasl_flags = LDAP_SASL_QUIET;
+ */
+ /*
+ * Avoid SASL messages
+ */
+#ifdef HAVE_SUN_LDAP_SDK
+ unsigned sasl_flags = LDAP_SASL_INTERACTIVE;
+#else
+ unsigned sasl_flags = LDAP_SASL_QUIET;
+#endif
+ char *sasl_realm = NULL;
+ char *sasl_authc_id = NULL;
+ char *sasl_authz_id = NULL;
+#ifdef HAVE_SUN_LDAP_SDK
+ char *sasl_mech = (char *) "GSSAPI";
+#else
+ char *sasl_mech = NULL;
+#endif
+ /*
+ * Force encryption
+ */
+ char *sasl_secprops;
+ /*
+ * char *sasl_secprops = (char *)"maxssf=56";
+ * char *sasl_secprops = NULL;
+ */
+ struct berval passwd =
+ {0, NULL};
+ void *defaults;
+ int rc = LDAP_SUCCESS;
+
+ if (ssl)
+ sasl_secprops = (char *) "maxssf=0";
+ else
+ sasl_secprops = (char *) "maxssf=56";
+/* sasl_secprops = (char *)"maxssf=0"; */
+/* sasl_secprops = (char *)"maxssf=56"; */
+
+ if (sasl_secprops != NULL) {
+ rc = ldap_set_option(ld, LDAP_OPT_X_SASL_SECPROPS,
+ (void *) sasl_secprops);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: Could not set LDAP_OPT_X_SASL_SECPROPS: %s: %s\n", LogTime(), PROGRAM, sasl_secprops, ldap_err2string(rc));
+ return rc;
+ }
+ }
+ defaults = lutil_sasl_defaults(ld,
+ sasl_mech,
+ sasl_realm,
+ sasl_authc_id,
+ passwd.bv_val,
+ sasl_authz_id);
+
+ rc = ldap_sasl_interactive_bind_s(ld, binddn,
+ sasl_mech, NULL, NULL,
+ sasl_flags, lutil_sasl_interact, defaults);
+
+ lutil_sasl_freedefs(defaults);
+ if (rc != LDAP_SUCCESS) {
+ error((char *) "%s| %s: ERROR: ldap_sasl_interactive_bind_s error: %s\n", LogTime(), PROGRAM, ldap_err2string(rc));
+ }
+ return rc;
+}
+#else
+void dummy(void);
+void
+dummy(void)
+{
+ fprintf(stderr, "%s| %s: ERROR: Dummy function\n", LogTime(), PROGRAM);
+}
+
+#endif
+#endif
#define BASE64_VALUE_SZ 256
int base64_value[BASE64_VALUE_SZ];
const char base64_code[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static void
int i;
for (i = 0; i < BASE64_VALUE_SZ; i++)
- base64_value[i] = -1;
+ base64_value[i] = -1;
for (i = 0; i < 64; i++)
- base64_value[(int) base64_code[i]] = i;
- base64_value[(int)'='] = 0;
+ base64_value[(int) base64_code[i]] = i;
+ base64_value[(int) '='] = 0;
base64_initialized = 1;
}
int c;
long val;
if (!data)
- return;
+ return;
if (!base64_initialized)
- ska_base64_init();
+ ska_base64_init();
val = c = 0;
for (j = 0; *data; data++) {
- unsigned int k = ((unsigned char) *data) % BASE64_VALUE_SZ;
- if (base64_value[k] < 0)
- continue;
- val <<= 6;
- val += base64_value[k];
- if (++c < 4)
- continue;
- /* One quantum of four encoding characters/24 bit */
- if (j >= result_size)
- break;
- result[j++] = val >> 16; /* High 8 bits */
- if (j >= result_size)
- break;
- result[j++] = (val >> 8) & 0xff; /* Mid 8 bits */
- if (j >= result_size)
- break;
- result[j++] = val & 0xff; /* Low 8 bits */
- val = c = 0;
+ unsigned int k = ((unsigned char) *data) % BASE64_VALUE_SZ;
+ if (base64_value[k] < 0)
+ continue;
+ val <<= 6;
+ val += base64_value[k];
+ if (++c < 4)
+ continue;
+ /* One quantum of four encoding characters/24 bit */
+ if (j >= result_size)
+ break;
+ result[j++] = val >> 16; /* High 8 bits */
+ if (j >= result_size)
+ break;
+ result[j++] = (val >> 8) & 0xff; /* Mid 8 bits */
+ if (j >= result_size)
+ break;
+ result[j++] = val & 0xff; /* Low 8 bits */
+ val = c = 0;
}
return;
}
/* adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments */
void
ska_base64_encode(char *result, const char *data, int result_size,
- int data_size)
+ int data_size)
{
int bits = 0;
int char_count = 0;
int out_cnt = 0;
if (!data)
- return;
+ return;
if (!base64_initialized)
- ska_base64_init();
+ ska_base64_init();
while (data_size--) {
- int c = (unsigned char) *data++;
- bits += c;
- char_count++;
- if (char_count == 3) {
- if (out_cnt >= result_size)
- break;
- result[out_cnt++] = base64_code[bits >> 18];
- if (out_cnt >= result_size)
- break;
- result[out_cnt++] = base64_code[(bits >> 12) & 0x3f];
- if (out_cnt >= result_size)
- break;
- result[out_cnt++] = base64_code[(bits >> 6) & 0x3f];
- if (out_cnt >= result_size)
- break;
- result[out_cnt++] = base64_code[bits & 0x3f];
- bits = 0;
- char_count = 0;
- } else {
- bits <<= 8;
- }
+ int c = (unsigned char) *data++;
+ bits += c;
+ char_count++;
+ if (char_count == 3) {
+ if (out_cnt >= result_size)
+ break;
+ result[out_cnt++] = base64_code[bits >> 18];
+ if (out_cnt >= result_size)
+ break;
+ result[out_cnt++] = base64_code[(bits >> 12) & 0x3f];
+ if (out_cnt >= result_size)
+ break;
+ result[out_cnt++] = base64_code[(bits >> 6) & 0x3f];
+ if (out_cnt >= result_size)
+ break;
+ result[out_cnt++] = base64_code[bits & 0x3f];
+ bits = 0;
+ char_count = 0;
+ } else {
+ bits <<= 8;
+ }
}
if (char_count != 0) {
- bits <<= 16 - (8 * char_count);
- if (out_cnt >= result_size)
- goto end;
- result[out_cnt++] = base64_code[bits >> 18];
- if (out_cnt >= result_size)
- goto end;
- result[out_cnt++] = base64_code[(bits >> 12) & 0x3f];
- if (char_count == 1) {
- if (out_cnt >= result_size)
- goto end;
- result[out_cnt++] = '=';
- if (out_cnt >= result_size)
- goto end;
- result[out_cnt++] = '=';
- } else {
- if (out_cnt >= result_size)
- goto end;
- result[out_cnt++] = base64_code[(bits >> 6) & 0x3f];
- if (out_cnt >= result_size)
- goto end;
- result[out_cnt++] = '=';
- }
+ bits <<= 16 - (8 * char_count);
+ if (out_cnt >= result_size)
+ goto end;
+ result[out_cnt++] = base64_code[bits >> 18];
+ if (out_cnt >= result_size)
+ goto end;
+ result[out_cnt++] = base64_code[(bits >> 12) & 0x3f];
+ if (char_count == 1) {
+ if (out_cnt >= result_size)
+ goto end;
+ result[out_cnt++] = '=';
+ if (out_cnt >= result_size)
+ goto end;
+ result[out_cnt++] = '=';
+ } else {
+ if (out_cnt >= result_size)
+ goto end;
+ result[out_cnt++] = base64_code[(bits >> 6) & 0x3f];
+ if (out_cnt >= result_size)
+ goto end;
+ result[out_cnt++] = '=';
+ }
}
-end:
+ end:
if (out_cnt >= result_size) {
- result[result_size - 1] = '\0'; /* terminate */
+ result[result_size - 1] = '\0'; /* terminate */
} else {
- result[out_cnt] = '\0'; /* terminate */
+ result[out_cnt] = '\0'; /* terminate */
}
return;
}
j = 0;
for (i = strlen(data) - 1; i >= 0; i--) {
- if (data[i] == '=')
- j++;
- if (data[i] != '=')
- break;
+ if (data[i] == '=')
+ j++;
+ if (data[i] != '=')
+ break;
}
return strlen(data) / 4 * 3 - j;
}
void ska_base64_decode(char *result, const char *data, int result_size);
void ska_base64_encode(char *result, const char *data, int result_size,
- int data_size);
+ int data_size);
int ska_base64_encode_len(int len);
int ska_base64_decode_len(const char *data);
#!/bin/sh
# Don't build without gssapi.h
-if test -f /usr/include/gssapi/gssapi.h || test -f /usr/include/gssapi.h ; then
+if test -f /usr/include/gssapi/gssapi.h || test -f /usr/include/gssapi.h || test -f /usr/include/kerberosV/gssapi.h ; then
exit 0
fi
exit 1
#define MAX_AUTHTOKEN_LEN 65535
#endif
#ifndef SQUID_KERB_AUTH_VERSION
-#define SQUID_KERB_AUTH_VERSION "3.0.2sq"
+#define SQUID_KERB_AUTH_VERSION "3.0.3sq"
#endif
int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
- const char *function, int log);
+ const char *function, int log);
char *gethost_name(void);
static const char *LogTime(void);
-static const unsigned char ntlmProtocol[] = { 'N', 'T', 'L', 'M', 'S', 'S', 'P', 0 };
+static const unsigned char ntlmProtocol[] =
+{'N', 'T', 'L', 'M', 'S', 'S', 'P', 0};
static const char *
LogTime()
gettimeofday(&now, NULL);
if (now.tv_sec != last_t) {
- tm = localtime((time_t *) & now.tv_sec);
- strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
- last_t = now.tv_sec;
+ tm = localtime((time_t *) & now.tv_sec);
+ strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
+ last_t = now.tv_sec;
}
return buf;
}
gethost_name(void)
{
/*
- char hostname[sysconf(_SC_HOST_NAME_MAX)];
- */
+ * char hostname[sysconf(_SC_HOST_NAME_MAX)];
+ */
char hostname[1024];
struct addrinfo *hres = NULL, *hres_list;
int rc, count;
rc = gethostname(hostname, sysconf(_SC_HOST_NAME_MAX));
if (rc) {
- fprintf(stderr, "%s| %s: ERROR: resolving hostname '%s' failed\n",
- LogTime(), PROGRAM, hostname);
- return NULL;
+ fprintf(stderr, "%s| %s: ERROR: resolving hostname '%s' failed\n",
+ LogTime(), PROGRAM, hostname);
+ return NULL;
}
rc = getaddrinfo(hostname, NULL, NULL, &hres);
if (rc != 0) {
- fprintf(stderr,
- "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
- LogTime(), PROGRAM, gai_strerror(rc));
- return NULL;
+ fprintf(stderr,
+ "%s| %s: ERROR: resolving hostname with getaddrinfo: %s failed\n",
+ LogTime(), PROGRAM, gai_strerror(rc));
+ return NULL;
}
hres_list = hres;
count = 0;
while (hres_list) {
- count++;
- hres_list = hres_list->ai_next;
+ count++;
+ hres_list = hres_list->ai_next;
}
rc = getnameinfo(hres->ai_addr, hres->ai_addrlen, hostname,
- sizeof(hostname), NULL, 0, 0);
+ sizeof(hostname), NULL, 0, 0);
if (rc != 0) {
- fprintf(stderr,
- "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
- LogTime(), PROGRAM, gai_strerror(rc));
- freeaddrinfo(hres);
- return NULL;
+ fprintf(stderr,
+ "%s| %s: ERROR: resolving ip address with getnameinfo: %s failed\n",
+ LogTime(), PROGRAM, gai_strerror(rc));
+ freeaddrinfo(hres);
+ return NULL;
}
-
freeaddrinfo(hres);
hostname[sysconf(_SC_HOST_NAME_MAX) - 1] = '\0';
return (xstrdup(hostname));
int
check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
- const char *function, int log)
+ const char *function, int log)
{
if (GSS_ERROR(major_status)) {
- OM_uint32 maj_stat, min_stat;
- OM_uint32 msg_ctx = 0;
- gss_buffer_desc status_string;
- char buf[1024];
- size_t len;
-
- len = 0;
- msg_ctx = 0;
- while (!msg_ctx) {
- /* convert major status code (GSS-API error) to text */
- maj_stat = gss_display_status(&min_stat, major_status,
- GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
- if (maj_stat == GSS_S_COMPLETE) {
- if (sizeof(buf) > len + status_string.length + 1) {
- snprintf(buf + len, (sizeof(buf)-len), "%s", (char *) status_string.value);
- len += status_string.length;
- }
- gss_release_buffer(&min_stat, &status_string);
- break;
- }
- gss_release_buffer(&min_stat, &status_string);
- }
- if (sizeof(buf) > len + 2) {
- snprintf(buf + len, (sizeof(buf)-len), "%s", ". ");
- len += 2;
- }
- msg_ctx = 0;
- while (!msg_ctx) {
- /* convert minor status code (underlying routine error) to text */
- maj_stat = gss_display_status(&min_stat, minor_status,
- GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
- if (maj_stat == GSS_S_COMPLETE) {
- if (sizeof(buf) > len + status_string.length) {
- snprintf(buf + len,(sizeof(buf)-len), "%s", (char *) status_string.value);
- len += status_string.length;
- }
- gss_release_buffer(&min_stat, &status_string);
- break;
- }
- gss_release_buffer(&min_stat, &status_string);
- }
- debug("%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM, function, buf);
- fprintf(stdout, "BH %s failed: %s\n", function, buf);
- if (log)
- fprintf(stderr, "%s| %s: INFO: User not authenticated\n", LogTime(),
- PROGRAM);
- return (1);
+ OM_uint32 maj_stat, min_stat;
+ OM_uint32 msg_ctx = 0;
+ gss_buffer_desc status_string;
+ char buf[1024];
+ size_t len;
+
+ len = 0;
+ msg_ctx = 0;
+ while (!msg_ctx) {
+ /* convert major status code (GSS-API error) to text */
+ maj_stat = gss_display_status(&min_stat, major_status,
+ GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
+ if (maj_stat == GSS_S_COMPLETE) {
+ if (sizeof(buf) > len + status_string.length + 1) {
+ snprintf(buf + len, (sizeof(buf) - len), "%s", (char *) status_string.value);
+ len += status_string.length;
+ }
+ gss_release_buffer(&min_stat, &status_string);
+ break;
+ }
+ gss_release_buffer(&min_stat, &status_string);
+ }
+ if (sizeof(buf) > len + 2) {
+ snprintf(buf + len, (sizeof(buf) - len), "%s", ". ");
+ len += 2;
+ }
+ msg_ctx = 0;
+ while (!msg_ctx) {
+ /* convert minor status code (underlying routine error) to text */
+ maj_stat = gss_display_status(&min_stat, minor_status,
+ GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
+ if (maj_stat == GSS_S_COMPLETE) {
+ if (sizeof(buf) > len + status_string.length) {
+ snprintf(buf + len, (sizeof(buf) - len), "%s", (char *) status_string.value);
+ len += status_string.length;
+ }
+ gss_release_buffer(&min_stat, &status_string);
+ break;
+ }
+ gss_release_buffer(&min_stat, &status_string);
+ }
+ debug((char *) "%s| %s: ERROR: %s failed: %s\n", LogTime(), PROGRAM, function, buf);
+ fprintf(stdout, "BH %s failed: %s\n", function, buf);
+ if (log)
+ fprintf(stderr, "%s| %s: INFO: User not authenticated\n", LogTime(),
+ PROGRAM);
+ return (1);
}
return (0);
}
setbuf(stdin, NULL);
while (-1 != (opt = getopt(argc, argv, "dirs:h"))) {
- switch (opt) {
- case 'd':
- debug_enabled = 1;
- break;
- case 'i':
- log = 1;
- break;
- case 'r':
- norealm = 1;
- break;
- case 's':
- service_principal = xstrdup(optarg);
- break;
- case 'h':
- fprintf(stderr, "Usage: \n");
- fprintf(stderr, "squid_kerb_auth [-d] [-i] [-s SPN] [-h]\n");
- fprintf(stderr, "-d full debug\n");
- fprintf(stderr, "-i informational messages\n");
- fprintf(stderr, "-r remove realm from username\n");
- fprintf(stderr, "-s service principal name\n");
- fprintf(stderr, "-h help\n");
- fprintf(stderr,
- "The SPN can be set to GSS_C_NO_NAME to allow any entry from keytab\n");
- fprintf(stderr, "default SPN is HTTP/fqdn@DEFAULT_REALM\n");
- exit(0);
- default:
- fprintf(stderr, "%s| %s: WARNING: unknown option: -%c.\n", LogTime(),
- PROGRAM, opt);
- }
+ switch (opt) {
+ case 'd':
+ debug_enabled = 1;
+ break;
+ case 'i':
+ log = 1;
+ break;
+ case 'r':
+ norealm = 1;
+ break;
+ case 's':
+ service_principal = xstrdup(optarg);
+ break;
+ case 'h':
+ fprintf(stderr, "Usage: \n");
+ fprintf(stderr, "squid_kerb_auth [-d] [-i] [-s SPN] [-h]\n");
+ fprintf(stderr, "-d full debug\n");
+ fprintf(stderr, "-i informational messages\n");
+ fprintf(stderr, "-r remove realm from username\n");
+ fprintf(stderr, "-s service principal name\n");
+ fprintf(stderr, "-h help\n");
+ fprintf(stderr,
+ "The SPN can be set to GSS_C_NO_NAME to allow any entry from keytab\n");
+ fprintf(stderr, "default SPN is HTTP/fqdn@DEFAULT_REALM\n");
+ exit(0);
+ default:
+ fprintf(stderr, "%s| %s: WARNING: unknown option: -%c.\n", LogTime(),
+ PROGRAM, opt);
+ }
}
- debug("%s| %s: INFO: Starting version %s\n", LogTime(), PROGRAM, SQUID_KERB_AUTH_VERSION);
+ debug((char *) "%s| %s: INFO: Starting version %s\n", LogTime(), PROGRAM, SQUID_KERB_AUTH_VERSION);
if (service_principal && strcasecmp(service_principal, "GSS_C_NO_NAME")) {
- service.value = service_principal;
- service.length = strlen((char *) service.value);
+ service.value = service_principal;
+ service.length = strlen((char *) service.value);
} else {
- host_name = gethost_name();
- if (!host_name) {
- fprintf(stderr,
- "%s| %s: FATAL: Local hostname could not be determined. Please specify the service principal\n",
- LogTime(), PROGRAM);
- fprintf(stdout, "BH hostname error\n");
- exit(-1);
- }
- service.value = xmalloc(strlen(service_name) + strlen(host_name) + 2);
- snprintf((char*)service.value, strlen(service_name) + strlen(host_name) + 2,
- "%s@%s", service_name, host_name);
- service.length = strlen((char *) service.value);
+ host_name = gethost_name();
+ if (!host_name) {
+ fprintf(stderr,
+ "%s| %s: FATAL: Local hostname could not be determined. Please specify the service principal\n",
+ LogTime(), PROGRAM);
+ fprintf(stdout, "BH hostname error\n");
+ exit(-1);
+ }
+ service.value = xmalloc(strlen(service_name) + strlen(host_name) + 2);
+ snprintf((char *) service.value, strlen(service_name) + strlen(host_name) + 2,
+ "%s@%s", service_name, host_name);
+ service.length = strlen((char *) service.value);
}
while (1) {
- if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
- if (ferror(stdin)) {
- debug("%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n",
- LogTime(), PROGRAM, ferror(stdin),
- strerror(ferror(stdin)));
-
- fprintf(stdout, "BH input error\n");
- exit(1); /* BIIG buffer */
- }
- fprintf(stdout, "BH input error\n");
- exit(0);
- }
-
- c = (char*)memchr(buf, '\n', sizeof(buf) - 1);
- if (c) {
- *c = '\0';
- length = c - buf;
- } else {
- err = 1;
- }
- if (err) {
- debug("%s| %s: ERROR: Oversized message\n", LogTime(), PROGRAM);
- fprintf(stdout, "BH Oversized message\n");
- err = 0;
- continue;
- }
-
- debug("%s| %s: DEBUG: Got '%s' from squid (length: %d).\n", LogTime(), PROGRAM, buf, length);
-
- if (buf[0] == '\0') {
- debug("%s| %s: ERROR: Invalid request\n", LogTime(), PROGRAM);
- fprintf(stdout, "BH Invalid request\n");
- continue;
- }
-
- if (strlen(buf) < 2) {
- debug("%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf);
- fprintf(stdout, "BH Invalid request\n");
- continue;
- }
-
- if (!strncmp(buf, "QQ", 2)) {
- gss_release_buffer(&minor_status, &input_token);
- gss_release_buffer(&minor_status, &output_token);
- gss_release_buffer(&minor_status, &service);
- gss_release_cred(&minor_status, &server_creds);
- if (server_name)
- gss_release_name(&minor_status, &server_name);
- if (client_name)
- gss_release_name(&minor_status, &client_name);
- if (gss_context != GSS_C_NO_CONTEXT)
- gss_delete_sec_context(&minor_status, &gss_context, NULL);
- if (kerberosToken) {
- /* Allocated by parseNegTokenInit, but no matching free function exists.. */
- if (!spnego_flag)
- xfree((char *) kerberosToken);
- kerberosToken = NULL;
- }
- if (spnego_flag) {
- /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
- if (spnegoToken)
- xfree((char *) spnegoToken);
- spnegoToken = NULL;
- }
- if (token) {
- xfree(token);
- token = NULL;
- }
- if (host_name) {
- xfree(host_name);
- host_name = NULL;
- }
- fprintf(stdout, "BH quit command\n");
- exit(0);
- }
-
- if (strncmp(buf, "YR", 2) && strncmp(buf, "KK", 2)) {
- debug("%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf);
- fprintf(stdout, "BH Invalid request\n");
- continue;
- }
- if (!strncmp(buf, "YR", 2)) {
- if (gss_context != GSS_C_NO_CONTEXT)
- gss_delete_sec_context(&minor_status, &gss_context, NULL);
- gss_context = GSS_C_NO_CONTEXT;
- }
-
- if (strlen(buf) <= 3) {
- debug("%s| %s: ERROR: Invalid negotiate request [%s]\n", LogTime(), PROGRAM, buf);
- fprintf(stdout, "BH Invalid negotiate request\n");
- continue;
- }
-
- input_token.length = ska_base64_decode_len(buf + 3);
- debug("%s| %s: DEBUG: Decode '%s' (decoded length: %d).\n",
- LogTime(), PROGRAM, buf + 3, (int) input_token.length);
- input_token.value = xmalloc(input_token.length);
-
- ska_base64_decode((char*)input_token.value, buf + 3, input_token.length);
-
-
- if ((input_token.length >= sizeof ntlmProtocol + 1) &&
- (!memcmp(input_token.value, ntlmProtocol, sizeof ntlmProtocol))) {
- debug("%s| %s: WARNING: received type %d NTLM token\n",
- LogTime(), PROGRAM,
- (int) *((unsigned char *) input_token.value +
- sizeof ntlmProtocol));
- fprintf(stdout, "BH received type %d NTLM token\n",
- (int) *((unsigned char *) input_token.value +
- sizeof ntlmProtocol));
- goto cleanup;
- }
-
- if (service_principal) {
- if (strcasecmp(service_principal, "GSS_C_NO_NAME")) {
- major_status = gss_import_name(&minor_status, &service,
- (gss_OID) GSS_C_NULL_OID, &server_name);
-
- } else {
- server_name = GSS_C_NO_NAME;
- major_status = GSS_S_COMPLETE;
- }
- } else {
- major_status = gss_import_name(&minor_status, &service,
- gss_nt_service_name, &server_name);
- }
-
- if (check_gss_err(major_status, minor_status, "gss_import_name()", log))
- goto cleanup;
-
- major_status =
- gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
- GSS_C_NO_OID_SET, GSS_C_ACCEPT, &server_creds, NULL, NULL);
- if (check_gss_err(major_status, minor_status, "gss_acquire_cred()", log))
- goto cleanup;
-
- major_status = gss_accept_sec_context(&minor_status,
- &gss_context,
- server_creds,
- &input_token,
- GSS_C_NO_CHANNEL_BINDINGS,
- &client_name, NULL, &output_token, &ret_flags, NULL, NULL);
-
-
- if (output_token.length) {
- spnegoToken = (const unsigned char*)output_token.value;
- spnegoTokenLength = output_token.length;
- token = (char*)xmalloc(ska_base64_encode_len(spnegoTokenLength));
- if (token == NULL) {
- debug("%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
- fprintf(stdout, "BH Not enough memory\n");
- goto cleanup;
- }
-
- ska_base64_encode(token, (const char *) spnegoToken,
- ska_base64_encode_len(spnegoTokenLength), spnegoTokenLength);
-
- if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log))
- goto cleanup;
- if (major_status & GSS_S_CONTINUE_NEEDED) {
- debug("%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
- fprintf(stdout, "TT %s\n", token);
- goto cleanup;
- }
- gss_release_buffer(&minor_status, &output_token);
- major_status =
- gss_display_name(&minor_status, client_name, &output_token,
- NULL);
-
- if (check_gss_err(major_status, minor_status, "gss_display_name()", log))
- goto cleanup;
- user = (char*)xmalloc(output_token.length + 1);
- if (user == NULL) {
- debug("%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
- fprintf(stdout, "BH Not enough memory\n");
- goto cleanup;
- }
- memcpy(user, output_token.value, output_token.length);
- user[output_token.length] = '\0';
- if (norealm && (p = strchr(user, '@')) != NULL) {
- *p = '\0';
- }
- fprintf(stdout, "AF %s %s\n", token, user);
- debug("%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, token, user);
- if (log)
- fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
- PROGRAM, user);
- goto cleanup;
- } else {
- if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log))
- goto cleanup;
- if (major_status & GSS_S_CONTINUE_NEEDED) {
- debug("%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
- fprintf(stdout, "NA %s\n", token);
- goto cleanup;
- }
- gss_release_buffer(&minor_status, &output_token);
- major_status =
- gss_display_name(&minor_status, client_name, &output_token,
- NULL);
-
- if (check_gss_err(major_status, minor_status, "gss_display_name()", log))
- goto cleanup;
- /*
- * Return dummy token AA. May need an extra return tag then AF
- */
- user = (char*)xmalloc(output_token.length + 1);
- if (user == NULL) {
- debug("%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
- fprintf(stdout, "BH Not enough memory\n");
- goto cleanup;
- }
- memcpy(user, output_token.value, output_token.length);
- user[output_token.length] = '\0';
- if (norealm && (p = strchr(user, '@')) != NULL) {
- *p = '\0';
- }
- fprintf(stdout, "AF %s %s\n", "AA==", user);
- debug("%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, "AA==", user);
- if (log)
- fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
- PROGRAM, user);
-
- }
-cleanup:
- gss_release_buffer(&minor_status, &input_token);
- gss_release_buffer(&minor_status, &output_token);
- gss_release_cred(&minor_status, &server_creds);
- if (server_name)
- gss_release_name(&minor_status, &server_name);
- if (client_name)
- gss_release_name(&minor_status, &client_name);
- if (kerberosToken) {
- /* Allocated by parseNegTokenInit, but no matching free function exists.. */
- if (!spnego_flag)
- xfree((char *) kerberosToken);
- kerberosToken = NULL;
- }
- if (spnego_flag) {
- /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
- if (spnegoToken)
- xfree((char *) spnegoToken);
- spnegoToken = NULL;
- }
- if (token) {
- xfree(token);
- token = NULL;
- }
- if (user) {
- xfree(user);
- user = NULL;
- }
- continue;
+ if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
+ if (ferror(stdin)) {
+ debug((char *) "%s| %s: FATAL: fgets() failed! dying..... errno=%d (%s)\n",
+ LogTime(), PROGRAM, ferror(stdin),
+ strerror(ferror(stdin)));
+
+ fprintf(stdout, "BH input error\n");
+ exit(1); /* BIIG buffer */
+ }
+ fprintf(stdout, "BH input error\n");
+ exit(0);
+ }
+ c = (char *) memchr(buf, '\n', sizeof(buf) - 1);
+ if (c) {
+ *c = '\0';
+ length = c - buf;
+ } else {
+ err = 1;
+ }
+ if (err) {
+ debug((char *) "%s| %s: ERROR: Oversized message\n", LogTime(), PROGRAM);
+ fprintf(stdout, "BH Oversized message\n");
+ err = 0;
+ continue;
+ }
+ debug((char *) "%s| %s: DEBUG: Got '%s' from squid (length: %d).\n", LogTime(), PROGRAM, buf, length);
+
+ if (buf[0] == '\0') {
+ debug((char *) "%s| %s: ERROR: Invalid request\n", LogTime(), PROGRAM);
+ fprintf(stdout, "BH Invalid request\n");
+ continue;
+ }
+ if (strlen(buf) < 2) {
+ debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf);
+ fprintf(stdout, "BH Invalid request\n");
+ continue;
+ }
+ if (!strncmp(buf, "QQ", 2)) {
+ gss_release_buffer(&minor_status, &input_token);
+ gss_release_buffer(&minor_status, &output_token);
+ gss_release_buffer(&minor_status, &service);
+ gss_release_cred(&minor_status, &server_creds);
+ if (server_name)
+ gss_release_name(&minor_status, &server_name);
+ if (client_name)
+ gss_release_name(&minor_status, &client_name);
+ if (gss_context != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&minor_status, &gss_context, NULL);
+ if (kerberosToken) {
+ /* Allocated by parseNegTokenInit, but no matching free function exists.. */
+ if (!spnego_flag)
+ xfree((char *) kerberosToken);
+ kerberosToken = NULL;
+ }
+ if (spnego_flag) {
+ /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
+ if (spnegoToken)
+ xfree((char *) spnegoToken);
+ spnegoToken = NULL;
+ }
+ if (token) {
+ xfree(token);
+ token = NULL;
+ }
+ if (host_name) {
+ xfree(host_name);
+ host_name = NULL;
+ }
+ fprintf(stdout, "BH quit command\n");
+ exit(0);
+ }
+ if (strncmp(buf, "YR", 2) && strncmp(buf, "KK", 2)) {
+ debug((char *) "%s| %s: ERROR: Invalid request [%s]\n", LogTime(), PROGRAM, buf);
+ fprintf(stdout, "BH Invalid request\n");
+ continue;
+ }
+ if (!strncmp(buf, "YR", 2)) {
+ if (gss_context != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&minor_status, &gss_context, NULL);
+ gss_context = GSS_C_NO_CONTEXT;
+ }
+ if (strlen(buf) <= 3) {
+ debug((char *) "%s| %s: ERROR: Invalid negotiate request [%s]\n", LogTime(), PROGRAM, buf);
+ fprintf(stdout, "BH Invalid negotiate request\n");
+ continue;
+ }
+ input_token.length = ska_base64_decode_len(buf + 3);
+ debug((char *) "%s| %s: DEBUG: Decode '%s' (decoded length: %d).\n",
+ LogTime(), PROGRAM, buf + 3, (int) input_token.length);
+ input_token.value = xmalloc(input_token.length);
+
+ ska_base64_decode((char *) input_token.value, buf + 3, input_token.length);
+
+
+ if ((input_token.length >= sizeof ntlmProtocol + 1) &&
+ (!memcmp(input_token.value, ntlmProtocol, sizeof ntlmProtocol))) {
+ debug((char *) "%s| %s: WARNING: received type %d NTLM token\n",
+ LogTime(), PROGRAM,
+ (int) *((unsigned char *) input_token.value +
+ sizeof ntlmProtocol));
+ fprintf(stdout, "BH received type %d NTLM token\n",
+ (int) *((unsigned char *) input_token.value +
+ sizeof ntlmProtocol));
+ goto cleanup;
+ }
+ if (service_principal) {
+ if (strcasecmp(service_principal, "GSS_C_NO_NAME")) {
+ major_status = gss_import_name(&minor_status, &service,
+ (gss_OID) GSS_C_NULL_OID, &server_name);
+
+ } else {
+ server_name = GSS_C_NO_NAME;
+ major_status = GSS_S_COMPLETE;
+ }
+ } else {
+ major_status = gss_import_name(&minor_status, &service,
+ gss_nt_service_name, &server_name);
+ }
+
+ if (check_gss_err(major_status, minor_status, "gss_import_name()", log))
+ goto cleanup;
+
+ major_status =
+ gss_acquire_cred(&minor_status, server_name, GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET, GSS_C_ACCEPT, &server_creds, NULL, NULL);
+ if (check_gss_err(major_status, minor_status, "gss_acquire_cred()", log))
+ goto cleanup;
+
+ major_status = gss_accept_sec_context(&minor_status,
+ &gss_context,
+ server_creds,
+ &input_token,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &client_name, NULL, &output_token, &ret_flags, NULL, NULL);
+
+
+ if (output_token.length) {
+ spnegoToken = (const unsigned char *) output_token.value;
+ spnegoTokenLength = output_token.length;
+ token = (char *) xmalloc(ska_base64_encode_len(spnegoTokenLength));
+ if (token == NULL) {
+ debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
+ fprintf(stdout, "BH Not enough memory\n");
+ goto cleanup;
+ }
+ ska_base64_encode(token, (const char *) spnegoToken,
+ ska_base64_encode_len(spnegoTokenLength), spnegoTokenLength);
+
+ if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log))
+ goto cleanup;
+ if (major_status & GSS_S_CONTINUE_NEEDED) {
+ debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
+ fprintf(stdout, "TT %s\n", token);
+ goto cleanup;
+ }
+ gss_release_buffer(&minor_status, &output_token);
+ major_status =
+ gss_display_name(&minor_status, client_name, &output_token,
+ NULL);
+
+ if (check_gss_err(major_status, minor_status, "gss_display_name()", log))
+ goto cleanup;
+ user = (char *) xmalloc(output_token.length + 1);
+ if (user == NULL) {
+ debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
+ fprintf(stdout, "BH Not enough memory\n");
+ goto cleanup;
+ }
+ memcpy(user, output_token.value, output_token.length);
+ user[output_token.length] = '\0';
+ if (norealm && (p = strchr(user, '@')) != NULL) {
+ *p = '\0';
+ }
+ fprintf(stdout, "AF %s %s\n", token, user);
+ debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, token, user);
+ if (log)
+ fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
+ PROGRAM, user);
+ goto cleanup;
+ } else {
+ if (check_gss_err(major_status, minor_status, "gss_accept_sec_context()", log))
+ goto cleanup;
+ if (major_status & GSS_S_CONTINUE_NEEDED) {
+ debug((char *) "%s| %s: INFO: continuation needed\n", LogTime(), PROGRAM);
+ fprintf(stdout, "NA %s\n", token);
+ goto cleanup;
+ }
+ gss_release_buffer(&minor_status, &output_token);
+ major_status =
+ gss_display_name(&minor_status, client_name, &output_token,
+ NULL);
+
+ if (check_gss_err(major_status, minor_status, "gss_display_name()", log))
+ goto cleanup;
+ /*
+ * Return dummy token AA. May need an extra return tag then AF
+ */
+ user = (char *) xmalloc(output_token.length + 1);
+ if (user == NULL) {
+ debug((char *) "%s| %s: ERROR: Not enough memory\n", LogTime(), PROGRAM);
+ fprintf(stdout, "BH Not enough memory\n");
+ goto cleanup;
+ }
+ memcpy(user, output_token.value, output_token.length);
+ user[output_token.length] = '\0';
+ if (norealm && (p = strchr(user, '@')) != NULL) {
+ *p = '\0';
+ }
+ fprintf(stdout, "AF %s %s\n", "AA==", user);
+ debug((char *) "%s| %s: DEBUG: AF %s %s\n", LogTime(), PROGRAM, "AA==", user);
+ if (log)
+ fprintf(stderr, "%s| %s: INFO: User %s authenticated\n", LogTime(),
+ PROGRAM, user);
+
+ }
+ cleanup:
+ gss_release_buffer(&minor_status, &input_token);
+ gss_release_buffer(&minor_status, &output_token);
+ gss_release_cred(&minor_status, &server_creds);
+ if (server_name)
+ gss_release_name(&minor_status, &server_name);
+ if (client_name)
+ gss_release_name(&minor_status, &client_name);
+ if (kerberosToken) {
+ /* Allocated by parseNegTokenInit, but no matching free function exists.. */
+ if (!spnego_flag)
+ xfree((char *) kerberosToken);
+ kerberosToken = NULL;
+ }
+ if (spnego_flag) {
+ /* Allocated by makeNegTokenTarg, but no matching free function exists.. */
+ if (spnegoToken)
+ xfree((char *) spnegoToken);
+ spnegoToken = NULL;
+ }
+ if (token) {
+ xfree(token);
+ token = NULL;
+ }
+ if (user) {
+ xfree(user);
+ user = NULL;
+ }
+ continue;
}
}
#else
setbuf(stdin, NULL);
char buf[MAX_AUTHTOKEN_LEN];
while (1) {
- if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
- fprintf(stdout, "BH input error\n");
- exit(0);
- }
- fprintf(stdout, "BH Kerberos authentication not supported\n");
+ if (fgets(buf, sizeof(buf) - 1, stdin) == NULL) {
+ fprintf(stdout, "BH input error\n");
+ exit(0);
+ }
+ fprintf(stdout, "BH Kerberos authentication not supported\n");
}
}
#endif /* HAVE_GSSAPI */
static const char *LogTime(void);
int check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
- const char *function);
+ const char *function);
const char *squid_kerb_proxy_auth(char *proxy);
gettimeofday(&now, NULL);
if (now.tv_sec != last_t) {
- tm = localtime((const time_t*)&now.tv_sec);
- strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
- last_t = now.tv_sec;
+ tm = localtime((const time_t *) &now.tv_sec);
+ strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
+ last_t = now.tv_sec;
}
return buf;
}
#ifndef gss_mech_spnego
-static gss_OID_desc _gss_mech_spnego = { 6, (void *) "\x2b\x06\x01\x05\x05\x02" };
+static gss_OID_desc _gss_mech_spnego =
+{6, (void *) "\x2b\x06\x01\x05\x05\x02"};
gss_OID gss_mech_spnego = &_gss_mech_spnego;
#endif
int
check_gss_err(OM_uint32 major_status, OM_uint32 minor_status,
- const char *function)
+ const char *function)
{
if (GSS_ERROR(major_status)) {
- OM_uint32 maj_stat, min_stat;
- OM_uint32 msg_ctx = 0;
- gss_buffer_desc status_string;
- char buf[1024];
- size_t len;
-
- len = 0;
- msg_ctx = 0;
- while (!msg_ctx) {
- /* convert major status code (GSS-API error) to text */
- maj_stat = gss_display_status(&min_stat, major_status,
- GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
- if (maj_stat == GSS_S_COMPLETE) {
- if (sizeof(buf) > len + status_string.length + 1) {
- snprintf(buf + len, (sizeof(buf)-len), "%s", (char *)status_string.value);
- len += status_string.length;
- }
- gss_release_buffer(&min_stat, &status_string);
- break;
- }
- gss_release_buffer(&min_stat, &status_string);
- }
- if (sizeof(buf) > len + 2) {
- snprintf(buf + len, (sizeof(buf)-len), "%s", ". ");
- len += 2;
- }
- msg_ctx = 0;
- while (!msg_ctx) {
- /* convert minor status code (underlying routine error) to text */
- maj_stat = gss_display_status(&min_stat, minor_status,
- GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
- if (maj_stat == GSS_S_COMPLETE) {
- if (sizeof(buf) > len + status_string.length) {
- snprintf(buf + len, (sizeof(buf)-len), "%s", (char *) status_string.value);
- len += status_string.length;
- }
- gss_release_buffer(&min_stat, &status_string);
- break;
- }
- gss_release_buffer(&min_stat, &status_string);
- }
- fprintf(stderr, "%s| %s: %s failed: %s\n", LogTime(), PROGRAM, function,
- buf);
- return (1);
+ OM_uint32 maj_stat, min_stat;
+ OM_uint32 msg_ctx = 0;
+ gss_buffer_desc status_string;
+ char buf[1024];
+ size_t len;
+
+ len = 0;
+ msg_ctx = 0;
+ while (!msg_ctx) {
+ /* convert major status code (GSS-API error) to text */
+ maj_stat = gss_display_status(&min_stat, major_status,
+ GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
+ if (maj_stat == GSS_S_COMPLETE) {
+ if (sizeof(buf) > len + status_string.length + 1) {
+ snprintf(buf + len, (sizeof(buf) - len), "%s", (char *) status_string.value);
+ len += status_string.length;
+ }
+ gss_release_buffer(&min_stat, &status_string);
+ break;
+ }
+ gss_release_buffer(&min_stat, &status_string);
+ }
+ if (sizeof(buf) > len + 2) {
+ snprintf(buf + len, (sizeof(buf) - len), "%s", ". ");
+ len += 2;
+ }
+ msg_ctx = 0;
+ while (!msg_ctx) {
+ /* convert minor status code (underlying routine error) to text */
+ maj_stat = gss_display_status(&min_stat, minor_status,
+ GSS_C_MECH_CODE, GSS_C_NULL_OID, &msg_ctx, &status_string);
+ if (maj_stat == GSS_S_COMPLETE) {
+ if (sizeof(buf) > len + status_string.length) {
+ snprintf(buf + len, (sizeof(buf) - len), "%s", (char *) status_string.value);
+ len += status_string.length;
+ }
+ gss_release_buffer(&min_stat, &status_string);
+ break;
+ }
+ gss_release_buffer(&min_stat, &status_string);
+ }
+ fprintf(stderr, "%s| %s: %s failed: %s\n", LogTime(), PROGRAM, function,
+ buf);
+ return (1);
}
return (0);
}
setbuf(stdin, NULL);
if (!proxy) {
- fprintf(stderr, "%s| %s: Error: No proxy server name\n", LogTime(),
- PROGRAM);
- return NULL;
+ fprintf(stderr, "%s| %s: Error: No proxy server name\n", LogTime(),
+ PROGRAM);
+ return NULL;
}
-
service.value = xmalloc(strlen("HTTP") + strlen(proxy) + 2);
- snprintf((char*)service.value, strlen("HTTP") + strlen(proxy) + 2, "%s@%s", "HTTP", proxy);
+ snprintf((char *) service.value, strlen("HTTP") + strlen(proxy) + 2, "%s@%s", "HTTP", proxy);
service.length = strlen((char *) service.value);
major_status = gss_import_name(&minor_status, &service,
- gss_nt_service_name, &server_name);
+ gss_nt_service_name, &server_name);
if (check_gss_err(major_status, minor_status, "gss_import_name()"))
- goto cleanup;
+ goto cleanup;
major_status = gss_init_sec_context(&minor_status,
- GSS_C_NO_CREDENTIAL, &gss_context, server_name,
- gss_mech_spnego,
- 0,
- 0,
- GSS_C_NO_CHANNEL_BINDINGS,
- &input_token, NULL, &output_token, NULL, NULL);
+ GSS_C_NO_CREDENTIAL, &gss_context, server_name,
+ gss_mech_spnego,
+ 0,
+ 0,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &input_token, NULL, &output_token, NULL, NULL);
if (check_gss_err(major_status, minor_status, "gss_init_sec_context()"))
- goto cleanup;
+ goto cleanup;
if (output_token.length) {
- token = (char*)xmalloc(ska_base64_encode_len(output_token.length));
- ska_base64_encode(token, (const char *) output_token.value,
- ska_base64_encode_len(output_token.length), output_token.length);
+ token = (char *) xmalloc(ska_base64_encode_len(output_token.length));
+ ska_base64_encode(token, (const char *) output_token.value,
+ ska_base64_encode_len(output_token.length), output_token.length);
}
-
-
-cleanup:
+ cleanup:
gss_delete_sec_context(&minor_status, &gss_context, NULL);
gss_release_buffer(&minor_status, &service);
gss_release_buffer(&minor_status, &input_token);
int count;
if (argc < 2) {
- fprintf(stderr, "%s| %s: Error: No proxy server name given\n",
- LogTime(), PROGRAM);
- exit(99);
+ fprintf(stderr, "%s| %s: Error: No proxy server name given\n",
+ LogTime(), PROGRAM);
+ exit(99);
}
if (argc == 3) {
- count = atoi(argv[2]);
- while (count > 0) {
- Token = (const char *) squid_kerb_proxy_auth(argv[1]);
- fprintf(stdout, "YR %s\n", Token ? Token : "NULL");
- count--;
- }
- fprintf(stdout, "QQ\n");
+ count = atoi(argv[2]);
+ while (count > 0) {
+ Token = (const char *) squid_kerb_proxy_auth(argv[1]);
+ fprintf(stdout, "YR %s\n", Token ? Token : "NULL");
+ count--;
+ }
+ fprintf(stdout, "QQ\n");
} else {
- Token = (const char *) squid_kerb_proxy_auth(argv[1]);
- fprintf(stdout, "Token: %s\n", Token ? Token : "NULL");
+ Token = (const char *) squid_kerb_proxy_auth(argv[1]);
+ fprintf(stdout, "Token: %s\n", Token ? Token : "NULL");
}
exit(0);
}
+
#else
#include <stdlib.h>
int
{
exit(-1);
}
+
#endif /* HAVE_GSSAPI */