diff options
author | Thomas Markwalder <tmark@isc.org> | 2015-07-29 13:32:16 -0400 |
---|---|---|
committer | Thomas Markwalder <tmark@isc.org> | 2015-07-29 13:32:16 -0400 |
commit | 743d69375c0e46ebb343cb3c6fa768103b7080d0 (patch) | |
tree | 4bea0683ffe2fb7c6428acc0740659f6ca4ae3b6 | |
parent | 6a39bcf0be695fa2e0b62312ea8bdc830a08f7bc (diff) | |
download | isc-dhcp-743d69375c0e46ebb343cb3c6fa768103b7080d0.tar.gz |
[master] LDAP: Pathces, IPv6 support, GSSAPI support
Merges in 39056.
-rw-r--r-- | Makefile.in | 1 | ||||
-rw-r--r-- | RELNOTES | 20 | ||||
-rw-r--r-- | client/Makefile.in | 1 | ||||
-rw-r--r-- | client/tests/Makefile.in | 1 | ||||
-rw-r--r-- | common/Makefile.in | 1 | ||||
-rw-r--r-- | common/conflex.c | 24 | ||||
-rw-r--r-- | common/tests/Makefile.in | 1 | ||||
-rwxr-xr-x | configure | 165 | ||||
-rw-r--r-- | configure.ac | 53 | ||||
-rw-r--r-- | contrib/ldap/README.ldap | 41 | ||||
-rw-r--r-- | contrib/ldap/dhcp.schema | 28 | ||||
-rw-r--r-- | contrib/ldap/dhcpd-conf-to-ldap | 84 | ||||
-rw-r--r-- | dhcpctl/Makefile.in | 1 | ||||
-rw-r--r-- | includes/Makefile.am | 1 | ||||
-rw-r--r-- | includes/Makefile.in | 2 | ||||
-rw-r--r-- | includes/config.h.in | 12 | ||||
-rw-r--r-- | includes/dhcpd.h | 7 | ||||
-rw-r--r-- | includes/ldap_casa.h | 2 | ||||
-rw-r--r-- | includes/ldap_krb_helper.h | 48 | ||||
-rw-r--r-- | omapip/Makefile.in | 1 | ||||
-rw-r--r-- | relay/Makefile.in | 1 | ||||
-rw-r--r-- | server/Makefile.am | 5 | ||||
-rw-r--r-- | server/Makefile.in | 27 | ||||
-rw-r--r-- | server/ldap.c | 1679 | ||||
-rw-r--r-- | server/ldap_casa.c | 4 | ||||
-rw-r--r-- | server/ldap_krb_helper.c | 216 | ||||
-rw-r--r-- | server/mdb.c | 5 | ||||
-rw-r--r-- | server/stables.c | 5 | ||||
-rw-r--r-- | server/tests/Makefile.in | 1 | ||||
-rw-r--r-- | tests/Makefile.in | 1 |
30 files changed, 2121 insertions, 317 deletions
diff --git a/Makefile.in b/Makefile.in index 8ce8974c..e5abacec 100644 --- a/Makefile.in +++ b/Makefile.in @@ -253,6 +253,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDAP_CFLAGS = @LDAP_CFLAGS@ +LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ @@ -179,6 +179,26 @@ by Eric Young (eay@cryptsoft.com). class has been deleted. [ISC-Bugs #39978] +- LDAP Patches - Numerous small patches submitted by contributors have + been applied to the contributed code which supplies LDAP support. + In addition, two larger submissions have also been included. The + first adds support for IPv6 configuration adn the second provides + GSSAPI authentication. + [ISC-Bugs #39056] + [ISC-Bugs #22742] + [ISC-Bugs #24449] + [ISC-Bugs #28545] + [ISC-Bugs #29873] + [ISC-Bugs #30183] + [ISC-Bugs #30402] + [ISC-Bugs #32217] + [ISC-Bugs #32240] + [ISC-Bugs #33176] + [ISC-Bugs #33178] + [ISC-Bugs #36409] + [ISC-Bugs #36774] + [ISC-Bugs #37876] + Changes since 4.3.2rc2 - None diff --git a/client/Makefile.in b/client/Makefile.in index 72fce7c8..a5311be2 100644 --- a/client/Makefile.in +++ b/client/Makefile.in @@ -260,6 +260,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDAP_CFLAGS = @LDAP_CFLAGS@ +LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ diff --git a/client/tests/Makefile.in b/client/tests/Makefile.in index 303eb130..f1ebbf11 100644 --- a/client/tests/Makefile.in +++ b/client/tests/Makefile.in @@ -233,6 +233,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDAP_CFLAGS = @LDAP_CFLAGS@ +LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ diff --git a/common/Makefile.in b/common/Makefile.in index ade60f17..8f1f432d 100644 --- a/common/Makefile.in +++ b/common/Makefile.in @@ -267,6 +267,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDAP_CFLAGS = @LDAP_CFLAGS@ +LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ diff --git a/common/conflex.c b/common/conflex.c index 39110160..f23a8c78 100644 --- a/common/conflex.c +++ b/common/conflex.c @@ -147,13 +147,21 @@ save_parse_state(struct parse *cfile) { /* * Return the parser to the previous saved state. * - * You must call save_parse_state() before calling - * restore_parse_state(), but you can call restore_parse_state() any - * number of times after that. + * You must call save_parse_state() every time before calling + * restore_parse_state(). + * + * Note: When the read function callback is in use in ldap mode, + * a call to get_char() may reallocate the buffer and will append + * config data to the buffer until a state restore. + * Do not restore to the (freed) pointer and size, but use new one. */ isc_result_t restore_parse_state(struct parse *cfile) { struct parse *saved_state; +#if defined(LDAP_CONFIGURATION) + char *inbuf = cfile->inbuf; + size_t size = cfile->bufsiz; +#endif if (cfile->saved_state == NULL) { return DHCP_R_NOTYET; @@ -161,7 +169,13 @@ restore_parse_state(struct parse *cfile) { saved_state = cfile->saved_state; memcpy(cfile, saved_state, sizeof(*cfile)); - cfile->saved_state = saved_state; + dfree(saved_state, MDL); + cfile->saved_state = NULL; + +#if defined(LDAP_CONFIGURATION) + cfile->inbuf = inbuf; + cfile->bufsiz = size; +#endif return ISC_R_SUCCESS; } @@ -476,6 +490,8 @@ read_whitespace(int c, struct parse *cfile) { } cfile->tokbuf[ofs++] = c; c = get_char(cfile); + if (c == EOF) + return END_OF_FILE; } while (!((c == '\n') && cfile->eol_token) && isascii(c) && isspace(c)); diff --git a/common/tests/Makefile.in b/common/tests/Makefile.in index 8fdddf63..1d174d12 100644 --- a/common/tests/Makefile.in +++ b/common/tests/Makefile.in @@ -271,6 +271,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDAP_CFLAGS = @LDAP_CFLAGS@ +LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ @@ -626,6 +626,7 @@ am__EXEEXT_TRUE LTLIBOBJS LIBOBJS LDAP_CFLAGS +LDAP_LIBS ac_prefix_program HAVE_ATF_FALSE HAVE_ATF_TRUE @@ -766,6 +767,8 @@ with_relay6_pid_file with_libbind with_ldap with_ldapcrypto +with_ldap_gssapi +with_ldapcasa ' ac_precious_vars='build_alias host_alias @@ -1463,6 +1466,10 @@ Optional Packages: --with-ldap enable OpenLDAP support in dhcpd (default is no) --with-ldapcrypto enable OpenLDAP crypto support in dhcpd (default is no) + --with-ldap-gssapi enable krb5/gssapi authentication for OpenLDAP in + dhcpd (default is no) + --with-ldapcasa enable LDAP CASA auth support in dhcpd (default is + no) Some influential environment variables: CC C compiler command @@ -6674,10 +6681,34 @@ else fi +# Gssapi to allow LDAP to authenticate with a keytab + +# Check whether --with-ldap-gssapi was given. +if test "${with_ldap_gssapi+set}" = set; then : + withval=$with_ldap_gssapi; ldap_gssapi=$withval +else + ldap_gssapi=no +fi + + + +# LDAP CASA auth support. + +# Check whether --with-ldapcasa was given. +if test "${with_ldapcasa+set}" = set; then : + withval=$with_ldapcasa; ldapcasa=$withval +else + ldapcasa=no +fi + + # OpenLDAP support is disabled by default, if enabled then SSL support is an # extra optional that is also disabled by default. Enabling LDAP SSL support -# implies enabling LDAP support. -if test x$ldap = xyes || test x$ldapcrypto = xyes ; then +# implies enabling LDAP support. Similarly, KRB5 support implies LDAP support, +# but doesn't include SSL. The two are not dependant. +if test x$ldap = xyes || test x$ldapcrypto = xyes || test x$ldap_gssapi = xyes; then + saved_LIBS="$LIBS" + LIBS="" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing ldap_initialize" >&5 $as_echo_n "checking for library containing ldap_initialize... " >&6; } if ${ac_cv_search_ldap_initialize+:} false; then : @@ -6800,14 +6831,136 @@ as_fn_error $? "*** Cannot find ber_pvt_opt_on with -llber - do you need to inst See \`config.log' for more details" "$LINENO" 5; } fi + if test x$ldap_gssapi = xyes ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing krb5_init_context" >&5 +$as_echo_n "checking for library containing krb5_init_context... " >&6; } +if ${ac_cv_search_krb5_init_context+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char krb5_init_context (); +int +main () +{ +return krb5_init_context (); + ; + return 0; +} +_ACEOF +for ac_lib in '' krb5; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_krb5_init_context=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_krb5_init_context+:} false; then : + break +fi +done +if ${ac_cv_search_krb5_init_context+:} false; then : + +else + ac_cv_search_krb5_init_context=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_krb5_init_context" >&5 +$as_echo "$ac_cv_search_krb5_init_context" >&6; } +ac_res=$ac_cv_search_krb5_init_context +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "*** Cannot find krb5_init_context with -lkrb5 - do you need to install a Kerberos Devel package? +See \`config.log' for more details" "$LINENO" 5; } +fi + + fi + + # Create LDAP_LIBS which we specify them explicitly rather than lumping them in with LIBS + LDAP_LIBS=$LIBS + + LIBS="$saved_LIBS" + + + for ac_header in ldap.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "ldap.h" "ac_cv_header_ldap_h" "$ac_includes_default" +if test "x$ac_cv_header_ldap_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LDAP_H 1 +_ACEOF + +fi + +done + + for ac_func in inet_pton inet_ntop +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done - if test x$ldapcrypto = xyes ; then - LDAP_CFLAGS="-DLDAP_CONFIGURATION -DLDAP_USE_SSL" - else - LDAP_CFLAGS="-DLDAP_CONFIGURATION" + LDAP_CFLAGS="-DLDAP_CONFIGURATION" + + if test x$ldapcasa = xyes ; then + for ac_header in micasa_mgmd.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "micasa_mgmd.h" "ac_cv_header_micasa_mgmd_h" "$ac_includes_default" +if test "x$ac_cv_header_micasa_mgmd_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_MICASA_MGMD_H 1 +_ACEOF + + LDAP_CFLAGS="$LDAP_CFLAGS -DLDAP_CASA_AUTH" + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "*** Cannot find micasa_mgmd.h for ldap casa auth support +See \`config.log' for more details" "$LINENO" 5; } +fi + +done + + fi + + if test x$ldapcrypto = xyes ; then + LDAP_CFLAGS="$LDAP_CFLAGS -DLDAP_USE_SSL" + fi + + if test x$ldap_gssapi = xyes; then + LDAP_CFLAGS="$LDAP_CFLAGS -DLDAP_USE_GSSAPI" fi + + LDAP_CFLAGS=$LDAP_CFLAGS + fi # Append selected warning levels to CFLAGS before substitution (but after diff --git a/configure.ac b/configure.ac index 6200d812..a9c3b8f4 100644 --- a/configure.ac +++ b/configure.ac @@ -636,20 +636,63 @@ AC_ARG_WITH(ldapcrypto, [ldapcrypto=$withval], [ldapcrypto=no]) +# Gssapi to allow LDAP to authenticate with a keytab +AC_ARG_WITH(ldap-gssapi, + AC_HELP_STRING([--with-ldap-gssapi], + [enable krb5/gssapi authentication for OpenLDAP in dhcpd (default is no)]), + [ldap_gssapi=$withval], + [ldap_gssapi=no]) + + +# LDAP CASA auth support. +AC_ARG_WITH(ldapcasa, + AC_HELP_STRING([--with-ldapcasa], + [enable LDAP CASA auth support in dhcpd (default is no)]), + [ldapcasa=$withval], + [ldapcasa=no]) + # OpenLDAP support is disabled by default, if enabled then SSL support is an # extra optional that is also disabled by default. Enabling LDAP SSL support -# implies enabling LDAP support. -if test x$ldap = xyes || test x$ldapcrypto = xyes ; then +# implies enabling LDAP support. Similarly, KRB5 support implies LDAP support, +# but doesn't include SSL. The two are not dependant. +if test x$ldap = xyes || test x$ldapcrypto = xyes || test x$ldap_gssapi = xyes; then + saved_LIBS="$LIBS" + LIBS="" AC_SEARCH_LIBS(ldap_initialize, [ldap], , AC_MSG_FAILURE([*** Cannot find ldap_initialize with -lldap - do you need to install an OpenLDAP2 Devel package?])) AC_SEARCH_LIBS(ber_pvt_opt_on, [lber], , AC_MSG_FAILURE([*** Cannot find ber_pvt_opt_on with -llber - do you need to install an OpenLDAP2 Devel package?])) + if test x$ldap_gssapi = xyes ; then + AC_SEARCH_LIBS(krb5_init_context, [krb5], , + AC_MSG_FAILURE([*** Cannot find krb5_init_context with -lkrb5 - do you need to install a Kerberos Devel package?])) + fi + + # Create LDAP_LIBS which we specify them explicitly rather than lumping them in with LIBS + AC_SUBST(LDAP_LIBS, [$LIBS]) + LIBS="$saved_LIBS" + + + AC_CHECK_HEADERS([ldap.h]) + AC_CHECK_FUNCS([inet_pton inet_ntop]) + + + LDAP_CFLAGS="-DLDAP_CONFIGURATION" + + if test x$ldapcasa = xyes ; then + AC_CHECK_HEADERS([micasa_mgmd.h],[ + LDAP_CFLAGS="$LDAP_CFLAGS -DLDAP_CASA_AUTH" + ], AC_MSG_FAILURE([*** Cannot find micasa_mgmd.h for ldap casa auth support])) + fi if test x$ldapcrypto = xyes ; then - AC_SUBST(LDAP_CFLAGS, ["-DLDAP_CONFIGURATION -DLDAP_USE_SSL"]) - else - AC_SUBST(LDAP_CFLAGS, ["-DLDAP_CONFIGURATION"]) + LDAP_CFLAGS="$LDAP_CFLAGS -DLDAP_USE_SSL" fi + + if test x$ldap_gssapi = xyes; then + LDAP_CFLAGS="$LDAP_CFLAGS -DLDAP_USE_GSSAPI" + fi + + AC_SUBST(LDAP_CFLAGS, [$LDAP_CFLAGS]) fi # Append selected warning levels to CFLAGS before substitution (but after diff --git a/contrib/ldap/README.ldap b/contrib/ldap/README.ldap index c4137907..5e4691e8 100644 --- a/contrib/ldap/README.ldap +++ b/contrib/ldap/README.ldap @@ -83,6 +83,12 @@ options: ldap-tls-reqcert, ldap-tls-ca-file, ldap-tls-ca-dir, ldap-tls-cert ldap-tls-key, ldap-tls-crlcheck, ldap-tls-ciphers, ldap-tls-randfile +The ldap-init-retry <num> enables an optional ldap connect retry loop with +the specified number of retries with a one second sleep between each try +during the initial startup of the dhcp server. +It allows to catch the condition, that the (remote) ldap server is not yet +started at the start time of the dhcp server. + All of these parameters should be self explanatory except for the ldap-method. You can set this to static or dynamic. If you set it to static, the configuration is read once on startup, and LDAP isn't used anymore. But, if @@ -189,3 +195,38 @@ into problems reading the configuration, try running dhcpd with the -d flag. If you still have problems, edit the site.conf file in the DHCP source and add the line: COPTS= -DDEBUG_LDAP and recompile DHCP. (make sure you run make clean and rerun configure before you rebuild). + +DHCPv6 requires a separate instance of the dhcpd server from the +DHCPv4 server. + +It is convenient to use distinct LDAP login DNs for the two servers, +and setup LDAP access restrictions in the LDAP server, so that each +DHCP server only has access to its own data. + +You will need to create a separate configuration file, +call it /etc/dhcpd6.conf. For example: + +ldap-server "localhost"; +ldap-port 389; +ldap-username "cn=DHCPv6 User, dc=ntelos, dc=net"; +ldap-password "blahblah"; +ldap-base-dn "dc=ntelos, dc=net"; +ldap-method dynamic; +ldap-debug-file "/var/log/dhcp-ldap-startup.log"; + +And use these command line arguments to dhcpd: + +dhcpd eth... -6 -cf /etc/dhcpd6.conf -pf /var/run/dhcpd6.pid -lf /var/lib/dhcpd6/dhcpd.leases + +For DHCPv6, the client configuration is the same, but substitute the +Client ID for the Ethernet hardware address. Here is an example of a +host definition for a DHCPv6 client: + +dn: cn=examplehost,cn=XXXX:XXXX:XXXX:XXXX::/64,cn=Network-eth1,cn=DHCPv6,dc=example,dc=com +objectClass: top +objectClass: dhcpHost +cn: examplehost +dhcpClientId: XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX +dhcpStatements: fixed-address6 XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX +option host-name "examplehost.ipv6.example.com" +option domain-name "ipv6.example.com" diff --git a/contrib/ldap/dhcp.schema b/contrib/ldap/dhcp.schema index c5ed6c72..0c24a7a2 100644 --- a/contrib/ldap/dhcp.schema +++ b/contrib/ldap/dhcp.schema @@ -334,6 +334,18 @@ attributetype ( 2.16.840.1.113719.1.203.4.56 DESC 'Generic attribute that allows coments within any DHCP object' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE ) +attributetype ( 2.16.840.1.113719.1.203.4.57 + NAME 'dhcpClientId' + EQUALITY caseIgnoreIA5Match + DESC 'client Identifier.' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) + +attributetype ( 2.16.840.1.113719.1.203.4.58 + NAME 'dhcpRange6' + EQUALITY caseIgnoreIA5Match + DESC 'The starting & ending IP Addresses in the range (inclusive), separated by a hyphen; if the range only contains one address, then just the address can be specified with no hyphen. Each range is defined as a separate value.' + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 ) + # Classes objectclass ( 2.16.840.1.113719.1.203.6.1 @@ -378,7 +390,7 @@ objectclass ( 2.16.840.1.113719.1.203.6.6 DESC 'This represents information about a particular client' SUP top MUST cn - MAY (dhcpLeaseDN $ dhcpHWAddress $ dhcpOptionsDN $ dhcpStatements $ dhcpComments $ dhcpOption) + MAY (dhcpLeaseDN $ dhcpHWAddress $ dhcpOptionsDN $ dhcpStatements $ dhcpComments $ dhcpOption $ dhcpClientId) X-NDS_CONTAINMENT ('dhcpService' 'dhcpSubnet' 'dhcpGroup') ) objectclass ( 2.16.840.1.113719.1.203.6.7 @@ -459,4 +471,18 @@ objectclass ( 2.16.840.1.113719.1.203.6.16 MAY ( dhcpServiceDN $dhcpServerDN $ dhcpSharedNetworkDN $ dhcpSubnetDN $ dhcpPoolDN $ dhcpGroupDN $ dhcpHostDN $ dhcpClassesDN $ dhcpKeyDN $ dhcpZoneDN $ dhcpFailOverPeerDN $ dhcpOption $ dhcpComments) X-NDS_CONTAINMENT ('organization' 'organizationalunit' 'domain') ) +objectclass ( 2.16.840.1.113719.1.203.6.17 + NAME 'dhcpSubnet6' + DESC 'This class defines an IPv6 subnet. This is a container object.' + SUP top + MUST ( cn ) + MAY ( dhcpRange6 $ dhcpPoolDN $ dhcpGroupDN $ dhcpHostDN $ dhcpClassesDN $ dhcpLeasesDN $ dhcpOptionsDN $ dhcpZoneDN $ dhcpKeyDN $ dhcpFailOverPeerDN $ dhcpStatements $ dhcpComments $ dhcpOption $ dhcpPermitList ) X-NDS_CONTAINMENT ('dhcpService' 'dhcpSharedNetwork') ) + +objectclass ( 2.16.840.1.113719.1.203.6.18 + NAME 'dhcpPool6' + DESC 'This stores configuration information about an IPv6 pool.' + SUP top + MUST ( cn $ dhcpRange6 ) + MAY ( dhcpClassesDN $ dhcpPermitList $ dhcpLeasesDN $ dhcpOptionsDN $ dhcpZoneDN $dhcpKeyDN $ dhcpStatements $ dhcpComments $ dhcpOption ) + X-NDS_CONTAINMENT ('dhcpSubnet' 'dhcpSharedNetwork') ) diff --git a/contrib/ldap/dhcpd-conf-to-ldap b/contrib/ldap/dhcpd-conf-to-ldap index aee6c979..95064c6f 100644 --- a/contrib/ldap/dhcpd-conf-to-ldap +++ b/contrib/ldap/dhcpd-conf-to-ldap @@ -137,6 +137,7 @@ add_dn_to_stack local ($dn) = @_; $current_dn = "$dn, $current_dn"; + $curentry{'current_dn'} = $current_dn; } @@ -154,6 +155,26 @@ parse_error exit (1); } +sub +new_entry +{ + if (%curentry) { + $curentry{'current_dn'} = $current_dn; + push(@entrystack, {%curentry}); + undef(%curentry); + } +} + +sub +pop_entry +{ + if (%curentry) { + push(@outputlist, {%curentry}); + } + $rentry = pop(@entrystack); + %curentry = %$rentry if $rentry; +} + sub print_entry @@ -167,7 +188,7 @@ print_entry print "cn: $server\n"; print "objectClass: top\n"; print "objectClass: dhcpServer\n"; - print "dhcpServiceDN: $current_dn\n"; + print "dhcpServiceDN: $curentry{'current_dn'}\n"; if(grep(/FaIlOvEr/i, @use)) { foreach my $fo_peer (keys %failover) @@ -179,7 +200,7 @@ print_entry } print "\n"; - print "dn: $current_dn\n"; + print "dn: $curentry{'current_dn'}\n"; print "cn: $dhcpcn\n"; print "objectClass: top\n"; print "objectClass: dhcpService\n"; @@ -195,7 +216,7 @@ print_entry } elsif ($curentry{'type'} eq 'subnet') { - print "dn: $current_dn\n"; + print "dn: $curentry{'current_dn'}\n"; print "cn: " . $curentry{'ip'} . "\n"; print "objectClass: top\n"; print "objectClass: dhcpSubnet\n"; @@ -215,7 +236,7 @@ print_entry } elsif ($curentry{'type'} eq 'shared-network') { - print "dn: $current_dn\n"; + print "dn: $curentry{'current_dn'}\n"; print "cn: " . $curentry{'descr'} . "\n"; print "objectClass: top\n"; print "objectClass: dhcpSharedNetwork\n"; @@ -226,7 +247,7 @@ print_entry } elsif ($curentry{'type'} eq 'group') { - print "dn: $current_dn\n"; + print "dn: $curentry{'current_dn'}\n"; print "cn: group", $curentry{'idx'}, "\n"; print "objectClass: top\n"; print "objectClass: dhcpGroup\n"; @@ -237,7 +258,7 @@ print_entry } elsif ($curentry{'type'} eq 'host') { - print "dn: $current_dn\n"; + print "dn: $curentry{'current_dn'}\n"; print "cn: " . $curentry{'host'} . "\n"; print "objectClass: top\n"; print "objectClass: dhcpHost\n"; @@ -254,7 +275,7 @@ print_entry } elsif ($curentry{'type'} eq 'pool') { - print "dn: $current_dn\n"; + print "dn: $curentry{'current_dn'}\n"; print "cn: pool", $curentry{'idx'}, "\n"; print "objectClass: top\n"; print "objectClass: dhcpPool\n"; @@ -273,7 +294,7 @@ print_entry } elsif ($curentry{'type'} eq 'class') { - print "dn: $current_dn\n"; + print "dn: $curentry{'current_dn'}\n"; print "cn: " . $curentry{'class'} . "\n"; print "objectClass: top\n"; print "objectClass: dhcpClass\n"; @@ -284,7 +305,7 @@ print_entry } elsif ($curentry{'type'} eq 'subclass') { - print "dn: $current_dn\n"; + print "dn: $curentry{'current_dn'}\n"; print "cn: " . $curentry{'subclass'} . "\n"; print "objectClass: top\n"; print "objectClass: dhcpSubClass\n"; @@ -344,7 +365,7 @@ sub parse_subnet { local ($ip, $tmp, $netmask); - print_entry () if %curentry; + new_entry (); $ip = next_token (0); parse_error () if !defined ($ip); @@ -374,7 +395,7 @@ sub parse_shared_network { local ($descr, $tmp); - print_entry () if %curentry; + new_entry (); $descr = next_token (0); parse_error () if !defined ($descr); @@ -393,7 +414,7 @@ sub parse_host { local ($descr, $tmp); - print_entry () if %curentry; + new_entry (); $host = next_token (0); parse_error () if !defined ($host); @@ -412,7 +433,7 @@ sub parse_group { local ($descr, $tmp); - print_entry () if %curentry; + new_entry (); $tmp = next_token (0); parse_error () if !defined ($tmp); @@ -435,7 +456,7 @@ sub parse_pool { local ($descr, $tmp); - print_entry () if %curentry; + new_entry (); $tmp = next_token (0); parse_error () if !defined ($tmp); @@ -458,7 +479,7 @@ sub parse_class { local ($descr, $tmp); - print_entry () if %curentry; + new_entry (); $class = next_token (0); parse_error () if !defined ($class); @@ -478,7 +499,7 @@ sub parse_subclass { local ($descr, $tmp); - print_entry () if %curentry; + new_entry (); $class = next_token (0); parse_error () if !defined ($class); @@ -486,14 +507,23 @@ sub parse_subclass $subclass = next_token (0); parse_error () if !defined ($subclass); - $tmp = next_token (0); - parse_error () if !defined ($tmp); - parse_error () if !($tmp eq '{'); - + if (substr($subclass,-1) eq ';') { + $tmp = ";"; + $subclass = substr($subclass,0,-1); + } else { + $tmp = next_token (0); + parse_error () if !defined ($tmp); + } + parse_error () if !($tmp eq '{' or $tmp eq ';'); add_dn_to_stack ("cn=$subclass"); $curentry{'type'} = 'subclass'; $curentry{'class'} = $class; $curentry{'subclass'} = $subclass; + + if ($tmp eq ';') { + pop_entry (); + remove_dn_from_stack (); + } } @@ -682,11 +712,11 @@ print STDERR "\n"; my $token; my $token_number = 0; my $line_number = 0; -my %curentry; my $cursubnet = ''; my %curcounter = ( '' => { pool => 0, group => 0 } ); $current_dn = "$dhcpdn"; +$curentry{'current_dn'} = $current_dn; $curentry{'descr'} = $dhcpcn; $line = ''; %failover = (); @@ -695,7 +725,7 @@ while (($token = next_token (1))) { if ($token eq '}') { - print_entry () if %curentry; + pop_entry (); if($current_dn =~ /.+?,\s*${dhcpdn}$/) { # don't go below dhcpdn ... remove_dn_from_stack (); @@ -753,6 +783,16 @@ while (($token = next_token (1))) } } +pop_entry (); + +while ($#outputlist >= 0) { + $rentry = pop(@outputlist); + if ($rentry) { + %curentry = %$rentry; + print_entry (); + } +} + close(STDIN) if($i_conf); close(STDOUT) if($o_ldif); diff --git a/dhcpctl/Makefile.in b/dhcpctl/Makefile.in index 0141b748..70f2d907 100644 --- a/dhcpctl/Makefile.in +++ b/dhcpctl/Makefile.in @@ -235,6 +235,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDAP_CFLAGS = @LDAP_CFLAGS@ +LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ diff --git a/includes/Makefile.am b/includes/Makefile.am index 4a462d5b..185b397a 100644 --- a/includes/Makefile.am +++ b/includes/Makefile.am @@ -6,6 +6,7 @@ nobase_include_HEADERS = omapip/alloc.h omapip/buffer.h omapip/convert.h \ EXTRA_DIST = cdefs.h ctrace.h dhcp.h dhcp6.h dhcpd.h dhctoken.h failover.h \ heap.h inet.h minires.h osdep.h site.h statement.h tree.h \ t_api.h \ + ldap_casa.h ldap_krb_helper.h \ arpa/nameser.h arpa/nameser_compat.h \ netinet/if_ether.h netinet/ip.h netinet/ip_icmp.h netinet/udp.h diff --git a/includes/Makefile.in b/includes/Makefile.in index 45c31889..4d8d4a5c 100644 --- a/includes/Makefile.in +++ b/includes/Makefile.in @@ -189,6 +189,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDAP_CFLAGS = @LDAP_CFLAGS@ +LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ @@ -270,6 +271,7 @@ nobase_include_HEADERS = omapip/alloc.h omapip/buffer.h omapip/convert.h \ EXTRA_DIST = cdefs.h ctrace.h dhcp.h dhcp6.h dhcpd.h dhctoken.h failover.h \ heap.h inet.h minires.h osdep.h site.h statement.h tree.h \ t_api.h \ + ldap_casa.h ldap_krb_helper.h \ arpa/nameser.h arpa/nameser_compat.h \ netinet/if_ether.h netinet/ip.h netinet/ip_icmp.h netinet/udp.h diff --git a/includes/config.h.in b/includes/config.h.in index 4eaee831..1730b8ec 100644 --- a/includes/config.h.in +++ b/includes/config.h.in @@ -52,9 +52,18 @@ /* Define to 1 if you have the <ifaddrs.h> header file. */ #undef HAVE_IFADDRS_H +/* Define to 1 if you have the `inet_ntop' function. */ +#undef HAVE_INET_NTOP + +/* Define to 1 if you have the `inet_pton' function. */ +#undef HAVE_INET_PTON + /* Define to 1 if you have the <inttypes.h> header file. */ #undef HAVE_INTTYPES_H +/* Define to 1 if you have the <ldap.h> header file. */ +#undef HAVE_LDAP_H + /* Define to 1 if you have the <linux/types.h> header file. */ #undef HAVE_LINUX_TYPES_H @@ -64,6 +73,9 @@ /* Define to 1 if you have the <memory.h> header file. */ #undef HAVE_MEMORY_H +/* Define to 1 if you have the <micasa_mgmd.h> header file. */ +#undef HAVE_MICASA_MGMD_H + /* Define to 1 if you have the <net/if6.h> header file. */ #undef HAVE_NET_IF6_H diff --git a/includes/dhcpd.h b/includes/dhcpd.h index 20bf30e9..1fd12dbb 100644 --- a/includes/dhcpd.h +++ b/includes/dhcpd.h @@ -774,6 +774,11 @@ struct lease_state { # define SV_LDAP_TLS_CIPHERS 76 # define SV_LDAP_TLS_RANDFILE 77 #endif +# define SV_LDAP_INIT_RETRY 178 +#if defined (LDAP_USE_GSSAPI) +# define SV_LDAP_GSSAPI_KEYTAB 179 +# define SV_LDAP_GSSAPI_PRINCIPAL 180 +#endif #endif #define SV_CACHE_THRESHOLD 78 #define SV_DONT_USE_FSYNC 79 @@ -3648,6 +3653,8 @@ int find_haddr_in_ldap (struct host_decl **, int, unsigned, const unsigned char *, const char *, int); int find_subclass_in_ldap (struct class *, struct class **, struct data_string *); +int find_client_in_ldap (struct host_decl **, struct packet*, + struct option_state *, const char *, int); #endif /* mdb6.c */ diff --git a/includes/ldap_casa.h b/includes/ldap_casa.h index 1437e414..f17690f7 100644 --- a/includes/ldap_casa.h +++ b/includes/ldap_casa.h @@ -59,8 +59,6 @@ #define __LDAP_CASA_H__ #include <micasa_mgmd.h> -#include <dlfcn.h> -#include <string.h> #define MICASA_LIB "libmicasa.so.1" diff --git a/includes/ldap_krb_helper.h b/includes/ldap_krb_helper.h new file mode 100644 index 00000000..879bc447 --- /dev/null +++ b/includes/ldap_krb_helper.h @@ -0,0 +1,48 @@ +/* ldap_krb_helper.h + + Helper routings for allowing LDAP to read configuration with GSSAPI/krb auth */ + +/* + * Copyright (c) 2014 William B. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This helper was written by William Brown <william@adelaide.edu.au>, + * inspired by krb5_helper.c from bind-dyndb-ldap by Simo Sorce (Redhat) + */ +#ifndef LDAP_KRB5_HELPER +#define LDAP_KRB5_HELPER + +#if defined(LDAP_USE_GSSAPI) +#include <krb5.h> + +extern isc_result_t krb5_get_tgt(const char *, const char *); +#endif + +#endif /* LDAP_KRB5_HELPER */ diff --git a/omapip/Makefile.in b/omapip/Makefile.in index 6086e9f0..fb8d3a4f 100644 --- a/omapip/Makefile.in +++ b/omapip/Makefile.in @@ -232,6 +232,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDAP_CFLAGS = @LDAP_CFLAGS@ +LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ diff --git a/relay/Makefile.in b/relay/Makefile.in index 5d9f9859..13c992f4 100644 --- a/relay/Makefile.in +++ b/relay/Makefile.in @@ -213,6 +213,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDAP_CFLAGS = @LDAP_CFLAGS@ +LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ diff --git a/server/Makefile.am b/server/Makefile.am index 614b4082..2d7cba43 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -10,12 +10,13 @@ dist_sysconf_DATA = dhcpd.conf.example sbin_PROGRAMS = dhcpd dhcpd_SOURCES = dhcpd.c dhcp.c bootp.c confpars.c db.c class.c failover.c \ omapi.c mdb.c stables.c salloc.c ddns.c dhcpleasequery.c \ - dhcpv6.c mdb6.c ldap.c ldap_casa.c leasechain.c + dhcpv6.c mdb6.c ldap.c ldap_casa.c leasechain.c ldap_krb_helper.c dhcpd_CFLAGS = $(LDAP_CFLAGS) dhcpd_LDADD = ../common/libdhcp.a ../omapip/libomapi.a \ ../dhcpctl/libdhcpctl.a ../bind/lib/libirs.a \ - ../bind/lib/libdns.a ../bind/lib/libisccfg.a ../bind/lib/libisc.a + ../bind/lib/libdns.a ../bind/lib/libisccfg.a ../bind/lib/libisc.a \ + $(LDAP_LIBS) man_MANS = dhcpd.8 dhcpd.conf.5 dhcpd.leases.5 EXTRA_DIST = $(man_MANS) diff --git a/server/Makefile.in b/server/Makefile.in index ae1bee87..5507cfde 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -102,12 +102,14 @@ am_dhcpd_OBJECTS = dhcpd-dhcpd.$(OBJEXT) dhcpd-dhcp.$(OBJEXT) \ dhcpd-salloc.$(OBJEXT) dhcpd-ddns.$(OBJEXT) \ dhcpd-dhcpleasequery.$(OBJEXT) dhcpd-dhcpv6.$(OBJEXT) \ dhcpd-mdb6.$(OBJEXT) dhcpd-ldap.$(OBJEXT) \ - dhcpd-ldap_casa.$(OBJEXT) dhcpd-leasechain.$(OBJEXT) + dhcpd-ldap_casa.$(OBJEXT) dhcpd-leasechain.$(OBJEXT) \ + dhcpd-ldap_krb_helper.$(OBJEXT) dhcpd_OBJECTS = $(am_dhcpd_OBJECTS) +am__DEPENDENCIES_1 = dhcpd_DEPENDENCIES = ../common/libdhcp.a ../omapip/libomapi.a \ ../dhcpctl/libdhcpctl.a ../bind/lib/libirs.a \ ../bind/lib/libdns.a ../bind/lib/libisccfg.a \ - ../bind/lib/libisc.a + ../bind/lib/libisc.a $(am__DEPENDENCIES_1) dhcpd_LINK = $(CCLD) $(dhcpd_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) @@ -274,6 +276,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDAP_CFLAGS = @LDAP_CFLAGS@ +LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ @@ -357,12 +360,13 @@ AM_CPPFLAGS = -I.. -DLOCALSTATEDIR='"@localstatedir@"' dist_sysconf_DATA = dhcpd.conf.example dhcpd_SOURCES = dhcpd.c dhcp.c bootp.c confpars.c db.c class.c failover.c \ omapi.c mdb.c stables.c salloc.c ddns.c dhcpleasequery.c \ - dhcpv6.c mdb6.c ldap.c ldap_casa.c leasechain.c + dhcpv6.c mdb6.c ldap.c ldap_casa.c leasechain.c ldap_krb_helper.c dhcpd_CFLAGS = $(LDAP_CFLAGS) dhcpd_LDADD = ../common/libdhcp.a ../omapip/libomapi.a \ ../dhcpctl/libdhcpctl.a ../bind/lib/libirs.a \ - ../bind/lib/libdns.a ../bind/lib/libisccfg.a ../bind/lib/libisc.a + ../bind/lib/libdns.a ../bind/lib/libisccfg.a ../bind/lib/libisc.a \ + $(LDAP_LIBS) man_MANS = dhcpd.8 dhcpd.conf.5 dhcpd.leases.5 EXTRA_DIST = $(man_MANS) @@ -465,6 +469,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-failover.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-ldap.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-ldap_casa.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-ldap_krb_helper.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-leasechain.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-mdb.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dhcpd-mdb6.Po@am__quote@ @@ -737,6 +742,20 @@ dhcpd-leasechain.obj: leasechain.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='leasechain.c' object='dhcpd-leasechain.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-leasechain.obj `if test -f 'leasechain.c'; then $(CYGPATH_W) 'leasechain.c'; else $(CYGPATH_W) '$(srcdir)/leasechain.c'; fi` + +dhcpd-ldap_krb_helper.o: ldap_krb_helper.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-ldap_krb_helper.o -MD -MP -MF $(DEPDIR)/dhcpd-ldap_krb_helper.Tpo -c -o dhcpd-ldap_krb_helper.o `test -f 'ldap_krb_helper.c' || echo '$(srcdir)/'`ldap_krb_helper.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dhcpd-ldap_krb_helper.Tpo $(DEPDIR)/dhcpd-ldap_krb_helper.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ldap_krb_helper.c' object='dhcpd-ldap_krb_helper.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-ldap_krb_helper.o `test -f 'ldap_krb_helper.c' || echo '$(srcdir)/'`ldap_krb_helper.c + +dhcpd-ldap_krb_helper.obj: ldap_krb_helper.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -MT dhcpd-ldap_krb_helper.obj -MD -MP -MF $(DEPDIR)/dhcpd-ldap_krb_helper.Tpo -c -o dhcpd-ldap_krb_helper.obj `if test -f 'ldap_krb_helper.c'; then $(CYGPATH_W) 'ldap_krb_helper.c'; else $(CYGPATH_W) '$(srcdir)/ldap_krb_helper.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dhcpd-ldap_krb_helper.Tpo $(DEPDIR)/dhcpd-ldap_krb_helper.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ldap_krb_helper.c' object='dhcpd-ldap_krb_helper.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dhcpd_CFLAGS) $(CFLAGS) -c -o dhcpd-ldap_krb_helper.obj `if test -f 'ldap_krb_helper.c'; then $(CYGPATH_W) 'ldap_krb_helper.c'; else $(CYGPATH_W) '$(srcdir)/ldap_krb_helper.c'; fi` install-man5: $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ diff --git a/server/ldap.c b/server/ldap.c index 2994106a..b5f0bc0d 100644 --- a/server/ldap.c +++ b/server/ldap.c @@ -40,6 +40,11 @@ #include "dhcpd.h" #include <signal.h> #include <errno.h> +#include <ctype.h> +#include <netdb.h> +#include <net/if.h> +#include <ifaddrs.h> +#include <string.h> #if defined(LDAP_CONFIGURATION) @@ -47,6 +52,11 @@ #include "ldap_casa.h" #endif +#if defined(LDAP_USE_GSSAPI) +#include <sasl/sasl.h> +#include "ldap_krb_helper.h" +#endif + static LDAP * ld = NULL; static char *ldap_server = NULL, *ldap_username = NULL, @@ -57,7 +67,9 @@ static char *ldap_server = NULL, static int ldap_port = LDAP_PORT, ldap_method = LDAP_METHOD_DYNAMIC, ldap_referrals = -1, - ldap_debug_fd = -1; + ldap_debug_fd = -1, + ldap_enable_retry = -1, + ldap_init_retry = -1; #if defined (LDAP_USE_SSL) static int ldap_use_ssl = -1, /* try TLS if possible */ ldap_tls_reqcert = -1, @@ -69,6 +81,25 @@ static char *ldap_tls_ca_file = NULL, *ldap_tls_ciphers = NULL, *ldap_tls_randfile = NULL; #endif + +#if defined (LDAP_USE_GSSAPI) +static char *ldap_gssapi_keytab = NULL, + *ldap_gssapi_principal = NULL; + +struct ldap_sasl_instance { + char *sasl_mech; + char *sasl_realm; + char *sasl_authz_id; + char *sasl_authc_id; + char *sasl_password; +}; + +static struct ldap_sasl_instance *ldap_sasl_inst = NULL; + +static int +_ldap_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *sin) ; +#endif + static struct ldap_config_stack *ldap_stack = NULL; typedef struct ldap_dn_node { @@ -80,12 +111,269 @@ typedef struct ldap_dn_node { static ldap_dn_node *ldap_service_dn_head = NULL; static ldap_dn_node *ldap_service_dn_tail = NULL; +static int ldap_read_function (struct parse *cfile); + +static struct parse * +x_parser_init(const char *name) +{ + struct parse *cfile; + isc_result_t res; + char *inbuf; + + inbuf = dmalloc (LDAP_BUFFER_SIZE, MDL); + if (inbuf == NULL) + return NULL; + + cfile = (struct parse *) NULL; + res = new_parse (&cfile, -1, inbuf, LDAP_BUFFER_SIZE, name, 0); + if (res != ISC_R_SUCCESS) + { + dfree(inbuf, MDL); + return NULL; + } + /* the buffer is still empty */ + cfile->bufsiz = LDAP_BUFFER_SIZE; + cfile->buflen = cfile->bufix = 0; + /* attach ldap read function */ + cfile->read_function = ldap_read_function; + return cfile; +} + +static isc_result_t +x_parser_free(struct parse **cfile) +{ + if (cfile && *cfile) + { + if ((*cfile)->inbuf) + dfree((*cfile)->inbuf, MDL); + (*cfile)->inbuf = NULL; + (*cfile)->bufsiz = 0; + return end_parse(cfile); + } + return ISC_R_SUCCESS; +} + +static int +x_parser_resize(struct parse *cfile, size_t len) +{ + size_t size; + char * temp; + + /* grow by len rounded up at LDAP_BUFFER_SIZE */ + size = cfile->bufsiz + (len | (LDAP_BUFFER_SIZE-1)) + 1; + + /* realloc would be better, but there isn't any */ + if ((temp = dmalloc (size, MDL)) != NULL) + { +#if defined (DEBUG_LDAP) + log_info ("Reallocated %s buffer from %zu to %zu", + cfile->tlname, cfile->bufsiz, size); +#endif + memcpy(temp, cfile->inbuf, cfile->bufsiz); + dfree(cfile->inbuf, MDL); + cfile->inbuf = temp; + cfile->bufsiz = size; + return 1; + } + + /* + * Hmm... what is worser, consider it as fatal error and + * bail out completely or discard config data in hope it + * is "only" an option in dynamic host lookup? + */ + log_error("Unable to reallocated %s buffer from %zu to %zu", + cfile->tlname, cfile->bufsiz, size); + return 0; +} static char * -x_strncat(char *dst, const char *src, size_t dst_size) +x_parser_strcat(struct parse *cfile, const char *str) +{ + size_t cur = strlen(cfile->inbuf); + size_t len = strlen(str); + size_t cnt; + + if (cur + len >= cfile->bufsiz && !x_parser_resize(cfile, len)) + return NULL; + + cnt = cfile->bufsiz > cur ? cfile->bufsiz - cur - 1 : 0; + return strncat(cfile->inbuf, str, cnt); +} + +static inline void +x_parser_reset(struct parse *cfile) +{ + cfile->inbuf[0] = '\0'; + cfile->bufix = cfile->buflen = 0; +} + +static inline size_t +x_parser_length(struct parse *cfile) +{ + cfile->buflen = strlen(cfile->inbuf); + return cfile->buflen; +} + +static char * +x_strxform(char *dst, const char *src, size_t dst_size, + int (*xform)(int)) +{ + if(dst && src && dst_size) + { + size_t len, pos; + + len = strlen(src); + for(pos=0; pos < len && pos + 1 < dst_size; pos++) + dst[pos] = xform((int)src[pos]); + dst[pos] = '\0'; + + return dst; + } + return NULL; +} + +static int +get_host_entry(char *fqdnname, size_t fqdnname_size, + char *hostaddr, size_t hostaddr_size) +{ +#if defined(MAXHOSTNAMELEN) + char hname[MAXHOSTNAMELEN+1]; +#else + char hname[65]; +#endif + struct hostent *hp; + + if (NULL == fqdnname || 1 >= fqdnname_size) + return -1; + + memset(hname, 0, sizeof(hname)); + if (gethostname(hname, sizeof(hname)-1)) + return -1; + + if (NULL == (hp = gethostbyname(hname))) + return -1; + + strncpy(fqdnname, hp->h_name, fqdnname_size-1); + fqdnname[fqdnname_size-1] = '\0'; + + if (hostaddr != NULL) + { + if (hp->h_addr != NULL) + { + struct in_addr *aptr = (struct in_addr *)hp->h_addr; +#if defined(HAVE_INET_NTOP) + if (hostaddr_size >= INET_ADDRSTRLEN && + inet_ntop(AF_INET, aptr, hostaddr, hostaddr_size) != NULL) + { + return 0; + } +#else + char *astr = inet_ntoa(*aptr); + size_t alen = strlen(astr); + if (astr && alen > 0 && hostaddr_size > alen) + { + strncpy(hostaddr, astr, hostaddr_size-1); + hostaddr[hostaddr_size-1] = '\0'; + return 0; + } +#endif + } + return -1; + } + return 0; +} + +static int +is_iface_address(struct ifaddrs *addrs, struct in_addr *addr) { - size_t len = strlen(dst); - return strncat(dst, src, dst_size > len ? dst_size - len - 1: 0); + struct ifaddrs *ia; + struct sockaddr_in *sa; + int num = 0; + + if(addrs == NULL || addr == NULL) + return -1; + + for (ia = addrs; ia != NULL; ia = ia->ifa_next) + { + ++num; + if (ia->ifa_addr && (ia->ifa_flags & IFF_UP) && + ia->ifa_addr->sa_family == AF_INET) + { + sa = (struct sockaddr_in *)(ia->ifa_addr); + if (addr->s_addr == sa->sin_addr.s_addr) + return num; + } + } + return 0; +} + +static int +get_host_address(const char *hostname, char *hostaddr, size_t hostaddr_size, struct ifaddrs *addrs) +{ + if (hostname && *hostname && hostaddr && hostaddr_size) + { + struct in_addr addr; + +#if defined(HAVE_INET_PTON) + if (inet_pton(AF_INET, hostname, &addr) == 1) +#else + if (inet_aton(hostname, &addr) != 0) +#endif + { + /* it is already IP address string */ + if(strlen(hostname) < hostaddr_size) + { + strncpy(hostaddr, hostname, hostaddr_size-1); + hostaddr[hostaddr_size-1] = '\0'; + + if (addrs != NULL && is_iface_address (addrs, &addr) > 0) + return 1; + else + return 0; + } + } + else + { + struct hostent *hp; + if ((hp = gethostbyname(hostname)) != NULL && hp->h_addr != NULL) + { + struct in_addr *aptr = (struct in_addr *)hp->h_addr; + int mret = 0; + + if (addrs != NULL) + { + char **h; + for (h=hp->h_addr_list; *h; h++) + { + struct in_addr *haddr = (struct in_addr *)*h; + if (is_iface_address (addrs, haddr) > 0) + { + aptr = haddr; + mret = 1; + } + } + } + +#if defined(HAVE_INET_NTOP) + if (hostaddr_size >= INET_ADDRSTRLEN && + inet_ntop(AF_INET, aptr, hostaddr, hostaddr_size) != NULL) + { + return mret; + } +#else + char *astr = inet_ntoa(*aptr); + size_t alen = strlen(astr); + if (astr && alen > 0 && alen < hostaddr_size) + { + strncpy(hostaddr, astr, hostaddr_size-1); + hostaddr[hostaddr_size-1] = '\0'; + return mret; + } +#endif + } + } + } + return -1; } static void @@ -102,19 +390,52 @@ ldap_parse_class (struct ldap_config_stack *item, struct parse *cfile) return; } - x_strncat (cfile->inbuf, "class \"", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, "\" {\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "class \""); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, "\" {\n"); item->close_brace = 1; ldap_value_free_len (tempbv); } +static int +is_hex_string(const char *str) +{ + int colon = 1; + int xdigit = 0; + size_t i; + + if (!str) + return 0; + + if (*str == '-') + str++; + + for (i=0; str[i]; ++i) + { + if (str[i] == ':') + { + xdigit = 0; + if(++colon > 1) + return 0; + } + else if(isxdigit((unsigned char)str[i])) + { + colon = 0; + if (++xdigit > 2) + return 0; + } + else + return 0; + } + return i > 0 && !colon; +} static void ldap_parse_subclass (struct ldap_config_stack *item, struct parse *cfile) { struct berval **tempbv, **classdata; + char *tmp; if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) == NULL || tempbv[0] == NULL) @@ -136,11 +457,22 @@ ldap_parse_subclass (struct ldap_config_stack *item, struct parse *cfile) return; } - x_strncat (cfile->inbuf, "subclass ", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, classdata[0]->bv_val, LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, " ", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, " {\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "subclass \""); + x_parser_strcat (cfile, classdata[0]->bv_val); + if (is_hex_string(tempbv[0]->bv_val)) + { + x_parser_strcat (cfile, "\" "); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, " {\n"); + } + else + { + tmp = quotify_string(tempbv[0]->bv_val, MDL); + x_parser_strcat (cfile, "\" \""); + x_parser_strcat (cfile, tmp); + x_parser_strcat (cfile, "\" {\n"); + dfree(tmp, MDL); + } item->close_brace = 1; ldap_value_free_len (tempbv); @@ -164,14 +496,18 @@ ldap_parse_host (struct ldap_config_stack *item, struct parse *cfile) hwaddr = ldap_get_values_len (ld, item->ldent, "dhcpHWAddress"); - x_strncat (cfile->inbuf, "host ", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "host "); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, " {\n"); - if (hwaddr != NULL && hwaddr[0] != NULL) + if (hwaddr != NULL) { - x_strncat (cfile->inbuf, " {\nhardware ", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, hwaddr[0]->bv_val, LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE); + if (hwaddr[0] != NULL) + { + x_parser_strcat (cfile, "hardware "); + x_parser_strcat (cfile, hwaddr[0]->bv_val); + x_parser_strcat (cfile, ";\n"); + } ldap_value_free_len (hwaddr); } @@ -194,9 +530,9 @@ ldap_parse_shared_network (struct ldap_config_stack *item, struct parse *cfile) return; } - x_strncat (cfile->inbuf, "shared-network \"", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, "\" {\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "shared-network \""); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, "\" {\n"); item->close_brace = 1; ldap_value_free_len (tempbv); @@ -249,14 +585,14 @@ ldap_parse_subnet (struct ldap_config_stack *item, struct parse *cfile) return; } - x_strncat (cfile->inbuf, "subnet ", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "subnet "); + x_parser_strcat (cfile, tempbv[0]->bv_val); - x_strncat (cfile->inbuf, " netmask ", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, " netmask "); parse_netmask (strtol (netmaskstr[0]->bv_val, NULL, 10), netmaskbuf); - x_strncat (cfile->inbuf, netmaskbuf, LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, netmaskbuf); - x_strncat (cfile->inbuf, " {\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, " {\n"); ldap_value_free_len (tempbv); ldap_value_free_len (netmaskstr); @@ -265,16 +601,63 @@ ldap_parse_subnet (struct ldap_config_stack *item, struct parse *cfile) { for (i=0; tempbv[i] != NULL; i++) { - x_strncat (cfile->inbuf, "range", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, " ", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, tempbv[i]->bv_val, LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "range"); + x_parser_strcat (cfile, " "); + x_parser_strcat (cfile, tempbv[i]->bv_val); + x_parser_strcat (cfile, ";\n"); } + ldap_value_free_len (tempbv); } item->close_brace = 1; } +static void +ldap_parse_subnet6 (struct ldap_config_stack *item, struct parse *cfile) +{ + struct berval **tempbv; + int i; + + if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) == NULL || + tempbv[0] == NULL) + { + if (tempbv != NULL) + ldap_value_free_len (tempbv); + + return; + } + + x_parser_strcat (cfile, "subnet6 "); + x_parser_strcat (cfile, tempbv[0]->bv_val); + + x_parser_strcat (cfile, " {\n"); + + ldap_value_free_len (tempbv); + + if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpRange")) != NULL) + { + for (i=0; tempbv[i] != NULL; i++) + { + x_parser_strcat (cfile, "range6"); + x_parser_strcat (cfile, " "); + x_parser_strcat (cfile, tempbv[i]->bv_val); + x_parser_strcat (cfile, ";\n"); + } + ldap_value_free_len (tempbv); + } + + if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpPermitList")) != NULL) + { + for (i=0; tempbv[i] != NULL; i++) + { + x_parser_strcat (cfile, tempbv[i]->bv_val); + x_parser_strcat (cfile, ";\n"); + } + ldap_value_free_len (tempbv); + } + + item->close_brace = 1; +} static void ldap_parse_pool (struct ldap_config_stack *item, struct parse *cfile) @@ -282,17 +665,17 @@ ldap_parse_pool (struct ldap_config_stack *item, struct parse *cfile) struct berval **tempbv; int i; - x_strncat (cfile->inbuf, "pool {\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "pool {\n"); if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpRange")) != NULL) { - x_strncat (cfile->inbuf, "range", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "range"); for (i=0; tempbv[i] != NULL; i++) { - x_strncat (cfile->inbuf, " ", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, tempbv[i]->bv_val, LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, " "); + x_parser_strcat (cfile, tempbv[i]->bv_val); } - x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, ";\n"); ldap_value_free_len (tempbv); } @@ -300,8 +683,8 @@ ldap_parse_pool (struct ldap_config_stack *item, struct parse *cfile) { for (i=0; tempbv[i] != NULL; i++) { - x_strncat (cfile->inbuf, tempbv[i]->bv_val, LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, tempbv[i]->bv_val); + x_parser_strcat (cfile, ";\n"); } ldap_value_free_len (tempbv); } @@ -309,11 +692,43 @@ ldap_parse_pool (struct ldap_config_stack *item, struct parse *cfile) item->close_brace = 1; } +static void +ldap_parse_pool6 (struct ldap_config_stack *item, struct parse *cfile) +{ + struct berval **tempbv; + int i; + + x_parser_strcat (cfile, "pool {\n"); + + if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpRange")) != NULL) + { + x_parser_strcat (cfile, "range6"); + for (i=0; tempbv[i] != NULL; i++) + { + x_parser_strcat (cfile, " "); + x_parser_strcat (cfile, tempbv[i]->bv_val); + } + x_parser_strcat (cfile, ";\n"); + ldap_value_free_len (tempbv); + } + + if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpPermitList")) != NULL) + { + for (i=0; tempbv[i] != NULL; i++) + { + x_parser_strcat(cfile, tempbv[i]->bv_val); + x_parser_strcat (cfile, ";\n"); + } + ldap_value_free_len (tempbv); + } + + item->close_brace = 1; +} static void ldap_parse_group (struct ldap_config_stack *item, struct parse *cfile) { - x_strncat (cfile->inbuf, "group {\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "group {\n"); item->close_brace = 1; } @@ -325,25 +740,25 @@ ldap_parse_key (struct ldap_config_stack *item, struct parse *cfile) if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) != NULL) { - x_strncat (cfile->inbuf, "key ", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, " {\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "key "); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, " {\n"); ldap_value_free_len (tempbv); } if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpKeyAlgorithm")) != NULL) { - x_strncat (cfile->inbuf, "algorithm ", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "algorithm "); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, ";\n"); ldap_value_free_len (tempbv); } if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpKeySecret")) != NULL) { - x_strncat (cfile->inbuf, "secret ", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "secret "); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, ";\n"); ldap_value_free_len (tempbv); } @@ -361,18 +776,18 @@ ldap_parse_zone (struct ldap_config_stack *item, struct parse *cfile) if ((tempbv = ldap_get_values_len (ld, item->ldent, "cn")) != NULL) { - x_strncat (cfile->inbuf, "zone ", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, " {\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "zone "); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, " {\n"); ldap_value_free_len (tempbv); } if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpDnsZoneServer")) != NULL) { - x_strncat (cfile->inbuf, "primary ", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, tempbv[0]->bv_val, LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "primary "); + x_parser_strcat (cfile, tempbv[0]->bv_val); - x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, ";\n"); ldap_value_free_len (tempbv); } @@ -400,9 +815,9 @@ ldap_parse_zone (struct ldap_config_stack *item, struct parse *cfile) strncpy (keyCn, cnFindStart, len); keyCn[len] = '\0'; - x_strncat (cfile->inbuf, "key ", LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, keyCn, LDAP_BUFFER_SIZE); - x_strncat (cfile->inbuf, ";\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "key "); + x_parser_strcat (cfile, keyCn); + x_parser_strcat (cfile, ";\n"); dfree (keyCn, MDL); } @@ -415,6 +830,227 @@ ldap_parse_zone (struct ldap_config_stack *item, struct parse *cfile) static void +ldap_parse_failover (struct ldap_config_stack *item, struct parse *cfile) +{ + struct berval **tempbv, **peername; + struct ifaddrs *addrs = NULL; + char srvaddr[2][64] = {"\0", "\0"}; + int primary, split = 0, match; + + if ((peername = ldap_get_values_len (ld, item->ldent, "cn")) == NULL || + peername[0] == NULL) + { + if (peername != NULL) + ldap_value_free_len (peername); + + // ldap with disabled schema checks? fail to avoid syntax error. + log_error("Unable to find mandatory failover peering name attribute"); + return; + } + + /* Get all interface addresses */ + getifaddrs(&addrs); + + /* + ** when dhcpFailOverPrimaryServer or dhcpFailOverSecondaryServer + ** matches one of our IP address, the following valiables are set: + ** - primary is 1 when we are primary or 0 when we are secondary + ** - srvaddr[0] contains ip address of the primary + ** - srvaddr[1] contains ip address of the secondary + */ + primary = -1; + if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverPrimaryServer")) != NULL && + tempbv[0] != NULL) + { + match = get_host_address (tempbv[0]->bv_val, srvaddr[0], sizeof(srvaddr[0]), addrs); + if (match >= 0) + { + /* we are the primary */ + if (match > 0) + primary = 1; + } + else + { + log_info("Can't resolve address of the primary failover '%s' server %s", + peername[0]->bv_val, tempbv[0]->bv_val); + ldap_value_free_len (tempbv); + ldap_value_free_len (peername); + if (addrs) + freeifaddrs(addrs); + return; + } + } + if (tempbv != NULL) + ldap_value_free_len (tempbv); + + if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverSecondaryServer")) != NULL && + tempbv[0] != NULL) + { + match = get_host_address (tempbv[0]->bv_val, srvaddr[1], sizeof(srvaddr[1]), addrs); + if (match >= 0) + { + if (match > 0) + { + if (primary == 1) + { + log_info("Both, primary and secondary failover '%s' server" + " attributes match our local address", peername[0]->bv_val); + ldap_value_free_len (tempbv); + ldap_value_free_len (peername); + if (addrs) + freeifaddrs(addrs); + return; + } + + /* we are the secondary */ + primary = 0; + } + } + else + { + log_info("Can't resolve address of the secondary failover '%s' server %s", + peername[0]->bv_val, tempbv[0]->bv_val); + ldap_value_free_len (tempbv); + ldap_value_free_len (peername); + if (addrs) + freeifaddrs(addrs); + return; + } + } + if (tempbv != NULL) + ldap_value_free_len (tempbv); + + + if (primary == -1 || srvaddr[0] == '\0' || srvaddr[1] == '\0') + { + log_error("Could not decide if the server type is primary" + " or secondary for failover peering '%s'.", peername[0]->bv_val); + ldap_value_free_len (peername); + if (addrs) + freeifaddrs(addrs); + return; + } + + x_parser_strcat (cfile, "failover peer \""); + x_parser_strcat (cfile, peername[0]->bv_val); + x_parser_strcat (cfile, "\" {\n"); + + if (primary) + x_parser_strcat (cfile, "primary;\n"); + else + x_parser_strcat (cfile, "secondary;\n"); + + x_parser_strcat (cfile, "address "); + if (primary) + x_parser_strcat (cfile, srvaddr[0]); + else + x_parser_strcat (cfile, srvaddr[1]); + x_parser_strcat (cfile, ";\n"); + + x_parser_strcat (cfile, "peer address "); + if (primary) + x_parser_strcat (cfile, srvaddr[1]); + else + x_parser_strcat (cfile, srvaddr[0]); + x_parser_strcat (cfile, ";\n"); + + if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverPrimaryPort")) != NULL && + tempbv[0] != NULL) + { + if (primary) + x_parser_strcat (cfile, "port "); + else + x_parser_strcat (cfile, "peer port "); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, ";\n"); + } + if (tempbv != NULL) + ldap_value_free_len (tempbv); + + if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverSecondaryPort")) != NULL && + tempbv[0] != NULL) + { + if (primary) + x_parser_strcat (cfile, "peer port "); + else + x_parser_strcat (cfile, "port "); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, ";\n"); + } + if (tempbv != NULL) + ldap_value_free_len (tempbv); + + if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverResponseDelay")) != NULL && + tempbv[0] != NULL) + { + x_parser_strcat (cfile, "max-response-delay "); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, ";\n"); + } + if (tempbv != NULL) + ldap_value_free_len (tempbv); + + if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverUnackedUpdates")) != NULL && + tempbv[0] != NULL) + { + x_parser_strcat (cfile, "max-unacked-updates "); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, ";\n"); + } + if (tempbv != NULL) + ldap_value_free_len (tempbv); + + if ((tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverLoadBalanceTime")) != NULL && + tempbv[0] != NULL) + { + x_parser_strcat (cfile, "load balance max seconds "); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, ";\n"); + } + if (tempbv != NULL) + ldap_value_free_len (tempbv); + + tempbv = NULL; + if (primary && + (tempbv = ldap_get_values_len (ld, item->ldent, "dhcpMaxClientLeadTime")) != NULL && + tempbv[0] != NULL) + { + x_parser_strcat (cfile, "mclt "); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, ";\n"); + } + if (tempbv != NULL) + ldap_value_free_len (tempbv); + + tempbv = NULL; + if (primary && + (tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverSplit")) != NULL && + tempbv[0] != NULL) + { + x_parser_strcat (cfile, "split "); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, ";\n"); + split = 1; + } + if (tempbv != NULL) + ldap_value_free_len (tempbv); + + tempbv = NULL; + if (primary && !split && + (tempbv = ldap_get_values_len (ld, item->ldent, "dhcpFailOverHashBucketAssignment")) != NULL && + tempbv[0] != NULL) + { + x_parser_strcat (cfile, "hba "); + x_parser_strcat (cfile, tempbv[0]->bv_val); + x_parser_strcat (cfile, ";\n"); + } + if (tempbv != NULL) + ldap_value_free_len (tempbv); + + item->close_brace = 1; +} + +static void add_to_config_stack (LDAPMessage * res, LDAPMessage * ent) { struct ldap_config_stack *ns; @@ -428,7 +1064,6 @@ add_to_config_stack (LDAPMessage * res, LDAPMessage * ent) ldap_stack = ns; } - static void ldap_stop() { @@ -570,6 +1205,7 @@ ldap_rebind_cb (LDAP *ld, LDAP_CONST char *url, ber_tag_t request, ber_int_t msg { log_error ("Error: Cannot init LDAPS session to %s:%d: %s", ldapurl->lud_host, ldapurl->lud_port, ldap_err2string (ret)); + ldap_free_urldesc(ldapurl); return ret; } else @@ -585,6 +1221,7 @@ ldap_rebind_cb (LDAP *ld, LDAP_CONST char *url, ber_tag_t request, ber_int_t msg { log_error ("Error: Cannot start TLS session to %s:%d: %s", ldapurl->lud_host, ldapurl->lud_port, ldap_err2string (ret)); + ldap_free_urldesc(ldapurl); return ret; } else @@ -595,21 +1232,80 @@ ldap_rebind_cb (LDAP *ld, LDAP_CONST char *url, ber_tag_t request, ber_int_t msg } #endif +#if defined(LDAP_USE_GSSAPI) + if (ldap_gssapi_principal != NULL) { + krb5_get_tgt(ldap_gssapi_principal, ldap_gssapi_keytab); + if ((ret = ldap_sasl_interactive_bind_s(ld, NULL, ldap_sasl_inst->sasl_mech, + NULL, NULL, LDAP_SASL_AUTOMATIC, + _ldap_sasl_interact, ldap_sasl_inst) + ) != LDAP_SUCCESS) + { + log_error ("Error: Cannot SASL bind to ldap server %s:%d: %s", + ldap_server, ldap_port, ldap_err2string (ret)); + char *msg=NULL; + ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&msg); + log_error ("\tAdditional info: %s", msg); + ldap_memfree(msg); + ldap_stop(); + } - if (ldap_username != NULL || *ldap_username != '\0') + ldap_free_urldesc(ldapurl); + return ret; + } +#endif + + if (ldap_username != NULL && *ldap_username != '\0' && ldap_password != NULL) { who = ldap_username; creds.bv_val = strdup(ldap_password); + if (creds.bv_val == NULL) + log_fatal ("Error: Unable to allocate memory to duplicate ldap_password"); + creds.bv_len = strlen(ldap_password); - } - if ((ret = ldap_sasl_bind_s (ld, who, LDAP_SASL_SIMPLE, &creds, - NULL, NULL, NULL)) != LDAP_SUCCESS) + if ((ret = ldap_sasl_bind_s (ld, who, LDAP_SASL_SIMPLE, &creds, + NULL, NULL, NULL)) != LDAP_SUCCESS) + { + log_error ("Error: Cannot login into ldap server %s:%d: %s", + ldapurl->lud_host, ldapurl->lud_port, ldap_err2string (ret)); + } + + if (creds.bv_val) + free(creds.bv_val); + } + + ldap_free_urldesc(ldapurl); + return ret; +} + +static int +_do_ldap_retry(int ret, const char *server, int port) +{ + static int inform = 1; + + if (ldap_enable_retry > 0 && ret == LDAP_SERVER_DOWN && ldap_init_retry > 0) { - log_error ("Error: Cannot login into ldap server %s:%d: %s", - ldapurl->lud_host, ldapurl->lud_port, ldap_err2string (ret)); + if (inform || (ldap_init_retry % 10) == 0) + { + inform = 0; + log_info ("Can't contact LDAP server %s:%d: retrying for %d sec", + server, port, ldap_init_retry); + } + sleep(1); + return ldap_init_retry--; } - return ret; + return 0; +} + +static struct berval * +_do_ldap_str2esc_filter_bv(const char *str, ber_len_t len, struct berval *bv_o) +{ + struct berval bv_i; + + if (!str || !bv_o || (ber_str2bv(str, len, 0, &bv_i) == NULL) || + (ldap_bv2escaped_filter_value(&bv_i, bv_o) != 0)) + return NULL; + return bv_o; } static void @@ -619,6 +1315,12 @@ ldap_start (void) int ret, version; char *uri = NULL; struct berval creds; +#if defined(LDAP_USE_GSSAPI) + char *gssapi_realm = NULL; + char *gssapi_user = NULL; + char *running = NULL; + const char *gssapi_delim = "@"; +#endif if (ld != NULL) return; @@ -641,6 +1343,7 @@ ldap_start (void) ldap_debug_file = _do_lookup_dhcp_string_option (options, SV_LDAP_DEBUG_FILE); ldap_referrals = _do_lookup_dhcp_enum_option (options, SV_LDAP_REFERRALS); + ldap_init_retry = _do_lookup_dhcp_int_option (options, SV_LDAP_INIT_RETRY); #if defined (LDAP_USE_SSL) ldap_use_ssl = _do_lookup_dhcp_enum_option (options, SV_LDAP_SSL); @@ -657,6 +1360,56 @@ ldap_start (void) } #endif +#if defined (LDAP_USE_GSSAPI) + ldap_gssapi_principal = _do_lookup_dhcp_string_option (options, + SV_LDAP_GSSAPI_PRINCIPAL); + + if (ldap_gssapi_principal == NULL) { + log_error("ldap_gssapi_principal is not set," + "GSSAPI Authentication for LDAP will not be used"); + } else { + ldap_gssapi_keytab = _do_lookup_dhcp_string_option (options, + SV_LDAP_GSSAPI_KEYTAB); + if (ldap_gssapi_keytab == NULL) { + log_fatal("ldap_gssapi_keytab must be specified"); + } + + running = strdup(ldap_gssapi_principal); + if (running == NULL) + log_fatal("Could not allocate memory to duplicate gssapi principal"); + + gssapi_user = strtok(running, gssapi_delim); + if (!gssapi_user || strlen(gssapi_user) == 0) { + log_fatal ("GSSAPI principal must specify user: user@realm"); + } + + gssapi_realm = strtok(NULL, gssapi_delim); + if (!gssapi_realm || strlen(gssapi_realm) == 0) { + log_fatal ("GSSAPI principal must specify realm: user@realm"); + } + + ldap_sasl_inst = malloc(sizeof(struct ldap_sasl_instance)); + if (ldap_sasl_inst == NULL) + log_fatal("Could not allocate memory for sasl instance! Can not run!"); + + ldap_sasl_inst->sasl_mech = ber_strdup("GSSAPI"); + if (ldap_sasl_inst->sasl_mech == NULL) + log_fatal("Could not allocate memory to duplicate gssapi mechanism"); + + ldap_sasl_inst->sasl_realm = ber_strdup(gssapi_realm); + if (ldap_sasl_inst->sasl_realm == NULL) + log_fatal("Could not allocate memory to duplicate gssapi realm"); + + ldap_sasl_inst->sasl_authz_id = ber_strdup(gssapi_user); + if (ldap_sasl_inst->sasl_authz_id == NULL) + log_fatal("Could not allocate memory to duplicate gssapi user"); + + ldap_sasl_inst->sasl_authc_id = NULL; + ldap_sasl_inst->sasl_password = NULL; //"" before + free(running); + } +#endif + #if defined (LDAP_CASA_AUTH) if (!load_uname_pwd_from_miCASA(&ldap_username,&ldap_password)) { @@ -853,7 +1606,13 @@ ldap_start (void) } else if (ldap_use_ssl != LDAP_SSL_OFF) { - if ((ret = ldap_start_tls_s (ld, NULL, NULL)) != LDAP_SUCCESS) + do + { + ret = ldap_start_tls_s (ld, NULL, NULL); + } + while(_do_ldap_retry(ret, ldap_server, ldap_port) > 0); + + if (ret != LDAP_SUCCESS) { log_error ("Error: Cannot start TLS session to %s:%d: %s", ldap_server, ldap_port, ldap_err2string (ret)); @@ -868,13 +1627,43 @@ ldap_start (void) } #endif - if (ldap_username != NULL && *ldap_username != '\0') +#if defined(LDAP_USE_GSSAPI) + if (ldap_gssapi_principal != NULL) { + krb5_get_tgt(ldap_gssapi_principal, ldap_gssapi_keytab); + if ((ret = ldap_sasl_interactive_bind_s(ld, NULL, ldap_sasl_inst->sasl_mech, + NULL, NULL, LDAP_SASL_AUTOMATIC, + _ldap_sasl_interact, ldap_sasl_inst) + ) != LDAP_SUCCESS) + { + log_error ("Error: Cannot SASL bind to ldap server %s:%d: %s", + ldap_server, ldap_port, ldap_err2string (ret)); + char *msg=NULL; + ldap_get_option( ld, LDAP_OPT_DIAGNOSTIC_MESSAGE, (void*)&msg); + log_error ("\tAdditional info: %s", msg); + ldap_memfree(msg); + ldap_stop(); + return; + } + } else +#endif + + if (ldap_username != NULL && *ldap_username != '\0' && ldap_password != NULL) { creds.bv_val = strdup(ldap_password); + if (creds.bv_val == NULL) + log_fatal ("Error: Unable to allocate memory to duplicate ldap_password"); + creds.bv_len = strlen(ldap_password); - if ((ret = ldap_sasl_bind_s (ld, ldap_username, LDAP_SASL_SIMPLE, - &creds, NULL, NULL, NULL)) != LDAP_SUCCESS) + do + { + ret = ldap_sasl_bind_s (ld, ldap_username, LDAP_SASL_SIMPLE, + &creds, NULL, NULL, NULL); + } + while(_do_ldap_retry(ret, ldap_server, ldap_port) > 0); + free(creds.bv_val); + + if (ret != LDAP_SUCCESS) { log_error ("Error: Cannot login into ldap server %s:%d: %s", ldap_server, ldap_port, ldap_err2string (ret)); @@ -894,7 +1683,15 @@ parse_external_dns (LDAPMessage * ent) { char *search[] = {"dhcpOptionsDN", "dhcpSharedNetworkDN", "dhcpSubnetDN", "dhcpGroupDN", "dhcpHostDN", "dhcpClassesDN", - "dhcpPoolDN", NULL}; + "dhcpPoolDN", "dhcpZoneDN", "dhcpFailOverPeerDN", NULL}; + + /* TODO: dhcpKeyDN can't be added. It is referenced in dhcpDnsZone to + retrive the key name (cn). Adding keyDN will reflect adding a key + declaration inside the zone configuration. + + dhcpSubClassesDN cant be added. It is also similar to the above. + Needs schema change. + */ LDAPMessage * newres, * newent; struct berval **tempbv; int i, j, ret; @@ -934,7 +1731,7 @@ parse_external_dns (LDAPMessage * ent) } #if defined (DEBUG_LDAP) - log_info ("Adding contents of subtree '%s' to config stack from '%s' reference", tempbv[j], search[i]); + log_info ("Adding contents of subtree '%s' to config stack from '%s' reference", tempbv[j]->bv_val, search[i]); #endif for (newent = ldap_first_entry (ld, newres); newent != NULL; @@ -989,17 +1786,17 @@ next_ldap_entry (struct parse *cfile) if (ldap_stack != NULL && ldap_stack->close_brace) { - x_strncat (cfile->inbuf, "}\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "}\n"); ldap_stack->close_brace = 0; } while (ldap_stack != NULL && - (ldap_stack->ldent == NULL || - (ldap_stack->ldent = ldap_next_entry (ld, ldap_stack->ldent)) == NULL)) + (ldap_stack->ldent == NULL || ( ldap_stack->processed && + (ldap_stack->ldent = ldap_next_entry (ld, ldap_stack->ldent)) == NULL))) { if (ldap_stack->close_brace) { - x_strncat (cfile->inbuf, "}\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "}\n"); ldap_stack->close_brace = 0; } @@ -1010,7 +1807,7 @@ next_ldap_entry (struct parse *cfile) if (ldap_stack != NULL && ldap_stack->close_brace) { - x_strncat (cfile->inbuf, "}\n", LDAP_BUFFER_SIZE); + x_parser_strcat (cfile, "}\n"); ldap_stack->close_brace = 0; } } @@ -1066,13 +1863,13 @@ check_statement_end (const char *statement) static isc_result_t -ldap_parse_entry_options (LDAPMessage *ent, char *buffer, size_t size, +ldap_parse_entry_options (LDAPMessage *ent, struct parse *cfile, int *lease_limit) { struct berval **tempbv; int i; - if (ent == NULL || buffer == NULL || size == 0) + if (ent == NULL || cfile == NULL) return (ISC_R_FAILURE); if ((tempbv = ldap_get_values_len (ld, ent, "dhcpStatements")) != NULL) @@ -1086,16 +1883,16 @@ ldap_parse_entry_options (LDAPMessage *ent, char *buffer, size_t size, continue; } - x_strncat (buffer, tempbv[i]->bv_val, size); + x_parser_strcat (cfile, tempbv[i]->bv_val); switch((int) check_statement_end (tempbv[i]->bv_val)) { case '}': case ';': - x_strncat (buffer, "\n", size); + x_parser_strcat (cfile, "\n"); break; default: - x_strncat (buffer, ";\n", size); + x_parser_strcat (cfile, ";\n"); break; } } @@ -1106,15 +1903,15 @@ ldap_parse_entry_options (LDAPMessage *ent, char *buffer, size_t size, { for (i=0; tempbv[i] != NULL; i++) { - x_strncat (buffer, "option ", size); - x_strncat (buffer, tempbv[i]->bv_val, size); + x_parser_strcat (cfile, "option "); + x_parser_strcat (cfile, tempbv[i]->bv_val); switch ((int) check_statement_end (tempbv[i]->bv_val)) { case ';': - x_strncat (buffer, "\n", size); + x_parser_strcat (cfile, "\n"); break; default: - x_strncat (buffer, ";\n", size); + x_parser_strcat (cfile, ";\n"); break; } } @@ -1131,9 +1928,10 @@ ldap_generate_config_string (struct parse *cfile) struct berval **objectClass; char *dn; struct ldap_config_stack *entry; - LDAPMessage * ent, * res; + LDAPMessage * ent, * res, *entfirst, *resfirst; int i, ignore, found; - int ret; + int ret, parsedn = 1; + size_t len = cfile->buflen; if (ld == NULL) ldap_start (); @@ -1144,7 +1942,8 @@ ldap_generate_config_string (struct parse *cfile) if ((objectClass = ldap_get_values_len (ld, entry->ldent, "objectClass")) == NULL) return; - + + entry->processed = 1; ignore = 0; found = 1; for (i=0; objectClass[i] != NULL; i++) @@ -1155,14 +1954,20 @@ ldap_generate_config_string (struct parse *cfile) ldap_parse_class (entry, cfile); else if (strcasecmp (objectClass[i]->bv_val, "dhcpSubnet") == 0) ldap_parse_subnet (entry, cfile); + else if (strcasecmp (objectClass[i]->bv_val, "dhcpSubnet6") == 0) + ldap_parse_subnet6 (entry, cfile); else if (strcasecmp (objectClass[i]->bv_val, "dhcpPool") == 0) ldap_parse_pool (entry, cfile); + else if (strcasecmp (objectClass[i]->bv_val, "dhcpPool6") == 0) + ldap_parse_pool6 (entry, cfile); else if (strcasecmp (objectClass[i]->bv_val, "dhcpGroup") == 0) ldap_parse_group (entry, cfile); else if (strcasecmp (objectClass[i]->bv_val, "dhcpTSigKey") == 0) ldap_parse_key (entry, cfile); else if (strcasecmp (objectClass[i]->bv_val, "dhcpDnsZone") == 0) ldap_parse_zone (entry, cfile); + else if (strcasecmp (objectClass[i]->bv_val, "dhcpFailOverPeer") == 0) + ldap_parse_failover (entry, cfile); else if (strcasecmp (objectClass[i]->bv_val, "dhcpHost") == 0) { if (ldap_method == LDAP_METHOD_STATIC) @@ -1186,7 +1991,7 @@ ldap_generate_config_string (struct parse *cfile) else found = 0; - if (found && cfile->inbuf[0] == '\0') + if (found && x_parser_length(cfile) <= len) { ignore = 1; break; @@ -1201,23 +2006,36 @@ ldap_generate_config_string (struct parse *cfile) return; } - ldap_parse_entry_options(entry->ldent, cfile->inbuf, - LDAP_BUFFER_SIZE-1, NULL); + ldap_parse_entry_options(entry->ldent, cfile, NULL); dn = ldap_get_dn (ld, entry->ldent); - + if (dn == NULL) + { + ldap_stop(); + return; + } #if defined(DEBUG_LDAP) - if (dn != NULL) - log_info ("Found LDAP entry '%s'", dn); + log_info ("Found LDAP entry '%s'", dn); #endif - if (dn == NULL || - (ret = ldap_search_ext_s (ld, dn, LDAP_SCOPE_ONELEVEL, - "objectClass=*", NULL, 0, NULL, NULL, + if ((ret = ldap_search_ext_s (ld, dn, LDAP_SCOPE_ONELEVEL, + "(!(|(|(objectClass=dhcpTSigKey)(objectClass=dhcpClass)) (objectClass=dhcpFailOverPeer)))", + NULL, 0, NULL, NULL, NULL, 0, &res)) != LDAP_SUCCESS) { - if (dn) - ldap_memfree (dn); + ldap_memfree (dn); + + ldap_stop(); + return; + } + + if ((ret = ldap_search_ext_s (ld, dn, LDAP_SCOPE_ONELEVEL, + "(|(|(objectClass=dhcpTSigKey)(objectClass=dhcpClass)) (objectClass=dhcpFailOverPeer))", + NULL, 0, NULL, NULL, + NULL, 0, &resfirst)) != LDAP_SUCCESS) + { + ldap_memfree (dn); + ldap_msgfree (res); ldap_stop(); return; @@ -1225,17 +2043,33 @@ ldap_generate_config_string (struct parse *cfile) ldap_memfree (dn); - if ((ent = ldap_first_entry (ld, res)) != NULL) + ent = ldap_first_entry(ld, res); + entfirst = ldap_first_entry(ld, resfirst); + + if (ent == NULL && entfirst == NULL) + { + parse_external_dns (entry->ldent); + next_ldap_entry (cfile); + } + + if (ent != NULL) { add_to_config_stack (res, ent); parse_external_dns (entry->ldent); + parsedn = 0; } else + ldap_msgfree (res); + + if (entfirst != NULL) { - ldap_msgfree (res); - parse_external_dns (entry->ldent); - next_ldap_entry (cfile); + add_to_config_stack (resfirst, entfirst); + if(parsedn) + parse_external_dns (entry->ldent); + } + else + ldap_msgfree (resfirst); } @@ -1268,25 +2102,30 @@ ldap_write_debug (const void *buff, size_t size) static int ldap_read_function (struct parse *cfile) { - cfile->inbuf[0] = '\0'; - cfile->buflen = 0; - - while (ldap_stack != NULL && *cfile->inbuf == '\0') + size_t len; + + /* append when in saved state */ + if (cfile->saved_state == NULL) + { + cfile->inbuf[0] = '\0'; + cfile->bufix = 0; + cfile->buflen = 0; + } + len = cfile->buflen; + + while (ldap_stack != NULL && x_parser_length(cfile) <= len) ldap_generate_config_string (cfile); - if (ldap_stack == NULL && *cfile->inbuf == '\0') + if (x_parser_length(cfile) <= len && ldap_stack == NULL) return (EOF); - cfile->bufix = 1; - cfile->buflen = strlen (cfile->inbuf) - 1; - if (cfile->buflen > 0) - ldap_write_debug (cfile->inbuf, cfile->buflen); - + if (cfile->buflen > len) + ldap_write_debug (cfile->inbuf + len, cfile->buflen - len); #if defined (DEBUG_LDAP) - log_info ("Sending config line '%s'", cfile->inbuf); + log_info ("Sending config portion '%s'", cfile->inbuf + len); #endif - return (cfile->inbuf[0]); + return (cfile->inbuf[cfile->bufix++]); } @@ -1321,38 +2160,12 @@ ldap_get_host_name (LDAPMessage * ent) } -static int -getfqhostname(char *fqhost, size_t size) -{ -#if defined(MAXHOSTNAMELEN) - char hname[MAXHOSTNAMELEN]; -#else - char hname[65]; -#endif - struct hostent *hp; - - if(NULL == fqhost || 1 >= size) - return -1; - - memset(hname, 0, sizeof(hname)); - if( gethostname(hname, sizeof(hname)-1)) - return -1; - - if(NULL == (hp = gethostbyname(hname))) - return -1; - - strncpy(fqhost, hp->h_name, size-1); - fqhost[size-1] = '\0'; - return 0; -} - - isc_result_t ldap_read_config (void) { LDAPMessage * ldres, * hostres, * ent, * hostent; char hfilter[1024], sfilter[1024], fqdn[257]; - char *buffer, *hostdn; + char *hostdn; ldap_dn_node *curr = NULL; struct parse *cfile; struct utsname unme; @@ -1360,52 +2173,97 @@ ldap_read_config (void) size_t length; int ret, cnt; struct berval **tempbv = NULL; + struct berval bv_o[2]; + + cfile = x_parser_init("LDAP"); + if (cfile == NULL) + return (ISC_R_NOMEMORY); + ldap_enable_retry = 1; if (ld == NULL) ldap_start (); + ldap_enable_retry = 0; + if (ld == NULL) - return (ldap_server == NULL ? ISC_R_SUCCESS : ISC_R_FAILURE); - - buffer = dmalloc (LDAP_BUFFER_SIZE+1, MDL); - if (buffer == NULL) - return (ISC_R_FAILURE); + { + x_parser_free(&cfile); + return (ldap_server == NULL ? ISC_R_SUCCESS : ISC_R_FAILURE); + } - cfile = (struct parse *) NULL; - res = new_parse (&cfile, -1, buffer, LDAP_BUFFER_SIZE, "LDAP", 0); - if (res != ISC_R_SUCCESS) - return (res); - uname (&unme); if (ldap_dhcp_server_cn != NULL) { + if (_do_ldap_str2esc_filter_bv(ldap_dhcp_server_cn, 0, &bv_o[0]) == NULL) + { + log_error ("Cannot escape ldap filter value %s: %m", ldap_dhcp_server_cn); + x_parser_free(&cfile); + return (ISC_R_FAILURE); + } + snprintf (hfilter, sizeof (hfilter), - "(&(objectClass=dhcpServer)(cn=%s))", ldap_dhcp_server_cn); + "(&(objectClass=dhcpServer)(cn=%s))", bv_o[0].bv_val); + + ber_memfree(bv_o[0].bv_val); } else - { - if(0 == getfqhostname(fqdn, sizeof(fqdn))) { - snprintf (hfilter, sizeof (hfilter), - "(&(objectClass=dhcpServer)(|(cn=%s)(cn=%s)))", - unme.nodename, fqdn); + if (_do_ldap_str2esc_filter_bv(unme.nodename, 0, &bv_o[0]) == NULL) + { + log_error ("Cannot escape ldap filter value %s: %m", unme.nodename); + x_parser_free(&cfile); + return (ISC_R_FAILURE); + } + + *fqdn ='\0'; + if(0 == get_host_entry(fqdn, sizeof(fqdn), NULL, 0)) + { + if (_do_ldap_str2esc_filter_bv(fqdn, 0, &bv_o[1]) == NULL) + { + log_error ("Cannot escape ldap filter value %s: %m", fqdn); + ber_memfree(bv_o[0].bv_val); + x_parser_free(&cfile); + return (ISC_R_FAILURE); + } + } + + // If we have fqdn and it isn't the same as nodename, use it in filter + // otherwise just use nodename + if ((*fqdn) && (strcmp(unme.nodename, fqdn))) { + snprintf (hfilter, sizeof (hfilter), + "(&(objectClass=dhcpServer)(|(cn=%s)(cn=%s)))", + bv_o[0].bv_val, bv_o[1].bv_val); + + ber_memfree(bv_o[1].bv_val); + } + else + { + snprintf (hfilter, sizeof (hfilter), + "(&(objectClass=dhcpServer)(cn=%s))", + bv_o[0].bv_val); + } + + ber_memfree(bv_o[0].bv_val); } - else + + ldap_enable_retry = 1; + do { - snprintf (hfilter, sizeof (hfilter), - "(&(objectClass=dhcpServer)(cn=%s))", unme.nodename); + hostres = NULL; + ret = ldap_search_ext_s (ld, ldap_base_dn, LDAP_SCOPE_SUBTREE, + hfilter, NULL, 0, NULL, NULL, NULL, 0, + &hostres); } + while(_do_ldap_retry(ret, ldap_server, ldap_port) > 0); + ldap_enable_retry = 0; - } - hostres = NULL; - if ((ret = ldap_search_ext_s (ld, ldap_base_dn, LDAP_SCOPE_SUBTREE, - hfilter, NULL, 0, NULL, NULL, NULL, 0, - &hostres)) != LDAP_SUCCESS) + if(ret != LDAP_SUCCESS) { log_error ("Cannot find host LDAP entry %s %s", - ((ldap_dhcp_server_cn == NULL)?(unme.nodename):(ldap_dhcp_server_cn)), hfilter); + ((ldap_dhcp_server_cn == NULL)?(unme.nodename):(ldap_dhcp_server_cn)), hfilter); if(NULL != hostres) ldap_msgfree (hostres); ldap_stop(); + x_parser_free(&cfile); return (ISC_R_FAILURE); } @@ -1414,6 +2272,7 @@ ldap_read_config (void) log_error ("Error: Cannot find LDAP entry matching %s", hfilter); ldap_msgfree (hostres); ldap_stop(); + x_parser_free(&cfile); return (ISC_R_FAILURE); } @@ -1427,7 +2286,9 @@ ldap_read_config (void) (tempbv = ldap_get_values_len (ld, hostent, "dhcpServiceDN")) == NULL || tempbv[0] == NULL) { - log_error ("Error: Cannot find LDAP entry matching %s", hfilter); + log_error ("Error: No dhcp service is associated with the server %s %s", + (hostdn ? "dn" : "name"), (hostdn ? hostdn : + (ldap_dhcp_server_cn ? ldap_dhcp_server_cn : unme.nodename))); if (tempbv != NULL) ldap_value_free_len (tempbv); @@ -1436,6 +2297,7 @@ ldap_read_config (void) ldap_memfree (hostdn); ldap_msgfree (hostres); ldap_stop(); + x_parser_free(&cfile); return (ISC_R_FAILURE); } @@ -1443,37 +2305,51 @@ ldap_read_config (void) log_info ("LDAP: Parsing dhcpServer options '%s' ...", hostdn); #endif - cfile->inbuf[0] = '\0'; - ldap_parse_entry_options(hostent, cfile->inbuf, LDAP_BUFFER_SIZE, NULL); - cfile->buflen = strlen (cfile->inbuf); - if(cfile->buflen > 0) + res = ldap_parse_entry_options(hostent, cfile, NULL); + if (res != ISC_R_SUCCESS) { - ldap_write_debug (cfile->inbuf, cfile->buflen); + ldap_value_free_len (tempbv); + ldap_msgfree (hostres); + ldap_memfree (hostdn); + ldap_stop(); + x_parser_free(&cfile); + return res; + } + if (x_parser_length(cfile) > 0) + { res = conf_file_subparse (cfile, root_group, ROOT_GROUP); if (res != ISC_R_SUCCESS) { log_error ("LDAP: cannot parse dhcpServer entry '%s'", hostdn); + ldap_value_free_len (tempbv); + ldap_msgfree (hostres); ldap_memfree (hostdn); ldap_stop(); + x_parser_free(&cfile); return res; } - cfile->inbuf[0] = '\0'; + x_parser_reset(cfile); } ldap_msgfree (hostres); - /* - ** attach ldap (tree) read function now - */ - cfile->bufix = cfile->buflen = 0; - cfile->read_function = ldap_read_function; - res = ISC_R_SUCCESS; for (cnt=0; tempbv[cnt] != NULL; cnt++) { + + if (_do_ldap_str2esc_filter_bv(hostdn, 0, &bv_o[0]) == NULL) + { + log_error ("Cannot escape ldap filter value %s: %m", hostdn); + res = ISC_R_FAILURE; + break; + } + snprintf(sfilter, sizeof(sfilter), "(&(objectClass=dhcpService)" - "(|(dhcpPrimaryDN=%s)(dhcpSecondaryDN=%s)))", - hostdn, hostdn); + "(|(|(dhcpPrimaryDN=%s)(dhcpSecondaryDN=%s))(dhcpServerDN=%s)))", + bv_o[0].bv_val, bv_o[0].bv_val, bv_o[0].bv_val); + + ber_memfree(bv_o[0].bv_val); + ldres = NULL; if ((ret = ldap_search_ext_s (ld, tempbv[cnt]->bv_val, LDAP_SCOPE_BASE, sfilter, NULL, 0, NULL, NULL, NULL, @@ -1489,7 +2365,7 @@ ldap_read_config (void) if ((ent = ldap_first_entry (ld, ldres)) == NULL) { - log_error ("Error: Cannot find dhcpService DN '%s' with primary or secondary server reference. Please update the LDAP server entry '%s'", + log_error ("Error: Cannot find dhcpService DN '%s' with server reference. Please update the LDAP server entry '%s'", tempbv[cnt]->bv_val, hostdn); ldap_msgfree(ldres); @@ -1533,7 +2409,7 @@ ldap_read_config (void) log_fatal ("no memory to remember ldap service dn"); #if defined (DEBUG_LDAP) - log_info ("LDAP: Parsing dhcpService DN '%s' ...", tempbv[cnt]); + log_info ("LDAP: Parsing dhcpService DN '%s' ...", tempbv[cnt]->bv_val); #endif add_to_config_stack (ldres, ent); res = conf_file_subparse (cfile, root_group, ROOT_GROUP); @@ -1544,7 +2420,7 @@ ldap_read_config (void) } } - end_parse (&cfile); + x_parser_free(&cfile); ldap_close_debug_fd(); ldap_memfree (hostdn); @@ -1592,17 +2468,18 @@ ldap_parse_options (LDAPMessage * ent, struct group *group, struct class **class) { int declaration, lease_limit; - char option_buffer[8192]; enum dhcp_token token; struct parse *cfile; isc_result_t res; const char *val; lease_limit = 0; - *option_buffer = '\0'; - - /* This block of code will try to find the parent of the host, and - if it is a group object, fetch the options and apply to the host. */ + cfile = x_parser_init(type == HOST_DECL ? "LDAP-HOST" : "LDAP-SUBCLASS"); + if (cfile == NULL) + return (lease_limit); + + /* This block of code will try to find the parent of the host, and + if it is a group object, fetch the options and apply to the host. */ if (type == HOST_DECL) { char *hostdn, *basedn, *temp1, *temp2, filter[1024]; @@ -1624,16 +2501,29 @@ ldap_parse_options (LDAPMessage * ent, struct group *group, if (temp2 != NULL) { - snprintf (filter, sizeof(filter), - "(&(cn=%.*s)(objectClass=dhcpGroup))", - (int)(temp2 - temp1), temp1); + struct berval bv_o; + + if (_do_ldap_str2esc_filter_bv(temp1, (temp2 - temp1), &bv_o) == NULL) + { + log_error ("Cannot escape ldap filter value %.*s: %m", + (int)(temp2 - temp1), temp1); + filter[0] = '\0'; + } + else + { + snprintf (filter, sizeof(filter), + "(&(cn=%s)(objectClass=dhcpGroup))", + bv_o.bv_val); + + ber_memfree(bv_o.bv_val); + } basedn = strchr (temp1, ','); if (basedn != NULL) ++basedn; } - if (basedn != NULL && *basedn != '\0') + if (basedn != NULL && *basedn != '\0' && filter[0] != '\0') { ret = ldap_search_ext_s (ld, basedn, LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL, NULL, 0, &groupdn); @@ -1641,13 +2531,11 @@ ldap_parse_options (LDAPMessage * ent, struct group *group, { if ((entry = ldap_first_entry (ld, groupdn)) != NULL) { - res = ldap_parse_entry_options (entry, option_buffer, - sizeof(option_buffer) - 1, - &lease_limit); + res = ldap_parse_entry_options (entry, cfile, &lease_limit); if (res != ISC_R_SUCCESS) { /* reset option buffer discarding any results */ - *option_buffer = '\0'; + x_parser_reset(cfile); lease_limit = 0; } } @@ -1658,24 +2546,18 @@ ldap_parse_options (LDAPMessage * ent, struct group *group, } } - res = ldap_parse_entry_options (ent, option_buffer, sizeof(option_buffer) - 1, - &lease_limit); - if (res != ISC_R_SUCCESS) - return (lease_limit); - - option_buffer[sizeof(option_buffer) - 1] = '\0'; - if (*option_buffer == '\0') - return (lease_limit); - - cfile = (struct parse *) NULL; - res = new_parse (&cfile, -1, option_buffer, strlen (option_buffer), - type == HOST_DECL ? "LDAP-HOST" : "LDAP-SUBCLASS", 0); + res = ldap_parse_entry_options (ent, cfile, &lease_limit); if (res != ISC_R_SUCCESS) - return (lease_limit); + { + x_parser_free(&cfile); + return (lease_limit); + } -#if defined (DEBUG_LDAP) - log_info ("Sending the following options: '%s'", option_buffer); -#endif + if (x_parser_length(cfile) == 0) + { + x_parser_free(&cfile); + return (lease_limit); + } declaration = 0; do @@ -1686,7 +2568,7 @@ ldap_parse_options (LDAPMessage * ent, struct group *group, declaration = parse_statement (cfile, group, type, host, declaration); } while (1); - end_parse (&cfile); + x_parser_free(&cfile); return (lease_limit); } @@ -1702,7 +2584,13 @@ find_haddr_in_ldap (struct host_decl **hp, int htype, unsigned hlen, struct host_decl * host; isc_result_t status; ldap_dn_node *curr; + char up_hwaddr[20]; + char lo_hwaddr[20]; int ret; + struct berval bv_o[2]; + + *hp = NULL; + if (ldap_method == LDAP_METHOD_STATIC) return (0); @@ -1732,9 +2620,28 @@ find_haddr_in_ldap (struct host_decl **hp, int htype, unsigned hlen, ** FIXME: It is not guaranteed, that the dhcpHWAddress attribute ** contains _exactly_ "type addr" with one space between! */ + snprintf(lo_hwaddr, sizeof(lo_hwaddr), "%s", + print_hw_addr (htype, hlen, haddr)); + x_strxform(up_hwaddr, lo_hwaddr, sizeof(up_hwaddr), toupper); + + if (_do_ldap_str2esc_filter_bv(lo_hwaddr, 0, &bv_o[0]) == NULL) + { + log_error ("Cannot escape ldap filter value %s: %m", lo_hwaddr); + return (0); + } + if (_do_ldap_str2esc_filter_bv(up_hwaddr, 0, &bv_o[1]) == NULL) + { + log_error ("Cannot escape ldap filter value %s: %m", up_hwaddr); + ber_memfree(bv_o[0].bv_val); + return (0); + } + snprintf (buf, sizeof (buf), - "(&(objectClass=dhcpHost)(dhcpHWAddress=%s %s))", - type_str, print_hw_addr (htype, hlen, haddr)); + "(&(objectClass=dhcpHost)(|(dhcpHWAddress=%s %s)(dhcpHWAddress=%s %s)))", + type_str, bv_o[0].bv_val, type_str, bv_o[1].bv_val); + + ber_memfree(bv_o[0].bv_val); + ber_memfree(bv_o[1].bv_val); res = ent = NULL; for (curr = ldap_service_dn_head; @@ -1765,18 +2672,61 @@ find_haddr_in_ldap (struct host_decl **hp, int htype, unsigned hlen, if (ret == LDAP_SUCCESS) { - if( (ent = ldap_first_entry (ld, res)) != NULL) - break; /* search OK and have entry */ - + ent = ldap_first_entry (ld, res); #if defined (DEBUG_LDAP) - log_info ("No host entry for %s in LDAP tree %s", - buf, curr->dn); + if (ent == NULL) { + log_info ("No host entry for %s in LDAP tree %s", + buf, curr->dn); + } +#endif + while (ent != NULL) { +#if defined (DEBUG_LDAP) + char *dn = ldap_get_dn (ld, ent); + if (dn != NULL) + { + log_info ("Found dhcpHWAddress LDAP entry %s", dn); + ldap_memfree(dn); + } #endif + + host = (struct host_decl *)0; + status = host_allocate (&host, MDL); + if (status != ISC_R_SUCCESS) + { + log_fatal ("can't allocate host decl struct: %s", + isc_result_totext (status)); + ldap_msgfree (res); + return (0); + } + + host->name = ldap_get_host_name (ent); + if (host->name == NULL) + { + host_dereference (&host, MDL); + ldap_msgfree (res); + return (0); + } + + if (!clone_group (&host->group, root_group, MDL)) + { + log_fatal ("can't clone group for host %s", host->name); + host_dereference (&host, MDL); + ldap_msgfree (res); + return (0); + } + + ldap_parse_options (ent, host->group, HOST_DECL, host, NULL); + + host->n_ipaddr = *hp; + *hp = host; + ent = ldap_next_entry (ld, ent); + } if(res) { ldap_msgfree (res); res = NULL; } + return (*hp != NULL); } else { @@ -1803,52 +2753,6 @@ find_haddr_in_ldap (struct host_decl **hp, int htype, unsigned hlen, } } - if (res && ent) - { -#if defined (DEBUG_LDAP) - char *dn = ldap_get_dn (ld, ent); - if (dn != NULL) - { - log_info ("Found dhcpHWAddress LDAP entry %s", dn); - ldap_memfree(dn); - } -#endif - - host = (struct host_decl *)0; - status = host_allocate (&host, MDL); - if (status != ISC_R_SUCCESS) - { - log_fatal ("can't allocate host decl struct: %s", - isc_result_totext (status)); - ldap_msgfree (res); - return (0); - } - - host->name = ldap_get_host_name (ent); - if (host->name == NULL) - { - host_dereference (&host, MDL); - ldap_msgfree (res); - return (0); - } - - if (!clone_group (&host->group, root_group, MDL)) - { - log_fatal ("can't clone group for host %s", host->name); - host_dereference (&host, MDL); - ldap_msgfree (res); - return (0); - } - - ldap_parse_options (ent, host->group, HOST_DECL, host, NULL); - - *hp = host; - ldap_msgfree (res); - return (1); - } - - - if(res) ldap_msgfree (res); return (0); } @@ -1861,7 +2765,10 @@ find_subclass_in_ldap (struct class *class, struct class **newclass, int ret, lease_limit; isc_result_t status; ldap_dn_node *curr; - char buf[1024]; + char buf[2048]; + struct berval bv_class; + struct berval bv_cdata; + char *hex_1; if (ldap_method == LDAP_METHOD_STATIC) return (0); @@ -1871,10 +2778,33 @@ find_subclass_in_ldap (struct class *class, struct class **newclass, if (ld == NULL) return (0); + hex_1 = print_hex_1 (data->len, data->data, 1024); + if (*hex_1 == '"') + { + /* result is a quotted not hex string: ldap escape the original string */ + if (_do_ldap_str2esc_filter_bv((const char*)data->data, data->len, &bv_cdata) == NULL) + { + log_error ("Cannot escape ldap filter value %s: %m", hex_1); + return (0); + } + hex_1 = NULL; + } + if (_do_ldap_str2esc_filter_bv(class->name, strlen (class->name), &bv_class) == NULL) + { + log_error ("Cannot escape ldap filter value %s: %m", class->name); + if (hex_1 == NULL) + ber_memfree(bv_cdata.bv_val); + return (0); + } + snprintf (buf, sizeof (buf), "(&(objectClass=dhcpSubClass)(cn=%s)(dhcpClassData=%s))", - print_hex_1 (data->len, data->data, 60), - print_hex_2 (strlen (class->name), (u_int8_t *) class->name, 60)); + (hex_1 == NULL ? bv_cdata.bv_val : hex_1), bv_class.bv_val); + + if (hex_1 == NULL) + ber_memfree(bv_cdata.bv_val); + ber_memfree(bv_class.bv_val); + #if defined (DEBUG_LDAP) log_info ("Searching LDAP for %s", buf); #endif @@ -2000,4 +2930,213 @@ find_subclass_in_ldap (struct class *class, struct class **newclass, return (0); } +int find_client_in_ldap (struct host_decl **hp, struct packet *packet, + struct option_state *state, const char *file, int line) +{ + LDAPMessage * res, * ent; + ldap_dn_node *curr; + struct host_decl * host; + isc_result_t status; + struct data_string client_id; + char buf[1024], buf1[1024]; + int ret; + + if (ldap_method == LDAP_METHOD_STATIC) + return (0); + + if (ld == NULL) + ldap_start (); + if (ld == NULL) + return (0); + + memset(&client_id, 0, sizeof(client_id)); + if (get_client_id(packet, &client_id) != ISC_R_SUCCESS) + return (0); + snprintf(buf, sizeof(buf), + "(&(objectClass=dhcpHost)(dhcpClientId=%s))", + print_hw_addr(0, client_id.len, client_id.data)); + + /* log_info ("Searching LDAP for %s (%s)", buf, packet->interface->shared_network->name); */ + + res = ent = NULL; + for (curr = ldap_service_dn_head; + curr != NULL && *curr->dn != '\0'; + curr = curr->next) + { + snprintf(buf1, sizeof(buf1), "cn=%s,%s", packet->interface->shared_network->name, curr->dn); +#if defined (DEBUG_LDAP) + log_info ("Searching for %s in LDAP tree %s", buf, buf1); +#endif + ret = ldap_search_ext_s (ld, buf1, LDAP_SCOPE_SUBTREE, buf, NULL, 0, + NULL, NULL, NULL, 0, &res); + + if(ret == LDAP_SERVER_DOWN) + { + log_info ("LDAP server was down, trying to reconnect..."); + + ldap_stop(); + ldap_start(); + + if(ld == NULL) + { + log_info ("LDAP reconnect failed - try again later..."); + return (0); + } + + ret = ldap_search_ext_s (ld, buf1, LDAP_SCOPE_SUBTREE, buf, + NULL, 0, NULL, NULL, NULL, 0, &res); + } + + if (ret == LDAP_SUCCESS) + { + if( (ent = ldap_first_entry (ld, res)) != NULL) { + log_info ("found entry in search %s", buf1); + break; /* search OK and have entry */ + } + +#if defined (DEBUG_LDAP) + log_info ("No subclass entry for %s in LDAP tree %s", buf, curr->dn); +#endif + if(res) + { + ldap_msgfree (res); + res = NULL; + } + } + else + { + if(res) + { + ldap_msgfree (res); + res = NULL; + } + + if (ret != LDAP_NO_SUCH_OBJECT && ret != LDAP_SUCCESS) + { + log_error ("Cannot search for %s in LDAP tree %s: %s", buf, + curr->dn, ldap_err2string (ret)); + ldap_stop(); + return (0); + } + else + { + log_info ("did not find: %s", buf); + } + } + } + + if (res && ent) + { +#if defined (DEBUG_LDAP) + log_info ("ldap_get_dn %s", curr->dn); + char *dn = ldap_get_dn (ld, ent); + if (dn != NULL) + { + log_info ("Found subclass LDAP entry %s", dn); + ldap_memfree(dn); + } else { + log_info ("DN is null %s", dn); + } +#endif + + host = (struct host_decl *)0; + status = host_allocate (&host, MDL); + if (status != ISC_R_SUCCESS) + { + log_fatal ("can't allocate host decl struct: %s", + isc_result_totext (status)); + ldap_msgfree (res); + return (0); + } + + host->name = ldap_get_host_name (ent); + if (host->name == NULL) + { + host_dereference (&host, MDL); + ldap_msgfree (res); + return (0); + } + /* log_info ("Host name %s", host->name); */ + + if (!clone_group (&host->group, root_group, MDL)) + { + log_fatal ("can't clone group for host %s", host->name); + host_dereference (&host, MDL); + ldap_msgfree (res); + return (0); + } + + ldap_parse_options (ent, host->group, HOST_DECL, host, NULL); + + *hp = host; + ldap_msgfree (res); + return (1); + } + else + { + log_info ("did not find clientid: %s", buf); + } + + if(res) ldap_msgfree (res); + return (0); + +} + +#if defined(LDAP_USE_GSSAPI) +static int +_ldap_sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *sin) +{ + sasl_interact_t *in; + struct ldap_sasl_instance *ldap_inst = defaults; + int ret = LDAP_OTHER; + size_t size; + + if (ld == NULL || sin == NULL) + return LDAP_PARAM_ERROR; + + log_info("doing interactive bind"); + for (in = sin; in != NULL && in->id != SASL_CB_LIST_END; in++) { + switch (in->id) { + case SASL_CB_USER: + log_info("got request for SASL_CB_USER %s", ldap_inst->sasl_authz_id); + size = strlen(ldap_inst->sasl_authz_id); + in->result = ldap_inst->sasl_authz_id; + in->len = size; + ret = LDAP_SUCCESS; + break; + case SASL_CB_GETREALM: + log_info("got request for SASL_CB_GETREALM %s", ldap_inst->sasl_realm); + size = strlen(ldap_inst->sasl_realm); + in->result = ldap_inst->sasl_realm; + in->len = size; + ret = LDAP_SUCCESS; + break; + case SASL_CB_AUTHNAME: + log_info("got request for SASL_CB_AUTHNAME %s", ldap_inst->sasl_authc_id); + size = strlen(ldap_inst->sasl_authc_id); + in->result = ldap_inst->sasl_authc_id; + in->len = size; + ret = LDAP_SUCCESS; + break; + case SASL_CB_PASS: + log_info("got request for SASL_CB_PASS %s", ldap_inst->sasl_password); + size = strlen(ldap_inst->sasl_password); + in->result = ldap_inst->sasl_password; + in->len = size; + ret = LDAP_SUCCESS; + break; + default: + goto cleanup; + } + } + return ret; + +cleanup: + in->result = NULL; + in->len = 0; + return LDAP_OTHER; +} +#endif /* LDAP_USE_GSSAPI */ + + #endif diff --git a/server/ldap_casa.c b/server/ldap_casa.c index 952d9b96..cd101570 100644 --- a/server/ldap_casa.c +++ b/server/ldap_casa.c @@ -55,8 +55,10 @@ */ #if defined(LDAP_CASA_AUTH) -#include "ldap_casa.h" #include "dhcpd.h" +#include "ldap_casa.h" +#include <dlfcn.h> +#include <string.h> int load_casa (void) diff --git a/server/ldap_krb_helper.c b/server/ldap_krb_helper.c new file mode 100644 index 00000000..1dcab802 --- /dev/null +++ b/server/ldap_krb_helper.c @@ -0,0 +1,216 @@ +/* ldap_krb_helper.c + + Helper routings for allowing LDAP to read configuration with GSSAPI/krb auth */ + +/* + * Copyright (c) 2014 William B. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of The Internet Software Consortium nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This helper was written by William Brown <william@adelaide.edu.au>, + * inspired by krb5_helper.c from bind-dyndb-ldap by Simo Sorce (Redhat) + */ +#if defined(LDAP_USE_GSSAPI) + +#include "dhcpd.h" +#include "ldap_krb_helper.h" + +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <time.h> + +#define KRB_DEFAULT_KEYTAB "FILE:/etc/dhcp/dhcp.keytab" +#define KRB_MIN_TIME 300 + +#define CHECK_KRB5(ctx, err, msg, ...) \ + do { \ + if (err) { \ + const char * errmsg = krb5_get_error_message(ctx, err); \ + log_error("Err: %s -> %s\n", msg, errmsg); \ + result = ISC_R_FAILURE; \ + goto cleanup; \ + } \ + } while (0) + +#define CHECK(ret_code, msg) \ + if (ret_code != 0) { \ + log_error("Error, %i %s\n", ret_code, msg); \ + goto cleanup; \ + } + +static isc_result_t +check_credentials(krb5_context context, krb5_ccache ccache, krb5_principal service) +{ + char *realm = NULL; + krb5_creds creds; + krb5_creds mcreds; + krb5_error_code krberr; + krb5_timestamp now; + isc_result_t result = ISC_R_FAILURE; + + memset(&mcreds, 0, sizeof(mcreds)); + memset(&creds, 0, sizeof(creds)); + + krberr = krb5_get_default_realm(context, &realm); + CHECK_KRB5(context, krberr, "Failed to retrieve default realm"); + + krberr = krb5_build_principal(context, &mcreds.server, + strlen(realm), realm, + "krbtgt", realm, NULL); + CHECK_KRB5(context, krberr, "Failed to build 'krbtgt/REALM' principal"); + + mcreds.client = service; + + krberr = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &creds); + + if (krberr) { + const char * errmsg = krb5_get_error_message(context, krberr); + log_error("Credentials are not present in cache (%s)\n", errmsg); + krb5_free_error_message(context, errmsg); + result = ISC_R_FAILURE; + goto cleanup; + } + CHECK_KRB5(context, krberr, "Credentials are not present in cache "); + + krberr = krb5_timeofday(context, &now); + CHECK_KRB5(context, krberr, "Failed to get time of day"); + + + if (now > (creds.times.endtime + KRB_MIN_TIME)) { + log_error("Credentials cache expired"); + result = ISC_R_FAILURE; + goto cleanup; + } else { + char buf[255]; + char fill = ' '; + krb5_timestamp_to_sfstring(creds.times.endtime, buf, 16, &fill); + log_info("Credentials valid til %s\n", buf); + } + + result = ISC_R_SUCCESS; + +cleanup: + krb5_free_cred_contents(context, &creds); + if (mcreds.server) krb5_free_principal(context, mcreds.server); + if (realm) krb5_free_default_realm(context, realm); + return result; +} + +isc_result_t +krb5_get_tgt(const char *principal, const char *keyfile) +{ + isc_result_t result = ISC_R_FAILURE; + char *ccname = NULL; + krb5_context context = NULL; + krb5_error_code krberr; + krb5_ccache ccache = NULL; + krb5_principal kprincpw = NULL; + krb5_creds my_creds; + krb5_creds * my_creds_ptr = NULL; + krb5_get_init_creds_opt options; + krb5_keytab keytab = NULL; + int ret; + + if (keyfile == NULL || keyfile[0] == '\0') { + keyfile = KRB_DEFAULT_KEYTAB; + log_info("Using default keytab %s\n", keyfile); + } else { + if (strncmp(keyfile, "FILE:", 5) != 0) { + log_error("Unknown keytab path format: Does it start with FILE:?\n"); + return ISC_R_FAILURE; + } + } + + krberr = krb5_init_context(&context); + CHECK_KRB5(NULL, krberr, "Kerberos context initialization failed"); + + result = ISC_R_SUCCESS; + + ccname = "MEMORY:dhcp_ld_krb5_cc"; + log_info("Using ccache %s\n" , ccname); + + ret = setenv("KRB5CCNAME", ccname, 1); + if (ret == -1) { + log_error("Failed to setup environment\n"); + result = ISC_R_FAILURE; + goto cleanup; + } + + krberr = krb5_cc_resolve(context, ccname, &ccache); + CHECK_KRB5(context, krberr, "Couldnt resolve ccache '%s'", ccname); + + krberr = krb5_parse_name(context, principal, &kprincpw); + CHECK_KRB5(context, krberr, "Failed to parse princ '%s'", princpal); + + result = check_credentials(context, ccache, kprincpw); + if (result == ISC_R_SUCCESS) { + log_info("Found valid kerberos credentials\n"); + goto cleanup; + } else { + log_error("No valid krb5 credentials\n"); + } + + krberr = krb5_kt_resolve(context, keyfile, &keytab); + CHECK_KRB5(context, krberr, + "Failed to resolve kt files '%s'\n", keyfile); + + memset(&my_creds, 0, sizeof(my_creds)); + memset(&options, 0, sizeof(options)); + + krb5_get_init_creds_opt_set_tkt_life(&options, KRB_MIN_TIME * 2); + krb5_get_init_creds_opt_set_address_list(&options, NULL); + krb5_get_init_creds_opt_set_forwardable(&options, 0); + krb5_get_init_creds_opt_set_proxiable(&options, 0); + + krberr = krb5_get_init_creds_keytab(context, &my_creds, kprincpw, + keytab, 0, NULL, &options); + CHECK_KRB5(context, krberr, "Failed to get initial credentials TGT\n"); + + my_creds_ptr = &my_creds; + + krberr = krb5_cc_initialize(context, ccache, kprincpw); + CHECK_KRB5(context, krberr, "Failed to init ccache\n"); + + krberr = krb5_cc_store_cred(context, ccache, &my_creds); + CHECK_KRB5(context, krberr, "Failed to store credentials\n"); + + result = ISC_R_SUCCESS; + log_info("Successfully init krb tgt %s", principal); + +cleanup: + if (ccache) krb5_cc_close(context, ccache); + if (keytab) krb5_kt_close(context, keytab); + if (kprincpw) krb5_free_principal(context, kprincpw); + if (my_creds_ptr) krb5_free_cred_contents(context, &my_creds); + if (context) krb5_free_context(context); + return result; +} + +#endif /* defined(LDAP_USE_GSSAPI) */ diff --git a/server/mdb.c b/server/mdb.c index 219db506..9a7da80b 100644 --- a/server/mdb.c +++ b/server/mdb.c @@ -645,6 +645,11 @@ find_hosts_by_option(struct host_decl **hp, int found; struct packet *relay_packet; struct option_state *relay_state; + +#if defined(LDAP_CONFIGURATION) + if ((found = find_client_in_ldap (hp, packet, opt_state, file, line))) + return found; +#endif for (p = host_id_info; p != NULL; p = p->next) { relay_packet = packet; diff --git a/server/stables.c b/server/stables.c index 4d53a834..963503e7 100644 --- a/server/stables.c +++ b/server/stables.c @@ -258,7 +258,12 @@ static struct option server_options[] = { { "ldap-tls-crlcheck", "Nldap-tls-crlcheck.", &server_universe, 75, 1 }, { "ldap-tls-ciphers", "t", &server_universe, 76, 1 }, { "ldap-tls-randfile", "t", &server_universe, 77, 1 }, + { "ldap-init-retry", "d", &server_universe, SV_LDAP_INIT_RETRY, 1 }, #endif /* LDAP_USE_SSL */ +#if defined(LDAP_USE_GSSAPI) + { "ldap-gssapi-keytab", "t", &server_universe, SV_LDAP_GSSAPI_KEYTAB, 1}, + { "ldap-gssapi-principal", "t", &server_universe, SV_LDAP_GSSAPI_PRINCIPAL, 1}, +#endif /* LDAP_USE_GSSAPI */ #endif /* LDAP_CONFIGURATION */ { "dhcp-cache-threshold", "B", &server_universe, 78, 1 }, { "dont-use-fsync", "f", &server_universe, 79, 1 }, diff --git a/server/tests/Makefile.in b/server/tests/Makefile.in index d0da6aa4..0bd2e3bd 100644 --- a/server/tests/Makefile.in +++ b/server/tests/Makefile.in @@ -293,6 +293,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDAP_CFLAGS = @LDAP_CFLAGS@ +LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ diff --git a/tests/Makefile.in b/tests/Makefile.in index 64297ae4..50c22fb2 100644 --- a/tests/Makefile.in +++ b/tests/Makefile.in @@ -184,6 +184,7 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDAP_CFLAGS = @LDAP_CFLAGS@ +LDAP_LIBS = @LDAP_LIBS@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ |