diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..8700bd4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,325 @@ +cmake_minimum_required(VERSION 3.16) + +project(tinyldap + VERSION 0.1.0 + LANGUAGES C) + +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) +include(CTest) + +option(TINYLDAP_BUILD_STATIC "Build static tinyldap library" OFF) +option(TINYLDAP_BUILD_SHARED "Build shared tinyldap library" ON) +option(TINYLDAP_BUILD_TOOLS "Build tinyldap command line tools" ON) +option(TINYLDAP_BUILD_TESTS "Build tinyldap test programs" ${PROJECT_IS_TOP_LEVEL}) +option(TINYLDAP_RUN_TESTS "Register non-interactive tinyldap tests with CTest" ${PROJECT_IS_TOP_LEVEL}) +option(TINYLDAP_INSTALL "Install tinyldap libraries, headers and tools" ON) + +set(TINYLDAP_OUTPUT_NAME "tinyldap" CACHE STRING "Library basename and default server name, e.g. nwdirectory builds libnwdirectory and nwdirectory") +set(TINYLDAP_SERVER_NAME "" CACHE STRING "Installed server executable name. Defaults to TINYLDAP_OUTPUT_NAME") +set(TINYLDAP_TOOL_PREFIX "" CACHE STRING "Optional prefix for non-server tool executable names") +set(TINYLDAP_INCLUDE_SUBDIR "" CACHE STRING "Public include subdirectory below CMAKE_INSTALL_INCLUDEDIR. Defaults to TINYLDAP_OUTPUT_NAME") +set(TINYLDAP_LIBOWFAT_INCLUDE_SUBDIR "" CACHE STRING "libowfat public include subdirectory. Defaults to LIBOWFAT_INSTALL_INCLUDE_SUBDIR if present, otherwise libowfat") + +if(NOT TINYLDAP_BUILD_STATIC AND NOT TINYLDAP_BUILD_SHARED) + message(FATAL_ERROR "At least one of TINYLDAP_BUILD_STATIC or TINYLDAP_BUILD_SHARED must be ON") +endif() + +if(TINYLDAP_SERVER_NAME STREQUAL "") + set(TINYLDAP_SERVER_NAME "${TINYLDAP_OUTPUT_NAME}") +endif() +if(TINYLDAP_INCLUDE_SUBDIR STREQUAL "") + set(TINYLDAP_INCLUDE_SUBDIR "${TINYLDAP_OUTPUT_NAME}") +endif() +if(TINYLDAP_LIBOWFAT_INCLUDE_SUBDIR STREQUAL "") + if(DEFINED LIBOWFAT_INSTALL_INCLUDE_SUBDIR) + set(TINYLDAP_LIBOWFAT_INCLUDE_SUBDIR "${LIBOWFAT_INSTALL_INCLUDE_SUBDIR}") + else() + set(TINYLDAP_LIBOWFAT_INCLUDE_SUBDIR "libowfat") + endif() +endif() + +set(TINYLDAP_BUILD_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include") +set(TINYLDAP_PUBLIC_INCLUDE_DIR "${TINYLDAP_BUILD_INCLUDE_DIR}/${TINYLDAP_INCLUDE_SUBDIR}") +set(TINYLDAP_COMPAT_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/compat-include") +file(MAKE_DIRECTORY "${TINYLDAP_PUBLIC_INCLUDE_DIR}" "${TINYLDAP_COMPAT_INCLUDE_DIR}") + +set(TINYLDAP_PUBLIC_HEADERS + acl.h + asn1.h + auth.h + bstr.h + ldap.h + ldif.h + mduptab.h + mstorage.h + strduptab.h + strstorage.h + tinytls.h) + +foreach(_hdr IN LISTS TINYLDAP_PUBLIC_HEADERS) + file(READ "${CMAKE_CURRENT_SOURCE_DIR}/${_hdr}" _tinyldap_public_header) + if(NOT TINYLDAP_LIBOWFAT_INCLUDE_SUBDIR STREQUAL "libowfat") + string(REPLACE ". When libowfat +# itself was exported as , keep TinyLDAP sources unchanged by +# providing build-local wrapper headers under compat-include/libowfat/. +if(NOT TINYLDAP_LIBOWFAT_INCLUDE_SUBDIR STREQUAL "libowfat") + file(GLOB _tinyldap_libowfat_refs CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/*.c" + "${CMAKE_CURRENT_SOURCE_DIR}/*.h" + "${CMAKE_CURRENT_SOURCE_DIR}/test/*.c") + set(_tinyldap_libowfat_headers) + foreach(_file IN LISTS _tinyldap_libowfat_refs) + file(STRINGS "${_file}" _matches REGEX "^[ \t]*#[ \t]*include[ \t]*[<\"]libowfat/[^>\"]+[>\"]") + foreach(_line IN LISTS _matches) + string(REGEX REPLACE ".*libowfat/([^>\"]+).*" "\\1" _ow_hdr "${_line}") + list(APPEND _tinyldap_libowfat_headers "${_ow_hdr}") + endforeach() + endforeach() + list(REMOVE_DUPLICATES _tinyldap_libowfat_headers) + file(MAKE_DIRECTORY "${TINYLDAP_COMPAT_INCLUDE_DIR}/libowfat") + foreach(_ow_hdr IN LISTS _tinyldap_libowfat_headers) + file(WRITE "${TINYLDAP_COMPAT_INCLUDE_DIR}/libowfat/${_ow_hdr}" + "#pragma once\n#include <${TINYLDAP_LIBOWFAT_INCLUDE_SUBDIR}/${_ow_hdr}>\n") + endforeach() +endif() + +if(TARGET libowfat::libowfat) + set(TINYLDAP_LIBOWFAT_TARGET libowfat::libowfat) +else() + find_package(libowfat CONFIG QUIET) + if(TARGET libowfat::libowfat) + set(TINYLDAP_LIBOWFAT_TARGET libowfat::libowfat) + else() + find_library(TINYLDAP_OWFAT_LIBRARY NAMES owfat nwowfat REQUIRED) + add_library(tinyldap_owfat_external UNKNOWN IMPORTED) + set_target_properties(tinyldap_owfat_external PROPERTIES IMPORTED_LOCATION "${TINYLDAP_OWFAT_LIBRARY}") + set(TINYLDAP_LIBOWFAT_TARGET tinyldap_owfat_external) + endif() +endif() + +if(TARGET OpenSSL::Crypto) + set(TINYLDAP_CRYPTO_TARGET OpenSSL::Crypto) +else() + find_package(OpenSSL REQUIRED COMPONENTS Crypto) + set(TINYLDAP_CRYPTO_TARGET OpenSSL::Crypto) +endif() + +find_library(TINYLDAP_CRYPT_LIBRARY crypt) + +set(TINYLDAP_LIBRARY_SOURCES + fmt_asn1intpayload.c fmt_asn1length.c fmt_asn1tag.c + fmt_asn1int.c fmt_asn1string.c fmt_asn1transparent.c scan_asn1tag.c + scan_asn1length.c scan_asn1int.c scan_asn1string.c scan_asn1INTEGER.c + scan_asn1STRING.c scan_asn1SEQUENCE.c scan_asn1ENUMERATED.c + scan_asn1BOOLEAN.c scan_asn1rawint.c scan_asn1SET.c fmt_asn1sint.c + fmt_asn1sintpayload.c scan_asn1oid.c scan_asn1BITSTRING.c + scan_asn1tagint.c fmt_asn1tagint.c fmt_asn1OID.c scan_asn1generic.c + fmt_asn1generic.c scan_asn1rawoid.c fmt_asn1bitstring.c asn1oid.c + scan_asn1SEQUENCE_nolengthcheck.c + + scan_ldapmessage.c fmt_ldapmessage.c fmt_ldapbindrequest.c + scan_ldapbindrequest.c scan_ldapbindresponse.c scan_ldapresult.c + scan_ldapstring.c scan_ldapsearchfilter.c scan_ldapsearchrequest.c + freefilter.c freeava.c scan_ldapava.c fmt_ldapsearchresultentry.c + fmt_ldapstring.c freepal.c scan_ldapsearchresultentry.c + fmt_ldapresult.c fmt_ldappal.c fmt_ldapadl.c fmt_ldapava.c + fmt_ldapsearchfilter.c fmt_ldapsearchrequest.c matchstring.c + matchprefix.c matchcasestring.c matchcaseprefix.c + scan_ldapmodifyrequest.c scan_ldapaddrequest.c bstrlen.c bstrfirst.c + bstrstart.c free_ldapadl.c free_ldappal.c free_ldapsearchfilter.c + scan_ldapsearchfilterstring.c free_ldapsearchresultentry.c + fmt_ldapsearchfilterstring.c + fmt_ldapdeleterequest.c scan_ldapdeleterequest.c normalize_dn.c + fmt_ldapmodifyrequest.c fmt_ldapaddrequest.c + scan_ldapmessage_nolengthcheck.c + + ldif_parse.c + + mstorage_add.c mduptab_add.c bstr_diff.c mduptab_adds.c bstr_diff2.c + mstorage_add_bin.c mstorage_init.c mstorage_init_persistent.c + mstorage_unmap.c mduptab_init.c mduptab_reset.c + + auth.c + + fmt_tls_clienthello.c init_tls_context.c fmt_tls_serverhello.c + fmt_tls_alert.c fmt_tls_packet.c tls_cipherprio.c fmt_tls_alert_pkt.c + fmt_tls_handshake_cert.c fmt_tls_handshake_certs_header.c + fmt_tls_serverhellodone.c tls_accept.c tls_connect.c tls_doread.c tls_dowrite.c) + +add_library(tinyldap_objects OBJECT ${TINYLDAP_LIBRARY_SOURCES}) +target_compile_features(tinyldap_objects PUBLIC c_std_99) +target_compile_definitions(tinyldap_objects PRIVATE _GNU_SOURCE _FILE_OFFSET_BITS=64) +target_include_directories(tinyldap_objects + PUBLIC + "$" + "$" + PRIVATE + "${TINYLDAP_COMPAT_INCLUDE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(tinyldap_objects PUBLIC ${TINYLDAP_LIBOWFAT_TARGET} ${TINYLDAP_CRYPTO_TARGET}) +if(TINYLDAP_CRYPT_LIBRARY) + target_link_libraries(tinyldap_objects PUBLIC "${TINYLDAP_CRYPT_LIBRARY}") +endif() +if(TINYLDAP_BUILD_SHARED) + set_target_properties(tinyldap_objects PROPERTIES POSITION_INDEPENDENT_CODE ON) +endif() + +set(_tinyldap_export_targets) +if(TINYLDAP_BUILD_STATIC) + add_library(tinyldap_static STATIC $) + add_library(tinyldap::static ALIAS tinyldap_static) + set_target_properties(tinyldap_static PROPERTIES + OUTPUT_NAME "${TINYLDAP_OUTPUT_NAME}" + EXPORT_NAME static) + target_link_libraries(tinyldap_static PUBLIC ${TINYLDAP_LIBOWFAT_TARGET} ${TINYLDAP_CRYPTO_TARGET}) + target_include_directories(tinyldap_static PUBLIC + "$" + "$") + if(TINYLDAP_CRYPT_LIBRARY) + target_link_libraries(tinyldap_static PUBLIC "${TINYLDAP_CRYPT_LIBRARY}") + endif() + list(APPEND _tinyldap_export_targets tinyldap_static) +endif() + +if(TINYLDAP_BUILD_SHARED) + add_library(tinyldap_shared SHARED $) + add_library(tinyldap::shared ALIAS tinyldap_shared) + set_target_properties(tinyldap_shared PROPERTIES + OUTPUT_NAME "${TINYLDAP_OUTPUT_NAME}" + VERSION "${PROJECT_VERSION}" + SOVERSION "${PROJECT_VERSION_MAJOR}" + EXPORT_NAME shared) + target_link_libraries(tinyldap_shared PUBLIC ${TINYLDAP_LIBOWFAT_TARGET} ${TINYLDAP_CRYPTO_TARGET}) + target_include_directories(tinyldap_shared PUBLIC + "$" + "$") + if(TINYLDAP_CRYPT_LIBRARY) + target_link_libraries(tinyldap_shared PUBLIC "${TINYLDAP_CRYPT_LIBRARY}") + endif() + list(APPEND _tinyldap_export_targets tinyldap_shared) +endif() + +if(TINYLDAP_BUILD_SHARED) + add_library(tinyldap::tinyldap ALIAS tinyldap_shared) +else() + add_library(tinyldap::tinyldap ALIAS tinyldap_static) +endif() + +function(tinyldap_add_program target source output_name) + add_executable(${target} ${source} ${ARGN}) + target_compile_features(${target} PRIVATE c_std_99) + target_compile_definitions(${target} PRIVATE _GNU_SOURCE _FILE_OFFSET_BITS=64) + target_include_directories(${target} PRIVATE + "${TINYLDAP_COMPAT_INCLUDE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINYLDAP_BUILD_INCLUDE_DIR}") + target_link_libraries(${target} PRIVATE tinyldap::tinyldap) + set_target_properties(${target} PROPERTIES OUTPUT_NAME "${output_name}") +endfunction() + +if(TINYLDAP_BUILD_TOOLS) + tinyldap_add_program(tinyldap_server tinyldap.c "${TINYLDAP_SERVER_NAME}" ldap_match_mapped.c ldap_match_sre.c) + tinyldap_add_program(tinyldap_server_standalone tinyldap.c "${TINYLDAP_SERVER_NAME}_standalone" ldap_match_mapped.c ldap_match_sre.c) + target_compile_definitions(tinyldap_server_standalone PRIVATE STANDALONE) + tinyldap_add_program(tinyldap_server_debug tinyldap.c "${TINYLDAP_SERVER_NAME}_debug" ldap_match_mapped.c ldap_match_sre.c) + target_compile_definitions(tinyldap_server_debug PRIVATE STANDALONE DEBUG) + + tinyldap_add_program(tinyldap_t2 t2.c "${TINYLDAP_TOOL_PREFIX}t2") + tinyldap_add_program(tinyldap_parse parse.c "${TINYLDAP_TOOL_PREFIX}parse") + tinyldap_add_program(tinyldap_dumpidx dumpidx.c "${TINYLDAP_TOOL_PREFIX}dumpidx") + tinyldap_add_program(tinyldap_idx2ldif idx2ldif.c "${TINYLDAP_TOOL_PREFIX}idx2ldif") + tinyldap_add_program(tinyldap_addindex addindex.c "${TINYLDAP_TOOL_PREFIX}addindex") + tinyldap_add_program(tinyldap_bindrequest bindrequest.c "${TINYLDAP_TOOL_PREFIX}bindrequest") + tinyldap_add_program(tinyldap_ldapclient ldapclient.c "${TINYLDAP_TOOL_PREFIX}ldapclient") + tinyldap_add_program(tinyldap_ldapclient_str ldapclient_str.c "${TINYLDAP_TOOL_PREFIX}ldapclient_str") + tinyldap_add_program(tinyldap_md5password md5password.c "${TINYLDAP_TOOL_PREFIX}md5password") + tinyldap_add_program(tinyldap_mysql2ldif mysql2ldif.c "${TINYLDAP_TOOL_PREFIX}mysql2ldif") + tinyldap_add_program(tinyldap_acl acl.c "${TINYLDAP_TOOL_PREFIX}acl") + tinyldap_add_program(tinyldap_dumpacls dumpacls.c "${TINYLDAP_TOOL_PREFIX}dumpacls") + tinyldap_add_program(tinyldap_ldapdelete ldapdelete.c "${TINYLDAP_TOOL_PREFIX}ldapdelete") + tinyldap_add_program(tinyldap_asn1dump asn1dump.c "${TINYLDAP_TOOL_PREFIX}asn1dump") + tinyldap_add_program(tinyldap_x x.c "${TINYLDAP_TOOL_PREFIX}x") +endif() + +if(TINYLDAP_BUILD_TESTS) + tinyldap_add_program(tinyldap_test_bind test/bind.c "${TINYLDAP_TOOL_PREFIX}test-bind") + tinyldap_add_program(tinyldap_test_ebind test/ebind.c "${TINYLDAP_TOOL_PREFIX}test-ebind") + if(TINYLDAP_RUN_TESTS) + # t2 is a simple non-network decoder smoke test. The other Makefile tests + # expect external LDAP data or a running server and are intentionally build-only. + add_test(NAME tinyldap_t2 COMMAND tinyldap_t2) + set_tests_properties(tinyldap_t2 PROPERTIES WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + endif() + + file(GLOB _unittest_sources CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.c") + foreach(_ut_src IN LISTS _unittest_sources) + file(STRINGS "${_ut_src}" _has_unittest REGEX "UNITTEST") + if(_has_unittest) + get_filename_component(_ut_name "${_ut_src}" NAME_WE) + set(_ut_target "tinyldap_unittest_${_ut_name}") + add_executable(${_ut_target} "${_ut_src}") + target_compile_definitions(${_ut_target} PRIVATE UNITTEST _GNU_SOURCE _FILE_OFFSET_BITS=64) + target_include_directories(${_ut_target} PRIVATE + "${TINYLDAP_COMPAT_INCLUDE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}" + "${TINYLDAP_BUILD_INCLUDE_DIR}") + target_link_libraries(${_ut_target} PRIVATE tinyldap::tinyldap) + if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(${_ut_target} PRIVATE -UNDEBUG -O0) + endif() + if(TINYLDAP_RUN_TESTS) + add_test(NAME tinyldap_unittest_${_ut_name} COMMAND ${_ut_target}) + set_tests_properties(tinyldap_unittest_${_ut_name} PROPERTIES WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + endif() + endif() + endforeach() +endif() + +if(TINYLDAP_INSTALL) + install(DIRECTORY "${TINYLDAP_PUBLIC_INCLUDE_DIR}/" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${TINYLDAP_INCLUDE_SUBDIR}") + + if(_tinyldap_export_targets) + install(TARGETS ${_tinyldap_export_targets} + EXPORT tinyldap-targets + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") + endif() + + if(TINYLDAP_BUILD_TOOLS) + install(TARGETS + tinyldap_server tinyldap_server_standalone tinyldap_server_debug + tinyldap_t2 tinyldap_parse tinyldap_dumpidx tinyldap_idx2ldif + tinyldap_addindex tinyldap_bindrequest tinyldap_ldapclient + tinyldap_ldapclient_str tinyldap_md5password tinyldap_mysql2ldif + tinyldap_acl tinyldap_dumpacls tinyldap_ldapdelete tinyldap_asn1dump + tinyldap_x + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") + endif() + + configure_package_config_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/tinyldap-config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/tinyldap-config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/tinyldap") + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/tinyldap-config-version.cmake" + VERSION "${PROJECT_VERSION}" + COMPATIBILITY SameMajorVersion) + install(EXPORT tinyldap-targets + NAMESPACE tinyldap:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/tinyldap") + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/tinyldap-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/tinyldap-config-version.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/tinyldap") +endif() + +message(STATUS "tinyldap: building lib${TINYLDAP_OUTPUT_NAME}, server=${TINYLDAP_SERVER_NAME}, include=${TINYLDAP_INCLUDE_SUBDIR}, libowfat include=${TINYLDAP_LIBOWFAT_INCLUDE_SUBDIR}") diff --git a/README.md b/README.md new file mode 100644 index 0000000..0ed041a --- /dev/null +++ b/README.md @@ -0,0 +1,86 @@ +# tinyldap CMake build + +This fork can be built with CMake in addition to the original Makefile. +The CMake build is out-of-tree friendly and can be used either as a top-level +project or via `add_subdirectory()`. + +## Basic build + +```sh +cmake -S . -B build +cmake --build build +ctest --test-dir build --output-on-failure +cmake --install build --prefix /usr/local +``` + +By default CMake builds a shared library, the server and the same command-line +tools that the original Makefile builds. + +## Static and shared libraries + +```sh +cmake -S . -B build \ + -DTINYLDAP_BUILD_STATIC=ON \ + -DTINYLDAP_BUILD_SHARED=ON +``` + +## Custom server and library name + +`TINYLDAP_OUTPUT_NAME` controls the library basename and, unless overridden, the +server executable name: + +```sh +cmake -S . -B build -DTINYLDAP_OUTPUT_NAME=nwdirectory +``` + +This builds `libnwdirectory.so` / `libnwdirectory.a` and a server named +`nwdirectory`. + +For a different server name only: + +```sh +cmake -S . -B build \ + -DTINYLDAP_OUTPUT_NAME=nwdirectory \ + -DTINYLDAP_SERVER_NAME=my-ldapd +``` + +## Include layout + +Public headers are exported under an include subdirectory. By default it matches +`TINYLDAP_OUTPUT_NAME`: + +```c +#include +``` + +or with `-DTINYLDAP_OUTPUT_NAME=nwdirectory`: + +```c +#include +``` + +The build tree has the same layout under `${binary_dir}/include`, which makes +submodule use straightforward. + +## libowfat dependency + +When libowfat is already present in the parent build, TinyLDAP uses the target +`libowfat::libowfat`. Otherwise it tries `find_package(libowfat CONFIG)` and +then a plain library lookup. + +TinyLDAP sources use `#include `. If the libowfat build exports a +prefixed include directory such as `nwlibowfat`, set: + +```sh +-DTINYLDAP_LIBOWFAT_INCLUDE_SUBDIR=nwlibowfat +``` + +When TinyLDAP is added below the CMake libowfat build, this is detected from +`LIBOWFAT_INSTALL_INCLUDE_SUBDIR` automatically. + +## OpenSSL / MatrixSSL compat + +TinyLDAP currently needs OpenSSL-style MD5 APIs for `auth.c` and `md5password.c`. +If the parent project already provides `OpenSSL::Crypto` via MatrixSSL's +OpenSSL-compat target, TinyLDAP will use it. Otherwise CMake falls back to the +system OpenSSL Crypto library. diff --git a/cmake/tinyldap-config.cmake.in b/cmake/tinyldap-config.cmake.in new file mode 100644 index 0000000..07e676b --- /dev/null +++ b/cmake/tinyldap-config.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ +include(CMakeFindDependencyMacro) +find_dependency(libowfat CONFIG) +find_dependency(OpenSSL COMPONENTS Crypto) +include("${CMAKE_CURRENT_LIST_DIR}/tinyldap-targets.cmake")