pax_global_header00006660000000000000000000000064147755473110014530gustar00rootroot0000000000000052 comment=d346418bc962863aead071322474b422eb2f7789 openiked-7.4/000077500000000000000000000000001477554731100132005ustar00rootroot00000000000000openiked-7.4/.clusterfuzzlite/000077500000000000000000000000001477554731100165345ustar00rootroot00000000000000openiked-7.4/.clusterfuzzlite/Dockerfile000066400000000000000000000003501477554731100205240ustar00rootroot00000000000000FROM gcr.io/oss-fuzz-base/base-builder:v1 ENV CLUSTERFUZZLITE=TRUE RUN apt-get update && apt-get install -y bison libssl-dev libevent-dev COPY . $SRC/openiked-portable WORKDIR openiked-portable COPY .clusterfuzzlite/build.sh $SRC/ openiked-7.4/.clusterfuzzlite/build.sh000077500000000000000000000004601477554731100201720ustar00rootroot00000000000000#!/bin/bash -eu # build project cmake -S . -B build -DCMAKE_BUILD_TYPE=DEBUG -DCLUSTERFUZZ=ON cmake --build build # copy binary and dict to $OUT cp build/regress/parser-libfuzzer/test_libfuzzer $OUT/ cp regress/parser-libfuzzer/test_libfuzzer.dict regress/parser-libfuzzer/test_libfuzzer.options $OUT/ openiked-7.4/.clusterfuzzlite/project.yaml000066400000000000000000000000141477554731100210610ustar00rootroot00000000000000language: c openiked-7.4/.github/000077500000000000000000000000001477554731100145405ustar00rootroot00000000000000openiked-7.4/.github/workflows/000077500000000000000000000000001477554731100165755ustar00rootroot00000000000000openiked-7.4/.github/workflows/cflite_batch.yml000066400000000000000000000020361477554731100217300ustar00rootroot00000000000000name: ClusterFuzzLite batch fuzzing on: schedule: - cron: '0 0/6 * * *' # Every 6th hour. Change this to whatever is suitable. permissions: read-all jobs: BatchFuzzing: runs-on: ubuntu-latest strategy: fail-fast: false matrix: sanitizer: - address - undefined - memory steps: - name: Build Fuzzers (${{ matrix.sanitizer }}) id: build uses: google/clusterfuzzlite/actions/build_fuzzers@v1 with: sanitizer: ${{ matrix.sanitizer }} - name: Run Fuzzers (${{ matrix.sanitizer }}) id: run uses: google/clusterfuzzlite/actions/run_fuzzers@v1 with: github-token: ${{ secrets.GITHUB_TOKEN }} fuzz-seconds: 10800 # run 3h mode: 'batch' sanitizer: ${{ matrix.sanitizer }} # For storing certain artifacts from fuzzing. storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/openiked/openiked-fuzzing.git storage-repo-branch: main storage-repo-branch-coverage: gh-pages openiked-7.4/.github/workflows/cflite_cron.yml000066400000000000000000000030071477554731100216070ustar00rootroot00000000000000name: ClusterFuzzLite cron tasks on: schedule: - cron: '0 0 * * *' # Once a day at midnight. permissions: read-all jobs: Pruning: runs-on: ubuntu-latest steps: - name: Build Fuzzers id: build uses: google/clusterfuzzlite/actions/build_fuzzers@v1 - name: Run Fuzzers id: run uses: google/clusterfuzzlite/actions/run_fuzzers@v1 with: github-token: ${{ secrets.GITHUB_TOKEN }} fuzz-seconds: 600 mode: 'prune' # For storing certain artifacts from fuzzing. storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/openiked/openiked-fuzzing.git storage-repo-branch: main storage-repo-branch-coverage: gh-pages Coverage: runs-on: ubuntu-latest steps: - name: Build Fuzzers id: build uses: google/clusterfuzzlite/actions/build_fuzzers@v1 with: sanitizer: coverage - name: Run Fuzzers id: run uses: google/clusterfuzzlite/actions/run_fuzzers@v1 with: github-token: ${{ secrets.GITHUB_TOKEN }} fuzz-seconds: 600 mode: 'coverage' sanitizer: 'coverage' # For storing certain artifacts from fuzzing. # coverage-report will be created at # https://openiked.github.io/openiked-fuzzing/coverage/latest/report/linux/report.html storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/openiked/openiked-fuzzing.git storage-repo-branch: main storage-repo-branch-coverage: gh-pages openiked-7.4/.github/workflows/cflite_pr.yml000066400000000000000000000023741477554731100212750ustar00rootroot00000000000000name: ClusterFuzzLite PR fuzzing on: pull_request: paths: - iked/ikev2_pld.c - regress/parser-libfuzzer/** permissions: read-all jobs: PR: runs-on: ubuntu-latest strategy: fail-fast: false matrix: sanitizer: - address - undefined steps: - name: Build Fuzzers (${{ matrix.sanitizer }}) id: build uses: google/clusterfuzzlite/actions/build_fuzzers@v1 with: sanitizer: ${{ matrix.sanitizer }} # used to only run fuzzers that are affected by the PR. storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/openiked/openiked-fuzzing.git storage-repo-branch: main storage-repo-branch-coverage: gh-pages - name: Run Fuzzers (${{ matrix.sanitizer }}) id: run uses: google/clusterfuzzlite/actions/run_fuzzers@v1 with: github-token: ${{ secrets.GITHUB_TOKEN }} fuzz-seconds: 600 mode: 'code-change' sanitizer: ${{ matrix.sanitizer }} # used to download the corpus produced by batch fuzzing. storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/openiked/openiked-fuzzing.git storage-repo-branch: main storage-repo-branch-coverage: gh-pages openiked-7.4/.github/workflows/cmake.yml000066400000000000000000000127571477554731100204140ustar00rootroot00000000000000name: CMake on: push: branches: [ master, ci ] pull_request: branches: [ master ] env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release CC: clang jobs: ubuntu: # The CMake configure and build commands are platform agnostic and should work equally # well on Windows or Mac. You can convert this to a matrix build if you need # cross-platform coverage. # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix strategy: matrix: os: [ ubuntu-latest, ubuntu-22.04 ] runs-on: ${{ matrix.os }} # env: # CFLAGS: -Werror steps: - uses: actions/checkout@v2 - name: Install libevent run: | sudo apt update sudo apt install libevent-dev libsystemd-dev - name: Create Build Environment # Some projects don't allow in-source building, so create a separate build directory # We'll use this as our working directory for all subsequent commands run: cmake -E make_directory ${{github.workspace}}/build - name: Configure CMake # Use a bash shell so we can use the same syntax for environment variable # access regardless of the host operating system shell: bash working-directory: ${{github.workspace}}/build # Note the current convention is to use the -S and -B options here to specify source # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE - name: Build working-directory: ${{github.workspace}}/build shell: bash # Execute the build. You can specify a specific target with "--target " run: cmake --build . --config $BUILD_TYPE - name: Test DH working-directory: ${{github.workspace}}/build/regress/dh shell: bash run: ./dhtest - name: Test Parser working-directory: ${{github.workspace}}/build/regress/parser shell: bash run: ./test_parser macos: strategy: matrix: os: [ macos-latest ] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build - name: Install dependencies run: | brew install bison libevent openssl echo "/opt/homebrew/opt/bison/bin" >> $GITHUB_PATH - name: Configure CMake shell: bash working-directory: ${{github.workspace}}/build run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DHOMEBREW=yes - name: Build working-directory: ${{github.workspace}}/build shell: bash run: cmake --build . --config $BUILD_TYPE - name: Test DH working-directory: ${{github.workspace}}/build/regress/dh shell: bash run: ./dhtest - name: Test Parser working-directory: ${{github.workspace}}/build/regress/parser shell: bash run: ./test_parser openbsd: runs-on: ubuntu-latest steps: - name: Bootstrap OpenBSD-latest uses: mario-campos/emulate@v1 with: operating-system: openbsd-latest - name: Install Dependencies run: pkg_add cmake git - name: Build run: | git clone --depth=1 https://github.com/openiked/openiked-portable.git cd openiked-portable [ "${{ github.event.pull_request.number }}" = "" ] || (echo "fetching PR ${{ github.event.pull_request.number }}"; git fetch origin pull/${{ github.event.pull_request.number }}/head:pr-${{ github.event.pull_request.number }} && git checkout "pr-${{ github.event.pull_request.number }}") mkdir build; cd build cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE make cd regress/dh; ./dhtest cd ../parser; ./test_parser freebsd: runs-on: ubuntu-latest steps: - name: Bootstrap FreeBSD-latest uses: mario-campos/emulate@v1 with: operating-system: freebsd-latest - name: Install Dependencies run: pkg install -y cmake libevent git - name: Build run: | git clone --depth=1 https://github.com/openiked/openiked-portable.git cd openiked-portable [ "${{ github.event.pull_request.number }}" = "" ] || (echo "fetching PR ${{ github.event.pull_request.number }}"; git fetch origin pull/${{ github.event.pull_request.number }}/head:pr-${{ github.event.pull_request.number }} && git checkout "pr-${{ github.event.pull_request.number }}") mkdir build; cd build cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE make cd regress/dh; ./dhtest cd ../parser; ./test_parser netbsd: runs-on: ubuntu-latest steps: - name: Bootstrap NetBSD-latest uses: mario-campos/emulate@v1 with: operating-system: netbsd-latest - name: Build run: | git clone --depth=1 https://github.com/openiked/openiked-portable.git cd openiked-portable [ "${{ github.event.pull_request.number }}" = "" ] || (echo "fetching PR ${{ github.event.pull_request.number }}"; git fetch origin pull/${{ github.event.pull_request.number }}/head:pr-${{ github.event.pull_request.number }} && git checkout "pr-${{ github.event.pull_request.number }}") mkdir build; cd build cmake .. -DCMAKE_BUILD_TYPE=$BUILD_TYPE make cd regress/dh; ./dhtest cd ../parser; ./test_parser openiked-7.4/.github/workflows/codechecker.yaml000066400000000000000000000025561477554731100217300ustar00rootroot00000000000000name: CodeChecker on: schedule: - cron: '0 0 * * *' workflow_dispatch: jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install libevent run: sudo apt install libevent-dev libsystemd-dev - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build - name: Configure CMake shell: bash working-directory: ${{github.workspace}}/build run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON - name: Build working-directory: ${{github.workspace}}/build shell: bash run: cmake --build . --config Debug # Run the analysis - uses: whisperity/codechecker-analysis-action@v1 id: codechecker with: logfile: ${{ github.workspace }}/build/compile_commands.json # Upload the results to the CI. - uses: actions/upload-pages-artifact@v3 with: path: ${{ steps.codechecker.outputs.result-html-dir }} deploy: needs: check permissions: pages: write id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 openiked-7.4/CMakeLists.txt000066400000000000000000000254551477554731100157530ustar00rootroot00000000000000# Copyright (c) 2020-2022 Tobias Heider # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. cmake_minimum_required(VERSION 3.12) project(openiked) include(CheckFunctionExists) include(CheckLibraryExists) include(CheckSymbolExists) include(CheckIncludeFiles) include(CheckLinkerFlag) include_directories("/usr/local/include") link_directories("/usr/local/lib") if (CMAKE_SYSTEM_NAME MATCHES "Darwin") if (NOT DEFINED CMAKE_INSTALL_SYSCONFDIR) set (CMAKE_INSTALL_SYSCONFDIR ${CMAKE_INSTALL_PREFIX}/etc) endif() add_definitions(-DIKED_CONFIG="${CMAKE_INSTALL_SYSCONFDIR}/iked.conf") add_definitions(-DIKED_CA="${CMAKE_INSTALL_SYSCONFDIR}/iked/") add_definitions(-DHAVE_APPLE_NATT) add_definitions(-DHAVE_SOCKADDR_SA_LEN) if (HOMEBREW AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "arm64") include_directories("/opt/homebrew/include") link_directories("/opt/homebrew/lib") include_directories("/opt/homebrew/opt/openssl/include") link_directories("/opt/homebrew/opt/openssl/lib") else() include_directories("/usr/local/opt/openssl/include") link_directories("/usr/local/opt/openssl/lib") endif() set(HAVE_VROUTE ON) elseif(CMAKE_SYSTEM_NAME MATCHES "OpenBSD") if (NOT DEFINED CMAKE_INSTALL_SYSCONFDIR) set (CMAKE_INSTALL_SYSCONFDIR /etc) endif() add_definitions(-DHAVE_ATTRIBUTE__BOUNDED__) add_definitions(-DHAVE_ATTRIBUTE__DEAD__) add_definitions(-DHAVE_SOCKADDR_SA_LEN) set(HAVE_VROUTE ON) elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") if (NOT DEFINED CMAKE_INSTALL_SYSCONFDIR) set (CMAKE_INSTALL_SYSCONFDIR ${CMAKE_INSTALL_PREFIX}/etc) endif() add_definitions(-DIKED_CONFIG="${CMAKE_INSTALL_SYSCONFDIR}/iked.conf") add_definitions(-DIKED_CA="${CMAKE_INSTALL_SYSCONFDIR}/iked/") add_definitions(-DHAVE_SOCKADDR_SA_LEN) set(HAVE_VROUTE ON) elseif(CMAKE_SYSTEM_NAME MATCHES "NetBSD") if (NOT DEFINED CMAKE_INSTALL_SYSCONFDIR) set (CMAKE_INSTALL_SYSCONFDIR /etc) endif() add_definitions(-DHAVE_SOCKADDR_SA_LEN) add_definitions(-D_OPENBSD_SOURCE) set(HAVE_VROUTE ON) elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") if (NOT DEFINED CMAKE_INSTALL_SYSCONFDIR) set (CMAKE_INSTALL_SYSCONFDIR /etc) endif() if (NOT DEFINED CMAKE_INSTALL_MANDIR) set (CMAKE_INSTALL_MANDIR /usr/share/man) endif() add_definitions(-D_GNU_SOURCE) add_definitions(-D_DEFAULT_SOURCE) add_definitions(-DHAVE_UDPENCAP6) add_definitions(-DSPT_TYPE=SPT_REUSEARGV) set(HAVE_VROUTE_NETLINK ON) endif() if (NOT DEFINED CMAKE_INSTALL_MANDIR) set (CMAKE_INSTALL_MANDIR ${CMAKE_INSTALL_PREFIX}/man) endif() if (NOT DEFINED CMAKE_INSTALL_SBINDIR) set (CMAKE_INSTALL_SBINDIR ${CMAKE_INSTALL_PREFIX}/sbin) endif() check_linker_flag(C "LINKER:-z,now,-z,relro" HAVE_LD_Z) check_include_files("sys/types.h;net/pfkeyv2.h" HAVE_NET_PFKEY_H) if(HAVE_NET_PFKEY_H) add_definitions(-DHAVE_NET_PFKEY_H) endif() check_include_files("linux/pfkeyv2.h" HAVE_LINUX_PFKEY_H) if(HAVE_LINUX_PFKEY_H) add_definitions(-DHAVE_LINUX_PFKEY_H) endif() if(CMAKE_BUILD_TYPE STREQUAL DEBUG) message("Debug build.") elseif(CMAKE_BUILD_TYPE STREQUAL RELEASE) message("Release build.") endif() check_include_files(unistd.h HAVE_UNISTD_H) if(HAVE_UNISTD_H) add_definitions(-DHAVE_UNISTD_H) endif() check_include_files(endian.h HAVE_ENDIAN_H) if(HAVE_ENDIAN_H) add_definitions(-DHAVE_ENDIAN_H) endif() check_include_files(dirent.h HAVE_DIRENT_H) if(HAVE_DIRENT_H) add_definitions(-DHAVE_DIRENT_H) endif() check_include_files(grp.h HAVE_GRP_H) if(HAVE_GRP_H) add_definitions(-DHAVE_GRP_H) endif() check_include_files("sys/socket.h;netinet/ip_ipsp.h" HAVE_IPSP_H) if(HAVE_IPSP_H) add_definitions(-DHAVE_IPSP_H) endif() check_include_files("sys/types.h;netipsec/ipsec.h" HAVE_NET_IPSEC_H) if(HAVE_NET_IPSEC_H) add_definitions(-DHAVE_NET_IPSEC_H) endif() check_include_files("netinet6/ipsec.h" HAVE_NETINET6_IPSEC_H) if(HAVE_NETINET6_IPSEC_H) add_definitions(-DHAVE_NETINET6_IPSEC_H) endif() check_include_files("linux/ipsec.h" HAVE_LINUX_IPSEC_H) if(HAVE_LINUX_IPSEC_H) add_definitions(-DHAVE_LINUX_IPSEC_H) endif() check_include_files("sys/types.h;sys/queue.h;imsg.h" HAVE_IMSG_H) if(HAVE_IMSG_H) add_definitions(-DHAVE_IMSG_H) endif() check_function_exists(recallocarray HAVE_RECALLOCARRAY) if(HAVE_RECALLOCARRAY) add_definitions(-DHAVE_RECALLOCARRAY) endif() check_function_exists(reallocarray HAVE_REALLOCARRAY) if(HAVE_REALLOCARRAY) add_definitions(-DHAVE_REALLOCARRAY) endif() check_function_exists(accept4 HAVE_ACCEPT4) if(HAVE_ACCEPT4) add_definitions(-DHAVE_ACCEPT4) endif() check_symbol_exists(SOCK_NONBLOCK "sys/socket.h" HAVE_SOCK_NONBLOCK) if(HAVE_SOCK_NONBLOCK) add_definitions(-DHAVE_SOCK_NONBLOCK) endif() check_function_exists(setproctitle HAVE_SETPROCTITLE) if(HAVE_SETPROCTITLE) add_definitions(-DHAVE_SETPROCTITLE) endif() check_function_exists(pledge HAVE_PLEDGE) if(HAVE_PLEDGE) add_definitions(-DHAVE_PLEDGE) endif() check_function_exists(setresgid HAVE_SETRESGID) if(HAVE_SETRESGID) add_definitions(-DHAVE_SETRESGID) endif() check_function_exists(setresuid HAVE_SETRESUID) if(HAVE_SETRESUID) add_definitions(-DHAVE_SETRESUID) endif() check_function_exists(setregid HAVE_SETREGID) if(HAVE_SETREGID) add_definitions(-DHAVE_SETREGID) endif() check_function_exists(setreuid HAVE_SETREUID) if(HAVE_SETREUID) add_definitions(-DHAVE_SETREUID) endif() check_function_exists(getrtable HAVE_GETRTABLE) if(HAVE_GETRTABLE) add_definitions(-DHAVE_GETRTABLE) endif() # setrtable check_function_exists(setrtable HAVE_SETRTABLE) if(HAVE_SETRTABLE) add_definitions(-DHAVE_SETRTABLE) endif() check_function_exists(strtonum HAVE_STRTONUM) if(HAVE_STRTONUM) add_definitions(-DHAVE_STRTONUM) endif() check_symbol_exists(ifgroupreq "net/if.h" HAVE_IFGROUPREQ) if(HAVE_IFGROUPREQ) add_definitions(-DHAVE_IFGROUPREQ) endif() check_function_exists(freezero HAVE_FREEZERO) if(HAVE_FREEZERO) add_definitions(-DHAVE_FREEZERO) endif() check_function_exists(getdtablecount HAVE_GETDTABLECOUNT) if(HAVE_GETDTABLECOUNT) add_definitions(-DHAVE_GETDTABLECOUNT) endif() check_symbol_exists(timespecsub "sys/time.h" HAVE_TIMESPECSUB) if(HAVE_TIMESPECSUB) add_definitions(-DHAVE_TIMESPECSUB) endif() check_function_exists(asprintf HAVE_ASPRINTF) if(HAVE_ASPRINTF) add_definitions(-DHAVE_ASPRINTF) endif() check_function_exists(strcasecmp HAVE_STRCASECMP) if(HAVE_STRCASECMP) add_definitions(-DHAVE_STRCASECMP) endif() check_function_exists(strlcat HAVE_STRLCAT) if(HAVE_STRLCAT) add_definitions(-DHAVE_STRLCAT) endif() check_function_exists(strlcpy HAVE_STRLCPY) if(HAVE_STRLCPY) add_definitions(-DHAVE_STRLCPY) endif() check_function_exists(strndup HAVE_STRNDUP) if(HAVE_STRNDUP) add_definitions(-DHAVE_STRNDUP) endif() check_function_exists(ffs HAVE_FFS) if(HAVE_FFS) add_definitions(-DHAVE_FFS) endif() check_function_exists(strnlen HAVE_STRNLEN) if(HAVE_STRNLEN) add_definitions(-DHAVE_STRNLEN) endif() check_function_exists(strsep HAVE_STRSEP) if(HAVE_STRSEP) add_definitions(-DHAVE_STRSEP) endif() check_function_exists(timegm HAVE_TIMEGM) if(HAVE_TIMEGM) add_definitions(-DHAVE_TIMEGM) endif() check_function_exists(arc4random_buf HAVE_ARC4RANDOM_BUF) if(HAVE_ARC4RANDOM_BUF) add_definitions(-DHAVE_ARC4RANDOM_BUF) endif() check_function_exists(arc4random_uniform HAVE_ARC4RANDOM_UNIFORM) if(HAVE_ARC4RANDOM_UNIFORM) add_definitions(-DHAVE_ARC4RANDOM_UNIFORM) endif() check_function_exists(explicit_bzero HAVE_EXPLICIT_BZERO) if(HAVE_EXPLICIT_BZERO) add_definitions(-DHAVE_EXPLICIT_BZERO) endif() check_function_exists(getauxval HAVE_GETAUXVAL) if(HAVE_GETAUXVAL) add_definitions(-DHAVE_GETAUXVAL) endif() check_function_exists(getentropy HAVE_GETENTROPY) if(HAVE_GETENTROPY) add_definitions(-DHAVE_GETENTROPY) endif() check_symbol_exists(getpagesize unistd.h HAVE_GETPAGESIZE) if(HAVE_GETPAGESIZE) add_definitions(-DHAVE_GETPAGESIZE) endif() check_function_exists(getprogname HAVE_GETPROGNAME) if(HAVE_GETPROGNAME) add_definitions(-DHAVE_GETPROGNAME) endif() check_function_exists(syslog_r HAVE_SYSLOG_R) if(HAVE_SYSLOG_R) add_definitions(-DHAVE_SYSLOG_R) endif() check_function_exists(syslog HAVE_SYSLOG) if(HAVE_SYSLOG) add_definitions(-DHAVE_SYSLOG) endif() check_function_exists(vis HAVE_VIS) if(HAVE_VIS) add_definitions(-DHAVE_VIS) endif() check_symbol_exists(timespecsub sys/time.h HAVE_TIMESPECSUB) if(HAVE_TIMESPECSUB) add_definitions(-DHAVE_TIMESPECSUB) endif() check_function_exists(timingsafe_bcmp HAVE_TIMINGSAFE_BCMP) if(HAVE_TIMINGSAFE_BCMP) add_definitions(-DHAVE_TIMINGSAFE_BCMP) endif() check_function_exists(timingsafe_memcmp HAVE_TIMINGSAFE_MEMCMP) if(HAVE_MEMCMP) add_definitions(-DHAVE_MEMCMP) endif() check_function_exists(memmem HAVE_MEMMEM) if(HAVE_MEMMEM) add_definitions(-DHAVE_MEMMEM) endif() check_include_files(err.h HAVE_ERR_H) if(HAVE_ERR_H) add_definitions(-DHAVE_ERR_H) endif() check_function_exists(usleep HAVE_USLEEP) if(HAVE_USLEEP) add_definitions(-DHAVE_USLEEP) endif() check_function_exists(getopt HAVE_GETOPT) if(HAVE_GETOPT) add_definitions(-DHAVE_GETOPT) endif() check_function_exists(msgbuf_new_reader HAVE_MSGBUF_NEW_READER) if(HAVE_MSGBUF_NEW_READER) add_definitions(-DHAVE_MSGBUF_NEW_READER) endif() if(HAVE_VROUTE OR HAVE_VROUTE_NETLINK) add_definitions(-DHAVE_VROUTE) endif() if(WITH_SYSTEMD) add_definitions(-DWITH_SYSTEMD) endif() if(WITH_APPARMOR) add_definitions(-DWITH_APPARMOR) endif() if(ASAN) message("Using ASAN") string(APPEND CMAKE_C_FLAGS " -fno-omit-frame-pointer -fsanitize=address") string(APPEND CMAKE_LINKER_FLAGS " -fno-omit-frame-pointer -fsanitize=address") endif() if(UBSAN) message("Using UBSAN") string(APPEND CMAKE_C_FLAGS " -fno-omit-frame-pointer -fsanitize=undefined") string(APPEND CMAKE_LINKER_FLAGS " -fno-omit-frame-pointer -fsanitize=undefined") endif() if(MSAN) message("Using MSAN") string(APPEND CMAKE_C_FLAGS " -fno-omit-frame-pointer -fsanitize=memory") string(APPEND CMAKE_LINKER_FLAGS " -fno-omit-frame-pointer -fsanitize=memory") endif() if(CLUSTERFUZZ) message("Compiling parser-libfuzzer") add_subdirectory(regress/parser-libfuzzer) if (NOT DEFINED ENV{CLUSTERFUZZLITE}) set(CMAKE_C_COMPILER clang) string(APPEND CMAKE_C_FLAGS " -g -O0 -fsanitize=fuzzer-no-link") endif() endif() add_subdirectory(compat) add_subdirectory(iked) add_subdirectory(ikectl) add_subdirectory(regress/dh) add_subdirectory(regress/parser) add_subdirectory(regress/test_helper) openiked-7.4/LICENSE000066400000000000000000000012741477554731100142110ustar00rootroot00000000000000Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. openiked-7.4/README.md000066400000000000000000000046571477554731100144730ustar00rootroot00000000000000# OpenIKED [![License](https://img.shields.io/github/license/openiked/openiked-portable)](https://github.com/openiked/openiked-portable/blob/master/LICENSE) [![CMake](https://github.com/openiked/openiked-portable/workflows/CMake/badge.svg)](https://github.com/openiked/openiked-portable/actions?query=workflow%3ACMake) [![#openiked on matrix.org](https://img.shields.io/badge/matrix-%23openiked-blue)](https://app.element.io/#/room/#openiked:matrix.org) [![#openiked on libera.chat](https://img.shields.io/badge/IRC-%23openiked-blue)](https://kiwiirc.com/nextclient/irc.libera.chat/#openiked) This is a port of OpenBSD's [OpenIKED](https://openiked.org) to other Unix-like operating systems including Linux, macOS, FreeBSD and NetBSD. ## Documentation The official documentation for OpenIKED are the man pages for each tool: * [iked(8)](https://man.openbsd.org/iked.8) * [ikectl(8)](https://man.openbsd.org/ikectl.8) * [iked.conf(5)](https://man.openbsd.org/iked.conf.5) and the [OpenBSD VPN FAQ](https://www.openbsd.org/faq/faq17.html). ## Installing OpenIKED ### Binary Packages Binary packages for OpenIKED are available for the package managers of various operating systems and Linux distributions: * [FreeBSD](https://www.freshports.org/security/openiked/) * [Debian](https://tracker.debian.org/pkg/openiked) * [Fedora](https://packages.fedoraproject.org/pkgs/openiked/openiked/index.html) * [Ubuntu](https://launchpad.net/ubuntu/+source/openiked) * [Arch Linux User Repository (AUR)](https://aur.archlinux.org/packages/openiked) * [openSUSE and SUSE Linux Enterprise](https://build.opensuse.org/package/show/network:vpn/openiked) * [MacPorts](https://ports.macports.org/port/openiked/) * [Homebrew](https://formulae.brew.sh/formula/openiked) ### Building from source Portable OpenIKED is built using ``cmake``. It requires a working C compiler, standard library and headers, a ``yacc`` compatible parser generator, ``libevent``, and ``libcrypto`` from either [LibreSSL](https://www.libressl.org/) or [OpenSSL](https://www.openssl.org). ``` git clone https://github.com/openiked/openiked-portable.git cd openiked-portable mkdir build && cd build cmake -DCMAKE_BUILD_TYPE=Release .. make # install make install ``` A few additional setup steps are required to create the intial configuration files, key material and system users. The easiest way to do this is running the setup scripts included in the source repository. ``` ./setup_config.sh ./useradd.sh ``` openiked-7.4/compat/000077500000000000000000000000001477554731100144635ustar00rootroot00000000000000openiked-7.4/compat/CMakeLists.txt000066400000000000000000000054551477554731100172340ustar00rootroot00000000000000# Copyright (c) 2020-2021 Tobias Heider # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. set(IKED_COMPAT "${CMAKE_CURRENT_SOURCE_DIR}") set(SRCS) if(NOT HAVE_RECALLOCARRAY) list(APPEND SRCS ${IKED_COMPAT}/recallocarray.c) endif() if(NOT HAVE_SOCK_NONBLOCK) list(APPEND SRCS ${IKED_COMPAT}/bsd-socket.c) endif() if(NOT HAVE_SETRESUID) list(APPEND SRCS ${IKED_COMPAT}/bsd-setres_id.c) endif() if(NOT HAVE_GETRTABLE) list(APPEND SRCS ${IKED_COMPAT}/getrtable.c) endif() if(NOT HAVE_GETDTABLECOUNT) list(APPEND SRCS ${IKED_COMPAT}/getdtablecount.c) endif() if(NOT HAVE_SETPROCTITLE) list(APPEND SRCS ${IKED_COMPAT}/setproctitle.c) endif() if(NOT HAVE_STRTONUM) list(APPEND SRCS ${IKED_COMPAT}/strtonum.c) endif() if(NOT HAVE_FFS) list(APPEND SRCS ${IKED_COMPAT}/ffs.c) endif() if(NOT HAVE_GETOPT) list(APPEND SRCS ${IKED_COMPAT}/getopt_long.c) endif() if(NOT HAVE_IMSG_H OR NOT HAVE_MSGBUF_NEW_READER) list(APPEND SRCS # imsg ${IKED_COMPAT}/imsg.c ${IKED_COMPAT}/imsg-buffer.c ) endif() if(NOT HAVE_STRLCPY) list(APPEND SRCS ${IKED_COMPAT}/strlcpy.c) endif() if(NOT HAVE_STRLCAT) list(APPEND SRCS ${IKED_COMPAT}/strlcat.c) endif() if(NOT HAVE_FREEZERO) list(APPEND SRCS ${IKED_COMPAT}/freezero.c) endif() if(NOT HAVE_ARC4RANDOM_BUF) list(APPEND SRCS ${IKED_COMPAT}/arc4random.c) endif() if(NOT HAVE_ARC4RANDOM_UNIFORM) list(APPEND SRCS ${IKED_COMPAT}/arc4random_uniform.c) endif() if(NOT HAVE_EXPLICIT_BZERO) list(APPEND SRCS ${IKED_COMPAT}/explicit_bzero.c) endif() if(NOT HAVE_REALLOCARRAY) list(APPEND SRCS ${IKED_COMPAT}/reallocarray.c) endif() if(NOT HAVE_VIS) list(APPEND SRCS ${IKED_COMPAT}/vis.c) endif() set(CFLAGS) list(APPEND CFLAGS -O2 -fstack-protector-strong -fPIE -D_FORTIFY_SOURCE=2 -Wall -Wno-pointer-sign -Wno-deprecated-declarations -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wshadow -Wpointer-arith -Wcast-qual -Wsign-compare "$<$:-O0;-g>" ) if(SRCS) add_library(compat OBJECT ${SRCS}) target_compile_options(compat PRIVATE ${CFLAGS}) target_include_directories(compat PUBLIC .) else() add_library(compat INTERFACE) target_include_directories(compat INTERFACE .) endif() openiked-7.4/compat/arc4random.c000066400000000000000000000115031477554731100166610ustar00rootroot00000000000000/* $OpenBSD: arc4random.c,v 1.58 2022/07/31 13:41:45 tb Exp $ */ /* * Copyright (c) 1996, David Mazieres * Copyright (c) 2008, Damien Miller * Copyright (c) 2013, Markus Friedl * Copyright (c) 2014, Theo de Raadt * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * ChaCha based random number generator for OpenBSD. */ #include #include #include #include #include #include #include #include #include #define KEYSTREAM_ONLY #include "chacha_private.h" #define minimum(a, b) ((a) < (b) ? (a) : (b)) #if defined(__GNUC__) || defined(_MSC_VER) #define inline __inline #else /* __GNUC__ || _MSC_VER */ #define inline #endif /* !__GNUC__ && !_MSC_VER */ #define KEYSZ 32 #define IVSZ 8 #define BLOCKSZ 64 #define RSBUFSZ (16*BLOCKSZ) #define REKEY_BASE (1024*1024) /* NB. should be a power of 2 */ /* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */ static struct _rs { size_t rs_have; /* valid bytes at end of rs_buf */ size_t rs_count; /* bytes till reseed */ } *rs; /* Maybe be preserved in fork children, if _rs_allocate() decides. */ static struct _rsx { chacha_ctx rs_chacha; /* chacha context for random keystream */ u_char rs_buf[RSBUFSZ]; /* keystream blocks */ } *rsx; static inline int _rs_allocate(struct _rs **, struct _rsx **); static inline void _rs_forkdetect(void); #include "arc4random.h" static inline void _rs_rekey(u_char *dat, size_t datlen); static inline void _rs_init(u_char *buf, size_t n) { if (n < KEYSZ + IVSZ) return; if (rs == NULL) { if (_rs_allocate(&rs, &rsx) == -1) _exit(1); } chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8); chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ); } static void _rs_stir(void) { u_char rnd[KEYSZ + IVSZ]; uint32_t rekey_fuzz = 0; if (getentropy(rnd, sizeof rnd) == -1) _getentropy_fail(); if (!rs) _rs_init(rnd, sizeof(rnd)); else _rs_rekey(rnd, sizeof(rnd)); explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */ /* invalidate rs_buf */ rs->rs_have = 0; memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf)); /* rekey interval should not be predictable */ chacha_encrypt_bytes(&rsx->rs_chacha, (uint8_t *)&rekey_fuzz, (uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz)); rs->rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE); } static inline void _rs_stir_if_needed(size_t len) { _rs_forkdetect(); if (!rs || rs->rs_count <= len) _rs_stir(); if (rs->rs_count <= len) rs->rs_count = 0; else rs->rs_count -= len; } static inline void _rs_rekey(u_char *dat, size_t datlen) { #ifndef KEYSTREAM_ONLY memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf)); #endif /* fill rs_buf with the keystream */ chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf, rsx->rs_buf, sizeof(rsx->rs_buf)); /* mix in optional user provided data */ if (dat) { size_t i, m; m = minimum(datlen, KEYSZ + IVSZ); for (i = 0; i < m; i++) rsx->rs_buf[i] ^= dat[i]; } /* immediately reinit for backtracking resistance */ _rs_init(rsx->rs_buf, KEYSZ + IVSZ); memset(rsx->rs_buf, 0, KEYSZ + IVSZ); rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ; } static inline void _rs_random_buf(void *_buf, size_t n) { u_char *buf = (u_char *)_buf; u_char *keystream; size_t m; _rs_stir_if_needed(n); while (n > 0) { if (rs->rs_have > 0) { m = minimum(n, rs->rs_have); keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have; memcpy(buf, keystream, m); memset(keystream, 0, m); buf += m; n -= m; rs->rs_have -= m; } if (rs->rs_have == 0) _rs_rekey(NULL, 0); } } static inline void _rs_random_u32(uint32_t *val) { u_char *keystream; _rs_stir_if_needed(sizeof(*val)); if (rs->rs_have < sizeof(*val)) _rs_rekey(NULL, 0); keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have; memcpy(val, keystream, sizeof(*val)); memset(keystream, 0, sizeof(*val)); rs->rs_have -= sizeof(*val); } uint32_t arc4random(void) { uint32_t val; _ARC4_LOCK(); _rs_random_u32(&val); _ARC4_UNLOCK(); return val; } void arc4random_buf(void *buf, size_t n) { _ARC4_LOCK(); _rs_random_buf(buf, n); _ARC4_UNLOCK(); } openiked-7.4/compat/arc4random.h000066400000000000000000000012671477554731100166740ustar00rootroot00000000000000/* * Public domain * arc4random.h compatibility shim */ #ifndef IKED_COMPAT_ARC4RANDOM_H #define IKED_COMPAT_ARC4RANDOM_H #if defined(_AIX) #include "arc4random_aix.h" #elif defined(__FreeBSD__) #include "arc4random_freebsd.h" #elif defined(__hpux) #include "arc4random_hpux.h" #elif defined(__linux__) #include "arc4random_linux.h" #elif defined(__midipix__) #include "arc4random_linux.h" #elif defined(__NetBSD__) #include "arc4random_netbsd.h" #elif defined(__APPLE__) #include "arc4random_osx.h" #elif defined(__sun) #include "arc4random_solaris.h" #elif defined(_WIN32) #include "arc4random_win.h" #else #error "No arc4random hooks defined for this platform." #endif #endif openiked-7.4/compat/arc4random_linux.h000066400000000000000000000050071477554731100201070ustar00rootroot00000000000000/* $OpenBSD: arc4random_linux.h,v 1.12 2019/07/11 10:37:28 inoguchi Exp $ */ /* * Copyright (c) 1996, David Mazieres * Copyright (c) 2008, Damien Miller * Copyright (c) 2013, Markus Friedl * Copyright (c) 2014, Theo de Raadt * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Stub functions for portability. */ #include #include #include static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER; #define _ARC4_LOCK() pthread_mutex_lock(&arc4random_mtx) #define _ARC4_UNLOCK() pthread_mutex_unlock(&arc4random_mtx) #if defined(__GLIBC__) && !(defined(__UCLIBC__) && !defined(__ARCH_USE_MMU__)) extern void *__dso_handle; extern int __register_atfork(void (*)(void), void(*)(void), void (*)(void), void *); #define _ARC4_ATFORK(f) __register_atfork(NULL, NULL, (f), __dso_handle) #else #define _ARC4_ATFORK(f) pthread_atfork(NULL, NULL, (f)) #endif static inline void _getentropy_fail(void) { raise(SIGKILL); } static volatile sig_atomic_t _rs_forked; static inline void _rs_forkhandler(void) { _rs_forked = 1; } static inline void _rs_forkdetect(void) { static pid_t _rs_pid = 0; pid_t pid = getpid(); /* XXX unusual calls to clone() can bypass checks */ if (_rs_pid == 0 || _rs_pid == 1 || _rs_pid != pid || _rs_forked) { _rs_pid = pid; _rs_forked = 0; if (rs) memset(rs, 0, sizeof(*rs)); } } static inline int _rs_allocate(struct _rs **rsp, struct _rsx **rsxp) { if ((*rsp = mmap(NULL, sizeof(**rsp), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) return (-1); if ((*rsxp = mmap(NULL, sizeof(**rsxp), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) { munmap(*rsp, sizeof(**rsp)); *rsp = NULL; return (-1); } _ARC4_ATFORK(_rs_forkhandler); return (0); } openiked-7.4/compat/arc4random_uniform.c000066400000000000000000000034031477554731100204200ustar00rootroot00000000000000/* $OpenBSD: arc4random_uniform.c,v 1.3 2019/01/20 02:59:07 bcook Exp $ */ /* * Copyright (c) 2008, Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include /* * Calculate a uniformly distributed random number less than upper_bound * avoiding "modulo bias". * * Uniformity is achieved by generating new random numbers until the one * returned is outside the range [0, 2**32 % upper_bound). This * guarantees the selected random number will be inside * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound) * after reduction modulo upper_bound. */ uint32_t arc4random_uniform(uint32_t upper_bound) { uint32_t r, min; if (upper_bound < 2) return 0; /* 2**32 % x == (2**32 - x) % x */ min = -upper_bound % upper_bound; /* * This could theoretically loop forever but each retry has * p > 0.5 (worst case, usually far better) of selecting a * number inside the range we need, so it should rarely need * to re-roll. */ for (;;) { r = arc4random(); if (r >= min) break; } return r % upper_bound; } openiked-7.4/compat/bsd-setres_id.c000066400000000000000000000033161477554731100173610ustar00rootroot00000000000000/* * Copyright (c) 2012 Darren Tucker (dtucker at zip com au). * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "openbsd-compat.h" #include #include #include #include #include #if !defined(HAVE_SETRESGID) || defined(BROKEN_SETRESGID) int setresgid(gid_t rgid, gid_t egid, gid_t sgid) { int ret = 0; if (rgid != sgid) { errno = ENOSYS; return -1; } #if defined(HAVE_SETREGID) && !defined(BROKEN_SETREGID) if (setregid(rgid, egid) < 0) ret = -1; #else if (setegid(egid) < 0) ret = -1; if (setgid(rgid) < 0) ret = -1; #endif return ret; } #endif #if !defined(HAVE_SETRESUID) || defined(BROKEN_SETRESUID) int setresuid(uid_t ruid, uid_t euid, uid_t suid) { int ret = 0; if (ruid != suid) { errno = ENOSYS; return -1; } #if defined(HAVE_SETREUID) && !defined(BROKEN_SETREUID) if (setreuid(ruid, euid) < 0) ret = -1; #else # ifndef SETEUID_BREAKS_SETUID if (seteuid(euid) < 0) ret = -1; # endif if (setuid(ruid) < 0) ret = -1; #endif return ret; } #endif openiked-7.4/compat/bsd-socket.c000066400000000000000000000057421477554731100166750ustar00rootroot00000000000000/* * Copyright (c) 2016 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include "openbsd-compat.h" #undef socket #undef accept4 #if defined(SOCK_SETFLAGS) #include static int bsd_socket_setflags(int s, int flags) { #ifdef _WIN32 /* see libressl/tests/compat/pipe2.c */ if (flags & FD_CLOEXEC) { HANDLE h = (HANDLE)_get_osfhandle(s); if (h != NULL) if (SetHandleInformation(h, HANDLE_FLAG_INHERIT, 0) == 0) return (-1); } if (flags & O_NONBLOCK) { unsigned long mode = 1; if (ioctlsocket(s, FIONBIO, &mode) != 0) return (-1); } return (0); #else int f; if (flags & SOCK_NONBLOCK) { if (fcntl(s, F_GETFL, &f) == -1) return (-1); f |= O_NONBLOCK; if (fcntl(s, F_SETFL, &f) == -1) return (-1); } if (flags & SOCK_CLOEXEC) { if (fcntl(s, F_GETFD, &f) == -1) return (-1); f |= FD_CLOEXEC; if (fcntl(s, F_SETFD, &f) == -1) return (-1); } return (0); #endif } #endif int bsd_socket(int domain, int type, int protocol) { int s; #if defined(SOCK_SETFLAGS) int setfl; setfl = type & SOCK_SETFLAGS; type &= ~SOCK_SETFLAGS; #endif if ((s = socket(domain, type, protocol)) == -1) return (-1); #if defined(SOCK_SETFLAGS) if (bsd_socket_setflags(s, setfl) == -1) { close(s); return (-1); } #endif return (s); } #if 0 /* conflicts w/ libressl/compat/pipe2.c */ int bsd_socketpair(int d, int type, int protocol, int sv[2]) { #if defined(SOCK_SETFLAGS) int setfl; int i; setfl = type & SOCK_SETFLAGS; type &= ~SOCK_SETFLAGS; #endif if (socketpair(d, type, protocol, sv) == -1) return (-1); #if defined(SOCK_SETFLAGS) for (i = 0; i < 2; i++) { if (bsd_socket_setflags(sv[i], setfl) == -1) { close(sv[0]); close(sv[1]); return (-1); } } #endif return (0); } #endif int bsd_accept4(int s, struct sockaddr *addr, socklen_t *addrlen, int flags) { #if !defined(SOCK_SETFLAGS) && defined(HAVE_ACCEPT4) return (accept4(s, addr, addrlen, flags)); #elif defined(SOCK_SETFLAGS) int c, setfl; setfl = flags & SOCK_SETFLAGS; flags &= ~SOCK_SETFLAGS; if ((c = accept(s, addr, addrlen)) == -1) return (-1); if (bsd_socket_setflags(c, setfl) == -1) { close(c); return (-1); } return (c); #elif defined(__NetBSD__) return (paccept(s, addr, addrlen, NULL, flags)); #endif } openiked-7.4/compat/chacha_private.h000066400000000000000000000124061477554731100176000ustar00rootroot00000000000000/* chacha-merged.c version 20080118 D. J. Bernstein Public domain. */ /* $OpenBSD: chacha_private.h,v 1.3 2022/02/28 21:56:29 dtucker Exp $ */ typedef unsigned char u8; typedef unsigned int u32; typedef struct { u32 input[16]; /* could be compressed */ } chacha_ctx; #define U8C(v) (v##U) #define U32C(v) (v##U) #define U8V(v) ((u8)(v) & U8C(0xFF)) #define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF)) #define ROTL32(v, n) \ (U32V((v) << (n)) | ((v) >> (32 - (n)))) #define U8TO32_LITTLE(p) \ (((u32)((p)[0]) ) | \ ((u32)((p)[1]) << 8) | \ ((u32)((p)[2]) << 16) | \ ((u32)((p)[3]) << 24)) #define U32TO8_LITTLE(p, v) \ do { \ (p)[0] = U8V((v) ); \ (p)[1] = U8V((v) >> 8); \ (p)[2] = U8V((v) >> 16); \ (p)[3] = U8V((v) >> 24); \ } while (0) #define ROTATE(v,c) (ROTL32(v,c)) #define XOR(v,w) ((v) ^ (w)) #define PLUS(v,w) (U32V((v) + (w))) #define PLUSONE(v) (PLUS((v),1)) #define QUARTERROUND(a,b,c,d) \ a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); static const char sigma[16] = "expand 32-byte k"; static const char tau[16] = "expand 16-byte k"; static void chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits) { const char *constants; x->input[4] = U8TO32_LITTLE(k + 0); x->input[5] = U8TO32_LITTLE(k + 4); x->input[6] = U8TO32_LITTLE(k + 8); x->input[7] = U8TO32_LITTLE(k + 12); if (kbits == 256) { /* recommended */ k += 16; constants = sigma; } else { /* kbits == 128 */ constants = tau; } x->input[8] = U8TO32_LITTLE(k + 0); x->input[9] = U8TO32_LITTLE(k + 4); x->input[10] = U8TO32_LITTLE(k + 8); x->input[11] = U8TO32_LITTLE(k + 12); x->input[0] = U8TO32_LITTLE(constants + 0); x->input[1] = U8TO32_LITTLE(constants + 4); x->input[2] = U8TO32_LITTLE(constants + 8); x->input[3] = U8TO32_LITTLE(constants + 12); } static void chacha_ivsetup(chacha_ctx *x,const u8 *iv) { x->input[12] = 0; x->input[13] = 0; x->input[14] = U8TO32_LITTLE(iv + 0); x->input[15] = U8TO32_LITTLE(iv + 4); } static void chacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes) { u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; u8 *ctarget = NULL; u8 tmp[64]; u_int i; if (!bytes) return; j0 = x->input[0]; j1 = x->input[1]; j2 = x->input[2]; j3 = x->input[3]; j4 = x->input[4]; j5 = x->input[5]; j6 = x->input[6]; j7 = x->input[7]; j8 = x->input[8]; j9 = x->input[9]; j10 = x->input[10]; j11 = x->input[11]; j12 = x->input[12]; j13 = x->input[13]; j14 = x->input[14]; j15 = x->input[15]; for (;;) { if (bytes < 64) { for (i = 0;i < bytes;++i) tmp[i] = m[i]; m = tmp; ctarget = c; c = tmp; } x0 = j0; x1 = j1; x2 = j2; x3 = j3; x4 = j4; x5 = j5; x6 = j6; x7 = j7; x8 = j8; x9 = j9; x10 = j10; x11 = j11; x12 = j12; x13 = j13; x14 = j14; x15 = j15; for (i = 20;i > 0;i -= 2) { QUARTERROUND( x0, x4, x8,x12) QUARTERROUND( x1, x5, x9,x13) QUARTERROUND( x2, x6,x10,x14) QUARTERROUND( x3, x7,x11,x15) QUARTERROUND( x0, x5,x10,x15) QUARTERROUND( x1, x6,x11,x12) QUARTERROUND( x2, x7, x8,x13) QUARTERROUND( x3, x4, x9,x14) } x0 = PLUS(x0,j0); x1 = PLUS(x1,j1); x2 = PLUS(x2,j2); x3 = PLUS(x3,j3); x4 = PLUS(x4,j4); x5 = PLUS(x5,j5); x6 = PLUS(x6,j6); x7 = PLUS(x7,j7); x8 = PLUS(x8,j8); x9 = PLUS(x9,j9); x10 = PLUS(x10,j10); x11 = PLUS(x11,j11); x12 = PLUS(x12,j12); x13 = PLUS(x13,j13); x14 = PLUS(x14,j14); x15 = PLUS(x15,j15); #ifndef KEYSTREAM_ONLY x0 = XOR(x0,U8TO32_LITTLE(m + 0)); x1 = XOR(x1,U8TO32_LITTLE(m + 4)); x2 = XOR(x2,U8TO32_LITTLE(m + 8)); x3 = XOR(x3,U8TO32_LITTLE(m + 12)); x4 = XOR(x4,U8TO32_LITTLE(m + 16)); x5 = XOR(x5,U8TO32_LITTLE(m + 20)); x6 = XOR(x6,U8TO32_LITTLE(m + 24)); x7 = XOR(x7,U8TO32_LITTLE(m + 28)); x8 = XOR(x8,U8TO32_LITTLE(m + 32)); x9 = XOR(x9,U8TO32_LITTLE(m + 36)); x10 = XOR(x10,U8TO32_LITTLE(m + 40)); x11 = XOR(x11,U8TO32_LITTLE(m + 44)); x12 = XOR(x12,U8TO32_LITTLE(m + 48)); x13 = XOR(x13,U8TO32_LITTLE(m + 52)); x14 = XOR(x14,U8TO32_LITTLE(m + 56)); x15 = XOR(x15,U8TO32_LITTLE(m + 60)); #endif j12 = PLUSONE(j12); if (!j12) { j13 = PLUSONE(j13); /* stopping at 2^70 bytes per nonce is user's responsibility */ } U32TO8_LITTLE(c + 0,x0); U32TO8_LITTLE(c + 4,x1); U32TO8_LITTLE(c + 8,x2); U32TO8_LITTLE(c + 12,x3); U32TO8_LITTLE(c + 16,x4); U32TO8_LITTLE(c + 20,x5); U32TO8_LITTLE(c + 24,x6); U32TO8_LITTLE(c + 28,x7); U32TO8_LITTLE(c + 32,x8); U32TO8_LITTLE(c + 36,x9); U32TO8_LITTLE(c + 40,x10); U32TO8_LITTLE(c + 44,x11); U32TO8_LITTLE(c + 48,x12); U32TO8_LITTLE(c + 52,x13); U32TO8_LITTLE(c + 56,x14); U32TO8_LITTLE(c + 60,x15); if (bytes <= 64) { if (bytes < 64) { for (i = 0;i < bytes;++i) ctarget[i] = c[i]; } x->input[12] = j12; x->input[13] = j13; return; } bytes -= 64; c += 64; #ifndef KEYSTREAM_ONLY m += 64; #endif } } openiked-7.4/compat/endian.h000066400000000000000000000033641477554731100161000ustar00rootroot00000000000000/* * Public domain * endian.h compatibility shim */ #ifndef IKED_COMPAT_ENDIAN_H #define IKED_COMPAT_ENDIAN_H #ifdef HAVE_ENDIAN_H #include_next #endif /* HAVE_ENDIAN_H */ #if defined(__APPLE__) && !defined(HAVE_ENDIAN_H) #include #define betoh16(x) OSSwapBigToHostInt16((x)) #define htobe16(x) OSSwapHostToBigInt16((x)) #define betoh32(x) OSSwapBigToHostInt32((x)) #define htobe32(x) OSSwapHostToBigInt32(x) #define htole64(x) OSSwapHostToLittleInt64(x) #define htobe64(x) OSSwapHostToBigInt64(x) #define letoh64(x) OSSwapLittleToHostInt64(x) #define betoh64(x) OSSwapBigToHostInt64(x) #define be16toh betoh16 #define be32toh betoh32 #define be64toh betoh64 #endif /* __APPLE__ && !HAVE_ENDIAN_H */ #if defined(_WIN32) && !defined(HAVE_ENDIAN_H) #include #define betoh16(x) ntohs((x)) #define htobe16(x) htons((x)) #define betoh32(x) ntohl((x)) #define htobe32(x) ntohl((x)) #define betoh64(x) ntohll((x)) #define htobe64(x) ntohll((x)) #define be16toh betoh16 #define be32toh betoh32 #define be64toh betoh64 #endif /* _WIN32 && !HAVE_ENDIAN_H */ #ifdef __linux__ #if !defined(betoh16) #define betoh16 be16toh #endif #if !defined(betoh32) #define betoh32 be32toh #endif #if !defined(betoh64) #define betoh64 be64toh #endif #endif /* __linux__ */ #if defined(__FreeBSD__) #if !defined(HAVE_ENDIAN_H) #include #endif #if !defined(betoh16) #define betoh16 be16toh #endif #if !defined(betoh32) #define betoh32 be32toh #endif #if !defined(betoh64) #define betoh64 be64toh #endif #endif #if defined(__NetBSD__) #if !defined(betoh16) #define betoh16 be16toh #endif #if !defined(betoh32) #define betoh32 be32toh #endif #if !defined(betoh64) #define betoh64 be64toh #endif #endif #endif /* IKED_COMPAT_ENDIAN_H */ openiked-7.4/compat/explicit_bzero.c000066400000000000000000000022311477554731100176470ustar00rootroot00000000000000/* OPENBSD ORIGINAL: lib/libc/string/explicit_bzero.c */ /* $OpenBSD: explicit_bzero.c,v 1.1 2014/01/22 21:06:45 tedu Exp $ */ /* * Public domain. * Written by Ted Unangst */ #include "openbsd-compat.h" #if !defined(HAVE_EXPLICIT_BZERO) && !defined(_WIN32) #include /* * explicit_bzero - don't let the compiler optimize away bzero */ #ifdef HAVE_MEMSET_S void explicit_bzero(void *p, size_t n) { if (n == 0) return; (void)memset_s(p, n, 0, n); } #else /* HAVE_MEMSET_S */ /* * Indirect bzero through a volatile pointer to hopefully avoid * dead-store optimisation eliminating the call. */ static void (* volatile ssh_bzero)(void *, size_t) = bzero; void explicit_bzero(void *p, size_t n) { if (n == 0) return; /* * clang -fsanitize=memory needs to intercept memset-like functions * to correctly detect memory initialisation. Make sure one is called * directly since our indirection trick above successfully confuses it. */ #if defined(__has_feature) # if __has_feature(memory_sanitizer) memset(p, 0, n); # endif #endif ssh_bzero(p, n); } #endif /* HAVE_MEMSET_S */ #endif /* !defined(HAVE_EXPLICIT_BZERO) && !defined(_WIN32) */ openiked-7.4/compat/explicit_bzero_win32.c000066400000000000000000000005161477554731100206750ustar00rootroot00000000000000/* * Public domain. * Win32 explicit_bzero compatibility shim. */ #include "openbsd-compat.h" #if !defined(HAVE_EXPLICIT_BZERO) && defined(_WIN32) #include #include void explicit_bzero(void *buf, size_t len) { SecureZeroMemory(buf, len); } #endif /* !defined(HAVE_EXPLICIT_BZERO) && defined(_WIN32) */ openiked-7.4/compat/ffs.c000066400000000000000000000010141477554731100154010ustar00rootroot00000000000000/* $OpenBSD: ffs.c,v 1.10 2018/01/18 08:23:44 guenther Exp $ */ /* * Public domain. * Written by Dale Rahn. */ #include "openbsd-compat.h" /* * ffs -- vax ffs instruction */ int ffs(int mask) { int bit; unsigned int r = mask; static const signed char t[16] = { -28, 1, 2, 1, 3, 1, 2, 1, 4, 1, 2, 1, 3, 1, 2, 1 }; bit = 0; if (!(r & 0xffff)) { bit += 16; r >>= 16; } if (!(r & 0xff)) { bit += 8; r >>= 8; } if (!(r & 0xf)) { bit += 4; r >>= 4; } return (bit + t[ r & 0xf ]); } openiked-7.4/compat/freezero.c000066400000000000000000000021761477554731100164560ustar00rootroot00000000000000/* * Copyright (c) 2008, 2010, 2011, 2016 Otto Moerbeek * Copyright (c) 2012 Matthew Dempsky * Copyright (c) 2008 Damien Miller * Copyright (c) 2000 Poul-Henning Kamp * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include void freezero(void *ptr, size_t sz) { /* This is legal. */ if (ptr == NULL) return; explicit_bzero(ptr, sz); free(ptr); } openiked-7.4/compat/getdtablecount.c000066400000000000000000000002231477554731100176300ustar00rootroot00000000000000/* Placed in the public domain */ #include "openbsd-compat.h" #if !defined(HAVE_GETDTABLECOUNT) int getdtablecount(void) { return (0); } #endif openiked-7.4/compat/getopt.h000066400000000000000000000050541477554731100161420ustar00rootroot00000000000000/* $OpenBSD: getopt.h,v 1.3 2013/11/22 21:32:49 millert Exp $ */ /* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #ifndef _GETOPT_H_ #define _GETOPT_H_ /* * GNU-like getopt_long() */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 struct option { /* name of long option */ const char *name; /* * one of no_argument, required_argument, and optional_argument: * whether option takes an argument */ int has_arg; /* if not NULL, set *flag to val when option found */ int *flag; /* if flag not NULL, value to set *flag to; else return value */ int val; }; int getopt_long(int, char * const *, const char *, const struct option *, int *); int getopt_long_only(int, char * const *, const char *, const struct option *, int *); #ifndef _GETOPT_DEFINED_ #define _GETOPT_DEFINED_ int getopt(int, char * const *, const char *); extern char *optarg; /* getopt(3) external variables */ extern int opterr; extern int optind; extern int optopt; extern int optreset; #endif #endif /* !_GETOPT_H_ */ openiked-7.4/compat/getopt_long.c000066400000000000000000000342411477554731100171540ustar00rootroot00000000000000/* $OpenBSD: getopt_long.c,v 1.32 2020/05/27 22:25:09 schwarze Exp $ */ /* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ /* * Copyright (c) 2002 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F39502-99-1-0512. */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Dieter Baron and Thomas Klausner. * * 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. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* OPENBSD ORIGINAL: lib/libc/stdlib/getopt_long.c */ #include "openbsd-compat.h" #if !defined(HAVE_GETOPT) #include #include #include #include #include int opterr = 1; /* if error message should be printed */ int optind = 1; /* index into parent argv vector */ int optopt = '?'; /* character checked for validity */ int optreset; /* reset getopt */ char *optarg; /* argument associated with option */ #define PRINT_ERROR ((opterr) && (*options != ':')) #define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ #define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ #define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ /* return values */ #define BADCH (int)'?' #define BADARG ((*options == ':') ? (int)':' : (int)'?') #define INORDER (int)1 #define EMSG "" static int getopt_internal(int, char * const *, const char *, const struct option *, int *, int); static int parse_long_options(char * const *, const char *, const struct option *, int *, int, int); static int gcd(int, int); static void permute_args(int, int, int, char * const *); static char *place = EMSG; /* option letter processing */ /* XXX: set optreset to 1 rather than these two */ static int nonopt_start = -1; /* first non option argument (for permute) */ static int nonopt_end = -1; /* first option after non options (for permute) */ /* Error messages */ static const char recargchar[] = "option requires an argument -- %c"; static const char recargstring[] = "option requires an argument -- %s"; static const char ambig[] = "ambiguous option -- %.*s"; static const char noarg[] = "option doesn't take an argument -- %.*s"; static const char illoptchar[] = "unknown option -- %c"; static const char illoptstring[] = "unknown option -- %s"; /* * Compute the greatest common divisor of a and b. */ static int gcd(int a, int b) { int c; c = a % b; while (c != 0) { a = b; b = c; c = a % b; } return (b); } /* * Exchange the block from nonopt_start to nonopt_end with the block * from nonopt_end to opt_end (keeping the same order of arguments * in each block). */ static void permute_args(int panonopt_start, int panonopt_end, int opt_end, char * const *nargv) { int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; char *swap; /* * compute lengths of blocks and number and size of cycles */ nnonopts = panonopt_end - panonopt_start; nopts = opt_end - panonopt_end; ncycle = gcd(nnonopts, nopts); cyclelen = (opt_end - panonopt_start) / ncycle; for (i = 0; i < ncycle; i++) { cstart = panonopt_end+i; pos = cstart; for (j = 0; j < cyclelen; j++) { if (pos >= panonopt_end) pos -= nnonopts; else pos += nopts; swap = nargv[pos]; ((char **)nargv)[pos] = nargv[cstart]; ((char **)nargv)[cstart] = swap; } } } /* * parse_long_options -- * Parse long options in argc/argv argument vector. * Returns -1 if short_too is set and the option does not match long_options. */ static int parse_long_options(char * const *nargv, const char *options, const struct option *long_options, int *idx, int short_too, int flags) { char *current_argv, *has_equal; size_t current_argv_len; int i, match, exact_match, second_partial_match; current_argv = place; match = -1; exact_match = 0; second_partial_match = 0; optind++; if ((has_equal = strchr(current_argv, '=')) != NULL) { /* argument found (--option=arg) */ current_argv_len = has_equal - current_argv; has_equal++; } else current_argv_len = strlen(current_argv); for (i = 0; long_options[i].name; i++) { /* find matching long option */ if (strncmp(current_argv, long_options[i].name, current_argv_len)) continue; if (strlen(long_options[i].name) == current_argv_len) { /* exact match */ match = i; exact_match = 1; break; } /* * If this is a known short option, don't allow * a partial match of a single character. */ if (short_too && current_argv_len == 1) continue; if (match == -1) /* first partial match */ match = i; else if ((flags & FLAG_LONGONLY) || long_options[i].has_arg != long_options[match].has_arg || long_options[i].flag != long_options[match].flag || long_options[i].val != long_options[match].val) second_partial_match = 1; } if (!exact_match && second_partial_match) { /* ambiguous abbreviation */ if (PRINT_ERROR) warnx(ambig, (int)current_argv_len, current_argv); optopt = 0; return (BADCH); } if (match != -1) { /* option found */ if (long_options[match].has_arg == no_argument && has_equal) { if (PRINT_ERROR) warnx(noarg, (int)current_argv_len, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; return (BADARG); } if (long_options[match].has_arg == required_argument || long_options[match].has_arg == optional_argument) { if (has_equal) optarg = has_equal; else if (long_options[match].has_arg == required_argument) { /* * optional argument doesn't use next nargv */ optarg = nargv[optind++]; } } if ((long_options[match].has_arg == required_argument) && (optarg == NULL)) { /* * Missing argument; leading ':' indicates no error * should be generated. */ if (PRINT_ERROR) warnx(recargstring, current_argv); /* * XXX: GNU sets optopt to val regardless of flag */ if (long_options[match].flag == NULL) optopt = long_options[match].val; else optopt = 0; --optind; return (BADARG); } } else { /* unknown option */ if (short_too) { --optind; return (-1); } if (PRINT_ERROR) warnx(illoptstring, current_argv); optopt = 0; return (BADCH); } if (idx) *idx = match; if (long_options[match].flag) { *long_options[match].flag = long_options[match].val; return (0); } else return (long_options[match].val); } /* * getopt_internal -- * Parse argc/argv argument vector. Called by user level routines. */ static int getopt_internal(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx, int flags) { char *oli; /* option letter list index */ int optchar, short_too; static int posixly_correct = -1; if (options == NULL) return (-1); /* * XXX Some GNU programs (like cvs) set optind to 0 instead of * XXX using optreset. Work around this braindamage. */ if (optind == 0) optind = optreset = 1; /* * Disable GNU extensions if POSIXLY_CORRECT is set or options * string begins with a '+'. */ if (posixly_correct == -1 || optreset) posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); if (*options == '-') flags |= FLAG_ALLARGS; else if (posixly_correct || *options == '+') flags &= ~FLAG_PERMUTE; if (*options == '+' || *options == '-') options++; optarg = NULL; if (optreset) nonopt_start = nonopt_end = -1; start: if (optreset || !*place) { /* update scanning pointer */ optreset = 0; if (optind >= nargc) { /* end of argument vector */ place = EMSG; if (nonopt_end != -1) { /* do permutation, if we have to */ permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } else if (nonopt_start != -1) { /* * If we skipped non-options, set optind * to the first of them. */ optind = nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } if (*(place = nargv[optind]) != '-' || (place[1] == '\0' && strchr(options, '-') == NULL)) { place = EMSG; /* found non-option */ if (flags & FLAG_ALLARGS) { /* * GNU extension: * return non-option as argument to option 1 */ optarg = nargv[optind++]; return (INORDER); } if (!(flags & FLAG_PERMUTE)) { /* * If no permutation wanted, stop parsing * at first non-option. */ return (-1); } /* do permutation */ if (nonopt_start == -1) nonopt_start = optind; else if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); nonopt_start = optind - (nonopt_end - nonopt_start); nonopt_end = -1; } optind++; /* process next argument */ goto start; } if (nonopt_start != -1 && nonopt_end == -1) nonopt_end = optind; /* * If we have "-" do nothing, if "--" we are done. */ if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { optind++; place = EMSG; /* * We found an option (--), so if we skipped * non-options, we have to permute. */ if (nonopt_end != -1) { permute_args(nonopt_start, nonopt_end, optind, nargv); optind -= nonopt_end - nonopt_start; } nonopt_start = nonopt_end = -1; return (-1); } } /* * Check long options if: * 1) we were passed some * 2) the arg is not just "-" * 3) either the arg starts with -- we are getopt_long_only() */ if (long_options != NULL && place != nargv[optind] && (*place == '-' || (flags & FLAG_LONGONLY))) { short_too = 0; if (*place == '-') place++; /* --foo long option */ else if (*place != ':' && strchr(options, *place) != NULL) short_too = 1; /* could be short option too */ optchar = parse_long_options(nargv, options, long_options, idx, short_too, flags); if (optchar != -1) { place = EMSG; return (optchar); } } if ((optchar = (int)*place++) == (int)':' || (oli = strchr(options, optchar)) == NULL) { if (!*place) ++optind; if (PRINT_ERROR) warnx(illoptchar, optchar); optopt = optchar; return (BADCH); } if (long_options != NULL && optchar == 'W' && oli[1] == ';') { /* -W long-option */ if (*place) /* no space */ /* NOTHING */; else if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else /* white space */ place = nargv[optind]; optchar = parse_long_options(nargv, options, long_options, idx, 0, flags); place = EMSG; return (optchar); } if (*++oli != ':') { /* doesn't take argument */ if (!*place) ++optind; } else { /* takes (optional) argument */ optarg = NULL; if (*place) /* no white space */ optarg = place; else if (oli[1] != ':') { /* arg not optional */ if (++optind >= nargc) { /* no arg */ place = EMSG; if (PRINT_ERROR) warnx(recargchar, optchar); optopt = optchar; return (BADARG); } else optarg = nargv[optind]; } place = EMSG; ++optind; } /* dump back option letter */ return (optchar); } /* * getopt -- * Parse argc/argv argument vector. */ int getopt(int nargc, char * const *nargv, const char *options) { /* * We don't pass FLAG_PERMUTE to getopt_internal() since * the BSD getopt(3) (unlike GNU) has never done this. * * Furthermore, since many privileged programs call getopt() * before dropping privileges it makes sense to keep things * as simple (and bug-free) as possible. */ return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); } #if 0 /* * getopt_long -- * Parse argc/argv argument vector. */ int getopt_long(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE)); } /* * getopt_long_only -- * Parse argc/argv argument vector. */ int getopt_long_only(int nargc, char * const *nargv, const char *options, const struct option *long_options, int *idx) { return (getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE|FLAG_LONGONLY)); } #endif #endif /* !defined(HAVE_GETOPT) */ openiked-7.4/compat/getrtable.c000066400000000000000000000003761477554731100166060ustar00rootroot00000000000000/* Placed in the public domain */ #include "openbsd-compat.h" #if !defined(HAVE_GETRTABLE) int getrtable(void) { return (0); } #endif #if !defined(HAVE_SETRTABLE) int setrtable(int rtableid) { if (rtableid == 0) return (0); return (-1); } #endif openiked-7.4/compat/imsg-buffer.c000066400000000000000000000427441477554731100170500ustar00rootroot00000000000000/* $OpenBSD: imsg-buffer.c,v 1.31 2024/11/26 13:57:31 claudio Exp $ */ /* * Copyright (c) 2023 Claudio Jeker * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include "openbsd-compat.h" #include "imsg.h" struct msgbuf { TAILQ_HEAD(, ibuf) bufs; TAILQ_HEAD(, ibuf) rbufs; uint32_t queued; char *rbuf; struct ibuf *rpmsg; struct ibuf *(*readhdr)(struct ibuf *, void *, int *); void *rarg; size_t roff; size_t hdrsize; }; static void msgbuf_read_enqueue(struct msgbuf *, struct ibuf *); static void msgbuf_enqueue(struct msgbuf *, struct ibuf *); static void msgbuf_dequeue(struct msgbuf *, struct ibuf *); static void msgbuf_drain(struct msgbuf *, size_t); #define IBUF_FD_MARK_ON_STACK -2 struct ibuf * ibuf_open(size_t len) { struct ibuf *buf; if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) return (NULL); if (len > 0) { if ((buf->buf = calloc(len, 1)) == NULL) { free(buf); return (NULL); } } buf->size = buf->max = len; buf->fd = -1; return (buf); } struct ibuf * ibuf_dynamic(size_t len, size_t max) { struct ibuf *buf; if (max == 0 || max < len) { errno = EINVAL; return (NULL); } if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) return (NULL); if (len > 0) { if ((buf->buf = calloc(len, 1)) == NULL) { free(buf); return (NULL); } } buf->size = len; buf->max = max; buf->fd = -1; return (buf); } void * ibuf_reserve(struct ibuf *buf, size_t len) { void *b; if (len > SIZE_MAX - buf->wpos) { errno = ERANGE; return (NULL); } if (buf->fd == IBUF_FD_MARK_ON_STACK) { /* can not grow stack buffers */ errno = EINVAL; return (NULL); } if (buf->wpos + len > buf->size) { unsigned char *nb; /* check if buffer is allowed to grow */ if (buf->wpos + len > buf->max) { errno = ERANGE; return (NULL); } nb = realloc(buf->buf, buf->wpos + len); if (nb == NULL) return (NULL); memset(nb + buf->size, 0, buf->wpos + len - buf->size); buf->buf = nb; buf->size = buf->wpos + len; } b = buf->buf + buf->wpos; buf->wpos += len; return (b); } int ibuf_add(struct ibuf *buf, const void *data, size_t len) { void *b; if ((b = ibuf_reserve(buf, len)) == NULL) return (-1); memcpy(b, data, len); return (0); } int ibuf_add_ibuf(struct ibuf *buf, const struct ibuf *from) { return ibuf_add(buf, ibuf_data(from), ibuf_size(from)); } int ibuf_add_n8(struct ibuf *buf, uint64_t value) { uint8_t v; if (value > UINT8_MAX) { errno = EINVAL; return (-1); } v = value; return ibuf_add(buf, &v, sizeof(v)); } int ibuf_add_n16(struct ibuf *buf, uint64_t value) { uint16_t v; if (value > UINT16_MAX) { errno = EINVAL; return (-1); } v = htobe16(value); return ibuf_add(buf, &v, sizeof(v)); } int ibuf_add_n32(struct ibuf *buf, uint64_t value) { uint32_t v; if (value > UINT32_MAX) { errno = EINVAL; return (-1); } v = htobe32(value); return ibuf_add(buf, &v, sizeof(v)); } int ibuf_add_n64(struct ibuf *buf, uint64_t value) { value = htobe64(value); return ibuf_add(buf, &value, sizeof(value)); } int ibuf_add_h16(struct ibuf *buf, uint64_t value) { uint16_t v; if (value > UINT16_MAX) { errno = EINVAL; return (-1); } v = value; return ibuf_add(buf, &v, sizeof(v)); } int ibuf_add_h32(struct ibuf *buf, uint64_t value) { uint32_t v; if (value > UINT32_MAX) { errno = EINVAL; return (-1); } v = value; return ibuf_add(buf, &v, sizeof(v)); } int ibuf_add_h64(struct ibuf *buf, uint64_t value) { return ibuf_add(buf, &value, sizeof(value)); } int ibuf_add_zero(struct ibuf *buf, size_t len) { void *b; if ((b = ibuf_reserve(buf, len)) == NULL) return (-1); memset(b, 0, len); return (0); } void * ibuf_seek(struct ibuf *buf, size_t pos, size_t len) { /* only allow seeking between rpos and wpos */ if (ibuf_size(buf) < pos || SIZE_MAX - pos < len || ibuf_size(buf) < pos + len) { errno = ERANGE; return (NULL); } return (buf->buf + buf->rpos + pos); } int ibuf_set(struct ibuf *buf, size_t pos, const void *data, size_t len) { void *b; if ((b = ibuf_seek(buf, pos, len)) == NULL) return (-1); memcpy(b, data, len); return (0); } int ibuf_set_n8(struct ibuf *buf, size_t pos, uint64_t value) { uint8_t v; if (value > UINT8_MAX) { errno = EINVAL; return (-1); } v = value; return (ibuf_set(buf, pos, &v, sizeof(v))); } int ibuf_set_n16(struct ibuf *buf, size_t pos, uint64_t value) { uint16_t v; if (value > UINT16_MAX) { errno = EINVAL; return (-1); } v = htobe16(value); return (ibuf_set(buf, pos, &v, sizeof(v))); } int ibuf_set_n32(struct ibuf *buf, size_t pos, uint64_t value) { uint32_t v; if (value > UINT32_MAX) { errno = EINVAL; return (-1); } v = htobe32(value); return (ibuf_set(buf, pos, &v, sizeof(v))); } int ibuf_set_n64(struct ibuf *buf, size_t pos, uint64_t value) { value = htobe64(value); return (ibuf_set(buf, pos, &value, sizeof(value))); } int ibuf_set_h16(struct ibuf *buf, size_t pos, uint64_t value) { uint16_t v; if (value > UINT16_MAX) { errno = EINVAL; return (-1); } v = value; return (ibuf_set(buf, pos, &v, sizeof(v))); } int ibuf_set_h32(struct ibuf *buf, size_t pos, uint64_t value) { uint32_t v; if (value > UINT32_MAX) { errno = EINVAL; return (-1); } v = value; return (ibuf_set(buf, pos, &v, sizeof(v))); } int ibuf_set_h64(struct ibuf *buf, size_t pos, uint64_t value) { return (ibuf_set(buf, pos, &value, sizeof(value))); } void * ibuf_data(const struct ibuf *buf) { return (buf->buf + buf->rpos); } size_t ibuf_size(const struct ibuf *buf) { return (buf->wpos - buf->rpos); } size_t ibuf_left(const struct ibuf *buf) { /* on stack buffers have no space left */ if (buf->fd == IBUF_FD_MARK_ON_STACK) return (0); return (buf->max - buf->wpos); } int ibuf_truncate(struct ibuf *buf, size_t len) { if (ibuf_size(buf) >= len) { buf->wpos = buf->rpos + len; return (0); } if (buf->fd == IBUF_FD_MARK_ON_STACK) { /* only allow to truncate down for stack buffers */ errno = ERANGE; return (-1); } return ibuf_add_zero(buf, len - ibuf_size(buf)); } void ibuf_rewind(struct ibuf *buf) { buf->rpos = 0; } void ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf) { msgbuf_enqueue(msgbuf, buf); } void ibuf_from_buffer(struct ibuf *buf, void *data, size_t len) { memset(buf, 0, sizeof(*buf)); buf->buf = data; buf->size = buf->wpos = len; buf->fd = IBUF_FD_MARK_ON_STACK; } void ibuf_from_ibuf(struct ibuf *buf, const struct ibuf *from) { ibuf_from_buffer(buf, ibuf_data(from), ibuf_size(from)); } int ibuf_get(struct ibuf *buf, void *data, size_t len) { if (ibuf_size(buf) < len) { errno = EBADMSG; return (-1); } memcpy(data, ibuf_data(buf), len); buf->rpos += len; return (0); } int ibuf_get_ibuf(struct ibuf *buf, size_t len, struct ibuf *new) { if (ibuf_size(buf) < len) { errno = EBADMSG; return (-1); } ibuf_from_buffer(new, ibuf_data(buf), len); buf->rpos += len; return (0); } int ibuf_get_h16(struct ibuf *buf, uint16_t *value) { return ibuf_get(buf, value, sizeof(*value)); } int ibuf_get_h32(struct ibuf *buf, uint32_t *value) { return ibuf_get(buf, value, sizeof(*value)); } int ibuf_get_h64(struct ibuf *buf, uint64_t *value) { return ibuf_get(buf, value, sizeof(*value)); } int ibuf_get_n8(struct ibuf *buf, uint8_t *value) { return ibuf_get(buf, value, sizeof(*value)); } int ibuf_get_n16(struct ibuf *buf, uint16_t *value) { int rv; rv = ibuf_get(buf, value, sizeof(*value)); *value = be16toh(*value); return (rv); } int ibuf_get_n32(struct ibuf *buf, uint32_t *value) { int rv; rv = ibuf_get(buf, value, sizeof(*value)); *value = be32toh(*value); return (rv); } int ibuf_get_n64(struct ibuf *buf, uint64_t *value) { int rv; rv = ibuf_get(buf, value, sizeof(*value)); *value = be64toh(*value); return (rv); } char * ibuf_get_string(struct ibuf *buf, size_t len) { char *str; if (ibuf_size(buf) < len) { errno = EBADMSG; return (NULL); } str = strndup(ibuf_data(buf), len); if (str == NULL) return (NULL); buf->rpos += len; return (str); } int ibuf_skip(struct ibuf *buf, size_t len) { if (ibuf_size(buf) < len) { errno = EBADMSG; return (-1); } buf->rpos += len; return (0); } void ibuf_free(struct ibuf *buf) { if (buf == NULL) return; /* if buf lives on the stack abort before causing more harm */ if (buf->fd == IBUF_FD_MARK_ON_STACK) abort(); if (buf->fd >= 0) close(buf->fd); freezero(buf->buf, buf->size); free(buf); } int ibuf_fd_avail(struct ibuf *buf) { return (buf->fd >= 0); } int ibuf_fd_get(struct ibuf *buf) { int fd; /* negative fds are internal use and equivalent to -1 */ if (buf->fd < 0) return (-1); fd = buf->fd; buf->fd = -1; return (fd); } void ibuf_fd_set(struct ibuf *buf, int fd) { /* if buf lives on the stack abort before causing more harm */ if (buf->fd == IBUF_FD_MARK_ON_STACK) abort(); if (buf->fd >= 0) close(buf->fd); buf->fd = -1; if (fd >= 0) buf->fd = fd; } struct msgbuf * msgbuf_new(void) { struct msgbuf *msgbuf; if ((msgbuf = calloc(1, sizeof(*msgbuf))) == NULL) return (NULL); msgbuf->queued = 0; TAILQ_INIT(&msgbuf->bufs); TAILQ_INIT(&msgbuf->rbufs); return msgbuf; } struct msgbuf * msgbuf_new_reader(size_t hdrsz, struct ibuf *(*readhdr)(struct ibuf *, void *, int *), void *arg) { struct msgbuf *msgbuf; char *buf; if (hdrsz == 0 || hdrsz > IBUF_READ_SIZE / 2) { errno = EINVAL; return (NULL); } if ((buf = malloc(IBUF_READ_SIZE)) == NULL) return (NULL); msgbuf = msgbuf_new(); if (msgbuf == NULL) { free(buf); return (NULL); } msgbuf->rbuf = buf; msgbuf->hdrsize = hdrsz; msgbuf->readhdr = readhdr; msgbuf->rarg = arg; return (msgbuf); } void msgbuf_free(struct msgbuf *msgbuf) { if (msgbuf == NULL) return; msgbuf_clear(msgbuf); free(msgbuf->rbuf); free(msgbuf); } uint32_t msgbuf_queuelen(struct msgbuf *msgbuf) { return (msgbuf->queued); } void msgbuf_clear(struct msgbuf *msgbuf) { struct ibuf *buf; /* write side */ while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) msgbuf_dequeue(msgbuf, buf); msgbuf->queued = 0; /* read side */ while ((buf = TAILQ_FIRST(&msgbuf->rbufs)) != NULL) { TAILQ_REMOVE(&msgbuf->rbufs, buf, entry); ibuf_free(buf); } msgbuf->roff = 0; ibuf_free(msgbuf->rpmsg); msgbuf->rpmsg = NULL; } struct ibuf * msgbuf_get(struct msgbuf *msgbuf) { struct ibuf *buf; if ((buf = TAILQ_FIRST(&msgbuf->rbufs)) != NULL) TAILQ_REMOVE(&msgbuf->rbufs, buf, entry); return buf; } int ibuf_write(int fd, struct msgbuf *msgbuf) { struct iovec iov[IOV_MAX]; struct ibuf *buf; unsigned int i = 0; ssize_t n; memset(&iov, 0, sizeof(iov)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; iov[i].iov_base = ibuf_data(buf); iov[i].iov_len = ibuf_size(buf); i++; } if (i == 0) return (0); /* nothing queued */ again: if ((n = writev(fd, iov, i)) == -1) { if (errno == EINTR) goto again; if (errno == EAGAIN || errno == ENOBUFS) /* lets retry later again */ return (0); return (-1); } msgbuf_drain(msgbuf, n); return (0); } int msgbuf_write(int fd, struct msgbuf *msgbuf) { struct iovec iov[IOV_MAX]; struct ibuf *buf, *buf0 = NULL; unsigned int i = 0; ssize_t n; struct msghdr msg; struct cmsghdr *cmsg; union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(int))]; } cmsgbuf; memset(&iov, 0, sizeof(iov)); memset(&msg, 0, sizeof(msg)); memset(&cmsgbuf, 0, sizeof(cmsgbuf)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; if (i > 0 && buf->fd != -1) break; iov[i].iov_base = ibuf_data(buf); iov[i].iov_len = ibuf_size(buf); i++; if (buf->fd != -1) buf0 = buf; } if (i == 0) return (0); /* nothing queued */ msg.msg_iov = iov; msg.msg_iovlen = i; if (buf0 != NULL) { msg.msg_control = (caddr_t)&cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; *(int *)CMSG_DATA(cmsg) = buf0->fd; } again: if ((n = sendmsg(fd, &msg, 0)) == -1) { if (errno == EINTR) goto again; if (errno == EAGAIN || errno == ENOBUFS) /* lets retry later again */ return (0); return (-1); } /* * assumption: fd got sent if sendmsg sent anything * this works because fds are passed one at a time */ if (buf0 != NULL) { close(buf0->fd); buf0->fd = -1; } msgbuf_drain(msgbuf, n); return (0); } static int ibuf_read_process(struct msgbuf *msgbuf, int fd) { struct ibuf rbuf, msg; ssize_t sz; ibuf_from_buffer(&rbuf, msgbuf->rbuf, msgbuf->roff); do { if (msgbuf->rpmsg == NULL) { if (ibuf_size(&rbuf) < msgbuf->hdrsize) break; /* get size from header */ ibuf_from_buffer(&msg, ibuf_data(&rbuf), msgbuf->hdrsize); if ((msgbuf->rpmsg = msgbuf->readhdr(&msg, msgbuf->rarg, &fd)) == NULL) goto fail; } if (ibuf_left(msgbuf->rpmsg) <= ibuf_size(&rbuf)) sz = ibuf_left(msgbuf->rpmsg); else sz = ibuf_size(&rbuf); /* neither call below can fail */ if (ibuf_get_ibuf(&rbuf, sz, &msg) == -1 || ibuf_add_ibuf(msgbuf->rpmsg, &msg) == -1) goto fail; if (ibuf_left(msgbuf->rpmsg) == 0) { msgbuf_read_enqueue(msgbuf, msgbuf->rpmsg); msgbuf->rpmsg = NULL; } } while (ibuf_size(&rbuf) > 0); if (ibuf_size(&rbuf) > 0) memmove(msgbuf->rbuf, ibuf_data(&rbuf), ibuf_size(&rbuf)); msgbuf->roff = ibuf_size(&rbuf); if (fd != -1) close(fd); return (1); fail: /* XXX how to properly clean up is unclear */ if (fd != -1) close(fd); return (-1); } int ibuf_read(int fd, struct msgbuf *msgbuf) { struct iovec iov; ssize_t n; if (msgbuf->rbuf == NULL) { errno = EINVAL; return (-1); } iov.iov_base = msgbuf->rbuf + msgbuf->roff; iov.iov_len = IBUF_READ_SIZE - msgbuf->roff; again: if ((n = readv(fd, &iov, 1)) == -1) { if (errno == EINTR) goto again; if (errno == EAGAIN) /* lets retry later again */ return (1); return (-1); } if (n == 0) /* connection closed */ return (0); msgbuf->roff += n; /* new data arrived, try to process it */ return (ibuf_read_process(msgbuf, -1)); } int msgbuf_read(int fd, struct msgbuf *msgbuf) { struct msghdr msg; struct cmsghdr *cmsg; union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(int) * 1)]; } cmsgbuf; struct iovec iov; ssize_t n; int fdpass = -1; if (msgbuf->rbuf == NULL) { errno = EINVAL; return (-1); } memset(&msg, 0, sizeof(msg)); memset(&cmsgbuf, 0, sizeof(cmsgbuf)); iov.iov_base = msgbuf->rbuf + msgbuf->roff; iov.iov_len = IBUF_READ_SIZE - msgbuf->roff; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = &cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); again: if ((n = recvmsg(fd, &msg, 0)) == -1) { if (errno == EINTR) goto again; if (errno == EMSGSIZE) /* * Not enough fd slots: fd passing failed, retry * to receive the message without fd. * imsg_get_fd() will return -1 in that case. */ goto again; if (errno == EAGAIN) /* lets retry later again */ return (1); return (-1); } if (n == 0) /* connection closed */ return (0); msgbuf->roff += n; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { int i, j, f; /* * We only accept one file descriptor. Due to C * padding rules, our control buffer might contain * more than one fd, and we must close them. */ j = ((char *)cmsg + cmsg->cmsg_len - (char *)CMSG_DATA(cmsg)) / sizeof(int); for (i = 0; i < j; i++) { f = ((int *)CMSG_DATA(cmsg))[i]; if (i == 0) fdpass = f; else close(f); } } /* we do not handle other ctl data level */ } /* new data arrived, try to process it */ return (ibuf_read_process(msgbuf, fdpass)); } static void msgbuf_read_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) { /* if buf lives on the stack abort before causing more harm */ if (buf->fd == IBUF_FD_MARK_ON_STACK) abort(); TAILQ_INSERT_TAIL(&msgbuf->rbufs, buf, entry); } static void msgbuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) { /* if buf lives on the stack abort before causing more harm */ if (buf->fd == IBUF_FD_MARK_ON_STACK) abort(); TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); msgbuf->queued++; } static void msgbuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) { TAILQ_REMOVE(&msgbuf->bufs, buf, entry); msgbuf->queued--; ibuf_free(buf); } static void msgbuf_drain(struct msgbuf *msgbuf, size_t n) { struct ibuf *buf, *next; for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; buf = next) { next = TAILQ_NEXT(buf, entry); if (n >= ibuf_size(buf)) { n -= ibuf_size(buf); msgbuf_dequeue(msgbuf, buf); } else { buf->rpos += n; n = 0; } } } openiked-7.4/compat/imsg.c000066400000000000000000000165261477554731100156000ustar00rootroot00000000000000/* $OpenBSD: imsg.c,v 1.38 2024/11/29 04:35:13 tb Exp $ */ /* * Copyright (c) 2023 Claudio Jeker * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "openbsd-compat.h" #include "imsg.h" #define IMSG_ALLOW_FDPASS 0x01 #define IMSG_FD_MARK 0x80000000U static struct ibuf *imsg_parse_hdr(struct ibuf *, void *, int *); int imsgbuf_init(struct imsgbuf *imsgbuf, int fd) { imsgbuf->w = msgbuf_new_reader(IMSG_HEADER_SIZE, imsg_parse_hdr, imsgbuf); if (imsgbuf->w == NULL) return (-1); imsgbuf->pid = getpid(); imsgbuf->maxsize = MAX_IMSGSIZE; imsgbuf->fd = fd; imsgbuf->flags = 0; return (0); } void imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf) { imsgbuf->flags |= IMSG_ALLOW_FDPASS; } int imsgbuf_set_maxsize(struct imsgbuf *imsgbuf, uint32_t maxsize) { if (maxsize < IMSG_HEADER_SIZE || maxsize & IMSG_FD_MARK) { errno = EINVAL; return (-1); } imsgbuf->maxsize = maxsize; return (0); } int imsgbuf_read(struct imsgbuf *imsgbuf) { if (imsgbuf->flags & IMSG_ALLOW_FDPASS) return msgbuf_read(imsgbuf->fd, imsgbuf->w); else return ibuf_read(imsgbuf->fd, imsgbuf->w); } int imsgbuf_write(struct imsgbuf *imsgbuf) { if (imsgbuf->flags & IMSG_ALLOW_FDPASS) return msgbuf_write(imsgbuf->fd, imsgbuf->w); else return ibuf_write(imsgbuf->fd, imsgbuf->w); } int imsgbuf_flush(struct imsgbuf *imsgbuf) { while (imsgbuf_queuelen(imsgbuf) > 0) { if (imsgbuf_write(imsgbuf) == -1) return (-1); } return (0); } void imsgbuf_clear(struct imsgbuf *imsgbuf) { msgbuf_free(imsgbuf->w); imsgbuf->w = NULL; } uint32_t imsgbuf_queuelen(struct imsgbuf *imsgbuf) { return msgbuf_queuelen(imsgbuf->w); } ssize_t imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg) { struct imsg m; struct ibuf *buf; if ((buf = msgbuf_get(imsgbuf->w)) == NULL) return (0); if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1) return (-1); if (ibuf_size(buf)) m.data = ibuf_data(buf); else m.data = NULL; m.buf = buf; m.hdr.len &= ~IMSG_FD_MARK; *imsg = m; return (ibuf_size(buf) + IMSG_HEADER_SIZE); } int imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf) { if (ibuf_size(imsg->buf) == 0) { errno = EBADMSG; return (-1); } return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf); } int imsg_get_data(struct imsg *imsg, void *data, size_t len) { if (len == 0) { errno = EINVAL; return (-1); } if (ibuf_size(imsg->buf) != len) { errno = EBADMSG; return (-1); } return ibuf_get(imsg->buf, data, len); } int imsg_get_fd(struct imsg *imsg) { return ibuf_fd_get(imsg->buf); } uint32_t imsg_get_id(struct imsg *imsg) { return (imsg->hdr.peerid); } size_t imsg_get_len(struct imsg *imsg) { return ibuf_size(imsg->buf); } pid_t imsg_get_pid(struct imsg *imsg) { return (imsg->hdr.pid); } uint32_t imsg_get_type(struct imsg *imsg) { return (imsg->hdr.type); } int imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, int fd, const void *data, size_t datalen) { struct ibuf *wbuf; if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL) return (-1); if (imsg_add(wbuf, data, datalen) == -1) return (-1); ibuf_fd_set(wbuf, fd); imsg_close(imsgbuf, wbuf); return (1); } int imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, int fd, const struct iovec *iov, int iovcnt) { struct ibuf *wbuf; int i; size_t datalen = 0; for (i = 0; i < iovcnt; i++) datalen += iov[i].iov_len; if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL) return (-1); for (i = 0; i < iovcnt; i++) if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) return (-1); ibuf_fd_set(wbuf, fd); imsg_close(imsgbuf, wbuf); return (1); } /* * Enqueue imsg with payload from ibuf buf. fd passing is not possible * with this function. */ int imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, struct ibuf *buf) { struct ibuf *hdrbuf = NULL; struct imsg_hdr hdr; int save_errno; if (ibuf_size(buf) + IMSG_HEADER_SIZE > imsgbuf->maxsize) { errno = ERANGE; goto fail; } hdr.type = type; hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE; hdr.peerid = id; if ((hdr.pid = pid) == 0) hdr.pid = imsgbuf->pid; if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL) goto fail; if (imsg_add(hdrbuf, &hdr, sizeof(hdr)) == -1) goto fail; ibuf_close(imsgbuf->w, hdrbuf); ibuf_close(imsgbuf->w, buf); return (1); fail: save_errno = errno; ibuf_free(buf); ibuf_free(hdrbuf); errno = save_errno; return (-1); } /* * Forward imsg to another channel. Any attached fd is closed. */ int imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg) { struct ibuf *wbuf; size_t len; ibuf_rewind(msg->buf); ibuf_skip(msg->buf, sizeof(msg->hdr)); len = ibuf_size(msg->buf); if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid, msg->hdr.pid, len)) == NULL) return (-1); if (len != 0) { if (ibuf_add_ibuf(wbuf, msg->buf) == -1) { ibuf_free(wbuf); return (-1); } } imsg_close(imsgbuf, wbuf); return (1); } struct ibuf * imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, size_t datalen) { struct ibuf *wbuf; struct imsg_hdr hdr; datalen += IMSG_HEADER_SIZE; if (datalen > imsgbuf->maxsize) { errno = ERANGE; return (NULL); } hdr.type = type; hdr.peerid = id; if ((hdr.pid = pid) == 0) hdr.pid = imsgbuf->pid; if ((wbuf = ibuf_dynamic(datalen, imsgbuf->maxsize)) == NULL) { return (NULL); } if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) return (NULL); return (wbuf); } int imsg_add(struct ibuf *msg, const void *data, size_t datalen) { if (datalen) if (ibuf_add(msg, data, datalen) == -1) { ibuf_free(msg); return (-1); } return (datalen); } void imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg) { uint32_t len; len = ibuf_size(msg); if (ibuf_fd_avail(msg)) len |= IMSG_FD_MARK; (void)ibuf_set_h32(msg, offsetof(struct imsg_hdr, len), len); ibuf_close(imsgbuf->w, msg); } void imsg_free(struct imsg *imsg) { ibuf_free(imsg->buf); } static struct ibuf * imsg_parse_hdr(struct ibuf *buf, void *arg, int *fd) { struct imsgbuf *imsgbuf = arg; struct imsg_hdr hdr; struct ibuf *b; uint32_t len; if (ibuf_get(buf, &hdr, sizeof(hdr)) == -1) return (NULL); len = hdr.len & ~IMSG_FD_MARK; if (len < IMSG_HEADER_SIZE || len > imsgbuf->maxsize) { errno = ERANGE; return (NULL); } if ((b = ibuf_open(len)) == NULL) return (NULL); if (hdr.len & IMSG_FD_MARK) { ibuf_fd_set(b, *fd); *fd = -1; } return b; } openiked-7.4/compat/imsg.h000066400000000000000000000126001477554731100155720ustar00rootroot00000000000000/* $OpenBSD: imsg.h,v 1.19 2024/11/26 13:57:31 claudio Exp $ */ /* * Copyright (c) 2023 Claudio Jeker * Copyright (c) 2006, 2007 Pierre-Yves Ritschard * Copyright (c) 2006, 2007, 2008 Reyk Floeter * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _IMSG_H_ #define _IMSG_H_ #include #define IBUF_READ_SIZE 65535 #define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) #define MAX_IMSGSIZE 16384 struct ibuf { TAILQ_ENTRY(ibuf) entry; unsigned char *buf; size_t size; size_t max; size_t wpos; size_t rpos; int fd; }; struct msgbuf; struct imsgbuf { struct msgbuf *w; pid_t pid; uint32_t maxsize; int fd; int flags; }; struct imsg_hdr { uint32_t type; uint32_t len; uint32_t peerid; uint32_t pid; }; struct imsg { struct imsg_hdr hdr; void *data; struct ibuf *buf; }; struct iovec; /* imsg-buffer.c */ struct ibuf *ibuf_open(size_t); struct ibuf *ibuf_dynamic(size_t, size_t); int ibuf_add(struct ibuf *, const void *, size_t); int ibuf_add_ibuf(struct ibuf *, const struct ibuf *); int ibuf_add_zero(struct ibuf *, size_t); int ibuf_add_n8(struct ibuf *, uint64_t); int ibuf_add_n16(struct ibuf *, uint64_t); int ibuf_add_n32(struct ibuf *, uint64_t); int ibuf_add_n64(struct ibuf *, uint64_t); int ibuf_add_h16(struct ibuf *, uint64_t); int ibuf_add_h32(struct ibuf *, uint64_t); int ibuf_add_h64(struct ibuf *, uint64_t); void *ibuf_reserve(struct ibuf *, size_t); void *ibuf_seek(struct ibuf *, size_t, size_t); int ibuf_set(struct ibuf *, size_t, const void *, size_t); int ibuf_set_n8(struct ibuf *, size_t, uint64_t); int ibuf_set_n16(struct ibuf *, size_t, uint64_t); int ibuf_set_n32(struct ibuf *, size_t, uint64_t); int ibuf_set_n64(struct ibuf *, size_t, uint64_t); int ibuf_set_h16(struct ibuf *, size_t, uint64_t); int ibuf_set_h32(struct ibuf *, size_t, uint64_t); int ibuf_set_h64(struct ibuf *, size_t, uint64_t); void *ibuf_data(const struct ibuf *); size_t ibuf_size(const struct ibuf *); size_t ibuf_left(const struct ibuf *); int ibuf_truncate(struct ibuf *, size_t); void ibuf_rewind(struct ibuf *); void ibuf_close(struct msgbuf *, struct ibuf *); void ibuf_from_buffer(struct ibuf *, void *, size_t); void ibuf_from_ibuf(struct ibuf *, const struct ibuf *); int ibuf_get(struct ibuf *, void *, size_t); int ibuf_get_ibuf(struct ibuf *, size_t, struct ibuf *); int ibuf_get_n8(struct ibuf *, uint8_t *); int ibuf_get_n16(struct ibuf *, uint16_t *); int ibuf_get_n32(struct ibuf *, uint32_t *); int ibuf_get_n64(struct ibuf *, uint64_t *); int ibuf_get_h16(struct ibuf *, uint16_t *); int ibuf_get_h32(struct ibuf *, uint32_t *); int ibuf_get_h64(struct ibuf *, uint64_t *); char *ibuf_get_string(struct ibuf *, size_t); int ibuf_skip(struct ibuf *, size_t); void ibuf_free(struct ibuf *); int ibuf_fd_avail(struct ibuf *); int ibuf_fd_get(struct ibuf *); void ibuf_fd_set(struct ibuf *, int); struct msgbuf *msgbuf_new(void); struct msgbuf *msgbuf_new_reader(size_t, struct ibuf *(*)(struct ibuf *, void *, int *), void *); void msgbuf_free(struct msgbuf *); void msgbuf_clear(struct msgbuf *); uint32_t msgbuf_queuelen(struct msgbuf *); int ibuf_write(int, struct msgbuf *); int msgbuf_write(int, struct msgbuf *); int ibuf_read(int, struct msgbuf *); int msgbuf_read(int, struct msgbuf *); struct ibuf *msgbuf_get(struct msgbuf *); /* imsg.c */ int imsgbuf_init(struct imsgbuf *, int); void imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf); int imsgbuf_set_maxsize(struct imsgbuf *, uint32_t); int imsgbuf_read(struct imsgbuf *); int imsgbuf_write(struct imsgbuf *); int imsgbuf_flush(struct imsgbuf *); void imsgbuf_clear(struct imsgbuf *); uint32_t imsgbuf_queuelen(struct imsgbuf *); ssize_t imsg_get(struct imsgbuf *, struct imsg *); int imsg_get_ibuf(struct imsg *, struct ibuf *); int imsg_get_data(struct imsg *, void *, size_t); int imsg_get_fd(struct imsg *); uint32_t imsg_get_id(struct imsg *); size_t imsg_get_len(struct imsg *); pid_t imsg_get_pid(struct imsg *); uint32_t imsg_get_type(struct imsg *); int imsg_forward(struct imsgbuf *, struct imsg *); int imsg_compose(struct imsgbuf *, uint32_t, uint32_t, pid_t, int, const void *, size_t); int imsg_composev(struct imsgbuf *, uint32_t, uint32_t, pid_t, int, const struct iovec *, int); int imsg_compose_ibuf(struct imsgbuf *, uint32_t, uint32_t, pid_t, struct ibuf *); struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, size_t); int imsg_add(struct ibuf *, const void *, size_t); void imsg_close(struct imsgbuf *, struct ibuf *); void imsg_free(struct imsg *); #endif openiked-7.4/compat/net/000077500000000000000000000000001477554731100152515ustar00rootroot00000000000000openiked-7.4/compat/net/ethertypes.h000066400000000000000000000430071477554731100176220ustar00rootroot00000000000000/* $OpenBSD: ethertypes.h,v 1.18 2023/04/13 23:02:12 dlg Exp $ */ /* $NetBSD: ethertypes.h,v 1.13 2002/02/10 01:28:32 thorpej Exp $ */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. * * @(#)if_ether.h 8.1 (Berkeley) 6/10/93 */ /* * Ethernet protocol types. * * According to "assigned numbers", the Ethernet protocol numbers are also * used as ARP protocol type numbers. * * I factor them out here to avoid pulling all the Ethernet header file * into the hardware independent ARP code. -is * * Additional sources of information: * http://standards.ieee.org/regauth/ethertype/eth.txt * http://www.mit.edu/~map/Ethernet/Ethernet.txt * */ #ifndef _NET_ETHERTYPES_H_ #define _NET_ETHERTYPES_H_ /* * NOTE: 0x0000-0x05DC (0..1500) are generally IEEE 802.3 length fields. * However, there are some conflicts. */ #define ETHERTYPE_8023 0x0004 /* IEEE 802.3 packet */ /* 0x0101 .. 0x1FF Experimental */ #define ETHERTYPE_PUP 0x0200 /* Xerox PUP protocol - see 0A00 */ #define ETHERTYPE_PUPAT 0x0200 /* PUP Address Translation - see 0A01 */ #define ETHERTYPE_SPRITE 0x0500 /* ??? */ /* 0x0400 Nixdorf */ #define ETHERTYPE_NS 0x0600 /* XNS */ #define ETHERTYPE_NSAT 0x0601 /* XNS Address Translation (3Mb only) */ #define ETHERTYPE_DLOG1 0x0660 /* DLOG (?) */ #define ETHERTYPE_DLOG2 0x0661 /* DLOG (?) */ #define ETHERTYPE_IP 0x0800 /* IP protocol */ #define ETHERTYPE_X75 0x0801 /* X.75 Internet */ #define ETHERTYPE_NBS 0x0802 /* NBS Internet */ #define ETHERTYPE_ECMA 0x0803 /* ECMA Internet */ #define ETHERTYPE_CHAOS 0x0804 /* CHAOSnet */ #define ETHERTYPE_X25 0x0805 /* X.25 Level 3 */ #define ETHERTYPE_ARP 0x0806 /* Address resolution protocol */ #define ETHERTYPE_NSCOMPAT 0x0807 /* XNS Compatibility */ #define ETHERTYPE_FRARP 0x0808 /* Frame Relay ARP (RFC1701) */ /* 0x081C Symbolics Private */ /* 0x0888 - 0x088A Xyplex */ #define ETHERTYPE_UBDEBUG 0x0900 /* Ungermann-Bass network debugger */ #define ETHERTYPE_IEEEPUP 0x0A00 /* Xerox IEEE802.3 PUP */ #define ETHERTYPE_IEEEPUPAT 0x0A01 /* Xerox IEEE802.3 PUP Address Translation */ #define ETHERTYPE_VINES 0x0BAD /* Banyan VINES */ #define ETHERTYPE_VINESLOOP 0x0BAE /* Banyan VINES Loopback */ #define ETHERTYPE_VINESECHO 0x0BAF /* Banyan VINES Echo */ /* 0x1000 - 0x100F Berkeley Trailer */ /* * The ETHERTYPE_NTRAILER packet types starting at ETHERTYPE_TRAIL have * (type-ETHERTYPE_TRAIL)*512 bytes of data followed * by an ETHER type (as given above) and then the (variable-length) header. */ #define ETHERTYPE_TRAIL 0x1000 /* Trailer packet */ #define ETHERTYPE_NTRAILER 16 #define ETHERTYPE_DCA 0x1234 /* DCA - Multicast */ #define ETHERTYPE_VALID 0x1600 /* VALID system protocol */ #define ETHERTYPE_DOGFIGHT 0x1989 /* Artificial Horizons ("Aviator" dogfight simulator [on Sun]) */ #define ETHERTYPE_RCL 0x1995 /* Datapoint Corporation (RCL lan protocol) */ #define ETHERTYPE_NHRP 0x2001 /* NBMA Next Hop Resolution Protocol (RFC2332) */ /* The following 3C0x types are unregistered: */ #define ETHERTYPE_NBPVCD 0x3C00 /* 3Com NBP virtual circuit datagram (like XNS SPP) not registered */ #define ETHERTYPE_NBPSCD 0x3C01 /* 3Com NBP System control datagram not registered */ #define ETHERTYPE_NBPCREQ 0x3C02 /* 3Com NBP Connect request (virtual cct) not registered */ #define ETHERTYPE_NBPCRSP 0x3C03 /* 3Com NBP Connect response not registered */ #define ETHERTYPE_NBPCC 0x3C04 /* 3Com NBP Connect complete not registered */ #define ETHERTYPE_NBPCLREQ 0x3C05 /* 3Com NBP Close request (virtual cct) not registered */ #define ETHERTYPE_NBPCLRSP 0x3C06 /* 3Com NBP Close response not registered */ #define ETHERTYPE_NBPDG 0x3C07 /* 3Com NBP Datagram (like XNS IDP) not registered */ #define ETHERTYPE_NBPDGB 0x3C08 /* 3Com NBP Datagram broadcast not registered */ #define ETHERTYPE_NBPCLAIM 0x3C09 /* 3Com NBP Claim NetBIOS name not registered */ #define ETHERTYPE_NBPDLTE 0x3C0A /* 3Com NBP Delete Netbios name not registered */ #define ETHERTYPE_NBPRAS 0x3C0B /* 3Com NBP Remote adaptor status request not registered */ #define ETHERTYPE_NBPRAR 0x3C0C /* 3Com NBP Remote adaptor response not registered */ #define ETHERTYPE_NBPRST 0x3C0D /* 3Com NBP Reset not registered */ #define ETHERTYPE_PCS 0x4242 /* PCS Basic Block Protocol */ #define ETHERTYPE_IMLBLDIAG 0x424C /* Information Modes Little Big LAN diagnostic */ #define ETHERTYPE_DIDDLE 0x4321 /* THD - Diddle */ #define ETHERTYPE_IMLBL 0x4C42 /* Information Modes Little Big LAN */ #define ETHERTYPE_SIMNET 0x5208 /* BBN Simnet Private */ #define ETHERTYPE_DECEXPER 0x6000 /* DEC Unassigned, experimental */ #define ETHERTYPE_MOPDL 0x6001 /* DEC MOP dump/load */ #define ETHERTYPE_MOPRC 0x6002 /* DEC MOP remote console */ #define ETHERTYPE_DECnet 0x6003 /* DEC DECNET Phase IV route */ #define ETHERTYPE_DN ETHERTYPE_DECnet /* libpcap, tcpdump */ #define ETHERTYPE_LAT 0x6004 /* DEC LAT */ #define ETHERTYPE_DECDIAG 0x6005 /* DEC diagnostic protocol (at interface initialization?) */ #define ETHERTYPE_DECCUST 0x6006 /* DEC customer protocol */ #define ETHERTYPE_SCA 0x6007 /* DEC LAVC, SCA */ #define ETHERTYPE_AMBER 0x6008 /* DEC AMBER */ #define ETHERTYPE_DECMUMPS 0x6009 /* DEC MUMPS */ /* 0x6010 - 0x6014 3Com Corporation */ #define ETHERTYPE_TRANSETHER 0x6558 /* Trans Ether Bridging (RFC1701)*/ #define ETHERTYPE_RAWFR 0x6559 /* Raw Frame Relay (RFC1701) */ #define ETHERTYPE_UBDL 0x7000 /* Ungermann-Bass download */ #define ETHERTYPE_UBNIU 0x7001 /* Ungermann-Bass NIUs */ #define ETHERTYPE_UBDIAGLOOP 0x7002 /* Ungermann-Bass diagnostic/loopback */ #define ETHERTYPE_UBNMC 0x7003 /* Ungermann-Bass ??? (NMC to/from UB Bridge) */ #define ETHERTYPE_UBBST 0x7005 /* Ungermann-Bass Bridge Spanning Tree */ #define ETHERTYPE_OS9 0x7007 /* OS/9 Microware */ #define ETHERTYPE_OS9NET 0x7009 /* OS/9 Net? */ /* 0x7020 - 0x7029 LRT (England) (now Sintrom) */ #define ETHERTYPE_RACAL 0x7030 /* Racal-Interlan */ #define ETHERTYPE_PRIMENTS 0x7031 /* Prime NTS (Network Terminal Service) */ #define ETHERTYPE_CABLETRON 0x7034 /* Cabletron */ #define ETHERTYPE_CRONUSVLN 0x8003 /* Cronus VLN */ #define ETHERTYPE_CRONUS 0x8004 /* Cronus Direct */ #define ETHERTYPE_HP 0x8005 /* HP Probe */ #define ETHERTYPE_NESTAR 0x8006 /* Nestar */ #define ETHERTYPE_ATTSTANFORD 0x8008 /* AT&T/Stanford (local use) */ #define ETHERTYPE_EXCELAN 0x8010 /* Excelan */ #define ETHERTYPE_SG_DIAG 0x8013 /* SGI diagnostic type */ #define ETHERTYPE_SG_NETGAMES 0x8014 /* SGI network games */ #define ETHERTYPE_SG_RESV 0x8015 /* SGI reserved type */ #define ETHERTYPE_SG_BOUNCE 0x8016 /* SGI bounce server */ #define ETHERTYPE_APOLLODOMAIN 0x8019 /* Apollo DOMAIN */ #define ETHERTYPE_TYMSHARE 0x802E /* Tymeshare */ #define ETHERTYPE_TIGAN 0x802F /* Tigan, Inc. */ #define ETHERTYPE_REVARP 0x8035 /* Reverse addr resolution protocol */ #define ETHERTYPE_AEONIC 0x8036 /* Aeonic Systems */ #define ETHERTYPE_IPXNEW 0x8037 /* IPX (Novell Netware?) */ #define ETHERTYPE_LANBRIDGE 0x8038 /* DEC LANBridge */ #define ETHERTYPE_DSMD 0x8039 /* DEC DSM/DDP */ #define ETHERTYPE_ARGONAUT 0x803A /* DEC Argonaut Console */ #define ETHERTYPE_VAXELN 0x803B /* DEC VAXELN */ #define ETHERTYPE_DECDNS 0x803C /* DEC DNS Naming Service */ #define ETHERTYPE_ENCRYPT 0x803D /* DEC Ethernet Encryption */ #define ETHERTYPE_DECDTS 0x803E /* DEC Distributed Time Service */ #define ETHERTYPE_DECLTM 0x803F /* DEC LAN Traffic Monitor */ #define ETHERTYPE_DECNETBIOS 0x8040 /* DEC PATHWORKS DECnet NETBIOS Emulation */ #define ETHERTYPE_DECLAST 0x8041 /* DEC Local Area System Transport */ /* 0x8042 DEC Unassigned */ #define ETHERTYPE_PLANNING 0x8044 /* Planning Research Corp. */ /* 0x8046 - 0x8047 AT&T */ #define ETHERTYPE_DECAM 0x8048 /* DEC Availability Manager for Distributed Systems DECamds (but someone at DEC says not) */ #define ETHERTYPE_EXPERDATA 0x8049 /* ExperData */ #define ETHERTYPE_VEXP 0x805B /* Stanford V Kernel exp. */ #define ETHERTYPE_VPROD 0x805C /* Stanford V Kernel prod. */ #define ETHERTYPE_ES 0x805D /* Evans & Sutherland */ #define ETHERTYPE_LITTLE 0x8060 /* Little Machines */ #define ETHERTYPE_COUNTERPOINT 0x8062 /* Counterpoint Computers */ /* 0x8065 - 0x8066 Univ. of Mass @ Amherst */ #define ETHERTYPE_VEECO 0x8067 /* Veeco Integrated Auto. */ #define ETHERTYPE_GENDYN 0x8068 /* General Dynamics */ #define ETHERTYPE_ATT 0x8069 /* AT&T */ #define ETHERTYPE_AUTOPHON 0x806A /* Autophon */ #define ETHERTYPE_COMDESIGN 0x806C /* ComDesign */ #define ETHERTYPE_COMPUGRAPHIC 0x806D /* Compugraphic Corporation */ /* 0x806E - 0x8077 Landmark Graphics Corp. */ #define ETHERTYPE_MATRA 0x807A /* Matra */ #define ETHERTYPE_DDE 0x807B /* Dansk Data Elektronik */ #define ETHERTYPE_MERIT 0x807C /* Merit Internodal (or Univ of Michigan?) */ /* 0x807D - 0x807F Vitalink Communications */ #define ETHERTYPE_VLTLMAN 0x8080 /* Vitalink TransLAN III Management */ /* 0x8081 - 0x8083 Counterpoint Computers */ /* 0x8088 - 0x808A Xyplex */ #define ETHERTYPE_ATALK 0x809B /* AppleTalk */ #define ETHERTYPE_AT ETHERTYPE_ATALK /* old NetBSD */ #define ETHERTYPE_APPLETALK ETHERTYPE_ATALK /* HP-UX */ /* 0x809C - 0x809E Datability */ #define ETHERTYPE_SPIDER 0x809F /* Spider Systems Ltd. */ /* 0x80A3 Nixdorf */ /* 0x80A4 - 0x80B3 Siemens Gammasonics Inc. */ /* 0x80C0 - 0x80C3 DCA (Digital Comm. Assoc.) Data Exchange Cluster */ /* 0x80C4 - 0x80C5 Banyan Systems */ #define ETHERTYPE_PACER 0x80C6 /* Pacer Software */ #define ETHERTYPE_APPLITEK 0x80C7 /* Applitek Corporation */ /* 0x80C8 - 0x80CC Intergraph Corporation */ /* 0x80CD - 0x80CE Harris Corporation */ /* 0x80CF - 0x80D2 Taylor Instrument */ /* 0x80D3 - 0x80D4 Rosemount Corporation */ #define ETHERTYPE_SNA 0x80D5 /* IBM SNA Services over Ethernet */ #define ETHERTYPE_VARIAN 0x80DD /* Varian Associates */ /* 0x80DE - 0x80DF TRFS (Integrated Solutions Transparent Remote File System) */ /* 0x80E0 - 0x80E3 Allen-Bradley */ /* 0x80E4 - 0x80F0 Datability */ #define ETHERTYPE_RETIX 0x80F2 /* Retix */ #define ETHERTYPE_AARP 0x80F3 /* AppleTalk AARP */ /* 0x80F4 - 0x80F5 Kinetics */ #define ETHERTYPE_APOLLO 0x80F7 /* Apollo Computer */ #define ETHERTYPE_VLAN 0x8100 /* IEEE 802.1Q VLAN tagging (XXX conflicts) */ /* 0x80FF - 0x8101 Wellfleet Communications (XXX conflicts) */ #define ETHERTYPE_BOFL 0x8102 /* Wellfleet; BOFL (Breath OF Life) pkts [every 5-10 secs.] */ #define ETHERTYPE_WELLFLEET 0x8103 /* Wellfleet Communications */ /* 0x8107 - 0x8109 Symbolics Private */ #define ETHERTYPE_TALARIS 0x812B /* Talaris */ #define ETHERTYPE_WATERLOO 0x8130 /* Waterloo Microsystems Inc. (XXX which?) */ #define ETHERTYPE_HAYES 0x8130 /* Hayes Microcomputers (XXX which?) */ #define ETHERTYPE_VGLAB 0x8131 /* VG Laboratory Systems */ /* 0x8132 - 0x8137 Bridge Communications */ #define ETHERTYPE_IPX 0x8137 /* Novell (old) NetWare IPX (ECONFIG E option) */ #define ETHERTYPE_NOVELL 0x8138 /* Novell, Inc. */ /* 0x8139 - 0x813D KTI */ #define ETHERTYPE_MUMPS 0x813F /* M/MUMPS data sharing */ #define ETHERTYPE_AMOEBA 0x8145 /* Vrije Universiteit (NL) Amoeba 4 RPC (obsolete) */ #define ETHERTYPE_FLIP 0x8146 /* Vrije Universiteit (NL) FLIP (Fast Local Internet Protocol) */ #define ETHERTYPE_VURESERVED 0x8147 /* Vrije Universiteit (NL) [reserved] */ #define ETHERTYPE_LOGICRAFT 0x8148 /* Logicraft */ #define ETHERTYPE_NCD 0x8149 /* Network Computing Devices */ #define ETHERTYPE_ALPHA 0x814A /* Alpha Micro */ #define ETHERTYPE_SNMP 0x814C /* SNMP over Ethernet (see RFC1089) */ /* 0x814D - 0x814E BIIN */ #define ETHERTYPE_TEC 0x814F /* Technically Elite Concepts */ #define ETHERTYPE_RATIONAL 0x8150 /* Rational Corp */ /* 0x8151 - 0x8153 Qualcomm */ /* 0x815C - 0x815E Computer Protocol Pty Ltd */ /* 0x8164 - 0x8166 Charles River Data Systems */ #define ETHERTYPE_XTP 0x817D /* Protocol Engines XTP */ #define ETHERTYPE_SGITW 0x817E /* SGI/Time Warner prop. */ #define ETHERTYPE_HIPPI_FP 0x8180 /* HIPPI-FP encapsulation */ #define ETHERTYPE_STP 0x8181 /* Scheduled Transfer STP, HIPPI-ST */ /* 0x8182 - 0x8183 Reserved for HIPPI-6400 */ /* 0x8184 - 0x818C SGI prop. */ #define ETHERTYPE_MOTOROLA 0x818D /* Motorola */ #define ETHERTYPE_NETBEUI 0x8191 /* PowerLAN NetBIOS/NetBEUI (PC) */ /* 0x819A - 0x81A3 RAD Network Devices */ /* 0x81B7 - 0x81B9 Xyplex */ /* 0x81CC - 0x81D5 Apricot Computers */ /* 0x81D6 - 0x81DD Artisoft Lantastic */ /* 0x81E6 - 0x81EF Polygon */ /* 0x81F0 - 0x81F2 Comsat Labs */ /* 0x81F3 - 0x81F5 SAIC */ /* 0x81F6 - 0x81F8 VG Analytical */ /* 0x8203 - 0x8205 QNX Software Systems Ltd. */ /* 0x8221 - 0x8222 Ascom Banking Systems */ /* 0x823E - 0x8240 Advanced Encryption Systems */ /* 0x8263 - 0x826A Charles River Data Systems */ /* 0x827F - 0x8282 Athena Programming */ /* 0x829A - 0x829B Inst Ind Info Tech */ /* 0x829C - 0x82AB Taurus Controls */ /* 0x82AC - 0x8693 Walker Richer & Quinn */ #define ETHERTYPE_ACCTON 0x8390 /* Accton Technologies (unregistered) */ #define ETHERTYPE_TALARISMC 0x852B /* Talaris multicast */ #define ETHERTYPE_KALPANA 0x8582 /* Kalpana */ /* 0x8694 - 0x869D Idea Courier */ /* 0x869E - 0x86A1 Computer Network Tech */ /* 0x86A3 - 0x86AC Gateway Communications */ #define ETHERTYPE_SECTRA 0x86DB /* SECTRA */ #define ETHERTYPE_IPV6 0x86DD /* IP protocol version 6 */ #define ETHERTYPE_DELTACON 0x86DE /* Delta Controls */ #define ETHERTYPE_ATOMIC 0x86DF /* ATOMIC */ /* 0x86E0 - 0x86EF Landis & Gyr Powers */ /* 0x8700 - 0x8710 Motorola */ #define ETHERTYPE_RDP 0x8739 /* Control Technology Inc. RDP Without IP */ #define ETHERTYPE_MICP 0x873A /* Control Technology Inc. Mcast Industrial Ctrl Proto. */ /* 0x873B - 0x873C Control Technology Inc. Proprietary */ #define ETHERTYPE_TCPCOMP 0x876B /* TCP/IP Compression (RFC1701) */ #define ETHERTYPE_IPAS 0x876C /* IP Autonomous Systems (RFC1701) */ #define ETHERTYPE_SECUREDATA 0x876D /* Secure Data (RFC1701) */ #define ETHERTYPE_FLOWCONTROL 0x8808 /* 802.3x flow control packet */ #define ETHERTYPE_SLOW 0x8809 /* 803.3ad slow protocols (LACP/Marker) */ #define ETHERTYPE_PPP 0x880B /* PPP (obsolete by PPPOE) */ #define ETHERTYPE_HITACHI 0x8820 /* Hitachi Cable (Optoelectronic Systems Laboratory) */ #define ETHERTYPE_MPLS 0x8847 /* MPLS Unicast */ #define ETHERTYPE_MPLS_MCAST 0x8848 /* MPLS Multicast */ #define ETHERTYPE_AXIS 0x8856 /* Axis Communications AB proprietary bootstrap/config */ #define ETHERTYPE_PPPOEDISC 0x8863 /* PPP Over Ethernet Discovery Stage */ #define ETHERTYPE_PPPOE 0x8864 /* PPP Over Ethernet Session Stage */ #define ETHERTYPE_LANPROBE 0x8888 /* HP LanProbe test? */ #define ETHERTYPE_EAPOL 0x888E /* 802.1X EAP over LAN */ #define ETHERTYPE_AOE 0x88A2 /* ATA over Ethernet */ #define ETHERTYPE_QINQ 0x88A8 /* 802.1ad VLAN stacking */ #define ETHERTYPE_LLDP 0x88CC /* Link Layer Discovery Protocol */ #define ETHERTYPE_802_EX1 0x88B5 /* IEEE Std 802 - Local Experimental */ #define ETHERTYPE_802_EX2 0x88B6 /* IEEE Std 802 - Local Experimental */ #define ETHERTYPE_MACSEC 0x88e5 /* 802.1AE MACsec */ #define ETHERTYPE_PBB 0x88e7 /* 802.1Q Provider Backbone Bridging */ #define ETHERTYPE_NSH 0x894F /* Network Service Header (RFC8300) */ #define ETHERTYPE_LOOPBACK 0x9000 /* Loopback */ #define ETHERTYPE_LBACK ETHERTYPE_LOOPBACK /* DEC MOP loopback */ #define ETHERTYPE_XNSSM 0x9001 /* 3Com (Formerly Bridge Communications), XNS Systems Management */ #define ETHERTYPE_TCPSM 0x9002 /* 3Com (Formerly Bridge Communications), TCP/IP Systems Management */ #define ETHERTYPE_BCLOOP 0x9003 /* 3Com (Formerly Bridge Communications), loopback detection */ #define ETHERTYPE_DEBNI 0xAAAA /* DECNET? Used by VAX 6220 DEBNI */ #define ETHERTYPE_SONIX 0xFAF5 /* Sonix Arpeggio */ #define ETHERTYPE_VITAL 0xFF00 /* BBN VITAL-LanBridge cache wakeups */ /* 0xFF00 - 0xFFOF ISC Bunker Ramo */ #define ETHERTYPE_MAX 0xFFFF /* Maximum valid ethernet type, reserved */ #endif /* _NET_ETHERTYPES_H_ */ openiked-7.4/compat/net/if_arp.h000066400000000000000000000065051477554731100166700ustar00rootroot00000000000000/* * Copyright (c) 1986, 1993 * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. * * @(#)if_arp.h 8.1 (Berkeley) 6/10/93 */ #ifndef _MSC_VER #include_next #else #ifndef IKED_COMPAT_SYS_WAIT_H #define IKED_COMPAT_SYS_WAIT_H /* * Address Resolution Protocol. * * See RFC 826 for protocol description. ARP packets are variable * in size; the arphdr structure defines the fixed-length portion. * Protocol type values are the same as those for 10 Mb/s Ethernet. * It is followed by the variable-sized fields ar_sha, arp_spa, * arp_tha and arp_tpa in that order, according to the lengths * specified. Field names used correspond to RFC 826. */ struct arphdr { uint16_t ar_hrd; /* format of hardware address */ #define ARPHRD_ETHER 1 /* ethernet hardware format */ #define ARPHRD_IEEE802 6 /* IEEE 802 hardware format */ #define ARPHRD_FRELAY 15 /* frame relay hardware format */ #define ARPHRD_IEEE1394 24 /* IEEE 1394 (FireWire) hardware format */ uint16_t ar_pro; /* format of protocol address */ uint8_t ar_hln; /* length of hardware address */ uint8_t ar_pln; /* length of protocol address */ uint16_t ar_op; /* one of: */ #define ARPOP_REQUEST 1 /* request to resolve address */ #define ARPOP_REPLY 2 /* response to previous request */ #define ARPOP_REVREQUEST 3 /* request protocol address given hardware */ #define ARPOP_REVREPLY 4 /* response giving protocol address */ #define ARPOP_INVREQUEST 8 /* request to identify peer */ #define ARPOP_INVREPLY 9 /* response identifying peer */ /* * The remaining fields are variable in size, * according to the sizes above. */ #ifdef COMMENT_ONLY uint8_t ar_sha[]; /* sender hardware address */ uint8_t ar_spa[]; /* sender protocol address */ uint8_t ar_tha[]; /* target hardware address */ uint8_t ar_tpa[]; /* target protocol address */ #endif }; #endif /* !IKED_COMPAT_SYS_WAIT_H */ #endif openiked-7.4/compat/net/pfkeyv2.h000066400000000000000000000016311477554731100170110ustar00rootroot00000000000000/* * Copyright (c) 2021 Tobias Heider * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #if defined(HAVE_NET_PFKEY_H) #include_next #elif defined(HAVE_LINUX_PFKEY_H) #include #endif openiked-7.4/compat/netinet/000077500000000000000000000000001477554731100161315ustar00rootroot00000000000000openiked-7.4/compat/netinet/if_ether.h000066400000000000000000000076231477554731100200770ustar00rootroot00000000000000/* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. * * @(#)if_ether.h 8.1 (Berkeley) 6/10/93 */ #ifndef _MSC_VER #include_next #else #ifndef IKED_COMPAT_NETINET_IF_ETHER_H #define IKED_COMPAT_NETINET_IF_ETHER_H /* * Some basic Ethernet constants. */ #define ETHER_ADDR_LEN 6 /* Ethernet address length */ #define ETHER_TYPE_LEN 2 /* Ethernet type field length */ #define ETHER_CRC_LEN 4 /* Ethernet CRC length */ #define ETHER_HDR_LEN ((ETHER_ADDR_LEN * 2) + ETHER_TYPE_LEN) #define ETHER_MIN_LEN 64 /* Minimum frame length, CRC included */ #define ETHER_MAX_LEN 1518 /* Maximum frame length, CRC included */ #define ETHER_MAX_DIX_LEN 1536 /* Maximum DIX frame length */ /* * Some Ethernet extensions. */ #define ETHER_VLAN_ENCAP_LEN 4 /* len of 802.1Q VLAN encapsulation */ /* * Mbuf adjust factor to force 32-bit alignment of IP header. * Drivers should do m_adj(m, ETHER_ALIGN) when setting up a * receive so the upper layers get the IP header properly aligned * past the 14-byte Ethernet header. */ #define ETHER_ALIGN 2 /* driver adjust for IP hdr alignment */ /* * The maximum supported Ethernet length and some space for encapsulation. */ #define ETHER_MAX_HARDMTU_LEN 65435 /* * Ethernet address - 6 octets */ struct ether_addr { uint8_t ether_addr_octet[ETHER_ADDR_LEN]; }; /* * The length of the combined header. */ struct ether_header { uint8_t ether_dhost[ETHER_ADDR_LEN]; uint8_t ether_shost[ETHER_ADDR_LEN]; uint16_t ether_type; }; /* * VLAN headers. */ struct ether_vlan_header { uchar evl_dhost[ETHER_ADDR_LEN]; uchar evl_shost[ETHER_ADDR_LEN]; uint16_t evl_encap_proto; uint16_t evl_tag; uint16_t evl_proto; }; /* * Ethernet Address Resolution Protocol. * * See RFC 826 for protocol description. Structure below is adapted * to resolving internet addresses. Field names used correspond to * RFC 826. */ struct ether_arp { struct arphdr ea_hdr; /* fixed-size header */ uint8_t arp_sha[ETHER_ADDR_LEN]; /* sender hardware address */ uint8_t arp_spa[4]; /* sender protocol address */ uint8_t arp_tha[ETHER_ADDR_LEN]; /* target hardware address */ uint8_t arp_tpa[4]; /* target protocol address */ }; #define arp_hrd ea_hdr.ar_hrd #define arp_pro ea_hdr.ar_pro #define arp_hln ea_hdr.ar_hln #define arp_pln ea_hdr.ar_pln #define arp_op ea_hdr.ar_op #endif /* !IKED_COMPAT_NETINET_IF_ETHER_H */ #endif openiked-7.4/compat/netinet/in.h000066400000000000000000000004351477554731100167120ustar00rootroot00000000000000/* * Public domain * in.h compatibility shim */ #ifdef _MSC_VER #elif defined(__linux__) #ifndef IKED_COMPAT_NETINET_IN_H #define IKED_COMPAT_NETINET_IN_H #include_next #define IPPROTO_IPV4 IPPROTO_IPIP #endif #else /* OpenBSD */ #include_next #endif openiked-7.4/compat/netinet/ip.h000066400000000000000000000062101477554731100167110ustar00rootroot00000000000000/* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. * * @(#)ip.h 8.1 (Berkeley) 6/10/93 */ #ifndef _MSC_VER #include_next #else #ifndef IKED_COMPAT_NETINET_IP_H #define IKED_COMPAT_NETINET_IP_H /* * Definitions for internet protocol version 4. * Per RFC 791, September 1981. */ #define IPVERSION 4 /* * Structure of an internet header, naked of options. */ #pragma pack(push,1) struct ip { #if _BYTE_ORDER == _LITTLE_ENDIAN uint8_t ip_hl:4, /* header length */ ip_v:4; /* version */ #elif _BYTE_ORDER == _BIG_ENDIAN uint8_t ip_v:4, /* version */ ip_hl:4; /* header length */ #endif uint8_t ip_tos; /* type of service */ uint16_t ip_len; /* total length */ uint16_t ip_id; /* identification */ uint16_t ip_off; /* fragment offset field */ #define IP_RF 0x8000 /* reserved fragment flag */ #define IP_DF 0x4000 /* dont fragment flag */ #define IP_MF 0x2000 /* more fragments flag */ #define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ uint8_t ip_ttl; /* time to live */ uint8_t ip_p; /* protocol */ uint16_t ip_sum; /* checksum */ struct in_addr ip_src, ip_dst; /* source and dest address */ }; #pragma pack(pop) #define IP_MAXPACKET 65535 /* maximum packet size */ /* * Definitions for IP type of service (ip_tos) */ #define IPTOS_LOWDELAY 0x10 #define IPTOS_THROUGHPUT 0x08 #define IPTOS_RELIABILITY 0x04 /* IPTOS_LOWCOST 0x02 XXX */ #if 1 /* ECN RFC3168 obsoletes RFC2481, and these will be deprecated soon. */ #define IPTOS_CE 0x01 /* congestion experienced */ #define IPTOS_ECT 0x02 /* ECN-capable transport */ #endif #endif /* !IKED_COMPAT_NETINET_IP_H */ #endif openiked-7.4/compat/netinet/ip6.h000066400000000000000000000105121477554731100167770ustar00rootroot00000000000000/* $OpenBSD: ip6.h,v 1.20 2011/04/05 15:14:59 blambert Exp $ */ /* $KAME: ip6.h,v 1.45 2003/06/05 04:46:38 keiichi Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * 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 project 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 PROJECT 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 PROJECT 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. */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. * * @(#)ip.h 8.1 (Berkeley) 6/10/93 */ #ifndef _MSC_VER #include_next #else #ifndef IKED_COMPAT_NETINET_IP6_H #define IKED_COMPAT_NETINET_IP6_H #ifndef __packed #define __packed __attribute__((__packed__)) #endif /* * Definition for internet protocol version 6. * RFC 2460 */ struct ip6_hdr { union { struct ip6_hdrctl { uint32_t ip6_un1_flow; /* 20 bits of flow-ID */ uint16_t ip6_un1_plen; /* payload length */ uint8_t ip6_un1_nxt; /* next header */ uint8_t ip6_un1_hlim; /* hop limit */ } ip6_un1; uint8_t ip6_un2_vfc; /* 4 bits version, top 4 bits class */ } ip6_ctlun; struct in6_addr ip6_src; /* source address */ struct in6_addr ip6_dst; /* destination address */ } __packed; #define ip6_vfc ip6_ctlun.ip6_un2_vfc #define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow #define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen #define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt #define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim #define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim #define IPV6_VERSION 0x60 #define IPV6_VERSION_MASK 0xf0 #endif /* !IKED_COMPAT_NETINET_IP6_H */ #endif openiked-7.4/compat/netinet/ip_ipsp.h000066400000000000000000000025771477554731100177600ustar00rootroot00000000000000/* * Copyright (c) 2021 Tobias Heider * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_IPSP_H #include_next "netinet/ip_ipsp.h" #else #ifdef HAVE_NET_IPSEC_H #include #include #include #endif #ifdef HAVE_LINUX_IPSEC_H #include #endif #ifdef HAVE_NETINET6_IPSEC_H #include #endif #if !defined HAVE_IPSP_H && (defined HAVE_NET_IPSEC_H || \ defined HAVE_LINUX_IPSEC_H || defined HAVE_NETINET6_IPSEC_H) #if !defined(IPSP_DIRECTION_IN) #define IPSP_DIRECTION_IN IPSEC_DIR_INBOUND #endif #if !defined(IPSP_DIRECTION_OUT) #define IPSP_DIRECTION_OUT IPSEC_DIR_OUTBOUND #endif #endif #endif /* HAVE_IPSP_H */ openiked-7.4/compat/netinet/ip_var.h000066400000000000000000000041671477554731100175720ustar00rootroot00000000000000/* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. * * @(#)ip.h 8.1 (Berkeley) 6/10/93 */ #if !defined _MSC_VER && !defined __linux__ #include_next #else #ifndef IKED_COMPAT_NETINET_IP_VAR_H #define IKED_COMPAT_NETINET_IP_VAR_H /* * Overlay for ip header used by other protocols (tcp, udp). */ struct ipovly { uint8_t ih_x1[9]; /* (unused) */ uint8_t ih_pr; /* protocol */ uint16_t ih_len; /* protocol length */ struct in_addr ih_src; /* source internet address */ struct in_addr ih_dst; /* destination internet address */ }; #endif /* !IKED_COMPAT_NETINET_IP_VAR_H */ #endif openiked-7.4/compat/netinet/udp.h000066400000000000000000000040051477554731100170710ustar00rootroot00000000000000/* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. * * @(#)udp.h 8.1 (Berkeley) 6/10/93 */ #ifndef _MSC_VER #include_next #else #ifndef IKED_COMPAT_NETINET_UDP_H #define IKED_COMPAT_NETINET_UDP_H /* * Udp protocol header. * Per RFC 768, September, 1981. */ struct udphdr { uint16_t uh_sport; /* source port */ uint16_t uh_dport; /* destination port */ uint16_t uh_ulen; /* udp length */ uint16_t uh_sum; /* udp checksum */ }; #endif /* !IKED_COMPAT_NETINET_UDP_H */ #endif openiked-7.4/compat/netinet/udp_var.h000066400000000000000000000043461477554731100177510ustar00rootroot00000000000000/* * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. * * @(#)udp_var.h 8.1 (Berkeley) 6/10/93 */ #if !defined _MSC_VER && !defined __linux__ #include_next #else #ifndef IKED_COMPAT_NETINET_UDP_VAR_H #define IKED_COMPAT_NETINET_UDP_VAR_H /* * UDP kernel structures and variables. */ struct udpiphdr { struct ipovly ui_i; /* overlaid ip structure */ struct udphdr ui_u; /* udp header */ }; #define ui_x1 ui_i.ih_x1 #define ui_pr ui_i.ih_pr #define ui_len ui_i.ih_len #define ui_src ui_i.ih_src #define ui_dst ui_i.ih_dst #define ui_sport ui_u.uh_sport #define ui_dport ui_u.uh_dport #define ui_ulen ui_u.uh_ulen #define ui_sum ui_u.uh_sum #endif /* !IKED_COMPAT_NETINET_UDP_VAR_H */ #endif openiked-7.4/compat/openbsd-compat.h000066400000000000000000000117431477554731100175550ustar00rootroot00000000000000/* Placed in the public domain */ #ifndef _OPENBSD_COMPAT_H #define _OPENBSD_COMPAT_H #define YYSTYPE_IS_DECLARED 1 /* for bison */ #ifndef LOGIN_NAME_MAX # define LOGIN_NAME_MAX 9 #endif #include #if !defined(HAVE_STRLCAT) size_t strlcat(char *, const char *, size_t); #endif #if !defined(HAVE_STRLCPY) size_t strlcpy(char *, const char *, size_t); #endif #if !defined(HAVE_REALLOCARRAY) void *reallocarray(void *, size_t, size_t); #endif #if !defined(HAVE_RECALLOCARRAY) void *recallocarray(void *, size_t, size_t, size_t); #endif #if !defined(HAVE_EXPLICIT_BZERO) void explicit_bzero(void *, size_t); #endif #if !defined(HAVE_GETPAGESIZE) int getpagesize(void); #endif #if !defined(HAVE_TIMINGSAFE_BCMP) int timingsafe_bcmp(const void *, const void *, size_t); #endif #if !defined(HAVE_ACCEPT4) #include #define accept4 bsd_accept4 int bsd_accept4(int, struct sockaddr *, socklen_t *, int flags); #endif #if !defined(HAVE_SOCK_NONBLOCK) #define SOCK_NONBLOCK 0x4000 /* Set O_NONBLOCK */ #define SOCK_CLOEXEC 0x8000 /* Set FD_CLOEXEC */ #define SOCK_SETFLAGS 0xf000 /* Set flags as checked above */ #define socket bsd_socket int bsd_socket(int domain, int type, int protocol); #endif #if !defined(HAVE_SETPROCTITLE) void compat_init_setproctitle(int argc, char *argv[]); void setproctitle(const char *fmt, ...); #endif #if !defined(HAVE_SETRESGID) int setresgid(gid_t rgid, gid_t egid, gid_t sgid); #endif #if !defined(HAVE_SETRESUID) int setresuid(uid_t ruid, uid_t euid, uid_t suid); #endif #ifdef _WIN32 #include uid_t geteuid(void); #endif #if !defined(HAVE_GETRTABLE) int getrtable(void); #endif #if !defined(HAVE_SETRTABLE) int setrtable(int rtableid); #endif #if !defined(HAVE_STRTONUM) long long strtonum(const char *nptr, long long minval, long long maxval, const char **errstr); #endif #if !defined(HAVE_FREEZERO) void freezero(void *ptr, size_t size); #endif #if !defined(HAVE_GETDTABLECOUNT) int getdtablecount(void); #endif #if !defined(HAVE_GETOPT) #include "getopt.h" #endif #if !defined(HAVE_USLEEP) int usleep(unsigned int x); #endif #ifdef HAVE_SOCKADDR_SA_LEN #ifndef SA_LEN #define SA_LEN(sa) (sa)->sa_len #endif #ifndef SS_LEN #define SS_LEN(ss) (ss).ss_len #endif #else #define SA_LEN(sa) \ ((sa->sa_family == AF_INET) ? sizeof(struct sockaddr_in) : \ (sa->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : \ sizeof(struct sockaddr)) #define SS_LEN(ss) \ ((ss.ss_family == AF_INET) ? sizeof(struct sockaddr_in) : \ (ss.ss_family == AF_INET6) ? sizeof(struct sockaddr_in6) : \ sizeof(struct sockaddr_storage)) #endif #ifndef HAVE_FFS int ffs(int); #endif #ifdef __OpenBSD__ typedef int evutil_socket_t; #endif #ifndef _PASSWORD_LEN #define _PASSWORD_LEN 120 #endif #ifdef HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # ifdef HAVE_SYS_NDIR_H # include # endif # ifdef HAVE_SYS_DIR_H # include # endif # ifdef HAVE_NDIR_H # include # endif #endif #if !defined(AF_LINK) && defined(AF_PACKET) #define AF_LINK AF_PACKET /* XXX workaround on Linux */ #endif #ifndef HOST_NAME_MAX # include "netdb.h" /* for MAXHOSTNAMELEN */ # if defined(_POSIX_HOST_NAME_MAX) # define HOST_NAME_MAX _POSIX_HOST_NAME_MAX # elif defined(MAXHOSTNAMELEN) # define HOST_NAME_MAX MAXHOSTNAMELEN # else # define HOST_NAME_MAX 255 # endif #endif /* HOST_NAME_MAX */ /* FreeBSD */ #ifndef CPI_PRIVATE_MIN #define CPI_PRIVATE_MIN 61440 #endif #ifndef CPI_PRIVATE_MAX #define CPI_PRIVATE_MAX 65535 #endif #if !defined(SADB_X_ADDFLOW) && defined(SADB_X_SPDUPDATE) #define SADB_X_ADDFLOW SADB_X_SPDUPDATE #endif #if !defined(SADB_X_DELFLOW) && defined(SADB_X_SPDDELETE) #define SADB_X_DELFLOW SADB_X_SPDDELETE #endif #if !defined(SADB_X_FLOW_TYPE_DENY) #define SADB_X_FLOW_TYPE_DENY 1 #endif #if defined(HAVE_LINUX_PFKEY_H) /* Encryption Algorithms */ #define SADB_X_EALG_AES SADB_X_EALG_AESCBC #define SADB_X_EALG_AESGCM16 SADB_X_EALG_AES_GCM_ICV16 #define SADB_X_EALG_AESGMAC SADB_X_EALG_NULL_AES_GMAC /* Authentication Algorithms */ #define SADB_X_AALG_SHA2_256 SADB_X_AALG_SHA2_256HMAC #define SADB_X_AALG_SHA2_384 SADB_X_AALG_SHA2_384HMAC #define SADB_X_AALG_SHA2_512 SADB_X_AALG_SHA2_512HMAC #endif #if !defined(__packed) #define __packed __attribute__((__packed__)) #endif #if defined(HAVE_APPLE_NATT) && !defined(SADB_X_EXT_NATT) /* * These are hidden in Apple XNU's private pfkeyv2.h header */ #define SADB_X_EXT_NATT 0x0002 /* Enable UDP encapsulation */ #define SADB_X_EXT_NATT_KEEPALIVE 0x0004 /* Send NAT-T keepalives */ #define SADB_X_EXT_NATT_MULTIPLEUSERS 0x0008 /* Use for VPN gateways */ #define SADB_X_EXT_NATT_DETECTED_PEER 0x1000 /* Opposite of KEEPALIVE */ struct sadb_sa_natt { uint16_t sadb_sa_natt_port; union { uint16_t sadb_reserved0; uint16_t sadb_sa_natt_interval; }; uint16_t sadb_sa_natt_offload_interval; uint16_t sadb_sa_natt_src_port; }; #endif #endif /* !_OPENBSD_COMPAT_H */ openiked-7.4/compat/reallocarray.c000066400000000000000000000025231477554731100173110ustar00rootroot00000000000000/* $OpenBSD: reallocarray.c,v 1.3 2015/09/13 08:31:47 guenther Exp $ */ /* * Copyright (c) 2008 Otto Moerbeek * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include /* * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW */ #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) void * reallocarray(void *optr, size_t nmemb, size_t size) { if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && nmemb > 0 && SIZE_MAX / nmemb < size) { errno = ENOMEM; return NULL; } return realloc(optr, size * nmemb); } openiked-7.4/compat/recallocarray.c000066400000000000000000000043301477554731100174520ustar00rootroot00000000000000/* $OpenBSD: recallocarray.c,v 1.2 2021/03/18 11:16:58 claudio Exp $ */ /* * Copyright (c) 2008, 2017 Otto Moerbeek * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include /* * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW */ #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) void * recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) { size_t oldsize, newsize; void *newptr; if (ptr == NULL) return calloc(newnmemb, size); if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && newnmemb > 0 && SIZE_MAX / newnmemb < size) { errno = ENOMEM; return NULL; } newsize = newnmemb * size; if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && oldnmemb > 0 && SIZE_MAX / oldnmemb < size) { errno = EINVAL; return NULL; } oldsize = oldnmemb * size; /* * Don't bother too much if we're shrinking just a bit, * we do not shrink for series of small steps, oh well. */ if (newsize <= oldsize) { size_t d = oldsize - newsize; if (d < oldsize / 2 && d < (size_t)getpagesize()) { memset((char *)ptr + newsize, 0, d); return ptr; } } newptr = malloc(newsize); if (newptr == NULL) return NULL; if (newsize > oldsize) { memcpy(newptr, ptr, oldsize); memset((char *)newptr + oldsize, 0, newsize - oldsize); } else memcpy(newptr, ptr, newsize); explicit_bzero(ptr, oldsize); free(ptr); return newptr; } openiked-7.4/compat/setproctitle.c000066400000000000000000000111101477554731100173420ustar00rootroot00000000000000/* Based on conf.c from UCB sendmail 8.8.8 */ /* * Copyright 2003 Damien Miller * Copyright (c) 1983, 1995-1997 Eric P. Allman * Copyright (c) 1988, 1993 * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. */ #include "openbsd-compat.h" #ifndef HAVE_SETPROCTITLE #include #include #include #include #ifdef HAVE_SYS_PSTAT_H #include #endif #include #include #define SPT_NONE 0 /* don't use it at all */ #define SPT_PSTAT 1 /* use pstat(PSTAT_SETCMD, ...) */ #define SPT_REUSEARGV 2 /* cover argv with title information */ #ifndef SPT_TYPE # define SPT_TYPE SPT_NONE #endif #ifndef SPT_PADCHAR # define SPT_PADCHAR '\0' #endif #if SPT_TYPE == SPT_REUSEARGV static char *argv_start = NULL; static size_t argv_env_len = 0; #endif #endif /* HAVE_SETPROCTITLE */ void compat_init_setproctitle(int argc, char *argv[]) { #if !defined(HAVE_SETPROCTITLE) && \ defined(SPT_TYPE) && SPT_TYPE == SPT_REUSEARGV extern char **environ; char *lastargv = NULL; char **envp = environ; int i; /* * NB: This assumes that argv has already been copied out of the * way. This is true for sshd, but may not be true for other * programs. Beware. */ if (argc == 0 || argv[0] == NULL) return; /* Fail if we can't allocate room for the new environment */ for (i = 0; envp[i] != NULL; i++) ; if ((environ = calloc(i + 1, sizeof(*environ))) == NULL) { environ = envp; /* put it back */ return; } /* * Find the last argv string or environment variable within * our process memory area. */ for (i = 0; i < argc; i++) { if (lastargv == NULL || lastargv + 1 == argv[i]) lastargv = argv[i] + strlen(argv[i]); } for (i = 0; envp[i] != NULL; i++) { if (lastargv + 1 == envp[i]) lastargv = envp[i] + strlen(envp[i]); } argv[1] = NULL; argv_start = argv[0]; argv_env_len = lastargv - argv[0] - 1; /* * Copy environment * XXX - will truncate env on strdup fail */ for (i = 0; envp[i] != NULL; i++) environ[i] = strdup(envp[i]); environ[i] = NULL; #endif /* SPT_REUSEARGV */ } #ifndef HAVE_SETPROCTITLE void setproctitle(const char *fmt, ...) { #if SPT_TYPE != SPT_NONE va_list ap; char buf[1024], ptitle[1024]; size_t len = 0; int r; extern char *__progname; #if SPT_TYPE == SPT_PSTAT union pstun pst; #endif #if SPT_TYPE == SPT_REUSEARGV if (argv_env_len <= 0) return; #endif strlcpy(buf, __progname, sizeof(buf)); r = -1; va_start(ap, fmt); if (fmt != NULL) { len = strlcat(buf, ": ", sizeof(buf)); if (len < sizeof(buf)) r = vsnprintf(buf + len, sizeof(buf) - len , fmt, ap); } va_end(ap); if (r == -1 || (size_t)r >= sizeof(buf) - len) return; strnvis(ptitle, buf, sizeof(ptitle), VIS_CSTYLE|VIS_NL|VIS_TAB|VIS_OCTAL); #if SPT_TYPE == SPT_PSTAT pst.pst_command = ptitle; pstat(PSTAT_SETCMD, pst, strlen(ptitle), 0, 0); #elif SPT_TYPE == SPT_REUSEARGV /* debug("setproctitle: copy \"%s\" into len %d", buf, argv_env_len); */ len = strlcpy(argv_start, ptitle, argv_env_len); for(; len < argv_env_len; len++) argv_start[len] = SPT_PADCHAR; #endif #endif /* SPT_NONE */ } #endif /* HAVE_SETPROCTITLE */ openiked-7.4/compat/stdlib.h000066400000000000000000000015371477554731100161230ustar00rootroot00000000000000/* * stdlib.h compatibility shim * Public domain */ #ifdef _MSC_VER #if _MSC_VER >= 1900 #include <../ucrt/stdlib.h> #else #include <../include/stdlib.h> #endif #else #include_next #endif #ifndef IKED_COMPAT_STDLIB_H #define IKED_COMPAT_STDLIB_H #include #include #ifndef HAVE_ARC4RANDOM_BUF uint32_t arc4random(void); void arc4random_buf(void *_buf, size_t n); uint32_t arc4random_uniform(uint32_t upper_bound); #endif #ifndef HAVE_FREEZERO void freezero(void *ptr, size_t sz); #endif #ifndef HAVE_GETPROGNAME const char * getprogname(void); #endif void *reallocarray(void *, size_t, size_t); #ifndef HAVE_RECALLOCARRAY void *recallocarray(void *, size_t, size_t, size_t); #endif #ifndef HAVE_STRTONUM long long strtonum(const char *nptr, long long minval, long long maxval, const char **errstr); #endif #endif openiked-7.4/compat/string.h000066400000000000000000000034061477554731100161450ustar00rootroot00000000000000/* * Public domain * string.h compatibility shim */ #ifndef IKED_COMPAT_STRING_H #define IKED_COMPAT_STRING_H #ifdef _MSC_VER #if _MSC_VER >= 1900 #include <../ucrt/string.h> #else #include <../include/string.h> #endif #else #include_next #endif #include #if defined(__sun) || defined(_AIX) || defined(__hpux) /* Some functions historically defined in string.h were placed in strings.h by * SUS. Use the same hack as OS X and FreeBSD use to work around on AIX, * Solaris, and HPUX. */ #include #endif #ifndef HAVE_STRCASECMP int strcasecmp(const char *s1, const char *s2); int strncasecmp(const char *s1, const char *s2, size_t len); #endif #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t siz); #endif #ifndef HAVE_STRLCAT size_t strlcat(char *dst, const char *src, size_t siz); #endif #ifndef HAVE_STRNDUP char * strndup(const char *str, size_t maxlen); /* the only user of strnlen is strndup, so only build it if needed */ #ifndef HAVE_STRNLEN size_t strnlen(const char *str, size_t maxlen); #endif #endif #ifndef HAVE_STRSEP char *strsep(char **stringp, const char *delim); #endif #ifndef HAVE_EXPLICIT_BZERO void explicit_bzero(void *, size_t); #endif #ifndef HAVE_TIMINGSAFE_BCMP int timingsafe_bcmp(const void *b1, const void *b2, size_t n); #endif #ifndef HAVE_TIMINGSAFE_MEMCMP int timingsafe_memcmp(const void *b1, const void *b2, size_t len); #endif #ifndef HAVE_MEMMEM void * memmem(const void *big, size_t big_len, const void *little, size_t little_len); #endif #ifdef _WIN32 #include static inline char * posix_strerror(int errnum) { if (errnum == ECONNREFUSED) { return "Connection refused"; } return strerror(errnum); } #define strerror(errnum) posix_strerror(errnum) #endif #endif openiked-7.4/compat/strlcat.c000066400000000000000000000035351477554731100163110ustar00rootroot00000000000000/* $OpenBSD: strlcat.c,v 1.19 2019/01/25 00:19:25 millert Exp $ */ /* * Copyright (c) 1998, 2015 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */ #include "openbsd-compat.h" #if !defined(HAVE_STRLCAT) #include #include /* * Appends src to string dst of size dsize (unlike strncat, dsize is the * full size of dst, not space left). At most dsize-1 characters * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). * Returns strlen(src) + MIN(dsize, strlen(initial dst)). * If retval >= dsize, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t dsize) { const char *odst = dst; const char *osrc = src; size_t n = dsize; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end. */ while (n-- != 0 && *dst != '\0') dst++; dlen = dst - odst; n = dsize - dlen; if (n-- == 0) return(dlen + strlen(src)); while (*src != '\0') { if (n != 0) { *dst++ = *src; n--; } src++; } *dst = '\0'; return(dlen + (src - osrc)); /* count does not include NUL */ } #endif /* !defined(HAVE_STRLCAT) */ openiked-7.4/compat/strlcpy.c000066400000000000000000000030711477554731100163300ustar00rootroot00000000000000/* $OpenBSD: strlcpy.c,v 1.16 2019/01/25 00:19:25 millert Exp $ */ /* * Copyright (c) 1998, 2015 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include /* * Copy string src to buffer dst of size dsize. At most dsize-1 * chars will be copied. Always NUL terminates (unless dsize == 0). * Returns strlen(src); if retval >= dsize, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t dsize) { const char *osrc = src; size_t nleft = dsize; /* Copy as many bytes as will fit. */ if (nleft != 0) { while (--nleft != 0) { if ((*dst++ = *src++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src. */ if (nleft == 0) { if (dsize != 0) *dst = '\0'; /* NUL-terminate dst */ while (*src++) ; } return(src - osrc - 1); /* count does not include NUL */ } openiked-7.4/compat/strtonum.c000066400000000000000000000033541477554731100165270ustar00rootroot00000000000000/* $OpenBSD: strtonum.c,v 1.8 2015/09/13 08:31:48 guenther Exp $ */ /* * Copyright (c) 2004 Ted Unangst and Todd Miller * All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #define INVALID 1 #define TOOSMALL 2 #define TOOLARGE 3 long long strtonum(const char *numstr, long long minval, long long maxval, const char **errstrp) { long long ll = 0; int error = 0; char *ep; struct errval { const char *errstr; int err; } ev[4] = { { NULL, 0 }, { "invalid", EINVAL }, { "too small", ERANGE }, { "too large", ERANGE }, }; ev[0].err = errno; errno = 0; if (minval > maxval) { error = INVALID; } else { ll = strtoll(numstr, &ep, 10); if (numstr == ep || *ep != '\0') error = INVALID; else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) error = TOOSMALL; else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) error = TOOLARGE; } if (errstrp != NULL) *errstrp = ev[error].errstr; errno = ev[error].err; if (error) ll = 0; return (ll); } openiked-7.4/compat/sys/000077500000000000000000000000001477554731100153015ustar00rootroot00000000000000openiked-7.4/compat/sys/_null.h000066400000000000000000000005151477554731100165640ustar00rootroot00000000000000/* $OpenBSD: _null.h,v 1.2 2016/09/09 22:07:58 millert Exp $ */ /* * Written by Todd C. Miller, September 9, 2016 * Public domain. */ #ifndef NULL #if !defined(__cplusplus) #define NULL ((void *)0) #elif __cplusplus >= 201103L #define NULL nullptr #elif defined(__GNUG__) #define NULL __null #else #define NULL 0L #endif #endif openiked-7.4/compat/sys/queue.h000066400000000000000000000517271477554731100166120ustar00rootroot00000000000000/* $OpenBSD: queue.h,v 1.46 2020/12/30 13:33:12 millert Exp $ */ /* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ #ifndef _SYS_QUEUE_H_ #define _SYS_QUEUE_H_ #include /* * This file defines five types of data structures: singly-linked lists, * lists, simple queues, tail queues and XOR simple queues. * * * A singly-linked list is headed by a single forward pointer. The elements * are singly linked for minimum space and pointer manipulation overhead at * the expense of O(n) removal for arbitrary elements. New elements can be * added to the list after an existing element or at the head of the list. * Elements being removed from the head of the list should use the explicit * macro for this purpose for optimum efficiency. A singly-linked list may * only be traversed in the forward direction. Singly-linked lists are ideal * for applications with large datasets and few or no removals or for * implementing a LIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A simple queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are singly * linked to save space, so elements can only be removed from the * head of the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the * list. A simple queue may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * An XOR simple queue is used in the same way as a regular simple queue. * The difference is that the head structure also includes a "cookie" that * is XOR'd with the queue pointer (first, last or next) to generate the * real pointer value. * * For details on the use of these macros, see the queue(3) manual page. */ #if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) #define _Q_INVALID ((void *)-1) #define _Q_INVALIDATE(a) (a) = _Q_INVALID #else #define _Q_INVALIDATE(a) #endif /* * Singly-linked List definitions. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } /* * Singly-linked List access methods. */ #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_END(head) NULL #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_FOREACH(var, head, field) \ for((var) = SLIST_FIRST(head); \ (var) != SLIST_END(head); \ (var) = SLIST_NEXT(var, field)) #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SLIST_FIRST(head); \ (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ (var) = (tvar)) /* * Singly-linked List functions. */ #define SLIST_INIT(head) { \ SLIST_FIRST(head) = SLIST_END(head); \ } #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ (elm)->field.sle_next = (slistelm)->field.sle_next; \ (slistelm)->field.sle_next = (elm); \ } while (0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ (elm)->field.sle_next = (head)->slh_first; \ (head)->slh_first = (elm); \ } while (0) #define SLIST_REMOVE_AFTER(elm, field) do { \ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ } while (0) #define SLIST_REMOVE_HEAD(head, field) do { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (0) #define SLIST_REMOVE(head, elm, type, field) do { \ if ((head)->slh_first == (elm)) { \ SLIST_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->slh_first; \ \ while (curelm->field.sle_next != (elm)) \ curelm = curelm->field.sle_next; \ curelm->field.sle_next = \ curelm->field.sle_next->field.sle_next; \ } \ _Q_INVALIDATE((elm)->field.sle_next); \ } while (0) /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List access methods. */ #define LIST_FIRST(head) ((head)->lh_first) #define LIST_END(head) NULL #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) #define LIST_NEXT(elm, field) ((elm)->field.le_next) #define LIST_FOREACH(var, head, field) \ for((var) = LIST_FIRST(head); \ (var)!= LIST_END(head); \ (var) = LIST_NEXT(var, field)) #define LIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = LIST_FIRST(head); \ (var) && ((tvar) = LIST_NEXT(var, field), 1); \ (var) = (tvar)) /* * List functions. */ #define LIST_INIT(head) do { \ LIST_FIRST(head) = LIST_END(head); \ } while (0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } while (0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.le_prev = (listelm)->field.le_prev; \ (elm)->field.le_next = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &(elm)->field.le_next; \ } while (0) #define LIST_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (0) #define LIST_REMOVE(elm, field) do { \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) #define LIST_REPLACE(elm, elm2, field) do { \ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ (elm2)->field.le_next->field.le_prev = \ &(elm2)->field.le_next; \ (elm2)->field.le_prev = (elm)->field.le_prev; \ *(elm2)->field.le_prev = (elm2); \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) /* * Simple queue definitions. */ #define SIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqh_first; /* first element */ \ struct type **sqh_last; /* addr of last next element */ \ } #define SIMPLEQ_HEAD_INITIALIZER(head) \ { NULL, &(head).sqh_first } #define SIMPLEQ_ENTRY(type) \ struct { \ struct type *sqe_next; /* next element */ \ } /* * Simple queue access methods. */ #define SIMPLEQ_FIRST(head) ((head)->sqh_first) #define SIMPLEQ_END(head) NULL #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) #define SIMPLEQ_FOREACH(var, head, field) \ for((var) = SIMPLEQ_FIRST(head); \ (var) != SIMPLEQ_END(head); \ (var) = SIMPLEQ_NEXT(var, field)) #define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SIMPLEQ_FIRST(head); \ (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ (var) = (tvar)) /* * Simple queue functions. */ #define SIMPLEQ_INIT(head) do { \ (head)->sqh_first = NULL; \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ (head)->sqh_first = (elm); \ } while (0) #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqe_next = NULL; \ *(head)->sqh_last = (elm); \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ (head)->sqh_last = &(elm)->field.sqe_next; \ (listelm)->field.sqe_next = (elm); \ } while (0) #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) #define SIMPLEQ_CONCAT(head1, head2) do { \ if (!SIMPLEQ_EMPTY((head2))) { \ *(head1)->sqh_last = (head2)->sqh_first; \ (head1)->sqh_last = (head2)->sqh_last; \ SIMPLEQ_INIT((head2)); \ } \ } while (0) /* * XOR Simple queue definitions. */ #define XSIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqx_first; /* first element */ \ struct type **sqx_last; /* addr of last next element */ \ unsigned long sqx_cookie; \ } #define XSIMPLEQ_ENTRY(type) \ struct { \ struct type *sqx_next; /* next element */ \ } /* * XOR Simple queue access methods. */ #define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ (unsigned long)(ptr))) #define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) #define XSIMPLEQ_END(head) NULL #define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) #define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) #define XSIMPLEQ_FOREACH(var, head, field) \ for ((var) = XSIMPLEQ_FIRST(head); \ (var) != XSIMPLEQ_END(head); \ (var) = XSIMPLEQ_NEXT(head, var, field)) #define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = XSIMPLEQ_FIRST(head); \ (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ (var) = (tvar)) /* * XOR Simple queue functions. */ #define XSIMPLEQ_INIT(head) do { \ arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ } while (0) #define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqx_next = (head)->sqx_first) == \ XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ } while (0) #define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ } while (0) #define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ } while (0) #define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ } while (0) #define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ (elm)->field.sqx_next)->field.sqx_next) \ == XSIMPLEQ_XOR(head, NULL)) \ (head)->sqx_last = \ XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ } while (0) /* * Tail queue definitions. */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ } /* * Tail queue access methods. */ #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_END(head) NULL #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) /* XXX */ #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_EMPTY(head) \ (TAILQ_FIRST(head) == TAILQ_END(head)) #define TAILQ_FOREACH(var, head, field) \ for((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head); \ (var) = TAILQ_NEXT(var, field)) #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head) && \ ((tvar) = TAILQ_NEXT(var, field), 1); \ (var) = (tvar)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head); \ (var) = TAILQ_PREV(var, headname, field)) #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ for ((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head) && \ ((tvar) = TAILQ_PREV(var, headname, field), 1); \ (var) = (tvar)) /* * Tail queue functions. */ #define TAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_REMOVE(head, elm, field) do { \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) #define TAILQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ (elm2)->field.tqe_next->field.tqe_prev = \ &(elm2)->field.tqe_next; \ else \ (head)->tqh_last = &(elm2)->field.tqe_next; \ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ *(elm2)->field.tqe_prev = (elm2); \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) #define TAILQ_CONCAT(head1, head2, field) do { \ if (!TAILQ_EMPTY(head2)) { \ *(head1)->tqh_last = (head2)->tqh_first; \ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ (head1)->tqh_last = (head2)->tqh_last; \ TAILQ_INIT((head2)); \ } \ } while (0) /* * Singly-linked Tail queue declarations. */ #define STAILQ_HEAD(name, type) \ struct name { \ struct type *stqh_first; /* first element */ \ struct type **stqh_last; /* addr of last next element */ \ } #define STAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).stqh_first } #define STAILQ_ENTRY(type) \ struct { \ struct type *stqe_next; /* next element */ \ } /* * Singly-linked Tail queue access methods. */ #define STAILQ_FIRST(head) ((head)->stqh_first) #define STAILQ_END(head) NULL #define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head)) #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) #define STAILQ_FOREACH(var, head, field) \ for ((var) = STAILQ_FIRST(head); \ (var) != STAILQ_END(head); \ (var) = STAILQ_NEXT(var, field)) #define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = STAILQ_FIRST(head); \ (var) && ((tvar) = STAILQ_NEXT(var, field), 1); \ (var) = (tvar)) /* * Singly-linked Tail queue functions. */ #define STAILQ_INIT(head) do { \ STAILQ_FIRST((head)) = NULL; \ (head)->stqh_last = &STAILQ_FIRST((head)); \ } while (0) #define STAILQ_INSERT_HEAD(head, elm, field) do { \ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ STAILQ_FIRST((head)) = (elm); \ } while (0) #define STAILQ_INSERT_TAIL(head, elm, field) do { \ STAILQ_NEXT((elm), field) = NULL; \ *(head)->stqh_last = (elm); \ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ } while (0) #define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((elm), field)) == NULL)\ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ STAILQ_NEXT((elm), field) = (elm); \ } while (0) #define STAILQ_REMOVE_HEAD(head, field) do { \ if ((STAILQ_FIRST((head)) = \ STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ (head)->stqh_last = &STAILQ_FIRST((head)); \ } while (0) #define STAILQ_REMOVE_AFTER(head, elm, field) do { \ if ((STAILQ_NEXT(elm, field) = \ STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ (head)->stqh_last = &STAILQ_NEXT((elm), field); \ } while (0) #define STAILQ_REMOVE(head, elm, type, field) do { \ if (STAILQ_FIRST((head)) == (elm)) { \ STAILQ_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->stqh_first; \ while (STAILQ_NEXT(curelm, field) != (elm)) \ curelm = STAILQ_NEXT(curelm, field); \ STAILQ_REMOVE_AFTER(head, curelm, field); \ } \ } while (0) #define STAILQ_CONCAT(head1, head2) do { \ if (!STAILQ_EMPTY((head2))) { \ *(head1)->stqh_last = (head2)->stqh_first; \ (head1)->stqh_last = (head2)->stqh_last; \ STAILQ_INIT((head2)); \ } \ } while (0) #define STAILQ_LAST(head, type, field) \ (STAILQ_EMPTY((head)) ? NULL : \ ((struct type *)(void *) \ ((char *)((head)->stqh_last) - offsetof(struct type, field)))) #endif /* !_SYS_QUEUE_H_ */ openiked-7.4/compat/sys/sysctl.h000066400000000000000000000004441477554731100167750ustar00rootroot00000000000000/* * Public domain * sys/sysctl.h compatibility shim */ #if defined _MSC_VER #ifndef IKED_COMPAT_SYS_SYSCTL_H #define IKED_COMPAT_SYS_SYSCTL_H /* XXX */ #endif /* !IKED_COMPAT_SYS_SYSCTL_H */ #elif defined __linux__ #include #else #include_next #endif openiked-7.4/compat/sys/tree.h000066400000000000000000001020431477554731100164110ustar00rootroot00000000000000/* $OpenBSD: tree.h,v 1.31 2023/03/08 04:43:09 guenther Exp $ */ /* * Copyright 2002 Niels Provos * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #ifndef _SYS_TREE_H_ #define _SYS_TREE_H_ #include /* * This file defines data structures for different types of trees: * splay trees and red-black trees. * * A splay tree is a self-organizing data structure. Every operation * on the tree causes a splay to happen. The splay moves the requested * node to the root of the tree and partly rebalances it. * * This has the benefit that request locality causes faster lookups as * the requested nodes move to the top of the tree. On the other hand, * every lookup causes memory writes. * * The Balance Theorem bounds the total access time for m operations * and n inserts on an initially empty tree as O((m + n)lg n). The * amortized cost for a sequence of m accesses to a splay tree is O(lg n); * * A red-black tree is a binary search tree with the node color as an * extra attribute. It fulfills a set of conditions: * - every search path from the root to a leaf consists of the * same number of black nodes, * - each red node (except for the root) has a black parent, * - each leaf node is black. * * Every operation on a red-black tree is bounded as O(lg n). * The maximum height of a red-black tree is 2lg (n+1). */ #define SPLAY_HEAD(name, type) \ struct name { \ struct type *sph_root; /* root of the tree */ \ } #define SPLAY_INITIALIZER(root) \ { NULL } #define SPLAY_INIT(root) do { \ (root)->sph_root = NULL; \ } while (0) #define SPLAY_ENTRY(type) \ struct { \ struct type *spe_left; /* left element */ \ struct type *spe_right; /* right element */ \ } #define SPLAY_LEFT(elm, field) (elm)->field.spe_left #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right #define SPLAY_ROOT(head) (head)->sph_root #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_LINKLEFT(head, tmp, field) do { \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ } while (0) #define SPLAY_LINKRIGHT(head, tmp, field) do { \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } while (0) #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ } while (0) /* Generates prototypes and inline functions */ #define SPLAY_PROTOTYPE(name, type, field, cmp) \ void name##_SPLAY(struct name *, struct type *); \ void name##_SPLAY_MINMAX(struct name *, int); \ struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ \ /* Finds the node with the same key as elm */ \ static __unused __inline struct type * \ name##_SPLAY_FIND(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) \ return(NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) \ return (head->sph_root); \ return (NULL); \ } \ \ static __unused __inline struct type * \ name##_SPLAY_NEXT(struct name *head, struct type *elm) \ { \ name##_SPLAY(head, elm); \ if (SPLAY_RIGHT(elm, field) != NULL) { \ elm = SPLAY_RIGHT(elm, field); \ while (SPLAY_LEFT(elm, field) != NULL) { \ elm = SPLAY_LEFT(elm, field); \ } \ } else \ elm = NULL; \ return (elm); \ } \ \ static __unused __inline struct type * \ name##_SPLAY_MIN_MAX(struct name *head, int val) \ { \ name##_SPLAY_MINMAX(head, val); \ return (SPLAY_ROOT(head)); \ } /* Main splay operation. * Moves node close to the key of elm to top */ #define SPLAY_GENERATE(name, type, field, cmp) \ struct type * \ name##_SPLAY_INSERT(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) { \ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ } else { \ int __comp; \ name##_SPLAY(head, elm); \ __comp = (cmp)(elm, (head)->sph_root); \ if(__comp < 0) { \ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ SPLAY_RIGHT(elm, field) = (head)->sph_root; \ SPLAY_LEFT((head)->sph_root, field) = NULL; \ } else if (__comp > 0) { \ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT(elm, field) = (head)->sph_root; \ SPLAY_RIGHT((head)->sph_root, field) = NULL; \ } else \ return ((head)->sph_root); \ } \ (head)->sph_root = (elm); \ return (NULL); \ } \ \ struct type * \ name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ { \ struct type *__tmp; \ if (SPLAY_EMPTY(head)) \ return (NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) { \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ } else { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ name##_SPLAY(head, elm); \ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ } \ return (elm); \ } \ return (NULL); \ } \ \ void \ name##_SPLAY(struct name *head, struct type *elm) \ { \ struct type __node, *__left, *__right, *__tmp; \ int __comp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while ((__comp = (cmp)(elm, (head)->sph_root))) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) > 0){ \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } \ \ /* Splay with either the minimum or the maximum element \ * Used to find minimum or maximum element in tree. \ */ \ void name##_SPLAY_MINMAX(struct name *head, int __comp) \ { \ struct type __node, *__left, *__right, *__tmp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while (1) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp > 0) { \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } #define SPLAY_NEGINF -1 #define SPLAY_INF 1 #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) #define SPLAY_FOREACH(x, name, head) \ for ((x) = SPLAY_MIN(name, head); \ (x) != NULL; \ (x) = SPLAY_NEXT(name, head, x)) /* Macros that define a red-black tree */ #define RB_HEAD(name, type) \ struct name { \ struct type *rbh_root; /* root of the tree */ \ } #define RB_INITIALIZER(root) \ { NULL } #define RB_INIT(root) do { \ (root)->rbh_root = NULL; \ } while (0) #define RB_BLACK 0 #define RB_RED 1 #define RB_ENTRY(type) \ struct { \ struct type *rbe_left; /* left element */ \ struct type *rbe_right; /* right element */ \ struct type *rbe_parent; /* parent element */ \ int rbe_color; /* node color */ \ } #define RB_LEFT(elm, field) (elm)->field.rbe_left #define RB_RIGHT(elm, field) (elm)->field.rbe_right #define RB_PARENT(elm, field) (elm)->field.rbe_parent #define RB_COLOR(elm, field) (elm)->field.rbe_color #define RB_ROOT(head) (head)->rbh_root #define RB_EMPTY(head) (RB_ROOT(head) == NULL) #define RB_SET(elm, parent, field) do { \ RB_PARENT(elm, field) = parent; \ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ RB_COLOR(elm, field) = RB_RED; \ } while (0) #define RB_SET_BLACKRED(black, red, field) do { \ RB_COLOR(black, field) = RB_BLACK; \ RB_COLOR(red, field) = RB_RED; \ } while (0) #ifndef RB_AUGMENT #define RB_AUGMENT(x) do {} while (0) #endif #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ (tmp) = RB_RIGHT(elm, field); \ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_LEFT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ (tmp) = RB_LEFT(elm, field); \ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_RIGHT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) /* Generates prototypes and inline functions */ #define RB_PROTOTYPE(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) #define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ attr struct type *name##_RB_INSERT(struct name *, struct type *); \ attr struct type *name##_RB_FIND(struct name *, struct type *); \ attr struct type *name##_RB_NFIND(struct name *, struct type *); \ attr struct type *name##_RB_NEXT(struct type *); \ attr struct type *name##_RB_PREV(struct type *); \ attr struct type *name##_RB_MINMAX(struct name *, int); \ \ /* Main rb operation. * Moves node close to the key of elm to top */ #define RB_GENERATE(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp,) #define RB_GENERATE_STATIC(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ attr void \ name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ { \ struct type *parent, *gparent, *tmp; \ while ((parent = RB_PARENT(elm, field)) && \ RB_COLOR(parent, field) == RB_RED) { \ gparent = RB_PARENT(parent, field); \ if (parent == RB_LEFT(gparent, field)) { \ tmp = RB_RIGHT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_RIGHT(parent, field) == elm) { \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_RIGHT(head, gparent, tmp, field); \ } else { \ tmp = RB_LEFT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_LEFT(parent, field) == elm) { \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_LEFT(head, gparent, tmp, field); \ } \ } \ RB_COLOR(head->rbh_root, field) = RB_BLACK; \ } \ \ attr void \ name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ { \ struct type *tmp; \ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ elm != RB_ROOT(head)) { \ if (RB_LEFT(parent, field) == elm) { \ tmp = RB_RIGHT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = RB_RIGHT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ struct type *oleft; \ if ((oleft = RB_LEFT(tmp, field)))\ RB_COLOR(oleft, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_RIGHT(head, tmp, oleft, field);\ tmp = RB_RIGHT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_RIGHT(tmp, field)) \ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_LEFT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } else { \ tmp = RB_LEFT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = RB_LEFT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ struct type *oright; \ if ((oright = RB_RIGHT(tmp, field)))\ RB_COLOR(oright, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_LEFT(head, tmp, oright, field);\ tmp = RB_LEFT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_LEFT(tmp, field)) \ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_RIGHT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } \ } \ if (elm) \ RB_COLOR(elm, field) = RB_BLACK; \ } \ \ attr struct type * \ name##_RB_REMOVE(struct name *head, struct type *elm) \ { \ struct type *child, *parent, *old = elm; \ int color; \ if (RB_LEFT(elm, field) == NULL) \ child = RB_RIGHT(elm, field); \ else if (RB_RIGHT(elm, field) == NULL) \ child = RB_LEFT(elm, field); \ else { \ struct type *left; \ elm = RB_RIGHT(elm, field); \ while ((left = RB_LEFT(elm, field))) \ elm = left; \ child = RB_RIGHT(elm, field); \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ if (RB_PARENT(elm, field) == old) \ parent = elm; \ (elm)->field = (old)->field; \ if (RB_PARENT(old, field)) { \ if (RB_LEFT(RB_PARENT(old, field), field) == old)\ RB_LEFT(RB_PARENT(old, field), field) = elm;\ else \ RB_RIGHT(RB_PARENT(old, field), field) = elm;\ RB_AUGMENT(RB_PARENT(old, field)); \ } else \ RB_ROOT(head) = elm; \ RB_PARENT(RB_LEFT(old, field), field) = elm; \ if (RB_RIGHT(old, field)) \ RB_PARENT(RB_RIGHT(old, field), field) = elm; \ if (parent) { \ left = parent; \ do { \ RB_AUGMENT(left); \ } while ((left = RB_PARENT(left, field))); \ } \ goto color; \ } \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ color: \ if (color == RB_BLACK) \ name##_RB_REMOVE_COLOR(head, parent, child); \ return (old); \ } \ \ /* Inserts a node into the RB tree */ \ attr struct type * \ name##_RB_INSERT(struct name *head, struct type *elm) \ { \ struct type *tmp; \ struct type *parent = NULL; \ int comp = 0; \ tmp = RB_ROOT(head); \ while (tmp) { \ parent = tmp; \ comp = (cmp)(elm, parent); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ RB_SET(elm, parent, field); \ if (parent != NULL) { \ if (comp < 0) \ RB_LEFT(parent, field) = elm; \ else \ RB_RIGHT(parent, field) = elm; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = elm; \ name##_RB_INSERT_COLOR(head, elm); \ return (NULL); \ } \ \ /* Finds the node with the same key as elm */ \ attr struct type * \ name##_RB_FIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (NULL); \ } \ \ /* Finds the first node greater than or equal to the search key */ \ attr struct type * \ name##_RB_NFIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *res = NULL; \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) { \ res = tmp; \ tmp = RB_LEFT(tmp, field); \ } \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (res); \ } \ \ attr struct type * \ name##_RB_NEXT(struct type *elm) \ { \ if (RB_RIGHT(elm, field)) { \ elm = RB_RIGHT(elm, field); \ while (RB_LEFT(elm, field)) \ elm = RB_LEFT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ attr struct type * \ name##_RB_PREV(struct type *elm) \ { \ if (RB_LEFT(elm, field)) { \ elm = RB_LEFT(elm, field); \ while (RB_RIGHT(elm, field)) \ elm = RB_RIGHT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ attr struct type * \ name##_RB_MINMAX(struct name *head, int val) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *parent = NULL; \ while (tmp) { \ parent = tmp; \ if (val < 0) \ tmp = RB_LEFT(tmp, field); \ else \ tmp = RB_RIGHT(tmp, field); \ } \ return (parent); \ } #define RB_NEGINF -1 #define RB_INF 1 #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) #define RB_FIND(name, x, y) name##_RB_FIND(x, y) #define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) #define RB_NEXT(name, x, y) name##_RB_NEXT(y) #define RB_PREV(name, x, y) name##_RB_PREV(y) #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) #define RB_FOREACH(x, name, head) \ for ((x) = RB_MIN(name, head); \ (x) != NULL; \ (x) = name##_RB_NEXT(x)) #define RB_FOREACH_SAFE(x, name, head, y) \ for ((x) = RB_MIN(name, head); \ ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ (x) = (y)) #define RB_FOREACH_REVERSE(x, name, head) \ for ((x) = RB_MAX(name, head); \ (x) != NULL; \ (x) = name##_RB_PREV(x)) #define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ for ((x) = RB_MAX(name, head); \ ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ (x) = (y)) /* * Copyright (c) 2016 David Gwynne * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ struct rb_type { int (*t_compare)(const void *, const void *); void (*t_augment)(void *); unsigned int t_offset; /* offset of rb_entry in type */ }; struct rb_tree { struct rb_entry *rbt_root; }; struct rb_entry { struct rb_entry *rbt_parent; struct rb_entry *rbt_left; struct rb_entry *rbt_right; unsigned int rbt_color; }; #define RBT_HEAD(_name, _type) \ struct _name { \ struct rb_tree rbh_root; \ } #define RBT_ENTRY(_type) struct rb_entry static inline void _rb_init(struct rb_tree *rbt) { rbt->rbt_root = NULL; } static inline int _rb_empty(struct rb_tree *rbt) { return (rbt->rbt_root == NULL); } void *_rb_insert(const struct rb_type *, struct rb_tree *, void *); void *_rb_remove(const struct rb_type *, struct rb_tree *, void *); void *_rb_find(const struct rb_type *, struct rb_tree *, const void *); void *_rb_nfind(const struct rb_type *, struct rb_tree *, const void *); void *_rb_root(const struct rb_type *, struct rb_tree *); void *_rb_min(const struct rb_type *, struct rb_tree *); void *_rb_max(const struct rb_type *, struct rb_tree *); void *_rb_next(const struct rb_type *, void *); void *_rb_prev(const struct rb_type *, void *); void *_rb_left(const struct rb_type *, void *); void *_rb_right(const struct rb_type *, void *); void *_rb_parent(const struct rb_type *, void *); void _rb_set_left(const struct rb_type *, void *, void *); void _rb_set_right(const struct rb_type *, void *, void *); void _rb_set_parent(const struct rb_type *, void *, void *); void _rb_poison(const struct rb_type *, void *, unsigned long); int _rb_check(const struct rb_type *, void *, unsigned long); #define RBT_INITIALIZER(_head) { { NULL } } #define RBT_PROTOTYPE(_name, _type, _field, _cmp) \ extern const struct rb_type *const _name##_RBT_TYPE; \ \ __unused static inline void \ _name##_RBT_INIT(struct _name *head) \ { \ _rb_init(&head->rbh_root); \ } \ \ __unused static inline struct _type * \ _name##_RBT_INSERT(struct _name *head, struct _type *elm) \ { \ return _rb_insert(_name##_RBT_TYPE, &head->rbh_root, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_REMOVE(struct _name *head, struct _type *elm) \ { \ return _rb_remove(_name##_RBT_TYPE, &head->rbh_root, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_FIND(struct _name *head, const struct _type *key) \ { \ return _rb_find(_name##_RBT_TYPE, &head->rbh_root, key); \ } \ \ __unused static inline struct _type * \ _name##_RBT_NFIND(struct _name *head, const struct _type *key) \ { \ return _rb_nfind(_name##_RBT_TYPE, &head->rbh_root, key); \ } \ \ __unused static inline struct _type * \ _name##_RBT_ROOT(struct _name *head) \ { \ return _rb_root(_name##_RBT_TYPE, &head->rbh_root); \ } \ \ __unused static inline int \ _name##_RBT_EMPTY(struct _name *head) \ { \ return _rb_empty(&head->rbh_root); \ } \ \ __unused static inline struct _type * \ _name##_RBT_MIN(struct _name *head) \ { \ return _rb_min(_name##_RBT_TYPE, &head->rbh_root); \ } \ \ __unused static inline struct _type * \ _name##_RBT_MAX(struct _name *head) \ { \ return _rb_max(_name##_RBT_TYPE, &head->rbh_root); \ } \ \ __unused static inline struct _type * \ _name##_RBT_NEXT(struct _type *elm) \ { \ return _rb_next(_name##_RBT_TYPE, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_PREV(struct _type *elm) \ { \ return _rb_prev(_name##_RBT_TYPE, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_LEFT(struct _type *elm) \ { \ return _rb_left(_name##_RBT_TYPE, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_RIGHT(struct _type *elm) \ { \ return _rb_right(_name##_RBT_TYPE, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_PARENT(struct _type *elm) \ { \ return _rb_parent(_name##_RBT_TYPE, elm); \ } \ \ __unused static inline void \ _name##_RBT_SET_LEFT(struct _type *elm, struct _type *left) \ { \ _rb_set_left(_name##_RBT_TYPE, elm, left); \ } \ \ __unused static inline void \ _name##_RBT_SET_RIGHT(struct _type *elm, struct _type *right) \ { \ _rb_set_right(_name##_RBT_TYPE, elm, right); \ } \ \ __unused static inline void \ _name##_RBT_SET_PARENT(struct _type *elm, struct _type *parent) \ { \ _rb_set_parent(_name##_RBT_TYPE, elm, parent); \ } \ \ __unused static inline void \ _name##_RBT_POISON(struct _type *elm, unsigned long poison) \ { \ _rb_poison(_name##_RBT_TYPE, elm, poison); \ } \ \ __unused static inline int \ _name##_RBT_CHECK(struct _type *elm, unsigned long poison) \ { \ return _rb_check(_name##_RBT_TYPE, elm, poison); \ } #define RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _aug) \ static int \ _name##_RBT_COMPARE(const void *lptr, const void *rptr) \ { \ const struct _type *l = lptr, *r = rptr; \ return _cmp(l, r); \ } \ static const struct rb_type _name##_RBT_INFO = { \ _name##_RBT_COMPARE, \ _aug, \ offsetof(struct _type, _field), \ }; \ const struct rb_type *const _name##_RBT_TYPE = &_name##_RBT_INFO #define RBT_GENERATE_AUGMENT(_name, _type, _field, _cmp, _aug) \ static void \ _name##_RBT_AUGMENT(void *ptr) \ { \ struct _type *p = ptr; \ return _aug(p); \ } \ RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _name##_RBT_AUGMENT) #define RBT_GENERATE(_name, _type, _field, _cmp) \ RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, NULL) #define RBT_INIT(_name, _head) _name##_RBT_INIT(_head) #define RBT_INSERT(_name, _head, _elm) _name##_RBT_INSERT(_head, _elm) #define RBT_REMOVE(_name, _head, _elm) _name##_RBT_REMOVE(_head, _elm) #define RBT_FIND(_name, _head, _key) _name##_RBT_FIND(_head, _key) #define RBT_NFIND(_name, _head, _key) _name##_RBT_NFIND(_head, _key) #define RBT_ROOT(_name, _head) _name##_RBT_ROOT(_head) #define RBT_EMPTY(_name, _head) _name##_RBT_EMPTY(_head) #define RBT_MIN(_name, _head) _name##_RBT_MIN(_head) #define RBT_MAX(_name, _head) _name##_RBT_MAX(_head) #define RBT_NEXT(_name, _elm) _name##_RBT_NEXT(_elm) #define RBT_PREV(_name, _elm) _name##_RBT_PREV(_elm) #define RBT_LEFT(_name, _elm) _name##_RBT_LEFT(_elm) #define RBT_RIGHT(_name, _elm) _name##_RBT_RIGHT(_elm) #define RBT_PARENT(_name, _elm) _name##_RBT_PARENT(_elm) #define RBT_SET_LEFT(_name, _elm, _l) _name##_RBT_SET_LEFT(_elm, _l) #define RBT_SET_RIGHT(_name, _elm, _r) _name##_RBT_SET_RIGHT(_elm, _r) #define RBT_SET_PARENT(_name, _elm, _p) _name##_RBT_SET_PARENT(_elm, _p) #define RBT_POISON(_name, _elm, _p) _name##_RBT_POISON(_elm, _p) #define RBT_CHECK(_name, _elm, _p) _name##_RBT_CHECK(_elm, _p) #define RBT_FOREACH(_e, _name, _head) \ for ((_e) = RBT_MIN(_name, (_head)); \ (_e) != NULL; \ (_e) = RBT_NEXT(_name, (_e))) #define RBT_FOREACH_SAFE(_e, _name, _head, _n) \ for ((_e) = RBT_MIN(_name, (_head)); \ (_e) != NULL && ((_n) = RBT_NEXT(_name, (_e)), 1); \ (_e) = (_n)) #define RBT_FOREACH_REVERSE(_e, _name, _head) \ for ((_e) = RBT_MAX(_name, (_head)); \ (_e) != NULL; \ (_e) = RBT_PREV(_name, (_e))) #define RBT_FOREACH_REVERSE_SAFE(_e, _name, _head, _n) \ for ((_e) = RBT_MAX(_name, (_head)); \ (_e) != NULL && ((_n) = RBT_PREV(_name, (_e)), 1); \ (_e) = (_n)) #endif /* _SYS_TREE_H_ */ openiked-7.4/compat/sys/types.h000066400000000000000000000027541477554731100166260ustar00rootroot00000000000000/* * Public domain * sys/types.h compatibility shim */ #ifdef _MSC_VER #if _MSC_VER >= 1900 #include <../ucrt/sys/types.h> #else #include <../include/sys/types.h> #endif #else #include_next #endif #ifndef IKED_COMPAT_SYS_TYPES_H #define IKED_COMPAT_SYS_TYPES_H #include #ifdef __MINGW32__ #include <_bsd_types.h> typedef uint32_t in_addr_t; typedef uint32_t uid_t; #endif #ifdef _MSC_VER typedef unsigned char u_char; typedef unsigned short u_short; typedef unsigned int u_int; typedef uint32_t in_addr_t; typedef uint32_t mode_t; typedef uint32_t uid_t; #include typedef SSIZE_T ssize_t; #ifndef SSIZE_MAX #ifdef _WIN64 #define SSIZE_MAX _I64_MAX #else #define SSIZE_MAX INT_MAX #endif #endif #endif #if !defined(HAVE_ATTRIBUTE__BOUNDED__) && !defined(__bounded__) # define __bounded__(x, y, z) #endif #if !defined(HAVE_ATTRIBUTE__DEAD) && !defined(__dead) #ifdef _MSC_VER #define __dead __declspec(noreturn) #else #define __dead __attribute__((__noreturn__)) #endif #endif #ifdef _WIN32 #define __warn_references(sym,msg) #else #ifndef __warn_references #ifndef __STRING #define __STRING(x) #x #endif #if defined(__GNUC__) && defined (HAS_GNU_WARNING_LONG) #define __warn_references(sym,msg) \ __asm__(".section .gnu.warning." __STRING(sym) \ "\n\t.ascii \"" msg "\"\n\t.text"); #else #define __warn_references(sym,msg) #endif #endif /* __warn_references */ #endif /* _WIN32 */ #endif openiked-7.4/compat/sys/uio.h000066400000000000000000000006301477554731100162450ustar00rootroot00000000000000/* * Public domain * sys/uio.h compatibility shim */ #ifndef _MSC_VER #include_next #else #ifndef IKED_COMPAT_SYS_UIO_H #define IKED_COMPAT_SYS_UIO_H #define IOV_MAX 16 /* XXX */ struct iovec { void *iov_base; /* Base address. */ size_t iov_len; /* Length. */ }; /* needs to be converted to WSABUF with buf/len attributes */ #endif /* !IKED_COMPAT_SYS_UIO_H */ #endif openiked-7.4/compat/sys/un.h000066400000000000000000000003461477554731100160770ustar00rootroot00000000000000/* * Public domain * sys/types.h compatibility shim */ #ifndef _MSC_VER #include_next #else #ifndef IKED_COMPAT_SYS_UN_H #define IKED_COMPAT_SYS_UN_H #include #endif /* !IKED_COMPAT_SYS_UN_H */ #endif openiked-7.4/compat/sys/wait.h000066400000000000000000000003461477554731100164210ustar00rootroot00000000000000/* * Public domain * sys/wait.h compatibility shim */ #ifndef _MSC_VER #include_next #else #ifndef IKED_COMPAT_SYS_WAIT_H #define IKED_COMPAT_SYS_WAIT_H /* FIXME */ #endif /* !IKED_COMPAT_SYS_WAIT_H */ #endif openiked-7.4/compat/timingsafe_bcmp.c000066400000000000000000000022561477554731100177630ustar00rootroot00000000000000/* $OpenBSD: timingsafe_bcmp.c,v 1.3 2015/08/31 02:53:57 guenther Exp $ */ /* * Copyright (c) 2010 Damien Miller. All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* OPENBSD ORIGINAL: lib/libc/string/timingsafe_bcmp.c */ #include "includes.h" #ifndef HAVE_TIMINGSAFE_BCMP int timingsafe_bcmp(const void *b1, const void *b2, size_t n) { const unsigned char *p1 = b1, *p2 = b2; int ret = 0; for (; n > 0; n--) ret |= *p1++ ^ *p2++; return (ret != 0); } #endif /* TIMINGSAFE_BCMP */ openiked-7.4/compat/tree.h000066400000000000000000001020431477554731100155730ustar00rootroot00000000000000/* $OpenBSD: tree.h,v 1.31 2023/03/08 04:43:09 guenther Exp $ */ /* * Copyright 2002 Niels Provos * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ #ifndef _SYS_TREE_H_ #define _SYS_TREE_H_ #include /* * This file defines data structures for different types of trees: * splay trees and red-black trees. * * A splay tree is a self-organizing data structure. Every operation * on the tree causes a splay to happen. The splay moves the requested * node to the root of the tree and partly rebalances it. * * This has the benefit that request locality causes faster lookups as * the requested nodes move to the top of the tree. On the other hand, * every lookup causes memory writes. * * The Balance Theorem bounds the total access time for m operations * and n inserts on an initially empty tree as O((m + n)lg n). The * amortized cost for a sequence of m accesses to a splay tree is O(lg n); * * A red-black tree is a binary search tree with the node color as an * extra attribute. It fulfills a set of conditions: * - every search path from the root to a leaf consists of the * same number of black nodes, * - each red node (except for the root) has a black parent, * - each leaf node is black. * * Every operation on a red-black tree is bounded as O(lg n). * The maximum height of a red-black tree is 2lg (n+1). */ #define SPLAY_HEAD(name, type) \ struct name { \ struct type *sph_root; /* root of the tree */ \ } #define SPLAY_INITIALIZER(root) \ { NULL } #define SPLAY_INIT(root) do { \ (root)->sph_root = NULL; \ } while (0) #define SPLAY_ENTRY(type) \ struct { \ struct type *spe_left; /* left element */ \ struct type *spe_right; /* right element */ \ } #define SPLAY_LEFT(elm, field) (elm)->field.spe_left #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right #define SPLAY_ROOT(head) (head)->sph_root #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_LINKLEFT(head, tmp, field) do { \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ } while (0) #define SPLAY_LINKRIGHT(head, tmp, field) do { \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } while (0) #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ } while (0) /* Generates prototypes and inline functions */ #define SPLAY_PROTOTYPE(name, type, field, cmp) \ void name##_SPLAY(struct name *, struct type *); \ void name##_SPLAY_MINMAX(struct name *, int); \ struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ \ /* Finds the node with the same key as elm */ \ static __unused __inline struct type * \ name##_SPLAY_FIND(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) \ return(NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) \ return (head->sph_root); \ return (NULL); \ } \ \ static __unused __inline struct type * \ name##_SPLAY_NEXT(struct name *head, struct type *elm) \ { \ name##_SPLAY(head, elm); \ if (SPLAY_RIGHT(elm, field) != NULL) { \ elm = SPLAY_RIGHT(elm, field); \ while (SPLAY_LEFT(elm, field) != NULL) { \ elm = SPLAY_LEFT(elm, field); \ } \ } else \ elm = NULL; \ return (elm); \ } \ \ static __unused __inline struct type * \ name##_SPLAY_MIN_MAX(struct name *head, int val) \ { \ name##_SPLAY_MINMAX(head, val); \ return (SPLAY_ROOT(head)); \ } /* Main splay operation. * Moves node close to the key of elm to top */ #define SPLAY_GENERATE(name, type, field, cmp) \ struct type * \ name##_SPLAY_INSERT(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) { \ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ } else { \ int __comp; \ name##_SPLAY(head, elm); \ __comp = (cmp)(elm, (head)->sph_root); \ if(__comp < 0) { \ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ SPLAY_RIGHT(elm, field) = (head)->sph_root; \ SPLAY_LEFT((head)->sph_root, field) = NULL; \ } else if (__comp > 0) { \ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT(elm, field) = (head)->sph_root; \ SPLAY_RIGHT((head)->sph_root, field) = NULL; \ } else \ return ((head)->sph_root); \ } \ (head)->sph_root = (elm); \ return (NULL); \ } \ \ struct type * \ name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ { \ struct type *__tmp; \ if (SPLAY_EMPTY(head)) \ return (NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) { \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ } else { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ name##_SPLAY(head, elm); \ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ } \ return (elm); \ } \ return (NULL); \ } \ \ void \ name##_SPLAY(struct name *head, struct type *elm) \ { \ struct type __node, *__left, *__right, *__tmp; \ int __comp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while ((__comp = (cmp)(elm, (head)->sph_root))) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) > 0){ \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } \ \ /* Splay with either the minimum or the maximum element \ * Used to find minimum or maximum element in tree. \ */ \ void name##_SPLAY_MINMAX(struct name *head, int __comp) \ { \ struct type __node, *__left, *__right, *__tmp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while (1) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp > 0) { \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } #define SPLAY_NEGINF -1 #define SPLAY_INF 1 #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) #define SPLAY_FOREACH(x, name, head) \ for ((x) = SPLAY_MIN(name, head); \ (x) != NULL; \ (x) = SPLAY_NEXT(name, head, x)) /* Macros that define a red-black tree */ #define RB_HEAD(name, type) \ struct name { \ struct type *rbh_root; /* root of the tree */ \ } #define RB_INITIALIZER(root) \ { NULL } #define RB_INIT(root) do { \ (root)->rbh_root = NULL; \ } while (0) #define RB_BLACK 0 #define RB_RED 1 #define RB_ENTRY(type) \ struct { \ struct type *rbe_left; /* left element */ \ struct type *rbe_right; /* right element */ \ struct type *rbe_parent; /* parent element */ \ int rbe_color; /* node color */ \ } #define RB_LEFT(elm, field) (elm)->field.rbe_left #define RB_RIGHT(elm, field) (elm)->field.rbe_right #define RB_PARENT(elm, field) (elm)->field.rbe_parent #define RB_COLOR(elm, field) (elm)->field.rbe_color #define RB_ROOT(head) (head)->rbh_root #define RB_EMPTY(head) (RB_ROOT(head) == NULL) #define RB_SET(elm, parent, field) do { \ RB_PARENT(elm, field) = parent; \ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ RB_COLOR(elm, field) = RB_RED; \ } while (0) #define RB_SET_BLACKRED(black, red, field) do { \ RB_COLOR(black, field) = RB_BLACK; \ RB_COLOR(red, field) = RB_RED; \ } while (0) #ifndef RB_AUGMENT #define RB_AUGMENT(x) do {} while (0) #endif #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ (tmp) = RB_RIGHT(elm, field); \ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_LEFT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ (tmp) = RB_LEFT(elm, field); \ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_RIGHT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) /* Generates prototypes and inline functions */ #define RB_PROTOTYPE(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) #define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ attr struct type *name##_RB_INSERT(struct name *, struct type *); \ attr struct type *name##_RB_FIND(struct name *, struct type *); \ attr struct type *name##_RB_NFIND(struct name *, struct type *); \ attr struct type *name##_RB_NEXT(struct type *); \ attr struct type *name##_RB_PREV(struct type *); \ attr struct type *name##_RB_MINMAX(struct name *, int); \ \ /* Main rb operation. * Moves node close to the key of elm to top */ #define RB_GENERATE(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp,) #define RB_GENERATE_STATIC(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ attr void \ name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ { \ struct type *parent, *gparent, *tmp; \ while ((parent = RB_PARENT(elm, field)) && \ RB_COLOR(parent, field) == RB_RED) { \ gparent = RB_PARENT(parent, field); \ if (parent == RB_LEFT(gparent, field)) { \ tmp = RB_RIGHT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_RIGHT(parent, field) == elm) { \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_RIGHT(head, gparent, tmp, field); \ } else { \ tmp = RB_LEFT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_LEFT(parent, field) == elm) { \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_LEFT(head, gparent, tmp, field); \ } \ } \ RB_COLOR(head->rbh_root, field) = RB_BLACK; \ } \ \ attr void \ name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ { \ struct type *tmp; \ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ elm != RB_ROOT(head)) { \ if (RB_LEFT(parent, field) == elm) { \ tmp = RB_RIGHT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = RB_RIGHT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ struct type *oleft; \ if ((oleft = RB_LEFT(tmp, field)))\ RB_COLOR(oleft, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_RIGHT(head, tmp, oleft, field);\ tmp = RB_RIGHT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_RIGHT(tmp, field)) \ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_LEFT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } else { \ tmp = RB_LEFT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = RB_LEFT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ struct type *oright; \ if ((oright = RB_RIGHT(tmp, field)))\ RB_COLOR(oright, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_LEFT(head, tmp, oright, field);\ tmp = RB_LEFT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_LEFT(tmp, field)) \ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_RIGHT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } \ } \ if (elm) \ RB_COLOR(elm, field) = RB_BLACK; \ } \ \ attr struct type * \ name##_RB_REMOVE(struct name *head, struct type *elm) \ { \ struct type *child, *parent, *old = elm; \ int color; \ if (RB_LEFT(elm, field) == NULL) \ child = RB_RIGHT(elm, field); \ else if (RB_RIGHT(elm, field) == NULL) \ child = RB_LEFT(elm, field); \ else { \ struct type *left; \ elm = RB_RIGHT(elm, field); \ while ((left = RB_LEFT(elm, field))) \ elm = left; \ child = RB_RIGHT(elm, field); \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ if (RB_PARENT(elm, field) == old) \ parent = elm; \ (elm)->field = (old)->field; \ if (RB_PARENT(old, field)) { \ if (RB_LEFT(RB_PARENT(old, field), field) == old)\ RB_LEFT(RB_PARENT(old, field), field) = elm;\ else \ RB_RIGHT(RB_PARENT(old, field), field) = elm;\ RB_AUGMENT(RB_PARENT(old, field)); \ } else \ RB_ROOT(head) = elm; \ RB_PARENT(RB_LEFT(old, field), field) = elm; \ if (RB_RIGHT(old, field)) \ RB_PARENT(RB_RIGHT(old, field), field) = elm; \ if (parent) { \ left = parent; \ do { \ RB_AUGMENT(left); \ } while ((left = RB_PARENT(left, field))); \ } \ goto color; \ } \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ color: \ if (color == RB_BLACK) \ name##_RB_REMOVE_COLOR(head, parent, child); \ return (old); \ } \ \ /* Inserts a node into the RB tree */ \ attr struct type * \ name##_RB_INSERT(struct name *head, struct type *elm) \ { \ struct type *tmp; \ struct type *parent = NULL; \ int comp = 0; \ tmp = RB_ROOT(head); \ while (tmp) { \ parent = tmp; \ comp = (cmp)(elm, parent); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ RB_SET(elm, parent, field); \ if (parent != NULL) { \ if (comp < 0) \ RB_LEFT(parent, field) = elm; \ else \ RB_RIGHT(parent, field) = elm; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = elm; \ name##_RB_INSERT_COLOR(head, elm); \ return (NULL); \ } \ \ /* Finds the node with the same key as elm */ \ attr struct type * \ name##_RB_FIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (NULL); \ } \ \ /* Finds the first node greater than or equal to the search key */ \ attr struct type * \ name##_RB_NFIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *res = NULL; \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) { \ res = tmp; \ tmp = RB_LEFT(tmp, field); \ } \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (res); \ } \ \ attr struct type * \ name##_RB_NEXT(struct type *elm) \ { \ if (RB_RIGHT(elm, field)) { \ elm = RB_RIGHT(elm, field); \ while (RB_LEFT(elm, field)) \ elm = RB_LEFT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ attr struct type * \ name##_RB_PREV(struct type *elm) \ { \ if (RB_LEFT(elm, field)) { \ elm = RB_LEFT(elm, field); \ while (RB_RIGHT(elm, field)) \ elm = RB_RIGHT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ attr struct type * \ name##_RB_MINMAX(struct name *head, int val) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *parent = NULL; \ while (tmp) { \ parent = tmp; \ if (val < 0) \ tmp = RB_LEFT(tmp, field); \ else \ tmp = RB_RIGHT(tmp, field); \ } \ return (parent); \ } #define RB_NEGINF -1 #define RB_INF 1 #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) #define RB_FIND(name, x, y) name##_RB_FIND(x, y) #define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) #define RB_NEXT(name, x, y) name##_RB_NEXT(y) #define RB_PREV(name, x, y) name##_RB_PREV(y) #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) #define RB_FOREACH(x, name, head) \ for ((x) = RB_MIN(name, head); \ (x) != NULL; \ (x) = name##_RB_NEXT(x)) #define RB_FOREACH_SAFE(x, name, head, y) \ for ((x) = RB_MIN(name, head); \ ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ (x) = (y)) #define RB_FOREACH_REVERSE(x, name, head) \ for ((x) = RB_MAX(name, head); \ (x) != NULL; \ (x) = name##_RB_PREV(x)) #define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ for ((x) = RB_MAX(name, head); \ ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ (x) = (y)) /* * Copyright (c) 2016 David Gwynne * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ struct rb_type { int (*t_compare)(const void *, const void *); void (*t_augment)(void *); unsigned int t_offset; /* offset of rb_entry in type */ }; struct rb_tree { struct rb_entry *rbt_root; }; struct rb_entry { struct rb_entry *rbt_parent; struct rb_entry *rbt_left; struct rb_entry *rbt_right; unsigned int rbt_color; }; #define RBT_HEAD(_name, _type) \ struct _name { \ struct rb_tree rbh_root; \ } #define RBT_ENTRY(_type) struct rb_entry static inline void _rb_init(struct rb_tree *rbt) { rbt->rbt_root = NULL; } static inline int _rb_empty(struct rb_tree *rbt) { return (rbt->rbt_root == NULL); } void *_rb_insert(const struct rb_type *, struct rb_tree *, void *); void *_rb_remove(const struct rb_type *, struct rb_tree *, void *); void *_rb_find(const struct rb_type *, struct rb_tree *, const void *); void *_rb_nfind(const struct rb_type *, struct rb_tree *, const void *); void *_rb_root(const struct rb_type *, struct rb_tree *); void *_rb_min(const struct rb_type *, struct rb_tree *); void *_rb_max(const struct rb_type *, struct rb_tree *); void *_rb_next(const struct rb_type *, void *); void *_rb_prev(const struct rb_type *, void *); void *_rb_left(const struct rb_type *, void *); void *_rb_right(const struct rb_type *, void *); void *_rb_parent(const struct rb_type *, void *); void _rb_set_left(const struct rb_type *, void *, void *); void _rb_set_right(const struct rb_type *, void *, void *); void _rb_set_parent(const struct rb_type *, void *, void *); void _rb_poison(const struct rb_type *, void *, unsigned long); int _rb_check(const struct rb_type *, void *, unsigned long); #define RBT_INITIALIZER(_head) { { NULL } } #define RBT_PROTOTYPE(_name, _type, _field, _cmp) \ extern const struct rb_type *const _name##_RBT_TYPE; \ \ __unused static inline void \ _name##_RBT_INIT(struct _name *head) \ { \ _rb_init(&head->rbh_root); \ } \ \ __unused static inline struct _type * \ _name##_RBT_INSERT(struct _name *head, struct _type *elm) \ { \ return _rb_insert(_name##_RBT_TYPE, &head->rbh_root, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_REMOVE(struct _name *head, struct _type *elm) \ { \ return _rb_remove(_name##_RBT_TYPE, &head->rbh_root, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_FIND(struct _name *head, const struct _type *key) \ { \ return _rb_find(_name##_RBT_TYPE, &head->rbh_root, key); \ } \ \ __unused static inline struct _type * \ _name##_RBT_NFIND(struct _name *head, const struct _type *key) \ { \ return _rb_nfind(_name##_RBT_TYPE, &head->rbh_root, key); \ } \ \ __unused static inline struct _type * \ _name##_RBT_ROOT(struct _name *head) \ { \ return _rb_root(_name##_RBT_TYPE, &head->rbh_root); \ } \ \ __unused static inline int \ _name##_RBT_EMPTY(struct _name *head) \ { \ return _rb_empty(&head->rbh_root); \ } \ \ __unused static inline struct _type * \ _name##_RBT_MIN(struct _name *head) \ { \ return _rb_min(_name##_RBT_TYPE, &head->rbh_root); \ } \ \ __unused static inline struct _type * \ _name##_RBT_MAX(struct _name *head) \ { \ return _rb_max(_name##_RBT_TYPE, &head->rbh_root); \ } \ \ __unused static inline struct _type * \ _name##_RBT_NEXT(struct _type *elm) \ { \ return _rb_next(_name##_RBT_TYPE, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_PREV(struct _type *elm) \ { \ return _rb_prev(_name##_RBT_TYPE, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_LEFT(struct _type *elm) \ { \ return _rb_left(_name##_RBT_TYPE, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_RIGHT(struct _type *elm) \ { \ return _rb_right(_name##_RBT_TYPE, elm); \ } \ \ __unused static inline struct _type * \ _name##_RBT_PARENT(struct _type *elm) \ { \ return _rb_parent(_name##_RBT_TYPE, elm); \ } \ \ __unused static inline void \ _name##_RBT_SET_LEFT(struct _type *elm, struct _type *left) \ { \ _rb_set_left(_name##_RBT_TYPE, elm, left); \ } \ \ __unused static inline void \ _name##_RBT_SET_RIGHT(struct _type *elm, struct _type *right) \ { \ _rb_set_right(_name##_RBT_TYPE, elm, right); \ } \ \ __unused static inline void \ _name##_RBT_SET_PARENT(struct _type *elm, struct _type *parent) \ { \ _rb_set_parent(_name##_RBT_TYPE, elm, parent); \ } \ \ __unused static inline void \ _name##_RBT_POISON(struct _type *elm, unsigned long poison) \ { \ _rb_poison(_name##_RBT_TYPE, elm, poison); \ } \ \ __unused static inline int \ _name##_RBT_CHECK(struct _type *elm, unsigned long poison) \ { \ return _rb_check(_name##_RBT_TYPE, elm, poison); \ } #define RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _aug) \ static int \ _name##_RBT_COMPARE(const void *lptr, const void *rptr) \ { \ const struct _type *l = lptr, *r = rptr; \ return _cmp(l, r); \ } \ static const struct rb_type _name##_RBT_INFO = { \ _name##_RBT_COMPARE, \ _aug, \ offsetof(struct _type, _field), \ }; \ const struct rb_type *const _name##_RBT_TYPE = &_name##_RBT_INFO #define RBT_GENERATE_AUGMENT(_name, _type, _field, _cmp, _aug) \ static void \ _name##_RBT_AUGMENT(void *ptr) \ { \ struct _type *p = ptr; \ return _aug(p); \ } \ RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _name##_RBT_AUGMENT) #define RBT_GENERATE(_name, _type, _field, _cmp) \ RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, NULL) #define RBT_INIT(_name, _head) _name##_RBT_INIT(_head) #define RBT_INSERT(_name, _head, _elm) _name##_RBT_INSERT(_head, _elm) #define RBT_REMOVE(_name, _head, _elm) _name##_RBT_REMOVE(_head, _elm) #define RBT_FIND(_name, _head, _key) _name##_RBT_FIND(_head, _key) #define RBT_NFIND(_name, _head, _key) _name##_RBT_NFIND(_head, _key) #define RBT_ROOT(_name, _head) _name##_RBT_ROOT(_head) #define RBT_EMPTY(_name, _head) _name##_RBT_EMPTY(_head) #define RBT_MIN(_name, _head) _name##_RBT_MIN(_head) #define RBT_MAX(_name, _head) _name##_RBT_MAX(_head) #define RBT_NEXT(_name, _elm) _name##_RBT_NEXT(_elm) #define RBT_PREV(_name, _elm) _name##_RBT_PREV(_elm) #define RBT_LEFT(_name, _elm) _name##_RBT_LEFT(_elm) #define RBT_RIGHT(_name, _elm) _name##_RBT_RIGHT(_elm) #define RBT_PARENT(_name, _elm) _name##_RBT_PARENT(_elm) #define RBT_SET_LEFT(_name, _elm, _l) _name##_RBT_SET_LEFT(_elm, _l) #define RBT_SET_RIGHT(_name, _elm, _r) _name##_RBT_SET_RIGHT(_elm, _r) #define RBT_SET_PARENT(_name, _elm, _p) _name##_RBT_SET_PARENT(_elm, _p) #define RBT_POISON(_name, _elm, _p) _name##_RBT_POISON(_elm, _p) #define RBT_CHECK(_name, _elm, _p) _name##_RBT_CHECK(_elm, _p) #define RBT_FOREACH(_e, _name, _head) \ for ((_e) = RBT_MIN(_name, (_head)); \ (_e) != NULL; \ (_e) = RBT_NEXT(_name, (_e))) #define RBT_FOREACH_SAFE(_e, _name, _head, _n) \ for ((_e) = RBT_MIN(_name, (_head)); \ (_e) != NULL && ((_n) = RBT_NEXT(_name, (_e)), 1); \ (_e) = (_n)) #define RBT_FOREACH_REVERSE(_e, _name, _head) \ for ((_e) = RBT_MAX(_name, (_head)); \ (_e) != NULL; \ (_e) = RBT_PREV(_name, (_e))) #define RBT_FOREACH_REVERSE_SAFE(_e, _name, _head, _n) \ for ((_e) = RBT_MAX(_name, (_head)); \ (_e) != NULL && ((_n) = RBT_PREV(_name, (_e)), 1); \ (_e) = (_n)) #endif /* _SYS_TREE_H_ */ openiked-7.4/compat/unistd.h000066400000000000000000000026311477554731100161440ustar00rootroot00000000000000/* * Public domain * unistd.h compatibility shim */ #ifndef IKED_COMPAT_UNISTD_H #define IKED_COMPAT_UNISTD_H #ifndef _MSC_VER #include_next #ifdef __MINGW32__ int ftruncate(int fd, off_t length); uid_t getuid(void); ssize_t pread(int d, void *buf, size_t nbytes, off_t offset); ssize_t pwrite(int d, const void *buf, size_t nbytes, off_t offset); #endif #else #include #include #include #define STDOUT_FILENO 1 #define STDERR_FILENO 2 #define R_OK 4 #define W_OK 2 #define X_OK 0 #define F_OK 0 #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 #define access _access #ifdef _MSC_VER #include static inline unsigned int sleep(unsigned int seconds) { Sleep(seconds * 1000); return seconds; } #endif int ftruncate(int fd, off_t length); uid_t getuid(void); ssize_t pread(int d, void *buf, size_t nbytes, off_t offset); ssize_t pwrite(int d, const void *buf, size_t nbytes, off_t offset); #endif #ifndef HAVE_GETENTROPY int getentropy(void *buf, size_t buflen); #else /* * Solaris 11.3 adds getentropy(2), but defines the function in sys/random.h */ #if defined(__sun) #include #endif #endif #ifndef HAVE_GETPAGESIZE int getpagesize(void); #endif #define pledge(request, paths) 0 #define unveil(path, permissions) 0 #ifndef HAVE_PIPE2 int pipe2(int fildes[2], int flags); #endif #endif openiked-7.4/compat/vis.c000066400000000000000000000143031477554731100154310ustar00rootroot00000000000000/* $OpenBSD: vis.c,v 1.26 2022/05/04 18:57:50 deraadt Exp $ */ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. */ /* OPENBSD ORIGINAL: lib/libc/gen/vis.c */ #if !defined(HAVE_STRNVIS) #include #include #include #include #include #include #include static int isoctal(int c) { u_char uc = c; return uc >= '0' && uc <= '7'; } static int isvisible(int c, int flag) { int vis_sp = flag & VIS_SP; int vis_tab = flag & VIS_TAB; int vis_nl = flag & VIS_NL; int vis_safe = flag & VIS_SAFE; int vis_glob = flag & VIS_GLOB; int vis_all = flag & VIS_ALL; u_char uc = c; if (c == '\\' || !vis_all) { if ((u_int)c <= UCHAR_MAX && isascii(uc) && ((c != '*' && c != '?' && c != '[' && c != '#') || !vis_glob) && isgraph(uc)) return 1; if (!vis_sp && c == ' ') return 1; if (!vis_tab && c == '\t') return 1; if (!vis_nl && c == '\n') return 1; if (vis_safe && (c == '\b' || c == '\007' || c == '\r' || isgraph(uc))) return 1; } return 0; } /* * vis - visually encode characters */ char * vis(char *dst, int c, int flag, int nextc) { int vis_dq = flag & VIS_DQ; int vis_noslash = flag & VIS_NOSLASH; int vis_cstyle = flag & VIS_CSTYLE; int vis_octal = flag & VIS_OCTAL; int vis_glob = flag & VIS_GLOB; if (isvisible(c, flag)) { if ((c == '"' && vis_dq) || (c == '\\' && !vis_noslash)) *dst++ = '\\'; *dst++ = c; *dst = '\0'; return (dst); } if (vis_cstyle) { switch (c) { case '\n': *dst++ = '\\'; *dst++ = 'n'; goto done; case '\r': *dst++ = '\\'; *dst++ = 'r'; goto done; case '\b': *dst++ = '\\'; *dst++ = 'b'; goto done; case '\a': *dst++ = '\\'; *dst++ = 'a'; goto done; case '\v': *dst++ = '\\'; *dst++ = 'v'; goto done; case '\t': *dst++ = '\\'; *dst++ = 't'; goto done; case '\f': *dst++ = '\\'; *dst++ = 'f'; goto done; case ' ': *dst++ = '\\'; *dst++ = 's'; goto done; case '\0': *dst++ = '\\'; *dst++ = '0'; if (isoctal(nextc)) { *dst++ = '0'; *dst++ = '0'; } goto done; } } if (((c & 0177) == ' ') || vis_octal || (vis_glob && (c == '*' || c == '?' || c == '[' || c == '#'))) { *dst++ = '\\'; *dst++ = ((u_char)c >> 6 & 07) + '0'; *dst++ = ((u_char)c >> 3 & 07) + '0'; *dst++ = ((u_char)c & 07) + '0'; goto done; } if (!vis_noslash) *dst++ = '\\'; if (c & 0200) { c &= 0177; *dst++ = 'M'; } if (iscntrl((u_char)c)) { *dst++ = '^'; if (c == 0177) *dst++ = '?'; else *dst++ = c + '@'; } else { *dst++ = '-'; *dst++ = c; } done: *dst = '\0'; return (dst); } /* * strvis, strnvis, strvisx - visually encode characters from src into dst * * Dst must be 4 times the size of src to account for possible * expansion. The length of dst, not including the trailing NULL, * is returned. * * Strnvis will write no more than siz-1 bytes (and will NULL terminate). * The number of bytes needed to fully encode the string is returned. * * Strvisx encodes exactly len bytes from src into dst. * This is useful for encoding a block of data. */ int strvis(char *dst, const char *src, int flag) { char c; char *start; for (start = dst; (c = *src);) dst = vis(dst, c, flag, *++src); *dst = '\0'; return (dst - start); } int strnvis(char *dst, const char *src, size_t siz, int flag) { int vis_dq = flag & VIS_DQ; int vis_noslash = flag & VIS_NOSLASH; char *start, *end; char tbuf[5]; int c, i; i = 0; for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) { if (isvisible(c, flag)) { if ((c == '"' && vis_dq) || (c == '\\' && !vis_noslash)) { /* need space for the extra '\\' */ if (dst + 1 >= end) { i = 2; break; } *dst++ = '\\'; } i = 1; *dst++ = c; src++; } else { i = vis(tbuf, c, flag, *++src) - tbuf; if (dst + i <= end) { memcpy(dst, tbuf, i); dst += i; } else { src--; break; } } } if (siz > 0) *dst = '\0'; if (dst + i > end) { /* adjust return value for truncation */ while ((c = *src)) dst += vis(tbuf, c, flag, *++src) - tbuf; } return (dst - start); } int stravis(char **outp, const char *src, int flag) { char *buf; int len, serrno; buf = reallocarray(NULL, 4, strlen(src) + 1); if (buf == NULL) return -1; len = strvis(buf, src, flag); serrno = errno; *outp = realloc(buf, len + 1); if (*outp == NULL) { *outp = buf; errno = serrno; } return (len); } int strvisx(char *dst, const char *src, size_t len, int flag) { char c; char *start; for (start = dst; len > 1; len--) { c = *src; dst = vis(dst, c, flag, *++src); } if (len) dst = vis(dst, *src, flag, '\0'); *dst = '\0'; return (dst - start); } #endif openiked-7.4/compat/vis.h000066400000000000000000000070031477554731100154350ustar00rootroot00000000000000/* $OpenBSD: vis.h,v 1.15 2015/07/20 01:52:27 millert Exp $ */ /* $NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd Exp $ */ /*- * Copyright (c) 1990 The Regents of the University of California. * 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 University 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 REGENTS 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 REGENTS 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. * * @(#)vis.h 5.9 (Berkeley) 4/3/91 */ /* OPENBSD ORIGINAL: include/vis.h */ #ifdef HAVE_CONFIG_H #include "includes.h" #endif #ifndef _VIS_H_ #define _VIS_H_ #include #include /* * to select alternate encoding format */ #define VIS_OCTAL 0x01 /* use octal \ddd format */ #define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */ /* * to alter set of characters encoded (default is to encode all * non-graphic except space, tab, and newline). */ #define VIS_SP 0x04 /* also encode space */ #define VIS_TAB 0x08 /* also encode tab */ #define VIS_NL 0x10 /* also encode newline */ #define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) #define VIS_SAFE 0x20 /* only encode "unsafe" characters */ #define VIS_DQ 0x200 /* backslash-escape double quotes */ #define VIS_ALL 0x400 /* encode all characters */ /* * other */ #define VIS_NOSLASH 0x40 /* inhibit printing '\' */ #define VIS_GLOB 0x100 /* encode glob(3) magics and '#' */ /* * unvis return codes */ #define UNVIS_VALID 1 /* character valid */ #define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */ #define UNVIS_NOCHAR 3 /* valid sequence, no character produced */ #define UNVIS_SYNBAD -1 /* unrecognized escape sequence */ #define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */ /* * unvis flags */ #define UNVIS_END 1 /* no more characters */ char *vis(char *, int, int, int); int strvis(char *, const char *, int); int stravis(char **, const char *, int); int strnvis(char *, const char *, size_t, int) __attribute__ ((__bounded__(__string__,1,3))); int strvisx(char *, const char *, size_t, int) __attribute__ ((__bounded__(__string__,1,3))); int strunvis(char *, const char *); int unvis(char *, char, int *, int); ssize_t strnunvis(char *, const char *, size_t) __attribute__ ((__bounded__(__string__,1,3))); #endif /* !_VIS_H_ */ openiked-7.4/ikectl/000077500000000000000000000000001477554731100144535ustar00rootroot00000000000000openiked-7.4/ikectl/CMakeLists.txt000066400000000000000000000036261477554731100172220ustar00rootroot00000000000000# Copyright (c) 2020-2021 Tobias Heider # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. set(SRCS) list(APPEND SRCS ikeca.c ikectl.c parser.c # iked ${CMAKE_CURRENT_SOURCE_DIR}/../iked/log.c ${CMAKE_CURRENT_SOURCE_DIR}/../iked/util.c ) add_executable(ikectl ${SRCS}) set(CFLAGS) list(APPEND CFLAGS -O2 -fstack-protector-strong -fPIE -D_FORTIFY_SOURCE=2 -Wall -Wno-pointer-sign -Wno-deprecated-declarations -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wshadow -Wpointer-arith -Wcast-qual -Wsign-compare "$<$:-O0;-g>" ) target_compile_options(ikectl PRIVATE ${CFLAGS}) if(HAVE_LD_Z) target_link_options(ikectl PRIVATE "LINKER:-z,relro,-z,now") endif() target_include_directories(ikectl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../iked ) target_link_libraries(ikectl PRIVATE util event crypto ssl compat ) install(TARGETS ikectl RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/ikectl.8 DESTINATION ${CMAKE_INSTALL_MANDIR}/man8/) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/ikeca.cnf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/ssl) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/ikex509v3.cnf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/ssl) openiked-7.4/ikectl/Makefile000066400000000000000000000010771477554731100161200ustar00rootroot00000000000000# $OpenBSD: Makefile,v 1.7 2016/09/11 14:31:02 natano Exp $ .PATH: ${.CURDIR}/../../sbin/iked PROG= ikectl SRCS= log.c ikeca.c ikectl.c parser.c util.c MAN= ikectl.8 LDADD= -lutil -lcrypto DPADD= ${LIBUTIL} ${LIBCRYPTO} CFLAGS+= -Wall -I${.CURDIR} -I${.CURDIR}/../../sbin/iked CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual CFLAGS+= -Wsign-compare distribution: ${INSTALL} -C -o root -g wheel -m 0644 ${.CURDIR}/ikeca.cnf \ ${DESTDIR}/etc/ssl/ikeca.cnf .include openiked-7.4/ikectl/ikeca.c000066400000000000000000000716771477554731100157150ustar00rootroot00000000000000/* $OpenBSD: ikeca.c,v 1.49 2019/05/08 23:59:19 tedu Exp $ */ /* * Copyright (c) 2010 Jonathan Gray * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "openbsd-compat.h" #include "types.h" #include "parser.h" #ifndef PREFIX #define PREFIX "" #endif #ifndef SSLDIR #define SSLDIR PREFIX "/etc/ssl" #endif #define SSL_CNF SSLDIR "/openssl.cnf" #define X509_CNF SSLDIR "/ikex509v3.cnf" #define IKECA_CNF SSLDIR "/ikeca.cnf" #define KEYBASE PREFIX "/etc/iked" #ifndef EXPDIR #define EXPDIR PREFIX "/usr/share/iked" #endif #ifndef PATH_OPENSSL #define PATH_OPENSSL "/usr/bin/openssl" #endif #ifndef PATH_ZIP #define PATH_ZIP "/usr/local/bin/zip" #endif #ifndef PATH_TAR #define PATH_TAR "/bin/tar" #endif struct ca { char sslpath[PATH_MAX]; char passfile[PATH_MAX + 5]; /* Includes the "file:" prefix */ char index[PATH_MAX]; char serial[PATH_MAX]; char sslcnf[PATH_MAX]; char extcnf[PATH_MAX]; char *batch; char *caname; }; struct { char *dir; mode_t mode; } hier[] = { { "", 0755 }, { "/ca", 0755 }, { "/certs", 0755 }, { "/crls", 0755 }, { "/export", 0755 }, { "/private", 0700 } }; /* explicitly list allowed variables */ char *ca_env[][2] = { { "$ENV::CADB", NULL }, { "$ENV::CASERIAL", NULL }, { "$ENV::CERTFQDN", NULL }, { "$ENV::CERTIP", NULL }, { "$ENV::CERTPATHLEN", NULL }, { "$ENV::CERTUSAGE", NULL }, { "$ENV::CERT_C", NULL }, { "$ENV::CERT_CN", NULL }, { "$ENV::CERT_EMAIL", NULL }, { "$ENV::CERT_L", NULL }, { "$ENV::CERT_O", NULL }, { "$ENV::CERT_OU", NULL }, { "$ENV::CERT_ST", NULL }, { "$ENV::EXTCERTUSAGE", NULL }, { "$ENV::NSCERTTYPE", NULL }, { "$ENV::REQ_EXT", NULL }, { NULL } }; int ca_sign(struct ca *, char *, int); int ca_request(struct ca *, char *, int); void ca_newpass(char *, char *); int fcopy(char *, char *, mode_t); void fcopy_env(const char *, const char *, mode_t); int rm_dir(char *); void ca_hier(char *); void ca_setenv(const char *, const char *); void ca_clrenv(void); void ca_setcnf(struct ca *, const char *); void ca_create_index(struct ca *); int static ca_execv(char *const []); /* util.c */ int expand_string(char *, size_t, const char *, const char *); int ca_delete(struct ca *ca) { return (rm_dir(ca->sslpath)); } int ca_key_create(struct ca *ca, char *keyname) { struct stat st; char path[PATH_MAX]; int len; len = snprintf(path, sizeof(path), "%s/private/%s.key", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(path)) err(1, "%s: snprintf", __func__); /* don't recreate key if one is already present */ if (stat(path, &st) == 0) { return (0); } char *cmd[] = { PATH_OPENSSL, "genrsa", "-out", path, "2048", NULL }; ca_execv(cmd); chmod(path, 0600); return (0); } int ca_key_import(struct ca *ca, char *keyname, char *import) { struct stat st; char dst[PATH_MAX]; int len; if (stat(import, &st) != 0) { warn("could not access keyfile %s", import); return (1); } len = snprintf(dst, sizeof(dst), "%s/private/%s.key", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); fcopy(import, dst, 0600); return (0); } int ca_key_delete(struct ca *ca, char *keyname) { char path[PATH_MAX]; int len; len = snprintf(path, sizeof(path), "%s/private/%s.key", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(path)) err(1, "%s: snprintf", __func__); unlink(path); return (0); } int ca_delkey(struct ca *ca, char *keyname) { char file[PATH_MAX]; int len; len = snprintf(file, sizeof(file), "%s/%s.crt", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(file)) err(1, "%s: snprintf", __func__); unlink(file); len = snprintf(file, sizeof(file), "%s/private/%s.key", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(file)) err(1, "%s: snprintf", __func__); unlink(file); len = snprintf(file, sizeof(file), "%s/private/%s.csr", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(file)) err(1, "%s: snprintf", __func__); unlink(file); len = snprintf(file, sizeof(file), "%s/private/%s.pfx", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(file)) err(1, "%s: snprintf", __func__); unlink(file); return (0); } int ca_request(struct ca *ca, char *keyname, int type) { char hostname[HOST_NAME_MAX+1]; char name[128]; char key[PATH_MAX]; char path[PATH_MAX]; int len; ca_setenv("$ENV::CERT_CN", keyname); strlcpy(name, keyname, sizeof(name)); if (type == HOST_IPADDR) { ca_setenv("$ENV::CERTIP", name); ca_setenv("$ENV::REQ_EXT", "x509v3_IPAddr"); } else if (type == HOST_FQDN) { if (!strcmp(keyname, "local")) { if (gethostname(hostname, sizeof(hostname))) err(1, "gethostname"); strlcpy(name, hostname, sizeof(name)); } ca_setenv("$ENV::CERTFQDN", name); ca_setenv("$ENV::REQ_EXT", "x509v3_FQDN"); } else { errx(1, "unknown host type %d", type); } ca_setcnf(ca, keyname); len = snprintf(key, sizeof(key), "%s/private/%s.key", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(key)) err(1, "%s: snprintf", __func__); len = snprintf(path, sizeof(path), "%s/private/%s.csr", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(path)) err(1, "%s: snprintf", __func__); char *cmd[] = { PATH_OPENSSL, "req", "-new", "-key", key, "-out", path, "-config", ca->sslcnf, ca->batch, NULL }; ca_execv(cmd); chmod(path, 0600); return (0); } int ca_sign(struct ca *ca, char *keyname, int type) { char cakey[PATH_MAX]; char cacrt[PATH_MAX]; char out[PATH_MAX]; char in[PATH_MAX]; char *extensions = NULL; int len; if (type == HOST_IPADDR) { extensions = "x509v3_IPAddr"; } else if (type == HOST_FQDN) { extensions = "x509v3_FQDN"; } else { errx(1, "unknown host type %d", type); } ca_create_index(ca); ca_setenv("$ENV::CADB", ca->index); ca_setenv("$ENV::CASERIAL", ca->serial); ca_setcnf(ca, keyname); len = snprintf(cakey, sizeof(cakey), "%s/private/ca.key", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(cakey)) err(1, "%s: snprintf", __func__); len = snprintf(cacrt, sizeof(cacrt), "%s/ca.crt", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(cacrt)) err(1, "%s: snprintf", __func__); len = snprintf(out, sizeof(out), "%s/%s.crt", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(out)) err(1, "%s: snprintf", __func__); len = snprintf(in, sizeof(in), "%s/private/%s.csr", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(in)) err(1, "%s: snprintf", __func__); char *cmd[] = { PATH_OPENSSL, "ca", "-config", ca->sslcnf, "-keyfile", cakey, "-cert", cacrt, "-extfile", ca->extcnf, "-extensions", extensions, "-out", out, "-in", in, "-passin", ca->passfile, "-outdir", ca->sslpath, "-batch", NULL }; ca_execv(cmd); return (0); } int ca_certificate(struct ca *ca, char *keyname, int type, int action) { ca_clrenv(); switch (action) { case CA_SERVER: ca_setenv("$ENV::EXTCERTUSAGE", "serverAuth"); ca_setenv("$ENV::NSCERTTYPE", "server"); ca_setenv("$ENV::CERTUSAGE", "digitalSignature,keyEncipherment"); break; case CA_CLIENT: ca_setenv("$ENV::EXTCERTUSAGE", "clientAuth"); ca_setenv("$ENV::NSCERTTYPE", "client"); ca_setenv("$ENV::CERTUSAGE", "digitalSignature,keyAgreement"); break; case CA_OCSP: ca_setenv("$ENV::EXTCERTUSAGE", "OCSPSigning"); ca_setenv("$ENV::CERTUSAGE", "nonRepudiation,digitalSignature,keyEncipherment"); break; default: break; } ca_key_create(ca, keyname); ca_request(ca, keyname, type); ca_sign(ca, keyname, type); return (0); } int ca_key_install(struct ca *ca, char *keyname, char *dir) { struct stat st; char src[PATH_MAX]; char dst[PATH_MAX]; char out[PATH_MAX]; char *p = NULL; int len; len = snprintf(src, sizeof(src), "%s/private/%s.key", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(src)) err(1, "%s: snprintf", __func__); if (stat(src, &st) == -1) { if (errno == ENOENT) printf("key for '%s' does not exist\n", ca->caname); else warn("could not access key"); return (1); } if (dir == NULL) p = dir = strdup(KEYBASE); ca_hier(dir); len = snprintf(dst, sizeof(dst), "%s/private/local.key", dir); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); fcopy(src, dst, 0600); len = snprintf(out, sizeof(out), "%s/local.pub", dir); if (len < 0 || (size_t)len >= sizeof(out)) err(1, "%s: snprintf", __func__); char *cmd[] = { PATH_OPENSSL, "rsa", "-out", out, "-in", dst, "-pubout", NULL }; ca_execv(cmd); free(p); return (0); } int ca_cert_install(struct ca *ca, char *keyname, char *dir) { char src[PATH_MAX]; char dst[PATH_MAX]; int r; char *p = NULL; int len; if (dir == NULL) p = dir = strdup(KEYBASE); ca_hier(dir); if ((r = ca_key_install(ca, keyname, dir)) != 0) { free(dir); return (r); } len = snprintf(src, sizeof(src), "%s/%s.crt", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(src)) err(1, "%s: snprintf", __func__); len = snprintf(dst, sizeof(dst), "%s/certs/%s.crt", dir, keyname); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); fcopy(src, dst, 0644); free(p); return (0); } void ca_newpass(char *passfile, char *password) { FILE *f; char *pass; char prev[_PASSWORD_LEN + 1]; if (password != NULL) { pass = password; goto done; } pass = getpass("CA passphrase:"); if (pass == NULL || *pass == '\0') err(1, "password not set"); strlcpy(prev, pass, sizeof(prev)); pass = getpass("Retype CA passphrase:"); if (pass == NULL || strcmp(prev, pass) != 0) errx(1, "passphrase does not match!"); done: if ((f = fopen(passfile, "wb")) == NULL) err(1, "could not open passfile %s", passfile); chmod(passfile, 0600); fprintf(f, "%s\n%s\n", pass, pass); fclose(f); } int ca_create(struct ca *ca) { char key[PATH_MAX]; char csr[PATH_MAX]; char crt[PATH_MAX]; int len; ca_clrenv(); len = snprintf(key, sizeof(key), "%s/private/ca.key", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(key)) err(1, "%s: snprintf", __func__); char *genrsa[] = { PATH_OPENSSL, "genrsa", "-aes256", "-out", key, "-passout", ca->passfile, "2048", NULL }; ca_execv(genrsa); chmod(key, 0600); ca_setenv("$ENV::CERT_CN", "VPN CA"); ca_setenv("$ENV::REQ_EXT", "x509v3_CA"); ca_setcnf(ca, "ca"); len = snprintf(csr, sizeof(csr), "%s/private/ca.csr", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(csr)) err(1, "%s: snprintf", __func__); char *reqcmd[] = { PATH_OPENSSL, "req", "-new", "-key", key, "-config", ca->sslcnf, "-out", csr, "-passin", ca->passfile, ca->batch, NULL }; ca_execv(reqcmd); chmod(csr, 0600); len = snprintf(crt, sizeof(crt), "%s/ca.crt", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(crt)) err(1, "%s: snprintf", __func__); char *x509[] = { PATH_OPENSSL, "x509", "-req", "-days", "4500", "-in", csr, "-signkey", key, "-sha256", "-extfile", ca->extcnf, "-extensions", "x509v3_CA", "-out", crt, "-passin", ca->passfile, NULL }; ca_execv(x509); /* Create the CRL revocation list */ ca_revoke(ca, NULL); return (0); } int ca_install(struct ca *ca, char *dir) { struct stat st; char src[PATH_MAX]; char dst[PATH_MAX]; char *p = NULL; int len; len = snprintf(src, sizeof(src), "%s/ca.crt", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(src)) err(1, "%s: snprintf", __func__); if (stat(src, &st) == -1) { printf("CA '%s' does not exist\n", ca->caname); return (1); } if (dir == NULL) p = dir = strdup(KEYBASE); ca_hier(dir); len = snprintf(dst, sizeof(dst), "%s/ca/ca.crt", dir); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); if (fcopy(src, dst, 0644) == 0) printf("certificate for CA '%s' installed into %s\n", ca->caname, dst); len = snprintf(src, sizeof(src), "%s/ca.crl", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(src)) err(1, "%s: snprintf", __func__); if (stat(src, &st) == 0) { len = snprintf(dst, sizeof(dst), "%s/crls/ca.crl", dir); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); if (fcopy(src, dst, 0644) == 0) printf("CRL for CA '%s' installed to %s\n", ca->caname, dst); } free(p); return (0); } int ca_show_certs(struct ca *ca, char *name) { DIR *dir; struct dirent *de; char path[PATH_MAX]; char *p; struct stat st; int len; if (name != NULL) { len = snprintf(path, sizeof(path), "%s/%s.crt", ca->sslpath, name); if (len < 0 || (size_t)len >= sizeof(path)) err(1, "%s: snprintf", __func__); if (stat(path, &st) != 0) err(1, "could not open file %s.crt", name); char *cmd[] = { PATH_OPENSSL, "x509", "-text", "-in", path, NULL }; ca_execv(cmd); printf("\n"); return (0); } if ((dir = opendir(ca->sslpath)) == NULL) err(1, "could not open directory %s", ca->sslpath); while ((de = readdir(dir)) != NULL) { if (NAMLEN(de) > 4) { p = de->d_name + NAMLEN(de) - 4; if (strcmp(".crt", p) != 0) continue; len = snprintf(path, sizeof(path), "%s/%s", ca->sslpath, de->d_name); if (len < 0 || (size_t)len >= sizeof(path)) err(1, "%s: snprintf", __func__); char *cmd[] = { PATH_OPENSSL, "x509", "-subject", "-fingerprint", "-dates", "-noout", "-in", path, NULL }; ca_execv(cmd); printf("\n"); } } closedir(dir); return (0); } int fcopy(char *src, char *dst, mode_t mode) { int ifd, ofd; uint8_t buf[BUFSIZ]; ssize_t r; if ((ifd = open(src, O_RDONLY)) == -1) err(1, "open %s", src); if ((ofd = open(dst, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) { int saved_errno = errno; close(ifd); errno = saved_errno; err(1, "open %s", dst); } while ((r = read(ifd, buf, sizeof(buf))) > 0) { if (write(ofd, buf, r) == -1) err(1, "%s: write", __func__); } close(ofd); close(ifd); return (r == -1); } void fcopy_env(const char *src, const char *dst, mode_t mode) { int ofd = -1, i; uint8_t buf[BUFSIZ]; ssize_t r = -1, len; FILE *ifp = NULL; int saved_errno; if ((ifp = fopen(src, "r")) == NULL) err(1, "fopen %s", src); if ((ofd = open(dst, O_WRONLY|O_CREAT|O_TRUNC, mode)) == -1) goto done; while (fgets(buf, sizeof(buf), ifp) != NULL) { for (i = 0; ca_env[i][0] != NULL; i++) { if (ca_env[i][1] == NULL) continue; if (expand_string(buf, sizeof(buf), ca_env[i][0], ca_env[i][1]) == -1) errx(1, "env %s value too long", ca_env[i][0]); } len = strlen(buf); if (write(ofd, buf, len) != len) goto done; } r = 0; done: saved_errno = errno; close(ofd); if (ifp != NULL) fclose(ifp); if (r == -1) { errno = saved_errno; err(1, "open %s", dst); } } int rm_dir(char *path) { FTS *fts; FTSENT *p; static char *fpath[] = { NULL, NULL }; fpath[0] = path; if ((fts = fts_open(fpath, FTS_PHYSICAL, NULL)) == NULL) { warn("fts_open %s", path); return (1); } while ((p = fts_read(fts)) != NULL) { switch (p->fts_info) { case FTS_DP: case FTS_DNR: if (rmdir(p->fts_accpath) == -1) warn("rmdir %s", p->fts_accpath); break; case FTS_F: if (unlink(p->fts_accpath) == -1) warn("unlink %s", p->fts_accpath); break; case FTS_D: case FTS_DOT: default: continue; } } fts_close(fts); return (0); } void ca_hier(char *path) { struct stat st; char dst[PATH_MAX]; unsigned int i; for (i = 0; i < nitems(hier); i++) { strlcpy(dst, path, sizeof(dst)); strlcat(dst, hier[i].dir, sizeof(dst)); if (stat(dst, &st) != 0 && errno == ENOENT && mkdir(dst, hier[i].mode) != 0) err(1, "failed to create dir %s", dst); } } int ca_export(struct ca *ca, char *keyname, char *myname, char *password) { DIR *dexp; struct dirent *de; struct stat st; char *pass; char prev[_PASSWORD_LEN + 1]; char passenv[_PASSWORD_LEN + 8]; char oname[PATH_MAX]; char src[PATH_MAX]; char dst[PATH_MAX]; char cacrt[PATH_MAX]; char capfx[PATH_MAX]; char key[PATH_MAX]; char crt[PATH_MAX]; char pfx[PATH_MAX]; char *p; char tpl[] = "/tmp/ikectl.XXXXXXXXXX"; unsigned int i; int fd; int len; if (keyname != NULL) { if (strlcpy(oname, keyname, sizeof(oname)) >= sizeof(oname)) errx(1, "name too long"); } else { strlcpy(oname, "ca", sizeof(oname)); } /* colons are not valid characters in windows filenames... */ while ((p = strchr(oname, ':')) != NULL) *p = '_'; if (password != NULL) pass = password; else { pass = getpass("Export passphrase:"); if (pass == NULL || *pass == '\0') err(1, "password not set"); strlcpy(prev, pass, sizeof(prev)); pass = getpass("Retype export passphrase:"); if (pass == NULL || strcmp(prev, pass) != 0) errx(1, "passphrase does not match!"); } len = snprintf(cacrt, sizeof(cacrt), "%s/ca.crt", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(cacrt)) err(1, "%s: snprintf", __func__); len = snprintf(capfx, sizeof(capfx), "%s/ca.pfx", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(capfx)) err(1, "%s: snprintf", __func__); len = snprintf(key, sizeof(key), "%s/private/%s.key", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(key)) err(1, "%s: snprintf", __func__); len = snprintf(crt, sizeof(crt), "%s/%s.crt", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(crt)) err(1, "%s: snprintf", __func__); len = snprintf(pfx, sizeof(pfx), "%s/private/%s.pfx", ca->sslpath, oname); if (len < 0 || (size_t)len >= sizeof(pfx)) err(1, "%s: snprintf", __func__); len = snprintf(passenv, sizeof(passenv), "EXPASS=%s", pass); if (len < 0 || (size_t)len >= sizeof(passenv)) err(1, "%s: snprintf", __func__); putenv(passenv); if (keyname != NULL) { char *cmd[] = { PATH_OPENSSL, "pkcs12", "-export", "-name", keyname, "-CAfile", cacrt, "-inkey", key, "-in", crt, "-out", pfx, "-passout", "env:EXPASS", "-passin", ca->passfile, NULL }; ca_execv(cmd); } char *pkcscmd[] = { PATH_OPENSSL, "pkcs12", "-export", "-caname", ca->caname, "-name", ca->caname, "-cacerts", "-nokeys", "-in", cacrt, "-out", capfx, "-passout", "env:EXPASS", "-passin", ca->passfile, NULL }; ca_execv(pkcscmd); unsetenv("EXPASS"); explicit_bzero(passenv, sizeof(passenv)); if ((p = mkdtemp(tpl)) == NULL) err(1, "could not create temp dir"); chmod(p, 0755); for (i = 0; i < nitems(hier); i++) { strlcpy(dst, p, sizeof(dst)); strlcat(dst, hier[i].dir, sizeof(dst)); if (stat(dst, &st) != 0 && errno == ENOENT && mkdir(dst, hier[i].mode) != 0) err(1, "failed to create dir %s", dst); } /* create a file with the address of the peer to connect to */ if (myname != NULL) { len = snprintf(dst, sizeof(dst), "%s/export/peer.txt", p); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); if ((fd = open(dst, O_WRONLY|O_CREAT, 0644)) == -1) err(1, "open %s", dst); if (write(fd, myname, strlen(myname)) == -1) err(1, "%s: write", __func__); close(fd); } len = snprintf(src, sizeof(src), "%s/ca.pfx", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(src)) err(1, "%s: snprintf", __func__); len = snprintf(dst, sizeof(dst), "%s/export/ca.pfx", p); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); fcopy(src, dst, 0644); len = snprintf(src, sizeof(src), "%s/ca.crt", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(src)) err(1, "%s: snprintf", __func__); len = snprintf(dst, sizeof(dst), "%s/ca/ca.crt", p); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); fcopy(src, dst, 0644); len = snprintf(src, sizeof(src), "%s/ca.crl", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(src)) err(1, "%s: snprintf", __func__); if (stat(src, &st) == 0) { len = snprintf(dst, sizeof(dst), "%s/crls/ca.crl", p); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); fcopy(src, dst, 0644); } if (keyname != NULL) { len = snprintf(src, sizeof(src), "%s/private/%s.pfx", ca->sslpath, oname); if (len < 0 || (size_t)len >= sizeof(src)) err(1, "%s: snprintf", __func__); len = snprintf(dst, sizeof(dst), "%s/export/%s.pfx", p, oname); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); fcopy(src, dst, 0644); len = snprintf(src, sizeof(src), "%s/private/%s.key", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(src)) err(1, "%s: snprintf", __func__); len = snprintf(dst, sizeof(dst), "%s/private/%s.key", p, keyname); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); fcopy(src, dst, 0600); len = snprintf(dst, sizeof(dst), "%s/private/local.key", p); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); fcopy(src, dst, 0600); len = snprintf(src, sizeof(src), "%s/%s.crt", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(src)) err(1, "%s: snprintf", __func__); len = snprintf(dst, sizeof(dst), "%s/certs/%s.crt", p, keyname); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); fcopy(src, dst, 0644); len = snprintf(dst, sizeof(dst), "%s/local.pub", p); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); char *cmd[] = { PATH_OPENSSL, "rsa", "-out", dst, "-in", key, "-pubout", NULL }; ca_execv(cmd); } if (stat(PATH_TAR, &st) == 0) { len = snprintf(src, sizeof(src), "%s.tgz", oname); if (len < 0 || (size_t)len >= sizeof(src)) err(1, "%s: snprintf", __func__); if (keyname == NULL) { char *cmd[] = { PATH_TAR, "-zcf", src, "-C", ca->sslpath, ".", NULL }; ca_execv(cmd); } else { char *cmd[] = { PATH_TAR, "-zcf", src, "-C", p, ".", NULL }; ca_execv(cmd); } if (realpath(src, dst) != NULL) printf("exported files in %s\n", dst); } if (stat(PATH_ZIP, &st) == 0) { dexp = opendir(EXPDIR); if (dexp) { while ((de = readdir(dexp)) != NULL) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; len = snprintf(src, sizeof(src), "%s/%s", EXPDIR, de->d_name); if (len < 0 || (size_t)len >= sizeof(src)) err(1, "%s: snprintf", __func__); len = snprintf(dst, sizeof(dst), "%s/export/%s", p, de->d_name); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); fcopy(src, dst, 0644); } closedir(dexp); } len = snprintf(dst, sizeof(dst), "%s/export", p); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); if (getcwd(src, sizeof(src)) == NULL) err(1, "could not get cwd"); if (chdir(dst) == -1) err(1, "could not change %s", dst); len = snprintf(dst, sizeof(dst), "%s/%s.zip", src, oname); if (len < 0 || (size_t)len >= sizeof(dst)) err(1, "%s: snprintf", __func__); char *cmd[] = { PATH_ZIP, "-qr", dst, ".", NULL }; ca_execv(cmd); printf("exported files in %s\n", dst); if (chdir(src) == -1) err(1, "could not change %s", dst); } rm_dir(p); return (0); } /* create index if it doesn't already exist */ void ca_create_index(struct ca *ca) { struct stat st; int fd; int len; len = snprintf(ca->index, sizeof(ca->index), "%s/index.txt", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(ca->index)) err(1, "%s: snprintf", __func__); if (stat(ca->index, &st) != 0) { if (errno == ENOENT) { if ((fd = open(ca->index, O_WRONLY | O_CREAT, 0644)) == -1) err(1, "could not create file %s", ca->index); close(fd); } else err(1, "could not access %s", ca->index); } len = snprintf(ca->serial, sizeof(ca->serial), "%s/serial.txt", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(ca->serial)) err(1, "%s: snprintf", __func__); if (stat(ca->serial, &st) != 0) { if (errno == ENOENT) { if ((fd = open(ca->serial, O_WRONLY | O_CREAT, 0644)) == -1) err(1, "could not create file %s", ca->serial); /* serial file must be created with a number */ if (write(fd, "01\n", 3) != 3) err(1, "write %s", ca->serial); close(fd); } else err(1, "could not access %s", ca->serial); } } int ca_revoke(struct ca *ca, char *keyname) { struct stat st; char path[PATH_MAX]; char cakey[PATH_MAX]; char cacrt[PATH_MAX]; size_t len; if (keyname) { len = snprintf(path, sizeof(path), "%s/%s.crt", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(path)) err(1, "%s: snprintf", __func__); if (stat(path, &st) != 0) { warn("Problem with certificate for '%s'", keyname); return (1); } } ca_create_index(ca); ca_setenv("$ENV::CADB", ca->index); ca_setenv("$ENV::CASERIAL", ca->serial); if (keyname) ca_setenv("$ENV::REQ_EXT", ""); ca_setcnf(ca, "ca-revoke"); len = snprintf(cakey, sizeof(cakey), "%s/private/ca.key", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(cakey)) err(1, "%s: snprintf", __func__); len = snprintf(cacrt, sizeof(cacrt), "%s/ca.crt", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(cacrt)) err(1, "%s: snprintf", __func__); if (keyname) { char *cmd[] = { PATH_OPENSSL, "ca", "-config", ca->sslcnf, "-keyfile", cakey, "-passin", ca->passfile, "-cert", cacrt, "-revoke", path, ca->batch, NULL }; ca_execv(cmd); } len = snprintf(path, sizeof(path), "%s/ca.crl", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(path)) err(1, "%s: snprintf", __func__); char *cmd[] = { PATH_OPENSSL, "ca", "-config", ca->sslcnf, "-keyfile", cakey, "-passin", ca->passfile, "-gencrl", "-cert", cacrt, "-crldays", "365", "-out", path, ca->batch, NULL }; ca_execv(cmd); return (0); } void ca_clrenv(void) { int i; for (i = 0; ca_env[i][0] != NULL; i++) { free(ca_env[i][1]); ca_env[i][1] = NULL; } } void ca_setenv(const char *key, const char *value) { int i; char *p = NULL; for (i = 0; ca_env[i][0] != NULL; i++) { if (strcmp(ca_env[i][0], key) == 0) { if (ca_env[i][1] != NULL) errx(1, "env %s already set: %s", key, value); p = strdup(value); if (p == NULL) err(1, NULL); ca_env[i][1] = p; return; } } errx(1, "env %s invalid", key); } void ca_setcnf(struct ca *ca, const char *keyname) { struct stat st; const char *extcnf, *sslcnf; int len; if (stat(IKECA_CNF, &st) == 0) { extcnf = IKECA_CNF; sslcnf = IKECA_CNF; } else { extcnf = X509_CNF; sslcnf = SSL_CNF; } len = snprintf(ca->extcnf, sizeof(ca->extcnf), "%s/%s-ext.cnf", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(ca->extcnf)) err(1, "%s: snprintf", __func__); len = snprintf(ca->sslcnf, sizeof(ca->sslcnf), "%s/%s-ssl.cnf", ca->sslpath, keyname); if (len < 0 || (size_t)len >= sizeof(ca->sslcnf)) err(1, "%s: snprintf", __func__); fcopy_env(extcnf, ca->extcnf, 0400); fcopy_env(sslcnf, ca->sslcnf, 0400); } struct ca * ca_setup(char *caname, int create, int quiet, char *pass) { struct stat st; struct ca *ca; char path[PATH_MAX]; int len; if (stat(PATH_OPENSSL, &st) == -1) err(1, "openssl binary not available"); if ((ca = calloc(1, sizeof(struct ca))) == NULL) err(1, "calloc"); ca->caname = strdup(caname); len = snprintf(ca->sslpath, sizeof(ca->sslpath), SSLDIR "/%s", caname); if (len < 0 || (size_t)len >= sizeof(ca->sslpath)) err(1, "%s: snprintf", __func__); if (quiet) ca->batch = "-batch"; if (create == 0 && stat(ca->sslpath, &st) == -1) { free(ca->caname); free(ca); errx(1, "CA '%s' does not exist", caname); } strlcpy(path, ca->sslpath, sizeof(path)); if (mkdir(path, 0777) == -1 && errno != EEXIST) err(1, "failed to create dir %s", path); strlcat(path, "/private", sizeof(path)); if (mkdir(path, 0700) == -1 && errno != EEXIST) err(1, "failed to create dir %s", path); len = snprintf(path, sizeof(path), "%s/ikeca.passwd", ca->sslpath); if (len < 0 || (size_t)len >= sizeof(path)) err(1, "%s: snprintf", __func__); if (create && stat(path, &st) == -1 && errno == ENOENT) ca_newpass(path, pass); len = snprintf(ca->passfile, sizeof(ca->passfile), "file:%s", path); if (len < 0 || (size_t)len >= sizeof(ca->passfile)) err(1, "%s: snprintf", __func__); return (ca); } int static ca_execv(char *const argv[]) { pid_t pid, cpid; int status; switch (cpid = fork()) { case -1: return -1; case 0: execv(argv[0], argv); _exit(127); } do { pid = waitpid(cpid, &status, 0); } while (pid == -1 && errno == EINTR); return (pid == -1 ? -1 : WEXITSTATUS(status)); } openiked-7.4/ikectl/ikeca.cnf000066400000000000000000000052171477554731100162240ustar00rootroot00000000000000# $OpenBSD: ikeca.cnf,v 1.10 2023/11/17 14:43:36 tobhe Exp $ CERT_C = DE CERT_ST = Lower Saxony CERT_L = Hanover CERT_O = OpenBSD CERT_OU = iked CERT_CN = CERT_EMAIL = reyk@openbsd.org # default settings CERTPATHLEN = 1 CERTUSAGE = digitalSignature,keyCertSign,cRLSign EXTCERTUSAGE = serverAuth,clientAuth CERTIP = 0.0.0.0 CERTFQDN = nohost.nodomain CADB = index.txt CASERIAL = serial.txt NSCERTTYPE = server,client [ req ] #default_bits = 2048 #default_md = sha256 #default_keyfile = privkey.pem distinguished_name = req_distinguished_name #attributes = req_attributes req_extensions = $ENV::REQ_EXT [ req_distinguished_name ] countryName = Country Name (2 letter code) countryName_default = $ENV::CERT_C countryName_min = 2 countryName_max = 2 stateOrProvinceName = State or Province Name (full name) stateOrProvinceName_default = $ENV::CERT_ST localityName = Locality Name (eg, city) localityName_default = $ENV::CERT_L 0.organizationName = Organization Name (eg, company) 0.organizationName_default = $ENV::CERT_O # we can do this but it is not needed normally :-) #1.organizationName = Second Organization Name (eg, company) #1.organizationName_default = OpenBSD organizationalUnitName = Organizational Unit Name (eg, section) organizationalUnitName_default = $ENV::CERT_OU commonName = Common Name (eg, fully qualified host name) commonName_max = 64 commonName_default = $ENV::CERT_CN emailAddress = Email Address emailAddress_max = 64 emailAddress_default = $ENV::CERT_EMAIL [ req_attributes ] challengePassword = A challenge password challengePassword_min = 4 challengePassword_max = 20 unstructuredName = An optional company name [ x509v3_extensions ] nsCaRevocationUrl = http://127.0.0.1/ca-crl.pem nsComment = "This is a comment" # under ASN.1, the 0 bit would be encoded as 80 nsCertType = 0x40 [x509v3_CA] basicConstraints=critical,CA:true,pathlen:$ENV::CERTPATHLEN keyUsage=$ENV::CERTUSAGE [x509v3_IPAddr] keyUsage=$ENV::CERTUSAGE nsCertType=$ENV::NSCERTTYPE subjectAltName=IP:$ENV::CERTIP extendedKeyUsage=$ENV::EXTCERTUSAGE [x509v3_FQDN] keyUsage=$ENV::CERTUSAGE nsCertType=$ENV::NSCERTTYPE subjectAltName=DNS:$ENV::CERTFQDN extendedKeyUsage=$ENV::EXTCERTUSAGE [ca] default_ca = CA_default [CA_sign_policy] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional [CA_default] database = $ENV::CADB serial = $ENV::CASERIAL default_md = sha256 default_days = 365 default_crl_days = 365 unique_subject = no email_in_dn = yes policy = CA_sign_policy openiked-7.4/ikectl/ikectl.8000066400000000000000000000217271477554731100160300ustar00rootroot00000000000000.\" $OpenBSD: ikectl.8,v 1.27 2020/04/25 18:38:21 tobhe Exp $ .\" .\" Copyright (c) 2007-2013 Reyk Floeter .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate: April 25 2020 $ .Dt IKECTL 8 .Os .Sh NAME .Nm ikectl .Nd control the IKEv2 daemon .Sh SYNOPSIS .Nm .Op Fl q .Op Fl s Ar socket .Ar command .Op Ar arg ... .Sh DESCRIPTION The .Nm program controls the .Xr iked 8 daemon and provides commands to maintain a simple X.509 certificate authority (CA) for IKEv2 peers. .Pp The options are as follows: .Bl -tag -width Ds .It Fl q Don't ask for confirmation of any default options. .It Fl s Ar socket Use .Ar socket instead of the default .Pa /var/run/iked.sock to communicate with .Xr iked 8 . .El .Sh IKED CONTROL COMMANDS The following commands are available to control .Xr iked 8 : .Bl -tag -width Ds .It Cm active Set .Xr iked 8 to active mode. .It Cm passive Set .Xr iked 8 to passive mode. In passive mode no packets are sent to peers and no connections are initiated by .Xr iked 8 . .It Cm couple Load the negotiated security associations (SAs) and flows into the kernel. .It Cm decouple Unload the negotiated SAs and flows from the kernel. This mode is only useful for testing and debugging. .It Cm load Ar filename Reload the configuration from the specified file. .It Cm log brief Disable verbose logging. .It Cm log verbose Enable verbose logging. .It Cm monitor Monitor internal messages of the .Xr iked 8 subsystems. .It Cm reload Reload the configuration from the default configuration file. .It Cm reset all Reset the running state. .It Cm reset ca Reset the X.509 CA and certificate state. .It Cm reset policy Flush the configured policies. .It Cm reset sa Flush the running SAs. .It Cm reset user Flush the local user database. .It Cm reset id Ar ikeid Delete all IKE SAs with matching ID. .It Cm show sa Show internal state of active IKE SAs, Child SAs and IPsec flows. .El .Sh PKI AND CERTIFICATE AUTHORITY COMMANDS In order to use public key based authentication with IKEv2, a public key infrastructure (PKI) has to be set up to create and sign the peer certificates. .Nm includes commands to simplify maintenance of the PKI and to set up a simple certificate authority (CA) for .Xr iked 8 and its peers. .Pp The following commands are available to control the CA: .Bl -tag -width Ds .It Xo .Cm ca Ar name Cm create .Op Cm password Ar password .Xc Create a new certificate authority with the specified .Ar name . The command will prompt for a CA password unless it is specified with the optional .Ar password argument. The password will be saved in a protected file .Pa ikeca.passwd in the CA directory and used for subsequent commands. .It Cm ca Ar name Cm delete Delete the certificate authority with the specified .Ar name . .It Xo .Cm ca Ar name Cm export .Op Cm peer Ar peer .Op Cm password Ar password .Xc Export the certificate authority with the specified .Ar name into the current directory for transport to other systems. This command will create a compressed tarball called .Pa ca.tgz in the local directory and optionally .Pa ca.zip if the .Sq zip tool is installed. The optional .Ar peer argument can be used to specify the address or FQDN of the local gateway which will be written into a text file .Pa peer.txt and included in the archives. .It Xo .Cm ca Ar name .Cm install Op Ar path .Xc Install the certificate and Certificate Revocation List (CRL) for CA .Ar name as the currently active CA or into the specified .Ar path . .It Xo .Cm ca Ar name Cm certificate Ar host .Cm create .Op Ic server | client | ocsp .Xc Create a private key and certificate for .Ar host and sign then with the key of certificate authority with the specified .Ar name . .Pp The certificate will be valid for client and server authentication by default by setting both flags as the extended key usage in the certificate; this can be restricted using the optional .Ic server or .Ic client argument. If the .Ic ocsp argument is specified the extended key usage will be set for OCSP signing. .It Xo .Cm ca Ar name Cm certificate Ar host .Cm delete .Xc Deletes the private key and certificates associated with .Ar host . .It Xo .Cm ca Ar name Cm certificate Ar host .Cm export .Op Cm peer Ar peer .Op Cm password Ar password .Xc Export key files for .Ar host of the certificate authority with the specified .Ar name into the current directory for transport to other systems. This command will create a compressed tarball .Pa host.tgz in the local directory and optionally .Pa host.zip if the .Sq zip tool is installed. The optional .Ar peer argument can be used to specify the address or FQDN of the local gateway which will be written into a text file .Pa peer.txt and included in the archives. .It Xo .Cm ca Ar name Cm certificate Ar host .Cm install Op Ar path .Xc Install the private and public key for .Ar host into the active configuration or specified .Ar path . .It Xo .Cm ca Ar name Cm certificate Ar host .Cm revoke .Xc Revoke the certificate specified by .Ar host and generate a new Certificate Revocation List (CRL). .It Xo .Cm show Cm ca Ar name Cm certificates .Op Ar host .Xc Display a listing of certificates associated with CA .Ar name or display certificate details if .Ar host is specified. .It Xo .Cm ca Ar name Cm key Ar host .Cm create .Xc Create a private key for .Ar host if one does not already exist. .It Xo .Cm ca Ar name Cm key Ar host .Cm install Op Ar path .Xc Install the private and public keys for .Ar host into the active configuration or specified .Ar path . .It Xo .Cm ca Ar name Cm key Ar host .Cm delete .Xc Delete the private key for .Ar host . .It Xo .Cm ca Ar name Cm key Ar host .Cm import .Ar file .Xc Source the private key for .Ar host from the named .Ar file . .El .Sh FILES .Bl -tag -width "/var/run/iked.sockXX" -compact .It Pa /etc/iked/ Active configuration. .It Pa /etc/ssl/ Directory to store the CA files. .It Pa /usr/share/iked/ If this optional directory exists, .Nm will include the contents with the .Cm ca export commands. .It Pa /var/run/iked.sock Default .Ux Ns -domain socket used for communication with .Xr iked 8 . .El .Sh EXAMPLES First create a new certificate authority: .Bd -literal -offset indent # ikectl ca vpn create .Ed .Pp Now create the certificates for the VPN peers. The specified hostname, either IP address or FQDN, will be saved in the signed certificate and has to match the IKEv2 identity, or .Ar srcid , of the peers: .Bd -literal -offset indent # ikectl ca vpn certificate 10.1.2.3 create # ikectl ca vpn certificate 10.2.3.4 create # ikectl ca vpn certificate 10.3.4.5 create .Ed .Pp It is possible that the host that was used to create the CA is also one of the VPN peers. In this case you can install the peer and CA certificates locally: .Bd -literal -offset indent # ikectl ca vpn install # ikectl ca vpn certificate 10.1.2.3 install .Ed .Pp Now export the individual host key, the certificate and the CA certificate to each other peer. First run the .Ic export command to create tarballs that include the required files: .Bd -literal -offset indent # ikectl ca vpn certificate 10.2.3.4 export # ikectl ca vpn certificate 10.3.4.5 export .Ed .Pp These commands will produce two tarballs .Em 10.2.3.4.tgz and .Em 10.3.4.5.tgz . Copy these tarballs over to the appropriate peers and extract them to the .Pa /etc/iked/ directory: .Bd -literal -offset indent 10.2.3.4# tar -C /etc/iked -xzpf 10.2.3.4.tgz 10.3.4.5# tar -C /etc/iked -xzpf 10.3.4.5.tgz .Ed .Pp .Nm will also create .Sq zip archives 10.2.3.4.zip and 10.3.4.5.zip in addition to the tarballs if the zip tool is found in .Pa /usr/local/bin/zip . These archives can be exported to peers running Windows and will include the certificates in a format that is supported by the OS. The zip tool can be installed from the .Ox packages or ports collection before running the .Ic export commands, see .Xr packages 7 for more information. For example: .Bd -literal -offset indent # pkg_add zip .Ed .Sh SEE ALSO .Xr packages 7 , .Xr iked 8 , .Xr ssl 8 .Sh HISTORY The .Nm program first appeared in .Ox 4.8 . .Sh AUTHORS The .Nm program was written by .An Reyk Floeter Aq Mt reyk@openbsd.org and .An Jonathan Gray Aq Mt jsg@openbsd.org . .Sh CAVEATS For ease of use, the .Ic ca commands maintain all peers' private keys on the CA machine. In contrast to a .Sq real CA, it does not support signing of public keys that have been imported from peers that do not want to expose their private keys to the CA. openiked-7.4/ikectl/ikectl.c000066400000000000000000000253721477554731100161030ustar00rootroot00000000000000/* $OpenBSD: ikectl.c,v 1.34 2024/11/21 13:21:34 claudio Exp $ */ /* * Copyright (c) 2007-2013 Reyk Floeter * Copyright (c) 2005 Claudio Jeker * Copyright (c) 2004, 2005 Esben Norby * Copyright (c) 2003 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" #include "parser.h" __dead void usage(void); struct imsgname { int type; char *name; void (*func)(struct imsg *); }; struct imsgname *monitor_lookup(uint8_t); int monitor(struct imsg *); int show_string(struct imsg *); int show_stats(struct imsg *, int); int ca_opt(struct parse_result *); struct imsgname imsgs[] = { { IMSG_CTL_OK, "ok", NULL }, { IMSG_CTL_FAIL, "fail", NULL }, { IMSG_CTL_VERBOSE, "verbose", NULL }, { IMSG_CTL_RELOAD, "reload", NULL }, { IMSG_CTL_RESET, "reset", NULL }, { IMSG_CTL_SHOW_SA, "show sa", NULL }, { IMSG_CTL_SHOW_CERTSTORE, "show certstore", NULL }, { 0, NULL, NULL } }; struct imsgname imsgunknown = { -1, "", NULL }; struct imsgbuf *ibuf; __dead void usage(void) { extern char *__progname; fprintf(stderr, "usage: %s [-q] [-s socket] command [arg ...]\n", __progname); exit(1); } int ca_opt(struct parse_result *res) { struct ca *ca; size_t len; char *p; ca = ca_setup(res->caname, (res->action == CA_CREATE), res->quiet, res->pass); if (ca == NULL) errx(1, "ca_setup failed"); /* assume paths are relative to /etc if not absolute */ if (res->path && (res->path[0] != '.') && (res->path[0] != '/')) { len = 5 + strlen(res->path) + 1; if ((p = malloc(len)) == NULL) err(1, "malloc"); snprintf(p, len, "/etc/%s", res->path); free(res->path); res->path = p; } switch (res->action) { case CA_CREATE: ca_create(ca); break; case CA_DELETE: ca_delete(ca); break; case CA_INSTALL: ca_install(ca, res->path); break; case CA_EXPORT: ca_export(ca, NULL, res->peer, res->pass); break; case CA_CERT_CREATE: case CA_SERVER: case CA_CLIENT: case CA_OCSP: ca_certificate(ca, res->host, res->htype, res->action); break; case CA_CERT_DELETE: ca_delkey(ca, res->host); break; case CA_CERT_INSTALL: ca_cert_install(ca, res->host, res->path); break; case CA_CERT_EXPORT: ca_export(ca, res->host, res->peer, res->pass); break; case CA_CERT_REVOKE: ca_revoke(ca, res->host); break; case SHOW_CA_CERTIFICATES: ca_show_certs(ca, res->host); break; case CA_KEY_CREATE: ca_key_create(ca, res->host); break; case CA_KEY_DELETE: ca_key_delete(ca, res->host); break; case CA_KEY_INSTALL: ca_key_install(ca, res->host, res->path); break; case CA_KEY_IMPORT: ca_key_import(ca, res->host, res->path); break; default: break; } return (0); } int main(int argc, char *argv[]) { struct sockaddr_un s_un; struct parse_result *res; struct imsg imsg; int ctl_sock; int done = 1; int n; int ch; int v = 0; int quiet = 0; const char *sock = IKED_SOCKET; while ((ch = getopt(argc, argv, "qs:")) != -1) { switch (ch) { case 'q': quiet = 1; break; case 's': sock = optarg; break; default: usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; /* parse options */ if ((res = parse(argc, argv)) == NULL) exit(1); res->quiet = quiet; switch (res->action) { case CA_CREATE: case CA_DELETE: case CA_INSTALL: case CA_EXPORT: case CA_CERT_CREATE: case CA_CLIENT: case CA_SERVER: case CA_OCSP: case CA_CERT_DELETE: case CA_CERT_INSTALL: case CA_CERT_EXPORT: case CA_CERT_REVOKE: case SHOW_CA: case SHOW_CA_CERTIFICATES: case CA_KEY_CREATE: case CA_KEY_DELETE: case CA_KEY_INSTALL: case CA_KEY_IMPORT: if (pledge("stdio proc exec rpath wpath cpath fattr tty", NULL) == -1) err(1, "pledge"); ca_opt(res); break; case NONE: usage(); break; default: goto connect; } return (0); connect: /* connect to iked control socket */ if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) err(1, "socket"); bzero(&s_un, sizeof(s_un)); s_un.sun_family = AF_UNIX; strlcpy(s_un.sun_path, sock, sizeof(s_un.sun_path)); reconnect: if (connect(ctl_sock, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { /* Keep retrying if running in monitor mode */ if (res->action == MONITOR && (errno == ENOENT || errno == ECONNREFUSED)) { usleep(100); goto reconnect; } err(1, "connect: %s", sock); } if (pledge("stdio", NULL) == -1) err(1, "pledge"); if (res->ibuf != NULL) ibuf = res->ibuf; else if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) err(1, "malloc"); imsgbuf_init(ibuf, ctl_sock); /* process user request */ switch (res->action) { case RESETALL: v = RESET_ALL; break; case RESETCA: v = RESET_CA; break; case RESETPOLICY: v = RESET_POLICY; break; case RESETSA: v = RESET_SA; break; case RESETUSER: v = RESET_USER; break; case LOG_VERBOSE: v = 2; break; case LOG_BRIEF: default: v = 0; break; } switch (res->action) { case NONE: usage(); /* NOTREACHED */ break; case RESETALL: case RESETCA: case RESETPOLICY: case RESETSA: case RESETUSER: imsg_compose(ibuf, IMSG_CTL_RESET, 0, 0, -1, &v, sizeof(v)); printf("reset request sent.\n"); break; case LOAD: imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, res->path, strlen(res->path)); break; case RESET_ID: imsg_compose(ibuf, IMSG_CTL_RESET_ID, 0, 0, -1, res->id, strlen(res->id)); break; case SHOW_SA: imsg_compose(ibuf, IMSG_CTL_SHOW_SA, 0, 0, -1, NULL, 0); done = 0; break; case SHOW_STATS: imsg_compose(ibuf, IMSG_CTL_SHOW_STATS, 0, 0, -1, NULL, 0); done = 0; break; case SHOW_CERTSTORE: imsg_compose(ibuf, IMSG_CTL_SHOW_CERTSTORE, 0, 0, -1, NULL, 0); done = 0; break; case RELOAD: imsg_compose(ibuf, IMSG_CTL_RELOAD, 0, 0, -1, NULL, 0); break; case MONITOR: imsg_compose(ibuf, IMSG_CTL_NOTIFY, 0, 0, -1, NULL, 0); done = 0; break; case COUPLE: imsg_compose(ibuf, IMSG_CTL_COUPLE, 0, 0, -1, NULL, 0); break; case DECOUPLE: imsg_compose(ibuf, IMSG_CTL_DECOUPLE, 0, 0, -1, NULL, 0); break; case ACTIVE: imsg_compose(ibuf, IMSG_CTL_ACTIVE, 0, 0, -1, NULL, 0); break; case PASSIVE: imsg_compose(ibuf, IMSG_CTL_PASSIVE, 0, 0, -1, NULL, 0); break; case LOG_VERBOSE: case LOG_BRIEF: imsg_compose(ibuf, IMSG_CTL_VERBOSE, 0, 0, -1, &v, sizeof(v)); printf("logging request sent.\n"); break; default: break; } if (imsgbuf_flush(ibuf) == -1) err(1, "write error"); while (!done) { if ((n = imsgbuf_read(ibuf)) == -1) errx(1, "imsgbuf_read error"); if (n == 0) errx(1, "pipe closed"); while (!done) { if ((n = imsg_get(ibuf, &imsg)) == -1) errx(1, "imsg_get error"); if (n == 0) break; switch (res->action) { case MONITOR: done = monitor(&imsg); break; case SHOW_STATS: done = show_stats(&imsg, quiet); break; case SHOW_SA: case SHOW_CERTSTORE: done = show_string(&imsg); break; default: break; } imsg_free(&imsg); } } close(ctl_sock); free(ibuf); return (0); } struct imsgname * monitor_lookup(uint8_t type) { int i; for (i = 0; imsgs[i].name != NULL; i++) if (imsgs[i].type == type) return (&imsgs[i]); return (&imsgunknown); } int monitor(struct imsg *imsg) { time_t now; int done = 0; struct imsgname *imn; now = time(NULL); imn = monitor_lookup(imsg->hdr.type); printf("%s: imsg type %u len %u peerid %u pid %d\n", imn->name, imsg->hdr.type, imsg->hdr.len, imsg->hdr.peerid, imsg->hdr.pid); printf("\ttimestamp: %lld, %s", (long long)now, ctime(&now)); if (imn->type == -1) done = 1; if (imn->func != NULL) (*imn->func)(imsg); return (done); } int show_string(struct imsg *imsg) { int done = 0; switch (imsg->hdr.type) { case IMSG_CTL_SHOW_SA: case IMSG_CTL_SHOW_CERTSTORE: break; default: return (done); } if (IMSG_DATA_SIZE(imsg) > 0) printf("%s", (char *)imsg->data); else done = 1; return (done); } static char * plural(uint64_t n) { return (n != 1 ? "s" : ""); } /* * Dump IKE statistics structure. */ int show_stats(struct imsg *imsg, int quiet) { struct iked_stats *stat; int done = 1; if (IMSG_DATA_SIZE(imsg) != sizeof(*stat)) return (done); stat = imsg->data; printf("ike:\n"); #define p(f, m) if (stat->f || !quiet) \ printf(m, (unsigned long long)stat->f, plural(stat->f)) p(ikes_sa_created, "\t%llu IKE SA%s created\n"); p(ikes_sa_established_total, "\t%llu IKE SA%s established\n"); p(ikes_sa_established_current, "\t%llu IKE SA%s currently established\n"); p(ikes_sa_established_failures, "\t%llu IKE SA%s failed to establish\n"); p(ikes_sa_proposals_negotiate_failures, "\t%llu failed proposal negotiation%s\n"); p(ikes_sa_rekeyed, "\t%llu IKE SA%s rekeyed\n"); p(ikes_sa_removed, "\t%llu IKE SA%s removed\n"); p(ikes_csa_created, "\t%llu Child SA%s created\n"); p(ikes_csa_removed, "\t%llu Child SA%s removed\n"); p(ikes_msg_sent, "\t%llu message%s sent\n"); p(ikes_msg_send_failures, "\t%llu message%s could not be sent\n"); p(ikes_msg_rcvd, "\t%llu message%s received\n"); p(ikes_msg_rcvd_dropped, "\t%llu message%s dropped\n"); p(ikes_msg_rcvd_busy, "\t%llu request%s dropped, response being worked on\n"); p(ikes_retransmit_response, "\t%llu response%s retransmitted\n"); p(ikes_retransmit_request, "\t%llu request%s retransmitted\n"); p(ikes_retransmit_limit, "\t%llu request%s timed out\n"); p(ikes_frag_sent, "\t%llu fragment%s sent\n"); p(ikes_frag_send_failures, "\t%llu fragment%s could not be sent\n"); p(ikes_frag_rcvd, "\t%llu fragment%s received\n"); p(ikes_frag_rcvd_drop, "\t%llu fragment%s dropped\n"); p(ikes_frag_reass_ok, "\t%llu fragment%s reassembled\n"); p(ikes_frag_reass_drop, "\t%llu fragment%s could not be reassembled\n"); p(ikes_update_addresses_sent, "\t%llu update addresses request%s sent\n"); p(ikes_dpd_sent, "\t%llu dpd request%s sent\n"); p(ikes_keepalive_sent, "\t%llu keepalive message%s sent\n"); #undef p return (done); } openiked-7.4/ikectl/ikex509v3.cnf000066400000000000000000000017561477554731100166230ustar00rootroot00000000000000# default settings CERTPATHLEN = 1 CERTUSAGE = digitalSignature,keyCertSign,cRLSign EXTCERTUSAGE = serverAuth,clientAuth CERTIP = 0.0.0.0 CERTFQDN = nohost.nodomain # This section should be referenced when building an x509v3 CA # Certificate. # The default path length and the key usage can be overridden # modified by setting the CERTPATHLEN and CERTUSAGE environment # variables. [x509v3_CA] basicConstraints=critical,CA:true,pathlen:$ENV::CERTPATHLEN keyUsage=$ENV::CERTUSAGE # This section should be referenced to add an IP Address # as an alternate subject name, needed by isakmpd # The address must be provided in the CERTIP environment variable [x509v3_IPAddr] subjectAltName=IP:$ENV::CERTIP extendedKeyUsage=$ENV::EXTCERTUSAGE # This section should be referenced to add a FQDN hostname # as an alternate subject name, needed by isakmpd # The address must be provided in the CERTFQDN environment variable [x509v3_FQDN] subjectAltName=DNS:$ENV::CERTFQDN extendedKeyUsage=$ENV::EXTCERTUSAGE openiked-7.4/ikectl/parser.c000066400000000000000000000244211477554731100161160ustar00rootroot00000000000000/* $OpenBSD: parser.c,v 1.21 2022/09/19 20:54:02 tobhe Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter * Copyright (c) 2004 Esben Norby * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" #include "parser.h" enum token_type { NOTOKEN, ENDTOKEN, KEYWORD, PATH, CANAME, PEER, ADDRESS, FQDN, PASSWORD, IKEID }; struct token { enum token_type type; const char *keyword; int value; const struct token *next; }; static const struct token t_main[]; static const struct token t_reset[]; static const struct token t_reset_id[]; static const struct token t_log[]; static const struct token t_load[]; static const struct token t_ca[]; static const struct token t_ca_pass[]; static const struct token t_ca_pass_val[]; static const struct token t_ca_export[]; static const struct token t_ca_ex_peer[]; static const struct token t_ca_ex_pass[]; static const struct token t_ca_modifiers[]; static const struct token t_ca_cert[]; static const struct token t_ca_cert_extusage[]; static const struct token t_ca_cert_modifiers[]; static const struct token t_ca_key[]; static const struct token t_ca_key_modifiers[]; static const struct token t_ca_key_path[]; static const struct token t_show[]; static const struct token t_show_ca[]; static const struct token t_show_ca_modifiers[]; static const struct token t_show_ca_cert[]; static const struct token t_opt_path[]; static const struct token t_main[] = { { KEYWORD, "active", ACTIVE, NULL }, { KEYWORD, "passive", PASSIVE, NULL }, { KEYWORD, "couple", COUPLE, NULL }, { KEYWORD, "decouple", DECOUPLE, NULL }, { KEYWORD, "load", LOAD, t_load }, { KEYWORD, "log", NONE, t_log }, { KEYWORD, "monitor", MONITOR, NULL }, { KEYWORD, "reload", RELOAD, NULL }, { KEYWORD, "reset", NONE, t_reset }, { KEYWORD, "show", NONE, t_show }, { KEYWORD, "ca", CA, t_ca }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_log[] = { { KEYWORD, "verbose", LOG_VERBOSE, NULL }, { KEYWORD, "brief", LOG_BRIEF, NULL }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_reset[] = { { KEYWORD, "all", RESETALL, NULL }, { KEYWORD, "ca", RESETCA, NULL }, { KEYWORD, "policy", RESETPOLICY, NULL }, { KEYWORD, "sa", RESETSA, NULL }, { KEYWORD, "user", RESETUSER, NULL }, { KEYWORD, "id", RESET_ID, t_reset_id }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_reset_id[] = { { IKEID, "", NONE, NULL }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_load[] = { { PATH, "", NONE, NULL }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_ca[] = { { CANAME, "", NONE, t_ca_modifiers }, { ENDTOKEN, "", NONE, NULL }, }; static const struct token t_ca_modifiers[] = { { KEYWORD, "create", CA_CREATE, t_ca_pass }, { KEYWORD, "delete", CA_DELETE, NULL }, { KEYWORD, "install", CA_INSTALL, t_opt_path }, { KEYWORD, "certificate", CA_CERTIFICATE, t_ca_cert }, { KEYWORD, "key", NONE, t_ca_key }, { KEYWORD, "export", CA_EXPORT, t_ca_export }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_ca_pass_val[] = { { PASSWORD, "", NONE, NULL }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_ca_pass[] = { { NOTOKEN, "", NONE, NULL }, { KEYWORD, "password", NONE, t_ca_pass_val }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_ca_export[] = { { NOTOKEN, "", NONE, NULL }, { KEYWORD, "peer", NONE, t_ca_ex_peer }, { KEYWORD, "password", NONE, t_ca_ex_pass }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_ca_ex_peer[] = { { PEER, "", NONE, t_ca_export }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_ca_ex_pass[] = { { PASSWORD, "", NONE, t_ca_export }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_opt_path[] = { { NOTOKEN, "", NONE, NULL }, { PATH, "", NONE, NULL }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_ca_cert[] = { { ADDRESS, "", NONE, t_ca_cert_modifiers }, { FQDN, "", NONE, t_ca_cert_modifiers }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_ca_cert_modifiers[] = { { KEYWORD, "create", CA_CERT_CREATE, t_ca_cert_extusage }, { KEYWORD, "delete", CA_CERT_DELETE, NULL }, { KEYWORD, "install", CA_CERT_INSTALL, t_opt_path }, { KEYWORD, "export", CA_CERT_EXPORT, t_ca_export }, { KEYWORD, "revoke", CA_CERT_REVOKE, NULL }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_ca_cert_extusage[] = { { NOTOKEN, "", NONE, NULL}, { KEYWORD, "server", CA_SERVER, NULL }, { KEYWORD, "client", CA_CLIENT, NULL }, { KEYWORD, "ocsp", CA_OCSP, NULL }, { ENDTOKEN, "", NONE, NULL }, }; static const struct token t_ca_key[] = { { ADDRESS, "", NONE, t_ca_key_modifiers }, { FQDN, "", NONE, t_ca_key_modifiers }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_ca_key_modifiers[] = { { KEYWORD, "create", CA_KEY_CREATE, NULL }, { KEYWORD, "delete", CA_KEY_DELETE, NULL }, { KEYWORD, "install", CA_KEY_INSTALL, t_opt_path }, { KEYWORD, "import", CA_KEY_IMPORT, t_ca_key_path }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_ca_key_path[] = { { PATH, "", NONE, NULL }, { PATH, "", NONE, NULL } }; static const struct token t_show[] = { { KEYWORD, "ca", SHOW_CA, t_show_ca }, { KEYWORD, "sa", SHOW_SA, NULL }, { KEYWORD, "certstore", SHOW_CERTSTORE,NULL }, { KEYWORD, "stats", SHOW_STATS, NULL }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_show_ca[] = { { CANAME, "", NONE, t_show_ca_modifiers }, { ENDTOKEN, "", NONE, NULL }, }; static const struct token t_show_ca_modifiers[] = { { KEYWORD, "certificates", SHOW_CA_CERTIFICATES, t_show_ca_cert }, { ENDTOKEN, "", NONE, NULL } }; static const struct token t_show_ca_cert[] = { { NOTOKEN, "", NONE, NULL }, { ADDRESS, "", NONE, NULL }, { FQDN, "", NONE, NULL }, { ENDTOKEN, "", NONE, NULL } }; static struct parse_result res; const struct token *match_token(char *, const struct token []); void show_valid_args(const struct token []); int parse_addr(const char *); struct parse_result * parse(int argc, char *argv[]) { const struct token *table = t_main; const struct token *match; bzero(&res, sizeof(res)); while (argc >= 0) { if ((match = match_token(argv[0], table)) == NULL) { fprintf(stderr, "valid commands/args:\n"); show_valid_args(table); return (NULL); } argc--; argv++; if (match->type == NOTOKEN || match->next == NULL) break; table = match->next; } if (argc > 0) { fprintf(stderr, "superfluous argument: %s\n", argv[0]); return (NULL); } return (&res); } int parse_addr(const char *word) { struct addrinfo hints, *r; bzero(&hints, sizeof(hints)); hints.ai_socktype = SOCK_DGRAM; /* dummy */ hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(word, "0", &hints, &r) == 0) { freeaddrinfo(r); return (0); } return (1); } const struct token * match_token(char *word, const struct token table[]) { unsigned int i, match = 0; const struct token *t = NULL; for (i = 0; table[i].type != ENDTOKEN; i++) { switch (table[i].type) { case NOTOKEN: if (word == NULL || strlen(word) == 0) { match++; t = &table[i]; } break; case KEYWORD: if (word != NULL && strncmp(word, table[i].keyword, strlen(word)) == 0) { match++; t = &table[i]; if (t->value) res.action = t->value; } break; case PATH: if (!match && word != NULL && strlen(word) > 0) { res.path = strdup(word); match++; t = &table[i]; } break; case CANAME: if (!match && word != NULL && strlen(word) > 0) { res.caname = strdup(word); match++; t = &table[i]; } break; case PEER: if (!match && word != NULL && strlen(word) > 0) { res.peer = strdup(word); match++; t = &table[i]; } break; case ADDRESS: case FQDN: if (!match && word != NULL && strlen(word) > 0) { res.host = strdup(word); if (parse_addr(word) == 0) res.htype = HOST_IPADDR; else res.htype = HOST_FQDN; match++; t = &table[i]; } break; case PASSWORD: if (!match && word != NULL && strlen(word) > 0) { res.pass = strdup(word); match++; t = &table[i]; } break; case IKEID: if (!match && word != NULL && strlen(word) > 0) { res.id = strdup(word); match++; t = &table[i]; } break; case ENDTOKEN: break; } } if (match != 1) { if (word == NULL) fprintf(stderr, "missing argument:\n"); else if (match > 1) fprintf(stderr, "ambiguous argument: %s\n", word); else if (match < 1) fprintf(stderr, "unknown argument: %s\n", word); return (NULL); } return (t); } void show_valid_args(const struct token table[]) { int i; for (i = 0; table[i].type != ENDTOKEN; i++) { switch (table[i].type) { case NOTOKEN: fprintf(stderr, " \n"); break; case KEYWORD: fprintf(stderr, " %s\n", table[i].keyword); break; case PATH: fprintf(stderr, " \n"); break; case CANAME: fprintf(stderr, " \n"); break; case PASSWORD: fprintf(stderr, " \n"); break; case PEER: fprintf(stderr, " \n"); break; case ADDRESS: fprintf(stderr, " \n"); break; case FQDN: fprintf(stderr, " \n"); break; case IKEID: fprintf(stderr, " \n"); break; case ENDTOKEN: break; } } } openiked-7.4/ikectl/parser.h000066400000000000000000000044341477554731100161250ustar00rootroot00000000000000/* $OpenBSD: parser.h,v 1.18 2022/09/19 20:54:02 tobhe Exp $ */ /* * Copyright (c) 2007-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef IKECTL_PARSER_H #define IKECTL_PARSER_H enum actions { NONE, LOAD, RELOAD, MONITOR, LOG_VERBOSE, LOG_BRIEF, COUPLE, DECOUPLE, ACTIVE, PASSIVE, RESETALL, RESETCA, RESETPOLICY, RESETSA, RESETUSER, CA, CA_CREATE, CA_DELETE, CA_INSTALL, CA_EXPORT, CA_CERTIFICATE, CA_CERT_CREATE, CA_SERVER, CA_CLIENT, CA_OCSP, CA_CERT_DELETE, CA_CERT_INSTALL, CA_CERT_EXPORT, CA_CERT_REVOKE, CA_KEY_CREATE, CA_KEY_DELETE, CA_KEY_INSTALL, CA_KEY_IMPORT, SHOW_CA, SHOW_CA_CERTIFICATES, SHOW_SA, RESET_ID, SHOW_CERTSTORE, SHOW_STATS }; struct parse_result { enum actions action; struct imsgbuf *ibuf; char *path; char *caname; char *pass; char *host; char *peer; char *id; int htype; int quiet; }; #define HOST_IPADDR 1 #define HOST_FQDN 2 struct parse_result *parse(int, char *[]); struct ca *ca_setup(char *, int, int, char *); int ca_create(struct ca *); int ca_certificate(struct ca *, char *, int, int); int ca_export(struct ca *, char *, char *, char *); int ca_revoke(struct ca *, char *); int ca_delete(struct ca *); int ca_delkey(struct ca *, char *); int ca_install(struct ca *, char *); int ca_cert_install(struct ca *, char *, char *); int ca_show_certs(struct ca *, char *); int ca_key_create(struct ca *, char *); int ca_key_delete(struct ca *, char *); int ca_key_install(struct ca *, char *, char *); int ca_key_import(struct ca *, char *, char *); #endif /* IKECTL_PARSER_H */ openiked-7.4/iked.conf000066400000000000000000000014671477554731100147730ustar00rootroot00000000000000# $OpenBSD: iked.conf,v 1.1 2014/07/11 21:20:10 deraadt Exp $ # # See iked.conf(5) for syntax and examples. # Configure users for the Extensible Authentication Protocol (EAP) #user "user1" "password123" #user "user2" "password456" # Configuration for clients connecting with EAP authentication. # Remember to set up a PKI, see ikectl(8) for more information. #ikev2 "win7" passive esp \ # from 10.1.0.0/24 to 10.2.0.0/24 \ # local any peer any \ # eap "mschap-v2" \ # config address 10.2.0.1 \ # config name-server 10.1.0.2 \ # tag "$name-$id" # Configuration for a client authenticating with a pre-shared key. #ikev2 esp \ # from 10.3.0.0/24 to 10.1.0.0/24 \ # from 10.5.0.0/24 to 10.1.0.0/24 \ # from 10.5.0.0/24 to 172.16.1.0/24 \ # local 192.168.1.1 peer 192.168.2.1 \ # psk "you-should-not-use-psk-authentication!" openiked-7.4/iked/000077500000000000000000000000001477554731100141145ustar00rootroot00000000000000openiked-7.4/iked/CMakeLists.txt000066400000000000000000000114561477554731100166630ustar00rootroot00000000000000# Copyright (c) 2020-2021 Tobias Heider # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. include(CheckFunctionExists) include(CheckLibraryExists) include(CheckSymbolExists) include(CheckIncludeFiles) set(VERSIONED_FILES) list(APPEND VERSIONED_FILES iked.c) set(SRCS) set(LIBS) set(CFLAGS) list(APPEND CFLAGS -O2 -fstack-protector-strong -fPIE -D_FORTIFY_SOURCE=2 -Wall -Wno-pointer-sign -Wno-deprecated-declarations -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wshadow -Wpointer-arith -Wcast-qual -Wsign-compare "$<$:-O0;-g>" ) set(INC_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../iked ) add_library(iked-shared OBJECT ikev2_pld.c imsg_util.c log.c util.c ${CMAKE_CURRENT_BINARY_DIR}/ikev2_map.c ${CMAKE_CURRENT_BINARY_DIR}/eap_map.c ) if(CMAKE_SYSTEM_NAME MATCHES "OpenBSD") list(APPEND SRCS ipsec.c pfkey.c) elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") list(APPEND SRCS ipsec.c pfkey.c) elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin") list(APPEND SRCS ipsec.c pfkey.c) elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") list(APPEND SRCS ipsec.c pfkey.c) elseif(CMAKE_SYSTEM_NAME MATCHES "NetBSD") list(APPEND SRCS ipsec.c pfkey.c) endif() if(HAVE_VROUTE) list(APPEND SRCS vroute.c) endif() if(HAVE_VROUTE_NETLINK) list(APPEND SRCS vroute-netlink.c) endif() if (WITH_APPARMOR) list(APPEND SRCS apparmor.c) endif() list(APPEND SRCS ca.c chap_ms.c config.c control.c crypto.c dh.c eap.c iked.c ikev2.c ikev2.h ikev2_msg.c ocsp.c policy.c print.c proc.c smult_curve25519_ref.c timer.c crypto_hash.c sntrup761.c # Generated files ${CMAKE_CURRENT_BINARY_DIR}/parse.c ) add_executable(iked ${SRCS}) target_compile_options(iked PRIVATE ${CFLAGS}) target_compile_options(iked-shared PRIVATE ${CFLAGS}) if(HAVE_LD_Z) target_link_options(iked PRIVATE "LINKER:-z,relro,-z,now") endif() target_include_directories(iked PRIVATE ${INC_DIRS}) target_include_directories(iked-shared PRIVATE ${INC_DIRS}) if(DEFINED ENV{CLUSTERFUZZLITE}) target_link_libraries(iked-shared "-lm -Wl,-Bstatic -lssl -lcrypto -levent -Wl,-Bdynamic" compat ) else() target_link_libraries(iked-shared PRIVATE util event crypto ssl compat ) endif() list(APPEND LIBS util event crypto ssl compat iked-shared ) if (WITH_SYSTEMD) list(APPEND LIBS systemd) endif() target_link_libraries(iked PRIVATE ${LIBS} ) add_custom_command( OUTPUT parse.c COMMAND yacc -o parse.c ${CMAKE_CURRENT_SOURCE_DIR}/parse.y DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/parse.y ) add_custom_command( OUTPUT ikev2_map.c COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/genmap.sh ${CMAKE_CURRENT_SOURCE_DIR}/ikev2.h ikev2 > ikev2_map.c DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/ikev2.h DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/genmap.sh ) add_custom_command( OUTPUT eap_map.c COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/genmap.sh ${CMAKE_CURRENT_SOURCE_DIR}/eap.h eap > eap_map.c DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/eap.h DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/genmap.sh ) install(TARGETS iked RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR}) install(FILES ${CMAKE_SOURCE_DIR}/iked.conf PERMISSIONS OWNER_READ OWNER_WRITE DESTINATION ${CMAKE_INSTALL_SYSCONFDIR} ) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/iked.conf.5 DESTINATION ${CMAKE_INSTALL_MANDIR}/man5/) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/iked.8 DESTINATION ${CMAKE_INSTALL_MANDIR}/man8/) if(WITH_APPARMOR) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../linux/iked.apparmor DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/apparmor.d/ RENAME usr.sbin.iked) endif() install(DIRECTORY DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/iked/ca) install(DIRECTORY DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/iked/certs) install(DIRECTORY DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/iked/crls) install(DIRECTORY DIRECTORY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/iked/private ) install(DIRECTORY DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/iked/pubkeys/ipv4) install(DIRECTORY DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/iked/pubkeys/ipv6) install(DIRECTORY DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/iked/pubkeys/fqdn) install(DIRECTORY DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/iked/pubkeys/ufqdn) openiked-7.4/iked/Makefile000066400000000000000000000020171477554731100155540ustar00rootroot00000000000000# $OpenBSD: Makefile,v 1.22 2021/05/28 18:01:39 tobhe Exp $ PROG= iked SRCS= ca.c chap_ms.c config.c control.c crypto.c dh.c \ eap.c iked.c ikev2.c ikev2_msg.c ikev2_pld.c \ log.c ocsp.c pfkey.c policy.c print.c proc.c timer.c util.c \ imsg_util.c smult_curve25519_ref.c vroute.c SRCS+= eap_map.c ikev2_map.c SRCS+= crypto_hash.c sntrup761.c SRCS+= parse.y SRCS+= ipsec.c MAN= iked.conf.5 iked.8 #NOMAN= yes LDADD= -lutil -levent -lcrypto DPADD= ${LIBUTIL} ${LIBEVENT} ${LIBCRYPTO} CFLAGS+= -Wall -I${.CURDIR} CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations CFLAGS+= -Wshadow -Wpointer-arith -Wcast-qual CFLAGS+= -Wsign-compare CLEANFILES+= ikev2_map.c eap_map.c GENERATED= ikev2_map.c eap_map.c YFLAGS= ikev2_map.c: genmap.sh ikev2.h /bin/sh ${.CURDIR}/genmap.sh ${.CURDIR}/ikev2.h ikev2 > $@ @touch $@ eap_map.c: genmap.sh eap.h /bin/sh ${.CURDIR}/genmap.sh ${.CURDIR}/eap.h eap > $@ @touch $@ .include # Don't compile iked as static binary by default LDSTATIC= openiked-7.4/iked/apparmor.c000066400000000000000000000027361477554731100161110ustar00rootroot00000000000000/* * Copyright (c) 2023 Tobias Heider * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "apparmor.h" static const char *armor_proc_path_tmpl = "/proc/%d/attr/apparmor/%s"; int armor_proc_open(void) { char *path; pid_t tid = gettid(); int fd; int ret = -1; ret = asprintf(&path, armor_proc_path_tmpl, tid, "current"); if (ret <= 0) return (-1); fd = open(path, O_WRONLY); free(path); return (fd); } int armor_change_profile(int fd, const char *profile) { char *cmd = NULL; int len; int ret = -1; len = asprintf(&cmd, "changeprofile %s", profile); if (len < 0) goto done; ret = write(fd, cmd, len); if (ret == -1) goto done; ret = 0; done: free(cmd); close(fd); return (ret); } openiked-7.4/iked/apparmor.h000066400000000000000000000017061477554731100161120ustar00rootroot00000000000000/* * Copyright (c) 2019-2023 Tobias Heider * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef IKED_APPARMOR_H #define IKED_APPARMOR_H /* apparmor.c */ int armor_proc_open(void); int armor_change_profile(int, const char *); #endif /* IKED_APPARMOR_H */ openiked-7.4/iked/ca.c000066400000000000000000001442301477554731100146470ustar00rootroot00000000000000/* $OpenBSD: ca.c,v 1.102 2024/06/18 05:08:41 tb Exp $ */ /* * Copyright (c) 2020-2021 Tobias Heider * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if OPENSSL_VERSION_NUMBER >= 0x30000000 #include #endif #include "iked.h" #include "ikev2.h" #ifdef WITH_APPARMOR #include "apparmor.h" #endif void ca_run(struct privsep *, struct privsep_proc *, void *); void ca_shutdown(void); void ca_reset(struct iked *); int ca_reload(struct iked *); int ca_cert_local(struct iked *, X509 *); int ca_getreq(struct iked *, struct imsg *); int ca_getcert(struct iked *, struct imsg *); int ca_getauth(struct iked *, struct imsg *); X509 *ca_by_subjectpubkey(X509_STORE *, uint8_t *, size_t); X509 *ca_by_issuer(X509_STORE *, X509_NAME *, struct iked_static_id *); X509 *ca_by_subjectaltname(X509_STORE *, struct iked_static_id *); void ca_store_certs_info(const char *, X509_STORE *); int ca_subjectpubkey_digest(X509 *, uint8_t *, unsigned int *); int ca_x509_subject_cmp(X509 *, struct iked_static_id *); int ca_validate_pubkey(struct iked *, struct iked_static_id *, void *, size_t, struct iked_id *); int ca_validate_cert(struct iked *, struct iked_static_id *, void *, size_t, STACK_OF(X509) *, X509 **); EVP_PKEY * ca_bytes_to_pkey(uint8_t *, size_t); int ca_privkey_to_method(struct iked_id *); struct ibuf * ca_x509_serialize(X509 *); int ca_x509_subjectaltname_do(X509 *, int, const char *, struct iked_static_id *, struct iked_id *); int ca_x509_subjectaltname_cmp(X509 *, struct iked_static_id *); int ca_x509_subjectaltname_log(X509 *, const char *); int ca_x509_subjectaltname_get(X509 *cert, struct iked_id *); int ca_dispatch_parent(int, struct privsep_proc *, struct imsg *); int ca_dispatch_ikev2(int, struct privsep_proc *, struct imsg *); int ca_dispatch_control(int, struct privsep_proc *, struct imsg *); void ca_store_info(struct iked *, struct imsg *, const char *, X509_STORE *); static struct privsep_proc procs[] = { { "parent", PROC_PARENT, ca_dispatch_parent }, { "ikev2", PROC_IKEV2, ca_dispatch_ikev2 }, { "control", PROC_CONTROL, ca_dispatch_control } }; struct ca_store { X509_STORE *ca_cas; X509_LOOKUP *ca_calookup; X509_STORE *ca_certs; X509_LOOKUP *ca_certlookup; struct iked_id ca_privkey; struct iked_id ca_pubkey; uint8_t ca_privkey_method; }; void caproc(struct privsep *ps, struct privsep_proc *p) { proc_run(ps, p, procs, nitems(procs), ca_run, NULL); } void ca_run(struct privsep *ps, struct privsep_proc *p, void *arg) { struct iked *env = iked_env; struct ca_store *store; /* * pledge in the ca process: * stdio - for malloc and basic I/O including events. * rpath - for certificate files. * recvfd - for ocsp sockets. */ if (pledge("stdio rpath recvfd", NULL) == -1) fatal("pledge"); #ifdef WITH_APPARMOR if (armor_change_profile(ps->ps_env->sc_apparmor, "iked//ca") == -1) log_warnx("warning: armor_change_profile " "(\"iked//ca\") failed"); #endif if ((store = calloc(1, sizeof(*store))) == NULL) fatal("%s: failed to allocate cert store", __func__); env->sc_priv = store; p->p_shutdown = ca_shutdown; } void ca_shutdown(void) { struct iked *env = iked_env; struct ca_store *store; ibuf_free(env->sc_certreq); if ((store = env->sc_priv) == NULL) return; X509_STORE_free(store->ca_cas); X509_STORE_free(store->ca_certs); ibuf_free(store->ca_pubkey.id_buf); ibuf_free(store->ca_privkey.id_buf); free(store); } void ca_getkey(struct privsep *ps, struct iked_id *key, enum imsg_type type) { struct iked *env = iked_env; struct ca_store *store = env->sc_priv; struct iked_id *id = NULL; const char *name; if (store == NULL) fatalx("%s: invalid store", __func__); if (type == IMSG_PRIVKEY) { name = "private"; id = &store->ca_privkey; store->ca_privkey_method = ca_privkey_to_method(key); if (store->ca_privkey_method == IKEV2_AUTH_NONE) fatalx("ca: failed to get auth method for privkey"); } else if (type == IMSG_PUBKEY) { name = "public"; id = &store->ca_pubkey; } else fatalx("%s: invalid type %d", __func__, type); log_debug("%s: received %s key type %s length %zd", __func__, name, print_map(key->id_type, ikev2_cert_map), ibuf_length(key->id_buf)); /* clear old key and copy new one */ ibuf_free(id->id_buf); memcpy(id, key, sizeof(*id)); } void ca_reset(struct iked *env) { struct ca_store *store = env->sc_priv; if (store->ca_privkey.id_type == IKEV2_ID_NONE || store->ca_pubkey.id_type == IKEV2_ID_NONE) fatalx("ca_reset: keys not loaded"); X509_STORE_free(store->ca_cas); X509_STORE_free(store->ca_certs); if ((store->ca_cas = X509_STORE_new()) == NULL) fatalx("ca_reset: failed to get ca store"); if ((store->ca_calookup = X509_STORE_add_lookup(store->ca_cas, X509_LOOKUP_file())) == NULL) fatalx("ca_reset: failed to add ca lookup"); if ((store->ca_certs = X509_STORE_new()) == NULL) fatalx("ca_reset: failed to get cert store"); if ((store->ca_certlookup = X509_STORE_add_lookup(store->ca_certs, X509_LOOKUP_file())) == NULL) fatalx("ca_reset: failed to add cert lookup"); if (ca_reload(env) != 0) fatal("ca_reset: reload"); } int ca_certbundle_add(struct ibuf *buf, struct iked_id *id) { uint8_t type = id->id_type; size_t len = ibuf_size(id->id_buf); void *val = ibuf_data(id->id_buf); if (buf == NULL || ibuf_add(buf, &type, sizeof(type)) != 0 || ibuf_add(buf, &len, sizeof(len)) != 0 || ibuf_add(buf, val, len) != 0) return -1; return 0; } /* * decode cert bundle to cert and untrusted intermediate CAs. * datap/lenp point to bundle on input and to decoded cert output */ static int ca_decode_cert_bundle(struct iked *env, struct iked_sahdr *sh, uint8_t **datap, size_t *lenp, STACK_OF(X509) **untrustedp) { STACK_OF(X509) *untrusted = NULL; X509 *cert; BIO *rawcert = NULL; uint8_t *certdata = NULL; size_t certlen = 0; uint8_t datatype; size_t datalen = 0; uint8_t *ptr; size_t len; int ret = -1; log_debug("%s: decoding cert bundle", SPI_SH(sh, __func__)); ptr = *datap; len = *lenp; *untrustedp = NULL; /* allocate stack for intermediate CAs */ if ((untrusted = sk_X509_new_null()) == NULL) goto done; /* parse TLV, see ca_certbundle_add() */ while (len > 0) { /* Type */ if (len < sizeof(datatype)) { log_debug("%s: short data (type)", SPI_SH(sh, __func__)); goto done; } memcpy(&datatype, ptr, sizeof(datatype)); ptr += sizeof(datatype); len -= sizeof(datatype); /* Only X509 certs/CAs are supported */ if (datatype != IKEV2_CERT_X509_CERT) { log_info("%s: unsupported data type: %s", SPI_SH(sh, __func__), print_map(datatype, ikev2_cert_map)); goto done; } /* Length */ if (len < sizeof(datalen)) { log_info("%s: short data (len)", SPI_SH(sh, __func__)); goto done; } memcpy(&datalen, ptr, sizeof(datalen)); ptr += sizeof(datalen); len -= sizeof(datalen); /* Value */ if (len < datalen) { log_info("%s: short len %zu < datalen %zu", SPI_SH(sh, __func__), len, datalen); goto done; } if (certdata == NULL) { /* First entry is cert */ certdata = ptr; certlen = datalen; } else { /* All other entries are intermediate CAs */ rawcert = BIO_new_mem_buf(ptr, datalen); if (rawcert == NULL) goto done; cert = d2i_X509_bio(rawcert, NULL); BIO_free(rawcert); if (cert == NULL) { log_warnx("%s: cannot parse CA", SPI_SH(sh, __func__)); ca_sslerror(__func__); goto done; } if (!sk_X509_push(untrusted, cert)) { log_warnx("%s: cannot store CA", SPI_SH(sh, __func__)); X509_free(cert); goto done; } } ptr += datalen; len -= datalen; } log_debug("%s: decoded cert bundle", SPI_SH(sh, __func__)); *datap = certdata; *lenp = certlen; *untrustedp = untrusted; untrusted = NULL; ret = 0; done: if (ret != 0) log_info("%s: failed to decode cert bundle", SPI_SH(sh, __func__)); sk_X509_free(untrusted); return ret; } int ca_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) { struct iked *env = iked_env; unsigned int mode; switch (imsg->hdr.type) { case IMSG_CTL_ACTIVE: case IMSG_CTL_PASSIVE: /* * send back to indicate we have processed * all messages from parent. */ proc_compose(&env->sc_ps, PROC_PARENT, imsg->hdr.type, NULL, 0); break; case IMSG_CTL_RESET: IMSG_SIZE_CHECK(imsg, &mode); memcpy(&mode, imsg->data, sizeof(mode)); if (mode == RESET_ALL || mode == RESET_CA) { log_debug("%s: config reset", __func__); ca_reset(env); } break; case IMSG_OCSP_FD: ocsp_receive_fd(env, imsg); break; case IMSG_OCSP_CFG: config_getocsp(env, imsg); break; case IMSG_PRIVKEY: case IMSG_PUBKEY: config_getkey(env, imsg); break; case IMSG_CTL_STATIC: config_getstatic(env, imsg); break; default: return (-1); } return (0); } int ca_dispatch_ikev2(int fd, struct privsep_proc *p, struct imsg *imsg) { struct iked *env = iked_env; switch (imsg->hdr.type) { case IMSG_CERTREQ: ca_getreq(env, imsg); break; case IMSG_CERT: ca_getcert(env, imsg); break; case IMSG_AUTH: ca_getauth(env, imsg); break; default: return (-1); } return (0); } int ca_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg) { struct iked *env = iked_env; struct ca_store *store = env->sc_priv; switch (imsg->hdr.type) { case IMSG_CTL_SHOW_CERTSTORE: ca_store_info(env, imsg, "CA", store->ca_cas); ca_store_info(env, imsg, "CERT", store->ca_certs); /* Send empty reply to indicate end of information. */ proc_compose_imsg(&env->sc_ps, PROC_CONTROL, -1, IMSG_CTL_SHOW_CERTSTORE, imsg->hdr.peerid, -1, NULL, 0); break; default: return (-1); } return (0); } int ca_setcert(struct iked *env, struct iked_sahdr *sh, struct iked_id *id, uint8_t type, uint8_t *data, size_t len, enum privsep_procid procid) { struct iovec iov[4]; int iovcnt = 0; struct iked_static_id idb; /* Must send the cert and a valid Id to the ca process */ if (procid == PROC_CERT) { if (id == NULL || id->id_type == IKEV2_ID_NONE || ibuf_size(id->id_buf) > IKED_ID_SIZE) return (-1); bzero(&idb, sizeof(idb)); /* Convert to a static Id */ idb.id_type = id->id_type; idb.id_offset = id->id_offset; idb.id_length = ibuf_size(id->id_buf); memcpy(&idb.id_data, ibuf_data(id->id_buf), ibuf_size(id->id_buf)); iov[iovcnt].iov_base = &idb; iov[iovcnt].iov_len = sizeof(idb); iovcnt++; } iov[iovcnt].iov_base = sh; iov[iovcnt].iov_len = sizeof(*sh); iovcnt++; iov[iovcnt].iov_base = &type; iov[iovcnt].iov_len = sizeof(type); iovcnt++; if (data != NULL) { iov[iovcnt].iov_base = data; iov[iovcnt].iov_len = len; iovcnt++; } if (proc_composev(&env->sc_ps, procid, IMSG_CERT, iov, iovcnt) == -1) return (-1); return (0); } static int ca_setscert(struct iked *env, struct iked_sahdr *sh, uint8_t type, X509 *cert) { struct iovec iov[3]; int iovcnt = 0; struct ibuf *buf; int ret; if ((buf = ca_x509_serialize(cert)) == NULL) return (-1); iov[iovcnt].iov_base = sh; iov[iovcnt].iov_len = sizeof(*sh); iovcnt++; iov[iovcnt].iov_base = &type; iov[iovcnt].iov_len = sizeof(type); iovcnt++; iov[iovcnt].iov_base = ibuf_data(buf); iov[iovcnt].iov_len = ibuf_size(buf); iovcnt++; ret = proc_composev(&env->sc_ps, PROC_IKEV2, IMSG_SCERT, iov, iovcnt); ibuf_free(buf); return (ret); } int ca_setreq(struct iked *env, struct iked_sa *sa, struct iked_static_id *localid, uint8_t type, uint8_t more, uint8_t *data, size_t len, enum privsep_procid procid) { struct iovec iov[5]; int iovcnt = 0; struct iked_static_id idb; struct iked_id id; int ret = -1; /* Convert to a static Id */ bzero(&id, sizeof(id)); if (ikev2_policy2id(localid, &id, 1) != 0) return (-1); if (ibuf_size(id.id_buf) > IKED_ID_SIZE) return (-1); bzero(&idb, sizeof(idb)); idb.id_type = id.id_type; idb.id_offset = id.id_offset; idb.id_length = ibuf_size(id.id_buf); memcpy(&idb.id_data, ibuf_data(id.id_buf), ibuf_size(id.id_buf)); iov[iovcnt].iov_base = &idb; iov[iovcnt].iov_len = sizeof(idb); iovcnt++; iov[iovcnt].iov_base = &sa->sa_hdr; iov[iovcnt].iov_len = sizeof(sa->sa_hdr); iovcnt++; iov[iovcnt].iov_base = &type; iov[iovcnt].iov_len = sizeof(type); iovcnt++; iov[iovcnt].iov_base = &more; iov[iovcnt].iov_len = sizeof(more); iovcnt++; if (data != NULL) { iov[iovcnt].iov_base = data; iov[iovcnt].iov_len = len; iovcnt++; } if (proc_composev(&env->sc_ps, procid, IMSG_CERTREQ, iov, iovcnt) == -1) goto done; sa_stateflags(sa, IKED_REQ_CERTREQ); ret = 0; done: ibuf_free(id.id_buf); return (ret); } static int auth_sig_compatible(uint8_t type) { switch (type) { case IKEV2_AUTH_RSA_SIG: case IKEV2_AUTH_ECDSA_256: case IKEV2_AUTH_ECDSA_384: case IKEV2_AUTH_ECDSA_521: case IKEV2_AUTH_SIG_ANY: return (1); } return (0); } int ca_setauth(struct iked *env, struct iked_sa *sa, struct ibuf *authmsg, enum privsep_procid id) { struct iovec iov[3]; int iovcnt = 3; struct iked_policy *policy = sa->sa_policy; uint8_t type = policy->pol_auth.auth_method; if (id == PROC_CERT) { /* switch encoding to IKEV2_AUTH_SIG if SHA2 is supported */ if (sa->sa_sigsha2 && auth_sig_compatible(type)) { log_debug("%s: switching %s to SIG", __func__, print_map(type, ikev2_auth_map)); type = IKEV2_AUTH_SIG; } else if (!sa->sa_sigsha2 && type == IKEV2_AUTH_SIG_ANY) { log_debug("%s: switching SIG to RSA_SIG(*)", __func__); /* XXX ca might auto-switch to ECDSA */ type = IKEV2_AUTH_RSA_SIG; } else if (type == IKEV2_AUTH_SIG) { log_debug("%s: using SIG (RFC7427)", __func__); } } if (type == IKEV2_AUTH_SHARED_KEY_MIC) { sa->sa_stateflags |= IKED_REQ_AUTH; return (ikev2_msg_authsign(env, sa, &policy->pol_auth, authmsg)); } iov[0].iov_base = &sa->sa_hdr; iov[0].iov_len = sizeof(sa->sa_hdr); iov[1].iov_base = &type; iov[1].iov_len = sizeof(type); if (type == IKEV2_AUTH_NONE) iovcnt--; else { iov[2].iov_base = ibuf_data(authmsg); iov[2].iov_len = ibuf_size(authmsg); log_debug("%s: auth length %zu", __func__, ibuf_size(authmsg)); } if (proc_composev(&env->sc_ps, id, IMSG_AUTH, iov, iovcnt) == -1) return (-1); return (0); } int ca_getcert(struct iked *env, struct imsg *imsg) { struct ca_store *store = env->sc_priv; X509 *issuer = NULL, *cert; STACK_OF(X509) *untrusted = NULL; EVP_PKEY *certkey; struct iked_sahdr sh; uint8_t type; uint8_t *ptr; size_t len; struct iked_static_id id; unsigned int i; struct iovec iov[3]; int iovcnt = 3, cmd, ret = 0; struct iked_id key; ptr = (uint8_t *)imsg->data; len = IMSG_DATA_SIZE(imsg); i = sizeof(id) + sizeof(sh) + sizeof(type); if (len < i) return (-1); memcpy(&id, ptr, sizeof(id)); if (id.id_type == IKEV2_ID_NONE) return (-1); memcpy(&sh, ptr + sizeof(id), sizeof(sh)); memcpy(&type, ptr + sizeof(id) + sizeof(sh), sizeof(uint8_t)); ptr += i; len -= i; bzero(&key, sizeof(key)); if (type == IKEV2_CERT_BUNDLE && ca_decode_cert_bundle(env, &sh, &ptr, &len, &untrusted) == 0) type = IKEV2_CERT_X509_CERT; switch (type) { case IKEV2_CERT_X509_CERT: /* Look in local cert storage first */ cert = ca_by_subjectaltname(store->ca_certs, &id); if (cert) { log_debug("%s: found local cert", __func__); if ((certkey = X509_get0_pubkey(cert)) != NULL) { ret = ca_pubkey_serialize(certkey, &key); if (ret == 0) { ptr = ibuf_data(key.id_buf); len = ibuf_size(key.id_buf); type = key.id_type; break; } } } if (env->sc_ocsp_url == NULL) ret = ca_validate_cert(env, &id, ptr, len, untrusted, NULL); else { ret = ca_validate_cert(env, &id, ptr, len, untrusted, &issuer); if (ret == 0) { ret = ocsp_validate_cert(env, ptr, len, sh, type, issuer); X509_free(issuer); if (ret == 0) { sk_X509_free(untrusted); return (0); } } else X509_free(issuer); } break; case IKEV2_CERT_RSA_KEY: case IKEV2_CERT_ECDSA: ret = ca_validate_pubkey(env, &id, ptr, len, NULL); break; case IKEV2_CERT_NONE: /* Fallback to public key */ ret = ca_validate_pubkey(env, &id, NULL, 0, &key); if (ret == 0) { ptr = ibuf_data(key.id_buf); len = ibuf_size(key.id_buf); type = key.id_type; } break; default: log_debug("%s: unsupported cert type %d", __func__, type); ret = -1; break; } if (ret == 0) cmd = IMSG_CERTVALID; else cmd = IMSG_CERTINVALID; iov[0].iov_base = &sh; iov[0].iov_len = sizeof(sh); iov[1].iov_base = &type; iov[1].iov_len = sizeof(type); iov[2].iov_base = ptr; iov[2].iov_len = len; ret = proc_composev(&env->sc_ps, PROC_IKEV2, cmd, iov, iovcnt); ibuf_free(key.id_buf); sk_X509_free(untrusted); return (ret); } static unsigned int ca_chain_by_issuer(struct ca_store *store, X509_NAME *subject, struct iked_static_id *id, X509 **dst, size_t dstlen) { STACK_OF(X509_OBJECT) *h; X509_OBJECT *xo; X509 *cert; int i; unsigned int n; X509_NAME *issuer, *subj; if (subject == NULL || dstlen == 0) return (0); if ((cert = ca_by_issuer(store->ca_certs, subject, id)) != NULL) { *dst = cert; return (1); } h = X509_STORE_get0_objects(store->ca_cas); for (i = 0; i < sk_X509_OBJECT_num(h); i++) { xo = sk_X509_OBJECT_value(h, i); if (X509_OBJECT_get_type(xo) != X509_LU_X509) continue; cert = X509_OBJECT_get0_X509(xo); if ((issuer = X509_get_issuer_name(cert)) == NULL) continue; if (X509_NAME_cmp(subject, issuer) == 0) { if ((subj = X509_get_subject_name(cert)) == NULL) continue; /* Skip root CAs */ if (X509_NAME_cmp(subj, issuer) == 0) continue; n = ca_chain_by_issuer(store, subj, id, dst + 1, dstlen - 1); if (n > 0) { *dst = cert; return (n + 1); } } } return (0); } int ca_getreq(struct iked *env, struct imsg *imsg) { struct ca_store *store = env->sc_priv; struct iked_sahdr sh; uint8_t type, more; uint8_t *ptr; size_t len; unsigned int i; X509 *ca = NULL, *cert = NULL; X509 *chain[IKED_SCERT_MAX + 1]; size_t chain_len = 0; struct ibuf *buf; struct iked_static_id id; char idstr[IKED_ID_SIZE]; X509_NAME *subj; char *subj_name; ptr = (uint8_t *)imsg->data; len = IMSG_DATA_SIZE(imsg); i = sizeof(id) + sizeof(type) + sizeof(sh) + sizeof(more); if (len < i) return (-1); memcpy(&id, ptr, sizeof(id)); if (id.id_type == IKEV2_ID_NONE) return (-1); memcpy(&sh, ptr + sizeof(id), sizeof(sh)); memcpy(&type, ptr + sizeof(id) + sizeof(sh), sizeof(type)); memcpy(&more, ptr + sizeof(id) + sizeof(sh) + sizeof(type), sizeof(more)); ptr += i; len -= i; switch (type) { case IKEV2_CERT_RSA_KEY: case IKEV2_CERT_ECDSA: /* * Find a local raw public key that matches the type * received in the CERTREQ payoad */ if (store->ca_pubkey.id_type != type || store->ca_pubkey.id_buf == NULL) goto fallback; buf = ibuf_dup(store->ca_pubkey.id_buf); log_debug("%s: using local public key of type %s", __func__, print_map(type, ikev2_cert_map)); break; case IKEV2_CERT_X509_CERT: if (len == 0 || len % SHA_DIGEST_LENGTH) { log_info("%s: invalid CERTREQ data.", SPI_SH(&sh, __func__)); return (-1); } /* * Find a local certificate signed by any of the CAs * received in the CERTREQ payload */ for (i = 0; i < len; i += SHA_DIGEST_LENGTH) { if ((ca = ca_by_subjectpubkey(store->ca_cas, ptr + i, SHA_DIGEST_LENGTH)) == NULL) continue; subj = X509_get_subject_name(ca); if (subj == NULL) return (-1); subj_name = X509_NAME_oneline(subj, NULL, 0); if (subj_name == NULL) return (-1); log_debug("%s: found CA %s", __func__, subj_name); OPENSSL_free(subj_name); chain_len = ca_chain_by_issuer(store, subj, &id, chain, nitems(chain)); if (chain_len > 0) { cert = chain[chain_len - 1]; if (!ca_cert_local(env, cert)) { log_info("%s: found cert with matching " "ID but without matching key.", SPI_SH(&sh, __func__)); continue; } break; } } for (i = chain_len; i >= 2; i--) ca_setscert(env, &sh, type, chain[i - 2]); /* Fallthrough */ case IKEV2_CERT_NONE: fallback: /* * If no certificate or key matching any of the trust-anchors * was found and this was the last CERTREQ, try to find one with * subjectAltName matching the ID */ if (cert == NULL && more) return (0); if (cert == NULL) cert = ca_by_subjectaltname(store->ca_certs, &id); /* Set type if coming from fallback */ if (cert != NULL) type = IKEV2_CERT_X509_CERT; /* If there is no matching certificate use local raw pubkey */ if (cert == NULL) { if (ikev2_print_static_id(&id, idstr, sizeof(idstr)) == -1) return (-1); log_info("%s: no valid local certificate found for %s", SPI_SH(&sh, __func__), idstr); ca_store_certs_info(SPI_SH(&sh, __func__), store->ca_certs); if (store->ca_pubkey.id_buf == NULL) return (-1); buf = ibuf_dup(store->ca_pubkey.id_buf); type = store->ca_pubkey.id_type; log_info("%s: using local public key of type %s", SPI_SH(&sh, __func__), print_map(type, ikev2_cert_map)); break; } subj = X509_get_subject_name(cert); if (subj == NULL) return (-1); subj_name = X509_NAME_oneline(subj, NULL, 0); if (subj_name == NULL) return (-1); log_debug("%s: found local certificate %s", __func__, subj_name); OPENSSL_free(subj_name); if ((buf = ca_x509_serialize(cert)) == NULL) return (-1); break; default: log_warnx("%s: unknown cert type requested", SPI_SH(&sh, __func__)); return (-1); } ca_setcert(env, &sh, NULL, type, ibuf_data(buf), ibuf_size(buf), PROC_IKEV2); ibuf_free(buf); return (0); } int ca_getauth(struct iked *env, struct imsg *imsg) { struct ca_store *store = env->sc_priv; struct iked_sahdr sh; uint8_t method; uint8_t *ptr; size_t len; unsigned int i; int ret = -1; struct iked_sa sa; struct iked_policy policy; struct iked_id *id; struct ibuf *authmsg; ptr = (uint8_t *)imsg->data; len = IMSG_DATA_SIZE(imsg); i = sizeof(method) + sizeof(sh); if (len <= i) return (-1); memcpy(&sh, ptr, sizeof(sh)); memcpy(&method, ptr + sizeof(sh), sizeof(uint8_t)); if (method == IKEV2_AUTH_SHARED_KEY_MIC) return (-1); ptr += i; len -= i; if ((authmsg = ibuf_new(ptr, len)) == NULL) return (-1); /* * Create fake SA and policy */ bzero(&sa, sizeof(sa)); bzero(&policy, sizeof(policy)); memcpy(&sa.sa_hdr, &sh, sizeof(sh)); sa.sa_policy = &policy; if (sh.sh_initiator) id = &sa.sa_icert; else id = &sa.sa_rcert; memcpy(id, &store->ca_privkey, sizeof(*id)); policy.pol_auth.auth_method = method == IKEV2_AUTH_SIG ? method : store->ca_privkey_method; if (ikev2_msg_authsign(env, &sa, &policy.pol_auth, authmsg) != 0) { log_debug("%s: AUTH sign failed", __func__); policy.pol_auth.auth_method = IKEV2_AUTH_NONE; } ret = ca_setauth(env, &sa, sa.sa_localauth.id_buf, PROC_IKEV2); ibuf_free(sa.sa_localauth.id_buf); sa.sa_localauth.id_buf = NULL; ibuf_free(authmsg); return (ret); } int ca_reload(struct iked *env) { struct ca_store *store = env->sc_priv; uint8_t md[EVP_MAX_MD_SIZE]; char file[PATH_MAX]; struct iovec iov[2]; struct dirent *entry; STACK_OF(X509_OBJECT) *h; X509_OBJECT *xo; X509 *x509; DIR *dir; int i, iovcnt = 0; unsigned int len; X509_NAME *subj; char *subj_name; /* * Load CAs */ if ((dir = opendir(IKED_CA_DIR)) == NULL) return (-1); while ((entry = readdir(dir)) != NULL) { if ((entry->d_type != DT_REG) && (entry->d_type != DT_LNK)) continue; if (snprintf(file, sizeof(file), "%s%s", IKED_CA_DIR, entry->d_name) < 0) continue; if (!X509_load_cert_file(store->ca_calookup, file, X509_FILETYPE_PEM)) { log_warn("%s: failed to load ca file %s", __func__, entry->d_name); ca_sslerror(__func__); continue; } log_debug("%s: loaded ca file %s", __func__, entry->d_name); } closedir(dir); /* * Load CRLs for the CAs */ if ((dir = opendir(IKED_CRL_DIR)) == NULL) return (-1); while ((entry = readdir(dir)) != NULL) { if ((entry->d_type != DT_REG) && (entry->d_type != DT_LNK)) continue; if (snprintf(file, sizeof(file), "%s%s", IKED_CRL_DIR, entry->d_name) < 0) continue; if (!X509_load_crl_file(store->ca_calookup, file, X509_FILETYPE_PEM)) { log_warn("%s: failed to load crl file %s", __func__, entry->d_name); ca_sslerror(__func__); continue; } /* Only enable CRL checks if we actually loaded a CRL */ X509_STORE_set_flags(store->ca_cas, X509_V_FLAG_CRL_CHECK); log_debug("%s: loaded crl file %s", __func__, entry->d_name); } closedir(dir); /* * Save CAs signatures for the IKEv2 CERTREQ */ ibuf_free(env->sc_certreq); if ((env->sc_certreq = ibuf_new(NULL, 0)) == NULL) return (-1); h = X509_STORE_get0_objects(store->ca_cas); for (i = 0; i < sk_X509_OBJECT_num(h); i++) { xo = sk_X509_OBJECT_value(h, i); if (X509_OBJECT_get_type(xo) != X509_LU_X509) continue; x509 = X509_OBJECT_get0_X509(xo); len = sizeof(md); ca_subjectpubkey_digest(x509, md, &len); subj = X509_get_subject_name(x509); if (subj == NULL) return (-1); subj_name = X509_NAME_oneline(subj, NULL, 0); if (subj_name == NULL) return (-1); log_debug("%s: %s", __func__, subj_name); OPENSSL_free(subj_name); if (ibuf_add(env->sc_certreq, md, len) != 0) { ibuf_free(env->sc_certreq); env->sc_certreq = NULL; return (-1); } } if (ibuf_size(env->sc_certreq)) { env->sc_certreqtype = IKEV2_CERT_X509_CERT; iov[0].iov_base = &env->sc_certreqtype; iov[0].iov_len = sizeof(env->sc_certreqtype); iovcnt++; iov[1].iov_base = ibuf_data(env->sc_certreq); iov[1].iov_len = ibuf_size(env->sc_certreq); iovcnt++; log_debug("%s: loaded %zu ca certificate%s", __func__, ibuf_size(env->sc_certreq) / SHA_DIGEST_LENGTH, ibuf_size(env->sc_certreq) == SHA_DIGEST_LENGTH ? "" : "s"); (void)proc_composev(&env->sc_ps, PROC_IKEV2, IMSG_CERTREQ, iov, iovcnt); } /* * Load certificates */ if ((dir = opendir(IKED_CERT_DIR)) == NULL) return (-1); while ((entry = readdir(dir)) != NULL) { if ((entry->d_type != DT_REG) && (entry->d_type != DT_LNK)) continue; if (snprintf(file, sizeof(file), "%s%s", IKED_CERT_DIR, entry->d_name) < 0) continue; if (!X509_load_cert_file(store->ca_certlookup, file, X509_FILETYPE_PEM)) { log_warn("%s: failed to load cert file %s", __func__, entry->d_name); ca_sslerror(__func__); continue; } log_debug("%s: loaded cert file %s", __func__, entry->d_name); } closedir(dir); h = X509_STORE_get0_objects(store->ca_certs); for (i = 0; i < sk_X509_OBJECT_num(h); i++) { xo = sk_X509_OBJECT_value(h, i); if (X509_OBJECT_get_type(xo) != X509_LU_X509) continue; x509 = X509_OBJECT_get0_X509(xo); (void)ca_validate_cert(env, NULL, x509, 0, NULL, NULL); } if (!env->sc_certreqtype) env->sc_certreqtype = store->ca_pubkey.id_type; log_debug("%s: local cert type %s", __func__, print_map(env->sc_certreqtype, ikev2_cert_map)); iov[0].iov_base = &env->sc_certreqtype; iov[0].iov_len = sizeof(env->sc_certreqtype); if (iovcnt == 0) iovcnt++; (void)proc_composev(&env->sc_ps, PROC_IKEV2, IMSG_CERTREQ, iov, iovcnt); return (0); } X509 * ca_by_subjectpubkey(X509_STORE *ctx, uint8_t *sig, size_t siglen) { STACK_OF(X509_OBJECT) *h; X509_OBJECT *xo; X509 *ca; int i; unsigned int len; uint8_t md[EVP_MAX_MD_SIZE]; h = X509_STORE_get0_objects(ctx); for (i = 0; i < sk_X509_OBJECT_num(h); i++) { xo = sk_X509_OBJECT_value(h, i); if (X509_OBJECT_get_type(xo) != X509_LU_X509) continue; ca = X509_OBJECT_get0_X509(xo); len = sizeof(md); ca_subjectpubkey_digest(ca, md, &len); if (len == siglen && memcmp(md, sig, len) == 0) return (ca); } return (NULL); } X509 * ca_by_issuer(X509_STORE *ctx, X509_NAME *subject, struct iked_static_id *id) { STACK_OF(X509_OBJECT) *h; X509_OBJECT *xo; X509 *cert; int i; X509_NAME *issuer; if (subject == NULL) return (NULL); h = X509_STORE_get0_objects(ctx); for (i = 0; i < sk_X509_OBJECT_num(h); i++) { xo = sk_X509_OBJECT_value(h, i); if (X509_OBJECT_get_type(xo) != X509_LU_X509) continue; cert = X509_OBJECT_get0_X509(xo); if ((issuer = X509_get_issuer_name(cert)) == NULL) continue; else if (X509_NAME_cmp(subject, issuer) == 0) { switch (id->id_type) { case IKEV2_ID_ASN1_DN: if (ca_x509_subject_cmp(cert, id) == 0) return (cert); break; default: if (ca_x509_subjectaltname_cmp(cert, id) == 0) return (cert); break; } } } return (NULL); } X509 * ca_by_subjectaltname(X509_STORE *ctx, struct iked_static_id *id) { STACK_OF(X509_OBJECT) *h; X509_OBJECT *xo; X509 *cert; int i; h = X509_STORE_get0_objects(ctx); for (i = 0; i < sk_X509_OBJECT_num(h); i++) { xo = sk_X509_OBJECT_value(h, i); if (X509_OBJECT_get_type(xo) != X509_LU_X509) continue; cert = X509_OBJECT_get0_X509(xo); switch (id->id_type) { case IKEV2_ID_ASN1_DN: if (ca_x509_subject_cmp(cert, id) == 0) return (cert); break; default: if (ca_x509_subjectaltname_cmp(cert, id) == 0) return (cert); break; } } return (NULL); } void ca_store_certs_info(const char *msg, X509_STORE *ctx) { STACK_OF(X509_OBJECT) *h; X509_OBJECT *xo; X509 *cert; int i; h = X509_STORE_get0_objects(ctx); for (i = 0; i < sk_X509_OBJECT_num(h); i++) { xo = sk_X509_OBJECT_value(h, i); if (X509_OBJECT_get_type(xo) != X509_LU_X509) continue; cert = X509_OBJECT_get0_X509(xo); ca_cert_info(msg, cert); } } int ca_cert_local(struct iked *env, X509 *cert) { struct ca_store *store = env->sc_priv; EVP_PKEY *certkey = NULL, *localpub = NULL; int ret = 0; if ((localpub = ca_bytes_to_pkey(ibuf_data(store->ca_pubkey.id_buf), ibuf_size(store->ca_pubkey.id_buf))) == NULL) goto done; if ((certkey = X509_get0_pubkey(cert)) == NULL) { log_info("%s: no public key in cert", __func__); goto done; } if (EVP_PKEY_cmp(certkey, localpub) != 1) { log_debug("%s: certificate key mismatch", __func__); goto done; } ret = 1; done: EVP_PKEY_free(localpub); return (ret); } void ca_cert_info(const char *msg, X509 *cert) { ASN1_INTEGER *asn1_serial; BUF_MEM *memptr; BIO *rawserial = NULL; char buf[BUFSIZ]; X509_NAME *name; if ((asn1_serial = X509_get_serialNumber(cert)) == NULL || (rawserial = BIO_new(BIO_s_mem())) == NULL || i2a_ASN1_INTEGER(rawserial, asn1_serial) <= 0) goto out; name = X509_get_issuer_name(cert); if (name != NULL && X509_NAME_oneline(name, buf, sizeof(buf))) log_info("%s: issuer: %s", msg, buf); BIO_get_mem_ptr(rawserial, &memptr); if (memptr->data != NULL && memptr->length < INT32_MAX) log_info("%s: serial: %.*s", msg, (int)memptr->length, memptr->data); name = X509_get_subject_name(cert); if (name != NULL && X509_NAME_oneline(name, buf, sizeof(buf))) log_info("%s: subject: %s", msg, buf); ca_x509_subjectaltname_log(cert, msg); out: BIO_free(rawserial); } int ca_subjectpubkey_digest(X509 *x509, uint8_t *md, unsigned int *size) { EVP_PKEY *pkey; uint8_t *buf = NULL; int buflen; if (*size < SHA_DIGEST_LENGTH) return (-1); /* * Generate a SHA-1 digest of the Subject Public Key Info * element in the X.509 certificate, an ASN.1 sequence * that includes the public key type (eg. RSA) and the * public key value (see 3.7 of RFC7296). */ if ((pkey = X509_get0_pubkey(x509)) == NULL) return (-1); buflen = i2d_PUBKEY(pkey, &buf); if (buflen == 0) return (-1); if (!EVP_Digest(buf, buflen, md, size, EVP_sha1(), NULL)) { OPENSSL_free(buf); return (-1); } OPENSSL_free(buf); return (0); } void ca_store_info(struct iked *env, struct imsg *imsg, const char *msg, X509_STORE *ctx) { STACK_OF(X509_OBJECT) *h; X509_OBJECT *xo; X509 *cert; int i; X509_NAME *subject; char *name; char *buf; int buflen; h = X509_STORE_get0_objects(ctx); for (i = 0; i < sk_X509_OBJECT_num(h); i++) { xo = sk_X509_OBJECT_value(h, i); if (X509_OBJECT_get_type(xo) != X509_LU_X509) continue; cert = X509_OBJECT_get0_X509(xo); if ((subject = X509_get_subject_name(cert)) == NULL || (name = X509_NAME_oneline(subject, NULL, 0)) == NULL) continue; buflen = asprintf(&buf, "%s: %s\n", msg, name); OPENSSL_free(name); if (buflen == -1) continue; proc_compose_imsg(&env->sc_ps, PROC_CONTROL, -1, IMSG_CTL_SHOW_CERTSTORE, imsg->hdr.peerid, -1, buf, buflen + 1); free(buf); } } struct ibuf * ca_x509_serialize(X509 *x509) { long len; struct ibuf *buf; uint8_t *d = NULL; BIO *out; if ((out = BIO_new(BIO_s_mem())) == NULL) return (NULL); if (!i2d_X509_bio(out, x509)) { BIO_free(out); return (NULL); } len = BIO_get_mem_data(out, &d); buf = ibuf_new(d, len); BIO_free(out); return (buf); } int ca_pubkey_serialize(EVP_PKEY *key, struct iked_id *id) { RSA *rsa = NULL; EC_KEY *ec = NULL; uint8_t *d; int len = 0; int ret = -1; switch (EVP_PKEY_id(key)) { case EVP_PKEY_RSA: id->id_type = 0; id->id_offset = 0; ibuf_free(id->id_buf); id->id_buf = NULL; if ((rsa = EVP_PKEY_get0_RSA(key)) == NULL) goto done; if ((len = i2d_RSAPublicKey(rsa, NULL)) <= 0) goto done; if ((id->id_buf = ibuf_new(NULL, len)) == NULL) goto done; d = ibuf_data(id->id_buf); if (i2d_RSAPublicKey(rsa, &d) != len) { ibuf_free(id->id_buf); id->id_buf = NULL; goto done; } id->id_type = IKEV2_CERT_RSA_KEY; break; case EVP_PKEY_EC: id->id_type = 0; id->id_offset = 0; ibuf_free(id->id_buf); id->id_buf = NULL; if ((ec = EVP_PKEY_get0_EC_KEY(key)) == NULL) goto done; if ((len = i2d_EC_PUBKEY(ec, NULL)) <= 0) goto done; if ((id->id_buf = ibuf_new(NULL, len)) == NULL) goto done; d = ibuf_data(id->id_buf); if (i2d_EC_PUBKEY(ec, &d) != len) { ibuf_free(id->id_buf); id->id_buf = NULL; goto done; } id->id_type = IKEV2_CERT_ECDSA; break; default: log_debug("%s: unsupported key type %d", __func__, EVP_PKEY_id(key)); return (-1); } log_debug("%s: type %s length %d", __func__, print_map(id->id_type, ikev2_cert_map), len); ret = 0; done: return (ret); } int ca_privkey_serialize(EVP_PKEY *key, struct iked_id *id) { RSA *rsa = NULL; EC_KEY *ec = NULL; uint8_t *d; int len = 0; int ret = -1; switch (EVP_PKEY_id(key)) { case EVP_PKEY_RSA: id->id_type = 0; id->id_offset = 0; ibuf_free(id->id_buf); id->id_buf = NULL; if ((rsa = EVP_PKEY_get0_RSA(key)) == NULL) goto done; if ((len = i2d_RSAPrivateKey(rsa, NULL)) <= 0) goto done; if ((id->id_buf = ibuf_new(NULL, len)) == NULL) goto done; d = ibuf_data(id->id_buf); if (i2d_RSAPrivateKey(rsa, &d) != len) { ibuf_free(id->id_buf); id->id_buf = NULL; goto done; } id->id_type = IKEV2_CERT_RSA_KEY; break; case EVP_PKEY_EC: id->id_type = 0; id->id_offset = 0; ibuf_free(id->id_buf); id->id_buf = NULL; if ((ec = EVP_PKEY_get0_EC_KEY(key)) == NULL) goto done; if ((len = i2d_ECPrivateKey(ec, NULL)) <= 0) goto done; if ((id->id_buf = ibuf_new(NULL, len)) == NULL) goto done; d = ibuf_data(id->id_buf); if (i2d_ECPrivateKey(ec, &d) != len) { ibuf_free(id->id_buf); id->id_buf = NULL; goto done; } id->id_type = IKEV2_CERT_ECDSA; break; default: log_debug("%s: unsupported key type %d", __func__, EVP_PKEY_id(key)); return (-1); } log_debug("%s: type %s length %d", __func__, print_map(id->id_type, ikev2_cert_map), len); ret = 0; done: return (ret); } EVP_PKEY * ca_bytes_to_pkey(uint8_t *data, size_t len) { BIO *rawcert = NULL; EVP_PKEY *localkey = NULL, *out = NULL; RSA *localrsa = NULL; EC_KEY *localec = NULL; if ((rawcert = BIO_new_mem_buf(data, len)) == NULL) goto done; if ((localkey = EVP_PKEY_new()) == NULL) goto sslerr; if ((localrsa = d2i_RSAPublicKey_bio(rawcert, NULL))) { if (EVP_PKEY_set1_RSA(localkey, localrsa) != 1) goto sslerr; } else if (BIO_reset(rawcert) == 1 && (localec = d2i_EC_PUBKEY_bio(rawcert, NULL))) { if (EVP_PKEY_set1_EC_KEY(localkey, localec) != 1) goto sslerr; } else { log_info("%s: unknown public key type", __func__); goto done; } out = localkey; localkey = NULL; sslerr: if (out == NULL) ca_sslerror(__func__); done: EVP_PKEY_free(localkey); RSA_free(localrsa); EC_KEY_free(localec); BIO_free(rawcert); return (out); } int ca_privkey_to_method(struct iked_id *privkey) { BIO *rawcert = NULL; EC_KEY *ec = NULL; const EC_GROUP *group = NULL; uint8_t method = IKEV2_AUTH_NONE; switch (privkey->id_type) { case IKEV2_CERT_RSA_KEY: method = IKEV2_AUTH_RSA_SIG; break; case IKEV2_CERT_ECDSA: if ((rawcert = BIO_new_mem_buf(ibuf_data(privkey->id_buf), ibuf_size(privkey->id_buf))) == NULL) goto out; if ((ec = d2i_ECPrivateKey_bio(rawcert, NULL)) == NULL) goto out; if ((group = EC_KEY_get0_group(ec)) == NULL) goto out; switch (EC_GROUP_get_degree(group)) { case 256: method = IKEV2_AUTH_ECDSA_256; break; case 384: method = IKEV2_AUTH_ECDSA_384; break; case 521: method = IKEV2_AUTH_ECDSA_521; break; } } log_debug("%s: type %s method %s", __func__, print_map(privkey->id_type, ikev2_cert_map), print_map(method, ikev2_auth_map)); out: EC_KEY_free(ec); BIO_free(rawcert); return (method); } /* * Return dynamically allocated buffer containing certificate name. * The resulting buffer must be freed with OpenSSL_free(). */ char * ca_asn1_name(uint8_t *asn1, size_t len) { X509_NAME *name = NULL; char *str = NULL; const uint8_t *p; p = asn1; if ((name = d2i_X509_NAME(NULL, &p, len)) == NULL) return (NULL); str = X509_NAME_oneline(name, NULL, 0); X509_NAME_free(name); return (str); } /* * Copy 'src' to 'dst' until 'marker' is found while unescaping '\' * characters. The return value tells the caller where to continue * parsing (might be the end of the string) or NULL on error. */ static char * ca_x509_name_unescape(char *src, char *dst, char marker) { while (*src) { if (*src == marker) { src++; break; } if (*src == '\\') { src++; if (!*src) { log_warnx("%s: '\\' at end of string", __func__); *dst = '\0'; return (NULL); } } *dst++ = *src++; } *dst = '\0'; return (src); } /* * Parse an X509 subject name where 'subject' is in the format * /type0=value0/type1=value1/type2=... * where characters may be escaped by '\'. * See lib/libssl/src/apps/apps.c:parse_name() */ void * ca_x509_name_parse(char *subject) { char *cp, *value = NULL, *type = NULL; size_t maxlen; X509_NAME *name = NULL; if (*subject != '/') { log_warnx("%s: leading '/' missing in '%s'", __func__, subject); goto err; } /* length of subject is upper bound for unescaped type/value */ maxlen = strlen(subject) + 1; if ((type = calloc(1, maxlen)) == NULL || (value = calloc(1, maxlen)) == NULL || (name = X509_NAME_new()) == NULL) goto err; cp = subject + 1; while (*cp) { /* unescape type, terminated by '=' */ cp = ca_x509_name_unescape(cp, type, '='); if (cp == NULL) { log_warnx("%s: could not parse type", __func__); goto err; } if (!*cp) { log_warnx("%s: missing value", __func__); goto err; } /* unescape value, terminated by '/' */ cp = ca_x509_name_unescape(cp, value, '/'); if (cp == NULL) { log_warnx("%s: could not parse value", __func__); goto err; } if (!*type || !*value) { log_warnx("%s: empty type or value", __func__); goto err; } log_debug("%s: setting '%s' to '%s'", __func__, type, value); if (!X509_NAME_add_entry_by_txt(name, type, MBSTRING_ASC, value, -1, -1, 0)) { log_warnx("%s: setting '%s' to '%s' failed", __func__, type, value); ca_sslerror(__func__); goto err; } } free(type); free(value); return (name); err: X509_NAME_free(name); free(type); free(value); return (NULL); } int ca_validate_pubkey(struct iked *env, struct iked_static_id *id, void *data, size_t len, struct iked_id *out) { RSA *localrsa = NULL; EVP_PKEY *peerkey = NULL, *localkey = NULL; int ret = -1; FILE *fp = NULL; char idstr[IKED_ID_SIZE]; char file[PATH_MAX]; struct iked_id idp; switch (id->id_type) { case IKEV2_ID_IPV4: case IKEV2_ID_FQDN: case IKEV2_ID_UFQDN: case IKEV2_ID_IPV6: break; default: /* Some types like ASN1_DN will not be mapped to file names */ log_debug("%s: unsupported public key type %s", __func__, print_map(id->id_type, ikev2_id_map)); return (-1); } bzero(&idp, sizeof(idp)); if ((idp.id_buf = ibuf_new(id->id_data, id->id_length)) == NULL) goto done; idp.id_type = id->id_type; idp.id_offset = id->id_offset; if (ikev2_print_id(&idp, idstr, sizeof(idstr)) == -1) goto done; if (len == 0 && data) { /* Data is already an public key */ peerkey = (EVP_PKEY *)data; } if (len > 0) { if ((peerkey = ca_bytes_to_pkey(data, len)) == NULL) goto done; } lc_idtype(idstr); if (strlcpy(file, IKED_PUBKEY_DIR, sizeof(file)) >= sizeof(file) || strlcat(file, idstr, sizeof(file)) >= sizeof(file)) { log_debug("%s: public key id too long %s", __func__, idstr); goto done; } if ((fp = fopen(file, "r")) == NULL) { /* Log to debug when called from ca_validate_cert */ logit(len == 0 ? LOG_DEBUG : LOG_INFO, "%s: could not open public key %s", __func__, file); goto done; } localkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL); if (localkey == NULL) { /* reading PKCS #8 failed, try PEM RSA */ rewind(fp); localrsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL); fclose(fp); if (localrsa == NULL) goto sslerr; if ((localkey = EVP_PKEY_new()) == NULL) goto sslerr; if (!EVP_PKEY_set1_RSA(localkey, localrsa)) goto sslerr; } else { fclose(fp); } if (localkey == NULL) goto sslerr; if (peerkey && EVP_PKEY_cmp(peerkey, localkey) != 1) { log_debug("%s: public key does not match %s", __func__, file); goto done; } log_debug("%s: valid public key in file %s", __func__, file); if (out && ca_pubkey_serialize(localkey, out)) goto done; ret = 0; sslerr: if (ret != 0) ca_sslerror(__func__); done: ibuf_free(idp.id_buf); EVP_PKEY_free(localkey); RSA_free(localrsa); if (len > 0) EVP_PKEY_free(peerkey); return (ret); } int ca_validate_cert(struct iked *env, struct iked_static_id *id, void *data, size_t len, STACK_OF(X509) *untrusted, X509 **issuerp) { struct ca_store *store = env->sc_priv; X509_STORE_CTX *csc = NULL; X509_VERIFY_PARAM *param; BIO *rawcert = NULL; X509 *cert = NULL; EVP_PKEY *pkey; int ret = -1, result, error; const char *errstr = "failed"; X509_NAME *subj; char *subj_name; if (issuerp) *issuerp = NULL; if (len == 0) { /* Data is already an X509 certificate */ cert = (X509 *)data; } else { /* Convert data to X509 certificate */ if ((rawcert = BIO_new_mem_buf(data, len)) == NULL) goto done; if ((cert = d2i_X509_bio(rawcert, NULL)) == NULL) goto done; } /* Certificate needs a valid subjectName */ if (X509_get_subject_name(cert) == NULL) { errstr = "invalid subject"; goto done; } if (id != NULL) { if ((pkey = X509_get0_pubkey(cert)) == NULL) { errstr = "no public key in cert"; goto done; } ret = ca_validate_pubkey(env, id, pkey, 0, NULL); if (ret == 0) { errstr = "in public key file, ok"; goto done; } switch (id->id_type) { case IKEV2_ID_ASN1_DN: if (ca_x509_subject_cmp(cert, id) < 0) { errstr = "ASN1_DN identifier mismatch"; goto done; } break; default: if (ca_x509_subjectaltname_cmp(cert, id) != 0) { errstr = "invalid subjectAltName extension"; goto done; } break; } } csc = X509_STORE_CTX_new(); if (csc == NULL) { errstr = "failed to alloc csc"; goto done; } X509_STORE_CTX_init(csc, store->ca_cas, cert, untrusted); param = X509_STORE_get0_param(store->ca_cas); if (X509_VERIFY_PARAM_get_flags(param) & X509_V_FLAG_CRL_CHECK) { X509_STORE_CTX_set_flags(csc, X509_V_FLAG_CRL_CHECK); X509_STORE_CTX_set_flags(csc, X509_V_FLAG_CRL_CHECK_ALL); } if (env->sc_cert_partial_chain) X509_STORE_CTX_set_flags(csc, X509_V_FLAG_PARTIAL_CHAIN); result = X509_verify_cert(csc); error = X509_STORE_CTX_get_error(csc); if (error == 0 && issuerp) { if (X509_STORE_CTX_get1_issuer(issuerp, csc, cert) != 1) { log_debug("%s: cannot get issuer", __func__); *issuerp = NULL; } } X509_STORE_CTX_cleanup(csc); if (error != 0) { errstr = X509_verify_cert_error_string(error); goto done; } if (!result) { /* XXX should we accept self-signed certificates? */ errstr = "rejecting self-signed certificate"; goto done; } /* Success */ ret = 0; errstr = "ok"; done: if (cert != NULL) { subj = X509_get_subject_name(cert); if (subj == NULL) goto err; subj_name = X509_NAME_oneline(subj, NULL, 0); if (subj_name == NULL) goto err; log_debug("%s: %s %.100s", __func__, subj_name, errstr); OPENSSL_free(subj_name); } err: if (len > 0) X509_free(cert); BIO_free(rawcert); X509_STORE_CTX_free(csc); return (ret); } /* check if subject from cert matches the id */ int ca_x509_subject_cmp(X509 *cert, struct iked_static_id *id) { X509_NAME *subject, *idname = NULL; const uint8_t *idptr; size_t idlen; int ret = -1; if (id->id_type != IKEV2_ID_ASN1_DN) return (-1); if ((subject = X509_get_subject_name(cert)) == NULL) return (-1); if (id->id_length <= id->id_offset) return (-1); idlen = id->id_length - id->id_offset; idptr = id->id_data + id->id_offset; if ((idname = d2i_X509_NAME(NULL, &idptr, idlen)) == NULL) return (-1); if (X509_NAME_cmp(subject, idname) == 0) ret = 0; X509_NAME_free(idname); return (ret); } #define MODE_ALT_LOG 1 #define MODE_ALT_GET 2 #define MODE_ALT_CMP 3 int ca_x509_subjectaltname_do(X509 *cert, int mode, const char *logmsg, struct iked_static_id *id, struct iked_id *retid) { STACK_OF(GENERAL_NAME) *stack = NULL; GENERAL_NAME *entry; ASN1_STRING *cstr; char idstr[IKED_ID_SIZE]; int crit, ret, i, type, len; const uint8_t *data; ret = -1; crit = -1; if ((stack = X509_get_ext_d2i(cert, NID_subject_alt_name, &crit, NULL)) != NULL) { for (i = 0; i < sk_GENERAL_NAME_num(stack); i++) { entry = sk_GENERAL_NAME_value(stack, i); switch (entry->type) { case GEN_DNS: cstr = entry->d.dNSName; if (ASN1_STRING_type(cstr) != V_ASN1_IA5STRING) continue; type = IKEV2_ID_FQDN; break; case GEN_EMAIL: cstr = entry->d.rfc822Name; if (ASN1_STRING_type(cstr) != V_ASN1_IA5STRING) continue; type = IKEV2_ID_UFQDN; break; case GEN_IPADD: cstr = entry->d.iPAddress; switch (ASN1_STRING_length(cstr)) { case 4: type = IKEV2_ID_IPV4; break; case 16: type = IKEV2_ID_IPV6; break; default: log_debug("%s: invalid subjectAltName" " IP address", __func__); continue; } break; default: continue; } len = ASN1_STRING_length(cstr); data = ASN1_STRING_get0_data(cstr); if (mode == MODE_ALT_LOG) { struct iked_id sanid; bzero(&sanid, sizeof(sanid)); sanid.id_offset = 0; sanid.id_type = type; if ((sanid.id_buf = ibuf_new(data, len)) == NULL) { log_debug("%s: failed to get id buffer", __func__); continue; } ikev2_print_id(&sanid, idstr, sizeof(idstr)); log_info("%s: altname: %s", logmsg, idstr); ibuf_free(sanid.id_buf); sanid.id_buf = NULL; } /* Compare length and data */ if (mode == MODE_ALT_CMP) { if (type == id->id_type && (len == (id->id_length - id->id_offset)) && (memcmp(id->id_data + id->id_offset, data, len)) == 0) { ret = 0; break; } } /* Get first ID */ if (mode == MODE_ALT_GET) { ibuf_free(retid->id_buf); if ((retid->id_buf = ibuf_new(data, len)) == NULL) { log_debug("%s: failed to get id buffer", __func__); ret = -2; break; } retid->id_offset = 0; ikev2_print_id(retid, idstr, sizeof(idstr)); log_debug("%s: %s", __func__, idstr); ret = 0; break; } } sk_GENERAL_NAME_pop_free(stack, GENERAL_NAME_free); } else if (crit == -2) log_info("%s: multiple subjectAltName extensions are invalid", __func__); else if (crit == -1) log_debug("%s: did not find subjectAltName in certificate", __func__); else log_debug("%s: failed to decode subjectAltName", __func__); return ret; } int ca_x509_subjectaltname_log(X509 *cert, const char *logmsg) { return ca_x509_subjectaltname_do(cert, MODE_ALT_LOG, logmsg, NULL, NULL); } int ca_x509_subjectaltname_cmp(X509 *cert, struct iked_static_id *id) { return ca_x509_subjectaltname_do(cert, MODE_ALT_CMP, NULL, id, NULL); } int ca_x509_subjectaltname_get(X509 *cert, struct iked_id *retid) { return ca_x509_subjectaltname_do(cert, MODE_ALT_GET, NULL, NULL, retid); } void ca_sslinit(void) { #if OPENSSL_VERSION_NUMBER >= 0x30000000 /* * This provider contains all the regular and safe algorithms, * such as AES, SHA... */ if (OSSL_PROVIDER_load(NULL, "default") == NULL) fatal("error enabling default provider"); /* * This provider contains unsafe or obsolete, such as MD4 which * we need for MSCHAPv2 */ if (OSSL_PROVIDER_load(NULL, "legacy") == NULL) fatal("error enabling legacy provider"); #endif } void ca_sslerror(const char *caller) { unsigned long error; while ((error = ERR_get_error()) != 0) log_warnx("%s: %s: %.100s", __func__, caller, ERR_error_string(error, NULL)); } openiked-7.4/iked/chap_ms.c000066400000000000000000000277111477554731100157020ustar00rootroot00000000000000/* $OpenBSD: chap_ms.c,v 1.9 2015/08/21 11:59:27 reyk Exp $ */ /* * Copyright (c) 2021 Tobias Heider * Copyright (c) 2010-2013 Reyk Floeter * Copyright (c) 1997-2001 Brian Somers * Copyright (c) 1997 Gabor Kincses * Copyright (c) 1995 Eric Rosenquist * * 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ #include #include #include #include #include #include #include #include #include #include "chap_ms.h" extern __dead void fatalx(const char *, ...) __attribute__((__format__ (printf, 1, 2))); /* * Documentation & specifications: * * MS-CHAP (CHAP80) RFC2433 * MS-CHAP-V2 (CHAP81) RFC2759 * MPPE key management RFC3079 * * Security analysis: * Schneier/Mudge/Wagner, "MS-CHAP-v2", Oct 99 * "It is unclear to us why this protocol is so complicated." */ static uint8_t sha1_pad1[40] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static uint8_t sha1_pad2[40] = { 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2 }; uint8_t get7bits(uint8_t *, int); void mschap_des_addparity(uint8_t *, uint8_t *); void mschap_des_encrypt(uint8_t *, uint8_t *, uint8_t *); void mschap_challenge_response(uint8_t *, uint8_t *, uint8_t *); uint8_t get7bits(uint8_t *in, int start) { unsigned int word; word = (unsigned int)in[start / 8] << 8; word |= (unsigned int)in[start / 8 + 1]; word >>= 15 - (start % 8 + 7); return (word & 0xfe); } /* IN 56 bit DES key missing parity bits OUT 64 bit DES key with parity bits added */ void mschap_des_addparity(uint8_t *key, uint8_t *des_key) { des_key[0] = get7bits(key, 0); des_key[1] = get7bits(key, 7); des_key[2] = get7bits(key, 14); des_key[3] = get7bits(key, 21); des_key[4] = get7bits(key, 28); des_key[5] = get7bits(key, 35); des_key[6] = get7bits(key, 42); des_key[7] = get7bits(key, 49); DES_set_odd_parity((DES_cblock *)des_key); } void mschap_des_encrypt(uint8_t *clear, uint8_t *key, uint8_t *cipher) { DES_cblock des_key; DES_key_schedule key_schedule; mschap_des_addparity(key, des_key); DES_set_key(&des_key, &key_schedule); DES_ecb_encrypt((DES_cblock *)clear, (DES_cblock *)cipher, &key_schedule, 1); } void mschap_challenge_response(uint8_t *challenge, uint8_t *pwhash, uint8_t *response) { uint8_t padpwhash[21 + 1]; bzero(&padpwhash, sizeof(padpwhash)); memcpy(padpwhash, pwhash, MSCHAP_HASH_SZ); mschap_des_encrypt(challenge, padpwhash + 0, response + 0); mschap_des_encrypt(challenge, padpwhash + 7, response + 8); mschap_des_encrypt(challenge, padpwhash + 14, response + 16); } void mschap_ntpassword_hash(uint8_t *in, int inlen, uint8_t *hash) { EVP_MD_CTX *ctx; unsigned int mdlen; ctx = EVP_MD_CTX_new(); if (ctx == NULL) fatalx("%s: EVP_MD_CTX_NEW()", __func__); EVP_DigestInit(ctx, EVP_md4()); EVP_DigestUpdate(ctx, in, inlen); EVP_DigestFinal(ctx, hash, &mdlen); EVP_MD_CTX_free(ctx); } void mschap_challenge_hash(uint8_t *peer_challenge, uint8_t *auth_challenge, uint8_t *username, int usernamelen, uint8_t *challenge) { EVP_MD_CTX *ctx; uint8_t md[SHA_DIGEST_LENGTH]; unsigned int mdlen; uint8_t *name; if ((name = strrchr(username, '\\')) == NULL) name = username; else name++; ctx = EVP_MD_CTX_new(); if (ctx == NULL) fatalx("%s: EVP_MD_CTX_NEW()", __func__); EVP_DigestInit(ctx, EVP_sha1()); EVP_DigestUpdate(ctx, peer_challenge, MSCHAPV2_CHALLENGE_SZ); EVP_DigestUpdate(ctx, auth_challenge, MSCHAPV2_CHALLENGE_SZ); EVP_DigestUpdate(ctx, name, strlen(name)); EVP_DigestFinal(ctx, md, &mdlen); EVP_MD_CTX_free(ctx); memcpy(challenge, md, MSCHAP_CHALLENGE_SZ); } void mschap_nt_response(uint8_t *auth_challenge, uint8_t *peer_challenge, uint8_t *username, int usernamelen, uint8_t *password, int passwordlen, uint8_t *response) { uint8_t challenge[MSCHAP_CHALLENGE_SZ]; uint8_t password_hash[MSCHAP_HASH_SZ]; mschap_challenge_hash(peer_challenge, auth_challenge, username, usernamelen, challenge); mschap_ntpassword_hash(password, passwordlen, password_hash); mschap_challenge_response(challenge, password_hash, response); } void mschap_auth_response(uint8_t *password, int passwordlen, uint8_t *ntresponse, uint8_t *auth_challenge, uint8_t *peer_challenge, uint8_t *username, int usernamelen, uint8_t *auth_response) { EVP_MD_CTX *ctx; uint8_t password_hash[MSCHAP_HASH_SZ]; uint8_t password_hash2[MSCHAP_HASH_SZ]; uint8_t challenge[MSCHAP_CHALLENGE_SZ]; uint8_t md[SHA_DIGEST_LENGTH], *ptr; unsigned int mdlen; int i; const uint8_t hex[] = "0123456789ABCDEF"; static uint8_t magic1[39] = { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67, 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 }; static uint8_t magic2[41] = { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B, 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F, 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E, 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E }; ctx = EVP_MD_CTX_new(); if (ctx == NULL) fatalx("%s: EVP_MD_CTX_NEW()", __func__); mschap_ntpassword_hash(password, passwordlen, password_hash); mschap_ntpassword_hash(password_hash, MSCHAP_HASH_SZ, password_hash2); EVP_DigestInit(ctx, EVP_sha1()); EVP_DigestUpdate(ctx, password_hash2, sizeof(password_hash2)); EVP_DigestUpdate(ctx, ntresponse, 24); EVP_DigestUpdate(ctx, magic1, 39); EVP_DigestFinal(ctx, md, &mdlen); mschap_challenge_hash(peer_challenge, auth_challenge, username, usernamelen, challenge); EVP_DigestInit(ctx, EVP_sha1()); EVP_DigestUpdate(ctx, md, sizeof(md)); EVP_DigestUpdate(ctx, challenge, sizeof(challenge)); EVP_DigestUpdate(ctx, magic2, 41); EVP_DigestFinal(ctx, md, &mdlen); EVP_MD_CTX_free(ctx); /* * Encode the value of 'Digest' as "S=" followed by * 40 ASCII hexadecimal digits and return it in * AuthenticatorResponse. * For example, * "S=0123456789ABCDEF0123456789ABCDEF01234567" */ ptr = auth_response; *ptr++ = 'S'; *ptr++ = '='; for (i = 0; i < SHA_DIGEST_LENGTH; i++) { *ptr++ = hex[md[i] >> 4]; *ptr++ = hex[md[i] & 0x0f]; } } void mschap_masterkey(uint8_t *password_hash2, uint8_t *ntresponse, uint8_t *masterkey) { uint8_t md[SHA_DIGEST_LENGTH]; unsigned int mdlen; EVP_MD_CTX *ctx; static uint8_t magic1[27] = { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 }; ctx = EVP_MD_CTX_new(); if (ctx == NULL) fatalx("%s: EVP_MD_CTX_NEW()", __func__); EVP_DigestInit(ctx, EVP_sha1()); EVP_DigestUpdate(ctx, password_hash2, MSCHAP_HASH_SZ); EVP_DigestUpdate(ctx, ntresponse, 24); EVP_DigestUpdate(ctx, magic1, 27); EVP_DigestFinal(ctx, md, &mdlen); EVP_MD_CTX_free(ctx); memcpy(masterkey, md, 16); } void mschap_asymetric_startkey(uint8_t *masterkey, uint8_t *sessionkey, int sessionkeylen, int issend, int isserver) { EVP_MD_CTX *ctx; uint8_t md[SHA_DIGEST_LENGTH]; unsigned int mdlen; uint8_t *s; static uint8_t magic2[84] = { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x2e }; static uint8_t magic3[84] = { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79, 0x2e }; if (issend) s = isserver ? magic3 : magic2; else s = isserver ? magic2 : magic3; ctx = EVP_MD_CTX_new(); if (ctx == NULL) fatalx("%s: EVP_MD_CTX_NEW()", __func__); EVP_DigestInit(ctx, EVP_sha1()); EVP_DigestUpdate(ctx, masterkey, 16); EVP_DigestUpdate(ctx, sha1_pad1, 40); EVP_DigestUpdate(ctx, s, 84); EVP_DigestUpdate(ctx, sha1_pad2, 40); EVP_DigestFinal(ctx, md, &mdlen); EVP_MD_CTX_free(ctx); memcpy(sessionkey, md, sessionkeylen); } void mschap_msk(uint8_t *password, int passwordlen, uint8_t *ntresponse, uint8_t *msk) { uint8_t password_hash[MSCHAP_HASH_SZ]; uint8_t password_hash2[MSCHAP_HASH_SZ]; uint8_t masterkey[MSCHAP_MASTERKEY_SZ]; uint8_t sendkey[MSCHAP_MASTERKEY_SZ]; uint8_t recvkey[MSCHAP_MASTERKEY_SZ]; mschap_ntpassword_hash(password, passwordlen, password_hash); mschap_ntpassword_hash(password_hash, MSCHAP_HASH_SZ, password_hash2); mschap_masterkey(password_hash2, ntresponse, masterkey); mschap_asymetric_startkey(masterkey, recvkey, sizeof(recvkey), 0, 1); mschap_asymetric_startkey(masterkey, sendkey, sizeof(sendkey), 1, 1); /* 16 bytes receive key + 16 bytes send key + 32 bytes 0 padding */ bzero(msk, MSCHAP_MSK_SZ); memcpy(msk, &recvkey, sizeof(recvkey)); memcpy(msk + sizeof(recvkey), &sendkey, sizeof(sendkey)); } void mschap_radiuskey(uint8_t *plain, const uint8_t *crypted, const uint8_t *authenticator, const uint8_t *secret) { EVP_MD_CTX *ctx; uint8_t b[MD5_DIGEST_LENGTH], p[32]; unsigned int i, mdlen; ctx = EVP_MD_CTX_new(); if (ctx == NULL) fatalx("%s: EVP_MD_CTX_NEW()", __func__); EVP_DigestInit(ctx, EVP_md5()); EVP_DigestUpdate(ctx, secret, strlen(secret)); EVP_DigestUpdate(ctx, authenticator, 16); EVP_DigestUpdate(ctx, crypted, 2); EVP_DigestFinal(ctx, b, &mdlen); for (i = 0; i < mdlen; i++) { p[i] = b[i] ^ crypted[i+2]; } EVP_DigestInit(ctx, EVP_md5()); EVP_DigestUpdate(ctx, secret, strlen(secret)); EVP_DigestUpdate(ctx, crypted + 2, mdlen); EVP_DigestFinal(ctx, b, &mdlen); EVP_MD_CTX_free(ctx); for (i = 0; i < mdlen; i++) { p[i+16] = b[i] ^ crypted[i+18]; } memcpy(plain, p+1, 16); } openiked-7.4/iked/chap_ms.h000066400000000000000000000034221477554731100157000ustar00rootroot00000000000000/* $OpenBSD: chap_ms.h,v 1.6 2015/08/21 11:59:27 reyk Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef CHAP_MS_H #define CHAP_MS_H #define MSCHAP_CHALLENGE_SZ 8 #define MSCHAPV2_CHALLENGE_SZ 16 #define MSCHAP_HASH_SZ 16 #define MSCHAP_MASTERKEY_SZ 16 #define MSCHAP_MSK_KEY_SZ 32 #define MSCHAP_MSK_PADDING_SZ 32 #define MSCHAP_MSK_SZ 64 #define MSCHAP_MAXNTPASSWORD_SZ 255 /* unicode chars */ void mschap_nt_response(uint8_t *, uint8_t *, uint8_t *, int, uint8_t *, int , uint8_t *); void mschap_auth_response(uint8_t *, int, uint8_t *, uint8_t *, uint8_t *, uint8_t *, int, uint8_t *); void mschap_ntpassword_hash(uint8_t *, int, uint8_t *); void mschap_challenge_hash(uint8_t *, uint8_t *, uint8_t *, int, uint8_t *); void mschap_asymetric_startkey(uint8_t *, uint8_t *, int, int, int); void mschap_masterkey(uint8_t *, uint8_t *, uint8_t *); void mschap_radiuskey(uint8_t *, const uint8_t *, const uint8_t *, const uint8_t *); void mschap_msk(uint8_t *, int, uint8_t *, uint8_t *); #endif /* CHAP_MS_H */ openiked-7.4/iked/config.c000066400000000000000000000617051477554731100155360ustar00rootroot00000000000000/* $OpenBSD: config.c,v 1.97 2024/02/15 19:11:00 tobhe Exp $ */ /* * Copyright (c) 2019-2021 Tobias Heider * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" #include "ikev2.h" struct iked_sa * config_new_sa(struct iked *env, int initiator) { struct iked_sa *sa; if ((sa = calloc(1, sizeof(*sa))) == NULL) return (NULL); TAILQ_INIT(&sa->sa_proposals); TAILQ_INIT(&sa->sa_childsas); TAILQ_INIT(&sa->sa_flows); TAILQ_INIT(&sa->sa_requests); TAILQ_INIT(&sa->sa_responses); sa->sa_hdr.sh_initiator = initiator; sa->sa_type = IKED_SATYPE_LOCAL; if (initiator) sa->sa_hdr.sh_ispi = config_getspi(); else sa->sa_hdr.sh_rspi = config_getspi(); gettimeofday(&sa->sa_timecreated, NULL); memcpy(&sa->sa_timeused, &sa->sa_timecreated, sizeof(sa->sa_timeused)); ikestat_inc(env, ikes_sa_created); return (sa); } uint64_t config_getspi(void) { uint64_t spi; do { arc4random_buf(&spi, sizeof spi); } while (spi == 0); return (spi); } void config_free_kex(struct iked_kex *kex) { if (kex == NULL) return; ibuf_free(kex->kex_inonce); ibuf_free(kex->kex_rnonce); group_free(kex->kex_dhgroup); ibuf_free(kex->kex_dhiexchange); ibuf_free(kex->kex_dhrexchange); free(kex); } void config_free_fragments(struct iked_frag *frag) { size_t i; if (frag && frag->frag_arr) { for (i = 0; i < frag->frag_total; i++) { if (frag->frag_arr[i] != NULL) free(frag->frag_arr[i]->frag_data); free(frag->frag_arr[i]); } free(frag->frag_arr); bzero(frag, sizeof(struct iked_frag)); } } void config_free_sa(struct iked *env, struct iked_sa *sa) { int i; timer_del(env, &sa->sa_timer); timer_del(env, &sa->sa_keepalive); timer_del(env, &sa->sa_rekey); config_free_fragments(&sa->sa_fragments); config_free_proposals(&sa->sa_proposals, 0); config_free_childsas(env, &sa->sa_childsas, NULL, NULL); sa_configure_iface(env, sa, 0); sa_free_flows(env, &sa->sa_flows); if (sa->sa_addrpool) { (void)RB_REMOVE(iked_addrpool, &env->sc_addrpool, sa); free(sa->sa_addrpool); } if (sa->sa_addrpool6) { (void)RB_REMOVE(iked_addrpool6, &env->sc_addrpool6, sa); free(sa->sa_addrpool6); } if (sa->sa_policy) { TAILQ_REMOVE(&sa->sa_policy->pol_sapeers, sa, sa_peer_entry); policy_unref(env, sa->sa_policy); } ikev2_msg_flushqueue(env, &sa->sa_requests); ikev2_msg_flushqueue(env, &sa->sa_responses); ibuf_free(sa->sa_inonce); ibuf_free(sa->sa_rnonce); group_free(sa->sa_dhgroup); ibuf_free(sa->sa_dhiexchange); ibuf_free(sa->sa_dhrexchange); ibuf_free(sa->sa_simult); hash_free(sa->sa_prf); hash_free(sa->sa_integr); cipher_free(sa->sa_encr); ibuf_free(sa->sa_key_d); ibuf_free(sa->sa_key_iauth); ibuf_free(sa->sa_key_rauth); ibuf_free(sa->sa_key_iencr); ibuf_free(sa->sa_key_rencr); ibuf_free(sa->sa_key_iprf); ibuf_free(sa->sa_key_rprf); ibuf_free(sa->sa_1stmsg); ibuf_free(sa->sa_2ndmsg); ibuf_free(sa->sa_iid.id_buf); ibuf_free(sa->sa_rid.id_buf); ibuf_free(sa->sa_icert.id_buf); ibuf_free(sa->sa_rcert.id_buf); for (i = 0; i < IKED_SCERT_MAX; i++) ibuf_free(sa->sa_scert[i].id_buf); ibuf_free(sa->sa_localauth.id_buf); ibuf_free(sa->sa_peerauth.id_buf); ibuf_free(sa->sa_eap.id_buf); free(sa->sa_eapid); ibuf_free(sa->sa_eapmsk); free(sa->sa_cp_addr); free(sa->sa_cp_addr6); free(sa->sa_cp_dns); free(sa->sa_tag); if (sa->sa_state == IKEV2_STATE_ESTABLISHED) ikestat_dec(env, ikes_sa_established_current); ikestat_inc(env, ikes_sa_removed); free(sa); } struct iked_policy * config_new_policy(struct iked *env) { struct iked_policy *pol; if ((pol = calloc(1, sizeof(*pol))) == NULL) return (NULL); /* XXX caller does this again */ TAILQ_INIT(&pol->pol_proposals); TAILQ_INIT(&pol->pol_sapeers); TAILQ_INIT(&pol->pol_tssrc); TAILQ_INIT(&pol->pol_tsdst); RB_INIT(&pol->pol_flows); return (pol); } void config_free_policy(struct iked *env, struct iked_policy *pol) { struct iked_sa *sa; struct iked_ts *tsi; if (pol->pol_flags & IKED_POLICY_REFCNT) goto remove; /* * Remove policy from the sc_policies list, but increment * refcount for every SA linked for the policy. */ pol->pol_flags |= IKED_POLICY_REFCNT; TAILQ_REMOVE(&env->sc_policies, pol, pol_entry); TAILQ_FOREACH(sa, &pol->pol_sapeers, sa_peer_entry) { if (sa->sa_policy == pol) policy_ref(env, pol); else log_warnx("%s: ERROR: sa_policy %p != pol %p", __func__, sa->sa_policy, pol); } if (pol->pol_refcnt) return; remove: while ((tsi = TAILQ_FIRST(&pol->pol_tssrc))) { TAILQ_REMOVE(&pol->pol_tssrc, tsi, ts_entry); free(tsi); } while ((tsi = TAILQ_FIRST(&pol->pol_tsdst))) { TAILQ_REMOVE(&pol->pol_tsdst, tsi, ts_entry); free(tsi); } config_free_proposals(&pol->pol_proposals, 0); config_free_flows(env, &pol->pol_flows); free(pol); } struct iked_proposal * config_add_proposal(struct iked_proposals *head, unsigned int id, unsigned int proto) { struct iked_proposal *pp; TAILQ_FOREACH(pp, head, prop_entry) { if (pp->prop_protoid == proto && pp->prop_id == id) return (pp); } if ((pp = calloc(1, sizeof(*pp))) == NULL) return (NULL); pp->prop_protoid = proto; pp->prop_id = id; TAILQ_INSERT_TAIL(head, pp, prop_entry); return (pp); } void config_free_proposal(struct iked_proposals *head, struct iked_proposal *prop) { TAILQ_REMOVE(head, prop, prop_entry); if (prop->prop_nxforms) free(prop->prop_xforms); free(prop); } void config_free_proposals(struct iked_proposals *head, unsigned int proto) { struct iked_proposal *prop, *proptmp; TAILQ_FOREACH_SAFE(prop, head, prop_entry, proptmp) { /* Free any proposal or only selected SA proto */ if (proto != 0 && prop->prop_protoid != proto) continue; log_debug("%s: free %p", __func__, prop); config_free_proposal(head, prop); } } void config_free_flows(struct iked *env, struct iked_flows *head) { struct iked_flow *flow; while ((flow = RB_MIN(iked_flows, head))) { log_debug("%s: free %p", __func__, flow); RB_REMOVE(iked_flows, head, flow); flow_free(flow); } } void config_free_childsas(struct iked *env, struct iked_childsas *head, struct iked_spi *peerspi, struct iked_spi *localspi) { struct iked_childsa *csa, *csatmp, *ipcomp; if (localspi != NULL) bzero(localspi, sizeof(*localspi)); TAILQ_FOREACH_SAFE(csa, head, csa_entry, csatmp) { if (peerspi != NULL) { /* Only delete matching peer SPIs */ if (peerspi->spi != csa->csa_peerspi) continue; /* Store assigned local SPI */ if (localspi != NULL && localspi->spi == 0) memcpy(localspi, &csa->csa_spi, sizeof(*localspi)); } log_debug("%s: free %p", __func__, csa); TAILQ_REMOVE(head, csa, csa_entry); if (csa->csa_loaded) { RB_REMOVE(iked_activesas, &env->sc_activesas, csa); (void)ipsec_sa_delete(env, csa); } if ((ipcomp = csa->csa_bundled) != NULL) { log_debug("%s: free IPCOMP %p", __func__, ipcomp); if (ipcomp->csa_loaded) (void)ipsec_sa_delete(env, ipcomp); childsa_free(ipcomp); } childsa_free(csa); ikestat_inc(env, ikes_csa_removed); } } int config_add_transform(struct iked_proposal *prop, unsigned int type, unsigned int id, unsigned int length, unsigned int keylength) { struct iked_transform *xform; struct iked_constmap *map = NULL; int score = 1; unsigned int i; switch (type) { case IKEV2_XFORMTYPE_ENCR: map = ikev2_xformencr_map; break; case IKEV2_XFORMTYPE_PRF: map = ikev2_xformprf_map; break; case IKEV2_XFORMTYPE_INTEGR: map = ikev2_xformauth_map; break; case IKEV2_XFORMTYPE_DH: map = ikev2_xformdh_map; break; case IKEV2_XFORMTYPE_ESN: map = ikev2_xformesn_map; break; default: log_debug("%s: invalid transform type %d", __func__, type); return (-2); } for (i = 0; i < prop->prop_nxforms; i++) { xform = prop->prop_xforms + i; if (xform->xform_type == type && xform->xform_id == id && xform->xform_length == length) return (0); } for (i = 0; i < prop->prop_nxforms; i++) { xform = prop->prop_xforms + i; if (xform->xform_type == type) { switch (type) { case IKEV2_XFORMTYPE_ENCR: case IKEV2_XFORMTYPE_INTEGR: score += 3; break; case IKEV2_XFORMTYPE_DH: score += 2; break; default: score += 1; break; } } } if ((xform = reallocarray(prop->prop_xforms, prop->prop_nxforms + 1, sizeof(*xform))) == NULL) { return (-1); } prop->prop_xforms = xform; xform = prop->prop_xforms + prop->prop_nxforms++; bzero(xform, sizeof(*xform)); xform->xform_type = type; xform->xform_id = id; xform->xform_length = length; xform->xform_keylength = keylength; xform->xform_score = score; xform->xform_map = map; return (0); } struct iked_transform * config_findtransform_ext(struct iked_proposals *props, uint8_t type, int id, unsigned int proto) { struct iked_proposal *prop; struct iked_transform *xform; unsigned int i; /* Search of the first transform with the desired type */ TAILQ_FOREACH(prop, props, prop_entry) { /* Find any proposal or only selected SA proto */ if (proto != 0 && prop->prop_protoid != proto) continue; for (i = 0; i < prop->prop_nxforms; i++) { xform = prop->prop_xforms + i; /* optional lookup of specific transform */ if (id >= 0 && xform->xform_id != id) continue; if (xform->xform_type == type) return (xform); } } return (NULL); } struct iked_transform * config_findtransform(struct iked_proposals *props, uint8_t type, unsigned int proto) { return config_findtransform_ext(props, type, -1, proto); } struct iked_user * config_new_user(struct iked *env, struct iked_user *new) { struct iked_user *usr, *old; if ((usr = calloc(1, sizeof(*usr))) == NULL) return (NULL); memcpy(usr, new, sizeof(*usr)); if ((old = RB_INSERT(iked_users, &env->sc_users, usr)) != NULL) { /* Update the password of an existing user*/ memcpy(old->usr_pass, new->usr_pass, IKED_PASSWORD_SIZE); log_debug("%s: updating user %s", __func__, usr->usr_name); freezero(usr, sizeof *usr); return (old); } log_debug("%s: inserting new user %s", __func__, usr->usr_name); return (usr); } /* * Inter-process communication of configuration items. */ int config_setcoupled(struct iked *env, unsigned int couple) { unsigned int type; type = couple ? IMSG_CTL_COUPLE : IMSG_CTL_DECOUPLE; proc_compose(&env->sc_ps, PROC_IKEV2, type, NULL, 0); return (0); } int config_getcoupled(struct iked *env, unsigned int type) { return (ipsec_couple(env, &env->sc_sas, type == IMSG_CTL_COUPLE ? 1 : 0)); } int config_setmode(struct iked *env, unsigned int passive) { unsigned int type; /* * In order to control the startup of the processes, * the messages are sent in this order: * PROC_PARENT -> PROC_CERT -> PROC_PARENT -> PROC_IKEV2 * so PROC_CERT is ready before PROC_IKEV2 is activated. */ type = passive ? IMSG_CTL_PASSIVE : IMSG_CTL_ACTIVE; proc_compose(&env->sc_ps, PROC_CERT, type, NULL, 0); return (0); } int config_getmode(struct iked *env, unsigned int type) { uint8_t old; unsigned char *mode[] = { "active", "passive" }; old = env->sc_passive ? 1 : 0; env->sc_passive = type == IMSG_CTL_PASSIVE ? 1 : 0; if (old == env->sc_passive) return (0); log_debug("%s: mode %s -> %s", __func__, mode[old], mode[env->sc_passive]); return (0); } int config_setreset(struct iked *env, unsigned int mode, enum privsep_procid id) { proc_compose(&env->sc_ps, id, IMSG_CTL_RESET, &mode, sizeof(mode)); return (0); } int config_getreset(struct iked *env, struct imsg *imsg) { unsigned int mode; IMSG_SIZE_CHECK(imsg, &mode); memcpy(&mode, imsg->data, sizeof(mode)); return (config_doreset(env, mode)); } int config_doreset(struct iked *env, unsigned int mode) { struct iked_policy *pol, *poltmp; struct iked_sa *sa; struct iked_user *usr; if (mode == RESET_ALL || mode == RESET_POLICY) { log_debug("%s: flushing policies", __func__); TAILQ_FOREACH_SAFE(pol, &env->sc_policies, pol_entry, poltmp) { config_free_policy(env, pol); } } if (mode == RESET_ALL || mode == RESET_SA) { log_debug("%s: flushing SAs", __func__); while ((sa = RB_MIN(iked_sas, &env->sc_sas))) { /* for RESET_SA we try send a DELETE */ if (mode == RESET_ALL || ikev2_ike_sa_delete(env, sa) != 0) { RB_REMOVE(iked_sas, &env->sc_sas, sa); if (sa->sa_dstid_entry_valid) sa_dstid_remove(env, sa); config_free_sa(env, sa); } } } if (mode == RESET_ALL || mode == RESET_USER) { log_debug("%s: flushing users", __func__); while ((usr = RB_MIN(iked_users, &env->sc_users))) { RB_REMOVE(iked_users, &env->sc_users, usr); free(usr); } } return (0); } /* * The first call of this function sets the UDP socket for IKEv2. * The second call is optional, setting the UDP socket used for NAT-T. */ int config_setsocket(struct iked *env, struct sockaddr_storage *ss, in_port_t port, enum privsep_procid id, int natt) { int s; if ((s = udp_bind((struct sockaddr *)ss, port)) == -1) return (-1); #if defined(UDP_ENCAP_ESPINUDP) if (natt #if !defined(HAVE_UDPENCAP6) && ss->ss_family != AF_INET6 #endif ) { int sopt; sopt = UDP_ENCAP_ESPINUDP; if (setsockopt(s, IPPROTO_UDP, UDP_ENCAP, &sopt, sizeof(sopt)) < 0) { log_warn("%s: failed to set UDP encap socket option", __func__); return (-1); } } #endif proc_compose_imsg(&env->sc_ps, id, -1, IMSG_UDP_SOCKET, -1, s, ss, sizeof(*ss)); return (0); } int config_getsocket(struct iked *env, struct imsg *imsg, void (*cb)(int, short, void *)) { struct iked_socket *sock, **sock0 = NULL, **sock1 = NULL; if ((sock = calloc(1, sizeof(*sock))) == NULL) fatal("config_getsocket: calloc"); IMSG_SIZE_CHECK(imsg, &sock->sock_addr); memcpy(&sock->sock_addr, imsg->data, sizeof(sock->sock_addr)); sock->sock_fd = imsg_get_fd(imsg); sock->sock_env = env; log_debug("%s: received socket fd %d", __func__, sock->sock_fd); switch (sock->sock_addr.ss_family) { case AF_INET: sock0 = &env->sc_sock4[0]; sock1 = &env->sc_sock4[1]; break; case AF_INET6: sock0 = &env->sc_sock6[0]; sock1 = &env->sc_sock6[1]; break; default: fatal("config_getsocket: socket af: %u", sock->sock_addr.ss_family); /* NOTREACHED */ } if (*sock0 == NULL) *sock0 = sock; else if (*sock1 == NULL) *sock1 = sock; else fatalx("%s: too many call", __func__); event_set(&sock->sock_ev, sock->sock_fd, EV_READ|EV_PERSIST, cb, sock); return (0); } void config_enablesocket(struct iked *env) { struct iked_socket *sock; size_t i; for (i = 0; i < nitems(env->sc_sock4); i++) if ((sock = env->sc_sock4[i]) != NULL) event_add(&sock->sock_ev, NULL); for (i = 0; i < nitems(env->sc_sock6); i++) if ((sock = env->sc_sock6[i]) != NULL) event_add(&sock->sock_ev, NULL); } int config_setpfkey(struct iked *env) { int s; if ((s = ipsec_socket(env)) == -1) return (-1); proc_compose_imsg(&env->sc_ps, PROC_IKEV2, -1, IMSG_PFKEY_SOCKET, -1, s, NULL, 0); return (0); } int config_getpfkey(struct iked *env, struct imsg *imsg) { int fd = imsg_get_fd(imsg); log_debug("%s: received pfkey fd %d", __func__, fd); ipsec_init(env, fd); return (0); } int config_setuser(struct iked *env, struct iked_user *usr, enum privsep_procid id) { if (env->sc_opts & IKED_OPT_NOACTION) { print_user(usr); return (0); } proc_compose(&env->sc_ps, id, IMSG_CFG_USER, usr, sizeof(*usr)); return (0); } int config_getuser(struct iked *env, struct imsg *imsg) { struct iked_user usr; int ret = -1; IMSG_SIZE_CHECK(imsg, &usr); memcpy(&usr, imsg->data, sizeof(usr)); if (config_new_user(env, &usr) != NULL) { print_user(&usr); ret = 0; } explicit_bzero(&usr, sizeof(usr)); return (ret); } int config_setpolicy(struct iked *env, struct iked_policy *pol, enum privsep_procid id) { struct iked_proposal *prop; struct iked_transform *xform; size_t iovcnt, j, c = 0; struct iovec iov[IOV_MAX]; iovcnt = 1; TAILQ_FOREACH(prop, &pol->pol_proposals, prop_entry) { iovcnt += prop->prop_nxforms + 1; } if (iovcnt > IOV_MAX) { log_warn("%s: too many proposals", __func__); return (-1); } iov[c].iov_base = pol; iov[c++].iov_len = sizeof(*pol); TAILQ_FOREACH(prop, &pol->pol_proposals, prop_entry) { iov[c].iov_base = prop; iov[c++].iov_len = sizeof(*prop); for (j = 0; j < prop->prop_nxforms; j++) { xform = prop->prop_xforms + j; iov[c].iov_base = xform; iov[c++].iov_len = sizeof(*xform); } } print_policy(pol); if (env->sc_opts & IKED_OPT_NOACTION) return (0); if (proc_composev(&env->sc_ps, id, IMSG_CFG_POLICY, iov, iovcnt) == -1) { log_debug("%s: proc_composev failed", __func__); return (-1); } return (0); } int config_setflow(struct iked *env, struct iked_policy *pol, enum privsep_procid id) { struct iked_flow *flow; struct iovec iov[2]; if (env->sc_opts & IKED_OPT_NOACTION) return (0); RB_FOREACH(flow, iked_flows, &pol->pol_flows) { iov[0].iov_base = &pol->pol_id; iov[0].iov_len = sizeof(pol->pol_id); iov[1].iov_base = flow; iov[1].iov_len = sizeof(*flow); if (proc_composev(&env->sc_ps, id, IMSG_CFG_FLOW, iov, 2) == -1) { log_debug("%s: proc_composev failed", __func__); return (-1); } } return (0); } int config_getpolicy(struct iked *env, struct imsg *imsg) { struct iked_policy *pol; struct iked_proposal pp, *prop; struct iked_transform xf; off_t offset = 0; unsigned int i, j; uint8_t *buf = (uint8_t *)imsg->data; IMSG_SIZE_CHECK(imsg, pol); log_debug("%s: received policy", __func__); if ((pol = config_new_policy(NULL)) == NULL) fatal("config_getpolicy: new policy"); memcpy(pol, buf, sizeof(*pol)); offset += sizeof(*pol); TAILQ_INIT(&pol->pol_tssrc); TAILQ_INIT(&pol->pol_tsdst); TAILQ_INIT(&pol->pol_proposals); TAILQ_INIT(&pol->pol_sapeers); RB_INIT(&pol->pol_flows); for (i = 0; i < pol->pol_nproposals; i++) { memcpy(&pp, buf + offset, sizeof(pp)); offset += sizeof(pp); if ((prop = config_add_proposal(&pol->pol_proposals, pp.prop_id, pp.prop_protoid)) == NULL) fatal("config_getpolicy: add proposal"); for (j = 0; j < pp.prop_nxforms; j++) { memcpy(&xf, buf + offset, sizeof(xf)); offset += sizeof(xf); if (config_add_transform(prop, xf.xform_type, xf.xform_id, xf.xform_length, xf.xform_keylength) != 0) fatal("config_getpolicy: add transform"); } } /* Flows are sent separately */ pol->pol_nflows = 0; TAILQ_INSERT_TAIL(&env->sc_policies, pol, pol_entry); if (pol->pol_flags & IKED_POLICY_DEFAULT) { /* Only one default policy, just free/unref the old one */ if (env->sc_defaultcon != NULL) config_free_policy(env, env->sc_defaultcon); env->sc_defaultcon = pol; } return (0); } int config_getflow(struct iked *env, struct imsg *imsg) { struct iked_policy *pol; struct iked_flow *flow; off_t offset = 0; unsigned int id; uint8_t *buf = (uint8_t *)imsg->data; if (IMSG_DATA_SIZE(imsg) < sizeof(id)) fatalx("bad length imsg received"); memcpy(&id, buf, sizeof(id)); offset += sizeof(id); TAILQ_FOREACH(pol, &env->sc_policies, pol_entry) { if (pol->pol_id == id) break; } if (pol == NULL) { log_warnx("%s: unknown policy %u", __func__, id); return (-1); } if ((flow = calloc(1, sizeof(*flow))) == NULL) fatal("config_getpolicy: new flow"); memcpy(flow, buf + offset, sizeof(*flow)); if (RB_INSERT(iked_flows, &pol->pol_flows, flow)) { log_warnx("%s: received duplicate flow", __func__); free(flow); return (-1); } pol->pol_nflows++; return (0); } int config_setcompile(struct iked *env, enum privsep_procid id) { if (env->sc_opts & IKED_OPT_NOACTION) return (0); proc_compose(&env->sc_ps, id, IMSG_COMPILE, NULL, 0); return (0); } int config_getcompile(struct iked *env) { /* * Do any necessary steps after configuration, for now we * only need to compile the skip steps. */ policy_calc_skip_steps(&env->sc_policies); log_debug("%s: compilation done", __func__); return (0); } int config_setstatic(struct iked *env) { proc_compose(&env->sc_ps, PROC_IKEV2, IMSG_CTL_STATIC, &env->sc_static, sizeof(env->sc_static)); proc_compose(&env->sc_ps, PROC_CERT, IMSG_CTL_STATIC, &env->sc_static, sizeof(env->sc_static)); return (0); } int config_getstatic(struct iked *env, struct imsg *imsg) { IMSG_SIZE_CHECK(imsg, &env->sc_static); memcpy(&env->sc_static, imsg->data, sizeof(env->sc_static)); log_debug("%s: dpd_check_interval %llu", __func__, (long long unsigned)env->sc_alive_timeout); log_debug("%s: %senforcesingleikesa", __func__, env->sc_enforcesingleikesa ? "" : "no "); log_debug("%s: %sfragmentation", __func__, env->sc_frag ? "" : "no "); log_debug("%s: %smobike", __func__, env->sc_mobike ? "" : "no "); log_debug("%s: nattport %u", __func__, env->sc_nattport); log_debug("%s: %sstickyaddress", __func__, env->sc_stickyaddress ? "" : "no "); ikev2_reset_alive_timer(env); return (0); } int config_setocsp(struct iked *env) { struct iovec iov[3]; int iovcnt = 0; if (env->sc_opts & IKED_OPT_NOACTION) return (0); iov[0].iov_base = &env->sc_ocsp_tolerate; iov[0].iov_len = sizeof(env->sc_ocsp_tolerate); iovcnt++; iov[1].iov_base = &env->sc_ocsp_maxage; iov[1].iov_len = sizeof(env->sc_ocsp_maxage); iovcnt++; if (env->sc_ocsp_url) { iov[2].iov_base = env->sc_ocsp_url; iov[2].iov_len = strlen(env->sc_ocsp_url); iovcnt++; } return (proc_composev(&env->sc_ps, PROC_CERT, IMSG_OCSP_CFG, iov, iovcnt)); } int config_getocsp(struct iked *env, struct imsg *imsg) { size_t have, need; uint8_t *ptr; free(env->sc_ocsp_url); ptr = (uint8_t *)imsg->data; have = IMSG_DATA_SIZE(imsg); /* get tolerate */ need = sizeof(env->sc_ocsp_tolerate); if (have < need) fatalx("bad 'tolerate' length imsg received"); memcpy(&env->sc_ocsp_tolerate, ptr, need); ptr += need; have -= need; /* get maxage */ need = sizeof(env->sc_ocsp_maxage); if (have < need) fatalx("bad 'maxage' length imsg received"); memcpy(&env->sc_ocsp_maxage, ptr, need); ptr += need; have -= need; /* get url */ if (have > 0) env->sc_ocsp_url = get_string(ptr, have); else env->sc_ocsp_url = NULL; log_debug("%s: ocsp_url %s tolerate %ld maxage %ld", __func__, env->sc_ocsp_url ? env->sc_ocsp_url : "none", env->sc_ocsp_tolerate, env->sc_ocsp_maxage); return (0); } int config_setkeys(struct iked *env) { FILE *fp = NULL; EVP_PKEY *key = NULL; struct iked_id privkey; struct iked_id pubkey; struct iovec iov[2]; int ret = -1; memset(&privkey, 0, sizeof(privkey)); memset(&pubkey, 0, sizeof(pubkey)); /* Read private key */ if ((fp = fopen(IKED_PRIVKEY, "r")) == NULL) { log_warn("%s: failed to open private key", __func__); goto done; } if ((key = PEM_read_PrivateKey(fp, NULL, NULL, NULL)) == NULL) { log_warnx("%s: failed to read private key", __func__); goto done; } if (ca_privkey_serialize(key, &privkey) != 0) { log_warnx("%s: failed to serialize private key", __func__); goto done; } if (ca_pubkey_serialize(key, &pubkey) != 0) { log_warnx("%s: failed to serialize public key", __func__); goto done; } iov[0].iov_base = &privkey; iov[0].iov_len = sizeof(privkey); iov[1].iov_base = ibuf_data(privkey.id_buf); iov[1].iov_len = ibuf_size(privkey.id_buf); if (proc_composev(&env->sc_ps, PROC_CERT, IMSG_PRIVKEY, iov, 2) == -1) { log_warnx("%s: failed to send private key", __func__); goto done; } iov[0].iov_base = &pubkey; iov[0].iov_len = sizeof(pubkey); iov[1].iov_base = ibuf_data(pubkey.id_buf); iov[1].iov_len = ibuf_size(pubkey.id_buf); if (proc_composev(&env->sc_ps, PROC_CERT, IMSG_PUBKEY, iov, 2) == -1) { log_warnx("%s: failed to send public key", __func__); goto done; } ret = 0; done: if (fp != NULL) fclose(fp); ibuf_free(pubkey.id_buf); ibuf_free(privkey.id_buf); EVP_PKEY_free(key); return (ret); } int config_getkey(struct iked *env, struct imsg *imsg) { size_t len; struct iked_id id; len = IMSG_DATA_SIZE(imsg); if (len <= sizeof(id)) fatalx("%s: invalid key message", __func__); memcpy(&id, imsg->data, sizeof(id)); if ((id.id_buf = ibuf_new((uint8_t *)imsg->data + sizeof(id), len - sizeof(id))) == NULL) fatalx("%s: failed to get key", __func__); explicit_bzero(imsg->data, len); ca_getkey(&env->sc_ps, &id, imsg->hdr.type); return (0); } openiked-7.4/iked/control.c000066400000000000000000000221231477554731100157400ustar00rootroot00000000000000/* $OpenBSD: control.c,v 1.45 2024/11/21 13:35:20 claudio Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" #ifdef WITH_APPARMOR #include "apparmor.h" #endif #define CONTROL_BACKLOG 5 struct ctl_connlist ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns); uint32_t ctl_peerid; void control_accept(int, short, void *); struct ctl_conn *control_connbyfd(int); void control_close(int, struct control_sock *); void control_dispatch_imsg(int, short, void *); void control_imsg_forward(struct imsg *); void control_imsg_forward_peerid(struct imsg *); void control_run(struct privsep *, struct privsep_proc *, void *); int control_dispatch_ikev2(int, struct privsep_proc *, struct imsg *); int control_dispatch_ca(int, struct privsep_proc *, struct imsg *); static struct privsep_proc procs[] = { { "parent", PROC_PARENT, NULL }, { "ikev2", PROC_IKEV2, control_dispatch_ikev2 }, { "ca", PROC_CERT, control_dispatch_ca }, }; void control(struct privsep *ps, struct privsep_proc *p) { proc_run(ps, p, procs, nitems(procs), control_run, NULL); } void control_run(struct privsep *ps, struct privsep_proc *p, void *arg) { /* * pledge in the control process: * stdio - for malloc and basic I/O including events. * unix - for the control socket. */ if (pledge("stdio unix recvfd", NULL) == -1) fatal("pledge"); #ifdef WITH_APPARMOR if (armor_change_profile(ps->ps_env->sc_apparmor, "iked//control") == -1) log_warnx("warning: armor_change_profile " "(\"iked//control\") failed"); #endif } int control_init(struct privsep *ps, struct control_sock *cs) { struct iked *env = iked_env; struct sockaddr_un s_un; int fd; mode_t old_umask, mode; if (cs->cs_name == NULL) return (0); if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) { log_warn("%s: socket", __func__); return (-1); } s_un.sun_family = AF_UNIX; if (strlcpy(s_un.sun_path, cs->cs_name, sizeof(s_un.sun_path)) >= sizeof(s_un.sun_path)) { log_warn("%s: %s name too long", __func__, cs->cs_name); close(fd); return (-1); } if (unlink(cs->cs_name) == -1) if (errno != ENOENT) { log_warn("%s: unlink %s", __func__, cs->cs_name); close(fd); return (-1); } if (cs->cs_restricted) { old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH); mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; } else { old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP; } if (bind(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { log_warn("%s: bind: %s", __func__, cs->cs_name); close(fd); (void)umask(old_umask); return (-1); } (void)umask(old_umask); if (chmod(cs->cs_name, mode) == -1) { log_warn("%s: chmod", __func__); close(fd); (void)unlink(cs->cs_name); return (-1); } cs->cs_fd = fd; cs->cs_env = env; return (0); } int control_listen(struct control_sock *cs) { if (cs->cs_name == NULL) return (0); if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1) { log_warn("%s: listen", __func__); return (-1); } event_set(&cs->cs_ev, cs->cs_fd, EV_READ, control_accept, cs); event_add(&cs->cs_ev, NULL); evtimer_set(&cs->cs_evt, control_accept, cs); return (0); } void control_accept(int listenfd, short event, void *arg) { struct control_sock *cs = arg; int connfd; socklen_t len; struct sockaddr_un s_un; struct ctl_conn *c; struct ctl_conn *other; event_add(&cs->cs_ev, NULL); if ((event & EV_TIMEOUT)) return; len = sizeof(s_un); if ((connfd = accept4(listenfd, (struct sockaddr *)&s_un, &len, SOCK_NONBLOCK)) == -1) { /* * Pause accept if we are out of file descriptors, or * libevent will haunt us here too. */ if (errno == ENFILE || errno == EMFILE) { struct timeval evtpause = { 1, 0 }; event_del(&cs->cs_ev); evtimer_add(&cs->cs_evt, &evtpause); } else if (errno != EWOULDBLOCK && errno != EINTR && errno != ECONNABORTED) log_warn("%s: accept", __func__); return; } if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) { log_warn("%s", __func__); close(connfd); return; } if (imsgbuf_init(&c->iev.ibuf, connfd) == -1) { log_warn("%s", __func__); close(connfd); free(c); return; } c->iev.handler = control_dispatch_imsg; c->iev.events = EV_READ; c->iev.data = cs; event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events, c->iev.handler, c->iev.data); event_add(&c->iev.ev, NULL); /* O(n^2), but n is small */ c->peerid = ctl_peerid++; TAILQ_FOREACH(other, &ctl_conns, entry) if (c->peerid == other->peerid) c->peerid = ctl_peerid++; TAILQ_INSERT_TAIL(&ctl_conns, c, entry); } struct ctl_conn * control_connbyfd(int fd) { struct ctl_conn *c; TAILQ_FOREACH(c, &ctl_conns, entry) { if (c->iev.ibuf.fd == fd) break; } return (c); } void control_close(int fd, struct control_sock *cs) { struct ctl_conn *c; if ((c = control_connbyfd(fd)) == NULL) { log_warn("%s: fd %d: not found", __func__, fd); return; } imsgbuf_clear(&c->iev.ibuf); TAILQ_REMOVE(&ctl_conns, c, entry); event_del(&c->iev.ev); close(c->iev.ibuf.fd); /* Some file descriptors are available again. */ if (evtimer_pending(&cs->cs_evt, NULL)) { evtimer_del(&cs->cs_evt); event_add(&cs->cs_ev, NULL); } free(c); } void control_dispatch_imsg(int fd, short event, void *arg) { struct control_sock *cs = arg; struct iked *env = cs->cs_env; struct ctl_conn *c; struct imsg imsg; int n, v; if ((c = control_connbyfd(fd)) == NULL) { log_warn("%s: fd %d: not found", __func__, fd); return; } if (event & EV_READ) { if (imsgbuf_read(&c->iev.ibuf) != 1) { control_close(fd, cs); return; } } if (event & EV_WRITE) { if (imsgbuf_write(&c->iev.ibuf) == -1) { control_close(fd, cs); return; } } for (;;) { if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) { control_close(fd, cs); return; } if (n == 0) break; control_imsg_forward(&imsg); /* record peerid of connection for reply */ imsg.hdr.peerid = c->peerid; switch (imsg.hdr.type) { case IMSG_CTL_NOTIFY: if (c->flags & CTL_CONN_NOTIFY) { log_debug("%s: " "client requested notify more than once", __func__); imsg_compose_event(&c->iev, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); break; } c->flags |= CTL_CONN_NOTIFY; break; case IMSG_CTL_VERBOSE: IMSG_SIZE_CHECK(&imsg, &v); memcpy(&v, imsg.data, sizeof(v)); log_setverbose(v); proc_forward_imsg(&env->sc_ps, &imsg, PROC_PARENT, -1); break; case IMSG_CTL_RELOAD: case IMSG_CTL_RESET: case IMSG_CTL_COUPLE: case IMSG_CTL_DECOUPLE: case IMSG_CTL_ACTIVE: case IMSG_CTL_PASSIVE: proc_forward_imsg(&env->sc_ps, &imsg, PROC_PARENT, -1); break; case IMSG_CTL_RESET_ID: proc_forward_imsg(&env->sc_ps, &imsg, PROC_IKEV2, -1); break; case IMSG_CTL_SHOW_SA: case IMSG_CTL_SHOW_STATS: proc_forward_imsg(&env->sc_ps, &imsg, PROC_IKEV2, -1); break; case IMSG_CTL_SHOW_CERTSTORE: proc_forward_imsg(&env->sc_ps, &imsg, PROC_CERT, -1); break; default: log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type); break; } imsg_free(&imsg); } imsg_event_add(&c->iev); } void control_imsg_forward(struct imsg *imsg) { struct ctl_conn *c; TAILQ_FOREACH(c, &ctl_conns, entry) if (c->flags & CTL_CONN_NOTIFY) imsg_compose_event(&c->iev, imsg->hdr.type, 0, imsg->hdr.pid, -1, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE); } void control_imsg_forward_peerid(struct imsg *imsg) { struct ctl_conn *c; TAILQ_FOREACH(c, &ctl_conns, entry) if (c->peerid == imsg->hdr.peerid) imsg_compose_event(&c->iev, imsg->hdr.type, 0, imsg->hdr.pid, -1, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE); } int control_dispatch_ikev2(int fd, struct privsep_proc *p, struct imsg *imsg) { switch (imsg->hdr.type) { case IMSG_CTL_SHOW_SA: case IMSG_CTL_SHOW_STATS: control_imsg_forward_peerid(imsg); return (0); default: break; } return (-1); } int control_dispatch_ca(int fd, struct privsep_proc *p, struct imsg *imsg) { switch (imsg->hdr.type) { case IMSG_CTL_SHOW_CERTSTORE: control_imsg_forward_peerid(imsg); return (0); default: break; } return (-1); } openiked-7.4/iked/crypto.c000066400000000000000000000730221477554731100156040ustar00rootroot00000000000000/* $OpenBSD: crypto.c,v 1.47 2024/11/21 13:26:49 claudio Exp $ */ /* * Copyright (c) 2020-2021 Tobias Heider * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" #include "ikev2.h" /* RFC 7427, A.1 RSA */ static const uint8_t sha256WithRSA[] = { 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00 }; static const uint8_t sha384WithRSA[] = { 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c, 0x05, 0x00 }; static const uint8_t sha512WithRSA[] = { 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d, 0x05, 0x00 }; /* RFC 7427, A.3 ECDSA */ static const uint8_t ecdsa_sha256[] = { 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 }; static const uint8_t ecdsa_sha384[] = { 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03 }; static const uint8_t ecdsa_sha512[] = { 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 }; /* RFC 7427, A.4.3 RSASSA-PSS with SHA-256 */ static const uint8_t rsapss_sha256[] = { 0x30, 0x46, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, 0x30, 0x39, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x20, 0xa3, 0x03, 0x02, 0x01, 0x01 }; /* RSASSA-PSS SHA-384 */ static const uint8_t rsapss_sha384[] = { 0x30, 0x46, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x30, 0xa3, 0x03, 0x02, 0x01, 0x01 }; /* RSASSA-PSS SHA-512 */ static const uint8_t rsapss_sha512[] = { 0x30, 0x46, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x40, 0xa3, 0x03, 0x02, 0x01, 0x01 }; /* RSASSA-PSS SHA-256, no trailer */ static const uint8_t rsapss_sha256nt[] = { 0x30, 0x41, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x20 }; /* RSASSA-PSS SHA-384, no trailer */ static const uint8_t rsapss_sha384nt[] = { 0x30, 0x41, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x30 }; /* RSASSA-PSS SHA-512, no trailer */ static const uint8_t rsapss_sha512nt[] = { 0x30, 0x41, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a, 0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x40 }; #define FLAG_RSA_PSS 0x00001 int force_rsa_pss = 0; /* XXX move to API */ static const struct { int sc_keytype; const EVP_MD *(*sc_md)(void); uint8_t sc_len; const uint8_t *sc_oid; uint32_t sc_flags; } schemes[] = { { EVP_PKEY_RSA, EVP_sha256, sizeof(sha256WithRSA), sha256WithRSA, 0 }, { EVP_PKEY_RSA, EVP_sha384, sizeof(sha384WithRSA), sha384WithRSA, 0 }, { EVP_PKEY_RSA, EVP_sha512, sizeof(sha512WithRSA), sha512WithRSA, 0 }, { EVP_PKEY_EC, EVP_sha256, sizeof(ecdsa_sha256), ecdsa_sha256, 0 }, { EVP_PKEY_EC, EVP_sha384, sizeof(ecdsa_sha384), ecdsa_sha384, 0 }, { EVP_PKEY_EC, EVP_sha512, sizeof(ecdsa_sha512), ecdsa_sha512, 0 }, { EVP_PKEY_RSA, EVP_sha256, sizeof(rsapss_sha256), rsapss_sha256, FLAG_RSA_PSS }, { EVP_PKEY_RSA, EVP_sha384, sizeof(rsapss_sha384), rsapss_sha384, FLAG_RSA_PSS }, { EVP_PKEY_RSA, EVP_sha512, sizeof(rsapss_sha512), rsapss_sha512, FLAG_RSA_PSS }, { EVP_PKEY_RSA, EVP_sha256, sizeof(rsapss_sha256nt), rsapss_sha256nt, FLAG_RSA_PSS }, { EVP_PKEY_RSA, EVP_sha384, sizeof(rsapss_sha384nt), rsapss_sha384nt, FLAG_RSA_PSS }, { EVP_PKEY_RSA, EVP_sha512, sizeof(rsapss_sha512nt), rsapss_sha512nt, FLAG_RSA_PSS }, }; int _dsa_verify_init(struct iked_dsa *, const uint8_t *, size_t); int _dsa_verify_prepare(struct iked_dsa *, uint8_t **, size_t *, uint8_t **); int _dsa_sign_encode(struct iked_dsa *, uint8_t *, size_t, size_t *); int _dsa_sign_ecdsa(struct iked_dsa *, uint8_t *, size_t); struct iked_hash * hash_new(uint8_t type, uint16_t id) { struct iked_hash *hash; const EVP_MD *md = NULL; int length = 0, fixedkey = 0, trunc = 0, isaead = 0; switch (type) { case IKEV2_XFORMTYPE_PRF: switch (id) { case IKEV2_XFORMPRF_HMAC_MD5: md = EVP_md5(); length = MD5_DIGEST_LENGTH; break; case IKEV2_XFORMPRF_HMAC_SHA1: md = EVP_sha1(); length = SHA_DIGEST_LENGTH; break; case IKEV2_XFORMPRF_HMAC_SHA2_256: md = EVP_sha256(); length = SHA256_DIGEST_LENGTH; break; case IKEV2_XFORMPRF_HMAC_SHA2_384: md = EVP_sha384(); length = SHA384_DIGEST_LENGTH; break; case IKEV2_XFORMPRF_HMAC_SHA2_512: md = EVP_sha512(); length = SHA512_DIGEST_LENGTH; break; case IKEV2_XFORMPRF_AES128_XCBC: fixedkey = 128 / 8; length = fixedkey; /* FALLTHROUGH */ case IKEV2_XFORMPRF_HMAC_TIGER: case IKEV2_XFORMPRF_AES128_CMAC: default: log_debug("%s: prf %s not supported", __func__, print_map(id, ikev2_xformprf_map)); break; } break; case IKEV2_XFORMTYPE_INTEGR: switch (id) { case IKEV2_XFORMAUTH_HMAC_MD5_96: md = EVP_md5(); length = MD5_DIGEST_LENGTH; trunc = 12; break; case IKEV2_XFORMAUTH_HMAC_SHA1_96: md = EVP_sha1(); length = SHA_DIGEST_LENGTH; trunc = 12; break; case IKEV2_XFORMAUTH_HMAC_SHA2_256_128: md = EVP_sha256(); length = SHA256_DIGEST_LENGTH; trunc = 16; break; case IKEV2_XFORMAUTH_HMAC_SHA2_384_192: md = EVP_sha384(); length = SHA384_DIGEST_LENGTH; trunc = 24; break; case IKEV2_XFORMAUTH_HMAC_SHA2_512_256: md = EVP_sha512(); length = SHA512_DIGEST_LENGTH; trunc = 32; break; case IKEV2_XFORMAUTH_AES_GCM_12: length = 12; isaead = 1; break; case IKEV2_XFORMAUTH_AES_GCM_16: length = 16; isaead = 1; break; case IKEV2_XFORMAUTH_NONE: case IKEV2_XFORMAUTH_DES_MAC: case IKEV2_XFORMAUTH_KPDK_MD5: case IKEV2_XFORMAUTH_AES_XCBC_96: case IKEV2_XFORMAUTH_HMAC_MD5_128: case IKEV2_XFORMAUTH_HMAC_SHA1_160: case IKEV2_XFORMAUTH_AES_CMAC_96: case IKEV2_XFORMAUTH_AES_128_GMAC: case IKEV2_XFORMAUTH_AES_192_GMAC: case IKEV2_XFORMAUTH_AES_256_GMAC: default: log_debug("%s: auth %s not supported", __func__, print_map(id, ikev2_xformauth_map)); break; } break; default: log_debug("%s: hash type %s not supported", __func__, print_map(id, ikev2_xformtype_map)); break; } if (!isaead && md == NULL) return (NULL); if ((hash = calloc(1, sizeof(*hash))) == NULL) { log_debug("%s: alloc hash", __func__); return (NULL); } hash->hash_type = type; hash->hash_id = id; hash->hash_priv = md; hash->hash_ctx = NULL; hash->hash_trunc = trunc; hash->hash_length = length; hash->hash_fixedkey = fixedkey; hash->hash_isaead = isaead; if (isaead) return (hash); hash->hash_ctx = HMAC_CTX_new(); if (hash->hash_ctx == NULL) { log_debug("%s: alloc hash ctx", __func__); hash_free(hash); return (NULL); } return (hash); } struct ibuf * hash_setkey(struct iked_hash *hash, void *key, size_t keylen) { ibuf_free(hash->hash_key); if ((hash->hash_key = ibuf_new(key, keylen)) == NULL) { log_debug("%s: alloc hash key", __func__); return (NULL); } return (hash->hash_key); } void hash_free(struct iked_hash *hash) { if (hash == NULL) return; HMAC_CTX_free(hash->hash_ctx); ibuf_free(hash->hash_key); free(hash); } void hash_init(struct iked_hash *hash) { HMAC_Init_ex(hash->hash_ctx, ibuf_data(hash->hash_key), ibuf_size(hash->hash_key), hash->hash_priv, NULL); } void hash_update(struct iked_hash *hash, void *buf, size_t len) { HMAC_Update(hash->hash_ctx, buf, len); } void hash_final(struct iked_hash *hash, void *buf, size_t *len) { unsigned int length = 0; HMAC_Final(hash->hash_ctx, buf, &length); *len = (size_t)length; /* Truncate the result if required by the alg */ if (hash->hash_trunc && *len > hash->hash_trunc) *len = hash->hash_trunc; } size_t hash_length(struct iked_hash *hash) { if (hash->hash_trunc) return (hash->hash_trunc); return (hash->hash_length); } size_t hash_keylength(struct iked_hash *hash) { return (hash->hash_length); } struct iked_cipher * cipher_new(uint8_t type, uint16_t id, uint16_t id_length) { struct iked_cipher *encr; const EVP_CIPHER *cipher = NULL; int length = 0, fixedkey = 0, ivlength = 0; int saltlength = 0, authid = 0; switch (type) { case IKEV2_XFORMTYPE_ENCR: switch (id) { case IKEV2_XFORMENCR_3DES: cipher = EVP_des_ede3_cbc(); length = EVP_CIPHER_block_size(cipher); fixedkey = EVP_CIPHER_key_length(cipher); ivlength = EVP_CIPHER_iv_length(cipher); break; case IKEV2_XFORMENCR_AES_CBC: switch (id_length) { case 128: cipher = EVP_aes_128_cbc(); break; case 192: cipher = EVP_aes_192_cbc(); break; case 256: cipher = EVP_aes_256_cbc(); break; default: log_debug("%s: invalid key length %d" " for cipher %s", __func__, id_length, print_map(id, ikev2_xformencr_map)); break; } if (cipher == NULL) break; length = EVP_CIPHER_block_size(cipher); ivlength = EVP_CIPHER_iv_length(cipher); fixedkey = EVP_CIPHER_key_length(cipher); break; case IKEV2_XFORMENCR_AES_GCM_16: case IKEV2_XFORMENCR_AES_GCM_12: switch (id_length) { case 128: cipher = EVP_aes_128_gcm(); break; case 256: cipher = EVP_aes_256_gcm(); break; default: log_debug("%s: invalid key length %d" " for cipher %s", __func__, id_length, print_map(id, ikev2_xformencr_map)); break; } if (cipher == NULL) break; switch(id) { case IKEV2_XFORMENCR_AES_GCM_16: authid = IKEV2_XFORMAUTH_AES_GCM_16; break; case IKEV2_XFORMENCR_AES_GCM_12: authid = IKEV2_XFORMAUTH_AES_GCM_12; break; } length = EVP_CIPHER_block_size(cipher); ivlength = 8; saltlength = 4; fixedkey = EVP_CIPHER_key_length(cipher) + saltlength; break; case IKEV2_XFORMENCR_DES_IV64: case IKEV2_XFORMENCR_DES: case IKEV2_XFORMENCR_RC5: case IKEV2_XFORMENCR_IDEA: case IKEV2_XFORMENCR_CAST: case IKEV2_XFORMENCR_BLOWFISH: case IKEV2_XFORMENCR_3IDEA: case IKEV2_XFORMENCR_DES_IV32: case IKEV2_XFORMENCR_NULL: case IKEV2_XFORMENCR_AES_CTR: /* FALLTHROUGH */ default: log_debug("%s: cipher %s not supported", __func__, print_map(id, ikev2_xformencr_map)); cipher = NULL; break; } break; default: log_debug("%s: cipher type %s not supported", __func__, print_map(id, ikev2_xformtype_map)); break; } if (cipher == NULL) return (NULL); if ((encr = calloc(1, sizeof(*encr))) == NULL) { log_debug("%s: alloc cipher", __func__); return (NULL); } encr->encr_id = id; encr->encr_priv = cipher; encr->encr_ctx = NULL; encr->encr_length = length; encr->encr_fixedkey = fixedkey; encr->encr_ivlength = ivlength ? ivlength : length; encr->encr_saltlength = saltlength; encr->encr_authid = authid; encr->encr_ctx = EVP_CIPHER_CTX_new(); if (encr->encr_ctx == NULL) { log_debug("%s: alloc cipher ctx", __func__); cipher_free(encr); return (NULL); } return (encr); } struct ibuf * cipher_setkey(struct iked_cipher *encr, const void *key, size_t keylen) { ibuf_free(encr->encr_key); if ((encr->encr_key = ibuf_new(key, keylen)) == NULL) { log_debug("%s: alloc cipher key", __func__); return (NULL); } return (encr->encr_key); } struct ibuf * cipher_setiv(struct iked_cipher *encr, const void *iv, size_t len) { ibuf_free(encr->encr_iv); encr->encr_iv = NULL; if (iv != NULL) { if (len < encr->encr_ivlength) { log_debug("%s: invalid IV length %zu", __func__, len); return (NULL); } encr->encr_iv = ibuf_new(iv, encr->encr_ivlength); } else { switch (encr->encr_id) { case IKEV2_XFORMENCR_AES_GCM_16: case IKEV2_XFORMENCR_AES_GCM_12: if (encr->encr_ivlength != sizeof(encr->encr_civ)) { log_info("%s: ivlen does not match %zu != %zu", __func__, encr->encr_ivlength, sizeof(encr->encr_civ)); return (NULL); } encr->encr_iv = ibuf_new(&encr->encr_civ, sizeof(encr->encr_civ)); encr->encr_civ++; break; default: /* Get new random IV */ encr->encr_iv = ibuf_random(encr->encr_ivlength); } } if (encr->encr_iv == NULL) { log_debug("%s: failed to set IV", __func__); return (NULL); } return (encr->encr_iv); } int cipher_settag(struct iked_cipher *encr, uint8_t *data, size_t len) { return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx, EVP_CTRL_GCM_SET_TAG, len, data) != 1); } int cipher_gettag(struct iked_cipher *encr, uint8_t *data, size_t len) { return (EVP_CIPHER_CTX_ctrl(encr->encr_ctx, EVP_CTRL_GCM_GET_TAG, len, data) != 1); } void cipher_free(struct iked_cipher *encr) { if (encr == NULL) return; EVP_CIPHER_CTX_free(encr->encr_ctx); ibuf_free(encr->encr_iv); ibuf_free(encr->encr_key); free(encr); } int cipher_init(struct iked_cipher *encr, int enc) { struct ibuf *nonce = NULL; int ret = -1; if (EVP_CipherInit_ex(encr->encr_ctx, encr->encr_priv, NULL, NULL, NULL, enc) != 1) return (-1); if (encr->encr_saltlength > 0) { /* For AEADs the nonce is salt + IV (see RFC5282) */ nonce = ibuf_new(ibuf_seek(encr->encr_key, ibuf_size(encr->encr_key) - encr->encr_saltlength, encr->encr_saltlength), encr->encr_saltlength); if (nonce == NULL) return (-1); if (ibuf_add_ibuf(nonce, encr->encr_iv) != 0) goto done; if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL, ibuf_data(encr->encr_key), ibuf_data(nonce), enc) != 1) goto done; } else if (EVP_CipherInit_ex(encr->encr_ctx, NULL, NULL, ibuf_data(encr->encr_key), ibuf_data(encr->encr_iv), enc) != 1) return (-1); EVP_CIPHER_CTX_set_padding(encr->encr_ctx, 0); ret = 0; done: ibuf_free(nonce); return (ret); } int cipher_init_encrypt(struct iked_cipher *encr) { return (cipher_init(encr, 1)); } int cipher_init_decrypt(struct iked_cipher *encr) { return (cipher_init(encr, 0)); } void cipher_aad(struct iked_cipher *encr, const void *in, size_t inlen, size_t *outlen) { int olen = 0; if (EVP_CipherUpdate(encr->encr_ctx, NULL, &olen, in, inlen) != 1) { ca_sslerror(__func__); *outlen = 0; return; } *outlen = (size_t)olen; } int cipher_update(struct iked_cipher *encr, const void *in, size_t inlen, void *out, size_t *outlen) { int olen; olen = 0; if (EVP_CipherUpdate(encr->encr_ctx, out, &olen, in, inlen) != 1) { ca_sslerror(__func__); *outlen = 0; return (-1); } *outlen = (size_t)olen; return (0); } int cipher_final(struct iked_cipher *encr) { int olen; /* * We always have EVP_CIPH_NO_PADDING set. This means arg * out is not used and olen should always be 0. */ if (EVP_CipherFinal_ex(encr->encr_ctx, NULL, &olen) != 1) { ca_sslerror(__func__); return (-1); } return (0); } size_t cipher_length(struct iked_cipher *encr) { return (encr->encr_length); } size_t cipher_keylength(struct iked_cipher *encr) { if (encr->encr_fixedkey) return (encr->encr_fixedkey); /* Might return zero */ return (ibuf_length(encr->encr_key)); } size_t cipher_ivlength(struct iked_cipher *encr) { return (encr->encr_ivlength); } size_t cipher_outlength(struct iked_cipher *encr, size_t inlen) { return (roundup(inlen, encr->encr_length)); } struct iked_dsa * dsa_new(uint8_t id, struct iked_hash *prf, int sign) { struct iked_dsa *dsap = NULL, dsa; bzero(&dsa, sizeof(dsa)); switch (id) { case IKEV2_AUTH_SIG: if (sign) dsa.dsa_priv = EVP_sha256(); /* XXX should be passed */ else dsa.dsa_priv = NULL; /* set later by dsa_init() */ break; case IKEV2_AUTH_RSA_SIG: /* RFC5996 says we SHOULD use SHA1 here */ dsa.dsa_priv = EVP_sha1(); break; case IKEV2_AUTH_SHARED_KEY_MIC: if (prf == NULL || prf->hash_priv == NULL) fatalx("dsa_new: invalid PRF"); dsa.dsa_priv = prf->hash_priv; dsa.dsa_hmac = 1; break; case IKEV2_AUTH_DSS_SIG: dsa.dsa_priv = EVP_sha1(); break; case IKEV2_AUTH_ECDSA_256: dsa.dsa_priv = EVP_sha256(); break; case IKEV2_AUTH_ECDSA_384: dsa.dsa_priv = EVP_sha384(); break; case IKEV2_AUTH_ECDSA_521: dsa.dsa_priv = EVP_sha512(); break; default: log_debug("%s: auth method %s not supported", __func__, print_map(id, ikev2_auth_map)); break; } if ((dsap = calloc(1, sizeof(*dsap))) == NULL) { log_debug("%s: alloc dsa ctx", __func__); return (NULL); } memcpy(dsap, &dsa, sizeof(*dsap)); dsap->dsa_method = id; dsap->dsa_sign = sign; if (dsap->dsa_hmac) { if ((dsap->dsa_ctx = HMAC_CTX_new()) == NULL) { log_debug("%s: alloc hash ctx", __func__); dsa_free(dsap); return (NULL); } } else { if ((dsap->dsa_ctx = EVP_MD_CTX_create()) == NULL) { log_debug("%s: alloc digest ctx", __func__); dsa_free(dsap); return (NULL); } } return (dsap); } struct iked_dsa * dsa_sign_new(uint8_t id, struct iked_hash *prf) { return (dsa_new(id, prf, 1)); } struct iked_dsa * dsa_verify_new(uint8_t id, struct iked_hash *prf) { return (dsa_new(id, prf, 0)); } void dsa_free(struct iked_dsa *dsa) { if (dsa == NULL) return; if (dsa->dsa_hmac) { HMAC_CTX_free((HMAC_CTX *)dsa->dsa_ctx); } else { EVP_MD_CTX_free((EVP_MD_CTX *)dsa->dsa_ctx); EVP_PKEY_free(dsa->dsa_key); } ibuf_free(dsa->dsa_keydata); free(dsa); } struct ibuf * dsa_setkey(struct iked_dsa *dsa, void *key, size_t keylen, uint8_t type) { BIO *rawcert = NULL; X509 *cert = NULL; RSA *rsa = NULL; EC_KEY *ec = NULL; EVP_PKEY *pkey = NULL; ibuf_free(dsa->dsa_keydata); if ((dsa->dsa_keydata = ibuf_new(key, keylen)) == NULL) { log_debug("%s: alloc signature key", __func__); return (NULL); } if ((rawcert = BIO_new_mem_buf(key, keylen)) == NULL) goto err; switch (type) { case IKEV2_CERT_X509_CERT: if ((cert = d2i_X509_bio(rawcert, NULL)) == NULL) goto sslerr; if ((pkey = X509_get_pubkey(cert)) == NULL) goto sslerr; dsa->dsa_key = pkey; break; case IKEV2_CERT_RSA_KEY: if (dsa->dsa_sign) { if ((rsa = d2i_RSAPrivateKey_bio(rawcert, NULL)) == NULL) goto sslerr; } else { if ((rsa = d2i_RSAPublicKey_bio(rawcert, NULL)) == NULL) goto sslerr; } if ((pkey = EVP_PKEY_new()) == NULL) goto sslerr; if (!EVP_PKEY_set1_RSA(pkey, rsa)) goto sslerr; RSA_free(rsa); /* pkey now has the reference */ dsa->dsa_key = pkey; break; case IKEV2_CERT_ECDSA: if (dsa->dsa_sign) { if ((ec = d2i_ECPrivateKey_bio(rawcert, NULL)) == NULL) goto sslerr; } else { if ((ec = d2i_EC_PUBKEY_bio(rawcert, NULL)) == NULL) goto sslerr; } if ((pkey = EVP_PKEY_new()) == NULL) goto sslerr; if (!EVP_PKEY_set1_EC_KEY(pkey, ec)) goto sslerr; EC_KEY_free(ec); /* pkey now has the reference */ dsa->dsa_key = pkey; break; default: if (dsa->dsa_hmac) break; log_debug("%s: unsupported key type", __func__); goto err; } X509_free(cert); BIO_free(rawcert); /* temporary for parsing */ return (dsa->dsa_keydata); sslerr: ca_sslerror(__func__); err: log_debug("%s: error", __func__); RSA_free(rsa); EC_KEY_free(ec); EVP_PKEY_free(pkey); X509_free(cert); BIO_free(rawcert); ibuf_free(dsa->dsa_keydata); dsa->dsa_keydata = NULL; return (NULL); } int _dsa_verify_init(struct iked_dsa *dsa, const uint8_t *sig, size_t len) { uint8_t oidlen; size_t i; int keytype; if (dsa->dsa_priv != NULL) return (0); /* * For IKEV2_AUTH_SIG the oid of the authentication signature * is encoded in the first bytes of the auth message. */ if (dsa->dsa_method != IKEV2_AUTH_SIG) { log_debug("%s: dsa_priv not set for %s", __func__, print_map(dsa->dsa_method, ikev2_auth_map)); return (-1); } if (dsa->dsa_key == NULL) { log_debug("%s: dsa_key not set for %s", __func__, print_map(dsa->dsa_method, ikev2_auth_map)); return (-1); } keytype = EVP_PKEY_type(EVP_PKEY_id(((EVP_PKEY *)dsa->dsa_key))); if (sig == NULL) { log_debug("%s: signature missing", __func__); return (-1); } if (len < sizeof(oidlen)) { log_debug("%s: signature (%zu) too small for oid length", __func__, len); return (-1); } memcpy(&oidlen, sig, sizeof(oidlen)); if (len < (size_t)oidlen + sizeof(oidlen)) { log_debug("%s: signature (%zu) too small for oid (%u)", __func__, len, oidlen); return (-1); } for (i = 0; i < nitems(schemes); i++) { if (keytype == schemes[i].sc_keytype && oidlen == schemes[i].sc_len && memcmp(sig + 1, schemes[i].sc_oid, schemes[i].sc_len) == 0) { dsa->dsa_priv = (*schemes[i].sc_md)(); dsa->dsa_flags = schemes[i].sc_flags; log_debug("%s: signature scheme %zd selected", __func__, i); return (0); } } log_debug("%s: unsupported signature (%d)", __func__, oidlen); return (-1); } int dsa_init(struct iked_dsa *dsa, const void *buf, size_t len) { int ret; EVP_PKEY_CTX *pctx = NULL; if (dsa->dsa_hmac) { if (!HMAC_Init_ex(dsa->dsa_ctx, ibuf_data(dsa->dsa_keydata), ibuf_size(dsa->dsa_keydata), dsa->dsa_priv, NULL)) return (-1); return (0); } if (dsa->dsa_sign) { if (force_rsa_pss && EVP_PKEY_base_id(dsa->dsa_key) == EVP_PKEY_RSA) dsa->dsa_flags = FLAG_RSA_PSS; ret = EVP_DigestSignInit(dsa->dsa_ctx, &pctx, dsa->dsa_priv, NULL, dsa->dsa_key); } else { /* sets dsa_priv, dsa_flags */ if ((ret = _dsa_verify_init(dsa, buf, len)) != 0) return (ret); ret = EVP_DigestVerifyInit(dsa->dsa_ctx, &pctx, dsa->dsa_priv, NULL, dsa->dsa_key); } if (ret == 1 && dsa->dsa_flags == FLAG_RSA_PSS) { if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) <= 0 || EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, -1) <= 0) return (-1); } if (_dsa_sign_encode(dsa, NULL, 0, NULL) < 0) return (-1); return (ret == 1 ? 0 : -1); } int dsa_update(struct iked_dsa *dsa, const void *buf, size_t len) { int ret; if (dsa->dsa_hmac) ret = HMAC_Update(dsa->dsa_ctx, buf, len); else if (dsa->dsa_sign) ret = EVP_DigestSignUpdate(dsa->dsa_ctx, buf, len); else ret = EVP_DigestVerifyUpdate(dsa->dsa_ctx, buf, len); return (ret == 1 ? 0 : -1); } /* Prefix signature hash with encoded type */ int _dsa_sign_encode(struct iked_dsa *dsa, uint8_t *ptr, size_t len, size_t *offp) { int keytype; size_t i, need; if (offp) *offp = 0; if (dsa->dsa_method != IKEV2_AUTH_SIG) return (0); if (dsa->dsa_key == NULL) return (-1); keytype = EVP_PKEY_type(EVP_PKEY_id(((EVP_PKEY *)dsa->dsa_key))); for (i = 0; i < nitems(schemes); i++) { /* XXX should avoid calling sc_md() each time... */ if (keytype == schemes[i].sc_keytype && dsa->dsa_flags == schemes[i].sc_flags && (dsa->dsa_priv == (*schemes[i].sc_md)())) break; } if (i >= nitems(schemes)) return (-1); log_debug("%s: signature scheme %zd selected", __func__, i); need = sizeof(ptr[0]) + schemes[i].sc_len; if (ptr) { if (len < need) return (-1); ptr[0] = schemes[i].sc_len; memcpy(ptr + sizeof(ptr[0]), schemes[i].sc_oid, schemes[i].sc_len); } if (offp) *offp = need; return (0); } /* Export size of encoded signature hash type */ size_t dsa_prefix(struct iked_dsa *dsa) { size_t off = 0; if (_dsa_sign_encode(dsa, NULL, 0, &off) < 0) fatal("dsa_prefix: internal error"); return off; } size_t dsa_length(struct iked_dsa *dsa) { if (dsa->dsa_hmac) return (EVP_MD_size(dsa->dsa_priv)); switch (dsa->dsa_method) { case IKEV2_AUTH_ECDSA_256: case IKEV2_AUTH_ECDSA_384: case IKEV2_AUTH_ECDSA_521: /* size of concat(r|s) */ return (2 * ((EVP_PKEY_bits(dsa->dsa_key) + 7) / 8)); } return (dsa_prefix(dsa) + EVP_PKEY_size(dsa->dsa_key)); } int _dsa_sign_ecdsa(struct iked_dsa *dsa, uint8_t *ptr, size_t len) { ECDSA_SIG *obj = NULL; uint8_t *tmp = NULL; const uint8_t *p; size_t tmplen; int ret = -1; int bnlen, off; const BIGNUM *r, *s; if (len % 2) goto done; /* must be even */ bnlen = len/2; /* * (a) create DER signature into 'tmp' buffer * (b) convert buffer to ECDSA_SIG object * (c) concatenate the padded r|s BIGNUMS into 'ptr' */ if (EVP_DigestSignFinal(dsa->dsa_ctx, NULL, &tmplen) != 1) goto done; if ((tmp = calloc(1, tmplen)) == NULL) goto done; if (EVP_DigestSignFinal(dsa->dsa_ctx, tmp, &tmplen) != 1) goto done; p = tmp; if ((obj = d2i_ECDSA_SIG(NULL, &p, tmplen)) == NULL) goto done; ECDSA_SIG_get0(obj, &r, &s); if (BN_num_bytes(r) > bnlen || BN_num_bytes(s) > bnlen) goto done; memset(ptr, 0, len); off = bnlen - BN_num_bytes(r); BN_bn2bin(r, ptr + off); off = 2 * bnlen - BN_num_bytes(s); BN_bn2bin(s, ptr + off); ret = 0; done: free(tmp); ECDSA_SIG_free(obj); return (ret); } ssize_t dsa_sign_final(struct iked_dsa *dsa, void *buf, size_t len) { unsigned int hmaclen; size_t off = 0; uint8_t *ptr = buf; if (len < dsa_length(dsa)) return (-1); if (dsa->dsa_hmac) { if (!HMAC_Final(dsa->dsa_ctx, buf, &hmaclen)) return (-1); if (hmaclen > INT_MAX) return (-1); return (ssize_t)hmaclen; } else { switch (dsa->dsa_method) { case IKEV2_AUTH_ECDSA_256: case IKEV2_AUTH_ECDSA_384: case IKEV2_AUTH_ECDSA_521: if (_dsa_sign_ecdsa(dsa, buf, len) < 0) return (-1); return (len); default: if (_dsa_sign_encode(dsa, ptr, len, &off) < 0) return (-1); if (off > len) return (-1); len -= off; ptr += off; if (EVP_DigestSignFinal(dsa->dsa_ctx, ptr, &len) != 1) return (-1); return (len + off); } } return (-1); } int _dsa_verify_prepare(struct iked_dsa *dsa, uint8_t **sigp, size_t *lenp, uint8_t **freemep) { ECDSA_SIG *obj = NULL; uint8_t *ptr = NULL; size_t bnlen, off; ssize_t len; int ret = -1; BIGNUM *r = NULL, *s = NULL; *freemep = NULL; /* don't return garbage in case of an error */ switch (dsa->dsa_method) { case IKEV2_AUTH_SIG: /* * The first byte of the signature encodes the OID * prefix length which we need to skip. */ off = (*sigp)[0] + 1; *sigp = *sigp + off; *lenp = *lenp - off; *freemep = NULL; ret = 0; break; case IKEV2_AUTH_ECDSA_256: case IKEV2_AUTH_ECDSA_384: case IKEV2_AUTH_ECDSA_521: /* * sigp points to concatenation r|s, while EVP_VerifyFinal() * expects the signature as a DER-encoded blob (of the two * values), so we need to convert the signature in a new * buffer (we cannot override the given buffer) and the caller * has to free this buffer ('freeme'). */ if (*lenp < 64 || *lenp > 132 || *lenp % 2) goto done; bnlen = (*lenp)/2; /* sigp points to concatenation: r|s */ if ((obj = ECDSA_SIG_new()) == NULL || (r = BN_bin2bn(*sigp, bnlen, NULL)) == NULL || (s = BN_bin2bn(*sigp+bnlen, bnlen, NULL)) == NULL || ECDSA_SIG_set0(obj, r, s) == 0 || (len = i2d_ECDSA_SIG(obj, &ptr)) <= 0) goto done; r = s = NULL; *lenp = len; *sigp = ptr; *freemep = ptr; ptr = NULL; ret = 0; break; default: return (0); } done: BN_clear_free(r); BN_clear_free(s); free(ptr); ECDSA_SIG_free(obj); return (ret); } ssize_t dsa_verify_final(struct iked_dsa *dsa, void *buf, size_t len) { uint8_t sig[EVP_MAX_MD_SIZE]; uint8_t *ptr = buf, *freeme = NULL; unsigned int siglen = sizeof(sig); if (dsa->dsa_hmac) { if (!HMAC_Final(dsa->dsa_ctx, sig, &siglen)) return (-1); if (siglen != len || memcmp(buf, sig, siglen) != 0) return (-1); } else { if (_dsa_verify_prepare(dsa, &ptr, &len, &freeme) < 0) return (-1); if (EVP_DigestVerifyFinal(dsa->dsa_ctx, ptr, len) != 1) { OPENSSL_free(freeme); ca_sslerror(__func__); return (-1); } OPENSSL_free(freeme); } return (0); } openiked-7.4/iked/crypto_api.h000066400000000000000000000023641477554731100164430ustar00rootroot00000000000000/* $OpenBSD: crypto_api.h,v 1.1 2021/05/28 18:01:39 tobhe Exp $ */ /* * Assembled from generated headers and source files by Markus Friedl. * Placed in the public domain. */ #ifndef crypto_api_h #define crypto_api_h #include #include typedef int8_t crypto_int8; typedef uint8_t crypto_uint8; typedef int16_t crypto_int16; typedef uint16_t crypto_uint16; typedef int32_t crypto_int32; typedef uint32_t crypto_uint32; typedef uint64_t crypto_uint64; #define randombytes(buf, buf_len) arc4random_buf((buf), (buf_len)) #define small_random32() arc4random() #define crypto_hash_sha512_BYTES 64U int crypto_hash_sha512(unsigned char *, const unsigned char *, unsigned long long); int crypto_verify_32(const unsigned char *, const unsigned char *); #define crypto_kem_sntrup761_PUBLICKEYBYTES 1158 #define crypto_kem_sntrup761_SECRETKEYBYTES 1763 #define crypto_kem_sntrup761_CIPHERTEXTBYTES 1039 #define crypto_kem_sntrup761_BYTES 32 int crypto_kem_sntrup761_enc(unsigned char *cstr, unsigned char *k, const unsigned char *pk); int crypto_kem_sntrup761_dec(unsigned char *k, const unsigned char *cstr, const unsigned char *sk); int crypto_kem_sntrup761_keypair(unsigned char *pk, unsigned char *sk); #endif /* crypto_api_h */ openiked-7.4/iked/crypto_hash.c000066400000000000000000000007051477554731100166050ustar00rootroot00000000000000/* $OpenBSD: crypto_hash.c,v 1.1 2021/05/28 18:01:39 tobhe Exp $ */ /* * Public domain. Author: Christian Weisgerber * API compatible reimplementation of function from nacl */ #include "crypto_api.h" #include int crypto_hash_sha512(unsigned char *out, const unsigned char *in, unsigned long long inlen) { u_int mdlen; if (!EVP_Digest(in, inlen, out, &mdlen, EVP_sha512(), NULL)) return -1; return 0; } openiked-7.4/iked/dh.c000066400000000000000000000607121477554731100146610ustar00rootroot00000000000000/* $OpenBSD: dh.c,v 1.34 2025/04/09 07:10:48 bluhm Exp $ */ /* * Copyright (c) 2020-2021 Tobias Heider * Copyright (c) 2010-2014 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dh.h" #include "iked.h" #include "crypto_api.h" int dh_init(struct dh_group *); int dh_getlen(struct dh_group *); int dh_secretlen(struct dh_group *); /* MODP */ int modp_init(struct dh_group *); int modp_getlen(struct dh_group *); int modp_create_exchange(struct dh_group *, uint8_t *); int modp_create_shared(struct dh_group *, uint8_t *, uint8_t *); /* ECP */ int ec_init(struct dh_group *); int ec_getlen(struct dh_group *); int ec_secretlen(struct dh_group *); int ec_create_exchange(struct dh_group *, uint8_t *); int ec_create_shared(struct dh_group *, uint8_t *, uint8_t *); #define EC_POINT2RAW_FULL 0 #define EC_POINT2RAW_XONLY 1 int ec_point2raw(struct dh_group *, const EC_POINT *, uint8_t *, size_t, int); EC_POINT * ec_raw2point(struct dh_group *, uint8_t *, size_t); /* curve25519 */ int ec25519_init(struct dh_group *); int ec25519_getlen(struct dh_group *); int ec25519_create_exchange(struct dh_group *, uint8_t *); int ec25519_create_shared(struct dh_group *, uint8_t *, uint8_t *); #define CURVE25519_SIZE 32 /* 256 bits */ struct curve25519_key { uint8_t secret[CURVE25519_SIZE]; uint8_t public[CURVE25519_SIZE]; }; extern int crypto_scalarmult_curve25519(unsigned char a[CURVE25519_SIZE], const unsigned char b[CURVE25519_SIZE], const unsigned char c[CURVE25519_SIZE]) __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE))) __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE))) __attribute__((__bounded__(__minbytes__, 3, CURVE25519_SIZE))); /* SNTRUP761 with X25519 */ int kemsx_init(struct dh_group *); int kemsx_getlen(struct dh_group *); int kemsx_create_exchange2(struct dh_group *, struct ibuf **, struct ibuf *); int kemsx_create_shared2(struct dh_group *, struct ibuf **, struct ibuf *); struct kemsx_key { uint8_t kemkey[crypto_kem_sntrup761_BYTES]; uint8_t secret[crypto_kem_sntrup761_SECRETKEYBYTES]; uint8_t public[crypto_kem_sntrup761_PUBLICKEYBYTES]; uint8_t initiator; }; const struct group_id ike_groups[] = { { GROUP_MODP, 1, 768, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF", "02" }, { GROUP_MODP, 2, 1024, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" "FFFFFFFFFFFFFFFF", "02" }, { GROUP_MODP, 5, 1536, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" "83655D23DCA3AD961C62F356208552BB9ED529077096966D" "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", "02" }, { GROUP_MODP, 14, 2048, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" "83655D23DCA3AD961C62F356208552BB9ED529077096966D" "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" "15728E5A8AACAA68FFFFFFFFFFFFFFFF", "02" }, { GROUP_MODP, 15, 3072, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" "83655D23DCA3AD961C62F356208552BB9ED529077096966D" "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF", "02" }, { GROUP_MODP, 16, 4096, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" "83655D23DCA3AD961C62F356208552BB9ED529077096966D" "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" "FFFFFFFFFFFFFFFF", "02" }, { GROUP_MODP, 17, 6144, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" "83655D23DCA3AD961C62F356208552BB9ED529077096966D" "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" "12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF", "02" }, { GROUP_MODP, 18, 8192, "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" "83655D23DCA3AD961C62F356208552BB9ED529077096966D" "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" "60C980DD98EDD3DFFFFFFFFFFFFFFFFF", "02" }, { GROUP_ECP, 19, 256, NULL, NULL, NID_X9_62_prime256v1 }, { GROUP_ECP, 20, 384, NULL, NULL, NID_secp384r1 }, { GROUP_ECP, 21, 521, NULL, NULL, NID_secp521r1 }, { GROUP_ECP, 25, 192, NULL, NULL, NID_X9_62_prime192v1 }, { GROUP_ECP, 26, 224, NULL, NULL, NID_secp224r1 }, { GROUP_ECP, 27, 224, NULL, NULL, NID_brainpoolP224r1 }, { GROUP_ECP, 28, 256, NULL, NULL, NID_brainpoolP256r1 }, { GROUP_ECP, 29, 384, NULL, NULL, NID_brainpoolP384r1 }, { GROUP_ECP, 30, 512, NULL, NULL, NID_brainpoolP512r1 }, { GROUP_CURVE25519, 31, CURVE25519_SIZE * 8 }, /* "Private use" extensions */ /* PQC KEM */ { GROUP_SNTRUP761X25519, 1035, (MAXIMUM(crypto_kem_sntrup761_PUBLICKEYBYTES, crypto_kem_sntrup761_CIPHERTEXTBYTES) + CURVE25519_SIZE) * 8 } }; void group_init(void) { /* currently not used */ return; } void group_free(struct dh_group *group) { if (group == NULL) return; if (group->dh != NULL) DH_free(group->dh); if (group->ec != NULL) EC_KEY_free(group->ec); freezero(group->curve25519, sizeof(struct curve25519_key)); freezero(group->kemsx, sizeof(struct kemsx_key)); group->spec = NULL; free(group); } struct dh_group * group_get(uint32_t id) { const struct group_id *p; struct dh_group *group; if ((p = group_getid(id)) == NULL) return (NULL); if ((group = calloc(1, sizeof(*group))) == NULL) return (NULL); group->id = id; group->spec = p; switch (p->type) { case GROUP_MODP: group->init = modp_init; group->getlen = modp_getlen; group->exchange = modp_create_exchange; group->shared = modp_create_shared; break; case GROUP_ECP: group->init = ec_init; group->getlen = ec_getlen; group->secretlen = ec_secretlen; group->exchange = ec_create_exchange; group->shared = ec_create_shared; break; case GROUP_CURVE25519: group->init = ec25519_init; group->getlen = ec25519_getlen; group->exchange = ec25519_create_exchange; group->shared = ec25519_create_shared; break; case GROUP_SNTRUP761X25519: group->init = kemsx_init; group->getlen = kemsx_getlen; group->exchange2 = kemsx_create_exchange2; group->shared2 = kemsx_create_shared2; break; default: group_free(group); return (NULL); } if (dh_init(group) != 0) { group_free(group); return (NULL); } return (group); } const struct group_id * group_getid(uint32_t id) { const struct group_id *p = NULL; unsigned int i, items; items = sizeof(ike_groups) / sizeof(ike_groups[0]); for (i = 0; i < items; i++) { if (id == ike_groups[i].id) { p = &ike_groups[i]; break; } } return (p); } int dh_init(struct dh_group *group) { return (group->init(group)); } int dh_getlen(struct dh_group *group) { return (group->getlen(group)); } int dh_secretlen(struct dh_group *group) { if (group->secretlen) return (group->secretlen(group)); else return (group->getlen(group)); } int dh_create_exchange(struct dh_group *group, struct ibuf **bufp, struct ibuf *iexchange) { struct ibuf *buf; *bufp = NULL; if (group->exchange2) return (group->exchange2(group, bufp, iexchange)); buf = ibuf_new(NULL, dh_getlen(group)); if (buf == NULL) return -1; *bufp = buf; return (group->exchange(group, ibuf_data(buf))); } int dh_create_shared(struct dh_group *group, struct ibuf **secretp, struct ibuf *exchange) { struct ibuf *buf; *secretp = NULL; if (group->shared2) return (group->shared2(group, secretp, exchange)); if (exchange == NULL || (ssize_t)ibuf_size(exchange) != dh_getlen(group)) return -1; buf = ibuf_new(NULL, dh_secretlen(group)); if (buf == NULL) return -1; *secretp = buf; return (group->shared(group, ibuf_data(buf), ibuf_data(exchange))); } int modp_init(struct dh_group *group) { BIGNUM *g = NULL, *p = NULL; DH *dh; int ret = -1; if ((dh = DH_new()) == NULL) return (-1); if (!BN_hex2bn(&p, group->spec->prime) || !BN_hex2bn(&g, group->spec->generator) || DH_set0_pqg(dh, p, NULL, g) == 0) goto done; p = g = NULL; group->dh = dh; ret = 0; done: BN_clear_free(g); BN_clear_free(p); return (ret); } int modp_getlen(struct dh_group *group) { if (group->spec == NULL) return (0); return (roundup(group->spec->bits, 8) / 8); } int modp_create_exchange(struct dh_group *group, uint8_t *buf) { const BIGNUM *pub; DH *dh = group->dh; int len, ret; if (!DH_generate_key(dh)) return (-1); DH_get0_key(group->dh, &pub, NULL); ret = BN_bn2bin(pub, buf); if (!ret) return (-1); len = dh_getlen(group); /* add zero padding */ if (ret < len) { bcopy(buf, buf + (len - ret), ret); bzero(buf, len - ret); } return (0); } int modp_create_shared(struct dh_group *group, uint8_t *secret, uint8_t *exchange) { BIGNUM *ex; int len, ret; len = dh_getlen(group); if ((ex = BN_bin2bn(exchange, len, NULL)) == NULL) return (-1); ret = DH_compute_key(secret, ex, group->dh); BN_clear_free(ex); if (ret <= 0) return (-1); /* add zero padding */ if (ret < len) { bcopy(secret, secret + (len - ret), ret); bzero(secret, len - ret); } return (0); } int ec_init(struct dh_group *group) { if ((group->ec = EC_KEY_new_by_curve_name(group->spec->nid)) == NULL) return (-1); if (!EC_KEY_generate_key(group->ec)) return (-1); if (!EC_KEY_check_key(group->ec)) return (-1); return (0); } int ec_getlen(struct dh_group *group) { if (group->spec == NULL) return (0); /* NB: Return value will always be even */ return ((roundup(group->spec->bits, 8) * 2) / 8); } /* * Note that the shared secret only includes the x value: * * See RFC 5903, 7. ECP Key Exchange Data Formats: * The Diffie-Hellman shared secret value consists of the x value of the * Diffie-Hellman common value. * See also RFC 5903, 9. Changes from RFC 4753. */ int ec_secretlen(struct dh_group *group) { return (ec_getlen(group) / 2); } int ec_create_exchange(struct dh_group *group, uint8_t *buf) { size_t len; len = ec_getlen(group); bzero(buf, len); return (ec_point2raw(group, EC_KEY_get0_public_key(group->ec), buf, len, EC_POINT2RAW_FULL)); } int ec_create_shared(struct dh_group *group, uint8_t *secret, uint8_t *exchange) { const EC_GROUP *ecgroup = NULL; const BIGNUM *privkey; EC_KEY *exkey = NULL; EC_POINT *exchangep = NULL, *secretp = NULL; int ret = -1; if ((ecgroup = EC_KEY_get0_group(group->ec)) == NULL || (privkey = EC_KEY_get0_private_key(group->ec)) == NULL) goto done; if ((exchangep = ec_raw2point(group, exchange, ec_getlen(group))) == NULL) goto done; if ((exkey = EC_KEY_new()) == NULL) goto done; if (!EC_KEY_set_group(exkey, ecgroup)) goto done; if (!EC_KEY_set_public_key(exkey, exchangep)) goto done; /* validate exchangep */ if (!EC_KEY_check_key(exkey)) goto done; if ((secretp = EC_POINT_new(ecgroup)) == NULL) goto done; if (!EC_POINT_mul(ecgroup, secretp, NULL, exchangep, privkey, NULL)) goto done; ret = ec_point2raw(group, secretp, secret, ec_secretlen(group), EC_POINT2RAW_XONLY); done: if (exkey != NULL) EC_KEY_free(exkey); if (exchangep != NULL) EC_POINT_clear_free(exchangep); if (secretp != NULL) EC_POINT_clear_free(secretp); return (ret); } int ec_point2raw(struct dh_group *group, const EC_POINT *point, uint8_t *buf, size_t len, int mode) { const EC_GROUP *ecgroup = NULL; BN_CTX *bnctx = NULL; BIGNUM *x = NULL, *y = NULL; int ret = -1; size_t eclen, xlen, ylen; off_t xoff, yoff; if ((bnctx = BN_CTX_new()) == NULL) goto done; BN_CTX_start(bnctx); if ((x = BN_CTX_get(bnctx)) == NULL || (y = BN_CTX_get(bnctx)) == NULL) goto done; eclen = ec_getlen(group); switch (mode) { case EC_POINT2RAW_XONLY: xlen = eclen / 2; ylen = 0; break; case EC_POINT2RAW_FULL: xlen = ylen = eclen / 2; break; default: goto done; } if (len < xlen + ylen) goto done; if ((ecgroup = EC_KEY_get0_group(group->ec)) == NULL) goto done; if (!EC_POINT_get_affine_coordinates(ecgroup, point, x, y, bnctx)) goto done; xoff = xlen - BN_num_bytes(x); bzero(buf, xoff); if (!BN_bn2bin(x, buf + xoff)) goto done; if (ylen > 0) { yoff = (ylen - BN_num_bytes(y)) + xlen; bzero(buf + xlen, yoff - xlen); if (!BN_bn2bin(y, buf + yoff)) goto done; } ret = 0; done: /* Make sure to erase sensitive data */ if (x != NULL) BN_clear(x); if (y != NULL) BN_clear(y); BN_CTX_end(bnctx); BN_CTX_free(bnctx); return (ret); } EC_POINT * ec_raw2point(struct dh_group *group, uint8_t *buf, size_t len) { const EC_GROUP *ecgroup = NULL; EC_POINT *point = NULL; EC_POINT *ret = NULL; BN_CTX *bnctx = NULL; BIGNUM *x = NULL, *y = NULL; size_t eclen; size_t xlen, ylen; if ((bnctx = BN_CTX_new()) == NULL) goto done; BN_CTX_start(bnctx); if ((x = BN_CTX_get(bnctx)) == NULL || (y = BN_CTX_get(bnctx)) == NULL) goto done; eclen = ec_getlen(group); if (len < eclen) goto done; xlen = ylen = eclen / 2; if ((x = BN_bin2bn(buf, xlen, x)) == NULL || (y = BN_bin2bn(buf + xlen, ylen, y)) == NULL) goto done; if ((ecgroup = EC_KEY_get0_group(group->ec)) == NULL) goto done; if ((point = EC_POINT_new(ecgroup)) == NULL) goto done; if (!EC_POINT_set_affine_coordinates(ecgroup, point, x, y, bnctx)) goto done; /* success */ ret = point; point = NULL; /* owned by caller */ done: EC_POINT_clear_free(point); /* Make sure to erase sensitive data */ if (x != NULL) BN_clear(x); if (y != NULL) BN_clear(y); BN_CTX_end(bnctx); BN_CTX_free(bnctx); return (ret); } int ec25519_init(struct dh_group *group) { static const uint8_t basepoint[CURVE25519_SIZE] = { 9 }; struct curve25519_key *curve25519; if ((curve25519 = calloc(1, sizeof(*curve25519))) == NULL) return (-1); group->curve25519 = curve25519; arc4random_buf(curve25519->secret, CURVE25519_SIZE); crypto_scalarmult_curve25519(curve25519->public, curve25519->secret, basepoint); return (0); } int ec25519_getlen(struct dh_group *group) { if (group->spec == NULL) return (0); return (CURVE25519_SIZE); } int ec25519_create_exchange(struct dh_group *group, uint8_t *buf) { struct curve25519_key *curve25519 = group->curve25519; memcpy(buf, curve25519->public, ec25519_getlen(group)); return (0); } int ec25519_create_shared(struct dh_group *group, uint8_t *shared, uint8_t *public) { struct curve25519_key *curve25519 = group->curve25519; crypto_scalarmult_curve25519(shared, curve25519->secret, public); return (0); } /* combine sntrup761 with curve25519 */ int kemsx_init(struct dh_group *group) { /* delayed until kemsx_create_exchange2 */ return (0); } int kemsx_getlen(struct dh_group *group) { return (0); } int kemsx_create_exchange2(struct dh_group *group, struct ibuf **bufp, struct ibuf *iexchange) { struct kemsx_key *kemsx; struct curve25519_key *curve25519; struct ibuf *buf = NULL; u_char *cp, *pk; size_t have, need; if (ec25519_init(group) == -1) return (-1); if (group->curve25519 == NULL) return (-1); if ((kemsx = calloc(1, sizeof(*kemsx))) == NULL) return (-1); group->kemsx = kemsx; if (iexchange == NULL) { kemsx->initiator = 1; crypto_kem_sntrup761_keypair(kemsx->public, kemsx->secret); /* output */ need = crypto_kem_sntrup761_PUBLICKEYBYTES + CURVE25519_SIZE; buf = ibuf_new(NULL, need); if (buf == NULL) return -1; cp = ibuf_data(buf); memcpy(cp, kemsx->public, crypto_kem_sntrup761_PUBLICKEYBYTES); cp += crypto_kem_sntrup761_PUBLICKEYBYTES; } else { kemsx->initiator = 0; /* input */ have = ibuf_size(iexchange); need = crypto_kem_sntrup761_PUBLICKEYBYTES + CURVE25519_SIZE; if (have != need) return -1; /* output */ need = crypto_kem_sntrup761_CIPHERTEXTBYTES + CURVE25519_SIZE; buf = ibuf_new(NULL, need); if (buf == NULL) return -1; cp = ibuf_data(buf); pk = ibuf_data(iexchange); crypto_kem_sntrup761_enc(cp, kemsx->kemkey, pk); cp += crypto_kem_sntrup761_CIPHERTEXTBYTES; } curve25519 = group->curve25519; memcpy(cp, curve25519->public, CURVE25519_SIZE); *bufp = buf; return (0); } int kemsx_create_shared2(struct dh_group *group, struct ibuf **sharedp, struct ibuf *exchange) { struct curve25519_key *curve25519 = group->curve25519; struct kemsx_key *kemsx = group->kemsx; struct ibuf *buf = NULL; EVP_MD_CTX *ctx = NULL; uint8_t *cp; uint8_t shared[CURVE25519_SIZE]; size_t have, need; u_int len; *sharedp = NULL; if (kemsx == NULL) return (-1); if (exchange == NULL) return (-1); have = ibuf_size(exchange); cp = ibuf_data(exchange); if (kemsx->initiator) { /* input */ need = crypto_kem_sntrup761_CIPHERTEXTBYTES + CURVE25519_SIZE; if (have != need) return (-1); crypto_kem_sntrup761_dec(kemsx->kemkey, cp, kemsx->secret); cp += crypto_kem_sntrup761_CIPHERTEXTBYTES; } else { /* input, should have been checked before */ need = crypto_kem_sntrup761_PUBLICKEYBYTES + CURVE25519_SIZE; if (have != need) return (-1); cp += crypto_kem_sntrup761_PUBLICKEYBYTES; } crypto_scalarmult_curve25519(shared, curve25519->secret, cp); /* result is hash of concatenation of KEM key and DH shared secret */ len = SHA512_DIGEST_LENGTH; buf = ibuf_new(NULL, len); if (buf == NULL) return (-1); if ((ctx = EVP_MD_CTX_new()) == NULL || EVP_DigestInit_ex(ctx, EVP_sha512(), NULL) != 1 || EVP_DigestUpdate(ctx, kemsx->kemkey, sizeof(kemsx->kemkey)) != 1 || EVP_DigestUpdate(ctx, shared, sizeof(shared)) != 1 || EVP_DigestFinal_ex(ctx, ibuf_data(buf), &len) != 1) { EVP_MD_CTX_free(ctx); ibuf_free(buf); return (-1); } EVP_MD_CTX_free(ctx); *sharedp = buf; return (0); } openiked-7.4/iked/dh.h000066400000000000000000000036641477554731100146710ustar00rootroot00000000000000/* $OpenBSD: dh.h,v 1.15 2021/05/28 18:01:39 tobhe Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef DH_GROUP_H #define DH_GROUP_H enum group_type { GROUP_MODP = 0, GROUP_ECP = 1, GROUP_CURVE25519 = 2, GROUP_SNTRUP761X25519 = 3 }; struct group_id { enum group_type type; unsigned int id; int bits; char *prime; char *generator; int nid; }; struct dh_group { int id; const struct group_id *spec; void *dh; void *ec; void *curve25519; void *kemsx; int (*init)(struct dh_group *); int (*getlen)(struct dh_group *); int (*secretlen)(struct dh_group *); int (*exchange)(struct dh_group *, uint8_t *); int (*exchange2)(struct dh_group *, struct ibuf **, struct ibuf *); int (*shared)(struct dh_group *, uint8_t *, uint8_t *); int (*shared2)(struct dh_group *, struct ibuf **, struct ibuf *); }; #define DH_MAXSZ 1024 /* 8192 bits */ void group_init(void); void group_free(struct dh_group *); struct dh_group *group_get(uint32_t); const struct group_id *group_getid(uint32_t); int dh_create_exchange(struct dh_group *, struct ibuf **, struct ibuf *); int dh_create_shared(struct dh_group *, struct ibuf **, struct ibuf *); #endif /* DH_GROUP_H */ openiked-7.4/iked/eap.c000066400000000000000000000367721477554731100150440ustar00rootroot00000000000000/* $OpenBSD: eap.c,v 1.28 2024/11/21 13:26:49 claudio Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" #include "ikev2.h" #include "eap.h" int eap_message_send(struct iked *, struct iked_sa *, int, int); ssize_t eap_add_id_request(struct ibuf *); char *eap_validate_id_response(struct eap_message *); int eap_mschap(struct iked *, const struct iked_sa *, struct iked_message *, struct eap_message *); ssize_t eap_add_id_request(struct ibuf *e) { struct eap_message *eap; if ((eap = ibuf_reserve(e, sizeof(*eap))) == NULL) return (-1); eap->eap_code = EAP_CODE_REQUEST; eap->eap_id = 0; eap->eap_length = htobe16(sizeof(*eap)); eap->eap_type = EAP_TYPE_IDENTITY; return (sizeof(*eap)); } char * eap_validate_id_response(struct eap_message *eap) { size_t len; char *str; uint8_t *ptr = (uint8_t *)eap; len = betoh16(eap->eap_length) - sizeof(*eap); ptr += sizeof(*eap); if (len == 0) { if ((str = strdup("")) == NULL) { log_warn("%s: strdup failed", __func__); return (NULL); } } else if ((str = get_string(ptr, len)) == NULL) { log_info("%s: invalid identity response, length %zu", __func__, len); return (NULL); } log_debug("%s: identity '%s' length %zd", __func__, str, len); return (str); } int eap_identity_request(struct iked *env, struct iked_sa *sa) { struct ikev2_payload *pld; struct ikev2_cert *cert; struct ikev2_auth *auth; struct iked_id *id, *certid; struct ibuf *e = NULL; uint8_t firstpayload; int ret = -1; ssize_t len = 0; int i; /* Responder only */ if (sa->sa_hdr.sh_initiator) return (-1); /* Check if "ca" has done its job yet */ if (!sa->sa_localauth.id_type) return (0); /* New encrypted message buffer */ if ((e = ibuf_static()) == NULL) goto done; id = &sa->sa_rid; certid = &sa->sa_rcert; /* ID payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; firstpayload = IKEV2_PAYLOAD_IDr; if (ibuf_add_ibuf(e, id->id_buf) != 0) goto done; len = ibuf_size(id->id_buf); if ((sa->sa_statevalid & IKED_REQ_CERT) && (certid->id_type != IKEV2_CERT_NONE)) { if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_CERT) == -1) goto done; /* CERT payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((cert = ibuf_reserve(e, sizeof(*cert))) == NULL) goto done; cert->cert_type = certid->id_type; if (ibuf_add_ibuf(e, certid->id_buf) != 0) goto done; len = ibuf_size(certid->id_buf) + sizeof(*cert); for (i = 0; i < IKED_SCERT_MAX; i++) { if (sa->sa_scert[i].id_type == IKEV2_CERT_NONE) break; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_CERT) == -1) goto done; if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((cert = ibuf_reserve(e, sizeof(*cert))) == NULL) goto done; cert->cert_type = sa->sa_scert[i].id_type; if (ibuf_add_ibuf(e, sa->sa_scert[i].id_buf) != 0) goto done; len = ibuf_size(sa->sa_scert[i].id_buf) + sizeof(*cert); } } if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_AUTH) == -1) goto done; /* AUTH payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((auth = ibuf_reserve(e, sizeof(*auth))) == NULL) goto done; auth->auth_method = sa->sa_localauth.id_type; if (ibuf_add_ibuf(e, sa->sa_localauth.id_buf) != 0) goto done; len = ibuf_size(sa->sa_localauth.id_buf) + sizeof(*auth); if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_EAP) == -1) goto done; /* EAP payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((len = eap_add_id_request(e)) == -1) goto done; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) goto done; ret = ikev2_msg_send_encrypt(env, sa, &e, IKEV2_EXCHANGE_IKE_AUTH, firstpayload, 1); done: ibuf_free(e); return (ret); } int eap_challenge_request(struct iked *env, struct iked_sa *sa, int eap_id) { struct eap_message *eap; struct eap_mschap_challenge *ms; const char *name; int ret = -1; struct ibuf *e; if ((e = ibuf_static()) == NULL) return (-1); if ((eap = ibuf_reserve(e, sizeof(*eap))) == NULL) goto done; eap->eap_code = EAP_CODE_REQUEST; eap->eap_id = eap_id + 1; eap->eap_type = sa->sa_policy->pol_auth.auth_eap; switch (sa->sa_policy->pol_auth.auth_eap) { case EAP_TYPE_MSCHAP_V2: name = IKED_USER; /* XXX should be user-configurable */ eap->eap_length = htobe16(sizeof(*eap) + sizeof(*ms) + strlen(name)); if ((ms = ibuf_reserve(e, sizeof(*ms))) == NULL) return (-1); ms->msc_opcode = EAP_MSOPCODE_CHALLENGE; ms->msc_id = eap->eap_id; ms->msc_length = htobe16(sizeof(*ms) + strlen(name)); ms->msc_valuesize = sizeof(ms->msc_challenge); arc4random_buf(ms->msc_challenge, sizeof(ms->msc_challenge)); if (ibuf_add(e, name, strlen(name)) == -1) goto done; /* Store the EAP challenge value */ sa->sa_eap.id_type = eap->eap_type; if ((sa->sa_eap.id_buf = ibuf_new(ms->msc_challenge, sizeof(ms->msc_challenge))) == NULL) goto done; break; default: log_debug("%s: unsupported EAP type %s", __func__, print_map(eap->eap_type, eap_type_map)); goto done; } ret = ikev2_send_ike_e(env, sa, e, IKEV2_PAYLOAD_EAP, IKEV2_EXCHANGE_IKE_AUTH, 1); done: ibuf_free(e); return (ret); } int eap_message_send(struct iked *env, struct iked_sa *sa, int eap_code, int eap_id) { struct eap_header *resp; int ret = -1; struct ibuf *e; if ((e = ibuf_static()) == NULL) return (-1); if ((resp = ibuf_reserve(e, sizeof(*resp))) == NULL) goto done; resp->eap_code = eap_code; resp->eap_id = eap_id; resp->eap_length = htobe16(sizeof(*resp)); ret = ikev2_send_ike_e(env, sa, e, IKEV2_PAYLOAD_EAP, IKEV2_EXCHANGE_IKE_AUTH, 1); done: ibuf_free(e); return (ret); } int eap_success(struct iked *env, struct iked_sa *sa, int eap_id) { return (eap_message_send(env, sa, EAP_CODE_SUCCESS, eap_id)); } int eap_mschap_challenge(struct iked *env, struct iked_sa *sa, int eap_id, int msr_id, uint8_t *successmsg, size_t success_size) { struct ibuf *eapmsg = NULL; struct eap_message *resp; struct eap_mschap_success *mss; char *msg; int ret = -1; if ((eapmsg = ibuf_static()) == NULL) return (-1); msg = " M=Welcome"; if ((resp = ibuf_reserve(eapmsg, sizeof(*resp))) == NULL) goto done; resp->eap_code = EAP_CODE_REQUEST; resp->eap_id = eap_id + 1; resp->eap_length = htobe16(sizeof(*resp) + sizeof(*mss) + success_size + strlen(msg)); resp->eap_type = EAP_TYPE_MSCHAP_V2; if ((mss = ibuf_reserve(eapmsg, sizeof(*mss))) == NULL) goto done; mss->mss_opcode = EAP_MSOPCODE_SUCCESS; mss->mss_id = msr_id; mss->mss_length = htobe16(sizeof(*mss) + success_size + strlen(msg)); if (ibuf_add(eapmsg, successmsg, success_size) != 0) goto done; if (ibuf_add(eapmsg, msg, strlen(msg)) != 0) goto done; ret = ikev2_send_ike_e(env, sa, eapmsg, IKEV2_PAYLOAD_EAP, IKEV2_EXCHANGE_IKE_AUTH, 1); done: ibuf_free(eapmsg); return (ret); } int eap_mschap_success(struct iked *env, struct iked_sa *sa, int eap_id) { struct ibuf *eapmsg = NULL; struct eap_message *resp; struct eap_mschap *ms; int ret = -1; if ((eapmsg = ibuf_static()) == NULL) return (-1); if ((resp = ibuf_reserve(eapmsg, sizeof(*resp))) == NULL) goto done; resp->eap_code = EAP_CODE_RESPONSE; resp->eap_id = eap_id; resp->eap_length = htobe16(sizeof(*resp) + sizeof(*ms)); resp->eap_type = EAP_TYPE_MSCHAP_V2; if ((ms = ibuf_reserve(eapmsg, sizeof(*ms))) == NULL) goto done; ms->ms_opcode = EAP_MSOPCODE_SUCCESS; ret = ikev2_send_ike_e(env, sa, eapmsg, IKEV2_PAYLOAD_EAP, IKEV2_EXCHANGE_IKE_AUTH, 1); done: ibuf_free(eapmsg); return (ret); } int eap_mschap(struct iked *env, const struct iked_sa *sa, struct iked_message *msg, struct eap_message *eap) { struct eap_mschap_response *msr; struct eap_mschap_peer *msp; struct eap_mschap *ms; uint8_t *ptr; size_t len; int ret = -1; if (!sa_stateok(sa, IKEV2_STATE_EAP)) { log_debug("%s: unexpected EAP", __func__); return (0); /* ignore */ } if (sa->sa_hdr.sh_initiator) { log_debug("%s: initiator EAP not supported", __func__); return (-1); } /* Only MSCHAP-V2 */ if (eap->eap_type != EAP_TYPE_MSCHAP_V2) { log_debug("%s: unsupported type EAP-%s", __func__, print_map(eap->eap_type, eap_type_map)); return (-1); } if (betoh16(eap->eap_length) < (sizeof(*eap) + sizeof(*ms))) { log_debug("%s: short message", __func__); return (-1); } ms = (struct eap_mschap *)(eap + 1); ptr = (uint8_t *)(eap + 1); switch (ms->ms_opcode) { case EAP_MSOPCODE_RESPONSE: msr = (struct eap_mschap_response *)ms; if (betoh16(eap->eap_length) < (sizeof(*eap) + sizeof(*msr))) { log_debug("%s: short response", __func__); return (-1); } ptr += sizeof(*msr); len = betoh16(eap->eap_length) - sizeof(*eap) - sizeof(*msr); if (len != 0) msg->msg_parent->msg_eap.eam_user = get_string(ptr, len); msg->msg_parent->msg_eap.eam_msrid = msr->msr_id; msp = &msr->msr_response.resp_peer; memcpy(msg->msg_parent->msg_eap.eam_challenge, msp->msp_challenge, EAP_MSCHAP_CHALLENGE_SZ); memcpy(msg->msg_parent->msg_eap.eam_ntresponse, msp->msp_ntresponse, EAP_MSCHAP_NTRESPONSE_SZ); msg->msg_parent->msg_eap.eam_state = EAP_STATE_MSCHAPV2_CHALLENGE; return (0); case EAP_MSOPCODE_SUCCESS: msg->msg_parent->msg_eap.eam_state = EAP_STATE_MSCHAPV2_SUCCESS; return (0); case EAP_MSOPCODE_FAILURE: case EAP_MSOPCODE_CHANGE_PASSWORD: case EAP_MSOPCODE_CHALLENGE: default: log_debug("%s: EAP-%s unsupported " "responder operation %s", __func__, print_map(eap->eap_type, eap_type_map), print_map(ms->ms_opcode, eap_msopcode_map)); return (-1); } return (ret); } int eap_parse(struct iked *env, const struct iked_sa *sa, struct iked_message *msg, void *data, int response) { struct eap_header *hdr = data; struct eap_message *eap = data; size_t len; uint8_t *ptr; struct eap_mschap *ms; struct eap_mschap_challenge *msc; struct eap_mschap_response *msr; struct eap_mschap_success *mss; struct eap_mschap_failure *msf; char *str; /* length is already verified by the caller against sizeof(eap) */ len = betoh16(hdr->eap_length); if (len < sizeof(*eap)) goto fail; ptr = (uint8_t *)(eap + 1); len -= sizeof(*eap); switch (hdr->eap_code) { case EAP_CODE_REQUEST: case EAP_CODE_RESPONSE: break; case EAP_CODE_SUCCESS: return (0); case EAP_CODE_FAILURE: if (response) return (0); return (-1); default: log_debug("%s: unsupported EAP code %s", __func__, print_map(hdr->eap_code, eap_code_map)); return (-1); } msg->msg_parent->msg_eap.eam_id = hdr->eap_id; msg->msg_parent->msg_eap.eam_type = eap->eap_type; switch (eap->eap_type) { case EAP_TYPE_IDENTITY: if (eap->eap_code == EAP_CODE_REQUEST) break; if ((str = eap_validate_id_response(eap)) == NULL) return (-1); if (response) { free(str); break; } if (sa->sa_eapid != NULL) { free(str); log_debug("%s: EAP identity already known", __func__); return (0); } msg->msg_parent->msg_eap.eam_response = 1; msg->msg_parent->msg_eap.eam_identity = str; msg->msg_parent->msg_eap.eam_state = EAP_STATE_IDENTITY; return (0); case EAP_TYPE_MSCHAP_V2: if (len < sizeof(*ms)) goto fail; ms = (struct eap_mschap *)ptr; switch (ms->ms_opcode) { case EAP_MSOPCODE_CHALLENGE: if (len < sizeof(*msc)) goto fail; msc = (struct eap_mschap_challenge *)ptr; ptr += sizeof(*msc); len -= sizeof(*msc); if ((str = get_string(ptr, len)) == NULL) { log_debug("%s: invalid challenge name", __func__); return (-1); } log_info("%s: %s %s id %d " "length %d valuesize %d name '%s' length %zu", SPI_SA(sa, __func__), print_map(eap->eap_type, eap_type_map), print_map(ms->ms_opcode, eap_msopcode_map), msc->msc_id, betoh16(msc->msc_length), msc->msc_valuesize, str, len); free(str); print_hex(msc->msc_challenge, 0, sizeof(msc->msc_challenge)); break; case EAP_MSOPCODE_RESPONSE: if (len < sizeof(*msr)) goto fail; msr = (struct eap_mschap_response *)ptr; ptr += sizeof(*msr); len -= sizeof(*msr); if ((str = get_string(ptr, len)) == NULL) { log_debug("%s: invalid response name", __func__); return (-1); } log_info("%s: %s %s id %d " "length %d valuesize %d name '%s' name-length %zu", __func__, print_map(eap->eap_type, eap_type_map), print_map(ms->ms_opcode, eap_msopcode_map), msr->msr_id, betoh16(msr->msr_length), msr->msr_valuesize, str, len); free(str); print_hex(msr->msr_response.resp_data, 0, sizeof(msr->msr_response.resp_data)); break; case EAP_MSOPCODE_SUCCESS: if (eap->eap_code == EAP_CODE_REQUEST) { if (len < sizeof(*mss)) goto fail; mss = (struct eap_mschap_success *)ptr; ptr += sizeof(*mss); len -= sizeof(*mss); if ((str = get_string(ptr, len)) == NULL) { log_debug("%s: invalid response name", __func__); return (-1); } log_info("%s: %s %s request id %d " "length %d message '%s' message-len %zu", __func__, print_map(eap->eap_type, eap_type_map), print_map(ms->ms_opcode, eap_msopcode_map), mss->mss_id, betoh16(mss->mss_length), str, len); free(str); } else { if (len < sizeof(*ms)) goto fail; ms = (struct eap_mschap *)ptr; log_info("%s: %s %s response", __func__, print_map(eap->eap_type, eap_type_map), print_map(ms->ms_opcode, eap_msopcode_map)); if (response) break; msg->msg_parent->msg_eap.eam_success = 1; msg->msg_parent->msg_eap.eam_state = EAP_STATE_SUCCESS; return (0); } break; case EAP_MSOPCODE_FAILURE: if (len < sizeof(*msf)) goto fail; msf = (struct eap_mschap_failure *)ptr; ptr += sizeof(*msf); len -= sizeof(*msf); if ((str = get_string(ptr, len)) == NULL) { log_debug("%s: invalid failure message", __func__); return (-1); } log_info("%s: %s %s id %d " "length %d message '%s'", __func__, print_map(eap->eap_type, eap_type_map), print_map(ms->ms_opcode, eap_msopcode_map), msf->msf_id, betoh16(msf->msf_length), str); free(str); break; default: log_info("%s: unknown ms opcode %d", __func__, ms->ms_opcode); return (-1); } if (response) break; return (eap_mschap(env, sa, msg, eap)); default: log_debug("%s: unsupported EAP type %s", __func__, print_map(eap->eap_type, eap_type_map)); return (-1); } return (0); fail: log_debug("%s: short message", __func__); return (-1); } openiked-7.4/iked/eap.h000066400000000000000000000130511477554731100150320ustar00rootroot00000000000000/* $OpenBSD: eap.h,v 1.6 2020/09/16 21:37:35 tobhe Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef IKED_EAP_H #define IKED_EAP_H #include "openbsd-compat.h" struct eap_header { uint8_t eap_code; uint8_t eap_id; uint16_t eap_length; } __packed; struct eap_message { uint8_t eap_code; uint8_t eap_id; uint16_t eap_length; uint8_t eap_type; /* Followed by type-specific data */ } __packed; #define EAP_CODE_REQUEST 1 /* Request */ #define EAP_CODE_RESPONSE 2 /* Response */ #define EAP_CODE_SUCCESS 3 /* Success */ #define EAP_CODE_FAILURE 4 /* Failure */ extern struct iked_constmap eap_code_map[]; /* http://www.iana.org/assignments/eap-numbers */ #define EAP_TYPE_NONE 0 /* NONE */ #define EAP_TYPE_IDENTITY 1 /* RFC3748 */ #define EAP_TYPE_NOTIFICATION 2 /* RFC3748 */ #define EAP_TYPE_NAK 3 /* RFC3748 */ #define EAP_TYPE_MD5 4 /* RFC3748 */ #define EAP_TYPE_OTP 5 /* RFC3748 */ #define EAP_TYPE_GTC 6 /* RFC3748 */ #define EAP_TYPE_RSA 9 /* Whelan */ #define EAP_TYPE_DSS 10 /* Nace */ #define EAP_TYPE_KEA 11 /* Nace */ #define EAP_TYPE_KEA_VALIDATE 12 /* Nace */ #define EAP_TYPE_TLS 13 /* RFC5216 */ #define EAP_TYPE_AXENT 14 /* Rosselli */ #define EAP_TYPE_SECURID 15 /* Nystrm */ #define EAP_TYPE_ARCOT 16 /* Jerdonek */ #define EAP_TYPE_CISCO 17 /* Norman */ #define EAP_TYPE_SIM 18 /* RFC4186 */ #define EAP_TYPE_SRP_SHA1 19 /* Carlson */ #define EAP_TYPE_TTLS 21 /* Funk */ #define EAP_TYPE_RAS 22 /* Fields */ #define EAP_TYPE_OAAKA 23 /* RFC4187 */ #define EAP_TYPE_3COM 24 /* Young */ #define EAP_TYPE_PEAP 25 /* Palekar */ #define EAP_TYPE_MSCHAP_V2 26 /* Palekar */ #define EAP_TYPE_MAKE 27 /* Berrendonner */ #define EAP_TYPE_CRYPTOCARD 28 /* Webb */ #define EAP_TYPE_MSCHAP_V2_2 29 /* Potter */ #define EAP_TYPE_DYNAMID 30 /* Merlin */ #define EAP_TYPE_ROB 31 /* Ullah */ #define EAP_TYPE_POTP 32 /* RFC4794 */ #define EAP_TYPE_MS_TLV 33 /* Palekar */ #define EAP_TYPE_SENTRINET 34 /* Kelleher */ #define EAP_TYPE_ACTIONTEC 35 /* Chang */ #define EAP_TYPE_BIOMETRICS 36 /* Xiong */ #define EAP_TYPE_AIRFORTRESS 37 /* Hibbard */ #define EAP_TYPE_HTTP_DIGEST 38 /* Tavakoli */ #define EAP_TYPE_SECURESUITE 39 /* Clements */ #define EAP_TYPE_DEVICECONNECT 40 /* Pitard */ #define EAP_TYPE_SPEKE 41 /* Zick */ #define EAP_TYPE_MOBAC 42 /* Rixom */ #define EAP_TYPE_FAST 43 /* Cam-Winget */ #define EAP_TYPE_ZLX 44 /* Bogue */ #define EAP_TYPE_LINK 45 /* Zick */ #define EAP_TYPE_PAX 46 /* Clancy */ #define EAP_TYPE_PSK 47 /* RFC-bersani-eap-psk-11.txt */ #define EAP_TYPE_SAKE 48 /* RFC-vanderveen-eap-sake-02.txt */ #define EAP_TYPE_IKEV2 49 /* RFC5106 */ #define EAP_TYPE_AKA2 50 /* RFC5448 */ #define EAP_TYPE_GPSK 51 /* RFC5106 */ #define EAP_TYPE_PWD 52 /* RFC-harkins-emu-eap-pwd-12.txt */ #define EAP_TYPE_EXPANDED_TYPE 254 /* RFC3748 */ #define EAP_TYPE_EXPERIMENTAL 255 /* RFC3748 */ extern struct iked_constmap eap_type_map[]; /* * EAP MSCHAP-V2 */ #define EAP_MSCHAP_CHALLENGE_SZ 16 #define EAP_MSCHAP_RESPONSE_SZ 49 #define EAP_MSCHAP_NTRESPONSE_SZ 24 #define EAP_MSCHAP_SUCCESS_SZ 42 #define EAP_MSOPCODE_CHALLENGE 1 /* Challenge */ #define EAP_MSOPCODE_RESPONSE 2 /* Response */ #define EAP_MSOPCODE_SUCCESS 3 /* Success */ #define EAP_MSOPCODE_FAILURE 4 /* Failure */ #define EAP_MSOPCODE_CHANGE_PASSWORD 7 /* Change Password */ extern struct iked_constmap eap_msopcode_map[]; struct eap_mschap { uint8_t ms_opcode; } __packed; struct eap_mschap_challenge { uint8_t msc_opcode; uint8_t msc_id; uint16_t msc_length; uint8_t msc_valuesize; uint8_t msc_challenge[EAP_MSCHAP_CHALLENGE_SZ]; /* Followed by variable-size name field */ } __packed; struct eap_mschap_peer { uint8_t msp_challenge[EAP_MSCHAP_CHALLENGE_SZ]; uint8_t msp_reserved[8]; uint8_t msp_ntresponse[EAP_MSCHAP_NTRESPONSE_SZ]; uint8_t msp_flags; }; struct eap_mschap_response { uint8_t msr_opcode; uint8_t msr_id; uint16_t msr_length; uint8_t msr_valuesize; union { uint8_t resp_data[EAP_MSCHAP_RESPONSE_SZ]; struct eap_mschap_peer resp_peer; } msr_response; /* Followed by variable-size name field */ } __packed; struct eap_mschap_success { uint8_t mss_opcode; uint8_t mss_id; uint16_t mss_length; /* Followed by variable-size success message */ } __packed; struct eap_mschap_failure { uint8_t msf_opcode; uint8_t msf_id; uint16_t msf_length; /* Followed by variable-size message field */ } __packed; #define EAP_MSERROR_RESTRICTED_LOGON_HOURS 646 /* eap-mschapv2 */ #define EAP_MSERROR_ACCT_DISABLED 647 /* eap-mschapv2 */ #define EAP_MSERROR_PASSWD_EXPIRED 648 /* eap-mschapv2 */ #define EAP_MSERROR_NO_DIALIN_PERMISSION 649 /* eap-mschapv2 */ #define EAP_MSERROR_AUTHENTICATION_FAILURE 691 /* eap-mschapv2 */ #define EAP_MSERROR_CHANGING_PASSWORD 709 /* eap-mschapv2 */ extern struct iked_constmap eap_mserror_map[]; #endif /* IKED_EAP_H */ openiked-7.4/iked/genmap.sh000066400000000000000000000030661477554731100157240ustar00rootroot00000000000000#!/bin/sh # $OpenBSD: genmap.sh,v 1.7 2015/01/16 06:39:58 deraadt Exp $ # Copyright (c) 2010-2013 Reyk Floeter # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. TOK=$(echo ${2} | tr "[:lower:]" "[:upper:]") tok=$(echo ${2} | tr "[:upper:]" "[:lower:]") MAP=$(grep "struct iked_constmap" $1 | sed -Ee "s/.*${tok}_([^_]+)_map.*/\1/g") # Print license/copyright notice and headers cat < #include "types.h" #include "${tok}.h" EOF for i in $MAP; do lower=$(echo $i | tr "[:upper:]" "[:lower:]") upper=$(echo $i | tr "[:lower:]" "[:upper:]") echo "struct iked_constmap ${tok}_${lower}_map[] = {" X="${TOK}_${upper}" grep "$X" $1 | grep -v '\\' | sed -Ee \ "s/#define.*${X}_([^[:blank:]]+).*\/\* (.+) \*\/$\ / { ${X}_\1, \"\1\", \"\2\" },/" echo " { 0 }" echo "};" done openiked-7.4/iked/iked.8000066400000000000000000000131361477554731100151250ustar00rootroot00000000000000.\" $OpenBSD: iked.8,v 1.30 2021/11/29 13:20:24 jmc Exp $ .\" .\" Copyright (c) 2010 - 2014 Reyk Floeter .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate: November 29 2021 $ .Dt IKED 8 .Os .Sh NAME .Nm iked .Nd Internet Key Exchange version 2 (IKEv2) daemon .Sh SYNOPSIS .Nm iked .Op Fl dnSTtVv .Op Fl D Ar macro Ns = Ns Ar value .Op Fl f Ar file .Op Fl p Ar udpencap_port .Op Fl s Ar socket .Sh DESCRIPTION .Nm is an Internet Key Exchange (IKEv2) daemon which performs mutual authentication and which establishes and maintains IPsec flows and security associations (SAs) between the two peers. .Pp The IKEv2 protocol is defined in RFC 7296, which combines and updates the previous standards: ISAKMP/Oakley (RFC 2408), IKE (RFC 2409), and the Internet DOI (RFC 2407). .Nm only supports the IKEv2 protocol; support for ISAKMP/Oakley and IKEv1 is provided by .Xr isakmpd 8 . .Pp .Nm supports mutual authentication using RSA or ECDSA public keys and X.509 certificates. See the .Sx PUBLIC KEY AUTHENTICATION section below and PKI AND CERTIFICATE AUTHORITY COMMANDS in .Xr ikectl 8 for more information about creating and maintaining the public key infrastructure. .Pp The options are as follows: .Bl -tag -width Ds .It Fl D Ar macro Ns = Ns Ar value Define .Ar macro to be set to .Ar value on the command line. Overrides the definition of .Ar macro in the configuration file. .It Fl d Do not daemonize and log to .Em stderr . .It Fl f Ar file Use .Ar file as the configuration file, instead of the default .Pa /etc/iked.conf . .It Fl n Configtest mode. Only check the configuration file for validity. .It Fl p Ar udpencap_port Specify the listen port for encapsulated UDP that the daemon will bind to as well as the UDP encapsulation port set in resulting IPsec SAs. In order to receive UDP encapsulated IPsec packets on ports other than 4500, the .Em net.inet.esp.udpencap_port .Xr sysctl 2 variable has to be set accordingly. Implies -t. .It Fl S Start .Nm in passive mode. See the .Ic set passive option in .Xr iked.conf 5 for more information. .It Fl s Ar socket Use .Ar socket as the control socket, instead of the default .Pa /var/run/iked.sock . .It Fl T Disable NAT-Traversal and do not propose NAT-Traversal support to the peers. .It Fl t Enforce NAT-Traversal and only listen to NAT-Traversal messages. This option is only recommended for testing; the default is to negotiate NAT-Traversal with the peers. .It Fl V Show the version and exit. .It Fl v Produce more verbose output. .El .Sh PUBLIC KEY AUTHENTICATION It is possible to store trusted public keys to make them directly usable by .Nm , bypassing the need to use certificates. The keys should be saved in PEM format (see .Xr openssl 1 ) and named and stored as follows: .Pp .Bl -tag -width "for_ufqdn_identitiesXX" -offset 3n -compact .It For IPv4 identities: /etc/iked/pubkeys/ipv4/A.B.C.D .It For IPv6 identities: /etc/iked/pubkeys/ipv6/abcd:abcd::ab:bc .It For FQDN identities: /etc/iked/pubkeys/fqdn/foo.bar.org .It For UFQDN identities: /etc/iked/pubkeys/ufqdn/user@foo.bar.org .El .Pp Depending on the .Ic srcid and .Ic dstid specifications in .Xr iked.conf 5 , keys may be named after their IPv4 address, IPv6 address, fully qualified domain name (FQDN) or user fully qualified domain name (UFQDN). .Pp For example, .Nm can authenticate using the pre-generated keys if the local public key, by default .Pa /etc/iked/local.pub , is copied to the remote gateway as .Pa /etc/iked/pubkeys/ipv4/local.gateway.ip.address and the remote gateway's public key is copied to the local gateway as .Pa /etc/iked/pubkeys/ipv4/remote.gateway.ip.address . Of course, new keys may also be generated (the user is not required to use the pre-generated keys). In this example, .Ic srcid and .Ic dstid would also have to be set to the specified addresses in .Xr iked.conf 5 . .Sh FILES .Bl -tag -width "/etc/iked/private/XXX" -compact .It Pa /etc/iked.conf The default .Nm configuration file. .It Pa /etc/iked/ca/ The directory where CA certificates are kept. .It Pa /etc/iked/certs/ The directory where IKE certificates are kept, both the local certificate(s) and those of the peers, if a choice to have them kept permanently has been made. .It Pa /etc/iked/crls/ The directory where CRLs are kept. .It Pa /etc/iked/private/ The directory where local private keys used for public key authentication are kept. The file .Pa local.key is used to store the local private key. .It Pa /etc/iked/pubkeys/ The directory in which trusted public keys are kept. The keys must be named in the fashion described above. .It Pa /var/run/iked.sock The default .Nm control socket. .El .Sh SEE ALSO .Xr iked.conf 5 , .Xr ikectl 8 , .Xr isakmpd 8 .Sh STANDARDS .Rs .%A C. Kaufman .%A P. Hoffman .%A Y. Nir .%A P. Eronen .%A T. Kivinen .%D October 2014 .%R RFC 7296 .%T Internet Key Exchange Protocol Version 2 (IKEv2) .Re .Sh HISTORY The .Nm program first appeared in .Ox 4.8 . .Sh AUTHORS The .Nm program was written by .An Reyk Floeter Aq Mt reyk@openbsd.org . openiked-7.4/iked/iked.c000066400000000000000000000323731477554731100152040ustar00rootroot00000000000000/* $OpenBSD: iked.c,v 1.72 2024/12/26 18:24:54 sthen Exp $ */ /* * Copyright (c) 2019 Tobias Heider * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #ifdef __APPLE__ #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" #include "ikev2.h" #include "version.h" #ifdef WITH_APPARMOR #include "apparmor.h" #endif __dead void usage(void); /* Saves a copy of argv for setproctitle emulation */ #ifndef HAVE_SETPROCTITLE static char **saved_av; #endif void parent_shutdown(struct iked *); void parent_sig_handler(int, short, void *); int parent_dispatch_ca(int, struct privsep_proc *, struct imsg *); int parent_dispatch_control(int, struct privsep_proc *, struct imsg *); int parent_dispatch_ikev2(int, struct privsep_proc *, struct imsg *); void parent_connected(struct privsep *); int parent_configure(struct iked *); struct iked *iked_env; static struct privsep_proc procs[] = { { "ca", PROC_CERT, parent_dispatch_ca, caproc, IKED_CA }, { "control", PROC_CONTROL, parent_dispatch_control, control }, { "ikev2", PROC_IKEV2, parent_dispatch_ikev2, ikev2 } }; __dead void usage(void) { extern char *__progname; fprintf(stderr, "usage: %s [-dnSTtVv] [-D macro=value] " "[-f file] [-p udpencap_port] [-s socket]\n", __progname); exit(1); } int main(int argc, char *argv[]) { int c; int debug = 0, verbose = 0; int opts = 0; enum natt_mode natt_mode = NATT_DEFAULT; in_port_t port = IKED_NATT_PORT; const char *conffile = IKED_CONFIG; const char *sock = IKED_SOCKET; const char *errstr, *title = NULL; struct iked *env = NULL; struct privsep *ps; enum privsep_procid proc_id = PROC_PARENT; int proc_instance = 0; int argc0 = argc; log_init(1, LOG_DAEMON); #ifndef HAVE_SETPROCTITLE int i; saved_av = calloc(argc + 1, sizeof(*saved_av)); if (saved_av == NULL) errx(1, "calloc"); for (i = 0; i < argc; i++) { saved_av[i] = strdup(argv[i]); if (saved_av[i] == NULL) errx(1, "strdup"); } saved_av[i] = NULL; compat_init_setproctitle(argc, argv); argv = saved_av; #endif while ((c = getopt(argc, argv, "6D:df:I:nP:p:Ss:TtvV")) != -1) { switch (c) { case '6': log_warnx("the -6 option is ignored and will be " "removed in the future."); break; case 'D': if (cmdline_symset(optarg) < 0) log_warnx("could not parse macro definition %s", optarg); break; case 'd': debug++; break; case 'f': conffile = optarg; break; case 'I': proc_instance = strtonum(optarg, 0, PROC_MAX_INSTANCES, &errstr); if (errstr) fatalx("invalid process instance"); break; case 'n': debug = 1; opts |= IKED_OPT_NOACTION; break; case 'P': title = optarg; proc_id = proc_getid(procs, nitems(procs), title); if (proc_id == PROC_MAX) fatalx("invalid process name"); break; case 'p': if (natt_mode == NATT_DISABLE) errx(1, "-T and -p are mutually exclusive"); port = strtonum(optarg, 1, UINT16_MAX, &errstr); if (errstr != NULL) errx(1, "port is %s: %s", errstr, optarg); natt_mode = NATT_FORCE; break; case 'S': opts |= IKED_OPT_PASSIVE; break; case 's': sock = optarg; break; case 'T': if (natt_mode == NATT_FORCE) errx(1, "-T and -t/-p are mutually exclusive"); natt_mode = NATT_DISABLE; break; case 't': if (natt_mode == NATT_DISABLE) errx(1, "-T and -t are mutually exclusive"); natt_mode = NATT_FORCE; break; case 'v': verbose++; opts |= IKED_OPT_VERBOSE; break; case 'V': fprintf(stderr, "OpenIKED %s\n", IKED_VERSION); return 0; default: usage(); } } /* log to stderr until daemonized */ log_init(debug ? debug : 1, LOG_DAEMON); argc -= optind; if (argc > 0) usage(); if ((env = calloc(1, sizeof(*env))) == NULL) fatal("calloc: env"); iked_env = env; env->sc_opts = opts; env->sc_nattmode = natt_mode; env->sc_nattport = port; #ifdef WITH_APPARMOR env->sc_apparmor = armor_proc_open(); #endif ps = &env->sc_ps; ps->ps_env = env; if (strlcpy(env->sc_conffile, conffile, PATH_MAX) >= PATH_MAX) errx(1, "config file exceeds PATH_MAX"); ca_sslinit(); group_init(); policy_init(env); if ((ps->ps_pw = getpwnam(IKED_USER)) == NULL) errx(1, "unknown user %s", IKED_USER); /* Configure the control socket */ ps->ps_csock.cs_name = sock; log_init(debug, LOG_DAEMON); log_setverbose(verbose); if (opts & IKED_OPT_NOACTION) ps->ps_noaction = 1; else { /* check for root privileges */ if (geteuid()) errx(1, "need root privileges"); } ps->ps_instance = proc_instance; if (title != NULL) ps->ps_title[proc_id] = title; /* only the parent returns */ proc_init(ps, procs, nitems(procs), debug, argc0, argv, proc_id); setproctitle("parent"); log_procinit("parent"); event_init(); signal_set(&ps->ps_evsigint, SIGINT, parent_sig_handler, ps); signal_set(&ps->ps_evsigterm, SIGTERM, parent_sig_handler, ps); signal_set(&ps->ps_evsigchld, SIGCHLD, parent_sig_handler, ps); signal_set(&ps->ps_evsighup, SIGHUP, parent_sig_handler, ps); signal_set(&ps->ps_evsigpipe, SIGPIPE, parent_sig_handler, ps); signal_set(&ps->ps_evsigusr1, SIGUSR1, parent_sig_handler, ps); signal_add(&ps->ps_evsigint, NULL); signal_add(&ps->ps_evsigterm, NULL); signal_add(&ps->ps_evsigchld, NULL); signal_add(&ps->ps_evsighup, NULL); signal_add(&ps->ps_evsigpipe, NULL); signal_add(&ps->ps_evsigusr1, NULL); #if defined(HAVE_VROUTE) vroute_init(env); #endif proc_connect(ps, parent_connected); event_dispatch(); log_debug("%d parent exiting", getpid()); parent_shutdown(env); return (0); } void parent_connected(struct privsep *ps) { struct iked *env = ps->ps_env; if (parent_configure(env) == -1) fatalx("configuration failed"); } int parent_configure(struct iked *env) { struct sockaddr_storage ss; if (parse_config(env->sc_conffile, env) == -1) { proc_kill(&env->sc_ps); exit(1); } if (env->sc_opts & IKED_OPT_NOACTION) { fprintf(stderr, "configuration OK\n"); proc_kill(&env->sc_ps); exit(0); } env->sc_pfkey = -1; config_setpfkey(env); /* Send private and public keys to cert after forking the children */ if (config_setkeys(env) == -1) fatalx("%s: failed to send keys", __func__); config_setreset(env, RESET_CA, PROC_CERT); /* Now compile the policies and calculate skip steps */ config_setcompile(env, PROC_IKEV2); bzero(&ss, sizeof(ss)); ss.ss_family = AF_INET; #ifdef __APPLE__ int nattport = env->sc_nattport; sysctlbyname("net.inet.ipsec.esp_port", NULL, NULL, &nattport, sizeof(nattport)); #endif /* see comment on config_setsocket() */ if (env->sc_nattmode != NATT_FORCE) config_setsocket(env, &ss, htons(IKED_IKE_PORT), PROC_IKEV2, 0); if (env->sc_nattmode != NATT_DISABLE) config_setsocket(env, &ss, htons(env->sc_nattport), PROC_IKEV2, 1); bzero(&ss, sizeof(ss)); ss.ss_family = AF_INET6; if (env->sc_nattmode != NATT_FORCE) config_setsocket(env, &ss, htons(IKED_IKE_PORT), PROC_IKEV2, 0); if (env->sc_nattmode != NATT_DISABLE) config_setsocket(env, &ss, htons(env->sc_nattport), PROC_IKEV2, 1); /* * pledge in the parent process: * It has to run fairly late to allow forking the processes and * opening the PFKEY socket and the listening UDP sockets (once) * that need the bypass ioctls that are never allowed by pledge. * * Other flags: * stdio - for malloc and basic I/O including events. * rpath - for reload to open and read the configuration files. * proc - run kill to terminate its children safely. * dns - for reload and ocsp connect. * inet - for ocsp connect. * route - for using interfaces in iked.conf (SIOCGIFGMEMB) * wroute - for adding and removing addresses (SIOCAIFGMEMB) * sendfd - for ocsp sockets. */ if (pledge("stdio rpath proc dns inet route wroute sendfd", NULL) == -1) fatal("pledge"); config_setstatic(env); config_setcoupled(env, env->sc_decoupled ? 0 : 1); config_setocsp(env); /* Must be last */ config_setmode(env, env->sc_passive ? 1 : 0); return (0); } void parent_reload(struct iked *env, int reset, const char *filename) { /* Switch back to the default config file */ if (filename == NULL || *filename == '\0') filename = env->sc_conffile; log_debug("%s: level %d config file %s", __func__, reset, filename); if (reset == RESET_RELOAD) { config_setreset(env, RESET_POLICY, PROC_IKEV2); if (config_setkeys(env) == -1) fatalx("%s: failed to send keys", __func__); config_setreset(env, RESET_CA, PROC_CERT); if (parse_config(filename, env) == -1) { log_debug("%s: failed to load config file %s", __func__, filename); } /* Re-compile policies and skip steps */ config_setcompile(env, PROC_IKEV2); config_setstatic(env); config_setcoupled(env, env->sc_decoupled ? 0 : 1); config_setocsp(env); /* Must be last */ config_setmode(env, env->sc_passive ? 1 : 0); } else { config_setreset(env, reset, PROC_IKEV2); config_setreset(env, reset, PROC_CERT); } } void parent_sig_handler(int sig, short event, void *arg) { struct privsep *ps = arg; int die = 0, status, fail, id; pid_t pid; char *cause; switch (sig) { case SIGHUP: log_info("%s: reload requested with SIGHUP", __func__); /* * This is safe because libevent uses async signal handlers * that run in the event loop and not in signal context. */ parent_reload(ps->ps_env, 0, NULL); break; case SIGPIPE: log_info("%s: ignoring SIGPIPE", __func__); break; case SIGUSR1: log_info("%s: ignoring SIGUSR1", __func__); break; case SIGTERM: case SIGINT: die = 1; /* FALLTHROUGH */ case SIGCHLD: do { int len = 0; pid = waitpid(-1, &status, WNOHANG); if (pid <= 0) continue; fail = 0; if (WIFSIGNALED(status)) { fail = 1; len = asprintf(&cause, "terminated; signal %d", WTERMSIG(status)); } else if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) { fail = 1; len = asprintf(&cause, "exited abnormally"); } else { len = asprintf(&cause, "exited okay"); break; } } else fatalx("unexpected cause of SIGCHLD"); if (len == -1) fatal("asprintf"); die = 1; for (id = 0; id < PROC_MAX; id++) if (pid == ps->ps_pid[id]) { if (fail) log_warnx("lost child: %s %s", ps->ps_title[id], cause); break; } free(cause); } while (pid > 0 || (pid == -1 && errno == EINTR)); if (die) parent_shutdown(ps->ps_env); break; default: fatalx("unexpected signal"); } } int parent_dispatch_ca(int fd, struct privsep_proc *p, struct imsg *imsg) { struct iked *env = iked_env; switch (imsg->hdr.type) { case IMSG_CTL_ACTIVE: case IMSG_CTL_PASSIVE: proc_forward_imsg(&env->sc_ps, imsg, PROC_IKEV2, -1); break; case IMSG_OCSP_FD: ocsp_connect(env, imsg); break; default: return (-1); } return (0); } int parent_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg) { struct iked *env = iked_env; int v; char *str = NULL; unsigned int type = imsg->hdr.type; switch (type) { case IMSG_CTL_RESET: IMSG_SIZE_CHECK(imsg, &v); memcpy(&v, imsg->data, sizeof(v)); parent_reload(env, v, NULL); break; case IMSG_CTL_COUPLE: case IMSG_CTL_DECOUPLE: case IMSG_CTL_ACTIVE: case IMSG_CTL_PASSIVE: proc_compose(&env->sc_ps, PROC_IKEV2, type, NULL, 0); break; case IMSG_CTL_RELOAD: if (IMSG_DATA_SIZE(imsg) > 0) str = get_string(imsg->data, IMSG_DATA_SIZE(imsg)); parent_reload(env, 0, str); free(str); break; case IMSG_CTL_VERBOSE: proc_forward_imsg(&env->sc_ps, imsg, PROC_IKEV2, -1); proc_forward_imsg(&env->sc_ps, imsg, PROC_CERT, -1); /* return 1 to let proc.c handle it locally */ return (1); default: return (-1); } return (0); } int parent_dispatch_ikev2(int fd, struct privsep_proc *p, struct imsg *imsg) { struct iked *env = iked_env; switch (imsg->hdr.type) { #if defined(HAVE_VROUTE) case IMSG_IF_ADDADDR: case IMSG_IF_DELADDR: return (vroute_getaddr(env, imsg)); case IMSG_VDNS_ADD: case IMSG_VDNS_DEL: return (vroute_getdns(env, imsg)); case IMSG_VROUTE_ADD: case IMSG_VROUTE_DEL: return (vroute_getroute(env, imsg)); case IMSG_VROUTE_CLONE: return (vroute_getcloneroute(env, imsg)); #endif default: return (-1); } return (0); } void parent_shutdown(struct iked *env) { proc_kill(&env->sc_ps); #if defined(HAVE_VROUTE) vroute_cleanup(env); #endif free(env->sc_vroute); free(env); log_warnx("parent terminating"); exit(0); } openiked-7.4/iked/iked.conf.5000066400000000000000000000775461477554731100160650ustar00rootroot00000000000000.\" $OpenBSD: iked.conf.5,v 1.99 2024/11/04 02:44:28 dlg Exp $ .\" .\" Copyright (c) 2010 - 2014 Reyk Floeter .\" Copyright (c) 2004 Mathieu Sauve-Frankel All rights reserved. .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate: November 4 2024 $ .Dt IKED.CONF 5 .Os .Sh NAME .Nm iked.conf .Nd IKEv2 configuration file .Sh DESCRIPTION .Nm is the configuration file for .Xr iked 8 , the Internet Key Exchange version 2 (IKEv2) daemon for IPsec. IPsec itself is a pair of protocols: Encapsulating Security Payload (ESP), which provides integrity and confidentiality; and Authentication Header (AH), which provides integrity. The IPsec protocol itself is described in .Xr ipsec 4 . .Pp In its most basic form, a flow is established between hosts and/or networks, and then Security Associations (SA) are established, which detail how the desired protection will be achieved. IPsec uses flows to determine whether to apply security services to an IP packet or not. .Xr iked 8 is used to set up flows and establish SAs automatically, by specifying .Sq ikev2 policies in .Nm (see .Sx AUTOMATIC KEYING POLICIES , below). .Pp Alternative methods of setting up flows and SAs are also possible using manual keying or automatic keying using the older ISAKMP/Oakley a.k.a. IKEv1 protocol. Manual keying is not recommended, but can be convenient for quick setups and testing. See .Xr ipsec.conf 5 and .Xr isakmpd 8 for more information about manual keying and ISAKMP support. .Sh IKED.CONF FILE FORMAT .Nm is divided into three main sections: .Bl -tag -width xxxx .It Sy Macros User-defined macros may be defined and used later, simplifying the configuration file. .It Sy Global Configuration Global settings for .Xr iked 8 . .It Sy Automatic Keying Policies Policies to set up IPsec flows and SAs automatically. .El .Pp Lines beginning with .Sq # and empty lines are regarded as comments, and ignored. Lines may be split using the .Sq \e character. .Pp Argument names not beginning with a letter, digit, or underscore must be quoted. .Pp Addresses can be specified in CIDR notation (matching netblocks), as symbolic host names, interface names, or interface group names. .Pp Additional configuration files can be included with the .Ic include keyword, for example: .Bd -literal -offset indent include "/etc/macros.conf" .Ed .Pp Certain parameters can be expressed as lists, in which case .Xr iked 8 generates all the necessary flow combinations. For example: .Bd -literal -offset indent ikev2 esp proto { tcp, udp } \e from 192.168.1.1 to 10.0.0.18 \e peer 192.168.10.1 .Ed .Sh MACROS Macros can be defined that will later be expanded in context. Macro names must start with a letter, digit, or underscore, and may contain any of those characters. Macro names may not be reserved words (for example .Ic flow , .Ic from , .Ic esp ) . Macros are not expanded inside quotes. .Pp For example: .Bd -literal -offset indent remote_gw = "192.168.3.12" ikev2 esp from 192.168.7.0/24 to 192.168.8.0/24 peer $remote_gw .Ed .Sh GLOBAL CONFIGURATION Here are the settings that can be set globally: .Bl -tag -width xxxx .It Ic set active Set .Xr iked 8 to global active mode. In active mode the per-policy .Ar mode setting is respected. .Xr iked 8 will initiate policies set to .Ar active and wait for incoming requests for policies set to .Ar passive . This is the default. .It Ic set passive Set .Xr iked 8 to global passive mode. In passive mode no packets are sent to peers and no connections are initiated by .Xr iked 8 , even for .Ar active policies. This option is used for setups using .Xr sasyncd 8 and .Xr carp 4 to provide redundancy. .Xr iked 8 will run in passive mode until sasyncd has determined that the host is the master and can switch to active mode. .It Ic set couple Load the negotiated security associations (SAs) and flows into the kernel. This is the default. .It Ic set decouple Don't load the negotiated SAs and flows from the kernel. This mode is only useful for testing and debugging. .It Ic set dpd_check_interval Ar time Specify the liveness check interval, in seconds. Setting .Ar time to 0 disables DPD. The default value is 60 seconds. .It Ic set enforcesingleikesa Allow only a single active IKE SA for each .Ic dstid . When a new SA with the same .Ic dstid is established, it replaces the old SA. .It Ic set noenforcesingleikesa Don't limit the number of IKE SAs per .Ic dstid . This is the default. .It Ic set fragmentation Enable IKEv2 Message Fragmentation (RFC 7383) support. This allows IKEv2 to operate in environments that might block IP fragments. .It Ic set nofragmentation Disables IKEv2 Message Fragmentation support. This is the default. .It Ic set mobike Enable MOBIKE (RFC 4555) support. This is the default. MOBIKE allows the peer IP address to be changed for IKE and IPsec SAs. Currently .Xr iked 8 only supports MOBIKE when acting as a responder. .It Ic set nomobike Disables MOBIKE support. .It Ic set cert_partial_chain Allow partial certificate chain if at least one certificate is a trusted CA from .Pa /etc/iked/ca/ . .It Ic set ocsp Ar URL Op Ic tolerate Ar time Op Ic maxage Ar time Enable OCSP and set the fallback URL of the OCSP responder. This fallback will be used if the trusted CA from .Pa /etc/iked/ca/ does not have an OCSP-URL extension. The matching responder certificates have to be placed in .Pa /etc/iked/ocsp/responder.crt . .Pp The optional .Ic tolerate parameter specifies how much the OCSP response attribute .Sq thisUpdate may be in the future and how much .Sq nextUpdate may be in the past, with respect to the local time. The optional .Ic maxage parameter specifies how much .Sq thisUpdate may be in the past. If .Ic tolerate is set to 0 then the times are not verified at all. This is the default setting. .It Ic set vendorid Send OpenIKED Vendor ID payload. This is the default. .It Ic set novendorid Don't send a Vendor ID payload. .It Ic user Ar name password .Xr iked 8 supports user-based authentication by tunneling the Extensible Authentication Protocol (EAP) over IKEv2. In its most basic form, the users will be authenticated against a local, integrated password database that is configured with the .Ic user lines in .Nm and the .Ar name and .Ar password arguments. The password has to be specified in plain text which is required to support different challenge-based EAP methods like EAP-MD5 or EAP-MSCHAPv2. .El .Sh AUTOMATIC KEYING POLICIES This section is used to configure policies that will be used by .Xr iked 8 to set up flows and SAs automatically. Some examples of setting up automatic keying: .Bd -literal -offset 3n # Set up a VPN: # First between the gateway machines 192.168.3.1 and 192.168.3.2 # Second between the networks 10.1.1.0/24 and 10.1.2.0/24 ikev2 esp from 192.168.3.1 to 192.168.3.2 ikev2 esp from 10.1.1.0/24 to 10.1.2.0/24 peer 192.168.3.2 .Ed .Pp For incoming connections from remote peers, the policies are evaluated in sequential order, from first to last. The last matching policy decides what action is taken; if no policy matches the connection, the default action is to ignore the connection attempt or to use the .Ar default policy, if set. See the .Sx EXAMPLES section for a detailed example of the policy evaluation. .Pp The first time an IKEv2 connection matches a policy, an IKE SA is created; for subsequent packets the connection is identified by the IKEv2 parameters that are stored in the SA without evaluating any policies. After the connection is closed or times out, the IKE SA is automatically removed. .Pp The commands are as follows: .Bl -tag -width xxxx -compact .It Xo .Ic ikev2 .Op Ar name .Xc The mandatory .Ic ikev2 keyword will identify an IKEv2 automatic keying policy. .Ar name is an optional arbitrary string identifying the policy. The name should only occur once in .Nm or any included files. If omitted, a name will be generated automatically for the policy. .Pp .It Op Ar eval The .Ar eval option modifies the policy evaluation for this policy. It can be one of .Ar quick , .Ar skip or .Ar default . If a new incoming connection matches a policy with the .Ar quick option set, that policy is considered the last matching policy, and evaluation of subsequent policies is skipped. The .Ar skip option will disable evaluation of this policy for incoming connections. The .Ar default option sets the default policy and should only be specified once. .Pp .It Op Ar mode .Ar mode specifies the IKEv2 mode to use: one of .Ar passive or .Ar active . When .Ar passive is specified, .Xr iked 8 will not immediately start negotiation of this tunnel, but wait for an incoming request from the remote peer. When .Ar active is specified, negotiation will be started at once. If omitted, .Ar passive mode will be used. .Pp .It Op Ar ipcomp The keyword .Ar ipcomp specifies that .Xr ipcomp 4 , the IP Payload Compression protocol, is negotiated in addition to encapsulation. The optional compression is applied before packets are encapsulated. IPcomp must be enabled in the kernel: .Pp .Dl # sysctl net.inet.ipcomp.enable=1 .Pp .It Op Ar tmode .Ar tmode describes the encapsulation mode to be used. Possible modes are .Ar tunnel and .Ar transport ; the default is .Ar tunnel . .Pp .It Op Ar natt .Ar natt forces negotiation of NAT-Traversal after the initial handshake. .Pp .It Op Ar encap .Ar encap specifies the encapsulation protocol to be used. Possible protocols are .Ar esp and .Ar ah ; the default is .Ar esp . .Pp .It Op Ar af This policy only applies to endpoints of the specified address family which can be either .Ar inet or .Ar inet6 . This only matters for IKEv2 endpoints and does not restrict the traffic selectors to negotiate flows with different address families, e.g. IPv6 flows negotiated by IPv4 endpoints. .Pp .It Xo .Ic proto Ar protocol .Xc .It Xo .Ic proto .Ic { Ar protocol ... Ic } .Xc The optional .Ic proto parameter restricts the flow to a specific IP protocol. Common protocols are .Xr icmp 4 , .Xr tcp 4 , and .Xr udp 4 . For a list of all the protocol name to number mappings used by .Xr iked 8 , see the file .Pa /etc/protocols . .Pp Multiple .Ar protocol entries can be specified, separated by commas or whitespace, if enclosed in curly brackets: .Bd -literal -offset indent proto { tcp, udp } .Ed .Pp .It Ic rdomain Ar number Specify a different routing domain for unencrypted traffic. The resulting IPsec SAs will match outgoing packets in the specified .Ic rdomain Ar number and move the encrypted packets to the rdomain the .Xr iked 8 instance is running in. Vice versa, incoming .Xr ipsec 4 traffic is moved to .Ic rdomain Ar number after decryption. .Pp .It Xo .Ic from Ar src .Op Ic port Ar sport .Op Pq Ar srcnat .Ic to Ar dst .Op Ic port Ar dport .Xc Specify one or more traffic selectors for this policy which will be used to negotiate the IPsec flows between the IKEv2 peers. During the negotiation, the peers may decide to narrow a flow to a subset of the configured traffic selector networks to match the policies on each side. .Pp Each traffic selector will apply for packets with source address .Ar src and destination address .Ar dst . If the .Ar src argument specifies a fictional source ID, the .Ar srcnat parameter can be used to specify the actual source address. This can be used in outgoing NAT/BINAT scenarios as described below. The keyword .Ar any will match any address (i.e. 0.0.0.0/0 and ::/0). If the .Ic config address option is specified, the .Ar dynamic keyword can be used to create flows from or to the dynamically assigned address. .Pp The optional .Ic port modifiers restrict the traffic selectors to the specified ports. They are only valid in conjunction with the .Xr tcp 4 and .Xr udp 4 protocols. Ports can be specified by number or by name. For a list of all port name to number mappings used by .Xr ipsecctl 8 , see the file .Pa /etc/services . .Pp .It Ic local Ar localip Ic peer Ar remote The .Ic local parameter specifies the address or FQDN of the local endpoint. Unless the gateway is multi-homed or uses address aliases, this option is generally not needed. .Pp The .Ic peer parameter specifies the address or FQDN of the remote endpoint. For host-to-host connections where .Ar dst is identical to .Ar remote , this option is generally not needed as it will be set to .Ar dst automatically. If it is not specified or if the keyword .Ar any is given, the default peer is used. .Pp .It Xo .Ic ikesa .Ic auth Ar algorithm .Ic enc Ar algorithm .Ic prf Ar algorithm .Ic group Ar group .Xc These parameters define the mode and cryptographic transforms to be used for the IKE SA negotiation, also known as phase 1. The IKE SA will be used to authenticate the machines and to set up an encrypted channel for the IKEv2 protocol. .Pp Possible values for .Ic auth , .Ic enc , .Ic prf , .Ic group , and the default proposals are described below in .Sx CRYPTO TRANSFORMS . If omitted, .Xr iked 8 will use the default proposals for the IKEv2 protocol. .Pp The keyword .Ic ikesa can be used multiple times as a delimiter between IKE SA proposals. The order of the proposals depend on the order in the configuration. The keywords .Ic auth , .Ic enc , .Ic prf and .Ic group can be used multiple times within a single proposal to configure multiple crypto transforms. .Pp .It Xo .Ic childsa .Ic auth Ar algorithm .Ic enc Ar algorithm .Ic group Ar group .Ic esn .Xc These parameters define the cryptographic transforms to be used for the Child SA negotiation, also known as phase 2. Each Child SA will be used to negotiate the actual IPsec SAs. The initial Child SA is always negotiated with the initial IKEv2 key exchange; additional Child SAs may be negotiated with additional Child SA key exchanges for an established IKE SA. .Pp Possible values for .Ic auth , .Ic enc , .Ic group , .Ic esn , and the default proposals are described below in .Sx CRYPTO TRANSFORMS . If omitted, .Xr iked 8 will use the default proposals for the ESP or AH protocol. .Pp The .Ic group option will only be used to enable Perfect Forward Secrecy (PFS) for additional Child SAs exchanges that are not part of the initial key exchange. .Pp The keyword .Ic childsa can be used multiple times as a delimiter between Child SA proposals. The order of the proposals depend on the order in the configuration. The keywords .Ic auth , .Ic enc and .Ic group can be used multiple times within a single proposal to configure multiple crypto transforms. .Pp .It Ic srcid Ar string Ic dstid Ar string .Ic srcid defines an ID of type .Dq FQDN , .Dq ASN1_DN , .Dq IPV4 , .Dq IPV6 , or .Dq UFQDN that will be used by .Xr iked 8 as the identity of the local peer. If the argument is an email address (reyk@example.com), .Xr iked 8 will use UFQDN as the ID type. The ASN1_DN type will be used if the string starts with a slash .Sq / (/C=DE/../CN=10.0.0.1/emailAddress=reyk@example.com). If the argument is an IPv4 address or a compressed IPv6 address, the ID types IPV4 or IPV6 will be used. Anything else is considered to be an FQDN. .Pp If .Ic srcid is omitted, the default is to use the hostname of the local machine, see .Xr hostname 1 to set or print the hostname. .Pp .Ic dstid is similar to .Ic srcid , but instead specifies the ID to be used by the remote peer. .Pp .It Ic ikelifetime Ar time The optional .Ic ikelifetime parameter defines the IKE SA expiration timeout by the .Ar time SA was created. A zero value disables active IKE SA rekeying. This is the default. .Pp The accepted format of the .Ar time specification is described below. .Pp .It Ic lifetime Ar time Op Ic bytes Ar bytes The optional .Ic lifetime parameter defines the Child SA expiration timeout by the .Ar time SA was in use and by the number of .Ar bytes that were processed using the SA. Default values are 3 hours and 4 gigabytes which means that SA will be rekeyed before reaching the time limit or 4 gigabytes of data will pass through. Zero values disable rekeying. .Pp Several unit specifiers are recognized (ignoring case): .Ql m and .Ql h for minutes and hours, and .Ql K , .Ql M and .Ql G for kilo-, mega- and gigabytes accordingly. .Pp Rekeying must happen at least several times a day as IPsec security heavily depends on frequent key renewals. .Pp .It Op Ar ikeauth Specify a method to be used to authenticate the remote peer. .Xr iked 8 will automatically determine a method based on public keys or certificates configured for the peer. .Ar ikeauth can be used to override this behaviour. Non-psk modes will require setting up certificates and RSA or ECDSA public keys; see .Xr iked 8 for more information. .Pp .Bl -tag -width $domain -compact -offset indent .It Ic eap Ar type Use EAP to authenticate the initiator. The only supported EAP .Ar type is currently .Ar MSCHAP-V2 . The responder will use RSA public key authentication. .It Ic ecdsa256 Use ECDSA with a 256-bit elliptic curve key and SHA2-256 for authentication. .It Ic ecdsa384 Use ECDSA with a 384-bit elliptic curve key and SHA2-384 for authentication. .It Ic ecdsa521 Use ECDSA with a 521-bit elliptic curve key and SHA2-512 for authentication. .It Ic psk Ar string Use a pre-shared key .Ar string or hex value (starting with 0x) for authentication. .It Ic psk file Ar path Use a pre-shared hex key (without leading 0x) read from .Ar path for authentication. .It Ic rfc7427 Only use RFC 7427 signatures for authentication. RFC 7427 signatures currently only support SHA2-256 as the hash. .It Ic rsa Use RSA public key authentication with SHA1 as the hash. .El .Pp The default is to allow any signature authentication. .Pp .It Cm config Ar option address .It Cm request Ar option address Request or serve one or more optional configuration payloads (CP). The configuration .Ar option can be one of the following with the expected address format: .Pp .Bl -tag -width Ds -compact -offset indent .It Ic address Ar address Assign a static address on the internal network. .It Ic address Ar address/prefix Assign a dynamic address on the internal network. The address will be assigned from an address pool with the size specified by .Ar prefix . .It Ic netmask Ar netmask The IPv4 netmask of the internal network. .It Ic name-server Ar address The DNS server address within the internal network. .It Ic netbios-server Ar address The NetBIOS name server (WINS) within the internal network. This option is provided for compatibility with legacy clients. .It Ic dhcp-server Ar address The address of an internal DHCP server for further configuration. .It Ic protected-subnet Ar address/prefix The address of an additional IPv4 or IPv6 subnet reachable over the gateway. This option is used to notify the peer of a subnet behind the gateway (that might require a second SA). Networks specified in this SA's "from" or "to" options do not need to be included. .It Ic access-server Ar address The address of an internal remote access server. .El .Pp .It Ic iface Ar interface Enable automatic network configuration as initiator. Received addresses, routes and nameservers will be installed on the specified .Ar interface . .Pp .It Ic tag Ar string Add a .Xr pf 4 tag to all packets of IPsec SAs created for this connection. This will allow matching packets for this connection by defining rules in .Xr pf.conf 5 using the .Cm tagged keyword. .Pp The following variables can be used in tags to include information from the remote peer on runtime: .Pp .Bl -tag -width $domain -compact -offset indent .It Ar $id The .Ic dstid that was proposed by the remote peer to identify itself. It will be expanded to .Ar id-value , e.g.\& .Ar FQDN/foo.example.com . To limit the size of the derived tag, .Xr iked 8 will extract the common name .Sq CN= from ASN1_DN IDs, for example .Ar ASN1_ID//C=DE/../CN=10.1.1.1/.. will be expanded to .Ar 10.1.1.1 . .It Ar $eapid For a connection using EAP, the identity (username) used by the remote peer. .It Ar $domain Extract the domain from IDs of type FQDN, UFQDN or ASN1_DN. .It Ar $name The name of the IKEv2 policy that was configured in .Nm or automatically generated by .Xr iked 8 . .El .Pp For example, if the ID is .Ar FQDN/foo.example.com or .Ar UFQDN/user@example.com , .Dq ipsec-$domain expands to .Dq ipsec-example.com . The variable expansion for the .Ar tag directive occurs only at runtime (not when the file is parsed) and must be quoted, or it will be interpreted as a macro. .Pp .It Ic tap Ar interface Send the decapsulated IPsec traffic to the specified .Xr enc 4 .Ar interface instead of .Ar enc0 for filtering and monitoring. The traffic will be blocked if the specified .Ar interface does not exist. .El .Sh PACKET FILTERING IPsec traffic appears unencrypted on the .Xr enc 4 interface and can be filtered accordingly using the .Ox packet filter, .Xr pf 4 . The grammar for the packet filter is described in .Xr pf.conf 5 . .Pp The following components are relevant to filtering IPsec traffic: .Bl -ohang -offset indent .It external interface Interface for IKE traffic and encapsulated IPsec traffic. .It proto udp port 500 IKE traffic on the external interface. .It proto udp port 4500 IKE NAT-Traversal traffic on the external interface. .It proto ah | esp Encapsulated IPsec traffic on the external interface. .It enc0 Default interface for outgoing traffic before it's been encapsulated, and incoming traffic after it's been decapsulated. State on this interface should be interface bound; see .Xr enc 4 for further information. .It proto ipencap [tunnel mode only] IP-in-IP traffic flowing between gateways on the enc0 interface. .It tagged ipsec-example.org Match traffic of IPsec SAs using the .Ic tag keyword. .El .Pp If the filtering rules specify to block everything by default, the following rule would ensure that IPsec traffic never hits the packet filtering engine, and is therefore passed: .Bd -literal -offset indent set skip on enc0 .Ed .Pp In the following example, all traffic is blocked by default. IPsec-related traffic from gateways {192.168.3.1, 192.168.3.2} and networks {10.0.1.0/24, 10.0.2.0/24} is permitted. .Bd -literal -offset indent block on ix0 block on enc0 pass in on ix0 proto udp from 192.168.3.2 to 192.168.3.1 \e port {500, 4500} pass out on ix0 proto udp from 192.168.3.1 to 192.168.3.2 \e port {500, 4500} pass in on ix0 proto esp from 192.168.3.2 to 192.168.3.1 pass out on ix0 proto esp from 192.168.3.1 to 192.168.3.2 pass in on enc0 proto ipencap from 192.168.3.2 to 192.168.3.1 \e keep state (if-bound) pass out on enc0 proto ipencap from 192.168.3.1 to 192.168.3.2 \e keep state (if-bound) pass in on enc0 from 10.0.2.0/24 to 10.0.1.0/24 \e keep state (if-bound) pass out on enc0 from 10.0.1.0/24 to 10.0.2.0/24 \e keep state (if-bound) .Ed .Pp .Xr pf 4 has the ability to filter IPsec-related packets based on an arbitrary .Em tag specified within a ruleset. The tag is used as an internal marker which can be used to identify the packets later on. This could be helpful, for example, in scenarios where users are connecting in from differing IP addresses, or to support queue-based bandwidth control, since the enc0 interface does not support it. .Pp The following .Xr pf.conf 5 fragment uses queues for all IPsec traffic with special handling for developers and employees: .Bd -literal -offset indent queue std on ix0 bandwidth 100M queue deflt parent std bandwidth 10M default queue developers parent std bandwidth 75M queue employees parent std bandwidth 5M queue ipsec parent std bandwidth 10M pass out on ix0 proto esp set queue ipsec pass out on ix0 tagged ipsec-developers.example.com \e set queue developers pass out on ix0 tagged ipsec-employees.example.com \e set queue employees .Ed .Pp The following example assigns the tags in the .Nm configuration and also sets an alternative .Xr enc 4 device: .Bd -literal -offset indent ikev2 esp from 10.1.1.0/24 to 10.1.2.0/24 peer 192.168.3.2 \e tag "ipsec-$domain" tap "enc1" .Ed .Sh OUTGOING NETWORK ADDRESS TRANSLATION In some network topologies it is desirable to perform NAT on traffic leaving through the VPN tunnel. In order to achieve that, the .Ar src argument is used to negotiate the desired network ID with the peer and the .Ar srcnat parameter defines the true local subnet, so that a correct SA can be installed on the local side. .Pp For example, if the local subnet is 192.168.1.0/24 and all the traffic for a specific VPN peer should appear as coming from 10.10.10.1, the following configuration is used: .Bd -literal -offset indent ikev2 esp from 10.10.10.1 (192.168.1.0/24) to 192.168.2.0/24 \e peer 10.10.20.1 .Ed .Pp Naturally, a relevant NAT rule is required in .Xr pf.conf 5 . For the example above, this would be: .Bd -literal -offset indent match out on enc0 from 192.168.1.0/24 to 192.168.2.0/24 \e nat-to 10.10.10.1 .Ed .Pp From the peer's point of view, the local end of the VPN tunnel is declared to be 10.10.10.1 and all the traffic arrives with that source address. .Sh CRYPTO TRANSFORMS The following authentication types are permitted with the .Ic auth keyword: .Bl -column "Authentication" "Key Length" "Truncated Length" "Default" -offset indent .It Em "Authentication" Ta Em "Key Length" Ta Em "Truncated Length" Ta Em "Default" .It Li hmac-md5 Ta "128 bits" Ta "96 bits" Ta "" .It Li hmac-sha1 Ta "160 bits" Ta "96 bits" Ta "x" .It Li hmac-sha2-256 Ta "256 bits" Ta "128 bits" Ta "x" .It Li hmac-sha2-384 Ta "384 bits" Ta "192 bits" Ta "x" .It Li hmac-sha2-512 Ta "512 bits" Ta "256 bits" Ta "x" .El .Pp The following pseudo-random function types are permitted with the .Ic prf keyword: .Bl -column "hmac-sha2-512" "Key Length" "Default" "[IKE only]" -offset indent .It Em "PRF" Ta Em "Key Length" Ta Em "Default" Ta "" .It Li hmac-md5 Ta "128 bits" Ta "" Ta "[IKE only]" .It Li hmac-sha1 Ta "160 bits" Ta "x" Ta "[IKE only]" .It Li hmac-sha2-256 Ta "256 bits" Ta "x" Ta "[IKE only]" .It Li hmac-sha2-384 Ta "384 bits" Ta "x" Ta "[IKE only]" .It Li hmac-sha2-512 Ta "512 bits" Ta "x" Ta "[IKE only]" .El .Pp The following cipher types are permitted with the .Ic enc keyword: .Bl -column "chacha20-poly1305" "Key Length" "Default" "[ESP only]" -offset indent .It Em "Cipher" Ta Em "Key Length" Ta Em "Default" Ta "" .It Li 3des Ta "168 bits" Ta "x" Ta "" .It Li aes-128 Ta "128 bits" Ta "x" Ta "" .It Li aes-192 Ta "192 bits" Ta "x" Ta "" .It Li aes-256 Ta "256 bits" Ta "x" Ta "" .It Li aes-128-ctr Ta "160 bits" Ta "" Ta "[ESP only]" .It Li aes-192-ctr Ta "224 bits" Ta "" Ta "[ESP only]" .It Li aes-256-ctr Ta "288 bits" Ta "" Ta "[ESP only]" .It Li aes-128-gcm Ta "160 bits" Ta "x" Ta "" .It Li aes-192-gcm Ta "224 bits" Ta "" Ta "[ESP only]" .It Li aes-256-gcm Ta "288 bits" Ta "x" Ta "" .It Li aes-128-gcm-12 Ta "160 bits" Ta "" Ta "[IKE only]" .It Li aes-256-gcm-12 Ta "288 bits" Ta "" Ta "[IKE only]" .It Li blowfish Ta "160 bits" Ta "" Ta "[ESP only]" .It Li cast Ta "128 bits" Ta "" Ta "[ESP only]" .It Li chacha20-poly1305 Ta "288 bits" Ta "" Ta "[ESP only]" .El .Pp The following cipher types provide only authentication, not encryption: .Bl -column "chacha20-poly1305" "Key Length" "Default" "[ESP only]" -offset indent .It Li aes-128-gmac Ta "160 bits" Ta "" Ta "[ESP only]" .It Li aes-192-gmac Ta "224 bits" Ta "" Ta "[ESP only]" .It Li aes-256-gmac Ta "288 bits" Ta "" Ta "[ESP only]" .It Li null Ta "" Ta "" Ta "[ESP only]" .El .Pp The Extended Sequence Numbers option can be enabled or disabled with the .Ic esn or .Ic noesn keywords: .Bl -column "noesn" "Default" "[ESP only]" -offset indent .It Em ESN Ta Em "Default" Ta Em "" .It Li esn Ta "x" Ta "[ESP only]" .It Li noesn Ta "x" Ta "[ESP only]" .El .Pp Transforms followed by .Bq IKE only can only be used with the .Ic ikesa keyword, transforms with .Bq ESP only can only be used with the .Ic childsa keyword. .Pp Using AES-GMAC or NULL with ESP will only provide authentication. This is useful in setups where AH cannot be used, e.g. when NAT is involved. .Pp The following group types are permitted with the .Ic group keyword: .Bl -column "sntrup761x25519" "Group" "1190 B" "Hybrid PQKE" "Default" -offset indent .It Em Name Ta Em Group Ta Em Size Ta Em Type Ta Em Default .It Li modp768 Ta grp1 Ta 768 Ta "MODP" Ta "" Ta "[insecure]" .It Li modp1024 Ta grp2 Ta 1024 Ta "MODP" Ta "x" Ta "[weak]" .It Li modp1536 Ta grp5 Ta 1536 Ta "MODP" Ta "x" Ta "[weak]" .It Li modp2048 Ta grp14 Ta 2048 Ta "MODP" Ta "x" .It Li modp3072 Ta grp15 Ta 3072 Ta "MODP" Ta "x" .It Li modp4096 Ta grp16 Ta 4096 Ta "MODP" Ta "x" .It Li modp6144 Ta grp17 Ta 6144 Ta "MODP" Ta "" .It Li modp8192 Ta grp18 Ta 8192 Ta "MODP" Ta "" .It Li ecp256 Ta grp19 Ta 256 Ta "ECP" Ta "x" .It Li ecp384 Ta grp20 Ta 384 Ta "ECP" Ta "x" .It Li ecp521 Ta grp21 Ta 521 Ta "ECP" Ta "x" .It Li ecp192 Ta grp25 Ta 192 Ta "ECP" Ta "" .It Li ecp224 Ta grp26 Ta 224 Ta "ECP" Ta "" .It Li brainpool224 Ta grp27 Ta 224 Ta "ECP" Ta "" .It Li brainpool256 Ta grp28 Ta 256 Ta "ECP" Ta "" .It Li brainpool384 Ta grp29 Ta 384 Ta "ECP" Ta "" .It Li brainpool512 Ta grp30 Ta 512 Ta "ECP" Ta "" .It Li curve25519 Ta grp31 Ta 256 Ta "Curve25519" Ta "x" .It Li sntrup761x25519 Ta "" Ta 1190 B Ta "Hybrid PQKE" Ta "" .El .Pp The currently supported group types are either MODP (exponentiation groups modulo a prime), ECP (elliptic curve groups modulo a prime), or Curve25519. MODP groups of less than 2048 bits are considered as weak or insecure (see RFC 8247 section 2.4) and only provided for backwards compatibility. .Sh FILES .Bl -tag -width /etc/examples/iked.conf -compact .It Pa /etc/iked.conf .It Pa /etc/examples/iked.conf .El .Sh EXAMPLES The first example is intended for a server with clients connecting to .Xr iked 8 as an IPsec gateway, or IKEv2 responder, using mutual public key authentication and additional challenge-based EAP-MSCHAPv2 password authentication: .Bd -literal -offset indent user "test" "password123" ikev2 "win7" esp \e from dynamic to 172.16.2.0/24 \e peer 10.0.0.0/8 local 192.168.56.0/24 \e eap "mschap-v2" \e config address 172.16.2.1 \e tag "$name-$id" .Ed .Pp The next example allows peers to authenticate using a pre-shared key .Sq foobar : .Bd -literal -offset indent ikev2 "big test" \e esp proto tcp \e from 10.0.0.0/8 port 23 to 20.0.0.0/8 port 40 \e from 192.168.1.1 to 192.168.2.2 \e peer any local any \e ikesa \e enc aes-128-gcm \e group ecp256 group curve25519 \e ikesa \e enc aes-128 auth hmac-sha2-256 \e group ecp256 group curve25519 \e childsa enc aes-128-gcm \e childsa enc aes-128 auth hmac-sha2-256 \e srcid host.example.com \e dstid 192.168.0.254 \e psk "foobar" .Ed .Pp The following example illustrates the last matching policy evaluation for incoming connections on an IKEv2 gateway. The peer 192.168.1.34 will always match the first policy because of the .Ar quick keyword; connections from the peers 192.168.1.3 and 192.168.1.2 will be matched by one of the last two policies; any other connections from 192.168.1.0/24 will be matched by the .Sq subnet policy; and any other connection will be matched by the .Sq catch all policy. .Bd -literal -offset indent ikev2 quick esp from 10.10.10.0/24 to 10.20.20.0/24 \e peer 192.168.1.34 ikev2 "catch all" esp from 10.0.1.0/24 to 10.0.2.0/24 \e peer any ikev2 "subnet" esp from 10.0.3.0/24 to 10.0.4.0/24 \e peer 192.168.1.0/24 ikev2 esp from 10.0.5.0/30 to 10.0.5.4/30 peer 192.168.1.2 ikev2 esp from 10.0.5.8/30 to 10.0.5.12/30 peer 192.168.1.3 .Ed .Pp This example encrypts a .Xr gre 4 tunnel from local machine A (2001:db8::aa:1) to peer D (2001:db8::dd:4) based on FQDN-based public key authentication; .Ar transport mode avoids double encapsulation: .Bd -literal -offset indent ikev2 transport \e proto gre \e from 2001:db8::aa:1 to 2001:db8::dd:4 \e peer D.example.com .Ed .Sh SEE ALSO .Xr enc 4 , .Xr ipsec 4 , .Xr ipsec.conf 5 , .Xr pf.conf 5 , .Xr ikectl 8 , .Xr iked 8 .Sh HISTORY The .Nm file format first appeared in .Ox 4.8 . .Sh AUTHORS The .Xr iked 8 program was written by .An Reyk Floeter Aq Mt reyk@openbsd.org . openiked-7.4/iked/iked.h000066400000000000000000001273451477554731100152150ustar00rootroot00000000000000/* $OpenBSD: iked.h,v 1.233 2024/11/04 02:44:28 dlg Exp $ */ /* * Copyright (c) 2019-2021 Tobias Heider * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "openbsd-compat.h" #include "types.h" #include "dh.h" #define MAXIMUM(a,b) (((a)>(b))?(a):(b)) #define MINIMUM(a,b) (((a)<(b))?(a):(b)) #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) #ifndef IKED_H #define IKED_H /* * Common IKEv1/IKEv2 header */ struct ike_header { uint64_t ike_ispi; /* Initiator cookie */ uint64_t ike_rspi; /* Responder cookie */ uint8_t ike_nextpayload; /* Next payload type */ uint8_t ike_version; /* Major/Minor version number */ uint8_t ike_exchange; /* Exchange type */ uint8_t ike_flags; /* Message options */ uint32_t ike_msgid; /* Message identifier */ uint32_t ike_length; /* Total message length */ } __packed; /* * Common daemon infrastructure, local imsg etc. */ struct imsgev { struct imsgbuf ibuf; void (*handler)(int, short, void *); struct event ev; struct privsep_proc *proc; void *data; short events; const char *name; }; #define IMSG_SIZE_CHECK(imsg, p) do { \ if (IMSG_DATA_SIZE(imsg) < sizeof(*p)) \ fatalx("bad length imsg received"); \ } while (0) #define IMSG_DATA_SIZE(imsg) ((imsg)->hdr.len - IMSG_HEADER_SIZE) #define IKED_ADDR_EQ(_a, _b) \ ((_a)->addr_mask == (_b)->addr_mask && \ sockaddr_cmp((struct sockaddr *)&(_a)->addr, \ (struct sockaddr *)&(_b)->addr, (_a)->addr_mask) == 0) #define IKED_ADDR_NEQ(_a, _b) \ ((_a)->addr_mask != (_b)->addr_mask || \ sockaddr_cmp((struct sockaddr *)&(_a)->addr, \ (struct sockaddr *)&(_b)->addr, (_a)->addr_mask) != 0) /* initially control.h */ struct control_sock { const char *cs_name; struct event cs_ev; struct event cs_evt; int cs_fd; int cs_restricted; void *cs_env; }; struct ctl_conn { TAILQ_ENTRY(ctl_conn) entry; uint8_t flags; #define CTL_CONN_NOTIFY 0x01 struct imsgev iev; uint32_t peerid; }; TAILQ_HEAD(ctl_connlist, ctl_conn); extern enum privsep_procid privsep_process; /* * Runtime structures */ struct iked_timer { struct event tmr_ev; struct iked *tmr_env; void (*tmr_cb)(struct iked *, void *); void *tmr_cbarg; }; struct iked_spi { uint64_t spi; uint8_t spi_size; uint8_t spi_protoid; }; struct iked_proposal { uint8_t prop_id; uint8_t prop_protoid; struct iked_spi prop_localspi; struct iked_spi prop_peerspi; struct iked_transform *prop_xforms; unsigned int prop_nxforms; TAILQ_ENTRY(iked_proposal) prop_entry; }; TAILQ_HEAD(iked_proposals, iked_proposal); struct iked_addr { int addr_af; struct sockaddr_storage addr; uint8_t addr_mask; int addr_net; in_port_t addr_port; }; struct iked_ts { struct iked_addr ts_addr; uint8_t ts_ipproto; TAILQ_ENTRY(iked_ts) ts_entry; }; TAILQ_HEAD(iked_tss, iked_ts); struct iked_flow { struct iked_addr flow_src; struct iked_addr flow_dst; unsigned int flow_dir; /* in/out */ int flow_rdomain; struct iked_addr flow_prenat; int flow_fixed; unsigned int flow_loaded; /* pfkey done */ uint8_t flow_saproto; uint8_t flow_ipproto; struct iked_addr *flow_local; /* outer source */ struct iked_addr *flow_peer; /* outer dest */ struct iked_sa *flow_ikesa; /* parent SA */ int flow_transport; RB_ENTRY(iked_flow) flow_node; TAILQ_ENTRY(iked_flow) flow_entry; }; RB_HEAD(iked_flows, iked_flow); TAILQ_HEAD(iked_saflows, iked_flow); struct iked_childsa { uint8_t csa_saproto; /* IPsec protocol */ unsigned int csa_dir; /* in/out */ uint64_t csa_peerspi; /* peer relation */ uint8_t csa_loaded; /* pfkey done */ uint8_t csa_rekey; /* will be deleted */ uint8_t csa_allocated; /* from the kernel */ uint8_t csa_persistent;/* do not rekey */ uint8_t csa_esn; /* use ESN */ uint8_t csa_transport; /* transport mode */ struct iked_spi csa_spi; struct ibuf *csa_encrkey; /* encryption key */ uint16_t csa_encrid; /* encryption xform id */ struct ibuf *csa_integrkey; /* auth key */ uint16_t csa_integrid; /* auth xform id */ struct iked_addr *csa_local; /* outer source */ struct iked_addr *csa_peer; /* outer dest */ struct iked_sa *csa_ikesa; /* parent SA */ struct iked_childsa *csa_peersa; /* peer */ struct iked_childsa *csa_bundled; /* IPCOMP */ uint16_t csa_pfsgrpid; /* pfs group id */ RB_ENTRY(iked_childsa) csa_node; TAILQ_ENTRY(iked_childsa) csa_entry; }; RB_HEAD(iked_activesas, iked_childsa); TAILQ_HEAD(iked_childsas, iked_childsa); struct iked_static_id { uint8_t id_type; uint8_t id_length; uint8_t id_offset; uint8_t id_data[IKED_ID_SIZE]; }; struct iked_auth { uint8_t auth_method; uint8_t auth_eap; /* optional EAP */ uint8_t auth_length; /* zero if EAP */ uint8_t auth_data[IKED_PSK_SIZE]; }; struct iked_cfg { uint8_t cfg_action; uint16_t cfg_type; union { struct iked_addr address; } cfg; }; TAILQ_HEAD(iked_sapeers, iked_sa); struct iked_lifetime { uint64_t lt_bytes; uint64_t lt_seconds; }; struct iked_policy { unsigned int pol_id; char pol_name[IKED_ID_SIZE]; unsigned int pol_iface; #define IKED_SKIP_FLAGS 0 #define IKED_SKIP_AF 1 #define IKED_SKIP_SRC_ADDR 2 #define IKED_SKIP_DST_ADDR 3 #define IKED_SKIP_COUNT 4 struct iked_policy *pol_skip[IKED_SKIP_COUNT]; unsigned int pol_flags; #define IKED_POLICY_PASSIVE 0x000 #define IKED_POLICY_DEFAULT 0x001 #define IKED_POLICY_ACTIVE 0x002 #define IKED_POLICY_REFCNT 0x004 #define IKED_POLICY_QUICK 0x008 #define IKED_POLICY_SKIP 0x010 #define IKED_POLICY_IPCOMP 0x020 #define IKED_POLICY_TRANSPORT 0x040 #define IKED_POLICY_ROUTING 0x080 #define IKED_POLICY_NATT_FORCE 0x100 int pol_refcnt; uint8_t pol_certreqtype; int pol_af; int pol_rdomain; uint8_t pol_saproto; unsigned int pol_ipproto[IKED_IPPROTO_MAX]; unsigned int pol_nipproto; struct iked_addr pol_peer; struct iked_static_id pol_peerid; uint32_t pol_peerdh; struct iked_addr pol_local; struct iked_static_id pol_localid; struct iked_auth pol_auth; char pol_tag[IKED_TAG_SIZE]; unsigned int pol_tap; struct iked_proposals pol_proposals; size_t pol_nproposals; struct iked_flows pol_flows; size_t pol_nflows; struct iked_tss pol_tssrc; /* Traffic Selectors Initiator*/ size_t pol_tssrc_count; struct iked_tss pol_tsdst; /* Traffic Selectors Responder*/ size_t pol_tsdst_count; struct iked_cfg pol_cfg[IKED_CFG_MAX]; unsigned int pol_ncfg; uint32_t pol_rekey; /* ike SA lifetime */ struct iked_lifetime pol_lifetime; /* child SA lifetime */ struct iked_sapeers pol_sapeers; TAILQ_ENTRY(iked_policy) pol_entry; }; TAILQ_HEAD(iked_policies, iked_policy); struct iked_hash { uint8_t hash_type; /* PRF or INTEGR */ uint16_t hash_id; /* IKE PRF/INTEGR hash id */ const void *hash_priv; /* Identifying the hash alg */ void *hash_ctx; /* Context of the current invocation */ int hash_fixedkey; /* Requires fixed key length */ struct ibuf *hash_key; /* MAC key derived from key seed */ size_t hash_length; /* Output length */ size_t hash_trunc; /* Truncate the output length */ struct iked_hash *hash_prf; /* PRF pointer */ int hash_isaead; }; struct iked_cipher { uint8_t encr_type; /* ENCR */ uint16_t encr_id; /* IKE ENCR hash id */ const void *encr_priv; /* Identifying the hash alg */ void *encr_ctx; /* Context of the current invocation */ int encr_fixedkey; /* Requires fixed key length */ struct ibuf *encr_key; /* MAC key derived from key seed */ struct ibuf *encr_iv; /* Initialization Vector */ uint64_t encr_civ; /* Counter IV for GCM */ size_t encr_ivlength; /* IV length */ size_t encr_length; /* Block length */ size_t encr_saltlength; /* IV salt length */ uint16_t encr_authid; /* ID of associated authentication */ }; struct iked_dsa { uint8_t dsa_method; /* AUTH method */ const void *dsa_priv; /* PRF or signature hash function */ void *dsa_ctx; /* PRF or signature hash ctx */ struct ibuf *dsa_keydata; /* public, private or shared key */ void *dsa_key; /* parsed public or private key */ int dsa_hmac; /* HMAC or public/private key */ int dsa_sign; /* Sign or verify operation */ uint32_t dsa_flags; /* State flags */ }; struct iked_id { uint8_t id_type; uint8_t id_offset; struct ibuf *id_buf; }; #define IKED_REQ_CERT 0x0001 /* get local certificate (if required) */ #define IKED_REQ_CERTVALID 0x0002 /* validated the peer cert */ #define IKED_REQ_CERTREQ 0x0004 /* CERTREQ has been received */ #define IKED_REQ_AUTH 0x0008 /* AUTH payload */ #define IKED_REQ_AUTHVALID 0x0010 /* AUTH payload has been verified */ #define IKED_REQ_SA 0x0020 /* SA available */ #define IKED_REQ_EAPVALID 0x0040 /* EAP payload has been verified */ #define IKED_REQ_CHILDSA 0x0080 /* Child SA initiated */ #define IKED_REQ_INF 0x0100 /* Informational exchange initiated */ #define IKED_REQ_BITS \ "\20\01CERT\02CERTVALID\03CERTREQ\04AUTH\05AUTHVALID\06SA\07EAPVALID" \ "\10CHILDSA\11INF" TAILQ_HEAD(iked_msgqueue, iked_msg_retransmit); TAILQ_HEAD(iked_msg_fragqueue, iked_message); struct iked_sahdr { uint64_t sh_ispi; /* Initiator SPI */ uint64_t sh_rspi; /* Responder SPI */ unsigned int sh_initiator; /* Is initiator? */ } __packed; struct iked_kex { struct ibuf *kex_inonce; /* Ni */ struct ibuf *kex_rnonce; /* Nr */ struct dh_group *kex_dhgroup; /* DH group */ struct ibuf *kex_dhiexchange; struct ibuf *kex_dhrexchange; struct ibuf *kex_dhpeer; /* pointer to i or r */ }; struct iked_frag_entry { uint8_t *frag_data; size_t frag_size; }; struct iked_frag { struct iked_frag_entry **frag_arr; /* list of fragment buffers */ size_t frag_count; /* number of fragments received */ #define IKED_FRAG_TOTAL_MAX 111 /* upper limit (64kB / 576B) */ size_t frag_total; /* total numbe of fragments */ size_t frag_total_size; uint8_t frag_nextpayload; }; struct iked_ipcomp { uint16_t ic_cpi_out; /* outgoing CPI */ uint16_t ic_cpi_in; /* incoming CPI */ uint8_t ic_transform; /* transform */ }; struct iked_sa { struct iked_sahdr sa_hdr; uint32_t sa_msgid; /* Last request rcvd */ int sa_msgid_set; /* msgid initialized */ uint32_t sa_msgid_current; /* Current requested rcvd */ uint32_t sa_reqid; /* Next request sent */ int sa_type; #define IKED_SATYPE_LOOKUP 0 /* Used for lookup */ #define IKED_SATYPE_LOCAL 1 /* Local SA */ struct iked_addr sa_peer; struct iked_addr sa_peer_loaded;/* MOBIKE */ struct iked_addr sa_local; int sa_fd; struct iked_frag sa_fragments; int sa_natt; /* for IKE messages */ int sa_udpencap; /* for pfkey */ int sa_usekeepalive;/* NAT-T keepalive */ int sa_state; unsigned int sa_stateflags; unsigned int sa_stateinit; /* SA_INIT */ unsigned int sa_statevalid; /* IKE_AUTH */ int sa_cp; /* XXX */ struct iked_addr *sa_cp_addr; /* requested address */ struct iked_addr *sa_cp_addr6; /* requested address */ struct iked_addr *sa_cp_dns; /* requested dns */ struct iked_policy *sa_policy; struct timeval sa_timecreated; struct timeval sa_timeused; char *sa_tag; const char *sa_reason; /* reason for close */ struct iked_kex sa_kex; /* XXX compat defines until everything is converted */ #define sa_inonce sa_kex.kex_inonce #define sa_rnonce sa_kex.kex_rnonce #define sa_dhgroup sa_kex.kex_dhgroup #define sa_dhiexchange sa_kex.kex_dhiexchange #define sa_dhrexchange sa_kex.kex_dhrexchange #define sa_dhpeer sa_kex.kex_dhpeer struct iked_hash *sa_prf; /* PRF alg */ struct iked_hash *sa_integr; /* integrity alg */ struct iked_cipher *sa_encr; /* encryption alg */ struct ibuf *sa_key_d; /* SK_d */ struct ibuf *sa_key_iauth; /* SK_ai */ struct ibuf *sa_key_rauth; /* SK_ar */ struct ibuf *sa_key_iencr; /* SK_ei */ struct ibuf *sa_key_rencr; /* SK_er */ struct ibuf *sa_key_iprf; /* SK_pi */ struct ibuf *sa_key_rprf; /* SK_pr */ struct ibuf *sa_1stmsg; /* for initiator AUTH */ struct ibuf *sa_2ndmsg; /* for responder AUTH */ struct iked_id sa_localauth; /* local AUTH message */ struct iked_id sa_peerauth; /* peer AUTH message */ int sa_sigsha2; /* use SHA2 for signatures */ #define IKED_SCERT_MAX 3 /* max # of supplemental cert payloads */ struct iked_id sa_iid; /* initiator id */ struct iked_id sa_rid; /* responder id */ struct iked_id sa_icert; /* initiator cert */ struct iked_id sa_rcert; /* responder cert */ struct iked_id sa_scert[IKED_SCERT_MAX]; /* supplemental certs */ #define IKESA_SRCID(x) ((x)->sa_hdr.sh_initiator ? &(x)->sa_iid : &(x)->sa_rid) #define IKESA_DSTID(x) ((x)->sa_hdr.sh_initiator ? &(x)->sa_rid : &(x)->sa_iid) char *sa_eapid; /* EAP identity */ struct iked_id sa_eap; /* EAP challenge */ struct ibuf *sa_eapmsk; /* EAK session key */ struct iked_proposals sa_proposals; /* SA proposals */ struct iked_childsas sa_childsas; /* IPsec Child SAs */ struct iked_saflows sa_flows; /* IPsec flows */ struct iked_sa *sa_nexti; /* initiated IKE SA */ struct iked_sa *sa_previ; /* matching back pointer */ struct iked_sa *sa_nextr; /* simultaneous rekey */ struct iked_sa *sa_prevr; /* matching back pointer */ uint64_t sa_rekeyspi; /* peerspi CSA rekey */ struct ibuf *sa_simult; /* simultaneous rekey */ struct iked_ipcomp sa_ipcompi; /* IPcomp initator */ struct iked_ipcomp sa_ipcompr; /* IPcomp responder */ int sa_mobike; /* MOBIKE */ int sa_frag; /* fragmentation */ int sa_use_transport_mode; /* peer requested */ int sa_used_transport_mode; /* we enabled */ struct iked_timer sa_timer; /* SA timeouts */ #define IKED_IKE_SA_EXCHANGE_TIMEOUT 300 /* 5 minutes */ #define IKED_IKE_SA_REKEY_TIMEOUT 120 /* 2 minutes */ #define IKED_IKE_SA_DELETE_TIMEOUT 120 /* 2 minutes */ #define IKED_IKE_SA_ALIVE_TIMEOUT 60 /* 1 minute */ struct iked_timer sa_keepalive; /* keepalive timer */ #define IKED_IKE_SA_KEEPALIVE_TIMEOUT 20 struct iked_timer sa_rekey; /* rekey timeout */ int sa_tmpfail; struct iked_msgqueue sa_requests; /* request queue */ #define IKED_RETRANSMIT_TIMEOUT 2 /* 2 seconds */ struct iked_msgqueue sa_responses; /* response queue */ #define IKED_RESPONSE_TIMEOUT 120 /* 2 minutes */ TAILQ_ENTRY(iked_sa) sa_peer_entry; RB_ENTRY(iked_sa) sa_entry; /* all SAs */ RB_ENTRY(iked_sa) sa_dstid_entry; /* SAs by DSTID */ int sa_dstid_entry_valid; /* sa_dstid_entry valid */ struct iked_addr *sa_addrpool; /* address from pool */ RB_ENTRY(iked_sa) sa_addrpool_entry; /* pool entries */ struct iked_addr *sa_addrpool6; /* address from pool */ RB_ENTRY(iked_sa) sa_addrpool6_entry; /* pool entries */ time_t sa_last_recvd; #define IKED_IKE_SA_LAST_RECVD_TIMEOUT 300 /* 5 minutes */ }; RB_HEAD(iked_sas, iked_sa); RB_HEAD(iked_dstid_sas, iked_sa); RB_HEAD(iked_addrpool, iked_sa); RB_HEAD(iked_addrpool6, iked_sa); /* stats */ struct iked_stats { uint64_t ikes_sa_created; uint64_t ikes_sa_established_total; uint64_t ikes_sa_established_current; /* gauge */ uint64_t ikes_sa_established_failures; uint64_t ikes_sa_proposals_negotiate_failures; uint64_t ikes_sa_rekeyed; uint64_t ikes_sa_removed; uint64_t ikes_csa_created; uint64_t ikes_csa_removed; uint64_t ikes_msg_sent; uint64_t ikes_msg_send_failures; uint64_t ikes_msg_rcvd; uint64_t ikes_msg_rcvd_busy; uint64_t ikes_msg_rcvd_dropped; uint64_t ikes_retransmit_request; uint64_t ikes_retransmit_response; uint64_t ikes_retransmit_limit; uint64_t ikes_frag_sent; uint64_t ikes_frag_send_failures; uint64_t ikes_frag_rcvd; uint64_t ikes_frag_rcvd_drop; uint64_t ikes_frag_reass_ok; uint64_t ikes_frag_reass_drop; uint64_t ikes_update_addresses_sent; uint64_t ikes_dpd_sent; uint64_t ikes_keepalive_sent; }; #define ikestat_add(env, c, n) do { env->sc_stats.c += (n); } while(0) #define ikestat_inc(env, c) ikestat_add(env, c, 1) #define ikestat_dec(env, c) ikestat_add(env, c, -1) struct iked_certreq { struct ibuf *cr_data; uint8_t cr_type; SIMPLEQ_ENTRY(iked_certreq) cr_entry; }; SIMPLEQ_HEAD(iked_certreqs, iked_certreq); #define EAP_STATE_IDENTITY (1) #define EAP_STATE_MSCHAPV2_CHALLENGE (2) #define EAP_STATE_MSCHAPV2_SUCCESS (3) #define EAP_STATE_SUCCESS (4) struct eap_msg { char *eam_identity; char *eam_user; int eam_type; uint8_t eam_id; uint8_t eam_msrid; int eam_success; int eam_found; int eam_response; uint8_t eam_challenge[16]; uint8_t eam_ntresponse[24]; uint32_t eam_state; }; struct iked_message { struct ibuf *msg_data; size_t msg_offset; struct sockaddr_storage msg_local; socklen_t msg_locallen; struct sockaddr_storage msg_peer; socklen_t msg_peerlen; struct iked_socket *msg_sock; int msg_fd; int msg_response; int msg_responded; int msg_valid; int msg_natt; int msg_natt_rcvd; int msg_nat_detected; int msg_error; int msg_e; struct iked_message *msg_parent; /* Associated policy and SA */ struct iked_policy *msg_policy; struct iked_sa *msg_sa; uint32_t msg_msgid; uint8_t msg_exchange; /* Parsed information */ struct iked_proposals msg_proposals; struct iked_certreqs msg_certreqs; struct iked_spi msg_rekey; struct ibuf *msg_nonce; /* dh NONCE */ uint16_t msg_dhgroup; /* dh group */ struct ibuf *msg_ke; /* dh key exchange */ struct iked_id msg_auth; /* AUTH payload */ struct iked_id msg_peerid; struct iked_id msg_localid; struct iked_id msg_cert; struct iked_id msg_scert[IKED_SCERT_MAX]; /* supplemental certs */ struct ibuf *msg_cookie; uint16_t msg_group; uint16_t msg_cpi; uint8_t msg_transform; uint16_t msg_flags; struct eap_msg msg_eap; size_t msg_del_spisize; size_t msg_del_cnt; struct ibuf *msg_del_buf; int msg_del_protoid; int msg_cp; struct iked_addr *msg_cp_addr; /* requested address */ struct iked_addr *msg_cp_addr6; /* requested address */ struct iked_addr *msg_cp_dns; /* requested dns */ uint16_t msg_frag_num; /* MOBIKE */ int msg_update_sa_addresses; struct ibuf *msg_cookie2; /* Parse stack */ struct iked_proposal *msg_prop; uint16_t msg_attrlength; /* Retransmit queue */ TAILQ_ENTRY(iked_message) msg_entry; }; struct iked_msg_retransmit { struct iked_msg_fragqueue mrt_frags; TAILQ_ENTRY(iked_msg_retransmit) mrt_entry; struct iked_timer mrt_timer; int mrt_tries; #define IKED_RETRANSMIT_TRIES 5 /* try 5 times */ }; #define IKED_MSG_NAT_SRC_IP 0x01 #define IKED_MSG_NAT_DST_IP 0x02 #define IKED_MSG_FLAGS_FRAGMENTATION 0x0001 #define IKED_MSG_FLAGS_MOBIKE 0x0002 #define IKED_MSG_FLAGS_SIGSHA2 0x0004 #define IKED_MSG_FLAGS_CHILD_SA_NOT_FOUND 0x0008 #define IKED_MSG_FLAGS_NO_ADDITIONAL_SAS 0x0010 #define IKED_MSG_FLAGS_AUTHENTICATION_FAILED 0x0020 #define IKED_MSG_FLAGS_INVALID_KE 0x0040 #define IKED_MSG_FLAGS_IPCOMP_SUPPORTED 0x0080 #define IKED_MSG_FLAGS_USE_TRANSPORT 0x0100 #define IKED_MSG_FLAGS_TEMPORARY_FAILURE 0x0200 #define IKED_MSG_FLAGS_NO_PROPOSAL_CHOSEN 0x0400 struct iked_user { char usr_name[LOGIN_NAME_MAX]; char usr_pass[IKED_PASSWORD_SIZE]; RB_ENTRY(iked_user) usr_entry; }; RB_HEAD(iked_users, iked_user); struct privsep_pipes { int *pp_pipes[PROC_MAX]; }; struct privsep { struct privsep_pipes *ps_pipes[PROC_MAX]; struct privsep_pipes *ps_pp; struct imsgev *ps_ievs[PROC_MAX]; const char *ps_title[PROC_MAX]; pid_t ps_pid[PROC_MAX]; struct passwd *ps_pw; int ps_noaction; struct control_sock ps_csock; unsigned int ps_instances[PROC_MAX]; unsigned int ps_ninstances; unsigned int ps_instance; /* Event and signal handlers */ struct event ps_evsigint; struct event ps_evsigterm; struct event ps_evsigchld; struct event ps_evsighup; struct event ps_evsigpipe; struct event ps_evsigusr1; struct iked *ps_env; unsigned int ps_connecting; void (*ps_connected)(struct privsep *); }; struct privsep_proc { const char *p_title; enum privsep_procid p_id; int (*p_cb)(int, struct privsep_proc *, struct imsg *); void (*p_init)(struct privsep *, struct privsep_proc *); const char *p_chroot; struct passwd *p_pw; struct privsep *p_ps; void (*p_shutdown)(void); }; struct privsep_fd { enum privsep_procid pf_procid; unsigned int pf_instance; }; #define PROC_PARENT_SOCK_FILENO 3 #define PROC_MAX_INSTANCES 32 struct iked_ocsp_entry { TAILQ_ENTRY(iked_ocsp_entry) ioe_entry; /* next request */ void *ioe_ocsp; /* private ocsp request data */ }; TAILQ_HEAD(iked_ocsp_requests, iked_ocsp_entry); /* * Daemon configuration */ enum natt_mode { NATT_DEFAULT, /* send/recv with both :500 and NAT-T port */ NATT_DISABLE, /* send/recv with only :500 */ NATT_FORCE, /* send/recv with only NAT-T port */ }; struct iked_static { uint64_t st_alive_timeout; int st_cert_partial_chain; int st_enforcesingleikesa; uint8_t st_frag; /* fragmentation */ uint8_t st_mobike; /* MOBIKE */ in_port_t st_nattport; int st_stickyaddress; /* addr per DSTID */ int st_vendorid; }; struct iked { char sc_conffile[PATH_MAX]; uint32_t sc_opts; enum natt_mode sc_nattmode; uint8_t sc_passive; uint8_t sc_decoupled; struct iked_static sc_static; #define sc_alive_timeout sc_static.st_alive_timeout #define sc_cert_partial_chain sc_static.st_cert_partial_chain #define sc_enforcesingleikesa sc_static.st_enforcesingleikesa #define sc_frag sc_static.st_frag #define sc_mobike sc_static.st_mobike #define sc_nattport sc_static.st_nattport #define sc_stickyaddress sc_static.st_stickyaddress #define sc_vendorid sc_static.st_vendorid struct iked_policies sc_policies; struct iked_policy *sc_defaultcon; struct iked_sas sc_sas; struct iked_dstid_sas sc_dstid_sas; struct iked_activesas sc_activesas; struct iked_flows sc_activeflows; struct iked_users sc_users; struct iked_stats sc_stats; void *sc_priv; /* per-process */ int sc_pfkey; /* ike process */ struct event sc_pfkeyev; struct event sc_routeev; uint8_t sc_certreqtype; struct ibuf *sc_certreq; void *sc_vroute; struct iked_socket *sc_sock4[2]; struct iked_socket *sc_sock6[2]; struct iked_timer sc_inittmr; #define IKED_INITIATOR_INITIAL 2 #define IKED_INITIATOR_INTERVAL 60 struct privsep sc_ps; struct iked_ocsp_requests sc_ocsp; char *sc_ocsp_url; long sc_ocsp_tolerate; long sc_ocsp_maxage; struct iked_addrpool sc_addrpool; struct iked_addrpool6 sc_addrpool6; #ifdef WITH_APPARMOR int sc_apparmor; #endif }; struct iked_socket { int sock_fd; struct event sock_ev; struct iked *sock_env; struct sockaddr_storage sock_addr; }; struct ipsec_xf { const char *name; unsigned int id; unsigned int length; unsigned int keylength; unsigned int nonce; unsigned int noauth; }; struct ipsec_transforms { const struct ipsec_xf **authxf; unsigned int nauthxf; const struct ipsec_xf **prfxf; unsigned int nprfxf; const struct ipsec_xf **encxf; unsigned int nencxf; const struct ipsec_xf **groupxf; unsigned int ngroupxf; const struct ipsec_xf **esnxf; unsigned int nesnxf; }; struct ipsec_mode { struct ipsec_transforms **xfs; unsigned int nxfs; }; /* iked.c */ void parent_reload(struct iked *, int, const char *); extern struct iked *iked_env; /* control.c */ void control(struct privsep *, struct privsep_proc *); int control_init(struct privsep *, struct control_sock *); int control_listen(struct control_sock *); /* config.c */ struct iked_policy * config_new_policy(struct iked *); void config_free_kex(struct iked_kex *); void config_free_fragments(struct iked_frag *); void config_free_sa(struct iked *, struct iked_sa *); struct iked_sa * config_new_sa(struct iked *, int); struct iked_user * config_new_user(struct iked *, struct iked_user *); uint64_t config_getspi(void); struct iked_transform * config_findtransform(struct iked_proposals *, uint8_t, unsigned int); struct iked_transform * config_findtransform_ext(struct iked_proposals *, uint8_t,int, unsigned int); void config_free_policy(struct iked *, struct iked_policy *); struct iked_proposal * config_add_proposal(struct iked_proposals *, unsigned int, unsigned int); void config_free_proposal(struct iked_proposals *, struct iked_proposal *); void config_free_proposals(struct iked_proposals *, unsigned int); void config_free_flows(struct iked *, struct iked_flows *); void config_free_childsas(struct iked *, struct iked_childsas *, struct iked_spi *, struct iked_spi *); int config_add_transform(struct iked_proposal *, unsigned int, unsigned int, unsigned int, unsigned int); int config_setcoupled(struct iked *, unsigned int); int config_getcoupled(struct iked *, unsigned int); int config_setmode(struct iked *, unsigned int); int config_getmode(struct iked *, unsigned int); int config_setreset(struct iked *, unsigned int, enum privsep_procid); int config_getreset(struct iked *, struct imsg *); int config_doreset(struct iked *, unsigned int); int config_setpolicy(struct iked *, struct iked_policy *, enum privsep_procid); int config_getpolicy(struct iked *, struct imsg *); int config_setflow(struct iked *, struct iked_policy *, enum privsep_procid); int config_getflow(struct iked *, struct imsg *); int config_setsocket(struct iked *, struct sockaddr_storage *, in_port_t, enum privsep_procid, int); int config_getsocket(struct iked *env, struct imsg *, void (*cb)(int, short, void *)); void config_enablesocket(struct iked *env); int config_setpfkey(struct iked *); int config_getpfkey(struct iked *, struct imsg *); int config_setuser(struct iked *, struct iked_user *, enum privsep_procid); int config_getuser(struct iked *, struct imsg *); int config_setcompile(struct iked *, enum privsep_procid); int config_getcompile(struct iked *); int config_setocsp(struct iked *); int config_getocsp(struct iked *, struct imsg *); int config_setkeys(struct iked *); int config_getkey(struct iked *, struct imsg *); int config_setstatic(struct iked *); int config_getstatic(struct iked *, struct imsg *); /* policy.c */ void policy_init(struct iked *); int policy_lookup(struct iked *, struct iked_message *, struct iked_proposals *, struct iked_flows *, int); int policy_lookup_sa(struct iked *, struct iked_sa *); struct iked_policy * policy_test(struct iked *, struct iked_policy *); int policy_generate_ts(struct iked_policy *); void policy_calc_skip_steps(struct iked_policies *); void policy_ref(struct iked *, struct iked_policy *); void policy_unref(struct iked *, struct iked_policy *); void sa_state(struct iked *, struct iked_sa *, int); void sa_stateflags(struct iked_sa *, unsigned int); int sa_stateok(const struct iked_sa *, int); struct iked_sa * sa_new(struct iked *, uint64_t, uint64_t, unsigned int, struct iked_policy *); void sa_free(struct iked *, struct iked_sa *); void sa_free_flows(struct iked *, struct iked_saflows *); int sa_configure_iface(struct iked *, struct iked_sa *, int); int sa_address(struct iked_sa *, struct iked_addr *, struct sockaddr *); void childsa_free(struct iked_childsa *); struct iked_childsa * childsa_lookup(struct iked_sa *, uint64_t, uint8_t); void flow_free(struct iked_flow *); int flow_equal(struct iked_flow *, struct iked_flow *); struct iked_sa * sa_lookup(struct iked *, uint64_t, uint64_t, unsigned int); struct iked_user * user_lookup(struct iked *, const char *); struct iked_sa * sa_dstid_lookup(struct iked *, struct iked_sa *); struct iked_sa * sa_dstid_insert(struct iked *, struct iked_sa *); void sa_dstid_remove(struct iked *, struct iked_sa *); int proposals_negotiate(struct iked_proposals *, struct iked_proposals *, struct iked_proposals *, int, int); RB_PROTOTYPE(iked_sas, iked_sa, sa_entry, sa_cmp); RB_PROTOTYPE(iked_dstid_sas, iked_sa, sa_dstid_entry, sa_dstid_cmp); RB_PROTOTYPE(iked_addrpool, iked_sa, sa_addrpool_entry, sa_addrpool_cmp); RB_PROTOTYPE(iked_addrpool6, iked_sa, sa_addrpool6_entry, sa_addrpool6_cmp); RB_PROTOTYPE(iked_users, iked_user, user_entry, user_cmp); RB_PROTOTYPE(iked_activesas, iked_childsa, csa_node, childsa_cmp); RB_PROTOTYPE(iked_flows, iked_flow, flow_node, flow_cmp); /* crypto.c */ struct iked_hash * hash_new(uint8_t, uint16_t); struct ibuf * hash_setkey(struct iked_hash *, void *, size_t); void hash_free(struct iked_hash *); void hash_init(struct iked_hash *); void hash_update(struct iked_hash *, void *, size_t); void hash_final(struct iked_hash *, void *, size_t *); size_t hash_keylength(struct iked_hash *); size_t hash_length(struct iked_hash *); struct iked_cipher * cipher_new(uint8_t, uint16_t, uint16_t); struct ibuf * cipher_setkey(struct iked_cipher *, const void *, size_t); struct ibuf * cipher_setiv(struct iked_cipher *, const void *, size_t); int cipher_settag(struct iked_cipher *, uint8_t *, size_t); int cipher_gettag(struct iked_cipher *, uint8_t *, size_t); void cipher_free(struct iked_cipher *); int cipher_init(struct iked_cipher *, int); int cipher_init_encrypt(struct iked_cipher *); int cipher_init_decrypt(struct iked_cipher *); void cipher_aad(struct iked_cipher *, const void *, size_t, size_t *); int cipher_update(struct iked_cipher *, const void *, size_t, void *, size_t *); int cipher_final(struct iked_cipher *); size_t cipher_length(struct iked_cipher *); size_t cipher_keylength(struct iked_cipher *); size_t cipher_ivlength(struct iked_cipher *); size_t cipher_outlength(struct iked_cipher *, size_t); struct iked_dsa * dsa_new(uint8_t, struct iked_hash *, int); struct iked_dsa * dsa_sign_new(uint8_t, struct iked_hash *); struct iked_dsa * dsa_verify_new(uint8_t, struct iked_hash *); struct ibuf * dsa_setkey(struct iked_dsa *, void *, size_t, uint8_t); void dsa_free(struct iked_dsa *); int dsa_init(struct iked_dsa *, const void *, size_t); size_t dsa_prefix(struct iked_dsa *); size_t dsa_length(struct iked_dsa *); int dsa_update(struct iked_dsa *, const void *, size_t); ssize_t dsa_sign_final(struct iked_dsa *, void *, size_t); ssize_t dsa_verify_final(struct iked_dsa *, void *, size_t); /* vroute.c */ void vroute_init(struct iked *); int vroute_setaddr(struct iked *, int, struct sockaddr *, int, unsigned int); void vroute_cleanup(struct iked *); int vroute_getaddr(struct iked *, struct imsg *); int vroute_setdns(struct iked *, int, struct sockaddr *, unsigned int); int vroute_getdns(struct iked *, struct imsg *); int vroute_setaddroute(struct iked *, uint8_t, struct sockaddr *, uint8_t, struct sockaddr *); int vroute_setcloneroute(struct iked *, uint8_t, struct sockaddr *, uint8_t, struct sockaddr *); int vroute_setdelroute(struct iked *, uint8_t, struct sockaddr *, uint8_t, struct sockaddr *); int vroute_getroute(struct iked *, struct imsg *); int vroute_getcloneroute(struct iked *, struct imsg *); /* ikev2.c */ void ikev2(struct privsep *, struct privsep_proc *); void ikev2_recv(struct iked *, struct iked_message *); void ikev2_init_ike_sa(struct iked *, void *); int ikev2_policy2id(struct iked_static_id *, struct iked_id *, int); int ikev2_childsa_enable(struct iked *, struct iked_sa *); int ikev2_childsa_delete(struct iked *, struct iked_sa *, uint8_t, uint64_t, uint64_t *, int); void ikev2_ikesa_recv_delete(struct iked *, struct iked_sa *); void ikev2_ike_sa_timeout(struct iked *env, void *); void ikev2_ike_sa_setreason(struct iked_sa *, char *); void ikev2_reset_alive_timer(struct iked *); int ikev2_ike_sa_delete(struct iked *, struct iked_sa *); struct ibuf * ikev2_prfplus(struct iked_hash *, struct ibuf *, struct ibuf *, size_t); ssize_t ikev2_psk(struct iked_sa *, uint8_t *, size_t, uint8_t **); ssize_t ikev2_nat_detection(struct iked *, struct iked_message *, void *, size_t, unsigned int, int); void ikev2_enable_natt(struct iked *, struct iked_sa *, struct iked_message *, int); int ikev2_send_informational(struct iked *, struct iked_message *); int ikev2_send_ike_e(struct iked *, struct iked_sa *, struct ibuf *, uint8_t, uint8_t, int); struct ike_header * ikev2_add_header(struct ibuf *, struct iked_sa *, uint32_t, uint8_t, uint8_t, uint8_t); int ikev2_set_header(struct ike_header *, size_t); struct ikev2_payload * ikev2_add_payload(struct ibuf *); int ikev2_next_payload(struct ikev2_payload *, size_t, uint8_t); int ikev2_child_sa_acquire(struct iked *, struct iked_flow *); int ikev2_child_sa_drop(struct iked *, struct iked_spi *); int ikev2_child_sa_rekey(struct iked *, struct iked_spi *); void ikev2_disable_rekeying(struct iked *, struct iked_sa *); int ikev2_print_id(struct iked_id *, char *, size_t); int ikev2_print_static_id(struct iked_static_id *, char *, size_t); const char *ikev2_ikesa_info(uint64_t, const char *msg); #define SPI_IH(hdr) ikev2_ikesa_info(betoh64((hdr)->ike_ispi), NULL) #define SPI_SH(sh, f) ikev2_ikesa_info((sh)->sh_ispi, (f)) #define SPI_SA(sa, f) SPI_SH(&(sa)->sa_hdr, (f)) /* ikev2_msg.c */ void ikev2_msg_cb(int, short, void *); struct ibuf * ikev2_msg_init(struct iked *, struct iked_message *, struct sockaddr_storage *, socklen_t, struct sockaddr_storage *, socklen_t, int); struct iked_message * ikev2_msg_copy(struct iked *, struct iked_message *); void ikev2_msg_cleanup(struct iked *, struct iked_message *); uint32_t ikev2_msg_id(struct iked *, struct iked_sa *); struct ibuf *ikev2_msg_auth(struct iked *, struct iked_sa *, int); int ikev2_msg_authsign(struct iked *, struct iked_sa *, struct iked_auth *, struct ibuf *); int ikev2_msg_authverify(struct iked *, struct iked_sa *, struct iked_auth *, uint8_t *, size_t, struct ibuf *); int ikev2_msg_valid_ike_sa(struct iked *, struct ike_header *, struct iked_message *); int ikev2_msg_send(struct iked *, struct iked_message *); int ikev2_msg_send_encrypt(struct iked *, struct iked_sa *, struct ibuf **, uint8_t, uint8_t, int); struct ibuf *ikev2_msg_encrypt(struct iked *, struct iked_sa *, struct ibuf *, struct ibuf *); struct ibuf * ikev2_msg_decrypt(struct iked *, struct iked_sa *, struct ibuf *, struct ibuf *); int ikev2_msg_integr(struct iked *, struct iked_sa *, struct ibuf *); int ikev2_msg_frompeer(struct iked_message *); struct iked_socket * ikev2_msg_getsocket(struct iked *, int, int); int ikev2_msg_enqueue(struct iked *, struct iked_msgqueue *, struct iked_message *, int); int ikev2_msg_retransmit_response(struct iked *, struct iked_sa *, struct iked_message *, struct ike_header *); void ikev2_msg_prevail(struct iked *, struct iked_msgqueue *, struct iked_message *); void ikev2_msg_dispose(struct iked *, struct iked_msgqueue *, struct iked_msg_retransmit *); void ikev2_msg_flushqueue(struct iked *, struct iked_msgqueue *); struct iked_msg_retransmit * ikev2_msg_lookup(struct iked *, struct iked_msgqueue *, struct iked_message *, uint8_t); /* ikev2_pld.c */ int ikev2_pld_parse(struct iked *, struct ike_header *, struct iked_message *, size_t); int ikev2_pld_parse_quick(struct iked *, struct ike_header *, struct iked_message *, size_t); /* eap.c */ int eap_parse(struct iked *, const struct iked_sa *, struct iked_message*, void *, int); int eap_success(struct iked *, struct iked_sa *, int); int eap_identity_request(struct iked *, struct iked_sa *); int eap_mschap_challenge(struct iked *, struct iked_sa *, int, int, uint8_t *, size_t); int eap_mschap_success(struct iked *, struct iked_sa *, int); int eap_challenge_request(struct iked *, struct iked_sa *, int); /* pfkey.c */ int pfkey_couple(struct iked *, struct iked_sas *, int); int pfkey_flow_add(struct iked *, struct iked_flow *); int pfkey_flow_delete(struct iked *, struct iked_flow *); int pfkey_sa_init(struct iked *, struct iked_childsa *, uint32_t *); int pfkey_sa_add(struct iked *, struct iked_childsa *, struct iked_childsa *); int pfkey_sa_update_addresses(struct iked *, struct iked_childsa *); int pfkey_sa_delete(struct iked *, struct iked_childsa *); int pfkey_sa_last_used(struct iked *, struct iked_childsa *, uint64_t *); int pfkey_flush(struct iked *); int pfkey_socket(struct iked *); void pfkey_init(struct iked *, int fd); /* ipsec.c */ int ipsec_couple(struct iked *, struct iked_sas *, int); int ipsec_flow_add(struct iked *, struct iked_flow *); int ipsec_flow_delete(struct iked *, struct iked_flow *); int ipsec_sa_init(struct iked *, struct iked_childsa *, uint32_t *); int ipsec_sa_add(struct iked *, struct iked_childsa *, struct iked_childsa *); int ipsec_sa_update_addresses(struct iked *, struct iked_childsa *); int ipsec_sa_delete(struct iked *, struct iked_childsa *); int ipsec_sa_last_used(struct iked *, struct iked_childsa *, uint64_t *); int ipsec_sa_rpl(struct iked *, struct iked_childsa *, uint32_t *); int ipsec_sa_lifetimes(struct iked *, struct iked_childsa *, struct iked_lifetime *, struct iked_lifetime *, struct iked_lifetime *); int ipsec_flush(struct iked *); int ipsec_socket(struct iked *); void ipsec_init(struct iked *, int fd); /* ca.c */ void caproc(struct privsep *, struct privsep_proc *); int ca_setreq(struct iked *, struct iked_sa *, struct iked_static_id *, uint8_t, uint8_t, uint8_t *, size_t, enum privsep_procid); int ca_setcert(struct iked *, struct iked_sahdr *, struct iked_id *, uint8_t, uint8_t *, size_t, enum privsep_procid); int ca_setauth(struct iked *, struct iked_sa *, struct ibuf *, enum privsep_procid); void ca_getkey(struct privsep *, struct iked_id *, enum imsg_type); int ca_certbundle_add(struct ibuf *, struct iked_id *); int ca_privkey_serialize(EVP_PKEY *, struct iked_id *); int ca_pubkey_serialize(EVP_PKEY *, struct iked_id *); void ca_sslinit(void); void ca_sslerror(const char *); char *ca_asn1_name(uint8_t *, size_t); void *ca_x509_name_parse(char *); void ca_cert_info(const char *, X509 *); /* timer.c */ void timer_set(struct iked *, struct iked_timer *, void (*)(struct iked *, void *), void *); void timer_add(struct iked *, struct iked_timer *, int); void timer_del(struct iked *, struct iked_timer *); /* proc.c */ void proc_init(struct privsep *, struct privsep_proc *, unsigned int, int, int, char **, enum privsep_procid); void proc_kill(struct privsep *); void proc_connect(struct privsep *, void (*)(struct privsep *)); void proc_dispatch(int, short event, void *); void proc_run(struct privsep *, struct privsep_proc *, struct privsep_proc *, unsigned int, void (*)(struct privsep *, struct privsep_proc *, void *), void *); void imsg_event_add(struct imsgev *); int imsg_compose_event(struct imsgev *, uint16_t, uint32_t, pid_t, int, void *, uint16_t); int imsg_composev_event(struct imsgev *, uint16_t, uint32_t, pid_t, int, const struct iovec *, int); int proc_compose_imsg(struct privsep *, enum privsep_procid, int, uint16_t, uint32_t, int, void *, uint16_t); int proc_compose(struct privsep *, enum privsep_procid, uint16_t, void *, uint16_t); int proc_composev_imsg(struct privsep *, enum privsep_procid, int, uint16_t, uint32_t, int, const struct iovec *, int); int proc_composev(struct privsep *, enum privsep_procid, uint16_t, const struct iovec *, int); int proc_forward_imsg(struct privsep *, struct imsg *, enum privsep_procid, int); struct imsgbuf * proc_ibuf(struct privsep *, enum privsep_procid, int); struct imsgev * proc_iev(struct privsep *, enum privsep_procid, int); enum privsep_procid proc_getid(struct privsep_proc *, unsigned int, const char *); int proc_flush_imsg(struct privsep *, enum privsep_procid, int); /* util.c */ int socket_af(struct sockaddr *, in_port_t); in_port_t socket_getport(struct sockaddr *); int socket_setport(struct sockaddr *, in_port_t); int socket_getaddr(int, struct sockaddr_storage *); int socket_bypass(int, struct sockaddr *); int udp_bind(struct sockaddr *, in_port_t); ssize_t sendtofrom(int, void *, size_t, int, struct sockaddr *, socklen_t, struct sockaddr *, socklen_t); ssize_t recvfromto(int, void *, size_t, int, struct sockaddr *, socklen_t *, struct sockaddr *, socklen_t *); const char * print_spi(uint64_t, int); const char * print_map(unsigned int, struct iked_constmap *); void lc_idtype(char *); void print_hex(const uint8_t *, off_t, size_t); void print_hexval(const uint8_t *, off_t, size_t); void print_hexbuf(struct ibuf *); const char * print_bits(unsigned short, unsigned char *); int sockaddr_cmp(struct sockaddr *, struct sockaddr *, int); uint8_t mask2prefixlen(struct sockaddr *); uint8_t mask2prefixlen6(struct sockaddr *); struct in6_addr * prefixlen2mask6(uint8_t, uint32_t *); uint32_t prefixlen2mask(uint8_t); const char * print_addr(void *); char *get_string(uint8_t *, size_t); const char * print_proto(uint8_t); int expand_string(char *, size_t, const char *, const char *); uint8_t *string2unicode(const char *, size_t *); void print_debug(const char *, ...) __attribute__((format(printf, 1, 2))); void print_verbose(const char *, ...) __attribute__((format(printf, 1, 2))); /* imsg_util.c */ struct ibuf * ibuf_new(const void *, size_t); struct ibuf * ibuf_static(void); size_t ibuf_length(struct ibuf *); int ibuf_setsize(struct ibuf *, size_t); struct ibuf * ibuf_getdata(struct ibuf *, size_t); struct ibuf * ibuf_dup(struct ibuf *); struct ibuf * ibuf_random(size_t); /* log.c */ void log_init(int, int); void log_procinit(const char *); void log_setverbose(int); int log_getverbose(void); void log_warn(const char *, ...) __attribute__((__format__ (printf, 1, 2))); void log_warnx(const char *, ...) __attribute__((__format__ (printf, 1, 2))); void log_info(const char *, ...) __attribute__((__format__ (printf, 1, 2))); void log_debug(const char *, ...) __attribute__((__format__ (printf, 1, 2))); void logit(int, const char *, ...) __attribute__((__format__ (printf, 2, 3))); void vlog(int, const char *, va_list) __attribute__((__format__ (printf, 2, 0))); __dead void fatal(const char *, ...) __attribute__((__format__ (printf, 1, 2))); __dead void fatalx(const char *, ...) __attribute__((__format__ (printf, 1, 2))); /* ocsp.c */ int ocsp_connect(struct iked *, struct imsg *); int ocsp_receive_fd(struct iked *, struct imsg *); int ocsp_validate_cert(struct iked *, void *, size_t, struct iked_sahdr, uint8_t, X509 *); /* parse.y */ int parse_config(const char *, struct iked *); int cmdline_symset(char *); extern const struct ipsec_xf authxfs[]; extern const struct ipsec_xf prfxfs[]; extern const struct ipsec_xf *encxfs; extern const struct ipsec_xf ikeencxfs[]; extern const struct ipsec_xf ipsecencxfs[]; extern const struct ipsec_xf groupxfs[]; extern const struct ipsec_xf esnxfs[]; extern const struct ipsec_xf methodxfs[]; extern const struct ipsec_xf saxfs[]; extern const struct ipsec_xf cpxfs[]; size_t keylength_xf(unsigned int, unsigned int, unsigned int); size_t noncelength_xf(unsigned int, unsigned int); int encxf_noauth(unsigned int); /* print.c */ void print_user(struct iked_user *); void print_policy(struct iked_policy *); const char *print_xf(unsigned int, unsigned int, const struct ipsec_xf *); #endif /* IKED_H */ openiked-7.4/iked/ikev2.c000066400000000000000000006345521477554731100153170ustar00rootroot00000000000000/* $OpenBSD: ikev2.c,v 1.391 2025/03/13 17:49:37 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider * Copyright (c) 2016 Marcel Moolenaar * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" #include "ikev2.h" #include "eap.h" #include "dh.h" #include "chap_ms.h" #include "version.h" #ifdef WITH_APPARMOR #include "apparmor.h" #endif void ikev2_info(struct iked *, struct imsg *, int); void ikev2_info_sa(struct iked *, struct imsg *, int, const char *, struct iked_sa *); void ikev2_info_csa(struct iked *, struct imsg *, int, const char *, struct iked_childsa *); void ikev2_info_flow(struct iked *, struct imsg *, int, const char *, struct iked_flow *); void ikev2_log_established(struct iked_sa *); void ikev2_log_proposal(struct iked_sa *, struct iked_proposals *); void ikev2_log_cert_info(const char *, struct iked_id *); void ikev2_run(struct privsep *, struct privsep_proc *, void *); void ikev2_shutdown(void); int ikev2_dispatch_parent(int, struct privsep_proc *, struct imsg *); int ikev2_dispatch_cert(int, struct privsep_proc *, struct imsg *); int ikev2_dispatch_control(int, struct privsep_proc *, struct imsg *); struct iked_sa * ikev2_getimsgdata(struct iked *, struct imsg *, struct iked_sahdr *, uint8_t *, uint8_t **, size_t *); int ikev2_ike_auth_compatible(struct iked_sa *, uint8_t, uint8_t); int ikev2_ike_auth_recv(struct iked *, struct iked_sa *, struct iked_message *); int ikev2_ike_auth(struct iked *, struct iked_sa *); int ikev2_auth_verify(struct iked *, struct iked_sa *); void ikev2_init_recv(struct iked *, struct iked_message *, struct ike_header *); void ikev2_init_ike_sa_timeout(struct iked *, void *); int ikev2_init_ike_sa_peer(struct iked *, struct iked_policy *, struct iked_addr *, struct iked_message *); int ikev2_init_ike_auth(struct iked *, struct iked_sa *); int ikev2_init_auth(struct iked *, struct iked_message *); int ikev2_init_done(struct iked *, struct iked_sa *); int ikev2_record_dstid(struct iked *, struct iked_sa *); void ikev2_enable_timer(struct iked *, struct iked_sa *); void ikev2_disable_timer(struct iked *, struct iked_sa *); void ikev2_resp_recv(struct iked *, struct iked_message *, struct ike_header *); int ikev2_resp_ike_sa_init(struct iked *, struct iked_message *); int ikev2_resp_ike_eap(struct iked *, struct iked_sa *, struct iked_message *); int ikev2_resp_ike_eap_mschap(struct iked *, struct iked_sa *, struct iked_message *); int ikev2_resp_ike_auth(struct iked *, struct iked_sa *); int ikev2_send_auth_failed(struct iked *, struct iked_sa *); int ikev2_send_error(struct iked *, struct iked_sa *, struct iked_message *, uint8_t); int ikev2_send_init_error(struct iked *, struct iked_message *); int ikev2_handle_certreq(struct iked*, struct iked_message *); ssize_t ikev2_handle_delete(struct iked *, struct iked_message *, struct ibuf *, struct ikev2_payload **, uint8_t *); int ikev2_send_create_child_sa(struct iked *, struct iked_sa *, struct iked_spi *, uint8_t, uint16_t); int ikev2_ikesa_enable(struct iked *, struct iked_sa *, struct iked_sa *); void ikev2_ikesa_delete(struct iked *, struct iked_sa *, int); int ikev2_nonce_cmp(struct ibuf *, struct ibuf *); int ikev2_init_create_child_sa(struct iked *, struct iked_message *); int ikev2_resp_create_child_sa(struct iked *, struct iked_message *); void ikev2_ike_sa_rekey(struct iked *, void *); void ikev2_ike_sa_rekey_timeout(struct iked *, void *); void ikev2_ike_sa_rekey_schedule(struct iked *, struct iked_sa *); void ikev2_ike_sa_rekey_schedule_fast(struct iked *, struct iked_sa *); void ikev2_ike_sa_alive(struct iked *, void *); void ikev2_ike_sa_keepalive(struct iked *, void *); int ikev2_sa_negotiate_common(struct iked *, struct iked_sa *, struct iked_message *, int); int ikev2_sa_initiator(struct iked *, struct iked_sa *, struct iked_sa *, struct iked_message *); int ikev2_sa_responder(struct iked *, struct iked_sa *, struct iked_sa *, struct iked_message *); int ikev2_sa_initiator_dh(struct iked_sa *, struct iked_message *, unsigned int, struct iked_sa *); int ikev2_sa_responder_dh(struct iked_kex *, struct iked_proposals *, struct iked_message *, unsigned int); void ikev2_sa_cleanup_dh(struct iked_sa *); int ikev2_sa_keys(struct iked *, struct iked_sa *, struct ibuf *); int ikev2_sa_tag(struct iked_sa *, struct iked_id *); int ikev2_set_sa_proposal(struct iked_sa *, struct iked_policy *, unsigned int); int ikev2_childsa_negotiate(struct iked *, struct iked_sa *, struct iked_kex *, struct iked_proposals *, int, int); int ikev2_childsa_delete_proposed(struct iked *, struct iked_sa *, struct iked_proposals *); int ikev2_valid_proposal(struct iked_proposal *, struct iked_transform **, struct iked_transform **, int *); int ikev2_handle_notifies(struct iked *, struct iked_message *); ssize_t ikev2_add_proposals(struct iked *, struct iked_sa *, struct ibuf *, struct iked_proposals *, uint8_t, int, int, int); ssize_t ikev2_add_cp(struct iked *, struct iked_sa *, int, struct ibuf *); ssize_t ikev2_init_add_cp(struct iked *, struct iked_sa *, struct ibuf *); ssize_t ikev2_resp_add_cp(struct iked *, struct iked_sa *, struct ibuf *); ssize_t ikev2_add_transform(struct ibuf *, uint8_t, uint8_t, uint16_t, uint16_t); ssize_t ikev2_add_ts(struct ibuf *, struct ikev2_payload **, ssize_t, struct iked_sa *, int); ssize_t ikev2_add_certreq(struct ibuf *, struct ikev2_payload **, ssize_t, struct ibuf *, uint8_t); ssize_t ikev2_add_ipcompnotify(struct iked *, struct ibuf *, struct ikev2_payload **, ssize_t, struct iked_sa *, int); ssize_t ikev2_add_ts_payload(struct ibuf *, unsigned int, struct iked_sa *); ssize_t ikev2_add_error(struct iked *, struct ibuf *, struct iked_message *); int ikev2_add_data(struct ibuf *, void *, size_t); int ikev2_add_buf(struct ibuf *buf, struct ibuf *); int ikev2_cp_setaddr(struct iked *, struct iked_sa *, sa_family_t); int ikev2_cp_setaddr_pool(struct iked *, struct iked_sa *, struct iked_cfg *, const char **, sa_family_t); int ikev2_cp_fixaddr(struct iked_sa *, struct iked_addr *, struct iked_addr *); int ikev2_cp_fixflow(struct iked_sa *, struct iked_flow *, struct iked_flow *); int ikev2_cp_request_configured(struct iked_sa *); ssize_t ikev2_add_sighashnotify(struct ibuf *, struct ikev2_payload **, ssize_t); ssize_t ikev2_add_nat_detection(struct iked *, struct ibuf *, struct ikev2_payload **, struct iked_message *, ssize_t); ssize_t ikev2_add_vendor_id(struct ibuf *, struct ikev2_payload **, ssize_t, struct ibuf *); ssize_t ikev2_add_notify(struct ibuf *, struct ikev2_payload **, ssize_t, uint16_t); ssize_t ikev2_add_mobike(struct ibuf *, struct ikev2_payload **, ssize_t); ssize_t ikev2_add_fragmentation(struct ibuf *, struct ikev2_payload **, ssize_t); ssize_t ikev2_add_transport_mode(struct iked *, struct ibuf *, struct ikev2_payload **, ssize_t, struct iked_sa *); int ikev2_update_sa_addresses(struct iked *, struct iked_sa *); int ikev2_resp_informational(struct iked *, struct iked_sa *, struct iked_message *); void ikev2_ctl_reset_id(struct iked *, struct imsg *, unsigned int); void ikev2_ctl_show_sa(struct iked *, struct imsg *); void ikev2_ctl_show_stats(struct iked *, struct imsg *); static struct privsep_proc procs[] = { { "parent", PROC_PARENT, ikev2_dispatch_parent }, { "certstore", PROC_CERT, ikev2_dispatch_cert }, { "control", PROC_CONTROL, ikev2_dispatch_control } }; void ikev2(struct privsep *ps, struct privsep_proc *p) { proc_run(ps, p, procs, nitems(procs), ikev2_run, NULL); } void ikev2_run(struct privsep *ps, struct privsep_proc *p, void *arg) { /* * pledge in the ikev2 process: * stdio - for malloc and basic I/O including events. * inet - for sendto with specified peer address. * recvfd - for PFKEYv2 and the listening UDP sockets. * In theory, recvfd could be dropped after getting the fds once. */ p->p_shutdown = ikev2_shutdown; if (pledge("stdio inet recvfd", NULL) == -1) fatal("pledge"); #ifdef WITH_APPARMOR if (armor_change_profile(ps->ps_env->sc_apparmor, "iked//ikev2") == -1) log_warnx("warning: armor_change_profile " "(\"iked//ikev2\") failed"); #endif } void ikev2_shutdown(void) { struct iked *env = iked_env; ibuf_free(env->sc_certreq); env->sc_certreq = NULL; config_doreset(env, RESET_ALL); } int ikev2_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) { struct iked *env = iked_env; struct iked_sa *sa, *satmp; struct iked_policy *pol, *old; switch (imsg->hdr.type) { case IMSG_CTL_RESET: return (config_getreset(env, imsg)); case IMSG_CTL_COUPLE: case IMSG_CTL_DECOUPLE: return (config_getcoupled(env, imsg->hdr.type)); case IMSG_CTL_ACTIVE: case IMSG_CTL_PASSIVE: if (config_getmode(env, imsg->hdr.type) == -1) return (0); /* ignore error */ config_enablesocket(env); timer_del(env, &env->sc_inittmr); TAILQ_FOREACH(pol, &env->sc_policies, pol_entry) { if (policy_generate_ts(pol) == -1) fatalx("%s: too many traffic selectors", __func__); } /* Find new policies for dangling SAs */ RB_FOREACH_SAFE(sa, iked_sas, &env->sc_sas, satmp) { if (sa->sa_state != IKEV2_STATE_ESTABLISHED) { sa_state(env, sa, IKEV2_STATE_CLOSING); ikev2_ike_sa_setreason(sa, "reload"); sa_free(env, sa); continue; } old = sa->sa_policy; if (policy_lookup_sa(env, sa) == -1) { log_info("%s: No matching Policy found, terminating SA.", SPI_SA(sa, __func__)); ikev2_ike_sa_setreason(sa, "Policy no longer exists"); ikev2_ikesa_delete(env, sa, sa->sa_hdr.sh_initiator); } if (old != sa->sa_policy) { /* Cleanup old policy */ TAILQ_REMOVE(&old->pol_sapeers, sa, sa_peer_entry); policy_unref(env, old); policy_ref(env, sa->sa_policy); TAILQ_INSERT_TAIL(&sa->sa_policy->pol_sapeers, sa, sa_peer_entry); } } if (!env->sc_passive) { timer_set(env, &env->sc_inittmr, ikev2_init_ike_sa, NULL); timer_add(env, &env->sc_inittmr, IKED_INITIATOR_INITIAL); } return (0); case IMSG_UDP_SOCKET: return (config_getsocket(env, imsg, ikev2_msg_cb)); case IMSG_PFKEY_SOCKET: return (config_getpfkey(env, imsg)); case IMSG_CFG_POLICY: return (config_getpolicy(env, imsg)); case IMSG_CFG_FLOW: return (config_getflow(env, imsg)); case IMSG_CFG_USER: return (config_getuser(env, imsg)); case IMSG_COMPILE: return (config_getcompile(env)); case IMSG_CTL_STATIC: return (config_getstatic(env, imsg)); default: break; } return (-1); } int ikev2_dispatch_cert(int fd, struct privsep_proc *p, struct imsg *imsg) { struct iked *env = iked_env; struct iked_sahdr sh; struct iked_sa *sa; uint8_t type; uint8_t *ptr; size_t len; struct iked_id *id = NULL; int ignore = 0; int i; switch (imsg->hdr.type) { case IMSG_CERTREQ: IMSG_SIZE_CHECK(imsg, &type); ptr = imsg->data; memcpy(&type, ptr, sizeof(type)); ptr += sizeof(type); ibuf_free(env->sc_certreq); env->sc_certreqtype = type; env->sc_certreq = ibuf_new(ptr, IMSG_DATA_SIZE(imsg) - sizeof(type)); log_debug("%s: updated local CERTREQ type %s length %zu", __func__, print_map(type, ikev2_cert_map), ibuf_length(env->sc_certreq)); break; case IMSG_CERTVALID: case IMSG_CERTINVALID: /* Ignore invalid or unauthenticated SAs */ if ((sa = ikev2_getimsgdata(env, imsg, &sh, &type, &ptr, &len)) == NULL || sa->sa_state < IKEV2_STATE_EAP) break; if (sh.sh_initiator) id = &sa->sa_rcert; else id = &sa->sa_icert; id->id_type = type; id->id_offset = 0; ibuf_free(id->id_buf); id->id_buf = NULL; if (len > 0 && (id->id_buf = ibuf_new(ptr, len)) == NULL) { log_debug("%s: failed to get cert payload", __func__); break; } if (imsg->hdr.type == IMSG_CERTVALID) { if (sa->sa_peerauth.id_type && ikev2_auth_verify(env, sa)) break; log_debug("%s: peer certificate is valid", __func__); sa_stateflags(sa, IKED_REQ_CERTVALID); if (ikev2_ike_auth(env, sa) != 0) log_debug("%s: failed to send ike auth", __func__); } else { log_warnx("%s: peer certificate is invalid", SPI_SA(sa, __func__)); ikev2_send_auth_failed(env, sa); } break; case IMSG_CERT: if ((sa = ikev2_getimsgdata(env, imsg, &sh, &type, &ptr, &len)) == NULL) { log_debug("%s: invalid cert reply", __func__); break; } /* * Ignore the message if we already got a valid certificate. * This might happen if the peer sent multiple CERTREQs. */ if (sa->sa_stateflags & IKED_REQ_CERT || type == IKEV2_CERT_NONE) ignore = 1; log_debug("%s: cert type %s length %zu, %s", __func__, print_map(type, ikev2_cert_map), len, ignore ? "ignored" : "ok"); if (ignore) break; if (sh.sh_initiator) id = &sa->sa_icert; else id = &sa->sa_rcert; id->id_type = type; id->id_offset = 0; ibuf_free(id->id_buf); id->id_buf = NULL; if (len <= 0 || (id->id_buf = ibuf_new(ptr, len)) == NULL) { log_debug("%s: failed to get cert payload", __func__); break; } sa_stateflags(sa, IKED_REQ_CERT); if (ikev2_ike_auth(env, sa) != 0) log_debug("%s: failed to send ike auth", __func__); break; case IMSG_SCERT: if ((sa = ikev2_getimsgdata(env, imsg, &sh, &type, &ptr, &len)) == NULL) { log_debug("%s: invalid supplemental cert reply", __func__); break; } if (sa->sa_stateflags & IKED_REQ_CERT || type == IKEV2_CERT_NONE) ignore = 1; log_debug("%s: supplemental cert type %s length %zu, %s", __func__, print_map(type, ikev2_cert_map), len, ignore ? "ignored" : "ok"); if (ignore) break; for (i = 0; i < IKED_SCERT_MAX; i++) { id = &sa->sa_scert[i]; if (id->id_type == IKEV2_CERT_NONE) break; id = NULL; } if (id == NULL) { log_debug("%s: too many supplemental cert. ignored", __func__); break; } id->id_type = type; id->id_offset = 0; ibuf_free(id->id_buf); id->id_buf = NULL; if (len <= 0 || (id->id_buf = ibuf_new(ptr, len)) == NULL) { log_debug("%s: failed to get supplemental cert payload", __func__); break; } break; case IMSG_AUTH: if ((sa = ikev2_getimsgdata(env, imsg, &sh, &type, &ptr, &len)) == NULL) { log_debug("%s: invalid auth reply", __func__); break; } if (sa_stateok(sa, IKEV2_STATE_VALID)) { log_warnx("%s: ignoring AUTH in state %s", SPI_SA(sa, __func__), print_map(sa->sa_state, ikev2_state_map)); break; } log_debug("%s: AUTH type %d len %zu", __func__, type, len); id = &sa->sa_localauth; id->id_type = type; id->id_offset = 0; ibuf_free(id->id_buf); id->id_buf = NULL; if (type != IKEV2_AUTH_NONE) { if (len <= 0 || (id->id_buf = ibuf_new(ptr, len)) == NULL) { log_debug("%s: failed to get auth payload", __func__); break; } } sa_stateflags(sa, IKED_REQ_AUTH); if (ikev2_ike_auth(env, sa) != 0) log_debug("%s: failed to send ike auth", __func__); break; default: return (-1); } return (0); } int ikev2_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg) { struct iked *env = iked_env; switch (imsg->hdr.type) { case IMSG_CTL_RESET_ID: ikev2_ctl_reset_id(env, imsg, imsg->hdr.type); break; case IMSG_CTL_SHOW_SA: ikev2_ctl_show_sa(env, imsg); break; case IMSG_CTL_SHOW_STATS: ikev2_ctl_show_stats(env, imsg); break; default: return (-1); } return (0); } /* try to delete established SA if no other exchange is active */ int ikev2_ike_sa_delete(struct iked *env, struct iked_sa *sa) { if (sa->sa_state != IKEV2_STATE_ESTABLISHED) return (-1); if (sa->sa_stateflags & (IKED_REQ_CHILDSA|IKED_REQ_INF)) return (-1); ikev2_disable_timer(env, sa); ikev2_ike_sa_setreason(sa, "reset sa control message"); ikev2_ikesa_delete(env, sa, 1); timer_add(env, &sa->sa_timer, 0); return (0); } void ikev2_ctl_reset_id(struct iked *env, struct imsg *imsg, unsigned int type) { struct iked_sa *sa; char *reset_id = NULL; char sa_id[IKED_ID_SIZE]; if ((reset_id = get_string(imsg->data, IMSG_DATA_SIZE(imsg))) == NULL) return; log_debug("%s: %s %d", __func__, reset_id, type); RB_FOREACH(sa, iked_sas, &env->sc_sas) { if (ikev2_print_id(IKESA_DSTID(sa), sa_id, sizeof(sa_id)) == -1) continue; if (strcmp(reset_id, sa_id) != 0) continue; if (sa->sa_state == IKEV2_STATE_CLOSED) continue; if (sa->sa_state == IKEV2_STATE_ESTABLISHED) ikev2_disable_timer(env, sa); log_info("%s: IKE SA %p id %s ispi %s rspi %s", __func__, sa, sa_id, print_spi(sa->sa_hdr.sh_ispi, 8), print_spi(sa->sa_hdr.sh_rspi, 8)); ikev2_ike_sa_setreason(sa, "reset control message"); ikev2_ikesa_delete(env, sa, 1); /* default IKED_IKE_SA_DELETE_TIMEOUT is 120s, so switch to 6s */ timer_add(env, &sa->sa_timer, 3 * IKED_RETRANSMIT_TIMEOUT); } free(reset_id); } void ikev2_ctl_show_sa(struct iked *env, struct imsg *imsg) { ikev2_info(env, imsg, 0); } void ikev2_ctl_show_stats(struct iked *env, struct imsg *imsg) { proc_compose_imsg(&env->sc_ps, PROC_CONTROL, -1, IMSG_CTL_SHOW_STATS, imsg->hdr.peerid, -1, &env->sc_stats, sizeof(env->sc_stats)); } struct iked_sa * ikev2_getimsgdata(struct iked *env, struct imsg *imsg, struct iked_sahdr *sh, uint8_t *type, uint8_t **buf, size_t *size) { uint8_t *ptr; size_t len; struct iked_sa *sa; ptr = imsg->data; len = IMSG_DATA_SIZE(imsg); if (len < sizeof(*sh)) fatalx("ikev2_getimsgdata: length too small for sh"); memcpy(sh, ptr, sizeof(*sh)); len -= sizeof(*sh); ptr += sizeof(*sh); if (len < sizeof(*type)) fatalx("ikev2_getimsgdata: length too small for type"); memcpy(type, ptr, sizeof(*type)); len -= sizeof(*type); ptr += sizeof(*type); sa = sa_lookup(env, sh->sh_ispi, sh->sh_rspi, sh->sh_initiator); log_debug("%s: imsg %d rspi %s ispi %s initiator %d sa %s" " type %d data length %zd", __func__, imsg->hdr.type, print_spi(sh->sh_rspi, 8), print_spi(sh->sh_ispi, 8), sh->sh_initiator, sa == NULL ? "invalid" : "valid", *type, len); if (sa == NULL) return (NULL); *buf = ptr; *size = len; return (sa); } static time_t gettime(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec; } void ikev2_recv(struct iked *env, struct iked_message *msg) { struct ike_header *hdr; struct iked_sa *sa; struct iked_msg_retransmit *mr; unsigned int initiator, flag = 0; int r; hdr = ibuf_seek(msg->msg_data, msg->msg_offset, sizeof(*hdr)); if (hdr == NULL || ibuf_size(msg->msg_data) < (betoh32(hdr->ike_length) - msg->msg_offset)) return; ikestat_inc(env, ikes_msg_rcvd); initiator = (hdr->ike_flags & IKEV2_FLAG_INITIATOR) ? 0 : 1; msg->msg_response = (hdr->ike_flags & IKEV2_FLAG_RESPONSE) ? 1 : 0; msg->msg_exchange = hdr->ike_exchange; msg->msg_sa = sa_lookup(env, betoh64(hdr->ike_ispi), betoh64(hdr->ike_rspi), initiator); msg->msg_msgid = betoh32(hdr->ike_msgid); if (policy_lookup(env, msg, NULL, NULL, 0) != 0) { log_debug("%s: no compatible policy found", __func__); ikestat_inc(env, ikes_msg_rcvd_dropped); return; } logit(hdr->ike_exchange == IKEV2_EXCHANGE_INFORMATIONAL ? LOG_DEBUG : LOG_INFO, "%srecv %s %s %u peer %s local %s, %zu bytes, policy '%s'", SPI_IH(hdr), print_map(hdr->ike_exchange, ikev2_exchange_map), msg->msg_response ? "res" : "req", msg->msg_msgid, print_addr(&msg->msg_peer), print_addr(&msg->msg_local), ibuf_size(msg->msg_data), msg->msg_policy->pol_name); log_debug("%s: ispi %s rspi %s", __func__, print_spi(betoh64(hdr->ike_ispi), 8), print_spi(betoh64(hdr->ike_rspi), 8)); if ((sa = msg->msg_sa) == NULL) goto done; sa->sa_last_recvd = gettime(); if (hdr->ike_exchange == IKEV2_EXCHANGE_CREATE_CHILD_SA) flag = IKED_REQ_CHILDSA; if (hdr->ike_exchange == IKEV2_EXCHANGE_INFORMATIONAL) flag = IKED_REQ_INF; if (hdr->ike_exchange != IKEV2_EXCHANGE_IKE_SA_INIT && hdr->ike_nextpayload != IKEV2_PAYLOAD_SK && hdr->ike_nextpayload != IKEV2_PAYLOAD_SKF) { ikestat_inc(env, ikes_msg_rcvd_dropped); return; } if (msg->msg_response) { if (msg->msg_msgid > sa->sa_reqid) { ikestat_inc(env, ikes_msg_rcvd_dropped); return; } mr = ikev2_msg_lookup(env, &sa->sa_requests, msg, hdr->ike_exchange); if (hdr->ike_exchange != IKEV2_EXCHANGE_INFORMATIONAL && mr == NULL && sa->sa_fragments.frag_count == 0) { ikestat_inc(env, ikes_msg_rcvd_dropped); return; } if (flag) { if ((sa->sa_stateflags & flag) == 0) { ikestat_inc(env, ikes_msg_rcvd_dropped); return; } /* * We have initiated this exchange, even if * we are not the initiator of the IKE SA. */ initiator = 1; } /* * There's no need to keep the request (fragments) around */ if (mr != NULL && hdr->ike_nextpayload != IKEV2_PAYLOAD_SKF) ikev2_msg_dispose(env, &sa->sa_requests, mr); } else { /* * IKE_SA_INIT is special since it always uses the message id 0. * Even when the message was rejected, and the new message has * different proposals, the id will be the same. To discern * retransmits and new messages, the RFC suggests to compare the * the messages. */ if (sa->sa_state == IKEV2_STATE_CLOSED && sa->sa_1stmsg && hdr->ike_exchange == IKEV2_EXCHANGE_IKE_SA_INIT && msg->msg_msgid == 0 && (ibuf_size(msg->msg_data) != ibuf_size(sa->sa_1stmsg) || memcmp(ibuf_data(msg->msg_data), ibuf_data(sa->sa_1stmsg), ibuf_size(sa->sa_1stmsg)) != 0)) { ikev2_ike_sa_setreason(sa, NULL); sa_free(env, sa); msg->msg_sa = sa = NULL; goto done; } if (msg->msg_msgid < sa->sa_msgid) { ikestat_inc(env, ikes_msg_rcvd_dropped); return; } if (flag) initiator = 0; /* * See if we have responded to this request before * For return values 0 and -1 we have. */ if ((r = ikev2_msg_retransmit_response(env, sa, msg, hdr)) != -2) { if (r == -1) { log_warn("%s: failed to retransmit a " "response", __func__); ikev2_ike_sa_setreason(sa, "retransmitting response failed"); sa_free(env, sa); } return; } else if (sa->sa_msgid_set && msg->msg_msgid == sa->sa_msgid) { /* * Response is being worked on, most likely we're * waiting for the CA process to get back to us */ ikestat_inc(env, ikes_msg_rcvd_busy); return; } sa->sa_msgid_current = msg->msg_msgid; } done: if (initiator) ikev2_init_recv(env, msg, hdr); else ikev2_resp_recv(env, msg, hdr); if (sa != NULL && !msg->msg_response && msg->msg_valid) { /* * If it's a valid request, make sure to update the peer's * message ID and dispose of all previous responses. * We need to set sa_msgid_set in order to distinguish between * "last msgid was 0" and "msgid not set yet". */ sa->sa_msgid = sa->sa_msgid_current; sa->sa_msgid_set = 1; ikev2_msg_prevail(env, &sa->sa_responses, msg); } if (sa != NULL && sa->sa_state == IKEV2_STATE_CLOSED) { log_debug("%s: closing SA", __func__); ikev2_ike_sa_setreason(sa, "closed"); sa_free(env, sa); } } int ikev2_ike_auth_compatible(struct iked_sa *sa, uint8_t policy, uint8_t wire) { if (wire == IKEV2_AUTH_SIG_ANY) /* internal, not on wire */ return (-1); if (policy == wire || policy == IKEV2_AUTH_NONE) return (0); switch (policy) { case IKEV2_AUTH_SIG_ANY: switch (wire) { case IKEV2_AUTH_SIG: case IKEV2_AUTH_RSA_SIG: case IKEV2_AUTH_ECDSA_256: case IKEV2_AUTH_ECDSA_384: case IKEV2_AUTH_ECDSA_521: return (0); } break; case IKEV2_AUTH_SIG: case IKEV2_AUTH_RSA_SIG: case IKEV2_AUTH_ECDSA_256: case IKEV2_AUTH_ECDSA_384: case IKEV2_AUTH_ECDSA_521: switch (wire) { /* * XXX Maybe we need an indication saying: * XXX Accept AUTH_SIG as long as its DSA? */ case IKEV2_AUTH_SIG: if (sa->sa_sigsha2) return (0); } break; } return (-1); } int ikev2_auth_verify(struct iked *env, struct iked_sa *sa) { struct iked_auth ikeauth; struct ibuf *authmsg; int ret; memcpy(&ikeauth, &sa->sa_policy->pol_auth, sizeof(ikeauth)); if (sa->sa_policy->pol_auth.auth_eap && sa->sa_eapmsk != NULL) { /* * The initiator EAP auth is a PSK derived * from the EAP-specific MSK */ ikeauth.auth_method = IKEV2_AUTH_SHARED_KEY_MIC; /* Copy session key as PSK */ memcpy(ikeauth.auth_data, ibuf_data(sa->sa_eapmsk), ibuf_size(sa->sa_eapmsk)); ikeauth.auth_length = ibuf_size(sa->sa_eapmsk); } if (ikev2_ike_auth_compatible(sa, ikeauth.auth_method, sa->sa_peerauth.id_type) < 0) { log_warnx("%s: unexpected auth method %s, was " "expecting %s", SPI_SA(sa, __func__), print_map(sa->sa_peerauth.id_type, ikev2_auth_map), print_map(ikeauth.auth_method, ikev2_auth_map)); ikev2_send_auth_failed(env, sa); explicit_bzero(&ikeauth, sizeof(ikeauth)); return (-1); } ikeauth.auth_method = sa->sa_peerauth.id_type; if ((authmsg = ikev2_msg_auth(env, sa, sa->sa_hdr.sh_initiator)) == NULL) { log_debug("%s: failed to get auth data", __func__); ikev2_send_auth_failed(env, sa); explicit_bzero(&ikeauth, sizeof(ikeauth)); return (-1); } ret = ikev2_msg_authverify(env, sa, &ikeauth, ibuf_data(sa->sa_peerauth.id_buf), ibuf_size(sa->sa_peerauth.id_buf), authmsg); ibuf_free(authmsg); if (ret != 0) { log_info("%s: ikev2_msg_authverify failed", SPI_SA(sa, __func__)); ikev2_send_auth_failed(env, sa); explicit_bzero(&ikeauth, sizeof(ikeauth)); return (-1); } if (sa->sa_eapmsk != NULL) { if ((authmsg = ikev2_msg_auth(env, sa, !sa->sa_hdr.sh_initiator)) == NULL) { log_debug("%s: failed to get auth data", __func__); explicit_bzero(&ikeauth, sizeof(ikeauth)); return (-1); } /* XXX 2nd AUTH for EAP messages */ ret = ikev2_msg_authsign(env, sa, &ikeauth, authmsg); ibuf_free(authmsg); if (ret != 0) { ikev2_send_auth_failed(env, sa); explicit_bzero(&ikeauth, sizeof(ikeauth)); return (-1); } /* ikev2_msg_authverify verified AUTH */ sa_stateflags(sa, IKED_REQ_AUTHVALID); sa_stateflags(sa, IKED_REQ_EAPVALID); sa_state(env, sa, IKEV2_STATE_EAP_SUCCESS); } explicit_bzero(&ikeauth, sizeof(ikeauth)); return (0); } int ikev2_ike_auth_recv(struct iked *env, struct iked_sa *sa, struct iked_message *msg) { struct iked_id *id; struct ibuf *authmsg, *buf; struct iked_policy *old; uint8_t *cert = NULL; size_t certlen = 0; int certtype = IKEV2_CERT_NONE; int i; /* The AUTH payload indicates if the responder wants EAP or not */ if (msg->msg_auth.id_type != IKEV2_AUTH_NONE && !sa_stateok(sa, IKEV2_STATE_EAP)) sa_state(env, sa, IKEV2_STATE_AUTH_REQUEST); if (!sa->sa_hdr.sh_initiator && !sa_stateok(sa, IKEV2_STATE_AUTH_REQUEST) && sa->sa_policy->pol_auth.auth_eap) sa_state(env, sa, IKEV2_STATE_EAP); if (sa->sa_hdr.sh_initiator) id = &sa->sa_rid; else id = &sa->sa_iid; /* try to relookup the policy based on the peerid */ if (msg->msg_peerid.id_type && !sa->sa_hdr.sh_initiator) { old = sa->sa_policy; sa->sa_policy = NULL; if (policy_lookup(env, msg, &sa->sa_proposals, NULL, 0) != 0 || msg->msg_policy == NULL) { log_info("%s: no compatible policy found", SPI_SA(sa, __func__)); ikev2_send_auth_failed(env, sa); TAILQ_REMOVE(&old->pol_sapeers, sa, sa_peer_entry); policy_unref(env, old); return (-1); } if (msg->msg_policy != old) { /* Clean up old policy */ TAILQ_REMOVE(&old->pol_sapeers, sa, sa_peer_entry); policy_unref(env, old); /* Update SA with new policy*/ if (sa_new(env, sa->sa_hdr.sh_ispi, sa->sa_hdr.sh_rspi, 0, msg->msg_policy) != sa) { log_warnx("%s: failed to update SA", SPI_SA(sa, __func__)); ikev2_send_auth_failed(env, sa); return (-1); } } else { /* restore */ msg->msg_policy = sa->sa_policy = old; } if (ikev2_handle_certreq(env, msg) != 0) return (-1); } else if (sa->sa_hdr.sh_initiator) { old = sa->sa_policy; /* verify policy on initiator */ sa->sa_policy = NULL; if (policy_lookup(env, msg, &sa->sa_proposals, &old->pol_flows, old->pol_nflows) != 0 || msg->msg_policy != old) { /* get dstid */ if (msg->msg_peerid.id_type) { memcpy(id, &msg->msg_peerid, sizeof(*id)); bzero(&msg->msg_peerid, sizeof(msg->msg_peerid)); } log_warnx("%s: policy mismatch", SPI_SA(sa, __func__)); ikev2_send_auth_failed(env, sa); TAILQ_REMOVE(&old->pol_sapeers, sa, sa_peer_entry); policy_unref(env, old); return (-1); } /* restore */ msg->msg_policy = sa->sa_policy = old; } /* AUTH payload is required for non-EAP */ if (!msg->msg_auth.id_type && !sa->sa_policy->pol_auth.auth_eap) { /* get dstid */ if (msg->msg_peerid.id_type) { memcpy(id, &msg->msg_peerid, sizeof(*id)); bzero(&msg->msg_peerid, sizeof(msg->msg_peerid)); } log_debug("%s: missing auth payload", SPI_SA(sa, __func__)); ikev2_send_auth_failed(env, sa); return (-1); } if (msg->msg_peerid.id_type) { memcpy(id, &msg->msg_peerid, sizeof(*id)); bzero(&msg->msg_peerid, sizeof(msg->msg_peerid)); if (!sa->sa_hdr.sh_initiator) { if ((authmsg = ikev2_msg_auth(env, sa, !sa->sa_hdr.sh_initiator)) == NULL) { log_debug("%s: failed to get response " "auth data", __func__); return (-1); } ca_setauth(env, sa, authmsg, PROC_CERT); ibuf_free(authmsg); } } /* Encode all received certs as single blob */ if (msg->msg_cert.id_type != IKEV2_CERT_BUNDLE && msg->msg_scert[0].id_type != IKEV2_CERT_NONE) { if ((buf = ibuf_new(NULL, 0)) == NULL) return (-1); /* begin with certificate */ if (ca_certbundle_add(buf, &msg->msg_cert) != 0) { ibuf_free(buf); return (-1); } /* add intermediate CAs */ for (i = 0; i < IKED_SCERT_MAX; i++) { if (msg->msg_scert[i].id_type == IKEV2_CERT_NONE) break; if (ca_certbundle_add(buf, &msg->msg_scert[i]) != 0) { ibuf_free(buf); return (-1); } } ibuf_free(msg->msg_cert.id_buf); msg->msg_cert.id_buf = buf; msg->msg_cert.id_type = IKEV2_CERT_BUNDLE; } if (!TAILQ_EMPTY(&msg->msg_proposals)) { if (proposals_negotiate(&sa->sa_proposals, &sa->sa_policy->pol_proposals, &msg->msg_proposals, 0, -1) != 0) { log_info("%s: no proposal chosen", __func__); msg->msg_error = IKEV2_N_NO_PROPOSAL_CHOSEN; ikestat_inc(env, ikes_sa_proposals_negotiate_failures); return (-1); } else sa_stateflags(sa, IKED_REQ_SA); } if (msg->msg_auth.id_type) { memcpy(&sa->sa_peerauth, &msg->msg_auth, sizeof(sa->sa_peerauth)); bzero(&msg->msg_auth, sizeof(msg->msg_auth)); } if (msg->msg_cp) { if (msg->msg_cp_addr) { sa->sa_cp_addr = msg->msg_cp_addr; msg->msg_cp_addr = NULL; } if (msg->msg_cp_addr6) { sa->sa_cp_addr6 = msg->msg_cp_addr6; msg->msg_cp_addr6 = NULL; } if (msg->msg_cp_dns) { sa->sa_cp_dns = msg->msg_cp_dns; msg->msg_cp_dns = NULL; } sa->sa_cp = msg->msg_cp; } /* For EAP and PSK AUTH can be verified without the CA process*/ if ((sa->sa_policy->pol_auth.auth_eap && sa->sa_eapmsk != NULL) || sa->sa_policy->pol_auth.auth_method == IKEV2_AUTH_SHARED_KEY_MIC) ikev2_auth_verify(env, sa); /* For CERT and Pubkey AUTH the CA process must find a matching key */ else if (sa->sa_peerauth.id_type) { if (msg->msg_cert.id_type) { certtype = msg->msg_cert.id_type; cert = ibuf_data(msg->msg_cert.id_buf); certlen = ibuf_size(msg->msg_cert.id_buf); } sa->sa_stateflags &= ~IKED_REQ_CERTVALID; if (ca_setcert(env, &sa->sa_hdr, id, certtype, cert, certlen, PROC_CERT) == -1) return (-1); } if (sa->sa_cp == IKEV2_CP_REPLY) { if (sa->sa_cp_addr) log_info("%s: obtained lease: %s", SPI_SA(sa, __func__), print_addr(&sa->sa_cp_addr->addr)); if (sa->sa_cp_addr6) log_info("%s: obtained lease: %s", SPI_SA(sa, __func__), print_addr(&sa->sa_cp_addr6->addr)); if (sa->sa_cp_dns) log_info("%s: obtained DNS: %s", SPI_SA(sa, __func__), print_addr(&sa->sa_cp_dns->addr)); } return ikev2_ike_auth(env, sa); } int ikev2_ike_auth(struct iked *env, struct iked_sa *sa) { /* Attempt state transition */ if (sa->sa_state == IKEV2_STATE_EAP_SUCCESS) sa_state(env, sa, IKEV2_STATE_EAP_VALID); else if (sa->sa_state == IKEV2_STATE_AUTH_SUCCESS) sa_state(env, sa, IKEV2_STATE_VALID); if (sa->sa_hdr.sh_initiator) { if (sa_stateok(sa, IKEV2_STATE_AUTH_SUCCESS)) return (ikev2_init_done(env, sa)); /* AUTH exchange is awaiting response from CA process, ignore */ else if (sa_stateok(sa, IKEV2_STATE_AUTH_REQUEST)) return (0); else return (ikev2_init_ike_auth(env, sa)); } return (ikev2_resp_ike_auth(env, sa)); } void ikev2_init_recv(struct iked *env, struct iked_message *msg, struct ike_header *hdr) { struct iked_sa *sa; struct iked_policy *pol; if (ikev2_msg_valid_ike_sa(env, hdr, msg) == -1) { log_debug("%s: unknown SA", __func__); return; } sa = msg->msg_sa; switch (hdr->ike_exchange) { case IKEV2_EXCHANGE_IKE_SA_INIT: /* Update the SPIs */ if ((sa = sa_new(env, betoh64(hdr->ike_ispi), betoh64(hdr->ike_rspi), 1, NULL)) == NULL || sa != msg->msg_sa) { log_debug("%s: invalid new SA", __func__); if (sa) { ikev2_ike_sa_setreason(sa, "invalid new SA"); sa_free(env, sa); } return; } break; case IKEV2_EXCHANGE_IKE_AUTH: case IKEV2_EXCHANGE_CREATE_CHILD_SA: case IKEV2_EXCHANGE_INFORMATIONAL: break; default: log_debug("%s: unsupported exchange: %s", __func__, print_map(hdr->ike_exchange, ikev2_exchange_map)); return; } if (ikev2_pld_parse(env, hdr, msg, msg->msg_offset) != 0) { log_debug("%s: failed to parse message", __func__); return; } if (sa_address(sa, &sa->sa_peer, (struct sockaddr *)&msg->msg_peer) == -1 || sa_address(sa, &sa->sa_local, (struct sockaddr *)&msg->msg_local) == -1) { ikestat_inc(env, ikes_msg_rcvd_dropped); return; } sa->sa_fd = msg->msg_fd; log_debug("%s: updated SA to peer %s local %s", __func__, print_addr(&sa->sa_peer.addr), print_addr(&sa->sa_local.addr)); if (sa->sa_fragments.frag_count != 0) return; if (!ikev2_msg_frompeer(msg)) return; if (ikev2_handle_notifies(env, msg) != 0) return; if (msg->msg_nat_detected && sa->sa_natt == 0) ikev2_enable_natt(env, sa, msg, 1); switch (hdr->ike_exchange) { case IKEV2_EXCHANGE_IKE_SA_INIT: if (ibuf_length(msg->msg_cookie)) { pol = sa->sa_policy; if (ikev2_init_ike_sa_peer(env, pol, &pol->pol_peer, msg) != 0) log_warnx("%s: failed to initiate a " "IKE_SA_INIT exchange", SPI_SA(sa, __func__)); break; } if (msg->msg_flags & IKED_MSG_FLAGS_NO_PROPOSAL_CHOSEN) { log_info("%s: failed to negotiate IKE SA", SPI_SA(sa, __func__)); ikev2_ike_sa_setreason(sa, "no proposal chosen"); sa_state(env, sa, IKEV2_STATE_CLOSED); msg->msg_sa = NULL; return; } if (ikev2_handle_certreq(env, msg) != 0) return; if (ikev2_init_auth(env, msg) != 0) { ikev2_ike_sa_setreason(sa, "failed to initiate IKE_AUTH exchange"); sa_state(env, sa, IKEV2_STATE_CLOSED); msg->msg_sa = NULL; return; } break; case IKEV2_EXCHANGE_IKE_AUTH: if (msg->msg_flags & IKED_MSG_FLAGS_AUTHENTICATION_FAILED) { log_debug("%s: AUTHENTICATION_FAILED, closing SA", __func__); ikev2_log_cert_info(SPI_SA(sa, __func__), sa->sa_hdr.sh_initiator ? &sa->sa_rcert : &sa->sa_icert); ikev2_ike_sa_setreason(sa, "authentication failed notification from peer"); sa_state(env, sa, IKEV2_STATE_CLOSED); msg->msg_sa = NULL; return; } if (msg->msg_flags & IKED_MSG_FLAGS_NO_PROPOSAL_CHOSEN) { log_info("%s: failed to negotiate IKE SA", SPI_SA(sa, __func__)); ikev2_ike_sa_setreason(sa, "no proposal chosen (IKE SA)"); sa_state(env, sa, IKEV2_STATE_CLOSED); msg->msg_sa = NULL; return; } (void)ikev2_ike_auth_recv(env, sa, msg); break; case IKEV2_EXCHANGE_CREATE_CHILD_SA: if (msg->msg_flags & IKED_MSG_FLAGS_NO_PROPOSAL_CHOSEN) { log_info("%s: CREATE_CHILD_SA failed", SPI_SA(sa, __func__)); ikev2_ike_sa_setreason(sa, "no proposal chosen (CHILD SA)"); sa_state(env, sa, IKEV2_STATE_CLOSED); msg->msg_sa = NULL; return; } (void)ikev2_init_create_child_sa(env, msg); break; case IKEV2_EXCHANGE_INFORMATIONAL: sa->sa_stateflags &= ~IKED_REQ_INF; break; default: log_debug("%s: exchange %s not implemented", __func__, print_map(hdr->ike_exchange, ikev2_exchange_map)); break; } } void ikev2_enable_natt(struct iked *env, struct iked_sa *sa, struct iked_message *msg, int udpencap) { struct iked_socket *sock; in_port_t port; sock = ikev2_msg_getsocket(env, sa->sa_local.addr_af, 1); if (sock == NULL) return; /* * Update address information and use the NAT-T * port and socket, if available. */ port = htons(socket_getport( (struct sockaddr *)&sock->sock_addr)); sa->sa_local.addr_port = port; sa->sa_peer.addr_port = port; (void)socket_af((struct sockaddr *)&sa->sa_local.addr, port); (void)socket_af((struct sockaddr *)&sa->sa_peer.addr, port); msg->msg_fd = sa->sa_fd = sock->sock_fd; msg->msg_sock = sock; sa->sa_natt = 1; if (udpencap) sa->sa_udpencap = 1; log_debug("%s: detected NAT, enabling UDP encapsulation," " updated SA to peer %s local %s", __func__, print_addr(&sa->sa_peer.addr), print_addr(&sa->sa_local.addr)); } void ikev2_init_ike_sa(struct iked *env, void *arg) { struct iked_policy *pol; TAILQ_FOREACH(pol, &env->sc_policies, pol_entry) { if ((pol->pol_flags & IKED_POLICY_ACTIVE) == 0) continue; if (!TAILQ_EMPTY(&pol->pol_sapeers)) { log_debug("%s: \"%s\" is already active", __func__, pol->pol_name); continue; } log_info("%s: initiating \"%s\"", __func__, pol->pol_name); if (ikev2_init_ike_sa_peer(env, pol, &pol->pol_peer, NULL)) log_debug("%s: failed to initiate with peer %s", __func__, print_addr(&pol->pol_peer.addr)); } timer_set(env, &env->sc_inittmr, ikev2_init_ike_sa, NULL); timer_add(env, &env->sc_inittmr, IKED_INITIATOR_INTERVAL); } void ikev2_init_ike_sa_timeout(struct iked *env, void *arg) { struct iked_sa *sa = arg; log_debug("%s: ispi %s rspi %s", __func__, print_spi(sa->sa_hdr.sh_ispi, 8), print_spi(sa->sa_hdr.sh_rspi, 8)); ikev2_ike_sa_setreason(sa, "SA_INIT timeout"); sa_free(env, sa); } int ikev2_init_ike_sa_peer(struct iked *env, struct iked_policy *pol, struct iked_addr *peer, struct iked_message *retry) { struct sockaddr_storage ss; struct iked_message req; struct ike_header *hdr; struct ikev2_payload *pld; struct ikev2_keyexchange *ke; struct ikev2_notify *n; struct iked_sa *sa = NULL; struct ibuf *buf, *cookie = NULL, *vendor_id = NULL; struct dh_group *group; ssize_t len; int ret = -1; struct iked_socket *sock; in_port_t port; if ((sock = ikev2_msg_getsocket(env, peer->addr_af, 0)) == NULL) return (-1); if (retry != NULL) { sa = retry->msg_sa; cookie = retry->msg_cookie; sa_state(env, sa, IKEV2_STATE_INIT); } /* Create a new initiator SA */ if (sa == NULL && (sa = sa_new(env, 0, 0, 1, pol)) == NULL) return (-1); /* Pick peer's DH group if asked */ if (pol->pol_peerdh > 0 && sa->sa_dhgroup == NULL && (sa->sa_dhgroup = group_get(pol->pol_peerdh)) == NULL) { log_warnx("%s: invalid peer DH group %u", SPI_SA(sa, __func__), pol->pol_peerdh); goto closeonly; } sa->sa_reqid = 0; if (ikev2_sa_initiator(env, sa, NULL, NULL) == -1) goto closeonly; if (pol->pol_local.addr.ss_family == AF_UNSPEC) { if (socket_getaddr(sock->sock_fd, &ss) == -1) goto closeonly; } else memcpy(&ss, &pol->pol_local.addr, SS_LEN(pol->pol_local.addr)); if ((buf = ikev2_msg_init(env, &req, &peer->addr, SS_LEN(peer->addr), &ss, SS_LEN(ss), 0)) == NULL) goto done; /* Inherit the port from the 1st send socket */ port = htons(socket_getport((struct sockaddr *)&sock->sock_addr)); (void)socket_af((struct sockaddr *)&req.msg_local, port); (void)socket_af((struct sockaddr *)&req.msg_peer, port); req.msg_fd = sock->sock_fd; req.msg_sa = sa; req.msg_sock = sock; req.msg_msgid = ikev2_msg_id(env, sa); /* IKE header */ if ((hdr = ikev2_add_header(buf, sa, req.msg_msgid, cookie == NULL ? IKEV2_PAYLOAD_SA : IKEV2_PAYLOAD_NOTIFY, IKEV2_EXCHANGE_IKE_SA_INIT, 0)) == NULL) goto done; /* Reflect COOKIE */ if (cookie) { if ((pld = ikev2_add_payload(buf)) == NULL) goto done; if ((n = ibuf_reserve(buf, sizeof(*n))) == NULL) goto done; n->n_protoid = IKEV2_SAPROTO_NONE; n->n_spisize = 0; n->n_type = htobe16(IKEV2_N_COOKIE); if (ikev2_add_buf(buf, cookie) == -1) goto done; len = sizeof(*n) + ibuf_size(cookie); log_debug("%s: added cookie, len %zu", __func__, ibuf_size(cookie)); print_hexbuf(cookie); if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1) goto done; } /* SA payload */ if ((pld = ikev2_add_payload(buf)) == NULL) goto done; if ((len = ikev2_add_proposals(env, sa, buf, &pol->pol_proposals, IKEV2_SAPROTO_IKE, sa->sa_hdr.sh_initiator, 0, 0)) == -1) goto done; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_KE) == -1) goto done; /* KE payload */ if ((pld = ikev2_add_payload(buf)) == NULL) goto done; if ((ke = ibuf_reserve(buf, sizeof(*ke))) == NULL) goto done; if ((group = sa->sa_dhgroup) == NULL) { log_debug("%s: invalid dh", __func__); goto done; } ke->kex_dhgroup = htobe16(group->id); if (ikev2_add_buf(buf, sa->sa_dhiexchange) == -1) goto done; len = sizeof(*ke) + ibuf_size(sa->sa_dhiexchange); if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONCE) == -1) goto done; /* NONCE payload */ if ((pld = ikev2_add_payload(buf)) == NULL) goto done; if (ikev2_add_buf(buf, sa->sa_inonce) == -1) goto done; len = ibuf_size(sa->sa_inonce); if (env->sc_vendorid != 0) { vendor_id = ibuf_new(IKED_VENDOR_ID, strlen(IKED_VENDOR_ID)); ibuf_add(vendor_id, IKED_VERSION, strlen(IKED_VERSION)); if ((len = ikev2_add_vendor_id(buf, &pld, len, vendor_id)) == -1) goto done; } /* Fragmentation Notify */ if (env->sc_frag) { if ((len = ikev2_add_fragmentation(buf, &pld, len)) == -1) goto done; } if (env->sc_nattmode != NATT_DISABLE) { if (ntohs(port) == env->sc_nattport) { /* Enforce NAT-T on the initiator side */ log_debug("%s: enforcing NAT-T", __func__); req.msg_natt = sa->sa_natt = sa->sa_udpencap = 1; } if ((len = ikev2_add_nat_detection(env, buf, &pld, &req, len)) == -1) goto done; } if ((len = ikev2_add_sighashnotify(buf, &pld, len)) == -1) goto done; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) goto done; if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1) goto done; (void)ikev2_pld_parse(env, hdr, &req, 0); ibuf_free(sa->sa_1stmsg); if ((sa->sa_1stmsg = ibuf_dup(buf)) == NULL) { log_debug("%s: failed to copy 1st message", __func__); goto done; } if ((ret = ikev2_msg_send(env, &req)) == 0) sa_state(env, sa, IKEV2_STATE_SA_INIT); /* Setup exchange timeout. */ timer_set(env, &sa->sa_timer, ikev2_init_ike_sa_timeout, sa); timer_add(env, &sa->sa_timer, IKED_IKE_SA_EXCHANGE_TIMEOUT); done: ikev2_msg_cleanup(env, &req); closeonly: if (ret == -1) { log_debug("%s: closing SA", __func__); ikev2_ike_sa_setreason(sa, "failed to send SA_INIT"); sa_free(env, sa); } ibuf_free(vendor_id); return (ret); } int ikev2_init_auth(struct iked *env, struct iked_message *msg) { struct iked_sa *sa = msg->msg_sa; struct ibuf *authmsg; if (sa == NULL) return (-1); if (ikev2_sa_initiator(env, sa, NULL, msg) == -1) { log_info("%s: failed to get IKE keys", SPI_SA(sa, __func__)); return (-1); } if ((authmsg = ikev2_msg_auth(env, sa, !sa->sa_hdr.sh_initiator)) == NULL) { log_info("%s: failed to get auth data", SPI_SA(sa, __func__)); return (-1); } if (ca_setauth(env, sa, authmsg, PROC_CERT) == -1) { log_info("%s: failed to get cert", SPI_SA(sa, __func__)); ibuf_free(authmsg); return (-1); } ibuf_free(authmsg); return (ikev2_init_ike_auth(env, sa)); } int ikev2_init_ike_auth(struct iked *env, struct iked_sa *sa) { struct iked_policy *pol = sa->sa_policy; struct ikev2_payload *pld; struct ikev2_cert *cert; struct ikev2_auth *auth; struct iked_id *id, *certid, peerid; struct ibuf *e = NULL; uint8_t firstpayload; int ret = -1; ssize_t len; int i; if (!sa_stateok(sa, IKEV2_STATE_SA_INIT)) return (0); if (!sa->sa_localauth.id_type) { log_debug("%s: no local auth", __func__); return (0); } /* New encrypted message buffer */ if ((e = ibuf_static()) == NULL) goto done; id = &sa->sa_iid; certid = &sa->sa_icert; /* ID payloads */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; firstpayload = IKEV2_PAYLOAD_IDi; if (ibuf_add_ibuf(e, id->id_buf) != 0) goto done; len = ibuf_size(id->id_buf); if (pol->pol_peerid.id_type) { bzero(&peerid, sizeof(peerid)); if (ikev2_policy2id(&pol->pol_peerid, &peerid, 0) != 0) { log_debug("%s: failed to get remote id", __func__); goto done; } if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_IDr) == -1) goto done; if ((pld = ikev2_add_payload(e)) == NULL) goto done; if (ibuf_add_ibuf(e, peerid.id_buf) != 0) goto done; len = ibuf_size(peerid.id_buf); } /* CERT payload */ if ((sa->sa_stateinit & IKED_REQ_CERT) && (certid->id_type != IKEV2_CERT_NONE)) { if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_CERT) == -1) goto done; if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((cert = ibuf_reserve(e, sizeof(*cert))) == NULL) goto done; cert->cert_type = certid->id_type; if (ibuf_add_ibuf(e, certid->id_buf) != 0) goto done; len = ibuf_size(certid->id_buf) + sizeof(*cert); for (i = 0; i < IKED_SCERT_MAX; i++) { if (sa->sa_scert[i].id_type == IKEV2_CERT_NONE) break; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_CERT) == -1) goto done; if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((cert = ibuf_reserve(e, sizeof(*cert))) == NULL) goto done; cert->cert_type = sa->sa_scert[i].id_type; if (ibuf_add_ibuf(e, sa->sa_scert[i].id_buf) != 0) goto done; len = ibuf_size(sa->sa_scert[i].id_buf) + sizeof(*cert); } /* CERTREQ payload(s) */ if ((len = ikev2_add_certreq(e, &pld, len, env->sc_certreq, env->sc_certreqtype)) == -1) goto done; if (env->sc_certreqtype != pol->pol_certreqtype && (len = ikev2_add_certreq(e, &pld, len, NULL, pol->pol_certreqtype)) == -1) goto done; } if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_AUTH) == -1) goto done; /* AUTH payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((auth = ibuf_reserve(e, sizeof(*auth))) == NULL) goto done; auth->auth_method = sa->sa_localauth.id_type; if (ibuf_add_ibuf(e, sa->sa_localauth.id_buf) != 0) goto done; len = ibuf_size(sa->sa_localauth.id_buf) + sizeof(*auth); /* CP payload */ if (ikev2_cp_request_configured(sa)) { if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_CP) == -1) goto done; if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((len = ikev2_init_add_cp(env, sa, e)) == -1) goto done; } if ((pol->pol_flags & IKED_POLICY_IPCOMP) && (len = ikev2_add_ipcompnotify(env, e, &pld, len, sa, 1)) == -1) goto done; if ((pol->pol_flags & IKED_POLICY_TRANSPORT) && (len = ikev2_add_transport_mode(env, e, &pld, len, sa)) == -1) goto done; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1) goto done; /* SA payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((len = ikev2_add_proposals(env, sa, e, &pol->pol_proposals, 0, sa->sa_hdr.sh_initiator, 0, 1)) == -1) goto done; if ((len = ikev2_add_ts(e, &pld, len, sa, 0)) == -1) goto done; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) goto done; ret = ikev2_msg_send_encrypt(env, sa, &e, IKEV2_EXCHANGE_IKE_AUTH, firstpayload, 0); done: ibuf_free(e); return (ret); } void ikev2_enable_timer(struct iked *env, struct iked_sa *sa) { sa->sa_last_recvd = gettime(); timer_set(env, &sa->sa_timer, ikev2_ike_sa_alive, sa); if (env->sc_alive_timeout > 0) timer_add(env, &sa->sa_timer, env->sc_alive_timeout); timer_set(env, &sa->sa_keepalive, ikev2_ike_sa_keepalive, sa); if (sa->sa_usekeepalive) timer_add(env, &sa->sa_keepalive, IKED_IKE_SA_KEEPALIVE_TIMEOUT); timer_set(env, &sa->sa_rekey, ikev2_ike_sa_rekey, sa); if (sa->sa_policy->pol_rekey) ikev2_ike_sa_rekey_schedule(env, sa); } void ikev2_reset_alive_timer(struct iked *env) { struct iked_sa *sa; RB_FOREACH(sa, iked_sas, &env->sc_sas) { if (sa->sa_state != IKEV2_STATE_ESTABLISHED) continue; timer_del(env, &sa->sa_timer); if (env->sc_alive_timeout > 0) timer_add(env, &sa->sa_timer, env->sc_alive_timeout); } } void ikev2_disable_timer(struct iked *env, struct iked_sa *sa) { timer_del(env, &sa->sa_timer); timer_del(env, &sa->sa_keepalive); timer_del(env, &sa->sa_rekey); } int ikev2_init_done(struct iked *env, struct iked_sa *sa) { int ret; if (!sa_stateok(sa, IKEV2_STATE_VALID)) return (0); /* ignored */ #if defined(UDP_ENCAP_ESPINUDP) if (sa->sa_udpencap) { int sopt; sopt = UDP_ENCAP_ESPINUDP; if (setsockopt(sa->sa_fd, IPPROTO_UDP, UDP_ENCAP, &sopt, sizeof(sopt)) < 0) { log_warn("%s: failed to set UDP encap socket option", __func__); return (-1); } } #endif ret = ikev2_childsa_negotiate(env, sa, &sa->sa_kex, &sa->sa_proposals, sa->sa_hdr.sh_initiator, 0); if (ret == 0) ret = ikev2_childsa_enable(env, sa); if (ret == 0) { sa_state(env, sa, IKEV2_STATE_ESTABLISHED); /* Delete exchange timeout. */ timer_del(env, &sa->sa_timer); ikev2_enable_timer(env, sa); ikev2_log_established(sa); ikev2_record_dstid(env, sa); sa_configure_iface(env, sa, 1); } if (ret) ikev2_childsa_delete(env, sa, 0, 0, NULL, 1); return (ret); } int ikev2_policy2id(struct iked_static_id *polid, struct iked_id *id, int srcid) { struct ikev2_id hdr; struct iked_static_id localpid; char idstr[IKED_ID_SIZE]; struct in_addr in4; struct in6_addr in6; X509_NAME *name = NULL; uint8_t *p; int len; /* Fixup the local Id if not specified */ if (srcid && polid->id_type == 0) { polid = &localpid; bzero(polid, sizeof(*polid)); /* Create a default local ID based on our FQDN */ polid->id_type = IKEV2_ID_FQDN; if (gethostname((char *)polid->id_data, sizeof(polid->id_data)) != 0) return (-1); polid->id_offset = 0; polid->id_length = strlen((char *)polid->id_data); /* excluding NUL */ } if (!polid->id_length) return (-1); /* Create an IKEv2 ID payload */ bzero(&hdr, sizeof(hdr)); hdr.id_type = id->id_type = polid->id_type; id->id_offset = sizeof(hdr); ibuf_free(id->id_buf); if ((id->id_buf = ibuf_new(&hdr, sizeof(hdr))) == NULL) return (-1); switch (id->id_type) { case IKEV2_ID_IPV4: if (inet_pton(AF_INET, (char *)polid->id_data, &in4) != 1 || ibuf_add(id->id_buf, &in4, sizeof(in4)) != 0) { ibuf_free(id->id_buf); id->id_buf = NULL; return (-1); } break; case IKEV2_ID_IPV6: if (inet_pton(AF_INET6, (char *)polid->id_data, &in6) != 1 || ibuf_add(id->id_buf, &in6, sizeof(in6)) != 0) { ibuf_free(id->id_buf); id->id_buf = NULL; return (-1); } break; case IKEV2_ID_ASN1_DN: /* policy has ID in string-format, convert to ASN1 */ if ((name = ca_x509_name_parse(polid->id_data)) == NULL || (len = i2d_X509_NAME(name, NULL)) < 0 || (p = ibuf_reserve(id->id_buf, len)) == NULL || (i2d_X509_NAME(name, &p)) < 0) { if (name) X509_NAME_free(name); ibuf_free(id->id_buf); id->id_buf = NULL; return (-1); } X509_NAME_free(name); break; default: if (ibuf_add(id->id_buf, polid->id_data, polid->id_length) != 0) { ibuf_free(id->id_buf); id->id_buf = NULL; return (-1); } break; } if (ikev2_print_id(id, idstr, sizeof(idstr)) == -1) return (-1); log_debug("%s: %s %s length %zu", __func__, srcid ? "srcid" : "dstid", idstr, ibuf_size(id->id_buf)); return (0); } struct ike_header * ikev2_add_header(struct ibuf *buf, struct iked_sa *sa, uint32_t msgid, uint8_t nextpayload, uint8_t exchange, uint8_t flags) { struct ike_header *hdr; if ((hdr = ibuf_reserve(buf, sizeof(*hdr))) == NULL) { log_debug("%s: failed to add header", __func__); return (NULL); } hdr->ike_ispi = htobe64(sa->sa_hdr.sh_ispi); hdr->ike_rspi = htobe64(sa->sa_hdr.sh_rspi); hdr->ike_nextpayload = nextpayload; hdr->ike_version = IKEV2_VERSION; hdr->ike_exchange = exchange; hdr->ike_msgid = htobe32(msgid); hdr->ike_length = htobe32(sizeof(*hdr)); hdr->ike_flags = flags; if (sa->sa_hdr.sh_initiator) hdr->ike_flags |= IKEV2_FLAG_INITIATOR; return (hdr); } int ikev2_set_header(struct ike_header *hdr, size_t length) { uint32_t hdrlength = sizeof(*hdr) + length; if (hdrlength > UINT32_MAX) { log_debug("%s: message too long", __func__); return (-1); } hdr->ike_length = htobe32(sizeof(*hdr) + length); return (0); } struct ikev2_payload * ikev2_add_payload(struct ibuf *buf) { struct ikev2_payload *pld; if ((pld = ibuf_reserve(buf, sizeof(*pld))) == NULL) { log_debug("%s: failed to add payload", __func__); return (NULL); } pld->pld_nextpayload = IKEV2_PAYLOAD_NONE; pld->pld_length = sizeof(*pld); return (pld); } ssize_t ikev2_add_ts_payload(struct ibuf *buf, unsigned int type, struct iked_sa *sa) { struct iked_policy *pol = sa->sa_policy; struct ikev2_tsp *tsp; struct ikev2_ts *ts; struct iked_addr *addr; struct iked_addr pooladdr; uint8_t *ptr; size_t len = 0; uint32_t av[4], bv[4], mv[4]; struct sockaddr_in *in4; struct sockaddr_in6 *in6; struct iked_tss *tss; struct iked_ts *tsi; bzero(&pooladdr, sizeof(pooladdr)); if ((tsp = ibuf_reserve(buf, sizeof(*tsp))) == NULL) return (-1); len = sizeof(*tsp); if (type == IKEV2_PAYLOAD_TSi) { if (sa->sa_hdr.sh_initiator) { tss = &pol->pol_tssrc; tsp->tsp_count = pol->pol_tssrc_count; } else { tss = &pol->pol_tsdst; tsp->tsp_count = pol->pol_tsdst_count; } } else if (type == IKEV2_PAYLOAD_TSr) { if (sa->sa_hdr.sh_initiator) { tss = &pol->pol_tsdst; tsp->tsp_count = pol->pol_tsdst_count; } else { tss = &pol->pol_tssrc; tsp->tsp_count = pol->pol_tssrc_count; } } else return (-1); TAILQ_FOREACH(tsi, tss, ts_entry) { if ((ts = ibuf_reserve(buf, sizeof(*ts))) == NULL) return (-1); addr = &tsi->ts_addr; /* patch remote address (if configured to 0.0.0.0) */ if ((type == IKEV2_PAYLOAD_TSi && !sa->sa_hdr.sh_initiator) || (type == IKEV2_PAYLOAD_TSr && sa->sa_hdr.sh_initiator)) { if (ikev2_cp_fixaddr(sa, addr, &pooladdr) == 0) addr = &pooladdr; } ts->ts_protoid = tsi->ts_ipproto; if (addr->addr_port) { ts->ts_startport = addr->addr_port; ts->ts_endport = addr->addr_port; } else { ts->ts_startport = 0; ts->ts_endport = 0xffff; } switch (addr->addr_af) { case AF_INET: ts->ts_type = IKEV2_TS_IPV4_ADDR_RANGE; ts->ts_length = htobe16(sizeof(*ts) + 8); if ((ptr = ibuf_reserve(buf, 8)) == NULL) return (-1); in4 = (struct sockaddr_in *)&addr->addr; if (addr->addr_net) { /* Convert IPv4 network to address range */ mv[0] = prefixlen2mask(addr->addr_mask); av[0] = in4->sin_addr.s_addr & mv[0]; bv[0] = in4->sin_addr.s_addr | ~mv[0]; } else av[0] = bv[0] = in4->sin_addr.s_addr; memcpy(ptr, &av[0], 4); memcpy(ptr + 4, &bv[0], 4); break; case AF_INET6: ts->ts_type = IKEV2_TS_IPV6_ADDR_RANGE; ts->ts_length = htobe16(sizeof(*ts) + 32); if ((ptr = ibuf_reserve(buf, 32)) == NULL) return (-1); in6 = (struct sockaddr_in6 *)&addr->addr; memcpy(&av, &in6->sin6_addr.s6_addr, 16); memcpy(&bv, &in6->sin6_addr.s6_addr, 16); if (addr->addr_net) { /* Convert IPv6 network to address range */ prefixlen2mask6(addr->addr_mask, mv); av[0] &= mv[0]; av[1] &= mv[1]; av[2] &= mv[2]; av[3] &= mv[3]; bv[0] |= ~mv[0]; bv[1] |= ~mv[1]; bv[2] |= ~mv[2]; bv[3] |= ~mv[3]; } memcpy(ptr, &av, 16); memcpy(ptr + 16, &bv, 16); break; } len += betoh16(ts->ts_length); } return (len); } ssize_t ikev2_add_ts(struct ibuf *e, struct ikev2_payload **pld, ssize_t len, struct iked_sa *sa, int reverse) { if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_TSi) == -1) return (-1); /* TSi payload */ if ((*pld = ikev2_add_payload(e)) == NULL) return (-1); if ((len = ikev2_add_ts_payload(e, reverse ? IKEV2_PAYLOAD_TSr : IKEV2_PAYLOAD_TSi, sa)) == -1) return (-1); if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_TSr) == -1) return (-1); /* TSr payload */ if ((*pld = ikev2_add_payload(e)) == NULL) return (-1); if ((len = ikev2_add_ts_payload(e, reverse ? IKEV2_PAYLOAD_TSi : IKEV2_PAYLOAD_TSr, sa)) == -1) return (-1); return (len); } ssize_t ikev2_add_certreq(struct ibuf *e, struct ikev2_payload **pld, ssize_t len, struct ibuf *certreq, uint8_t type) { struct ikev2_cert *cert; if (type == IKEV2_CERT_NONE) return (len); if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_CERTREQ) == -1) return (-1); /* CERTREQ payload */ if ((*pld = ikev2_add_payload(e)) == NULL) return (-1); if ((cert = ibuf_reserve(e, sizeof(*cert))) == NULL) return (-1); cert->cert_type = type; len = sizeof(*cert); if (certreq != NULL && cert->cert_type == IKEV2_CERT_X509_CERT) { if (ikev2_add_buf(e, certreq) == -1) return (-1); len += ibuf_size(certreq); } log_debug("%s: type %s length %zd", __func__, print_map(type, ikev2_cert_map), len); return (len); } ssize_t ikev2_add_ipcompnotify(struct iked *env, struct ibuf *e, struct ikev2_payload **pld, ssize_t len, struct iked_sa *sa, int initiator) { struct iked_childsa csa; struct iked_ipcomp *ic; struct ikev2_notify *n; uint8_t *ptr; uint16_t cpi; uint32_t spi; uint8_t transform; /* we only support deflate */ transform = IKEV2_IPCOMP_DEFLATE; ic = initiator ? &sa->sa_ipcompi : &sa->sa_ipcompr; if (initiator) { bzero(&csa, sizeof(csa)); csa.csa_saproto = IKEV2_SAPROTO_IPCOMP; csa.csa_ikesa = sa; csa.csa_local = &sa->sa_peer; csa.csa_peer = &sa->sa_local; if (ipsec_sa_init(env, &csa, &spi) == -1) return (-1); ic->ic_cpi_in = spi; } else { spi = ic->ic_cpi_in; /* make sure IPCOMP CPIs are not reused */ ic->ic_transform = 0; ic->ic_cpi_in = 0; ic->ic_cpi_out = 0; } log_debug("%s: ic_cpi_in 0x%04x", __func__, spi); /* * We get spi == 0 if the kernel does not support IPcomp, * so just return the length of the current payload. */ if (spi == 0) return (len); cpi = htobe16((uint16_t)spi); if (*pld) if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1) return (-1); if ((*pld = ikev2_add_payload(e)) == NULL) return (-1); len = sizeof(*n) + sizeof(cpi) + sizeof(transform); if ((ptr = ibuf_reserve(e, len)) == NULL) return (-1); n = (struct ikev2_notify *)ptr; n->n_protoid = 0; n->n_spisize = 0; n->n_type = htobe16(IKEV2_N_IPCOMP_SUPPORTED); ptr += sizeof(*n); memcpy(ptr, &cpi, sizeof(cpi)); ptr += sizeof(cpi); memcpy(ptr, &transform, sizeof(transform)); return (len); } ssize_t ikev2_add_notify(struct ibuf *e, struct ikev2_payload **pld, ssize_t len, uint16_t notify) { struct ikev2_notify *n; if (*pld) if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1) return (-1); if ((*pld = ikev2_add_payload(e)) == NULL) return (-1); len = sizeof(*n); if ((n = ibuf_reserve(e, len)) == NULL) return (-1); n->n_protoid = 0; n->n_spisize = 0; n->n_type = htobe16(notify); log_debug("%s: done", __func__); return (len); } ssize_t ikev2_add_vendor_id(struct ibuf *e, struct ikev2_payload **pld, ssize_t len, struct ibuf *id) { if (*pld) if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_VENDOR) == -1) return (-1); if ((*pld = ikev2_add_payload(e)) == NULL) return (-1); if (ibuf_add_ibuf(e, id) == -1) return (-1); return (ibuf_size(id)); } ssize_t ikev2_add_mobike(struct ibuf *e, struct ikev2_payload **pld, ssize_t len) { return ikev2_add_notify(e, pld, len, IKEV2_N_MOBIKE_SUPPORTED); } ssize_t ikev2_add_fragmentation(struct ibuf *buf, struct ikev2_payload **pld, ssize_t len) { return ikev2_add_notify(buf, pld, len, IKEV2_N_FRAGMENTATION_SUPPORTED); } ssize_t ikev2_add_sighashnotify(struct ibuf *e, struct ikev2_payload **pld, ssize_t len) { struct ikev2_notify *n; uint8_t *ptr; size_t i; uint16_t hash, signature_hashes[] = { IKEV2_SIGHASH_SHA2_256, IKEV2_SIGHASH_SHA2_384, IKEV2_SIGHASH_SHA2_512 }; if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1) return (-1); /* XXX signature_hashes are hardcoded for now */ len = sizeof(*n) + nitems(signature_hashes) * sizeof(hash); /* NOTIFY payload */ if ((*pld = ikev2_add_payload(e)) == NULL) return (-1); if ((ptr = ibuf_reserve(e, len)) == NULL) return (-1); n = (struct ikev2_notify *)ptr; n->n_protoid = 0; n->n_spisize = 0; n->n_type = htobe16(IKEV2_N_SIGNATURE_HASH_ALGORITHMS); ptr += sizeof(*n); for (i = 0; i < nitems(signature_hashes); i++) { hash = htobe16(signature_hashes[i]); memcpy(ptr, &hash, sizeof(hash)); ptr += sizeof(hash); } return (len); } ssize_t ikev2_add_transport_mode(struct iked *env, struct ibuf *e, struct ikev2_payload **pld, ssize_t len, struct iked_sa *sa) { return ikev2_add_notify(e, pld, len, IKEV2_N_USE_TRANSPORT_MODE); } int ikev2_next_payload(struct ikev2_payload *pld, size_t length, uint8_t nextpayload) { size_t pldlength = sizeof(*pld) + length; if (pldlength > UINT16_MAX) { log_debug("%s: payload too long", __func__); return (-1); } log_debug("%s: length %zu nextpayload %s", __func__, pldlength, print_map(nextpayload, ikev2_payload_map)); pld->pld_length = htobe16(pldlength); pld->pld_nextpayload = nextpayload; return (0); } ssize_t ikev2_nat_detection(struct iked *env, struct iked_message *msg, void *ptr, size_t len, unsigned int type, int frompeer) { EVP_MD_CTX *ctx; struct ike_header *hdr; uint8_t md[SHA_DIGEST_LENGTH]; unsigned int mdlen = sizeof(md); struct iked_sa *sa = msg->msg_sa; struct sockaddr_in *in4; struct sockaddr_in6 *in6; ssize_t ret = -1; struct sockaddr_storage *src, *dst, *ss; uint64_t rspi, ispi; struct ibuf *buf; uint32_t rnd; int natt_force = 0; if (ptr == NULL) return (mdlen); if (frompeer) { buf = msg->msg_parent->msg_data; if ((hdr = ibuf_seek(buf, 0, sizeof(*hdr))) == NULL) return (-1); ispi = hdr->ike_ispi; rspi = hdr->ike_rspi; src = &msg->msg_peer; dst = &msg->msg_local; } else { ispi = htobe64(sa->sa_hdr.sh_ispi); rspi = htobe64(sa->sa_hdr.sh_rspi); src = &msg->msg_local; dst = &msg->msg_peer; } ctx = EVP_MD_CTX_new(); if (ctx == NULL) return (-1); EVP_DigestInit_ex(ctx, EVP_sha1(), NULL); switch (type) { case IKEV2_N_NAT_DETECTION_SOURCE_IP: log_debug("%s: %s source %s %s %s", __func__, frompeer ? "peer" : "local", print_spi(betoh64(ispi), 8), print_spi(betoh64(rspi), 8), print_addr(src)); ss = src; break; case IKEV2_N_NAT_DETECTION_DESTINATION_IP: log_debug("%s: %s destination %s %s %s", __func__, frompeer ? "peer" : "local", print_spi(betoh64(ispi), 8), print_spi(betoh64(rspi), 8), print_addr(dst)); ss = dst; break; default: goto done; } EVP_DigestUpdate(ctx, &ispi, sizeof(ispi)); EVP_DigestUpdate(ctx, &rspi, sizeof(rspi)); switch (ss->ss_family) { case AF_INET: in4 = (struct sockaddr_in *)ss; EVP_DigestUpdate(ctx, &in4->sin_addr.s_addr, sizeof(in4->sin_addr.s_addr)); EVP_DigestUpdate(ctx, &in4->sin_port, sizeof(in4->sin_port)); break; case AF_INET6: in6 = (struct sockaddr_in6 *)ss; EVP_DigestUpdate(ctx, &in6->sin6_addr.s6_addr, sizeof(in6->sin6_addr.s6_addr)); EVP_DigestUpdate(ctx, &in6->sin6_port, sizeof(in6->sin6_port)); break; default: goto done; } if (env->sc_nattmode == NATT_FORCE) natt_force = 1; else if (msg->msg_policy != NULL) { if (msg->msg_policy->pol_flags & IKED_POLICY_NATT_FORCE) natt_force = 1; } if (natt_force) { /* Enforce NAT-T/UDP-encapsulation by distorting the digest */ rnd = arc4random(); EVP_DigestUpdate(ctx, &rnd, sizeof(rnd)); } EVP_DigestFinal_ex(ctx, md, &mdlen); if (len < mdlen) goto done; memcpy(ptr, md, mdlen); ret = mdlen; done: EVP_MD_CTX_free(ctx); return (ret); } ssize_t ikev2_add_nat_detection(struct iked *env, struct ibuf *buf, struct ikev2_payload **pld, struct iked_message *msg, ssize_t len) { struct ikev2_notify *n; uint8_t *ptr; /* *pld is NULL if there is no previous payload */ if (*pld != NULL) { if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1) return (-1); } /* NAT-T notify payloads */ if ((*pld = ikev2_add_payload(buf)) == NULL) return (-1); if ((n = ibuf_reserve(buf, sizeof(*n))) == NULL) return (-1); n->n_type = htobe16(IKEV2_N_NAT_DETECTION_SOURCE_IP); len = ikev2_nat_detection(env, msg, NULL, 0, 0, 0); if ((ptr = ibuf_reserve(buf, len)) == NULL) return (-1); if ((len = ikev2_nat_detection(env, msg, ptr, len, betoh16(n->n_type), 0)) == -1) return (-1); len += sizeof(*n); if (ikev2_next_payload(*pld, len, IKEV2_PAYLOAD_NOTIFY) == -1) return (-1); if ((*pld = ikev2_add_payload(buf)) == NULL) return (-1); if ((n = ibuf_reserve(buf, sizeof(*n))) == NULL) return (-1); n->n_type = htobe16(IKEV2_N_NAT_DETECTION_DESTINATION_IP); len = ikev2_nat_detection(env, msg, NULL, 0, 0, 0); if ((ptr = ibuf_reserve(buf, len)) == NULL) return (-1); if ((len = ikev2_nat_detection(env, msg, ptr, len, betoh16(n->n_type), 0)) == -1) return (-1); len += sizeof(*n); return (len); } ssize_t ikev2_add_cp(struct iked *env, struct iked_sa *sa, int type, struct ibuf *buf) { struct iked_policy *pol = sa->sa_policy; struct ikev2_cp *cp; struct ikev2_cfg *cfg; struct iked_cfg *ikecfg; unsigned int i; uint32_t mask4; size_t len; struct sockaddr_in *in4; struct sockaddr_in6 *in6; uint8_t prefixlen; int sent_addr4 = 0, sent_addr6 = 0; int have_mask4 = 0, sent_mask4 = 0; if ((cp = ibuf_reserve(buf, sizeof(*cp))) == NULL) return (-1); len = sizeof(*cp); switch (type) { case IKEV2_CP_REQUEST: case IKEV2_CP_REPLY: cp->cp_type = type; break; default: /* Not yet supported */ return (-1); } for (i = 0; i < pol->pol_ncfg; i++) { ikecfg = &pol->pol_cfg[i]; if (ikecfg->cfg_action != cp->cp_type) continue; /* only return one address in case of multiple pools */ if (type == IKEV2_CP_REPLY) { switch (ikecfg->cfg_type) { case IKEV2_CFG_INTERNAL_IP4_ADDRESS: if (sent_addr4) continue; break; case IKEV2_CFG_INTERNAL_IP6_ADDRESS: if (sent_addr6) continue; break; } } if ((cfg = ibuf_reserve(buf, sizeof(*cfg))) == NULL) return (-1); cfg->cfg_type = htobe16(ikecfg->cfg_type); len += sizeof(*cfg); switch (ikecfg->cfg_type) { case IKEV2_CFG_INTERNAL_IP4_ADDRESS: case IKEV2_CFG_INTERNAL_IP4_NETMASK: case IKEV2_CFG_INTERNAL_IP4_DNS: case IKEV2_CFG_INTERNAL_IP4_NBNS: case IKEV2_CFG_INTERNAL_IP4_DHCP: case IKEV2_CFG_INTERNAL_IP4_SERVER: /* 4 bytes IPv4 address */ in4 = ((ikecfg->cfg_type == IKEV2_CFG_INTERNAL_IP4_ADDRESS) && sa->sa_addrpool && sa->sa_addrpool->addr_af == AF_INET) ? (struct sockaddr_in *)&sa->sa_addrpool->addr : (struct sockaddr_in *)&ikecfg->cfg.address.addr; /* don't include unspecified address in request */ if (type == IKEV2_CP_REQUEST && !in4->sin_addr.s_addr) break; cfg->cfg_length = htobe16(4); if (ibuf_add(buf, &in4->sin_addr.s_addr, 4) == -1) return (-1); len += 4; if (ikecfg->cfg_type == IKEV2_CFG_INTERNAL_IP4_ADDRESS) { sent_addr4 = 1; if (sa->sa_addrpool && sa->sa_addrpool->addr_af == AF_INET && sa->sa_addrpool->addr_mask != 0) have_mask4 = 1; } if (ikecfg->cfg_type == IKEV2_CFG_INTERNAL_IP4_NETMASK) sent_mask4 = 1; break; case IKEV2_CFG_INTERNAL_IP4_SUBNET: /* 4 bytes IPv4 address + 4 bytes IPv4 mask + */ in4 = (struct sockaddr_in *)&ikecfg->cfg.address.addr; mask4 = prefixlen2mask(ikecfg->cfg.address.addr_mask); cfg->cfg_length = htobe16(8); if (ibuf_add(buf, &in4->sin_addr.s_addr, 4) == -1) return (-1); if (ibuf_add(buf, &mask4, 4) == -1) return (-1); len += 8; break; case IKEV2_CFG_INTERNAL_IP6_DNS: case IKEV2_CFG_INTERNAL_IP6_NBNS: case IKEV2_CFG_INTERNAL_IP6_DHCP: case IKEV2_CFG_INTERNAL_IP6_SERVER: /* 16 bytes IPv6 address */ in6 = (struct sockaddr_in6 *)&ikecfg->cfg.address.addr; cfg->cfg_length = htobe16(16); if (ibuf_add(buf, &in6->sin6_addr.s6_addr, 16) == -1) return (-1); len += 16; break; case IKEV2_CFG_INTERNAL_IP6_ADDRESS: case IKEV2_CFG_INTERNAL_IP6_SUBNET: /* 16 bytes IPv6 address + 1 byte prefix length */ in6 = ((ikecfg->cfg_type == IKEV2_CFG_INTERNAL_IP6_ADDRESS) && sa->sa_addrpool6 && sa->sa_addrpool6->addr_af == AF_INET6) ? (struct sockaddr_in6 *)&sa->sa_addrpool6->addr : (struct sockaddr_in6 *)&ikecfg->cfg.address.addr; /* don't include unspecified address in request */ if (type == IKEV2_CP_REQUEST && IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) break; cfg->cfg_length = htobe16(17); if (ibuf_add(buf, &in6->sin6_addr.s6_addr, 16) == -1) return (-1); if (ikecfg->cfg.address.addr_net) prefixlen = ikecfg->cfg.address.addr_mask; else prefixlen = 128; if (ibuf_add(buf, &prefixlen, 1) == -1) return (-1); len += 16 + 1; if (ikecfg->cfg_type == IKEV2_CFG_INTERNAL_IP6_ADDRESS) sent_addr6 = 1; break; case IKEV2_CFG_APPLICATION_VERSION: /* Reply with an empty string (non-NUL terminated) */ cfg->cfg_length = 0; break; } } /* derive netmask from pool */ if (type == IKEV2_CP_REPLY && have_mask4 && !sent_mask4) { if ((cfg = ibuf_reserve(buf, sizeof(*cfg))) == NULL) return (-1); cfg->cfg_type = htobe16(IKEV2_CFG_INTERNAL_IP4_NETMASK); len += sizeof(*cfg); mask4 = prefixlen2mask(sa->sa_addrpool->addr_mask); cfg->cfg_length = htobe16(4); if (ibuf_add(buf, &mask4, 4) == -1) return (-1); len += 4; } return (len); } ssize_t ikev2_init_add_cp(struct iked *env, struct iked_sa *sa, struct ibuf *buf) { return (ikev2_add_cp(env, sa, IKEV2_CP_REQUEST, buf)); } ssize_t ikev2_resp_add_cp(struct iked *env, struct iked_sa *sa, struct ibuf *buf) { int ret; switch (sa->sa_cp) { case IKEV2_CP_REQUEST: ret = ikev2_add_cp(env, sa, IKEV2_CP_REPLY, buf); break; case IKEV2_CP_REPLY: case IKEV2_CP_SET: case IKEV2_CP_ACK: default: /* Not yet supported */ ret = -1; } return (ret); } ssize_t ikev2_add_proposals(struct iked *env, struct iked_sa *sa, struct ibuf *buf, struct iked_proposals *proposals, uint8_t protoid, int initiator, int sendikespi, int skipdh) { struct ikev2_sa_proposal *sap = NULL; struct iked_transform *xform; struct iked_proposal *prop; struct iked_childsa csa; ssize_t length = 0, saplength, xflen; uint64_t spi64; uint32_t spi32, spi = 0; unsigned int i, xfi, nxforms; int prop_skipdh; TAILQ_FOREACH(prop, proposals, prop_entry) { if ((protoid && prop->prop_protoid != protoid) || (!protoid && prop->prop_protoid == IKEV2_SAPROTO_IKE)) continue; prop_skipdh = skipdh; if (protoid != IKEV2_SAPROTO_IKE && initiator) { if (spi == 0) { bzero(&csa, sizeof(csa)); csa.csa_ikesa = sa; csa.csa_saproto = prop->prop_protoid; csa.csa_local = &sa->sa_peer; csa.csa_peer = &sa->sa_local; if (ipsec_sa_init(env, &csa, &spi) == -1) return (-1); } prop->prop_localspi.spi = spi; prop->prop_localspi.spi_size = 4; prop->prop_localspi.spi_protoid = prop->prop_protoid; } if ((sap = ibuf_reserve(buf, sizeof(*sap))) == NULL) { log_debug("%s: failed to add proposal", __func__); return (-1); } if (sendikespi) { /* Special case for IKE SA rekeying */ prop->prop_localspi.spi = initiator ? sa->sa_hdr.sh_ispi : sa->sa_hdr.sh_rspi; prop->prop_localspi.spi_size = 8; prop->prop_localspi.spi_protoid = IKEV2_SAPROTO_IKE; } /* * A single DH transform of type NONE is equivalent with * not sending a DH transform at all. * Prefer the latter for downwards compatibility. */ if (protoid != IKEV2_SAPROTO_IKE) { for (i = 0; i < prop->prop_nxforms; i++) { xform = prop->prop_xforms + i; if (xform->xform_type == IKEV2_XFORMTYPE_DH && xform->xform_id != IKEV2_XFORMDH_NONE) break; } if (i == prop->prop_nxforms) prop_skipdh = 1; } /* * RFC 7296: 1.2. The Initial Exchanges * IKE_AUTH messages do not contain KE/N payloads, thus * SA payloads cannot contain groups. */ if (prop_skipdh) { nxforms = 0; for (i = 0; i < prop->prop_nxforms; i++) { xform = prop->prop_xforms + i; if (xform->xform_type == IKEV2_XFORMTYPE_DH) continue; nxforms++; } } else nxforms = prop->prop_nxforms; sap->sap_more = IKEV1_PAYLOAD_PROPOSAL; sap->sap_proposalnr = prop->prop_id; sap->sap_protoid = prop->prop_protoid; sap->sap_spisize = prop->prop_localspi.spi_size; sap->sap_transforms = nxforms; saplength = sizeof(*sap); switch (prop->prop_localspi.spi_size) { case 4: spi32 = htobe32(prop->prop_localspi.spi); if (ibuf_add(buf, &spi32, sizeof(spi32)) != 0) return (-1); saplength += 4; break; case 8: spi64 = htobe64(prop->prop_localspi.spi); if (ibuf_add(buf, &spi64, sizeof(spi64)) != 0) return (-1); saplength += 8; break; default: break; } for (i = 0, xfi = 0; i < prop->prop_nxforms; i++) { xform = prop->prop_xforms + i; if (prop_skipdh && xform->xform_type == IKEV2_XFORMTYPE_DH) continue; if ((xflen = ikev2_add_transform(buf, xfi == nxforms - 1 ? IKEV2_XFORM_LAST : IKEV2_XFORM_MORE, xform->xform_type, xform->xform_id, xform->xform_length)) == -1) return (-1); xfi++; saplength += xflen; } sap->sap_length = htobe16(saplength); length += saplength; } if (sap != NULL) sap->sap_more = IKEV1_PAYLOAD_NONE; log_debug("%s: length %zd", __func__, length); return (length); } ssize_t ikev2_add_transform(struct ibuf *buf, uint8_t more, uint8_t type, uint16_t id, uint16_t length) { struct ikev2_transform *xfrm; struct ikev2_attribute *attr; if ((xfrm = ibuf_reserve(buf, sizeof(*xfrm))) == NULL) { log_debug("%s: failed to add transform", __func__); return (-1); } xfrm->xfrm_more = more; xfrm->xfrm_type = type; xfrm->xfrm_id = htobe16(id); if (length) { xfrm->xfrm_length = htobe16(sizeof(*xfrm) + sizeof(*attr)); if ((attr = ibuf_reserve(buf, sizeof(*attr))) == NULL) { log_debug("%s: failed to add attribute", __func__); return (-1); } attr->attr_type = htobe16(IKEV2_ATTRAF_TV | IKEV2_ATTRTYPE_KEY_LENGTH); attr->attr_length = htobe16(length); } else xfrm->xfrm_length = htobe16(sizeof(*xfrm)); return (betoh16(xfrm->xfrm_length)); } int ikev2_add_data(struct ibuf *buf, void *data, size_t length) { void *msgbuf; if ((msgbuf = ibuf_reserve(buf, length)) == NULL) { log_debug("%s: failed", __func__); return (-1); } memcpy(msgbuf, data, length); return (0); } int ikev2_add_buf(struct ibuf *buf, struct ibuf *data) { void *msgbuf; if ((msgbuf = ibuf_reserve(buf, ibuf_size(data))) == NULL) { log_debug("%s: failed", __func__); return (-1); } memcpy(msgbuf, ibuf_data(data), ibuf_size(data)); return (0); } int ikev2_resp_informational(struct iked *env, struct iked_sa *sa, struct iked_message *msg) { struct ikev2_notify *n; struct ikev2_payload *pld = NULL; struct ibuf *buf = NULL; ssize_t len = 0; int ret = -1; uint8_t firstpayload = IKEV2_PAYLOAD_NONE; if (!sa_stateok(sa, IKEV2_STATE_AUTH_REQUEST) || msg->msg_responded || msg->msg_error) goto done; if ((buf = ibuf_static()) == NULL) goto done; if ((len = ikev2_handle_delete(env, msg, buf, &pld, &firstpayload)) == -1) goto done; /* * Include NAT_DETECTION notification on UPDATE_SA_ADDRESSES or if * the peer did include them, too (RFC 4555, 3.8). */ if (sa->sa_mobike && (msg->msg_update_sa_addresses || msg->msg_natt_rcvd)) { /* NAT-T notify payloads */ len = ikev2_add_nat_detection(env, buf, &pld, msg, len); if (len == -1) goto done; firstpayload = IKEV2_PAYLOAD_NOTIFY; } /* Reflect COOKIE2 */ if (msg->msg_cookie2) { /* *pld is NULL if there is no previous payload */ if (pld != NULL) { if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NOTIFY) == -1) goto done; } if ((pld = ikev2_add_payload(buf)) == NULL) goto done; if ((n = ibuf_reserve(buf, sizeof(*n))) == NULL) goto done; n->n_protoid = IKEV2_SAPROTO_IKE; n->n_spisize = 0; n->n_type = htobe16(IKEV2_N_COOKIE2); if (ikev2_add_buf(buf, msg->msg_cookie2) == -1) goto done; len = sizeof(*n) + ibuf_size(msg->msg_cookie2); log_debug("%s: added cookie2", __func__); if (firstpayload == IKEV2_PAYLOAD_NONE) firstpayload = IKEV2_PAYLOAD_NOTIFY; } /* add terminator, if there is already a payload */ if (firstpayload != IKEV2_PAYLOAD_NONE) if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) goto done; ret = ikev2_msg_send_encrypt(env, sa, &buf, IKEV2_EXCHANGE_INFORMATIONAL, firstpayload, 1); if (ret != -1) msg->msg_responded = 1; if (msg->msg_flags & IKED_MSG_FLAGS_AUTHENTICATION_FAILED) { log_debug("%s: AUTHENTICATION_FAILED, closing SA", __func__); ikev2_log_cert_info(SPI_SA(sa, __func__), sa->sa_hdr.sh_initiator ? &sa->sa_rcert : &sa->sa_icert); ikev2_ike_sa_setreason(sa, "authentication failed notification from peer"); sa_state(env, sa, IKEV2_STATE_CLOSED); } done: ibuf_free(buf); return (ret); } void ikev2_resp_recv(struct iked *env, struct iked_message *msg, struct ike_header *hdr) { struct iked_sa *sa; switch (hdr->ike_exchange) { case IKEV2_EXCHANGE_IKE_SA_INIT: if (msg->msg_sa != NULL) { log_debug("%s: SA already exists", __func__); return; } if ((msg->msg_sa = sa_new(env, betoh64(hdr->ike_ispi), betoh64(hdr->ike_rspi), 0, msg->msg_policy)) == NULL) { log_debug("%s: failed to get new SA", __func__); return; } /* Setup exchange timeout. */ timer_set(env, &msg->msg_sa->sa_timer, ikev2_init_ike_sa_timeout, msg->msg_sa); timer_add(env, &msg->msg_sa->sa_timer, IKED_IKE_SA_EXCHANGE_TIMEOUT); break; case IKEV2_EXCHANGE_IKE_AUTH: if (ikev2_msg_valid_ike_sa(env, hdr, msg) == -1) return; if (sa_stateok(msg->msg_sa, IKEV2_STATE_VALID)) { log_debug("%s: already authenticated", __func__); return; } break; case IKEV2_EXCHANGE_CREATE_CHILD_SA: case IKEV2_EXCHANGE_INFORMATIONAL: if (ikev2_msg_valid_ike_sa(env, hdr, msg) == -1) return; break; default: log_debug("%s: unsupported exchange: %s", __func__, print_map(hdr->ike_exchange, ikev2_exchange_map)); return; } if (ikev2_pld_parse(env, hdr, msg, msg->msg_offset) != 0) { log_info("%s: failed to parse message", SPI_SA(msg->msg_sa, __func__)); return; } if (!ikev2_msg_frompeer(msg)) return; if (ikev2_handle_notifies(env, msg) != 0) return; if ((sa = msg->msg_sa) == NULL) return; if (sa_address(sa, &sa->sa_peer, (struct sockaddr *)&msg->msg_peer) == -1 || sa_address(sa, &sa->sa_local, (struct sockaddr *)&msg->msg_local) == -1) { ikestat_inc(env, ikes_msg_rcvd_dropped); return; } sa->sa_fd = msg->msg_fd; log_debug("%s: updated SA to peer %s local %s", __func__, print_addr(&sa->sa_peer.addr), print_addr(&sa->sa_local.addr)); if (sa->sa_fragments.frag_count != 0) return; msg->msg_valid = 1; if (msg->msg_natt && sa->sa_natt == 0) { log_debug("%s: NAT-T message received, updated SA", __func__); sa->sa_natt = 1; } switch (hdr->ike_exchange) { case IKEV2_EXCHANGE_IKE_SA_INIT: if (ikev2_sa_responder(env, sa, NULL, msg) != 0) { log_info("%s: failed to negotiate IKE SA", SPI_SA(sa, __func__)); if (msg->msg_error == 0) msg->msg_error = IKEV2_N_NO_PROPOSAL_CHOSEN; ikev2_send_init_error(env, msg); ikev2_ike_sa_setreason(sa, "no proposal chosen"); sa_state(env, sa, IKEV2_STATE_CLOSED); return; } if (ikev2_resp_ike_sa_init(env, msg) != 0) { log_debug("%s: failed to send init response", __func__); ikev2_ike_sa_setreason(sa, "SA_INIT response failed"); sa_state(env, sa, IKEV2_STATE_CLOSED); return; } break; case IKEV2_EXCHANGE_IKE_AUTH: if (!sa_stateok(sa, IKEV2_STATE_SA_INIT)) { log_debug("%s: state mismatch", __func__); ikev2_ike_sa_setreason(sa, "state mismatch IKE_AUTH"); sa_state(env, sa, IKEV2_STATE_CLOSED); return; } /* Handle EAP authentication */ if (msg->msg_eap.eam_found) { if (ikev2_resp_ike_eap(env, sa, msg)) { log_info("%s: failed eap response", SPI_SA(sa, __func__)); ikev2_ike_sa_setreason(sa, "EAP failed"); sa_state(env, sa, IKEV2_STATE_CLOSED); return; } return; } if (ikev2_ike_auth_recv(env, sa, msg) != 0) { log_debug("%s: failed to send auth response", __func__); ikev2_send_error(env, sa, msg, hdr->ike_exchange); ikev2_ike_sa_setreason(sa, "IKE_AUTH failed"); sa_state(env, sa, IKEV2_STATE_CLOSED); return; } break; case IKEV2_EXCHANGE_CREATE_CHILD_SA: if (ikev2_resp_create_child_sa(env, msg) != 0) { if (msg->msg_error == 0) msg->msg_error = IKEV2_N_NO_PROPOSAL_CHOSEN; ikev2_send_error(env, sa, msg, hdr->ike_exchange); } break; case IKEV2_EXCHANGE_INFORMATIONAL: if (msg->msg_update_sa_addresses) ikev2_update_sa_addresses(env, sa); (void)ikev2_resp_informational(env, sa, msg); break; default: break; } } ssize_t ikev2_handle_delete(struct iked *env, struct iked_message *msg, struct ibuf *resp, struct ikev2_payload **pld, uint8_t *firstpayload) { struct iked_childsa **peersas = NULL; struct iked_sa *sa = msg->msg_sa; struct ikev2_delete *localdel; FILE *spif; char *spibuf = NULL; uint64_t *localspi = NULL; uint64_t spi64, spi = 0; uint32_t spi32; uint8_t *buf; size_t found = 0; int ret = -1; size_t i, sz, cnt, len, dummy; if (!msg->msg_del_protoid) return (0); if ((spif = open_memstream(&spibuf, &dummy)) == NULL) { log_warn("%s", __func__); return (0); } sz = msg->msg_del_spisize; switch (sz) { case 4: case 8: break; case 0: if (msg->msg_del_protoid != IKEV2_SAPROTO_IKE) { log_debug("%s: invalid SPI size", __func__); goto done; } ikev2_ikesa_recv_delete(env, sa); return (0); default: log_info("%s: error: invalid SPI size", __func__); goto done; } cnt = msg->msg_del_cnt; len = ibuf_length(msg->msg_del_buf); if ((len / sz) != cnt) { log_debug("%s: invalid payload length %zu/%zu != %zu", __func__, len, sz, cnt); return (-1); } if (((peersas = calloc(cnt, sizeof(struct iked_childsa *))) == NULL || (localspi = calloc(cnt, sizeof(uint64_t))) == NULL)) { log_warn("%s", __func__); goto done; } buf = ibuf_data(msg->msg_del_buf); for (i = 0; i < cnt; i++) { switch (sz) { case 4: memcpy(&spi32, buf + (i * sz), sizeof(spi32)); spi = betoh32(spi32); break; case 8: memcpy(&spi64, buf + (i * sz), sizeof(spi64)); spi = betoh64(spi64); break; } log_debug("%s: spi %s", __func__, print_spi(spi, sz)); if (peersas == NULL || sa == NULL) continue; if ((peersas[i] = childsa_lookup(sa, spi, msg->msg_del_protoid)) == NULL) { log_warnx("%s: CHILD SA doesn't exist for spi %s", SPI_SA(sa, __func__), print_spi(spi, sz)); continue; } if (ikev2_childsa_delete(env, sa, msg->msg_del_protoid, spi, &localspi[i], 0) != -1) { found++; /* append SPI to log buffer */ if (ftello(spif) > 0) fputs(", ", spif); fputs(print_spi(spi, sz), spif); } /* * Flows are left in the require mode so that it would be * possible to quickly negotiate a new Child SA */ } if (resp == NULL) { ret = 0; goto done; } /* Response to the INFORMATIONAL with Delete payload */ if (found) { if ((*pld = ikev2_add_payload(resp)) == NULL) goto done; *firstpayload = IKEV2_PAYLOAD_DELETE; if ((localdel = ibuf_reserve(resp, sizeof(*localdel))) == NULL) goto done; localdel->del_protoid = msg->msg_del_protoid; localdel->del_spisize = sz; localdel->del_nspi = htobe16(found); ret = sizeof(*localdel); for (i = 0; i < cnt; i++) { if (localspi[i] == 0) /* happens if found < cnt */ continue; switch (sz) { case 4: spi32 = htobe32(localspi[i]); if (ibuf_add(resp, &spi32, sizeof(spi32)) != 0) goto done; ret += sizeof(spi32); break; case 8: spi64 = htobe64(localspi[i]); if (ibuf_add(resp, &spi64, sizeof(spi64)) != 0) goto done; ret += sizeof(spi64); break; } } fflush(spif); if (!ferror(spif)) { log_info("%sdeleted %zu SPI%s: %s", SPI_SA(sa, NULL), found, found == 1 ? "" : "s", spibuf); } } else { /* XXX should we send an INVALID_SPI notification? */ ret = 0; } done: free(localspi); free(peersas); fclose(spif); free(spibuf); return (ret); } int ikev2_handle_notifies(struct iked *env, struct iked_message *msg) { struct iked_ipcomp *ic; struct iked_sa *sa; struct iked_spi rekey; struct dh_group *group; uint16_t groupid; unsigned int protoid; if ((sa = msg->msg_sa) == NULL) return (-1); if (msg->msg_flags & IKED_MSG_FLAGS_CHILD_SA_NOT_FOUND) sa->sa_stateflags &= ~IKED_REQ_CHILDSA; if ((msg->msg_flags & IKED_MSG_FLAGS_FRAGMENTATION) && env->sc_frag) { log_debug("%s: fragmentation enabled", __func__); sa->sa_frag = 1; } if ((msg->msg_flags & IKED_MSG_FLAGS_MOBIKE) && env->sc_mobike) { log_debug("%s: mobike enabled", __func__); sa->sa_mobike = 1; /* enforce natt */ if (sa->sa_natt == 0 && sa->sa_udpencap == 0) ikev2_enable_natt(env, sa, msg, 0); } if ((msg->msg_flags & IKED_MSG_FLAGS_NO_ADDITIONAL_SAS) && sa->sa_stateflags & IKED_REQ_CHILDSA) { /* This makes sense for Child SAs only atm */ ikev2_disable_rekeying(env, sa); sa->sa_stateflags &= ~IKED_REQ_CHILDSA; } if (msg->msg_flags & IKED_MSG_FLAGS_INVALID_KE) { groupid = betoh16(msg->msg_group); if (group_getid(groupid) == NULL) { log_debug("%s: unable to select DH group %u", __func__, groupid); ikev2_ike_sa_setreason(sa, "unable to select DH group"); sa_state(env, sa, IKEV2_STATE_CLOSED); msg->msg_sa = NULL; return (-1); } log_debug("%s: responder selected DH group %u", __func__, groupid); switch (msg->msg_exchange) { case IKEV2_EXCHANGE_IKE_SA_INIT: protoid = IKEV2_SAPROTO_IKE; if (!sa->sa_hdr.sh_initiator) { log_debug("%s: not an initiator", __func__); ikev2_ike_sa_setreason(sa, "received invalid KE as responder"); sa_state(env, sa, IKEV2_STATE_CLOSED); msg->msg_sa = NULL; return (-1); } if (config_findtransform_ext(&msg->msg_policy->pol_proposals, IKEV2_XFORMTYPE_DH, groupid, protoid) == NULL) { log_debug("%s: DH group %u denied by policy", __func__, groupid); ikev2_ike_sa_setreason(sa, "unsupported group in INVALID_KE message"); sa_state(env, sa, IKEV2_STATE_CLOSED); msg->msg_sa = NULL; return (-1); } ikev2_ike_sa_setreason(sa, "reinitiating with new DH group"); sa_state(env, sa, IKEV2_STATE_CLOSED); msg->msg_sa = NULL; msg->msg_policy->pol_peerdh = groupid; timer_set(env, &env->sc_inittmr, ikev2_init_ike_sa, NULL); timer_add(env, &env->sc_inittmr, IKED_INITIATOR_INITIAL); return (-1); case IKEV2_EXCHANGE_CREATE_CHILD_SA: if (!(sa->sa_stateflags & IKED_REQ_CHILDSA)) { log_debug("%s: IKED_REQ_CHILDSA missing", __func__); return (-1); } sa->sa_stateflags &= ~IKED_REQ_CHILDSA; protoid = sa->sa_rekeyspi ? IKEV2_SAPROTO_ESP : IKEV2_SAPROTO_IKE; if (config_findtransform_ext(&msg->msg_policy->pol_proposals, IKEV2_XFORMTYPE_DH, groupid, protoid) == NULL) { log_debug("%s: DH group %u denied by policy", __func__, groupid); ikev2_ike_sa_setreason(sa, "unsupported group in INVALID_KE message"); sa_state(env, sa, IKEV2_STATE_CLOSED); msg->msg_sa = NULL; return (-1); } if (protoid == IKEV2_SAPROTO_ESP) { /* CHILDSA */ rekey.spi = sa->sa_rekeyspi; rekey.spi_size = 4; rekey.spi_protoid = protoid; (void)ikev2_send_create_child_sa(env, sa, &rekey, rekey.spi_protoid, groupid); } else { /* IKESA */ if ((group = group_get(groupid)) == NULL) return -1; group_free(sa->sa_dhgroup); sa->sa_dhgroup = group; timer_set(env, &sa->sa_rekey, ikev2_ike_sa_rekey, sa); timer_add(env, &sa->sa_rekey, 0); } return (-1); } } if (msg->msg_flags & IKED_MSG_FLAGS_IPCOMP_SUPPORTED) { /* we only support deflate */ if ((msg->msg_policy->pol_flags & IKED_POLICY_IPCOMP) && (msg->msg_transform == IKEV2_IPCOMP_DEFLATE)) { ic = msg->msg_response ? &sa->sa_ipcompi : &sa->sa_ipcompr; ic->ic_transform = msg->msg_transform; ic->ic_cpi_out = betoh16(msg->msg_cpi); } } if (msg->msg_nat_detected & IKED_MSG_NAT_DST_IP) { /* Send keepalive, since we are behind a NAT-gw */ sa->sa_usekeepalive = 1; } /* Signature hash algorithm */ if (msg->msg_flags & IKED_MSG_FLAGS_SIGSHA2) sa->sa_sigsha2 = 1; if (msg->msg_flags & IKED_MSG_FLAGS_USE_TRANSPORT) sa->sa_use_transport_mode = 1; if ((msg->msg_flags & IKED_MSG_FLAGS_TEMPORARY_FAILURE) && sa->sa_nexti != NULL) sa->sa_tmpfail = 1; return (0); } int ikev2_resp_ike_sa_init(struct iked *env, struct iked_message *msg) { struct iked_message resp; struct ike_header *hdr; struct ikev2_payload *pld; struct ikev2_keyexchange *ke; struct iked_sa *sa = msg->msg_sa; struct ibuf *buf; struct ibuf *vendor_id = NULL; struct dh_group *group; ssize_t len; int ret = -1; if (sa->sa_hdr.sh_initiator) { log_debug("%s: called by initiator", __func__); return (-1); } if (msg->msg_nat_detected && sa->sa_udpencap == 0) { log_debug("%s: detected NAT, enabling UDP encapsulation", __func__); sa->sa_udpencap = 1; } if ((buf = ikev2_msg_init(env, &resp, &msg->msg_peer, msg->msg_peerlen, &msg->msg_local, msg->msg_locallen, 1)) == NULL) goto done; resp.msg_sa = sa; resp.msg_fd = msg->msg_fd; resp.msg_natt = msg->msg_natt; resp.msg_msgid = 0; resp.msg_policy = sa->sa_policy; /* IKE header */ if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid, IKEV2_PAYLOAD_SA, IKEV2_EXCHANGE_IKE_SA_INIT, IKEV2_FLAG_RESPONSE)) == NULL) goto done; /* SA payload */ if ((pld = ikev2_add_payload(buf)) == NULL) goto done; if ((len = ikev2_add_proposals(env, sa, buf, &sa->sa_proposals, IKEV2_SAPROTO_IKE, sa->sa_hdr.sh_initiator, 0, 0)) == -1) goto done; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_KE) == -1) goto done; /* KE payload */ if ((pld = ikev2_add_payload(buf)) == NULL) goto done; if ((ke = ibuf_reserve(buf, sizeof(*ke))) == NULL) goto done; if ((group = sa->sa_dhgroup) == NULL) { log_debug("%s: invalid dh", __func__); goto done; } ke->kex_dhgroup = htobe16(group->id); if (ikev2_add_buf(buf, sa->sa_dhrexchange) == -1) goto done; len = sizeof(*ke) + ibuf_size(sa->sa_dhrexchange); if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONCE) == -1) goto done; /* NONCE payload */ if ((pld = ikev2_add_payload(buf)) == NULL) goto done; if (ikev2_add_buf(buf, sa->sa_rnonce) == -1) goto done; len = ibuf_size(sa->sa_rnonce); if (env->sc_vendorid != 0) { vendor_id = ibuf_new(IKED_VENDOR_ID, strlen(IKED_VENDOR_ID)); ibuf_add(vendor_id, IKED_VERSION, strlen(IKED_VERSION)); if ((len = ikev2_add_vendor_id(buf, &pld, len, vendor_id)) == -1) goto done; } /* Fragmentation Notify*/ if (sa->sa_frag) { if ((len = ikev2_add_fragmentation(buf, &pld, len)) == -1) goto done; } if ((env->sc_nattmode != NATT_DISABLE) && msg->msg_local.ss_family != AF_UNSPEC) { if ((len = ikev2_add_nat_detection(env, buf, &pld, &resp, len)) == -1) goto done; } if (sa->sa_statevalid & IKED_REQ_CERT) { /* CERTREQ payload(s) */ if ((len = ikev2_add_certreq(buf, &pld, len, env->sc_certreq, env->sc_certreqtype)) == -1) goto done; if (env->sc_certreqtype != sa->sa_policy->pol_certreqtype && (len = ikev2_add_certreq(buf, &pld, len, NULL, sa->sa_policy->pol_certreqtype)) == -1) goto done; } if (sa->sa_sigsha2 && (len = ikev2_add_sighashnotify(buf, &pld, len)) == -1) goto done; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) goto done; if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1) goto done; (void)ikev2_pld_parse(env, hdr, &resp, 0); ibuf_free(sa->sa_2ndmsg); if ((sa->sa_2ndmsg = ibuf_dup(buf)) == NULL) { log_debug("%s: failed to copy 2nd message", __func__); goto done; } ret = ikev2_msg_send(env, &resp); done: ibuf_free(vendor_id); ikev2_msg_cleanup(env, &resp); return (ret); } int ikev2_send_auth_failed(struct iked *env, struct iked_sa *sa) { char dstid[IKED_ID_SIZE]; struct ikev2_notify *n; struct ibuf *buf = NULL; int ret = -1, exchange, response; if (ikev2_print_id(IKESA_DSTID(sa), dstid, sizeof(dstid)) == -1) bzero(dstid, sizeof(dstid)); log_info("%s: authentication failed for %s", SPI_SA(sa, __func__), dstid); /* Log certificate information */ ikev2_log_cert_info(SPI_SA(sa, __func__), sa->sa_hdr.sh_initiator ? &sa->sa_rcert : &sa->sa_icert); /* Notify payload */ if ((buf = ibuf_static()) == NULL) goto done; if ((n = ibuf_reserve(buf, sizeof(*n))) == NULL) goto done; n->n_protoid = IKEV2_SAPROTO_IKE; n->n_spisize = 0; n->n_type = htobe16(IKEV2_N_AUTHENTICATION_FAILED); if (sa->sa_hdr.sh_initiator) { exchange = IKEV2_EXCHANGE_INFORMATIONAL; response = 0; } else { exchange = IKEV2_EXCHANGE_IKE_AUTH; response = 1; } ret = ikev2_send_ike_e(env, sa, buf, IKEV2_PAYLOAD_NOTIFY, exchange, response); if (exchange == IKEV2_EXCHANGE_INFORMATIONAL) sa->sa_stateflags |= IKED_REQ_INF; done: ibuf_free(buf); /* cleanup SA after timeout */ sa_state(env, sa, IKEV2_STATE_CLOSING); timer_del(env, &sa->sa_timer); timer_set(env, &sa->sa_timer, ikev2_ike_sa_timeout, sa); timer_add(env, &sa->sa_timer, IKED_IKE_SA_DELETE_TIMEOUT); config_free_fragments(&sa->sa_fragments); ikev2_ike_sa_setreason(sa, "authentication failed"); return (ret); } ssize_t ikev2_add_error(struct iked *env, struct ibuf *buf, struct iked_message *msg) { struct ikev2_notify *n; struct iked_spi *rekey; uint16_t group; uint32_t spi32; uint64_t spi64; size_t len; uint8_t *ptr; switch (msg->msg_error) { case IKEV2_N_CHILD_SA_NOT_FOUND: break; case IKEV2_N_NO_PROPOSAL_CHOSEN: ikev2_log_proposal(msg->msg_sa, &msg->msg_proposals); break; case IKEV2_N_INVALID_KE_PAYLOAD: break; default: return (-1); } log_info("%s: %s", SPI_SA(msg->msg_sa, __func__), print_map(msg->msg_error, ikev2_n_map)); len = sizeof(*n); if ((ptr = ibuf_reserve(buf, len)) == NULL) return (-1); n = (struct ikev2_notify *)ptr; n->n_type = htobe16(msg->msg_error); switch (msg->msg_error) { case IKEV2_N_CHILD_SA_NOT_FOUND: rekey = &msg->msg_rekey; switch (rekey->spi_size) { case 4: spi32 = htobe32(rekey->spi); if (ibuf_add(buf, &spi32, sizeof(spi32)) != 0) return (-1); len += sizeof(spi32); break; case 8: spi64 = htobe64(rekey->spi); if (ibuf_add(buf, &spi64, sizeof(spi64)) != 0) return (-1); len += sizeof(spi64); break; default: log_debug("%s: invalid SPI size %d", __func__, rekey->spi_size); return (-1); } n->n_protoid = rekey->spi_protoid; n->n_spisize = rekey->spi_size; break; case IKEV2_N_INVALID_KE_PAYLOAD: group = htobe16(msg->msg_dhgroup); if (ibuf_add(buf, &group, sizeof(group)) != 0) return (-1); len += sizeof(group); n->n_protoid = 0; n->n_spisize = 0; break; default: n->n_protoid = 0; n->n_spisize = 0; break; } log_debug("%s: done", __func__); return (len); } int ikev2_record_dstid(struct iked *env, struct iked_sa *sa) { struct iked_sa *osa; osa = sa_dstid_lookup(env, sa); if (osa == sa) return (0); if (osa != NULL) { sa_dstid_remove(env, osa); if (env->sc_enforcesingleikesa && osa->sa_state < IKEV2_STATE_CLOSING) { log_info("%sreplaced by IKESA %s (identical DSTID)", SPI_SA(osa, NULL), print_spi(sa->sa_hdr.sh_ispi, 8)); if (osa->sa_state == IKEV2_STATE_ESTABLISHED) ikev2_disable_timer(env, osa); ikev2_ike_sa_setreason(osa, "sa replaced"); ikev2_ikesa_delete(env, osa, 0); timer_add(env, &osa->sa_timer, 3 * IKED_RETRANSMIT_TIMEOUT); } } osa = sa_dstid_insert(env, sa); if (osa != NULL) { /* XXX how can this fail */ log_info("%s: could not replace old IKESA %s", SPI_SA(sa, __func__), print_spi(osa->sa_hdr.sh_ispi, 8)); return (-1); } return (0); } int ikev2_send_error(struct iked *env, struct iked_sa *sa, struct iked_message *msg, uint8_t exchange) { struct ibuf *buf = NULL; int ret = -1; if (msg->msg_error == 0) return (0); if ((buf = ibuf_static()) == NULL) goto done; if (ikev2_add_error(env, buf, msg) == 0) goto done; ret = ikev2_send_ike_e(env, sa, buf, IKEV2_PAYLOAD_NOTIFY, exchange, 1); done: ibuf_free(buf); return (ret); } /* * Variant of ikev2_send_error() that can be used before encryption * is enabled. Based on ikev2_resp_ike_sa_init() code. */ int ikev2_send_init_error(struct iked *env, struct iked_message *msg) { struct iked_message resp; struct ike_header *hdr; struct ikev2_payload *pld; struct iked_sa *sa = msg->msg_sa; struct ibuf *buf; ssize_t len = 0; int ret = -1; if (sa->sa_hdr.sh_initiator) { log_debug("%s: called by initiator", __func__); return (-1); } if (msg->msg_error == 0) return (0); if ((buf = ikev2_msg_init(env, &resp, &msg->msg_peer, msg->msg_peerlen, &msg->msg_local, msg->msg_locallen, 1)) == NULL) goto done; resp.msg_sa = sa; resp.msg_fd = msg->msg_fd; resp.msg_natt = msg->msg_natt; resp.msg_msgid = 0; resp.msg_policy = sa->sa_policy; /* IKE header */ if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid, IKEV2_PAYLOAD_NOTIFY, IKEV2_EXCHANGE_IKE_SA_INIT, IKEV2_FLAG_RESPONSE)) == NULL) goto done; /* NOTIFY payload */ if ((pld = ikev2_add_payload(buf)) == NULL) goto done; if ((len = ikev2_add_error(env, buf, msg)) == 0) goto done; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) goto done; if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1) goto done; (void)ikev2_pld_parse(env, hdr, &resp, 0); ret = ikev2_msg_send(env, &resp); done: ikev2_msg_cleanup(env, &resp); return (ret); } int ikev2_handle_certreq(struct iked* env, struct iked_message *msg) { struct iked_certreq *cr; struct iked_sa *sa; uint8_t crtype; uint8_t more; if ((sa = msg->msg_sa) == NULL) return (-1); /* Ignore CERTREQ when policy uses PSK authentication */ if (sa->sa_policy->pol_auth.auth_method == IKEV2_AUTH_SHARED_KEY_MIC) return (0); if (sa->sa_hdr.sh_initiator) sa->sa_stateinit |= IKED_REQ_CERT; else sa->sa_statevalid |= IKED_REQ_CERT; /* * If we have to send a local certificate but did not receive an * optional CERTREQ, use our own certreq to find a local certificate. * We could alternatively extract the CA from the peer certificate * to find a matching local one. */ if (SIMPLEQ_EMPTY(&msg->msg_certreqs)) { if (sa->sa_policy->pol_certreqtype) crtype = sa->sa_policy->pol_certreqtype; else crtype = env->sc_certreqtype; ca_setreq(env, sa, &sa->sa_policy->pol_localid, crtype, 0, ibuf_data(env->sc_certreq), ibuf_size(env->sc_certreq), PROC_CERT); } else { while ((cr = SIMPLEQ_FIRST(&msg->msg_certreqs))) { if (SIMPLEQ_NEXT(cr, cr_entry) != NULL) more = 1; else more = 0; ca_setreq(env, sa, &sa->sa_policy->pol_localid, cr->cr_type, more, ibuf_data(cr->cr_data), ibuf_size(cr->cr_data), PROC_CERT); ibuf_free(cr->cr_data); SIMPLEQ_REMOVE_HEAD(&msg->msg_certreqs, cr_entry); free(cr); } } return (0); } int ikev2_resp_ike_eap_mschap(struct iked *env, struct iked_sa *sa, struct iked_message *msg) { uint8_t successmsg[EAP_MSCHAP_SUCCESS_SZ]; uint8_t ntresponse[EAP_MSCHAP_NTRESPONSE_SZ]; struct eap_msg *eap = &msg->msg_eap; struct iked_user *usr; uint8_t *pass; char *name = NULL; size_t passlen; int ret; switch (eap->eam_state) { case EAP_STATE_IDENTITY: sa->sa_eapid = eap->eam_identity; return (eap_challenge_request(env, sa, eap->eam_id)); case EAP_STATE_MSCHAPV2_CHALLENGE: if (eap->eam_user) { name = eap->eam_user; } else if (sa->sa_eapid) { name = sa->sa_eapid; } if (name == NULL) { log_info("%s: invalid response name", SPI_SA(sa, __func__)); return (-1); } if ((usr = user_lookup(env, name)) == NULL) { log_info("%s: unknown user '%s'", SPI_SA(sa, __func__), name); return (-1); } if ((pass = string2unicode(usr->usr_pass, &passlen)) == NULL) return (-1); mschap_nt_response(ibuf_data(sa->sa_eap.id_buf), eap->eam_challenge, usr->usr_name, strlen(usr->usr_name), pass, passlen, ntresponse); if (memcmp(ntresponse, eap->eam_ntresponse, sizeof(ntresponse)) != 0) { log_info("%s: '%s' authentication failed", SPI_SA(sa, __func__), usr->usr_name); freezero(pass, passlen); /* XXX should we send an EAP failure packet? */ return (-1); } bzero(&successmsg, sizeof(successmsg)); mschap_auth_response(pass, passlen, ntresponse, ibuf_data(sa->sa_eap.id_buf), eap->eam_challenge, usr->usr_name, strlen(usr->usr_name), successmsg); if ((sa->sa_eapmsk = ibuf_new(NULL, MSCHAP_MSK_SZ)) == NULL) { log_info("%s: failed to get MSK", SPI_SA(sa, __func__)); freezero(pass, passlen); return (-1); } mschap_msk(pass, passlen, ntresponse, ibuf_data(sa->sa_eapmsk)); freezero(pass, passlen); log_info("%s: '%s' authenticated", __func__, usr->usr_name); ret = eap_mschap_challenge(env, sa, eap->eam_id, eap->eam_msrid, successmsg, EAP_MSCHAP_SUCCESS_SZ); if (ret == 0) sa_state(env, sa, IKEV2_STATE_AUTH_SUCCESS); break; case EAP_STATE_MSCHAPV2_SUCCESS: return (eap_mschap_success(env, sa, eap->eam_id)); case EAP_STATE_SUCCESS: if (!sa_stateok(sa, IKEV2_STATE_AUTH_SUCCESS)) return (-1); return (eap_success(env, sa, msg->msg_eap.eam_id)); default: log_info("%s: eap ignored.", __func__); break; } return 0; } int ikev2_resp_ike_eap(struct iked *env, struct iked_sa *sa, struct iked_message *msg) { if (!sa_stateok(sa, IKEV2_STATE_EAP)) return (-1); switch (sa->sa_policy->pol_auth.auth_eap) { case EAP_TYPE_MSCHAP_V2: return ikev2_resp_ike_eap_mschap(env, sa, msg); } return -1; } int ikev2_resp_ike_auth(struct iked *env, struct iked_sa *sa) { struct ikev2_payload *pld; struct ikev2_cert *cert; struct ikev2_auth *auth; struct iked_id *id, *certid; struct ibuf *e = NULL; uint8_t firstpayload; int ret = -1; ssize_t len; int i; if (sa == NULL) return (-1); if (sa->sa_state == IKEV2_STATE_EAP) return (eap_identity_request(env, sa)); if (!sa_stateok(sa, IKEV2_STATE_VALID)) return (0); /* ignore */ if (ikev2_cp_setaddr(env, sa, AF_INET) < 0 || ikev2_cp_setaddr(env, sa, AF_INET6) < 0) return (-1); if (ikev2_childsa_negotiate(env, sa, &sa->sa_kex, &sa->sa_proposals, sa->sa_hdr.sh_initiator, 0) < 0) return (-1); /* New encrypted message buffer */ if ((e = ibuf_static()) == NULL) goto done; if (!sa->sa_localauth.id_type) { /* Downgrade the state */ sa_state(env, sa, IKEV2_STATE_AUTH_SUCCESS); } if (sa->sa_hdr.sh_initiator) { id = &sa->sa_iid; certid = &sa->sa_icert; } else { id = &sa->sa_rid; certid = &sa->sa_rcert; } if (sa->sa_state != IKEV2_STATE_EAP_VALID) { /* ID payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; firstpayload = IKEV2_PAYLOAD_IDr; if (ibuf_add_ibuf(e, id->id_buf) != 0) goto done; len = ibuf_size(id->id_buf); /* CERT payload */ if ((sa->sa_statevalid & IKED_REQ_CERT) && (certid->id_type != IKEV2_CERT_NONE)) { if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_CERT) == -1) goto done; if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((cert = ibuf_reserve(e, sizeof(*cert))) == NULL) goto done; cert->cert_type = certid->id_type; if (ibuf_add_ibuf(e, certid->id_buf) != 0) goto done; len = ibuf_size(certid->id_buf) + sizeof(*cert); for (i = 0; i < IKED_SCERT_MAX; i++) { if (sa->sa_scert[i].id_type == IKEV2_CERT_NONE) break; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_CERT) == -1) goto done; if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((cert = ibuf_reserve(e, sizeof(*cert))) == NULL) goto done; cert->cert_type = sa->sa_scert[i].id_type; if (ibuf_add_ibuf(e, sa->sa_scert[i].id_buf) != 0) goto done; len = ibuf_size(sa->sa_scert[i].id_buf) + sizeof(*cert); } } if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_AUTH) == -1) goto done; } else firstpayload = IKEV2_PAYLOAD_AUTH; /* AUTH payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((auth = ibuf_reserve(e, sizeof(*auth))) == NULL) goto done; auth->auth_method = sa->sa_localauth.id_type; if (ibuf_add_ibuf(e, sa->sa_localauth.id_buf) != 0) goto done; len = ibuf_size(sa->sa_localauth.id_buf) + sizeof(*auth); /* CP payload */ if (sa->sa_cp) { if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_CP) == -1) goto done; if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((len = ikev2_resp_add_cp(env, sa, e)) == -1) goto done; } if (sa->sa_ipcompr.ic_transform && (len = ikev2_add_ipcompnotify(env, e, &pld, len, sa, 0)) == -1) goto done; if (sa->sa_used_transport_mode && (len = ikev2_add_transport_mode(env, e, &pld, len, sa)) == -1) goto done; /* MOBIKE */ if (sa->sa_mobike && (len = ikev2_add_mobike(e, &pld, len)) == -1) goto done; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1) goto done; /* SA payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((len = ikev2_add_proposals(env, sa, e, &sa->sa_proposals, 0, sa->sa_hdr.sh_initiator, 0, 1)) == -1) goto done; if ((len = ikev2_add_ts(e, &pld, len, sa, 0)) == -1) goto done; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) goto done; ret = ikev2_msg_send_encrypt(env, sa, &e, IKEV2_EXCHANGE_IKE_AUTH, firstpayload, 1); if (ret == 0) ret = ikev2_childsa_enable(env, sa); if (ret == 0) { sa_state(env, sa, IKEV2_STATE_ESTABLISHED); /* Delete exchange timeout. */ timer_del(env, &sa->sa_timer); ikev2_enable_timer(env, sa); ikev2_log_established(sa); ikev2_record_dstid(env, sa); } done: if (ret) ikev2_childsa_delete(env, sa, 0, 0, NULL, 1); ibuf_free(e); return (ret); } int ikev2_send_ike_e(struct iked *env, struct iked_sa *sa, struct ibuf *buf, uint8_t firstpayload, uint8_t exchange, int response) { struct ikev2_payload *pld; struct ibuf *e = NULL; int ret = -1; /* New encrypted message buffer */ if ((e = ibuf_static()) == NULL) goto done; if (buf) { if ((pld = ikev2_add_payload(e)) == NULL) goto done; if (ibuf_add_ibuf(e, buf) != 0) goto done; if (ikev2_next_payload(pld, ibuf_size(buf), IKEV2_PAYLOAD_NONE) == -1) goto done; } ret = ikev2_msg_send_encrypt(env, sa, &e, exchange, firstpayload, response); done: ibuf_free(e); return (ret); } int ikev2_set_sa_proposal(struct iked_sa *sa, struct iked_policy *pol, unsigned int proto) { struct iked_proposal *prop, *copy; struct iked_transform *xform; unsigned int i; /* create copy of the policy proposals */ config_free_proposals(&sa->sa_proposals, proto); TAILQ_FOREACH(prop, &pol->pol_proposals, prop_entry) { if (proto != 0 && prop->prop_protoid != proto) continue; if ((copy = config_add_proposal(&sa->sa_proposals, prop->prop_id, prop->prop_protoid)) == NULL) return (-1); for (i = 0; i < prop->prop_nxforms; i++) { xform = &prop->prop_xforms[i]; if (config_add_transform(copy, xform->xform_type, xform->xform_id, xform->xform_length, xform->xform_keylength) != 0) return (-1); } } return (0); } int ikev2_send_create_child_sa(struct iked *env, struct iked_sa *sa, struct iked_spi *rekey, uint8_t protoid, uint16_t proposed_group) { struct iked_policy *pol = sa->sa_policy; struct iked_childsa *csa = NULL, *csb = NULL; struct iked_transform *xform; struct ikev2_notify *n; struct ikev2_payload *pld = NULL; struct ikev2_keyexchange *ke; struct dh_group *group; struct ibuf *e = NULL, *nonce = NULL; uint8_t *ptr; uint8_t firstpayload; uint32_t spi; ssize_t len = 0; int initiator, ret = -1; if (rekey) log_debug("%s: rekeying %s spi %s", __func__, print_map(rekey->spi_protoid, ikev2_saproto_map), print_spi(rekey->spi, rekey->spi_size)); else log_debug("%s: creating new CHILD SAs", __func__); /* XXX cannot initiate multiple concurrent CREATE_CHILD_SA exchanges */ if (sa->sa_stateflags & (IKED_REQ_CHILDSA|IKED_REQ_INF)) { log_debug("%s: another exchange already active", __func__); return (-1); } ibuf_free(sa->sa_simult); sa->sa_simult = NULL; sa->sa_rekeyspi = 0; /* clear rekey spi */ initiator = sa->sa_hdr.sh_initiator ? 1 : 0; if (rekey && ((csa = childsa_lookup(sa, rekey->spi, rekey->spi_protoid)) == NULL || (csb = csa->csa_peersa) == NULL)) { log_debug("%s: CHILD SA %s wasn't found", __func__, print_spi(rekey->spi, rekey->spi_size)); goto done; } /* Generate new nonce */ if ((nonce = ibuf_random(IKED_NONCE_SIZE)) == NULL) goto done; /* Update initiator nonce */ ibuf_free(sa->sa_inonce); sa->sa_inonce = nonce; if ((e = ibuf_static()) == NULL) goto done; if ((pol->pol_flags & IKED_POLICY_IPCOMP) && (len = ikev2_add_ipcompnotify(env, e, &pld, 0, sa, 1)) == -1) goto done; if ((pol->pol_flags & IKED_POLICY_TRANSPORT) && (len = ikev2_add_transport_mode(env, e, &pld, len, sa)) == -1) goto done; if (pld) { firstpayload = IKEV2_PAYLOAD_NOTIFY; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1) goto done; } else firstpayload = IKEV2_PAYLOAD_SA; /* SA payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; /* * We need to reset the sa_proposal. Otherwise it would be * left over from the IKE_AUTH exchange and would not contain * any DH groups (e.g. for ESP child SAs). */ if (ikev2_set_sa_proposal(sa, pol, protoid) < 0) { log_debug("%s: ikev2_set_sa_proposal failed", __func__); goto done; } if ((len = ikev2_add_proposals(env, sa, e, &sa->sa_proposals, protoid, 1, 0, 0)) == -1) goto done; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONCE) == -1) goto done; /* NONCE payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if (ikev2_add_buf(e, nonce) == -1) goto done; len = ibuf_size(nonce); if ((xform = config_findtransform(&pol->pol_proposals, IKEV2_XFORMTYPE_DH, protoid)) && xform->xform_id != IKEV2_XFORMDH_NONE) { log_debug("%s: enable PFS", __func__); ikev2_sa_cleanup_dh(sa); if (proposed_group) { if ((sa->sa_dhgroup = group_get(proposed_group)) == NULL) { log_debug("%s: failed to get group", __func__); goto done; } } if (ikev2_sa_initiator_dh(sa, NULL, protoid, NULL) < 0) { log_debug("%s: failed to setup DH", __func__); goto done; } if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_KE) == -1) goto done; /* KE payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((ke = ibuf_reserve(e, sizeof(*ke))) == NULL) goto done; if ((group = sa->sa_dhgroup) == NULL) { log_debug("%s: invalid dh", __func__); goto done; } ke->kex_dhgroup = htobe16(group->id); if (ikev2_add_buf(e, sa->sa_dhiexchange) == -1) goto done; len = sizeof(*ke) + ibuf_size(sa->sa_dhiexchange); } if ((len = ikev2_add_ts(e, &pld, len, sa, !initiator)) == -1) goto done; if (rekey) { if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NOTIFY) == -1) goto done; /* REKEY_SA notification */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((n = ibuf_reserve(e, sizeof(*n))) == NULL) goto done; n->n_type = htobe16(IKEV2_N_REKEY_SA); n->n_protoid = rekey->spi_protoid; n->n_spisize = rekey->spi_size; if ((ptr = ibuf_reserve(e, rekey->spi_size)) == NULL) goto done; len = rekey->spi_size; spi = htobe32((uint32_t)csa->csa_peerspi); memcpy(ptr, &spi, rekey->spi_size); len += sizeof(*n); } if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) goto done; ret = ikev2_msg_send_encrypt(env, sa, &e, IKEV2_EXCHANGE_CREATE_CHILD_SA, firstpayload, 0); if (ret == 0) { if (rekey) { csa->csa_rekey = 1; csb->csa_rekey = 1; /* * Remember the peer spi of the rekeyed * SA for ikev2_init_create_child_sa(). */ sa->sa_rekeyspi = csa->csa_peerspi; } sa->sa_stateflags |= IKED_REQ_CHILDSA; } done: ibuf_free(e); return (ret); } void ikev2_ike_sa_rekey(struct iked *env, void *arg) { struct iked_sa *sa = arg; struct iked_sa *nsa = NULL; struct ikev2_payload *pld = NULL; struct ikev2_keyexchange *ke; struct dh_group *group; struct ibuf *e = NULL, *nonce = NULL; ssize_t len = 0; int ret = -1; log_debug("%s: IKE SA %p ispi %s rspi %s", __func__, sa, print_spi(sa->sa_hdr.sh_ispi, 8), print_spi(sa->sa_hdr.sh_rspi, 8)); if (sa->sa_nexti) { log_debug("%s: already rekeying", __func__); goto done; } if (sa->sa_stateflags & (IKED_REQ_CHILDSA|IKED_REQ_INF)) { /* * We cannot initiate multiple concurrent CREATE_CHILD_SA * exchanges, so retry again fast. */ log_info("%s: busy, delaying rekey", SPI_SA(sa, __func__)); ikev2_ike_sa_rekey_schedule_fast(env, sa); return; } /* We need to make sure the rekeying finishes in time */ timer_set(env, &sa->sa_rekey, ikev2_ike_sa_rekey_timeout, sa); timer_add(env, &sa->sa_rekey, IKED_IKE_SA_REKEY_TIMEOUT); if ((nsa = sa_new(env, 0, 0, 1, sa->sa_policy)) == NULL) { log_debug("%s: failed to get new SA", __func__); goto done; } if (ikev2_sa_initiator(env, nsa, sa, NULL)) { log_debug("%s: failed to setup DH", __func__); goto done; } sa_state(env, nsa, IKEV2_STATE_AUTH_SUCCESS); nonce = nsa->sa_inonce; if ((e = ibuf_static()) == NULL) goto done; /* SA payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; /* just reuse the old IKE SA proposals */ if ((len = ikev2_add_proposals(env, nsa, e, &sa->sa_proposals, IKEV2_SAPROTO_IKE, 1, 1, 0)) == -1) goto done; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONCE) == -1) goto done; /* NONCE payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if (ikev2_add_buf(e, nonce) == -1) goto done; len = ibuf_size(nonce); if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_KE) == -1) goto done; /* KE payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((ke = ibuf_reserve(e, sizeof(*ke))) == NULL) goto done; if ((group = nsa->sa_dhgroup) == NULL) { log_debug("%s: invalid dh", __func__); goto done; } ke->kex_dhgroup = htobe16(group->id); if (ikev2_add_buf(e, nsa->sa_dhiexchange) == -1) goto done; len = sizeof(*ke) + ibuf_size(nsa->sa_dhiexchange); if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) goto done; ret = ikev2_msg_send_encrypt(env, sa, &e, IKEV2_EXCHANGE_CREATE_CHILD_SA, IKEV2_PAYLOAD_SA, 0); if (ret == 0) { sa->sa_stateflags |= IKED_REQ_CHILDSA; sa->sa_nexti = nsa; nsa->sa_previ = sa; sa->sa_tmpfail = 0; nsa = NULL; } done: if (nsa) { ikev2_ike_sa_setreason(nsa, "failed to send CREATE_CHILD_SA"); sa_free(env, nsa); } ibuf_free(e); if (ret == 0) log_debug("%s: create child SA sent", __func__); else log_debug("%s: could not send create child SA", __func__); /* XXX should we try again in case of ret != 0 ? */ } int ikev2_nonce_cmp(struct ibuf *a, struct ibuf *b) { size_t alen, blen, len; int ret; alen = ibuf_size(a); blen = ibuf_size(b); len = MINIMUM(alen, blen); ret = memcmp(ibuf_data(a), ibuf_data(b), len); if (ret == 0) ret = (alen < blen ? -1 : 1); return (ret); } int ikev2_init_create_child_sa(struct iked *env, struct iked_message *msg) { struct iked_childsa *csa = NULL; struct iked_proposal *prop; struct iked_sa *sa = msg->msg_sa; struct iked_sa *nsa, *dsa; struct iked_spi *spi; struct ikev2_delete *del; struct ibuf *buf = NULL; struct ibuf *ni, *nr; uint32_t spi32; int pfs = 0, ret = -1; if (!ikev2_msg_frompeer(msg) || (sa->sa_stateflags & (IKED_REQ_CHILDSA|IKED_REQ_INF)) == 0) return (0); if (sa->sa_nexti != NULL && sa->sa_tmpfail) { sa->sa_stateflags &= ~IKED_REQ_CHILDSA; ikev2_ike_sa_setreason(sa->sa_nexti, "tmpfail"); sa_free(env, sa->sa_nexti); sa->sa_nexti = NULL; timer_set(env, &sa->sa_rekey, ikev2_ike_sa_rekey, sa); ikev2_ike_sa_rekey_schedule_fast(env, sa); log_info("%s: IKESA rekey delayed", SPI_SA(sa, __func__)); return (0); } if (msg->msg_prop == NULL || TAILQ_EMPTY(&msg->msg_proposals)) { log_info("%s: no proposal specified", SPI_SA(sa, __func__)); return (-1); } if (proposals_negotiate(&sa->sa_proposals, &sa->sa_proposals, &msg->msg_proposals, 1, -1) != 0) { log_info("%s: no proposal chosen", SPI_SA(sa, __func__)); ikestat_inc(env, ikes_sa_proposals_negotiate_failures); return (-1); } TAILQ_FOREACH(prop, &sa->sa_proposals, prop_entry) { if (prop->prop_protoid == msg->msg_prop->prop_protoid) break; } if (prop == NULL) { log_info("%s: failed to find %s proposals", SPI_SA(sa, __func__), print_map(msg->msg_prop->prop_protoid, ikev2_saproto_map)); return (-1); } /* IKE SA rekeying */ if (prop->prop_protoid == IKEV2_SAPROTO_IKE) { if (sa->sa_nexti == NULL) { log_info("%s: missing IKE SA for rekeying", SPI_SA(sa, __func__)); return (-1); } /* Update the responder SPI */ /* XXX sa_new() is just a lookup, so nsa == sa->sa_nexti */ spi = &msg->msg_prop->prop_peerspi; if ((nsa = sa_new(env, sa->sa_nexti->sa_hdr.sh_ispi, spi->spi, 1, NULL)) == NULL || nsa != sa->sa_nexti) { log_info("%s: invalid rekey SA", SPI_SA(sa, __func__)); if (nsa) { ikev2_ike_sa_setreason(nsa, "invalid SA for rekey"); sa_free(env, nsa); } ikev2_ike_sa_setreason(sa->sa_nexti, "invalid SA nexti"); sa_free(env, sa->sa_nexti); sa->sa_nexti = NULL; /* reset by sa_free */ return (-1); } if (ikev2_sa_initiator(env, nsa, sa, msg) == -1) { log_info("%s: failed to get IKE keys", SPI_SA(sa, __func__)); return (-1); } sa->sa_stateflags &= ~IKED_REQ_CHILDSA; if (sa->sa_nextr) { /* * Resolve simultaneous IKE SA rekeying by * deleting the SA with the lowest NONCE. */ log_info("%s: resolving simultaneous IKE SA rekeying", SPI_SA(sa, __func__)); /* ni: minimum nonce of sa_nexti */ if (ikev2_nonce_cmp(sa->sa_nexti->sa_inonce, sa->sa_nexti->sa_rnonce) < 0) ni = sa->sa_nexti->sa_inonce; else ni = sa->sa_nexti->sa_rnonce; /* nr: minimum nonce of sa_nextr */ if (ikev2_nonce_cmp(sa->sa_nextr->sa_inonce, sa->sa_nextr->sa_rnonce) < 0) nr = sa->sa_nextr->sa_inonce; else nr = sa->sa_nextr->sa_rnonce; /* delete SA with minumum nonce */ if (ikev2_nonce_cmp(ni, nr) < 0) { dsa = sa->sa_nexti; nsa = sa->sa_nextr; } else { dsa = sa->sa_nextr; nsa = sa->sa_nexti; } /* unlink sa_nextr */ sa->sa_nextr->sa_prevr = NULL; sa->sa_nextr = NULL; /* Setup address, socket and NAT information */ sa_address(dsa, &dsa->sa_peer, (struct sockaddr *)&sa->sa_peer.addr); sa_address(dsa, &dsa->sa_local, (struct sockaddr *)&sa->sa_local.addr); dsa->sa_fd = sa->sa_fd; dsa->sa_natt = sa->sa_natt; dsa->sa_udpencap = sa->sa_udpencap; ikev2_ike_sa_setreason(dsa, "resolving simultaneous rekeying"); ikev2_ikesa_delete(env, dsa, dsa->sa_hdr.sh_initiator); } /* unlink sa_nexti */ sa->sa_nexti->sa_previ = NULL; sa->sa_nexti = NULL; return (ikev2_ikesa_enable(env, sa, nsa)); } /* Child SA rekeying */ if (sa->sa_rekeyspi && (csa = childsa_lookup(sa, sa->sa_rekeyspi, prop->prop_protoid)) != NULL) { log_info("%s: rekeying CHILD SA old %s spi %s", SPI_SA(sa, __func__), print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size), print_spi(prop->prop_peerspi.spi, prop->prop_peerspi.spi_size)); } /* check KE payload for PFS */ if (ibuf_length(msg->msg_ke)) { log_debug("%s: using PFS", __func__); if (ikev2_sa_initiator_dh(sa, msg, prop->prop_protoid, NULL) < 0) { log_info("%s: failed to setup DH", SPI_SA(sa, __func__)); return (ret); } if (sa->sa_dhpeer == NULL) { log_info("%s: no peer DH", SPI_SA(sa, __func__)); return (ret); } pfs = 1; /* XXX check group against policy ? */ /* XXX should proposals_negotiate do this? */ } /* Update responder's nonce */ if (!ibuf_length(msg->msg_nonce)) { log_info("%s: responder didn't send nonce", SPI_SA(sa, __func__)); return (-1); } ibuf_free(sa->sa_rnonce); sa->sa_rnonce = msg->msg_nonce; msg->msg_nonce = NULL; if (csa && (nr = sa->sa_simult) != NULL) { log_info("%s: resolving simultaneous CHILD SA rekeying", SPI_SA(sa, __func__)); /* set ni to minimum nonce for exchange initiated by us */ if (ikev2_nonce_cmp(sa->sa_inonce, sa->sa_rnonce) < 0) ni = sa->sa_inonce; else ni = sa->sa_rnonce; /* * If the exchange initated by us has smaller nonce, * then we have to delete our SAs. */ if (ikev2_nonce_cmp(ni, nr) < 0) { ret = ikev2_childsa_delete_proposed(env, sa, &sa->sa_proposals); goto done; } } if (ikev2_childsa_negotiate(env, sa, &sa->sa_kex, &sa->sa_proposals, 1, pfs)) { log_info("%s: failed to get CHILD SAs", SPI_SA(sa, __func__)); return (-1); } if (csa) { /* Child SA rekeying */ if ((buf = ibuf_static()) == NULL) goto done; if ((del = ibuf_reserve(buf, sizeof(*del))) == NULL) goto done; del->del_protoid = prop->prop_protoid; del->del_spisize = sizeof(spi32); del->del_nspi = htobe16(1); spi32 = htobe32(csa->csa_spi.spi); if (ibuf_add(buf, &spi32, sizeof(spi32))) goto done; if (ikev2_send_ike_e(env, sa, buf, IKEV2_PAYLOAD_DELETE, IKEV2_EXCHANGE_INFORMATIONAL, 0)) goto done; sa->sa_stateflags |= IKED_REQ_INF; } ret = ikev2_childsa_enable(env, sa); done: sa->sa_stateflags &= ~IKED_REQ_CHILDSA; if (ret) ikev2_childsa_delete(env, sa, 0, 0, NULL, 1); else if (csa) { /* delete the rekeyed SA pair */ ikev2_childsa_delete(env, sa, csa->csa_saproto, csa->csa_peerspi, NULL, 0); } ibuf_free(buf); return (ret); } int ikev2_ikesa_enable(struct iked *env, struct iked_sa *sa, struct iked_sa *nsa) { struct iked_childsa *csa, *csatmp, *ipcomp; struct iked_flow *flow, *flowtmp; struct iked_proposal *prop, *proptmp; int i; log_debug("%s: IKE SA %p ispi %s rspi %s replaced" " by SA %p ispi %s rspi %s ", __func__, sa, print_spi(sa->sa_hdr.sh_ispi, 8), print_spi(sa->sa_hdr.sh_rspi, 8), nsa, print_spi(nsa->sa_hdr.sh_ispi, 8), print_spi(nsa->sa_hdr.sh_rspi, 8)); /* Transfer socket and NAT information */ nsa->sa_fd = sa->sa_fd; nsa->sa_natt = sa->sa_natt; nsa->sa_udpencap = sa->sa_udpencap; nsa->sa_usekeepalive = sa->sa_usekeepalive; nsa->sa_mobike = sa->sa_mobike; nsa->sa_frag = sa->sa_frag; /* Transfer old addresses */ memcpy(&nsa->sa_local, &sa->sa_local, sizeof(nsa->sa_local)); memcpy(&nsa->sa_peer, &sa->sa_peer, sizeof(nsa->sa_peer)); memcpy(&nsa->sa_peer_loaded, &sa->sa_peer_loaded, sizeof(nsa->sa_peer_loaded)); /* Transfer all Child SAs and flows from the old IKE SA */ TAILQ_FOREACH_SAFE(flow, &sa->sa_flows, flow_entry, flowtmp) { TAILQ_REMOVE(&sa->sa_flows, flow, flow_entry); TAILQ_INSERT_TAIL(&nsa->sa_flows, flow, flow_entry); flow->flow_ikesa = nsa; flow->flow_local = &nsa->sa_local; flow->flow_peer = &nsa->sa_peer; } TAILQ_FOREACH_SAFE(csa, &sa->sa_childsas, csa_entry, csatmp) { TAILQ_REMOVE(&sa->sa_childsas, csa, csa_entry); TAILQ_INSERT_TAIL(&nsa->sa_childsas, csa, csa_entry); csa->csa_ikesa = nsa; if (csa->csa_dir == IPSP_DIRECTION_IN) { csa->csa_local = &nsa->sa_peer; csa->csa_peer = &nsa->sa_local; } else { csa->csa_local = &nsa->sa_local; csa->csa_peer = &nsa->sa_peer; } if ((ipcomp = csa->csa_bundled) != NULL) { ipcomp->csa_ikesa = nsa; ipcomp->csa_local = csa->csa_local; ipcomp->csa_peer = csa->csa_peer; } } /* Transfer all non-IKE proposals */ TAILQ_FOREACH_SAFE(prop, &sa->sa_proposals, prop_entry, proptmp) { if (prop->prop_protoid == IKEV2_SAPROTO_IKE) continue; TAILQ_REMOVE(&sa->sa_proposals, prop, prop_entry); TAILQ_INSERT_TAIL(&nsa->sa_proposals, prop, prop_entry); } /* Preserve ID information */ ibuf_free(nsa->sa_iid.id_buf); ibuf_free(nsa->sa_rid.id_buf); ibuf_free(nsa->sa_icert.id_buf); ibuf_free(nsa->sa_rcert.id_buf); if (sa->sa_hdr.sh_initiator == nsa->sa_hdr.sh_initiator) { nsa->sa_iid = sa->sa_iid; nsa->sa_rid = sa->sa_rid; nsa->sa_icert = sa->sa_icert; nsa->sa_rcert = sa->sa_rcert; } else { /* initiator and responder role swapped */ nsa->sa_iid = sa->sa_rid; nsa->sa_rid = sa->sa_iid; nsa->sa_icert = sa->sa_rcert; nsa->sa_rcert = sa->sa_icert; } for (i = 0; i < IKED_SCERT_MAX; i++) nsa->sa_scert[i] = sa->sa_scert[i]; /* duplicate the actual buffer */ nsa->sa_iid.id_buf = ibuf_dup(nsa->sa_iid.id_buf); nsa->sa_rid.id_buf = ibuf_dup(nsa->sa_rid.id_buf); nsa->sa_icert.id_buf = ibuf_dup(nsa->sa_icert.id_buf); nsa->sa_rcert.id_buf = ibuf_dup(nsa->sa_rcert.id_buf); for (i = 0; i < IKED_SCERT_MAX; i++) nsa->sa_scert[i].id_buf = ibuf_dup(nsa->sa_scert[i].id_buf); /* Transfer sa_addrpool address */ if (sa->sa_addrpool) { RB_REMOVE(iked_addrpool, &env->sc_addrpool, sa); nsa->sa_addrpool = sa->sa_addrpool; sa->sa_addrpool = NULL; RB_INSERT(iked_addrpool, &env->sc_addrpool, nsa); } if (sa->sa_addrpool6) { RB_REMOVE(iked_addrpool6, &env->sc_addrpool6, sa); nsa->sa_addrpool6 = sa->sa_addrpool6; sa->sa_addrpool6 = NULL; RB_INSERT(iked_addrpool6, &env->sc_addrpool6, nsa); } nsa->sa_cp = sa->sa_cp; nsa->sa_cp_addr = sa->sa_cp_addr; sa->sa_cp_addr = NULL; nsa->sa_cp_addr6 = sa->sa_cp_addr6; sa->sa_cp_addr6 = NULL; nsa->sa_cp_dns = sa->sa_cp_dns; sa->sa_cp_dns = NULL; /* Transfer other attributes */ if (sa->sa_dstid_entry_valid) { sa_dstid_remove(env, sa); sa_dstid_insert(env, nsa); } if (sa->sa_tag) { nsa->sa_tag = sa->sa_tag; sa->sa_tag = NULL; } if (sa->sa_eapid) { nsa->sa_eapid = sa->sa_eapid; sa->sa_eapid = NULL; } log_info("%srekeyed as new IKESA %s (enc %s%s%s group %s prf %s)", SPI_SA(sa, NULL), print_spi(nsa->sa_hdr.sh_ispi, 8), print_xf(nsa->sa_encr->encr_id, cipher_keylength(nsa->sa_encr) - nsa->sa_encr->encr_saltlength, ikeencxfs), nsa->sa_encr->encr_authid ? "" : " auth ", nsa->sa_encr->encr_authid ? "" : print_xf(nsa->sa_integr->hash_id, hash_keylength(nsa->sa_integr), authxfs), print_xf(nsa->sa_dhgroup->id, 0, groupxfs), print_xf(nsa->sa_prf->hash_id, hash_keylength(sa->sa_prf), prfxfs)); sa_state(env, nsa, IKEV2_STATE_ESTABLISHED); ikev2_enable_timer(env, nsa); ikestat_inc(env, ikes_sa_rekeyed); nsa->sa_stateflags = nsa->sa_statevalid; /* XXX */ /* unregister DPD keep alive timer & rekey first */ if (sa->sa_state == IKEV2_STATE_ESTABLISHED) ikev2_disable_timer(env, sa); ikev2_ike_sa_setreason(sa, "SA rekeyed"); ikev2_ikesa_delete(env, sa, nsa->sa_hdr.sh_initiator); return (0); } void ikev2_ikesa_delete(struct iked *env, struct iked_sa *sa, int initiator) { struct ibuf *buf = NULL; struct ikev2_delete *del; if (initiator) { /* XXX: Can not have simultaneous INFORMATIONAL exchanges */ if (sa->sa_stateflags & IKED_REQ_INF) goto done; /* Send PAYLOAD_DELETE */ if ((buf = ibuf_static()) == NULL) goto done; if ((del = ibuf_reserve(buf, sizeof(*del))) == NULL) goto done; del->del_protoid = IKEV2_SAPROTO_IKE; del->del_spisize = 0; del->del_nspi = 0; if (ikev2_send_ike_e(env, sa, buf, IKEV2_PAYLOAD_DELETE, IKEV2_EXCHANGE_INFORMATIONAL, 0) == -1) goto done; sa->sa_stateflags |= IKED_REQ_INF; log_info("%s: sent delete, closing SA", SPI_SA(sa, __func__)); done: ibuf_free(buf); sa_state(env, sa, IKEV2_STATE_CLOSED); } else { sa_state(env, sa, IKEV2_STATE_CLOSING); } /* Remove IKE-SA after timeout, e.g. if we don't get a delete */ timer_set(env, &sa->sa_timer, ikev2_ike_sa_timeout, sa); timer_add(env, &sa->sa_timer, IKED_IKE_SA_DELETE_TIMEOUT); ikev2_ike_sa_setreason(sa, "deleting SA"); } void ikev2_ikesa_recv_delete(struct iked *env, struct iked_sa *sa) { log_info("%s: received delete", SPI_SA(sa, __func__)); if (sa->sa_nexti) { /* * We initiated rekeying, but since sa_nexti is still set * we have to assume that the the peer did not receive our * rekey message. So remove the initiated SA and -- if * sa_nextr is set -- keep the responder SA instead. */ if (sa->sa_nextr) { log_debug("%s: resolving simultaneous IKE SA rekeying", SPI_SA(sa, __func__)); ikev2_ikesa_enable(env, sa, sa->sa_nextr); /* unlink sa_nextr */ sa->sa_nextr->sa_prevr = NULL; sa->sa_nextr = NULL; } ikev2_ike_sa_setreason(sa->sa_nexti, "received delete (simultaneous rekeying)"); sa_free(env, sa->sa_nexti); sa->sa_nexti = NULL; /* reset by sa_free */ } ikev2_ike_sa_setreason(sa, "received delete"); if (env->sc_stickyaddress) { /* delay deletion if client reconnects soon */ sa_state(env, sa, IKEV2_STATE_CLOSING); timer_del(env, &sa->sa_timer); timer_set(env, &sa->sa_timer, ikev2_ike_sa_timeout, sa); timer_add(env, &sa->sa_timer, 3 * IKED_RETRANSMIT_TIMEOUT); } else { sa_state(env, sa, IKEV2_STATE_CLOSED); } } int ikev2_resp_create_child_sa(struct iked *env, struct iked_message *msg) { struct iked_childsa *csa = NULL; struct iked_proposal *prop; struct iked_proposals proposals; struct iked_kex *kex, *kextmp = NULL; struct iked_sa *nsa = NULL, *sa = msg->msg_sa; struct iked_spi *spi, *rekey = &msg->msg_rekey; struct iked_transform *xform; struct ikev2_keyexchange *ke; struct ikev2_payload *pld = NULL; struct ibuf *e = NULL, *nonce = NULL; uint8_t firstpayload; ssize_t len = 0; int initiator, protoid, rekeying = 1; int ret = -1; int pfs = 0; initiator = sa->sa_hdr.sh_initiator ? 1 : 0; if (!ikev2_msg_frompeer(msg) || msg->msg_prop == NULL) return (0); TAILQ_INIT(&proposals); if ((protoid = rekey->spi_protoid) == 0) { /* * If REKEY_SA notification is not present, then it's either * IKE SA rekeying or the client wants to create additional * CHILD SAs */ if (msg->msg_prop->prop_protoid == IKEV2_SAPROTO_IKE) { protoid = rekey->spi_protoid = IKEV2_SAPROTO_IKE; if (sa->sa_hdr.sh_initiator) rekey->spi = sa->sa_hdr.sh_rspi; else rekey->spi = sa->sa_hdr.sh_ispi; rekey->spi_size = 8; } else { protoid = msg->msg_prop->prop_protoid; rekeying = 0; } } if (rekeying) log_debug("%s: rekey %s spi %s", __func__, print_map(rekey->spi_protoid, ikev2_saproto_map), print_spi(rekey->spi, rekey->spi_size)); else log_debug("%s: creating new %s SA", __func__, print_map(protoid, ikev2_saproto_map)); if (protoid == IKEV2_SAPROTO_IKE) { if ((sa->sa_stateflags & IKED_REQ_CHILDSA) && !(sa->sa_nexti)) { log_debug("%s: Ignore IKE SA rekey: waiting for Child " "SA response.", __func__); /* Ignore, don't send error */ msg->msg_valid = 0; return (0); } /* IKE SA rekeying */ spi = &msg->msg_prop->prop_peerspi; if ((nsa = sa_new(env, spi->spi, 0, 0, msg->msg_policy)) == NULL) { log_debug("%s: failed to get new SA", __func__); return (ret); } if (ikev2_sa_responder(env, nsa, sa, msg)) { log_debug("%s: failed to get IKE SA keys", __func__); return (ret); } sa_state(env, nsa, IKEV2_STATE_AUTH_SUCCESS); nonce = nsa->sa_rnonce; kex = &nsa->sa_kex; } else { /* Child SA creating/rekeying */ if ((kex = kextmp = calloc(1, sizeof(*kextmp))) == NULL) { log_debug("%s: calloc kex", __func__); goto fail; } if (proposals_negotiate(&proposals, &sa->sa_policy->pol_proposals, &msg->msg_proposals, 1, msg->msg_dhgroup) != 0) { log_info("%s: no proposal chosen", __func__); msg->msg_error = IKEV2_N_NO_PROPOSAL_CHOSEN; ikestat_inc(env, ikes_sa_proposals_negotiate_failures); goto fail; } /* Check KE payload for PFS, ignore if DH transform is NONE */ if (((xform = config_findtransform(&proposals, IKEV2_XFORMTYPE_DH, protoid)) != NULL) && xform->xform_id != IKEV2_XFORMDH_NONE && ibuf_length(msg->msg_ke)) { log_debug("%s: using PFS", __func__); if (ikev2_sa_responder_dh(kex, &proposals, msg, protoid) < 0) { log_debug("%s: failed to setup DH", __func__); goto fail; } pfs = 1; /* XXX check group against policy ? */ } /* Update peer SPI */ TAILQ_FOREACH(prop, &proposals, prop_entry) { if (prop->prop_protoid == protoid) break; } if (prop == NULL) { log_debug("%s: failed to find %s proposals", __func__, print_map(protoid, ikev2_saproto_map)); goto fail; } else prop->prop_peerspi = msg->msg_prop->prop_peerspi; /* Set rekeying flags on Child SAs */ if (rekeying) { if ((csa = childsa_lookup(sa, rekey->spi, rekey->spi_protoid)) == NULL) { log_info("%s: CHILD SA %s wasn't found", SPI_SA(sa, __func__), print_spi(rekey->spi, rekey->spi_size)); msg->msg_error = IKEV2_N_CHILD_SA_NOT_FOUND; goto fail; } if (!csa->csa_loaded || !csa->csa_peersa || !csa->csa_peersa->csa_loaded) { log_info("%s: CHILD SA %s is not loaded" " or no peer SA", SPI_SA(sa, __func__), print_spi(rekey->spi, rekey->spi_size)); msg->msg_error = IKEV2_N_CHILD_SA_NOT_FOUND; goto fail; } csa->csa_rekey = 1; csa->csa_peersa->csa_rekey = 1; } /* Update initiator's nonce */ if (!ibuf_length(msg->msg_nonce)) { log_debug("%s: initiator didn't send nonce", __func__); goto fail; } ibuf_free(kex->kex_inonce); kex->kex_inonce = msg->msg_nonce; msg->msg_nonce = NULL; /* Generate new responder's nonce */ if ((nonce = ibuf_random(IKED_NONCE_SIZE)) == NULL) goto fail; /* Update responder's nonce */ ibuf_free(kex->kex_rnonce); kex->kex_rnonce = nonce; if (ikev2_childsa_negotiate(env, sa, kex, &proposals, 0, pfs)) { log_debug("%s: failed to get CHILD SAs", __func__); goto fail; } if (rekeying && (sa->sa_stateflags & IKED_REQ_CHILDSA) && csa && (sa->sa_rekeyspi == csa->csa_peerspi)) { log_info("%s: simultaneous rekeying for CHILD SA %s/%s", SPI_SA(sa, __func__), print_spi(rekey->spi, rekey->spi_size), print_spi(sa->sa_rekeyspi, rekey->spi_size)); ibuf_free(sa->sa_simult); if (ikev2_nonce_cmp(kex->kex_inonce, nonce) < 0) sa->sa_simult = ibuf_dup(kex->kex_inonce); else sa->sa_simult = ibuf_dup(nonce); } } if ((e = ibuf_static()) == NULL) goto done; if (!nsa && sa->sa_ipcompr.ic_transform && (len = ikev2_add_ipcompnotify(env, e, &pld, 0, sa, 0)) == -1) goto done; if (!nsa && sa->sa_used_transport_mode && (len = ikev2_add_transport_mode(env, e, &pld, len, sa)) == -1) goto done; if (pld) { firstpayload = IKEV2_PAYLOAD_NOTIFY; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_SA) == -1) goto done; } else firstpayload = IKEV2_PAYLOAD_SA; /* SA payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((len = ikev2_add_proposals(env, nsa ? nsa : sa, e, nsa ? &nsa->sa_proposals : &proposals, protoid, 0, nsa ? 1 : 0, 0)) == -1) goto done; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONCE) == -1) goto done; /* NONCE payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if (ikev2_add_buf(e, nonce) == -1) goto done; len = ibuf_size(nonce); if (protoid == IKEV2_SAPROTO_IKE || pfs) { if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_KE) == -1) goto done; /* KE payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((ke = ibuf_reserve(e, sizeof(*ke))) == NULL) goto done; if (kex->kex_dhgroup == NULL) { log_debug("%s: invalid dh", __func__); goto done; } ke->kex_dhgroup = htobe16(kex->kex_dhgroup->id); if (ikev2_add_buf(e, kex->kex_dhrexchange) == -1) goto done; len = sizeof(*ke) + ibuf_size(kex->kex_dhrexchange); } if (protoid != IKEV2_SAPROTO_IKE) if ((len = ikev2_add_ts(e, &pld, len, sa, initiator)) == -1) goto done; if (ikev2_next_payload(pld, len, IKEV2_PAYLOAD_NONE) == -1) goto done; if ((ret = ikev2_msg_send_encrypt(env, sa, &e, IKEV2_EXCHANGE_CREATE_CHILD_SA, firstpayload, 1)) == -1) goto done; if (protoid == IKEV2_SAPROTO_IKE) { /* * If we also have initiated rekeying for this IKE SA, then * sa_nexti is already set. In this case don't enable the new SA * immediately, but record it in sa_nextr, until the exchange * for sa_nexti completes in ikev2_init_create_child_sa() and * the 'winner' can be selected by comparing nonces. */ if (sa->sa_nexti) { log_info("%s: simultaneous IKE SA rekeying", SPI_SA(sa, __func__)); sa->sa_nextr = nsa; nsa->sa_prevr = sa; /* backpointer */ ret = 0; } else ret = ikev2_ikesa_enable(env, sa, nsa); } else ret = ikev2_childsa_enable(env, sa); done: if (ret && protoid != IKEV2_SAPROTO_IKE) ikev2_childsa_delete(env, sa, 0, 0, NULL, 1); ibuf_free(e); config_free_proposals(&proposals, 0); config_free_kex(kextmp); return (ret); fail: config_free_proposals(&proposals, 0); config_free_kex(kextmp); return (-1); } void ikev2_ike_sa_setreason(struct iked_sa *sa, char *reason) { /* allow update only if reason is reset to NULL */ if (reason == NULL || sa->sa_reason == NULL) sa->sa_reason = reason; } void ikev2_ike_sa_timeout(struct iked *env, void *arg) { struct iked_sa *sa = arg; log_debug("%s: closing SA", __func__); ikev2_ike_sa_setreason(sa, "timeout"); sa_free(env, sa); } void ikev2_ike_sa_rekey_timeout(struct iked *env, void *arg) { struct iked_sa *sa = arg; log_debug("%s: closing SA", __func__); ikev2_ike_sa_setreason(sa, "rekey timeout"); sa_free(env, sa); } void ikev2_ike_sa_rekey_schedule(struct iked *env, struct iked_sa *sa) { timer_add(env, &sa->sa_rekey, (sa->sa_policy->pol_rekey * 850 + arc4random_uniform(100)) / 1000); } /* rekey delayed, so re-try after short delay (1% of configured) */ void ikev2_ike_sa_rekey_schedule_fast(struct iked *env, struct iked_sa *sa) { int timeout = sa->sa_policy->pol_rekey / 100; /* 1% */ if (timeout > 60) timeout = 60; /* max */ else if (timeout < 4) timeout = 4; /* min */ timer_add(env, &sa->sa_rekey, timeout); } void ikev2_ike_sa_alive(struct iked *env, void *arg) { struct iked_sa *sa = arg; struct iked_childsa *csa = NULL; uint64_t last_used, diff; int foundin = 0, foundout = 0; int ikeidle = 0; if (env->sc_alive_timeout == 0) return; /* check for incoming traffic on any child SA */ TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) { if (!csa->csa_loaded) continue; if (ipsec_sa_last_used(env, csa, &last_used) != 0) continue; diff = (uint32_t)(gettime() - last_used); log_debug("%s: %s CHILD SA spi %s last used %llu second(s) ago", __func__, csa->csa_dir == IPSP_DIRECTION_IN ? "incoming" : "outgoing", print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size), (long long unsigned)diff); if (diff < env->sc_alive_timeout) { if (csa->csa_dir == IPSP_DIRECTION_IN) { foundin = 1; break; } else { foundout = 1; } } } diff = (uint32_t)(gettime() - sa->sa_last_recvd); if (diff >= IKED_IKE_SA_LAST_RECVD_TIMEOUT) { ikeidle = 1; log_debug("%s: IKE SA %p ispi %s rspi %s last received %llu" " second(s) ago", __func__, sa, print_spi(sa->sa_hdr.sh_ispi, 8), print_spi(sa->sa_hdr.sh_rspi, 8), (long long unsigned)diff); } /* * send probe if any outgoing SA has been used, but no incoming * SA, or if we haven't received an IKE message. but only if we * are not already waiting for an answer. */ if (((!foundin && foundout) || ikeidle) && (sa->sa_stateflags & (IKED_REQ_CHILDSA|IKED_REQ_INF)) == 0) { log_debug("%s: sending alive check", __func__); ikev2_send_ike_e(env, sa, NULL, IKEV2_PAYLOAD_NONE, IKEV2_EXCHANGE_INFORMATIONAL, 0); sa->sa_stateflags |= IKED_REQ_INF; ikestat_inc(env, ikes_dpd_sent); } /* re-register */ timer_add(env, &sa->sa_timer, env->sc_alive_timeout); } void ikev2_ike_sa_keepalive(struct iked *env, void *arg) { struct iked_sa *sa = arg; uint8_t marker = 0xff; if (sendtofrom(sa->sa_fd, &marker, sizeof(marker), 0, (struct sockaddr *)&sa->sa_peer.addr, SS_LEN(sa->sa_peer.addr), (struct sockaddr *)&sa->sa_local.addr, SS_LEN(sa->sa_local.addr)) == -1) log_warn("%s: sendtofrom: peer %s local %s", __func__, print_addr(&sa->sa_peer.addr), print_addr(&sa->sa_local.addr)); else log_debug("%s: peer %s local %s", __func__, print_addr(&sa->sa_peer.addr), print_addr(&sa->sa_local.addr)); ikestat_inc(env, ikes_keepalive_sent); timer_add(env, &sa->sa_keepalive, IKED_IKE_SA_KEEPALIVE_TIMEOUT); } int ikev2_send_informational(struct iked *env, struct iked_message *msg) { struct iked_message resp; struct ike_header *hdr; struct ikev2_payload *pld; struct ikev2_notify *n; struct iked_sa *sa = msg->msg_sa, sah; struct ibuf *buf, *e = NULL; int ret = -1; if (msg->msg_error == 0) return (0); if ((buf = ikev2_msg_init(env, &resp, &msg->msg_peer, msg->msg_peerlen, &msg->msg_local, msg->msg_locallen, 0)) == NULL) goto done; /* New encrypted message buffer */ if ((e = ibuf_static()) == NULL) goto done; /* NOTIFY payload */ if ((pld = ikev2_add_payload(e)) == NULL) goto done; if ((n = ibuf_reserve(e, sizeof(*n))) == NULL) goto done; n->n_protoid = IKEV2_SAPROTO_IKE; /* XXX ESP etc. */ n->n_spisize = 0; n->n_type = htobe16(msg->msg_error); switch (msg->msg_error) { case IKEV2_N_INVALID_IKE_SPI: break; case IKEV2_N_NO_PROPOSAL_CHOSEN: ikev2_log_proposal(msg->msg_sa, &msg->msg_proposals); break; default: log_warnx("%s: unsupported notification %s", SPI_SA(sa, __func__), print_map(msg->msg_error, ikev2_n_map)); goto done; } log_info("%s: %s", SPI_SA(sa, __func__), print_map(msg->msg_error, ikev2_n_map)); if (ikev2_next_payload(pld, sizeof(*n), IKEV2_PAYLOAD_NONE) == -1) goto done; if (sa != NULL && msg->msg_e) { resp.msg_msgid = ikev2_msg_id(env, sa); /* IKE header */ if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid, IKEV2_PAYLOAD_SK, IKEV2_EXCHANGE_INFORMATIONAL, 0)) == NULL) goto done; if ((pld = ikev2_add_payload(buf)) == NULL) goto done; /* Encrypt message and add as an E payload */ if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) { log_debug("%s: encryption failed", __func__); goto done; } if (ibuf_add_ibuf(buf, e) != 0) goto done; if (ikev2_next_payload(pld, ibuf_size(e), IKEV2_PAYLOAD_NOTIFY) == -1) goto done; if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1) goto done; /* Add integrity checksum (HMAC) */ if (ikev2_msg_integr(env, sa, buf) != 0) { log_debug("%s: integrity checksum failed", __func__); goto done; } } else { if ((hdr = ibuf_seek(msg->msg_data, 0, sizeof(*hdr))) == NULL) goto done; bzero(&sah, sizeof(sah)); sah.sa_hdr.sh_rspi = betoh64(hdr->ike_rspi); sah.sa_hdr.sh_ispi = betoh64(hdr->ike_ispi); sah.sa_hdr.sh_initiator = hdr->ike_flags & IKEV2_FLAG_INITIATOR ? 0 : 1; resp.msg_msgid = ikev2_msg_id(env, &sah); /* IKE header */ if ((hdr = ikev2_add_header(buf, &sah, resp.msg_msgid, IKEV2_PAYLOAD_NOTIFY, IKEV2_EXCHANGE_INFORMATIONAL, 0)) == NULL) goto done; if (ibuf_add_ibuf(buf, e) != 0) goto done; if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1) goto done; } resp.msg_data = buf; resp.msg_fd = msg->msg_fd; TAILQ_INIT(&resp.msg_proposals); ret = ikev2_msg_send(env, &resp); done: ibuf_free(e); ikev2_msg_cleanup(env, &resp); return (ret); } ssize_t ikev2_psk(struct iked_sa *sa, uint8_t *data, size_t length, uint8_t **pskptr) { uint8_t *psk; size_t psklen = -1; if (hash_setkey(sa->sa_prf, data, length) == NULL) return (-1); if ((psk = calloc(1, hash_keylength(sa->sa_prf))) == NULL) return (-1); hash_init(sa->sa_prf); hash_update(sa->sa_prf, IKEV2_KEYPAD, strlen(IKEV2_KEYPAD)); hash_final(sa->sa_prf, psk, &psklen); *pskptr = psk; return (psklen); } int ikev2_sa_initiator_dh(struct iked_sa *sa, struct iked_message *msg, unsigned int proto, struct iked_sa *osa) { struct iked_policy *pol = sa->sa_policy; struct iked_transform *xform; struct iked_proposals *proposals; proposals = osa ? &osa->sa_proposals : &pol->pol_proposals; if (sa->sa_dhgroup == NULL) { if ((xform = config_findtransform(proposals, IKEV2_XFORMTYPE_DH, proto)) == NULL) { log_debug("%s: did not find dh transform", __func__); return (-1); } if ((sa->sa_dhgroup = group_get(xform->xform_id)) == NULL) { log_debug("%s: invalid dh %d", __func__, xform->xform_id); return (-1); } } if (!ibuf_length(sa->sa_dhiexchange)) { if (dh_create_exchange(sa->sa_dhgroup, &sa->sa_dhiexchange, NULL) == -1) { log_debug("%s: failed to get dh exchange", __func__); return (-1); } } /* Initial message */ if (msg == NULL) return (0); if (!ibuf_length(sa->sa_dhrexchange)) { if (!ibuf_length(msg->msg_ke)) { log_debug("%s: invalid peer dh exchange", __func__); return (-1); } sa->sa_dhrexchange = msg->msg_ke; msg->msg_ke = NULL; } /* Set a pointer to the peer exchange */ sa->sa_dhpeer = sa->sa_dhrexchange; return (0); } int ikev2_sa_negotiate_common(struct iked *env, struct iked_sa *sa, struct iked_message *msg, int groupid) { struct iked_transform *xform; /* XXX we need a better way to get this */ if (proposals_negotiate(&sa->sa_proposals, &msg->msg_policy->pol_proposals, &msg->msg_proposals, 0, groupid) != 0) { log_info("%s: proposals_negotiate", __func__); ikestat_inc(env, ikes_sa_proposals_negotiate_failures); return (-1); } if (sa_stateok(sa, IKEV2_STATE_SA_INIT)) sa_stateflags(sa, IKED_REQ_SA); if (sa->sa_encr == NULL) { if ((xform = config_findtransform(&sa->sa_proposals, IKEV2_XFORMTYPE_ENCR, 0)) == NULL) { log_info("%s: did not find encr transform", SPI_SA(sa, __func__)); return (-1); } if ((sa->sa_encr = cipher_new(xform->xform_type, xform->xform_id, xform->xform_length)) == NULL) { log_info("%s: failed to get encr", SPI_SA(sa, __func__)); return (-1); } } /* For AEAD ciphers integrity is implicit */ if (sa->sa_encr->encr_authid && sa->sa_integr == NULL) { if ((sa->sa_integr = hash_new(IKEV2_XFORMTYPE_INTEGR, sa->sa_encr->encr_authid)) == NULL) { log_info("%s: failed to get AEAD integr", SPI_SA(sa, __func__)); return (-1); } } if (sa->sa_prf == NULL) { if ((xform = config_findtransform(&sa->sa_proposals, IKEV2_XFORMTYPE_PRF, 0)) == NULL) { log_info("%s: did not find prf transform", SPI_SA(sa, __func__)); return (-1); } if ((sa->sa_prf = hash_new(xform->xform_type, xform->xform_id)) == NULL) { log_info("%s: failed to get prf", SPI_SA(sa, __func__)); return (-1); } } if (sa->sa_integr == NULL) { if ((xform = config_findtransform(&sa->sa_proposals, IKEV2_XFORMTYPE_INTEGR, 0)) == NULL) { log_info("%s: did not find integr transform", SPI_SA(sa, __func__)); return (-1); } if ((sa->sa_integr = hash_new(xform->xform_type, xform->xform_id)) == NULL) { log_info("%s: failed to get integr", SPI_SA(sa, __func__)); return (-1); } } return (0); } int ikev2_sa_initiator(struct iked *env, struct iked_sa *sa, struct iked_sa *osa, struct iked_message *msg) { if (ikev2_sa_initiator_dh(sa, msg, 0, osa) < 0) return (-1); if (!ibuf_length(sa->sa_inonce)) { if ((sa->sa_inonce = ibuf_random(IKED_NONCE_SIZE)) == NULL) { log_info("%s: failed to get local nonce", SPI_SA(sa, __func__)); return (-1); } } /* Initial message */ if (msg == NULL) return (0); if (!ibuf_length(sa->sa_rnonce)) { if (!ibuf_length(msg->msg_nonce)) { log_info("%s: invalid peer nonce", SPI_SA(sa, __func__)); return (-1); } sa->sa_rnonce = msg->msg_nonce; msg->msg_nonce = NULL; } if (ikev2_sa_negotiate_common(env, sa, msg, -1) != 0) return (-1); ibuf_free(sa->sa_2ndmsg); if ((sa->sa_2ndmsg = ibuf_dup(msg->msg_data)) == NULL) { log_info("%s: failed to copy 2nd message", SPI_SA(sa, __func__)); return (-1); } return (ikev2_sa_keys(env, sa, osa ? osa->sa_key_d : NULL)); } int ikev2_sa_responder_dh(struct iked_kex *kex, struct iked_proposals *proposals, struct iked_message *msg, unsigned int proto) { struct iked_transform *xform; if (kex->kex_dhgroup == NULL) { if ((xform = config_findtransform(proposals, IKEV2_XFORMTYPE_DH, proto)) == NULL) { log_info("%s: did not find dh transform", SPI_SA(msg->msg_sa, __func__)); return (-1); } if ((kex->kex_dhgroup = group_get(xform->xform_id)) == NULL) { log_debug("%s: invalid dh %d", SPI_SA(msg->msg_sa, __func__), xform->xform_id); return (-1); } } /* Look for dhgroup mismatch during an IKE SA negotiation */ if (msg->msg_dhgroup != kex->kex_dhgroup->id) { log_info("%s: want dh %s, KE has %s", SPI_SA(msg->msg_sa, __func__), print_map(kex->kex_dhgroup->id, ikev2_xformdh_map), print_map(msg->msg_dhgroup, ikev2_xformdh_map)); msg->msg_error = IKEV2_N_INVALID_KE_PAYLOAD; msg->msg_dhgroup = kex->kex_dhgroup->id; return (-1); } if (!ibuf_length(kex->kex_dhiexchange)) { kex->kex_dhiexchange = msg->msg_ke; msg->msg_ke = NULL; } if (!ibuf_length(kex->kex_dhrexchange)) { if (dh_create_exchange(kex->kex_dhgroup, &kex->kex_dhrexchange, kex->kex_dhiexchange) == -1) { log_info("%s: failed to get dh exchange", SPI_SA(msg->msg_sa, __func__)); return (-1); } } /* Set a pointer to the peer exchange */ kex->kex_dhpeer = kex->kex_dhiexchange; return (0); } int ikev2_sa_responder(struct iked *env, struct iked_sa *sa, struct iked_sa *osa, struct iked_message *msg) { struct iked_policy *old; /* re-lookup policy based on 'msg' (unless IKESA is rekeyed) */ if (osa == NULL) { old = sa->sa_policy; sa->sa_policy = NULL; if (policy_lookup(env, msg, &msg->msg_proposals, NULL, 0) != 0 || msg->msg_policy == NULL) { sa->sa_policy = old; log_info("%s: no proposal chosen", __func__); msg->msg_error = IKEV2_N_NO_PROPOSAL_CHOSEN; return (-1); } /* move sa to new policy */ sa->sa_policy = msg->msg_policy; TAILQ_REMOVE(&old->pol_sapeers, sa, sa_peer_entry); TAILQ_INSERT_TAIL(&sa->sa_policy->pol_sapeers, sa, sa_peer_entry); policy_unref(env, old); policy_ref(env, sa->sa_policy); } sa_state(env, sa, IKEV2_STATE_SA_INIT); ibuf_free(sa->sa_1stmsg); if ((sa->sa_1stmsg = ibuf_dup(msg->msg_data)) == NULL) { log_debug("%s: failed to copy 1st message", __func__); return (-1); } if (sa->sa_rnonce == NULL && (sa->sa_rnonce = ibuf_random(IKED_NONCE_SIZE)) == NULL) { log_debug("%s: failed to get local nonce", __func__); return (-1); } if (!ibuf_length(sa->sa_inonce) && (ibuf_length(msg->msg_nonce) < IKED_NONCE_MIN)) { log_debug("%s: failed to get peer nonce", __func__); return (-1); } sa->sa_inonce = msg->msg_nonce; msg->msg_nonce = NULL; if (ikev2_sa_negotiate_common(env, sa, msg, msg->msg_dhgroup) != 0) return (-1); if (ikev2_sa_responder_dh(&sa->sa_kex, &sa->sa_proposals, msg, 0) < 0) return (-1); return (ikev2_sa_keys(env, sa, osa ? osa->sa_key_d : NULL)); } int ikev2_sa_keys(struct iked *env, struct iked_sa *sa, struct ibuf *key) { struct iked_hash *prf, *integr; struct iked_cipher *encr; struct dh_group *group; struct ibuf *ninr, *dhsecret, *skeyseed, *s, *t; size_t nonceminlen, ilen, rlen, tmplen; uint64_t ispi, rspi; int ret = -1; int isaead = 0; ninr = dhsecret = skeyseed = s = t = NULL; if ((encr = sa->sa_encr) == NULL || (prf = sa->sa_prf) == NULL || (integr = sa->sa_integr) == NULL || (group = sa->sa_dhgroup) == NULL) { log_info("%s: failed to get key input data", SPI_SA(sa, __func__)); return (-1); } /* For AEADs no auth keys are required (see RFC 5282) */ isaead = !!integr->hash_isaead; if (prf->hash_fixedkey) nonceminlen = prf->hash_fixedkey; else nonceminlen = IKED_NONCE_MIN; /* Nonces need a minimal size and should have an even length */ if (ibuf_length(sa->sa_inonce) < nonceminlen || (ibuf_length(sa->sa_inonce) % 2) != 0 || ibuf_length(sa->sa_rnonce) < nonceminlen || (ibuf_length(sa->sa_rnonce) % 2) != 0) { log_info("%s: invalid nonces", SPI_SA(sa, __func__)); return (-1); } if (prf->hash_fixedkey) { /* Half of the key bits must come from Ni, and half from Nr */ ilen = prf->hash_fixedkey / 2; rlen = prf->hash_fixedkey / 2; } else { /* Most PRF functions accept a variable-length key */ ilen = ibuf_length(sa->sa_inonce); rlen = ibuf_length(sa->sa_rnonce); } /* * Depending on whether we're generating new keying material * or rekeying existing SA the algorithm is different. If the * "key" argument is not specified a concatenation of nonces * (Ni | Nr) is used as a PRF key, otherwise a "key" buffer * is used and PRF is performed on the concatenation of DH * exchange result and nonces (g^ir | Ni | Nr). See sections * 2.14 and 2.18 of RFC7296 for more information. */ /* * Generate g^ir */ if (dh_create_shared(group, &dhsecret, sa->sa_dhpeer) == -1) { log_info("%s: failed to get dh secret" " group %d secret %zu exchange %zu", SPI_SA(sa, __func__), group->id, ibuf_length(dhsecret), ibuf_length(sa->sa_dhpeer)); goto done; } log_debug("%s: DHSECRET with %zu bytes", SPI_SA(sa, __func__), ibuf_size(dhsecret)); print_hexbuf(dhsecret); if (!key) { /* * Set PRF key to generate SKEYSEED = prf(Ni | Nr, g^ir) */ if ((ninr = ibuf_new(ibuf_data(sa->sa_inonce), ilen)) == NULL || ibuf_add(ninr, ibuf_data(sa->sa_rnonce), rlen) != 0) { log_info("%s: failed to get nonce key buffer", SPI_SA(sa, __func__)); goto done; } key = ninr; } else { /* * Set PRF key to generate SKEYSEED = prf(key, g^ir | Ni | Nr) */ if (ibuf_add(dhsecret, ibuf_data(sa->sa_inonce), ilen) != 0 || ibuf_add(dhsecret, ibuf_data(sa->sa_rnonce), rlen) != 0) { log_info("%s: failed to get nonce key buffer", SPI_SA(sa, __func__)); goto done; } } if ((hash_setkey(prf, ibuf_data(key), ibuf_size(key))) == NULL) { log_info("%s: failed to set prf key", SPI_SA(sa, __func__)); goto done; } if ((skeyseed = ibuf_new(NULL, hash_keylength(prf))) == NULL) { log_info("%s: failed to get SKEYSEED buffer", SPI_SA(sa, __func__)); goto done; } tmplen = 0; hash_init(prf); hash_update(prf, ibuf_data(dhsecret), ibuf_size(dhsecret)); hash_final(prf, ibuf_data(skeyseed), &tmplen); log_debug("%s: SKEYSEED with %zu bytes", __func__, tmplen); print_hex(ibuf_data(skeyseed), 0, tmplen); if (ibuf_setsize(skeyseed, tmplen) == -1) { log_info("%s: failed to set keymaterial length", SPI_SA(sa, __func__)); goto done; } /* * Now generate the key material * * S = Ni | Nr | SPIi | SPIr */ /* S = Ni | Nr | SPIi | SPIr */ ilen = ibuf_length(sa->sa_inonce); rlen = ibuf_length(sa->sa_rnonce); ispi = htobe64(sa->sa_hdr.sh_ispi); rspi = htobe64(sa->sa_hdr.sh_rspi); if ((s = ibuf_new(ibuf_data(sa->sa_inonce), ilen)) == NULL || ibuf_add(s, ibuf_data(sa->sa_rnonce), rlen) != 0 || ibuf_add(s, &ispi, sizeof(ispi)) != 0 || ibuf_add(s, &rspi, sizeof(rspi)) != 0) { log_info("%s: failed to set S buffer", SPI_SA(sa, __func__)); goto done; } log_debug("%s: S with %zu bytes", SPI_SA(sa, __func__), ibuf_size(s)); print_hexbuf(s); /* * Get the size of the key material we need and the number * of rounds we need to run the prf+ function. */ ilen = hash_length(prf) + /* SK_d */ (isaead ? 0 : hash_keylength(integr)) + /* SK_ai */ (isaead ? 0 : hash_keylength(integr)) + /* SK_ar */ cipher_keylength(encr) + /* SK_ei */ cipher_keylength(encr) + /* SK_er */ hash_keylength(prf) + /* SK_pi */ hash_keylength(prf); /* SK_pr */ if ((t = ikev2_prfplus(prf, skeyseed, s, ilen)) == NULL) { log_info("%s: failed to get IKE SA key material", SPI_SA(sa, __func__)); goto done; } /* ibuf_getdata() returns a new buffer from the next read offset */ if ((sa->sa_key_d = ibuf_getdata(t, hash_length(prf))) == NULL || (!isaead && (sa->sa_key_iauth = ibuf_getdata(t, hash_keylength(integr))) == NULL) || (!isaead && (sa->sa_key_rauth = ibuf_getdata(t, hash_keylength(integr))) == NULL) || (sa->sa_key_iencr = ibuf_getdata(t, cipher_keylength(encr))) == NULL || (sa->sa_key_rencr = ibuf_getdata(t, cipher_keylength(encr))) == NULL || (sa->sa_key_iprf = ibuf_getdata(t, hash_length(prf))) == NULL || (sa->sa_key_rprf = ibuf_getdata(t, hash_length(prf))) == NULL) { log_debug("%s: failed to get SA keys", SPI_SA(sa, __func__)); goto done; } log_debug("%s: SK_d with %zu bytes", __func__, ibuf_size(sa->sa_key_d)); print_hexbuf(sa->sa_key_d); if (!isaead) { log_debug("%s: SK_ai with %zu bytes", __func__, ibuf_size(sa->sa_key_iauth)); print_hexbuf(sa->sa_key_iauth); log_debug("%s: SK_ar with %zu bytes", __func__, ibuf_size(sa->sa_key_rauth)); print_hexbuf(sa->sa_key_rauth); } log_debug("%s: SK_ei with %zu bytes", __func__, ibuf_size(sa->sa_key_iencr)); print_hexbuf(sa->sa_key_iencr); log_debug("%s: SK_er with %zu bytes", __func__, ibuf_size(sa->sa_key_rencr)); print_hexbuf(sa->sa_key_rencr); log_debug("%s: SK_pi with %zu bytes", __func__, ibuf_size(sa->sa_key_iprf)); print_hexbuf(sa->sa_key_iprf); log_debug("%s: SK_pr with %zu bytes", __func__, ibuf_size(sa->sa_key_rprf)); print_hexbuf(sa->sa_key_rprf); ret = 0; done: ibuf_free(ninr); ibuf_free(dhsecret); ibuf_free(skeyseed); ibuf_free(s); ibuf_free(t); return (ret); } void ikev2_sa_cleanup_dh(struct iked_sa *sa) { ibuf_free(sa->sa_dhiexchange); ibuf_free(sa->sa_dhrexchange); group_free(sa->sa_dhgroup); sa->sa_dhiexchange = NULL; sa->sa_dhrexchange = NULL; sa->sa_dhgroup = NULL; } struct ibuf * ikev2_prfplus(struct iked_hash *prf, struct ibuf *key, struct ibuf *seed, size_t keymatlen) { struct ibuf *t = NULL, *t1 = NULL, *t2 = NULL; size_t rlen, i, hashlen = 0; uint8_t pad = 0; /* * prf+ (K, S) = T1 | T2 | T3 | T4 | ... * * T1 = prf (K, S | 0x01) * T2 = prf (K, T1 | S | 0x02) * T3 = prf (K, T2 | S | 0x03) * T4 = prf (K, T3 | S | 0x04) */ if ((hash_setkey(prf, ibuf_data(key), ibuf_size(key))) == NULL) { log_debug("%s: failed to set prf+ key", __func__); goto fail; } if ((t = ibuf_new(NULL, 0)) == NULL) { log_debug("%s: failed to get T buffer", __func__); goto fail; } rlen = roundup(keymatlen, hash_length(prf)) / hash_length(prf); if (rlen > 255) fatalx("ikev2_prfplus: key material too large"); for (i = 0; i < rlen; i++) { if (t1 != NULL) { t2 = t1; } else t2 = ibuf_new(NULL, 0); t1 = ibuf_new(NULL, hash_keylength(prf)); ibuf_add_ibuf(t2, seed); pad = i + 1; ibuf_add(t2, &pad, 1); hash_init(prf); hash_update(prf, ibuf_data(t2), ibuf_size(t2)); hash_final(prf, ibuf_data(t1), &hashlen); if (hashlen != hash_length(prf)) fatalx("ikev2_prfplus: hash length mismatch"); ibuf_free(t2); ibuf_add_ibuf(t, t1); log_debug("%s: T%d with %zu bytes", __func__, pad, ibuf_size(t1)); print_hexbuf(t1); } log_debug("%s: Tn with %zu bytes", __func__, ibuf_size(t)); print_hexbuf(t); ibuf_free(t1); return (t); fail: ibuf_free(t1); ibuf_free(t); return (NULL); } int ikev2_sa_tag(struct iked_sa *sa, struct iked_id *id) { char *format, *domain = NULL, *idrepl = NULL; char idstr[IKED_ID_SIZE]; int ret = -1; size_t len; free(sa->sa_tag); sa->sa_tag = NULL; format = sa->sa_policy->pol_tag; len = IKED_TAG_SIZE; if ((sa->sa_tag = calloc(1, len)) == NULL) { log_debug("%s: calloc", __func__); goto fail; } if (strlcpy(sa->sa_tag, format, len) >= len) { log_debug("%s: tag too long", __func__); goto fail; } if (ikev2_print_id(id, idstr, sizeof(idstr)) == -1) { log_debug("%s: invalid id", __func__); goto fail; } /* ASN.1 DER IDs are too long, use the CN part instead */ if ((id->id_type == IKEV2_ID_ASN1_DN) && (idrepl = strstr(idstr, "CN=")) != NULL) { domain = strstr(idrepl, "emailAddress="); idrepl[strcspn(idrepl, "/")] = '\0'; } else idrepl = idstr; if (strstr(format, "$id") != NULL) { if (expand_string(sa->sa_tag, len, "$id", idrepl) != 0) { log_debug("%s: failed to expand tag", __func__); goto fail; } } if (strstr(format, "$eapid") != NULL && sa->sa_eapid != NULL) { if (expand_string(sa->sa_tag, len, "$eapid", sa->sa_eapid) != 0) { log_debug("%s: failed to expand tag", __func__); goto fail; } } if (strstr(format, "$name") != NULL) { if (expand_string(sa->sa_tag, len, "$name", sa->sa_policy->pol_name) != 0) { log_debug("%s: failed to expand tag", __func__); goto fail; } } if (strstr(format, "$domain") != NULL) { if (id->id_type == IKEV2_ID_FQDN) domain = strchr(idrepl, '.'); else if (id->id_type == IKEV2_ID_UFQDN) domain = strchr(idrepl, '@'); else if (*idstr == '/' && domain != NULL) domain = strchr(domain, '@'); else domain = NULL; if (domain == NULL || strlen(domain) < 2) { log_debug("%s: no valid domain in ID %s", __func__, idstr); goto fail; } domain++; if (expand_string(sa->sa_tag, len, "$domain", domain) != 0) { log_debug("%s: failed to expand tag", __func__); goto fail; } } log_debug("%s: %s (%zu)", __func__, sa->sa_tag, strlen(sa->sa_tag)); ret = 0; fail: if (ret != 0) { free(sa->sa_tag); sa->sa_tag = NULL; } return (ret); } int ikev2_childsa_delete_proposed(struct iked *env, struct iked_sa *sa, struct iked_proposals *proposals) { struct ibuf *buf = NULL; struct iked_proposal *prop; struct ikev2_delete *del; uint32_t spi32; uint8_t protoid = 0; int ret = -1, count; if (!sa_stateok(sa, IKEV2_STATE_VALID)) return (-1); count = 0; TAILQ_FOREACH(prop, proposals, prop_entry) { if (ikev2_valid_proposal(prop, NULL, NULL, NULL) != 0) continue; protoid = prop->prop_protoid; count++; } if (count == 0) return (0); if ((buf = ibuf_static()) == NULL) return (-1); if ((del = ibuf_reserve(buf, sizeof(*del))) == NULL) goto done; /* XXX we assume all have the same protoid */ del->del_protoid = protoid; del->del_spisize = 4; del->del_nspi = htobe16(count); TAILQ_FOREACH(prop, proposals, prop_entry) { if (ikev2_valid_proposal(prop, NULL, NULL, NULL) != 0) continue; spi32 = htobe32(prop->prop_localspi.spi); if (ibuf_add(buf, &spi32, sizeof(spi32))) goto done; } if (ikev2_send_ike_e(env, sa, buf, IKEV2_PAYLOAD_DELETE, IKEV2_EXCHANGE_INFORMATIONAL, 0) == -1) goto done; sa->sa_stateflags |= IKED_REQ_INF; ret = 0; done: ibuf_free(buf); return (ret); } int ikev2_childsa_negotiate(struct iked *env, struct iked_sa *sa, struct iked_kex *kex, struct iked_proposals *proposals, int initiator, int pfs) { struct iked_proposal *prop; struct iked_transform *xform, *encrxf = NULL, *integrxf = NULL; struct iked_childsa *csa = NULL, *csb = NULL; struct iked_childsa *csa2 = NULL, *csb2 = NULL; struct iked_flow *flow, *saflow, *flowa, *flowb; struct iked_ipcomp *ic; struct ibuf *keymat = NULL, *seed = NULL, *dhsecret = NULL; struct dh_group *group = NULL; uint32_t spi = 0; unsigned int i; size_t ilen = 0; int esn, skip, ret = -1; if (!sa_stateok(sa, IKEV2_STATE_VALID)) return (-1); if (ikev2_sa_tag(sa, IKESA_DSTID(sa)) == -1) return (-1); ic = initiator ? &sa->sa_ipcompi : &sa->sa_ipcompr; if (ic->ic_transform == 0 || ic->ic_cpi_out == 0 || (initiator && ic->ic_cpi_in == 0)) ic = NULL; /* reset state */ sa->sa_used_transport_mode = 0; /* We need to determine the key material length first */ TAILQ_FOREACH(prop, proposals, prop_entry) { if (prop->prop_protoid == IKEV2_SAPROTO_IKE) continue; log_debug("%s: proposal %d", __func__, prop->prop_id); for (i = 0; i < prop->prop_nxforms; i++) { xform = prop->prop_xforms + i; xform->xform_keylength = keylength_xf(prop->prop_protoid, xform->xform_type, xform->xform_id); switch (xform->xform_type) { case IKEV2_XFORMTYPE_ENCR: case IKEV2_XFORMTYPE_INTEGR: if (xform->xform_length) xform->xform_keylength = xform->xform_length; xform->xform_keylength += noncelength_xf(xform->xform_type, xform->xform_id); ilen += xform->xform_keylength / 8; break; } } } /* double key material length for inbound/outbound */ ilen *= 2; log_debug("%s: key material length %zu", __func__, ilen); if ((seed = ibuf_new(NULL, 0)) == NULL) { log_debug("%s: failed to setup IKE SA key material", __func__); goto done; } if (pfs) { log_debug("%s: using PFS", __func__); if (kex->kex_dhpeer == NULL || ibuf_size(kex->kex_dhpeer) == 0 || (group = kex->kex_dhgroup) == NULL) { log_debug("%s: no dh group for pfs", __func__); goto done; } if (dh_create_shared(group, &dhsecret, kex->kex_dhpeer) == -1) { log_debug("%s: failed to get dh secret" " group %d secret %zu exchange %zu", __func__, group->id, ibuf_length(dhsecret), ibuf_length(kex->kex_dhpeer)); goto done; } if (ibuf_add_ibuf(seed, dhsecret) != 0) { log_debug("%s: failed to set dh secret", __func__); goto done; } } if (ibuf_add_ibuf(seed, kex->kex_inonce) != 0 || ibuf_add_ibuf(seed, kex->kex_rnonce) != 0 || (keymat = ikev2_prfplus(sa->sa_prf, sa->sa_key_d, seed, ilen)) == NULL) { log_debug("%s: failed to get IKE SA key material", __func__); goto done; } /* Create the new flows */ TAILQ_FOREACH(prop, proposals, prop_entry) { if (ikev2_valid_proposal(prop, NULL, NULL, NULL) != 0) continue; RB_FOREACH(flow, iked_flows, &sa->sa_policy->pol_flows) { if ((flowa = calloc(1, sizeof(*flowa))) == NULL) { log_debug("%s: failed to get flow", __func__); goto done; } memcpy(flowa, flow, sizeof(*flow)); flowa->flow_dir = IPSP_DIRECTION_OUT; flowa->flow_saproto = ic ? IKEV2_SAPROTO_IPCOMP : prop->prop_protoid; flowa->flow_rdomain = sa->sa_policy->pol_rdomain; flowa->flow_local = &sa->sa_local; flowa->flow_peer = &sa->sa_peer; flowa->flow_ikesa = sa; flowa->flow_transport = sa->sa_policy->pol_flags & IKED_POLICY_TRANSPORT; if (ikev2_cp_fixflow(sa, flow, flowa) == -1) { flow_free(flowa); continue; } skip = 0; TAILQ_FOREACH(saflow, &sa->sa_flows, flow_entry) { if (flow_equal(saflow, flowa)) { skip = 1; break; } } if (skip) { flow_free(flowa); continue; } if ((flowb = calloc(1, sizeof(*flowb))) == NULL) { log_debug("%s: failed to get flow", __func__); flow_free(flowa); goto done; } memcpy(flowb, flowa, sizeof(*flow)); flowb->flow_dir = IPSP_DIRECTION_IN; memcpy(&flowb->flow_src, &flow->flow_dst, sizeof(flow->flow_dst)); memcpy(&flowb->flow_dst, &flow->flow_src, sizeof(flow->flow_src)); if (ikev2_cp_fixflow(sa, flow, flowb) == -1) { flow_free(flowa); flow_free(flowb); continue; } #if defined(HAVE_LINUX_IPSEC_H) struct iked_flow *flowc; if ((flowc = calloc(1, sizeof(*flowc))) == NULL) { log_debug("%s: failed to get flow", __func__); flow_free(flowa); flow_free(flowb); goto done; } /* Linux is special and requires a FWD flow */ memcpy(flowc, flowb, sizeof(*flow)); flowc->flow_dir = IPSEC_DIR_FWD; TAILQ_INSERT_TAIL(&sa->sa_flows, flowc, flow_entry); #endif TAILQ_INSERT_TAIL(&sa->sa_flows, flowa, flow_entry); TAILQ_INSERT_TAIL(&sa->sa_flows, flowb, flow_entry); } } /* create the CHILD SAs using the key material */ TAILQ_FOREACH(prop, proposals, prop_entry) { if (ikev2_valid_proposal(prop, &encrxf, &integrxf, &esn) != 0) continue; spi = 0; if ((csa = calloc(1, sizeof(*csa))) == NULL) { log_debug("%s: failed to get CHILD SA", __func__); goto done; } csa->csa_saproto = prop->prop_protoid; csa->csa_ikesa = sa; csa->csa_spi.spi_protoid = prop->prop_protoid; csa->csa_esn = esn; csa->csa_transport = sa->sa_use_transport_mode; sa->sa_used_transport_mode = sa->sa_use_transport_mode; if (pfs && group) csa->csa_pfsgrpid = group->id; /* Set up responder's SPIs */ if (initiator) { csa->csa_dir = IPSP_DIRECTION_OUT; csa->csa_local = &sa->sa_local; csa->csa_peer = &sa->sa_peer; csa->csa_peerspi = prop->prop_localspi.spi; csa->csa_spi.spi = prop->prop_peerspi.spi; csa->csa_spi.spi_size = prop->prop_peerspi.spi_size; } else { csa->csa_dir = IPSP_DIRECTION_IN; csa->csa_local = &sa->sa_peer; csa->csa_peer = &sa->sa_local; if ((ret = ipsec_sa_init(env, csa, &spi)) != 0) goto done; csa->csa_allocated = 1; csa->csa_peerspi = prop->prop_peerspi.spi; csa->csa_spi.spi = prop->prop_localspi.spi = spi; csa->csa_spi.spi_size = 4; } if (encrxf && (csa->csa_encrkey = ibuf_getdata(keymat, encrxf->xform_keylength / 8)) == NULL) { log_debug("%s: failed to get CHILD SA encryption key", __func__); goto done; } if (integrxf && (csa->csa_integrkey = ibuf_getdata(keymat, integrxf->xform_keylength / 8)) == NULL) { log_debug("%s: failed to get CHILD SA integrity key", __func__); goto done; } if (encrxf) csa->csa_encrid = encrxf->xform_id; if (integrxf) csa->csa_integrid = integrxf->xform_id; if ((csb = calloc(1, sizeof(*csb))) == NULL) { log_debug("%s: failed to get CHILD SA", __func__); goto done; } memcpy(csb, csa, sizeof(*csb)); /* Set up initiator's SPIs */ csb->csa_spi.spi = csa->csa_peerspi; csb->csa_peerspi = csa->csa_spi.spi; csb->csa_allocated = csa->csa_allocated ? 0 : 1; csb->csa_dir = csa->csa_dir == IPSP_DIRECTION_IN ? IPSP_DIRECTION_OUT : IPSP_DIRECTION_IN; csb->csa_local = csa->csa_peer; csb->csa_peer = csa->csa_local; if (encrxf && (csb->csa_encrkey = ibuf_getdata(keymat, encrxf->xform_keylength / 8)) == NULL) { log_debug("%s: failed to get CHILD SA encryption key", __func__); goto done; } if (integrxf && (csb->csa_integrkey = ibuf_getdata(keymat, integrxf->xform_keylength / 8)) == NULL) { log_debug("%s: failed to get CHILD SA integrity key", __func__); goto done; } if (ic && prop->prop_protoid == IKEV2_SAPROTO_ESP) { /* add IPCOMP SAs */ if ((csa2 = calloc(1, sizeof(*csa2))) == NULL) { log_debug("%s: failed to get CHILD SA", __func__); goto done; } if ((csb2 = calloc(1, sizeof(*csb2))) == NULL) { log_debug("%s: failed to get CHILD SA", __func__); goto done; } csa2->csa_saproto = IKEV2_SAPROTO_IPCOMP; csa2->csa_ikesa = csa->csa_ikesa; csa2->csa_dir = csa->csa_dir; csa2->csa_local = csa->csa_local; csa2->csa_peer = csa->csa_peer; if (initiator) { csa2->csa_spi.spi = ic->ic_cpi_out; csa2->csa_peerspi = ic->ic_cpi_in; csa2->csa_allocated = 0; /* make sure IPCOMP CPIs are not reused */ ic->ic_transform = 0; ic->ic_cpi_in = ic->ic_cpi_out = 0; } else { if ((ret = ipsec_sa_init(env, csa2, &spi)) != 0) goto done; ic->ic_cpi_in = spi; csa2->csa_spi.spi = ic->ic_cpi_in; csa2->csa_peerspi = ic->ic_cpi_out; csa2->csa_allocated = 1; } csa2->csa_spi.spi_size = 2; memcpy(csb2, csa2, sizeof(*csb2)); csb2->csa_spi.spi = csa2->csa_peerspi; csb2->csa_peerspi = csa2->csa_spi.spi; csb2->csa_allocated = csa2->csa_allocated ? 0 : 1; csb2->csa_dir = csa2->csa_dir == IPSP_DIRECTION_IN ? IPSP_DIRECTION_OUT : IPSP_DIRECTION_IN; csb2->csa_local = csa2->csa_peer; csb2->csa_peer = csa2->csa_local; /* link IPComp and ESP SAs, switch ESP to transport */ csa->csa_transport = 1; csa->csa_bundled = csa2; csa2->csa_bundled = csa; csb->csa_transport = 1; csb->csa_bundled = csb2; csb2->csa_bundled = csb; csa2 = NULL; csb2 = NULL; ic = NULL; } TAILQ_INSERT_TAIL(&sa->sa_childsas, csa, csa_entry); TAILQ_INSERT_TAIL(&sa->sa_childsas, csb, csa_entry); ikestat_add(env, ikes_csa_created, 2); csa->csa_peersa = csb; csb->csa_peersa = csa; csa = NULL; csb = NULL; } ret = 0; done: sa->sa_use_transport_mode = 0; /* reset state after use */ ibuf_free(dhsecret); ibuf_free(keymat); ibuf_free(seed); childsa_free(csa); childsa_free(csb); childsa_free(csa2); childsa_free(csb2); return (ret); } int ikev2_childsa_enable(struct iked *env, struct iked_sa *sa) { struct iked_childsa *csa, *ocsa, *ipcomp; struct iked_flow *flow, *oflow; int peer_changed, reload; FILE *spif, *flowf; char *spibuf = NULL, *flowbuf = NULL; char prenat_mask[10]; uint16_t encrid = 0, integrid = 0, groupid = 0; size_t encrlen = 0, integrlen = 0, spisz, flowsz; int esn = 0; int ret = -1; spif = open_memstream(&spibuf, &spisz); if (spif == NULL) { log_warn("%s", __func__); return (ret); } flowf = open_memstream(&flowbuf, &flowsz); if (flowf == NULL) { log_warn("%s", __func__); fclose(spif); return (ret); } TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) { if (csa->csa_rekey || csa->csa_loaded) continue; ipcomp = csa->csa_bundled; if (ipcomp && ipcomp->csa_loaded) { log_info("%s: IPCOMP SA for CHILD SA spi %s" " already loaded", __func__, print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size)); continue; } if (ipsec_sa_add(env, csa, NULL) != 0) { log_debug("%s: failed to load CHILD SA spi %s", __func__, print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size)); goto done; } if (ipcomp) { if (ipsec_sa_add(env, ipcomp, csa) != 0) { log_debug("%s: failed to load IPCOMP spi %s", __func__, print_spi(ipcomp->csa_spi.spi, ipcomp->csa_spi.spi_size)); ipcomp = NULL; } } if ((ocsa = RB_FIND(iked_activesas, &env->sc_activesas, csa)) != NULL) { log_debug("%s: replaced CHILD SA %p with %p spi %s", __func__, ocsa, csa, print_spi(ocsa->csa_spi.spi, ocsa->csa_spi.spi_size)); ocsa->csa_loaded = 0; ocsa->csa_rekey = 1; /* prevent re-loading */ RB_REMOVE(iked_activesas, &env->sc_activesas, ocsa); } RB_INSERT(iked_activesas, &env->sc_activesas, csa); log_debug("%s: loaded CHILD SA spi %s", __func__, print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size)); /* append SPI to log buffer */ if (ftello(spif) > 0) fputs(", ", spif); fputs(print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size), spif); if (ipcomp) fprintf(spif, "(%s)", print_spi(ipcomp->csa_spi.spi, ipcomp->csa_spi.spi_size)); if (!encrid) { encrid = csa->csa_encrid; encrlen = ibuf_length(csa->csa_encrkey); switch (encrid) { case IKEV2_XFORMENCR_AES_GCM_16: case IKEV2_XFORMENCR_AES_GCM_12: encrlen -= 4; break; default: if (!csa->csa_integrid) break; integrid = csa->csa_integrid; integrlen = ibuf_length(csa->csa_integrkey); } groupid = csa->csa_pfsgrpid; esn = csa->csa_esn; } } peer_changed = (memcmp(&sa->sa_peer_loaded, &sa->sa_peer, sizeof(sa->sa_peer_loaded)) != 0); if (!(sa->sa_policy->pol_flags & IKED_POLICY_ROUTING)) { TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) { /* re-load the flow if the peer for the flow has changed */ reload = 0; if (flow->flow_loaded) { if (!peer_changed) { log_debug("%s: flow already loaded %p", __func__, flow); continue; } RB_REMOVE(iked_flows, &env->sc_activeflows, flow); (void)ipsec_flow_delete(env, flow); flow->flow_loaded = 0; /* we did RB_REMOVE */ reload = 1; } if (ipsec_flow_add(env, flow) != 0) { log_debug("%s: failed to load flow", __func__); goto done; } if ((oflow = RB_FIND(iked_flows, &env->sc_activeflows, flow)) != NULL) { log_debug("%s: replaced old flow %p with %p", __func__, oflow, flow); oflow->flow_loaded = 0; RB_REMOVE(iked_flows, &env->sc_activeflows, oflow); } RB_INSERT(iked_flows, &env->sc_activeflows, flow); log_debug("%s: %sloaded flow %p", __func__, reload ? "re" : "", flow); /* append flow to log buffer */ if (flow->flow_dir == IPSP_DIRECTION_OUT && flow->flow_prenat.addr_af != 0) snprintf(prenat_mask, sizeof(prenat_mask), "%d", flow->flow_prenat.addr_mask); else prenat_mask[0] = '\0'; if (flow->flow_dir == IPSP_DIRECTION_OUT) { if (ftello(flowf) > 0) fputs(", ", flowf); fprintf(flowf, "%s-%s/%d%s%s%s%s%s=%s/%d(%u)%s", print_map(flow->flow_saproto, ikev2_saproto_map), print_addr(&flow->flow_src.addr), flow->flow_src.addr_mask, flow->flow_prenat.addr_af != 0 ? "[": "", flow->flow_prenat.addr_af != 0 ? print_addr(&flow->flow_prenat.addr) : "", flow->flow_prenat.addr_af != 0 ? "/" : "", flow->flow_prenat.addr_af != 0 ? prenat_mask : "", flow->flow_prenat.addr_af != 0 ? "]": "", print_addr(&flow->flow_dst.addr), flow->flow_dst.addr_mask, flow->flow_ipproto, reload ? "-R" : ""); } } } /* remember the current address for ikev2_update_sa_addresses() */ if (peer_changed) { memcpy(&sa->sa_peer_loaded, &sa->sa_peer, sizeof(sa->sa_peer_loaded)); log_debug("%s: remember SA peer %s", __func__, print_addr(&sa->sa_peer_loaded.addr)); } fflush(spif); if (ftello(spif) > 0 && !ferror(spif)) { log_info("%s: loaded SPIs: %s (enc %s%s%s%s%s%s)", SPI_SA(sa, __func__), spibuf, print_xf(encrid, encrlen, ipsecencxfs), integrid ? " auth " : "", integrid ? print_xf(integrid, integrlen, authxfs) : "", groupid ? " group " : "", groupid ? print_xf(groupid, 0, groupxfs) : "", esn ? " esn" : ""); } fflush(flowf); if (ftello(flowf) > 0 && !ferror(flowf)) { log_info("%s: loaded flows: %s", SPI_SA(sa, __func__), flowbuf); } ret = 0; done: fclose(spif); fclose(flowf); free(spibuf); free(flowbuf); return (ret); } int ikev2_childsa_delete(struct iked *env, struct iked_sa *sa, uint8_t saproto, uint64_t spi, uint64_t *spiptr, int cleanup) { struct iked_childsa *csa, *csatmp = NULL, *ipcomp; uint64_t peerspi = 0; int found = 0; TAILQ_FOREACH_SAFE(csa, &sa->sa_childsas, csa_entry, csatmp) { if ((saproto && csa->csa_saproto != saproto) || (spi && (csa->csa_spi.spi != spi && csa->csa_peerspi != spi)) || (cleanup && csa->csa_loaded)) continue; if (csa->csa_loaded) RB_REMOVE(iked_activesas, &env->sc_activesas, csa); if (ipsec_sa_delete(env, csa) != 0) log_info("%s: failed to delete CHILD SA spi %s", SPI_SA(sa, __func__), print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size)); else log_debug("%s: deleted CHILD SA spi %s", SPI_SA(sa, __func__), print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size)); found++; if (spi && csa->csa_spi.spi == spi) peerspi = csa->csa_peerspi; ipcomp = csa->csa_bundled; if (ipcomp) { if (ipcomp->csa_loaded) { if (ipsec_sa_delete(env, ipcomp) != 0) log_info("%s: failed to delete IPCOMP" " SA spi %s", SPI_SA(sa, __func__), print_spi(ipcomp->csa_spi.spi, ipcomp->csa_spi.spi_size)); else log_debug("%s: deleted IPCOMP SA spi %s", SPI_SA(sa, __func__), print_spi(ipcomp->csa_spi.spi, ipcomp->csa_spi.spi_size)); } childsa_free(ipcomp); } TAILQ_REMOVE(&sa->sa_childsas, csa, csa_entry); ikestat_inc(env, ikes_csa_removed); childsa_free(csa); } if (spiptr) *spiptr = peerspi; return (found ? 0 : -1); } int ikev2_valid_proposal(struct iked_proposal *prop, struct iked_transform **exf, struct iked_transform **ixf, int *esn) { struct iked_transform *xform, *encrxf, *integrxf; unsigned int i, doesn = 0; switch (prop->prop_protoid) { case IKEV2_SAPROTO_ESP: case IKEV2_SAPROTO_AH: break; default: return (-1); } encrxf = integrxf = NULL; for (i = 0; i < prop->prop_nxforms; i++) { xform = prop->prop_xforms + i; if (xform->xform_type == IKEV2_XFORMTYPE_ENCR) encrxf = xform; else if (xform->xform_type == IKEV2_XFORMTYPE_INTEGR) integrxf = xform; else if (xform->xform_type == IKEV2_XFORMTYPE_ESN && xform->xform_id == IKEV2_XFORMESN_ESN) doesn = 1; } if (prop->prop_protoid == IKEV2_SAPROTO_IKE) { if (encrxf == NULL || integrxf == NULL) return (-1); } else if (prop->prop_protoid == IKEV2_SAPROTO_AH) { if (integrxf == NULL) return (-1); } else if (prop->prop_protoid == IKEV2_SAPROTO_ESP) { if (encrxf == NULL) return (-1); } if (exf) *exf = encrxf; if (ixf) *ixf = integrxf; if (esn) *esn = doesn; return (0); } /* return 0 if processed, -1 if busy */ int ikev2_child_sa_acquire(struct iked *env, struct iked_flow *acquire) { struct iked_flow *flow; struct iked_sa *sa; struct iked_policy pol, *p = NULL; if (env->sc_passive) return (0); /* First try to find an active flow with IKE SA */ flow = RB_FIND(iked_flows, &env->sc_activeflows, acquire); if (!flow) { /* Otherwise try to find a matching policy */ bzero(&pol, sizeof(pol)); pol.pol_af = acquire->flow_peer->addr_af; memcpy(&pol.pol_peer, acquire->flow_peer, sizeof(pol.pol_peer)); RB_INIT(&pol.pol_flows); RB_INSERT(iked_flows, &pol.pol_flows, acquire); pol.pol_nflows = 1; if ((p = policy_test(env, &pol)) == NULL) { log_warnx("%s: flow wasn't found", __func__); return (0); } log_debug("%s: found matching policy '%s'", __func__, p->pol_name); if (ikev2_init_ike_sa_peer(env, p, &p->pol_peer, NULL) != 0) log_warnx("%s: failed to initiate a " "IKE_SA_INIT exchange for policy '%s'", __func__, p->pol_name); } else { log_debug("%s: found active flow", __func__); if ((sa = flow->flow_ikesa) == NULL) { log_warnx("%s: flow without SA", __func__); return (0); } if (sa->sa_stateflags & (IKED_REQ_CHILDSA|IKED_REQ_INF)) return (-1); /* busy, retry later */ if (ikev2_send_create_child_sa(env, sa, NULL, flow->flow_saproto, 0) != 0) log_warnx("%s: failed to initiate a " "CREATE_CHILD_SA exchange", SPI_SA(sa, __func__)); } return (0); } void ikev2_disable_rekeying(struct iked *env, struct iked_sa *sa) { struct iked_childsa *csa; TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) { csa->csa_persistent = 1; csa->csa_rekey = 0; } (void)ikev2_childsa_delete(env, sa, 0, 0, NULL, 1); } /* return 0 if processed, -1 if busy */ int ikev2_child_sa_rekey(struct iked *env, struct iked_spi *rekey) { struct iked_childsa *csa, key; struct iked_sa *sa; key.csa_spi = *rekey; csa = RB_FIND(iked_activesas, &env->sc_activesas, &key); if (!csa) return (0); if (csa->csa_rekey) /* See if it's already taken care of */ return (0); if ((sa = csa->csa_ikesa) == NULL) { log_warnx("%s: SA %s doesn't have a parent SA", __func__, print_spi(rekey->spi, rekey->spi_size)); return (0); } if (!sa_stateok(sa, IKEV2_STATE_ESTABLISHED)) { log_warnx("%s: not established, SPI %s", SPI_SA(sa, __func__), print_spi(rekey->spi, rekey->spi_size)); return (0); } if (sa->sa_stateflags & (IKED_REQ_CHILDSA|IKED_REQ_INF)) { log_info("%s: busy, retrying, SPI %s", SPI_SA(sa, __func__), print_spi(rekey->spi, rekey->spi_size)); return (-1); /* busy, retry later */ } if (sa->sa_tmpfail) { log_info("%s: peer busy, retrying, SPI %s", SPI_SA(sa, __func__), print_spi(rekey->spi, rekey->spi_size)); return (-1); /* peer is busy, retry later */ } if (csa->csa_allocated) /* Peer SPI died first, get the local one */ rekey->spi = csa->csa_peerspi; if (ikev2_send_create_child_sa(env, sa, rekey, rekey->spi_protoid, 0)) log_warnx("%s: failed to initiate a CREATE_CHILD_SA exchange", SPI_SA(sa, __func__)); return (0); } /* return 0 if processed, -1 if busy */ int ikev2_child_sa_drop(struct iked *env, struct iked_spi *drop) { struct ibuf *buf = NULL; struct iked_childsa *csa, key; struct iked_sa *sa; struct ikev2_delete *del; uint32_t spi32; key.csa_spi = *drop; csa = RB_FIND(iked_activesas, &env->sc_activesas, &key); if (!csa || csa->csa_rekey) return (0); sa = csa->csa_ikesa; if (sa && (sa->sa_stateflags & (IKED_REQ_CHILDSA|IKED_REQ_INF))) { /* XXXX might loop, should we add a counter? */ log_debug("%s: parent SA busy", __func__); return (-1); /* busy, retry later */ } RB_REMOVE(iked_activesas, &env->sc_activesas, csa); csa->csa_loaded = 0; csa->csa_rekey = 1; /* prevent re-loading */ if (sa == NULL) { log_debug("%s: failed to find a parent SA", __func__); return (0); } if (csa->csa_allocated) spi32 = htobe32(csa->csa_spi.spi); else spi32 = htobe32(csa->csa_peerspi); if (ikev2_childsa_delete(env, sa, csa->csa_saproto, csa->csa_peerspi, NULL, 0)) log_debug("%s: failed to delete CHILD SA %s", __func__, print_spi(csa->csa_peerspi, drop->spi_size)); /* Send PAYLOAD_DELETE */ if ((buf = ibuf_static()) == NULL) return (0); if ((del = ibuf_reserve(buf, sizeof(*del))) == NULL) goto done; del->del_protoid = drop->spi_protoid; del->del_spisize = 4; del->del_nspi = htobe16(1); if (ibuf_add(buf, &spi32, sizeof(spi32))) goto done; if (ikev2_send_ike_e(env, sa, buf, IKEV2_PAYLOAD_DELETE, IKEV2_EXCHANGE_INFORMATIONAL, 0) == -1) goto done; sa->sa_stateflags |= IKED_REQ_INF; done: ibuf_free(buf); return (0); } int ikev2_print_static_id(struct iked_static_id *id, char *idstr, size_t idstrlen) { struct iked_id idp; int ret = -1; bzero(&idp, sizeof(idp)); if ((idp.id_buf = ibuf_new(id->id_data, id->id_length)) == NULL) { bzero(idstr, idstrlen); return (-1); } idp.id_type = id->id_type; idp.id_offset = id->id_offset; if (ikev2_print_id(&idp, idstr, idstrlen) == -1) { bzero(idstr, idstrlen); goto done; } ret = 0; done: ibuf_free(idp.id_buf); return (ret); } int ikev2_print_id(struct iked_id *id, char *idstr, size_t idstrlen) { uint8_t *ptr; struct sockaddr_in s4 = { 0 }; struct sockaddr_in6 s6 = { 0 }; char *str; ssize_t len; int i; const char *type; bzero(idstr, idstrlen); if (id->id_buf == NULL) return (-1); len = ibuf_size(id->id_buf); ptr = ibuf_data(id->id_buf); if (len <= id->id_offset) return (-1); len -= id->id_offset; ptr += id->id_offset; type = print_map(id->id_type, ikev2_id_map); if (strlcpy(idstr, type, idstrlen) >= idstrlen || strlcat(idstr, "/", idstrlen) >= idstrlen) return (-1); switch (id->id_type) { case IKEV2_ID_IPV4: s4.sin_family = AF_INET; #ifdef HAVE_SOCKADDR_SA_LEN s4.sin_len = sizeof(s4); #endif memcpy(&s4.sin_addr.s_addr, ptr, len); if (strlcat(idstr, print_addr(&s4), idstrlen) >= idstrlen) return (-1); break; case IKEV2_ID_FQDN: case IKEV2_ID_UFQDN: if ((str = get_string(ptr, len)) == NULL) return (-1); if (strlcat(idstr, str, idstrlen) >= idstrlen) { free(str); return (-1); } free(str); break; case IKEV2_ID_IPV6: s6.sin6_family = AF_INET6; #ifdef HAVE_SOCKADDR_SA_LEN s6.sin6_len = sizeof(s6); #endif memcpy(&s6.sin6_addr, ptr, len); if (strlcat(idstr, print_addr(&s6), idstrlen) >= idstrlen) return (-1); break; case IKEV2_ID_ASN1_DN: if ((str = ca_asn1_name(ptr, len)) == NULL) return (-1); if (strlcat(idstr, str, idstrlen) >= idstrlen) { OPENSSL_free(str); return (-1); } OPENSSL_free(str); break; default: /* XXX test */ for (i = 0; i < len; i++) { char buf[3]; snprintf(buf, sizeof(buf), "%02x", ptr[i]); if (strlcat(idstr, buf, idstrlen) >= idstrlen) break; } break; } return (0); } /* * If we have an IKEV2_CP_REQUEST for IKEV2_CFG_INTERNAL_IP4_ADDRESS and * if a network(pool) is configured, then select an address from that pool * and remember it in the sa_addrpool attribute. */ int ikev2_cp_setaddr(struct iked *env, struct iked_sa *sa, sa_family_t family) { struct iked_policy *pol = sa->sa_policy; struct iked_cfg *ikecfg = NULL; const char *errstr = NULL; int ret, pass, passes; size_t i; switch (family) { case AF_INET: if (sa->sa_addrpool) return (0); break; case AF_INET6: if (sa->sa_addrpool6) return (0); break; default: return (-1); } if (pol->pol_ncfg == 0) return (0); /* default if no pool configured */ ret = 0; /* two passes if client requests from specific pool */ passes = (sa->sa_cp_addr != NULL || sa->sa_cp_addr6 != NULL) ? 2 : 1; for (pass = 0; pass < passes; pass++) { /* loop over all address pool configs (addr_net) */ for (i = 0; i < pol->pol_ncfg; i++) { ikecfg = &pol->pol_cfg[i]; if (!ikecfg->cfg.address.addr_net) continue; if ((family == AF_INET && ikecfg->cfg_type == IKEV2_CFG_INTERNAL_IP4_ADDRESS) || (family == AF_INET6 && ikecfg->cfg_type == IKEV2_CFG_INTERNAL_IP6_ADDRESS)) { if ((ret = ikev2_cp_setaddr_pool(env, sa, ikecfg, &errstr, family)) == 0) return (0); } } if (sa->sa_cp_addr != NULL) { free(sa->sa_cp_addr); sa->sa_cp_addr = NULL; } if (sa->sa_cp_addr6 != NULL) { free(sa->sa_cp_addr6); sa->sa_cp_addr6 = NULL; } } if (errstr != NULL) log_warnx("%s: %s", SPI_SA(sa, __func__), errstr); return (ret); } int ikev2_cp_setaddr_pool(struct iked *env, struct iked_sa *sa, struct iked_cfg *ikecfg, const char **errstr, sa_family_t family) { struct sockaddr_in *in4 = NULL, *cfg4 = NULL; struct sockaddr_in6 *in6 = NULL, *cfg6 = NULL; struct iked_sa key; struct iked_sa *osa; char idstr[IKED_ID_SIZE]; struct iked_addr addr; uint32_t mask, host, lower, upper, start, nhost; int requested = 0; /* * failure: pool configured, but not requested. * If we continue, we might end up with flows where 0.0.0.0 is NOT * replaced with an address from the pool with ikev2_cp_fixaddr(). */ if (sa->sa_cp != IKEV2_CP_REQUEST) { log_debug("%s: pool configured, but IKEV2_CP_REQUEST missing", __func__); return (-1); } bzero(&addr, sizeof(addr)); addr.addr_af = family; /* check if old IKESA for same DSTID already exists and transfer IPs */ if (env->sc_stickyaddress && (osa = sa_dstid_lookup(env, sa)) != NULL && ((family == AF_INET && osa->sa_addrpool) || (family == AF_INET6 && osa->sa_addrpool6))) { /* we have to transfer both, even if we just need one */ if (osa->sa_addrpool) { if (RB_REMOVE(iked_addrpool, &env->sc_addrpool, osa) != osa) { log_info("%s: addrpool error", SPI_SA(osa, __func__)); return (-1); } } if (osa->sa_addrpool6) { if (RB_REMOVE(iked_addrpool6, &env->sc_addrpool6, osa) != osa) { log_info("%s: addrpool6 error", SPI_SA(osa, __func__)); return (-1); } } sa_dstid_remove(env, osa); sa->sa_addrpool = osa->sa_addrpool; osa->sa_addrpool = NULL; sa->sa_addrpool6 = osa->sa_addrpool6; osa->sa_addrpool6 = NULL; if (osa->sa_state < IKEV2_STATE_CLOSING) { if (osa->sa_state == IKEV2_STATE_ESTABLISHED) ikev2_disable_timer(env, osa); ikev2_ike_sa_setreason(osa, "address re-use (identical dstid)"); ikev2_ikesa_delete(env, osa, 1); timer_add(env, &osa->sa_timer, 3 * IKED_RETRANSMIT_TIMEOUT); } if (sa->sa_addrpool) { RB_INSERT(iked_addrpool, &env->sc_addrpool, sa); log_info( "%s: giving up assigned address %s to IKESA %s", SPI_SA(osa, __func__), print_addr(&sa->sa_addrpool->addr), print_spi(sa->sa_hdr.sh_ispi, 8)); } if (sa->sa_addrpool6) { RB_INSERT(iked_addrpool6, &env->sc_addrpool6, sa); log_info( "%s: giving up assigned v6 address %s to IKESA %s", SPI_SA(osa, __func__), print_addr(&sa->sa_addrpool6->addr), print_spi(sa->sa_hdr.sh_ispi, 8)); } if (family == AF_INET && sa->sa_addrpool != NULL) memcpy(&addr, sa->sa_addrpool, sizeof(addr)); else if (family == AF_INET6 && sa->sa_addrpool6 != NULL) memcpy(&addr, sa->sa_addrpool6, sizeof(addr)); goto done; } switch (addr.addr_af) { case AF_INET: cfg4 = (struct sockaddr_in *)&ikecfg->cfg.address.addr; mask = prefixlen2mask(ikecfg->cfg.address.addr_mask); if (sa->sa_cp_addr != NULL) { memcpy(&addr, sa->sa_cp_addr, sizeof(addr)); key.sa_addrpool = &addr; in4 = (struct sockaddr_in *)&addr.addr; if ((in4->sin_addr.s_addr & mask) != (cfg4->sin_addr.s_addr & mask)) { *errstr = "requested addr out of range"; return (-1); } if (RB_FIND(iked_addrpool, &env->sc_addrpool, &key)) { *errstr = "requested addr in use"; return (-1); } sa->sa_addrpool = sa->sa_cp_addr; sa->sa_cp_addr = NULL; RB_INSERT(iked_addrpool, &env->sc_addrpool, sa); requested = 1; goto done; } in4 = (struct sockaddr_in *)&addr.addr; in4->sin_family = AF_INET; #ifdef HAVE_SOCKADDR_SA_LEN in4->sin_len = sizeof(*in4); #endif lower = ntohl(cfg4->sin_addr.s_addr & ~mask); key.sa_addrpool = &addr; break; case AF_INET6: cfg6 = (struct sockaddr_in6 *)&ikecfg->cfg.address.addr; in6 = (struct sockaddr_in6 *)&addr.addr; if (sa->sa_cp_addr6 != NULL) { /* XXX not yet supported */ } in6->sin6_family = AF_INET6; #ifdef HAVE_SOCKADDR_SA_LEN in6->sin6_len = sizeof(*in6); #endif /* truncate prefixlen to get a 32-bit space */ mask = (ikecfg->cfg.address.addr_mask >= 96) ? prefixlen2mask(ikecfg->cfg.address.addr_mask - 96) : prefixlen2mask(0); memcpy(&lower, &cfg6->sin6_addr.s6_addr[12], sizeof(uint32_t)); lower = ntohl(lower & ~mask); key.sa_addrpool6 = &addr; break; default: return (-1); } /* Note that start, upper and host are in HOST byte order */ upper = ntohl(~mask); /* skip .0 address if possible */ if (lower < upper && lower == 0) lower = 1; if (upper < lower) upper = lower; /* Randomly select start from [lower, upper-1] */ start = arc4random_uniform(upper - lower) + lower; for (host = start;;) { log_debug("%s: mask %x start %x lower %x host %x upper %x", __func__, mask, start, lower, host, upper); switch (addr.addr_af) { case AF_INET: in4->sin_addr.s_addr = (cfg4->sin_addr.s_addr & mask) | htonl(host); break; case AF_INET6: memcpy(in6, cfg6, sizeof(*in6)); memcpy(&nhost, &cfg6->sin6_addr.s6_addr[12], sizeof(uint32_t)); nhost = (nhost & mask) | htonl(host); memcpy(&in6->sin6_addr.s6_addr[12], &nhost, sizeof(uint32_t)); break; default: return (-1); } if ((addr.addr_af == AF_INET && !RB_FIND(iked_addrpool, &env->sc_addrpool, &key)) || (addr.addr_af == AF_INET6 && !RB_FIND(iked_addrpool6, &env->sc_addrpool6, &key))) break; /* try next address */ host++; /* but skip broadcast and network address */ if (host >= upper || host < lower) host = lower; if (host == start) { *errstr = "address pool exhausted"; return (-1); /* exhausted */ } } addr.addr_mask = ikecfg->cfg.address.addr_mask; switch (addr.addr_af) { case AF_INET: if (!key.sa_addrpool) return (-1); /* cannot happen? */ if ((sa->sa_addrpool = calloc(1, sizeof(addr))) == NULL) return (-1); memcpy(sa->sa_addrpool, &addr, sizeof(addr)); RB_INSERT(iked_addrpool, &env->sc_addrpool, sa); break; case AF_INET6: if (!key.sa_addrpool6) return (-1); /* cannot happen? */ if ((sa->sa_addrpool6 = calloc(1, sizeof(addr))) == NULL) return (-1); memcpy(sa->sa_addrpool6, &addr, sizeof(addr)); RB_INSERT(iked_addrpool6, &env->sc_addrpool6, sa); break; default: return (-1); } done: if (ikev2_print_id(IKESA_DSTID(sa), idstr, sizeof(idstr)) == -1) bzero(idstr, sizeof(idstr)); log_info("%sassigned address %s to %s%s", SPI_SA(sa, NULL), print_addr(&addr.addr), idstr, requested ? " (requested by peer)" : ""); return (0); } int ikev2_cp_request_configured(struct iked_sa *sa) { struct iked_policy *pol = sa->sa_policy; struct iked_cfg *ikecfg; unsigned int i; for (i = 0; i < pol->pol_ncfg; i++) { ikecfg = &pol->pol_cfg[i]; if (ikecfg->cfg_action == IKEV2_CP_REQUEST) { log_debug("%s: yes", SPI_SA(sa, __func__)); return 1; } } log_debug("%s: no", SPI_SA(sa, __func__)); return 0; } /* * if 'addr' is 'UNSPECIFIED' replace it with sa_addrpool from * the ip-pool or the sa_cp_addr received from peer and store the * result in 'patched'. */ int ikev2_cp_fixaddr(struct iked_sa *sa, struct iked_addr *addr, struct iked_addr *patched) { struct sockaddr_in *in4; struct sockaddr_in6 *in6; struct iked_addr *naddr; if (addr->addr_net) return (-2); if (sa->sa_cp == 0) return (-1); switch (addr->addr_af) { case AF_INET: in4 = (struct sockaddr_in *)&addr->addr; if (in4->sin_addr.s_addr) return (-2); naddr = (sa->sa_cp == IKEV2_CP_REQUEST) ? sa->sa_addrpool : sa->sa_cp_addr; if (naddr == NULL) return (-1); memcpy(patched, naddr, sizeof(*patched)); patched->addr_net = 0; patched->addr_mask = 32; break; case AF_INET6: in6 = (struct sockaddr_in6 *)&addr->addr; if (!IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) return (-2); naddr = (sa->sa_cp == IKEV2_CP_REQUEST) ? sa->sa_addrpool6 : sa->sa_cp_addr6; if (naddr == NULL) return (-1); memcpy(patched, naddr, sizeof(*patched)); patched->addr_net = 0; patched->addr_mask = 128; break; } return (0); } /* replace unspecified address in flow with requested address */ int ikev2_cp_fixflow(struct iked_sa *sa, struct iked_flow *flow, struct iked_flow *patched) { switch (sa->sa_cp) { case IKEV2_CP_REQUEST: if (patched->flow_dir == IPSP_DIRECTION_OUT) return (ikev2_cp_fixaddr(sa, &flow->flow_dst, &patched->flow_dst)); else return (ikev2_cp_fixaddr(sa, &flow->flow_dst, &patched->flow_src)); case IKEV2_CP_REPLY: if (patched->flow_dir == IPSP_DIRECTION_OUT) return (ikev2_cp_fixaddr(sa, &flow->flow_src, &patched->flow_src)); else return (ikev2_cp_fixaddr(sa, &flow->flow_src, &patched->flow_dst)); default: return (0); } } int ikev2_update_sa_addresses(struct iked *env, struct iked_sa *sa) { struct iked_childsa *csa, *ipcomp; struct iked_flow *flow, *oflow; struct iked_message *msg; struct iked_msg_retransmit *mr; if (!sa_stateok(sa, IKEV2_STATE_ESTABLISHED)) return -1; log_info("%s: old %s new %s", SPI_SA(sa, __func__), print_addr(&sa->sa_peer_loaded.addr), print_addr(&sa->sa_peer.addr)); TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) { if (!csa->csa_loaded) continue; if (ipsec_sa_update_addresses(env, csa) != 0) log_debug("%s: failed to update sa", __func__); if ((ipcomp = csa->csa_bundled) != NULL && ipcomp->csa_loaded) if (ipsec_sa_update_addresses(env, ipcomp) != 0) log_debug("%s: failed to update sa", __func__); } /* delete and re-add flows */ TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) { if (flow->flow_loaded) { RB_REMOVE(iked_flows, &env->sc_activeflows, flow); (void)ipsec_flow_delete(env, flow); flow->flow_loaded = 0; } if (ipsec_flow_add(env, flow) != 0) log_debug("%s: failed to add flow %p", __func__, flow); if (!flow->flow_loaded) continue; if ((oflow = RB_FIND(iked_flows, &env->sc_activeflows, flow)) != NULL) { log_debug("%s: replaced old flow %p with %p", __func__, oflow, flow); oflow->flow_loaded = 0; RB_REMOVE(iked_flows, &env->sc_activeflows, oflow); } RB_INSERT(iked_flows, &env->sc_activeflows, flow); } /* update pending requests and responses */ TAILQ_FOREACH(mr, &sa->sa_requests, mrt_entry) { TAILQ_FOREACH(msg, &mr->mrt_frags, msg_entry) { msg->msg_local = sa->sa_local.addr; msg->msg_locallen = SS_LEN(sa->sa_local.addr); msg->msg_peer = sa->sa_peer.addr; msg->msg_peerlen = SS_LEN(sa->sa_peer.addr); } } TAILQ_FOREACH(mr, &sa->sa_responses, mrt_entry) { TAILQ_FOREACH(msg, &mr->mrt_frags, msg_entry) { msg->msg_local = sa->sa_local.addr; msg->msg_locallen = SS_LEN(sa->sa_local.addr); msg->msg_peer = sa->sa_peer.addr; msg->msg_peerlen = SS_LEN(sa->sa_peer.addr); } } /* Update sa_peer_loaded, to match in-kernel information */ memcpy(&sa->sa_peer_loaded, &sa->sa_peer, sizeof(sa->sa_peer_loaded)); return 0; } void ikev2_info_sa(struct iked *env, struct imsg *imsg, int dolog, const char *msg, struct iked_sa *sa) { char idstr[IKED_ID_SIZE]; char *buf; int buflen; if (ikev2_print_id(IKESA_DSTID(sa), idstr, sizeof(idstr)) == -1) bzero(idstr, sizeof(idstr)); buflen = asprintf(&buf, "%s: %p rspi %s ispi %s %s->%s<%s>[%s] %s %c%s%s nexti %p pol %p\n", msg, sa, print_spi(sa->sa_hdr.sh_rspi, 8), print_spi(sa->sa_hdr.sh_ispi, 8), print_addr(&sa->sa_local.addr), print_addr(&sa->sa_peer.addr), idstr, sa->sa_addrpool ? print_addr(&sa->sa_addrpool->addr) : "", print_map(sa->sa_state, ikev2_state_map), sa->sa_hdr.sh_initiator ? 'i' : 'r', sa->sa_natt ? " natt" : "", sa->sa_udpencap ? " udpecap" : "", sa->sa_nexti, sa->sa_policy); if (buflen == -1 || buf == NULL) return; if (dolog) { if (buflen > 1) buf[buflen - 1] = '\0'; log_debug("%s", buf); } else proc_compose_imsg(&env->sc_ps, PROC_CONTROL, -1, IMSG_CTL_SHOW_SA, imsg->hdr.peerid, -1, buf, buflen + 1); free(buf); } void ikev2_info_csa(struct iked *env, struct imsg *imsg, int dolog, const char *msg, struct iked_childsa *csa) { char *buf; int buflen; buflen = asprintf(&buf, "%s: %p %s %s %s %s -> %s (%s%s%s%s) B=%p P=%p @%p\n", msg, csa, print_map(csa->csa_saproto, ikev2_saproto_map), print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size), csa->csa_dir == IPSP_DIRECTION_IN ? "in" : "out", print_addr(&csa->csa_local->addr), print_addr(&csa->csa_peer->addr), csa->csa_loaded ? "L" : "", csa->csa_rekey ? "R" : "", csa->csa_allocated ? "A" : "", csa->csa_persistent ? "P" : "", csa->csa_bundled, csa->csa_peersa, csa->csa_ikesa); if (buflen == -1 || buf == NULL) return; if (dolog) { if (buflen > 1) buf[buflen - 1] = '\0'; log_debug("%s", buf); } else proc_compose_imsg(&env->sc_ps, PROC_CONTROL, -1, IMSG_CTL_SHOW_SA, imsg->hdr.peerid, -1, buf, buflen + 1); free(buf); } void ikev2_info_flow(struct iked *env, struct imsg *imsg, int dolog, const char *msg, struct iked_flow *flow) { char prenat_mask[10]; char *buf; int buflen; if (flow->flow_prenat.addr_af != 0) snprintf(prenat_mask, sizeof(prenat_mask), "%d", flow->flow_prenat.addr_mask); else prenat_mask[0] = '\0'; buflen = asprintf(&buf, "%s: %p %s %s %s/%d -> %s/%d %s%s%s%s%s[%u]@%d (%s) @%p\n", msg, flow, print_map(flow->flow_saproto, ikev2_saproto_map), flow->flow_dir == IPSP_DIRECTION_IN ? "in" : "out", print_addr(&flow->flow_src.addr), flow->flow_src.addr_mask, print_addr(&flow->flow_dst.addr), flow->flow_dst.addr_mask, flow->flow_prenat.addr_af != 0 ? "[": "", flow->flow_prenat.addr_af != 0 ? print_addr(&flow->flow_prenat.addr) : "", flow->flow_prenat.addr_af != 0 ? "/" : "", flow->flow_prenat.addr_af != 0 ? prenat_mask : "", flow->flow_prenat.addr_af != 0 ? "] ": "", flow->flow_ipproto, flow->flow_rdomain, flow->flow_loaded ? "L" : "", flow->flow_ikesa); if (buflen == -1 || buf == NULL) return; if (dolog) { if (buflen > 1) buf[buflen - 1] = '\0'; log_debug("%s", buf); } else proc_compose_imsg(&env->sc_ps, PROC_CONTROL, -1, IMSG_CTL_SHOW_SA, imsg->hdr.peerid, -1, buf, buflen + 1); free(buf); } void ikev2_info(struct iked *env, struct imsg *imsg, int dolog) { struct iked_sa *sa; struct iked_childsa *csa, *ipcomp; struct iked_flow *flow; log_debug("%s: called", __func__); RB_FOREACH(sa, iked_sas, &env->sc_sas) { ikev2_info_sa(env, imsg, dolog, "iked_sas", sa); TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) { ikev2_info_csa(env, imsg, dolog, " sa_childsas", csa); if ((ipcomp = csa->csa_bundled) != NULL) ikev2_info_csa(env, imsg, dolog, " ", ipcomp); } TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) { ikev2_info_flow(env, imsg, dolog, " sa_flows", flow); } } RB_FOREACH(csa, iked_activesas, &env->sc_activesas) { ikev2_info_csa(env, imsg, dolog, "iked_activesas", csa); if ((ipcomp = csa->csa_bundled) != NULL) ikev2_info_csa(env, imsg, dolog, " ", ipcomp); } RB_FOREACH(flow, iked_flows, &env->sc_activeflows) { ikev2_info_flow(env, imsg, dolog, "iked_flows", flow); } RB_FOREACH(sa, iked_dstid_sas, &env->sc_dstid_sas) { ikev2_info_sa(env, imsg, dolog, "iked_dstid_sas", sa); } if (dolog) return; /* Send empty reply to indicate end of information. */ proc_compose_imsg(&env->sc_ps, PROC_CONTROL, -1, IMSG_CTL_SHOW_SA, imsg->hdr.peerid, -1, NULL, 0); } const char * ikev2_ikesa_info(uint64_t spi, const char *msg) { static char buf[1024]; const char *spistr; spistr = print_spi(spi, 8); if (msg) snprintf(buf, sizeof(buf), "spi=%s: %s", spistr, msg); else snprintf(buf, sizeof(buf), "spi=%s: ", spistr); return buf; } void ikev2_log_established(struct iked_sa *sa) { char dstid[IKED_ID_SIZE], srcid[IKED_ID_SIZE]; if (ikev2_print_id(IKESA_DSTID(sa), dstid, sizeof(dstid)) == -1) bzero(dstid, sizeof(dstid)); if (ikev2_print_id(IKESA_SRCID(sa), srcid, sizeof(srcid)) == -1) bzero(srcid, sizeof(srcid)); log_info( "%sestablished peer %s[%s] local %s[%s]%s%s%s%s policy '%s'%s" " (enc %s%s%s group %s prf %s)", SPI_SA(sa, NULL), print_addr(&sa->sa_peer.addr), dstid, print_addr(&sa->sa_local.addr), srcid, sa->sa_addrpool ? " assigned " : "", sa->sa_addrpool ? print_addr(&sa->sa_addrpool->addr) : "", sa->sa_addrpool6 ? " assigned " : "", sa->sa_addrpool6 ? print_addr(&sa->sa_addrpool6->addr) : "", sa->sa_policy ? sa->sa_policy->pol_name : "", sa->sa_hdr.sh_initiator ? " as initiator" : " as responder", print_xf(sa->sa_encr->encr_id, cipher_keylength(sa->sa_encr) - sa->sa_encr->encr_saltlength, ikeencxfs), sa->sa_encr->encr_authid ? "" : " auth ", sa->sa_encr->encr_authid ? "" : print_xf(sa->sa_integr->hash_id, hash_keylength(sa->sa_integr), authxfs), print_xf(sa->sa_dhgroup->id, 0, groupxfs), print_xf(sa->sa_prf->hash_id, hash_keylength(sa->sa_prf), prfxfs)); } void ikev2_log_cert_info(const char *msg, struct iked_id *certid) { X509 *cert = NULL; BIO *rawcert = NULL; if (certid->id_type != IKEV2_CERT_X509_CERT || certid->id_buf == NULL) return; if ((rawcert = BIO_new_mem_buf(ibuf_data(certid->id_buf), ibuf_size(certid->id_buf))) == NULL || (cert = d2i_X509_bio(rawcert, NULL)) == NULL) goto out; ca_cert_info(msg, cert); out: if (cert) X509_free(cert); if (rawcert) BIO_free(rawcert); } void ikev2_log_proposal(struct iked_sa *sa, struct iked_proposals *proposals) { struct iked_proposal *prop; struct iked_transform *xform; unsigned int i; char lenstr[20]; TAILQ_FOREACH(prop, proposals, prop_entry) { for (i = 0; i < prop->prop_nxforms; i++) { xform = &prop->prop_xforms[i]; if (xform->xform_keylength) snprintf(lenstr, sizeof(lenstr), "-%u", xform->xform_keylength); else lenstr[0] = '\0'; log_info("%s: %s #%u %s=%s%s", sa ? SPI_SA(sa, __func__) : __func__, print_map(prop->prop_protoid, ikev2_saproto_map), prop->prop_id, print_map(xform->xform_type, ikev2_xformtype_map), xform->xform_map ? print_map(xform->xform_id, xform->xform_map) : "UNKNOWN", lenstr); } } } openiked-7.4/iked/ikev2.h000066400000000000000000000511711477554731100153120ustar00rootroot00000000000000/* $OpenBSD: ikev2.h,v 1.35 2023/06/28 14:10:24 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef IKED_IKEV2_H #define IKED_IKEV2_H #include "openbsd-compat.h" #define IKEV2_VERSION 0x20 /* IKE version 2.0 */ #define IKEV1_VERSION 0x10 /* IKE version 1.0 */ #define IKEV2_KEYPAD "Key Pad for IKEv2" /* don't change! */ /* * IKEv2 pseudo states */ #define IKEV2_STATE_INIT 0 /* new IKE SA */ #define IKEV2_STATE_COOKIE 1 /* cookie requested */ #define IKEV2_STATE_SA_INIT 2 /* init IKE SA */ #define IKEV2_STATE_EAP 3 /* EAP requested */ #define IKEV2_STATE_EAP_SUCCESS 4 /* EAP succeeded */ #define IKEV2_STATE_AUTH_REQUEST 5 /* auth received */ #define IKEV2_STATE_AUTH_SUCCESS 6 /* authenticated */ #define IKEV2_STATE_VALID 7 /* authenticated AND validated certs */ #define IKEV2_STATE_EAP_VALID 8 /* EAP validated */ #define IKEV2_STATE_ESTABLISHED 9 /* active IKE SA */ #define IKEV2_STATE_CLOSING 10 /* expect delete for this SA */ #define IKEV2_STATE_CLOSED 11 /* delete this SA */ extern struct iked_constmap ikev2_state_map[]; /* * "IKEv2 Parameters" based on the official RFC-based assignments by IANA * (http://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.txt) */ /* * IKEv2 definitions of the IKE header */ /* IKEv2 exchange types */ #define IKEV2_EXCHANGE_IKE_SA_INIT 34 /* Initial Exchange */ #define IKEV2_EXCHANGE_IKE_AUTH 35 /* Authentication */ #define IKEV2_EXCHANGE_CREATE_CHILD_SA 36 /* Create Child SA */ #define IKEV2_EXCHANGE_INFORMATIONAL 37 /* Informational */ #define IKEV2_EXCHANGE_IKE_SESSION_RESUME 38 /* RFC5723 */ extern struct iked_constmap ikev2_exchange_map[]; /* IKEv2 message flags */ #define IKEV2_FLAG_INITIATOR 0x08 /* Sent by the initiator */ #define IKEV2_FLAG_OLDVERSION 0x10 /* Supports a higher IKE version */ #define IKEV2_FLAG_RESPONSE 0x20 /* Message is a response */ extern struct iked_constmap ikev2_flag_map[]; /* * IKEv2 payloads */ struct ikev2_payload { uint8_t pld_nextpayload; /* Next payload type */ uint8_t pld_reserved; /* Contains the critical bit */ uint16_t pld_length; /* Payload length with header */ } __packed; struct ikev2_frag_payload { uint16_t frag_num; /* current fragment message number */ uint16_t frag_total; /* total number of fragment messages */ } __packed; #define IKEV2_CRITICAL_PAYLOAD 0x01 /* First bit in the reserved field */ /* IKEv2 payload types */ #define IKEV2_PAYLOAD_NONE 0 /* No payload */ #define IKEV2_PAYLOAD_SA 33 /* Security Association */ #define IKEV2_PAYLOAD_KE 34 /* Key Exchange */ #define IKEV2_PAYLOAD_IDi 35 /* Identification - Initiator */ #define IKEV2_PAYLOAD_IDr 36 /* Identification - Responder */ #define IKEV2_PAYLOAD_CERT 37 /* Certificate */ #define IKEV2_PAYLOAD_CERTREQ 38 /* Certificate Request */ #define IKEV2_PAYLOAD_AUTH 39 /* Authentication */ #define IKEV2_PAYLOAD_NONCE 40 /* Nonce */ #define IKEV2_PAYLOAD_NOTIFY 41 /* Notify */ #define IKEV2_PAYLOAD_DELETE 42 /* Delete */ #define IKEV2_PAYLOAD_VENDOR 43 /* Vendor ID */ #define IKEV2_PAYLOAD_TSi 44 /* Traffic Selector - Initiator */ #define IKEV2_PAYLOAD_TSr 45 /* Traffic Selector - Responder */ #define IKEV2_PAYLOAD_SK 46 /* Encrypted */ #define IKEV2_PAYLOAD_CP 47 /* Configuration Payload */ #define IKEV2_PAYLOAD_EAP 48 /* Extensible Authentication */ #define IKEV2_PAYLOAD_GSPM 49 /* RFC6467 Generic Secure Password */ #define IKEV2_PAYLOAD_SKF 53 /* RFC7383 Encrypted Fragment Payload */ extern struct iked_constmap ikev2_payload_map[]; /* * SA payload */ struct ikev2_sa_proposal { uint8_t sap_more; /* Last proposal or more */ uint8_t sap_reserved; /* Must be set to zero */ uint16_t sap_length; /* Proposal length */ uint8_t sap_proposalnr; /* Proposal number */ uint8_t sap_protoid; /* Protocol Id */ uint8_t sap_spisize; /* SPI size */ uint8_t sap_transforms; /* Number of transforms */ /* Followed by variable-length SPI */ /* Followed by variable-length transforms */ } __packed; #define IKEV2_SAP_LAST 0 #define IKEV2_SAP_MORE 2 #define IKEV2_SAPROTO_NONE 0 /* None */ #define IKEV2_SAPROTO_IKE 1 /* IKEv2 */ #define IKEV2_SAPROTO_AH 2 /* AH */ #define IKEV2_SAPROTO_ESP 3 /* ESP */ #define IKEV2_SAPROTO_FC_ESP_HEADER 4 /* RFC4595 */ #define IKEV2_SAPROTO_FC_CT_AUTH 5 /* RFC4595 */ #define IKEV2_SAPROTO_IPCOMP 204 /* private, should be 4 */ extern struct iked_constmap ikev2_saproto_map[]; struct ikev2_transform { uint8_t xfrm_more; /* Last transform or more */ uint8_t xfrm_reserved; /* Must be set to zero */ uint16_t xfrm_length; /* Transform length */ uint8_t xfrm_type; /* Transform type */ uint8_t xfrm_reserved1; /* Must be set to zero */ uint16_t xfrm_id; /* Transform Id */ /* Followed by variable-length transform attributes */ } __packed; #define IKEV2_XFORM_LAST 0 #define IKEV2_XFORM_MORE 3 #define IKEV2_XFORMTYPE_ENCR 1 /* Encryption */ #define IKEV2_XFORMTYPE_PRF 2 /* Pseudo-Random Function */ #define IKEV2_XFORMTYPE_INTEGR 3 /* Integrity Algorithm */ #define IKEV2_XFORMTYPE_DH 4 /* Diffie-Hellman Group */ #define IKEV2_XFORMTYPE_ESN 5 /* Extended Sequence Numbers */ #define IKEV2_XFORMTYPE_MAX 6 extern struct iked_constmap ikev2_xformtype_map[]; #define IKEV2_XFORMENCR_NONE 0 /* None */ #define IKEV2_XFORMENCR_DES_IV64 1 /* RFC1827 */ #define IKEV2_XFORMENCR_DES 2 /* RFC2405 */ #define IKEV2_XFORMENCR_3DES 3 /* RFC2451 */ #define IKEV2_XFORMENCR_RC5 4 /* RFC2451 */ #define IKEV2_XFORMENCR_IDEA 5 /* RFC2451 */ #define IKEV2_XFORMENCR_CAST 6 /* RFC2451 */ #define IKEV2_XFORMENCR_BLOWFISH 7 /* RFC2451 */ #define IKEV2_XFORMENCR_3IDEA 8 /* RFC2451 */ #define IKEV2_XFORMENCR_DES_IV32 9 /* DESIV32 */ #define IKEV2_XFORMENCR_RC4 10 /* RFC2451 */ #define IKEV2_XFORMENCR_NULL 11 /* RFC2410 */ #define IKEV2_XFORMENCR_AES_CBC 12 /* RFC3602 */ #define IKEV2_XFORMENCR_AES_CTR 13 /* RFC3664 */ #define IKEV2_XFORMENCR_AES_CCM_8 14 /* RFC5282 */ #define IKEV2_XFORMENCR_AES_CCM_12 15 /* RFC5282 */ #define IKEV2_XFORMENCR_AES_CCM_16 16 /* RFC5282 */ #define IKEV2_XFORMENCR_AES_GCM_8 18 /* RFC5282 */ #define IKEV2_XFORMENCR_AES_GCM_12 19 /* RFC5282 */ #define IKEV2_XFORMENCR_AES_GCM_16 20 /* RFC5282 */ #define IKEV2_XFORMENCR_NULL_AES_GMAC 21 /* RFC4543 */ #define IKEV2_XFORMENCR_XTS_AES 22 /* IEEE P1619 */ #define IKEV2_XFORMENCR_CAMELLIA_CBC 23 /* RFC5529 */ #define IKEV2_XFORMENCR_CAMELLIA_CTR 24 /* RFC5529 */ #define IKEV2_XFORMENCR_CAMELLIA_CCM_8 25 /* RFC5529 */ #define IKEV2_XFORMENCR_CAMELLIA_CCM_12 26 /* RFC5529 */ #define IKEV2_XFORMENCR_CAMELLIA_CCM_16 27 /* RFC5529 */ #define IKEV2_XFORMENCR_CHACHA20_POLY1305 28 /* RFC7634 */ extern struct iked_constmap ikev2_xformencr_map[]; #define IKEV2_IPCOMP_OUI 1 /* UNSPECIFIED */ #define IKEV2_IPCOMP_DEFLATE 2 /* RFC2394 */ #define IKEV2_IPCOMP_LZS 3 /* RFC2395 */ #define IKEV2_IPCOMP_LZJH 4 /* RFC3051 */ extern struct iked_constmap ikev2_ipcomp_map[]; #define IKEV2_XFORMPRF_HMAC_MD5 1 /* RFC2104 */ #define IKEV2_XFORMPRF_HMAC_SHA1 2 /* RFC2104 */ #define IKEV2_XFORMPRF_HMAC_TIGER 3 /* RFC2104 */ #define IKEV2_XFORMPRF_AES128_XCBC 4 /* RFC3664 */ #define IKEV2_XFORMPRF_HMAC_SHA2_256 5 /* RFC4868 */ #define IKEV2_XFORMPRF_HMAC_SHA2_384 6 /* RFC4868 */ #define IKEV2_XFORMPRF_HMAC_SHA2_512 7 /* RFC4868 */ #define IKEV2_XFORMPRF_AES128_CMAC 8 /* RFC4615 */ extern struct iked_constmap ikev2_xformprf_map[]; #define IKEV2_XFORMAUTH_NONE 0 /* No Authentication */ #define IKEV2_XFORMAUTH_HMAC_MD5_96 1 /* RFC2403 */ #define IKEV2_XFORMAUTH_HMAC_SHA1_96 2 /* RFC2404 */ #define IKEV2_XFORMAUTH_DES_MAC 3 /* DES-MAC */ #define IKEV2_XFORMAUTH_KPDK_MD5 4 /* RFC1826 */ #define IKEV2_XFORMAUTH_AES_XCBC_96 5 /* RFC3566 */ #define IKEV2_XFORMAUTH_HMAC_MD5_128 6 /* RFC4595 */ #define IKEV2_XFORMAUTH_HMAC_SHA1_160 7 /* RFC4595 */ #define IKEV2_XFORMAUTH_AES_CMAC_96 8 /* RFC4494 */ #define IKEV2_XFORMAUTH_AES_128_GMAC 9 /* RFC4543 */ #define IKEV2_XFORMAUTH_AES_192_GMAC 10 /* RFC4543 */ #define IKEV2_XFORMAUTH_AES_256_GMAC 11 /* RFC4543 */ #define IKEV2_XFORMAUTH_HMAC_SHA2_256_128 12 /* RFC4868 */ #define IKEV2_XFORMAUTH_HMAC_SHA2_384_192 13 /* RFC4868 */ #define IKEV2_XFORMAUTH_HMAC_SHA2_512_256 14 /* RFC4868 */ /* Placeholders for AEAD ciphers (only used internally) */ #define IKEV2_XFORMAUTH_AES_GCM_8 2018 /* internal */ #define IKEV2_XFORMAUTH_AES_GCM_12 2019 /* internal */ #define IKEV2_XFORMAUTH_AES_GCM_16 2020 /* internal */ extern struct iked_constmap ikev2_xformauth_map[]; #define IKEV2_XFORMDH_NONE 0 /* No DH */ #define IKEV2_XFORMDH_MODP_768 1 /* DH Group 1 */ #define IKEV2_XFORMDH_MODP_1024 2 /* DH Group 2 */ #define IKEV2_XFORMDH_MODP_1536 5 /* DH Group 5 */ #define IKEV2_XFORMDH_MODP_2048 14 /* DH Group 14 */ #define IKEV2_XFORMDH_MODP_3072 15 /* DH Group 15 */ #define IKEV2_XFORMDH_MODP_4096 16 /* DH Group 16 */ #define IKEV2_XFORMDH_MODP_6144 17 /* DH Group 17 */ #define IKEV2_XFORMDH_MODP_8192 18 /* DH Group 18 */ #define IKEV2_XFORMDH_ECP_256 19 /* RFC5114 */ #define IKEV2_XFORMDH_ECP_384 20 /* RFC5114 */ #define IKEV2_XFORMDH_ECP_521 21 /* RFC5114 */ #define IKEV2_XFORMDH_ECP_192 25 /* RFC5114 */ #define IKEV2_XFORMDH_ECP_224 26 /* RFC5114 */ #define IKEV2_XFORMDH_BRAINPOOL_P224R1 27 /* RFC6954 */ #define IKEV2_XFORMDH_BRAINPOOL_P256R1 28 /* RFC6954 */ #define IKEV2_XFORMDH_BRAINPOOL_P384R1 29 /* RFC6954 */ #define IKEV2_XFORMDH_BRAINPOOL_P512R1 30 /* RFC6954 */ #define IKEV2_XFORMDH_CURVE25519 31 /* RFC8031 */ #define IKEV2_XFORMDH_X_SNTRUP761X25519 1035 /* private */ extern struct iked_constmap ikev2_xformdh_map[]; #define IKEV2_IPV4_OVERHEAD (20 + 8 + 28) /* IPv4 + UDP + IKE_HDR*/ #define IKEV2_MAXLEN_IPV4_FRAG (576 - IKEV2_IPV4_OVERHEAD) #define IKEV2_IPV6_OVERHEAD (40 + 8 + 28) /* IPv6 + UDP + IKE_HDR*/ #define IKEV2_MAXLEN_IPV6_FRAG (1280 - IKEV2_IPV6_OVERHEAD) #define IKEV2_MAXNUM_TSS 255 /* 8 bit Number of TSs field */ #define IKEV2_XFORMESN_NONE 0 /* No ESN */ #define IKEV2_XFORMESN_ESN 1 /* ESN */ extern struct iked_constmap ikev2_xformesn_map[]; struct ikev2_attribute { uint16_t attr_type; /* Attribute type */ uint16_t attr_length; /* Attribute length or value */ /* Followed by variable length (TLV) */ } __packed; #define IKEV2_ATTRAF_TLV 0x0000 /* Type-Length-Value format */ #define IKEV2_ATTRAF_TV 0x8000 /* Type-Value format */ #define IKEV2_ATTRTYPE_KEY_LENGTH 14 /* Key length */ extern struct iked_constmap ikev2_attrtype_map[]; /* * KE Payload */ struct ikev2_keyexchange { uint16_t kex_dhgroup; /* DH Group # */ uint16_t kex_reserved; /* Reserved */ } __packed; /* * N payload */ struct ikev2_notify { uint8_t n_protoid; /* Protocol Id */ uint8_t n_spisize; /* SPI size */ uint16_t n_type; /* Notify message type */ /* Followed by variable length SPI */ /* Followed by variable length notification data */ } __packed; #define IKEV2_N_UNSUPPORTED_CRITICAL_PAYLOAD 1 /* RFC7296 */ #define IKEV2_N_INVALID_IKE_SPI 4 /* RFC7296 */ #define IKEV2_N_INVALID_MAJOR_VERSION 5 /* RFC7296 */ #define IKEV2_N_INVALID_SYNTAX 7 /* RFC7296 */ #define IKEV2_N_INVALID_MESSAGE_ID 9 /* RFC7296 */ #define IKEV2_N_INVALID_SPI 11 /* RFC7296 */ #define IKEV2_N_NO_PROPOSAL_CHOSEN 14 /* RFC7296 */ #define IKEV2_N_INVALID_KE_PAYLOAD 17 /* RFC7296 */ #define IKEV2_N_AUTHENTICATION_FAILED 24 /* RFC7296 */ #define IKEV2_N_SINGLE_PAIR_REQUIRED 34 /* RFC7296 */ #define IKEV2_N_NO_ADDITIONAL_SAS 35 /* RFC7296 */ #define IKEV2_N_INTERNAL_ADDRESS_FAILURE 36 /* RFC7296 */ #define IKEV2_N_FAILED_CP_REQUIRED 37 /* RFC7296 */ #define IKEV2_N_TS_UNACCEPTABLE 38 /* RFC7296 */ #define IKEV2_N_INVALID_SELECTORS 39 /* RFC7296 */ #define IKEV2_N_UNACCEPTABLE_ADDRESSES 40 /* RFC4555 */ #define IKEV2_N_UNEXPECTED_NAT_DETECTED 41 /* RFC4555 */ #define IKEV2_N_USE_ASSIGNED_HoA 42 /* RFC5026 */ #define IKEV2_N_TEMPORARY_FAILURE 43 /* RFC7296 */ #define IKEV2_N_CHILD_SA_NOT_FOUND 44 /* RFC7296 */ #define IKEV2_N_INITIAL_CONTACT 16384 /* RFC7296 */ #define IKEV2_N_SET_WINDOW_SIZE 16385 /* RFC7296 */ #define IKEV2_N_ADDITIONAL_TS_POSSIBLE 16386 /* RFC7296 */ #define IKEV2_N_IPCOMP_SUPPORTED 16387 /* RFC7296 */ #define IKEV2_N_NAT_DETECTION_SOURCE_IP 16388 /* RFC7296 */ #define IKEV2_N_NAT_DETECTION_DESTINATION_IP 16389 /* RFC7296 */ #define IKEV2_N_COOKIE 16390 /* RFC7296 */ #define IKEV2_N_USE_TRANSPORT_MODE 16391 /* RFC7296 */ #define IKEV2_N_HTTP_CERT_LOOKUP_SUPPORTED 16392 /* RFC7296 */ #define IKEV2_N_REKEY_SA 16393 /* RFC7296 */ #define IKEV2_N_ESP_TFC_PADDING_NOT_SUPPORTED 16394 /* RFC7296 */ #define IKEV2_N_NON_FIRST_FRAGMENTS_ALSO 16395 /* RFC7296 */ #define IKEV2_N_MOBIKE_SUPPORTED 16396 /* RFC4555 */ #define IKEV2_N_ADDITIONAL_IP4_ADDRESS 16397 /* RFC4555 */ #define IKEV2_N_ADDITIONAL_IP6_ADDRESS 16398 /* RFC4555 */ #define IKEV2_N_NO_ADDITIONAL_ADDRESSES 16399 /* RFC4555 */ #define IKEV2_N_UPDATE_SA_ADDRESSES 16400 /* RFC4555 */ #define IKEV2_N_COOKIE2 16401 /* RFC4555 */ #define IKEV2_N_NO_NATS_ALLOWED 16402 /* RFC4555 */ #define IKEV2_N_AUTH_LIFETIME 16403 /* RFC4478 */ #define IKEV2_N_MULTIPLE_AUTH_SUPPORTED 16404 /* RFC4739 */ #define IKEV2_N_ANOTHER_AUTH_FOLLOWS 16405 /* RFC4739 */ #define IKEV2_N_REDIRECT_SUPPORTED 16406 /* RFC5685 */ #define IKEV2_N_REDIRECT 16407 /* RFC5685 */ #define IKEV2_N_REDIRECTED_FROM 16408 /* RFC5685 */ #define IKEV2_N_TICKET_LT_OPAQUE 16409 /* RFC5723 */ #define IKEV2_N_TICKET_REQUEST 16410 /* RFC5723 */ #define IKEV2_N_TICKET_ACK 16411 /* RFC5723 */ #define IKEV2_N_TICKET_NACK 16412 /* RFC5723 */ #define IKEV2_N_TICKET_OPAQUE 16413 /* RFC5723 */ #define IKEV2_N_LINK_ID 16414 /* RFC5739 */ #define IKEV2_N_USE_WESP_MODE 16415 /* RFC5415 */ #define IKEV2_N_ROHC_SUPPORTED 16416 /* RFC5857 */ #define IKEV2_N_EAP_ONLY_AUTHENTICATION 16417 /* RFC5998 */ #define IKEV2_N_CHILDLESS_IKEV2_SUPPORTED 16418 /* RFC6023 */ #define IKEV2_N_QUICK_CRASH_DETECTION 16419 /* RFC6290 */ #define IKEV2_N_IKEV2_MESSAGE_ID_SYNC_SUPPORTED 16420 /* RFC6311 */ #define IKEV2_N_IPSEC_REPLAY_CTR_SYNC_SUPPORTED 16421 /* RFC6311 */ #define IKEV2_N_IKEV2_MESSAGE_ID_SYNC 16422 /* RFC6311 */ #define IKEV2_N_IPSEC_REPLAY_CTR_SYNC 16423 /* RFC6311 */ #define IKEV2_N_SECURE_PASSWORD_METHODS 16424 /* RFC6467 */ #define IKEV2_N_PSK_PERSIST 16425 /* RFC6631 */ #define IKEV2_N_PSK_CONFIRM 16426 /* RFC6631 */ #define IKEV2_N_ERX_SUPPORTED 16427 /* RFC6867 */ #define IKEV2_N_IFOM_CAPABILITY 16428 /* OA3GPP */ #define IKEV2_N_FRAGMENTATION_SUPPORTED 16430 /* RFC7383 */ #define IKEV2_N_SIGNATURE_HASH_ALGORITHMS 16431 /* RFC7427 */ extern struct iked_constmap ikev2_n_map[]; /* * DELETE payload */ struct ikev2_delete { uint8_t del_protoid; /* Protocol Id */ uint8_t del_spisize; /* SPI size */ uint16_t del_nspi; /* Number of SPIs */ /* Followed by variable length SPIs */ } __packed; /* * ID payload */ struct ikev2_id { uint8_t id_type; /* Id type */ uint8_t id_reserved[3]; /* Reserved */ /* Followed by the identification data */ } __packed; #define IKEV2_ID_NONE 0 /* No ID */ #define IKEV2_ID_IPV4 1 /* RFC7296 (ID_IPV4_ADDR) */ #define IKEV2_ID_FQDN 2 /* RFC7296 */ #define IKEV2_ID_UFQDN 3 /* RFC7296 (ID_RFC822_ADDR) */ #define IKEV2_ID_IPV6 5 /* RFC7296 (ID_IPV6_ADDR) */ #define IKEV2_ID_ASN1_DN 9 /* RFC7296 */ #define IKEV2_ID_ASN1_GN 10 /* RFC7296 */ #define IKEV2_ID_KEY_ID 11 /* RFC7296 */ #define IKEV2_ID_FC_NAME 12 /* RFC4595 */ extern struct iked_constmap ikev2_id_map[]; /* * CERT/CERTREQ payloads */ struct ikev2_cert { uint8_t cert_type; /* Encoding */ /* Followed by the certificate data */ } __packed; #define IKEV2_CERT_NONE 0 /* None */ #define IKEV2_CERT_X509_PKCS7 1 /* UNSPECIFIED */ #define IKEV2_CERT_PGP 2 /* UNSPECIFIED */ #define IKEV2_CERT_DNS_SIGNED_KEY 3 /* UNSPECIFIED */ #define IKEV2_CERT_X509_CERT 4 /* RFC7296 */ #define IKEV2_CERT_KERBEROS_TOKEN 6 /* UNSPECIFIED */ #define IKEV2_CERT_CRL 7 /* RFC7296 */ #define IKEV2_CERT_ARL 8 /* UNSPECIFIED */ #define IKEV2_CERT_SPKI 9 /* UNSPECIFIED */ #define IKEV2_CERT_X509_ATTR 10 /* UNSPECIFIED */ #define IKEV2_CERT_RSA_KEY 11 /* RFC7296 */ #define IKEV2_CERT_HASHURL_X509 12 /* RFC7296 */ #define IKEV2_CERT_HASHURL_X509_BUNDLE 13 /* RFC7296 */ #define IKEV2_CERT_OCSP 14 /* RFC4806 */ /* * As of November 2014, work was still in progress to add a more generic * format for raw public keys (RFC7296), so we use a number in IANA's private * use range (201-255, same RFC) for ECDSA. */ #define IKEV2_CERT_ECDSA 201 /* Private */ #define IKEV2_CERT_BUNDLE 254 /* Private */ extern struct iked_constmap ikev2_cert_map[]; /* * TSi/TSr payloads */ struct ikev2_tsp { uint8_t tsp_count; /* Number of TSs */ uint8_t tsp_reserved[3]; /* Reserved */ /* Followed by the traffic selectors */ } __packed; struct ikev2_ts { uint8_t ts_type; /* TS type */ uint8_t ts_protoid; /* Protocol Id */ uint16_t ts_length; /* Length */ uint16_t ts_startport; /* Start port */ uint16_t ts_endport; /* End port */ } __packed; #define IKEV2_TS_IPV4_ADDR_RANGE 7 /* RFC7296 */ #define IKEV2_TS_IPV6_ADDR_RANGE 8 /* RFC7296 */ #define IKEV2_TS_FC_ADDR_RANGE 9 /* RFC4595 */ extern struct iked_constmap ikev2_ts_map[]; /* * AUTH payload */ struct ikev2_auth { uint8_t auth_method; /* Signature type */ uint8_t auth_reserved[3]; /* Reserved */ /* Followed by the signature */ } __packed; #define IKEV2_AUTH_NONE 0 /* None */ #define IKEV2_AUTH_RSA_SIG 1 /* RFC7296 */ #define IKEV2_AUTH_SHARED_KEY_MIC 2 /* RFC7296 */ #define IKEV2_AUTH_DSS_SIG 3 /* RFC7296 */ #define IKEV2_AUTH_ECDSA_256 9 /* RFC4754 */ #define IKEV2_AUTH_ECDSA_384 10 /* RFC4754 */ #define IKEV2_AUTH_ECDSA_521 11 /* RFC4754 */ #define IKEV2_AUTH_GSPM 12 /* RFC6467 */ #define IKEV2_AUTH_NULL 13 /* RFC7619 */ #define IKEV2_AUTH_SIG 14 /* RFC7427 */ #define IKEV2_AUTH_SIG_ANY 255 /* Internal (any signature) */ /* * AUTH_SIG also serves as an indication that a given policy has * been configured to accept RSA or ECDSA payloads, as long as it * successfully authenticates against a configured CA. */ extern struct iked_constmap ikev2_auth_map[]; /* Notifications used together with IKEV2_AUTH_SIG */ #define IKEV2_SIGHASH_RESERVED 0 /* RFC7427 */ #define IKEV2_SIGHASH_SHA1 1 /* RFC7427 */ #define IKEV2_SIGHASH_SHA2_256 2 /* RFC7427 */ #define IKEV2_SIGHASH_SHA2_384 3 /* RFC7427 */ #define IKEV2_SIGHASH_SHA2_512 4 /* RFC7427 */ extern struct iked_constmap ikev2_sighash_map[]; /* * CP payload */ struct ikev2_cp { uint8_t cp_type; uint8_t cp_reserved[3]; /* Followed by the attributes */ } __packed; #define IKEV2_CP_REQUEST 1 /* CFG-Request */ #define IKEV2_CP_REPLY 2 /* CFG-Reply */ #define IKEV2_CP_SET 3 /* CFG-SET */ #define IKEV2_CP_ACK 4 /* CFG-ACK */ extern struct iked_constmap ikev2_cp_map[]; struct ikev2_cfg { uint16_t cfg_type; /* first bit must be set to zero */ uint16_t cfg_length; /* Followed by variable-length data */ } __packed; #define IKEV2_CFG_INTERNAL_IP4_ADDRESS 1 /* RFC7296 */ #define IKEV2_CFG_INTERNAL_IP4_NETMASK 2 /* RFC7296 */ #define IKEV2_CFG_INTERNAL_IP4_DNS 3 /* RFC7296 */ #define IKEV2_CFG_INTERNAL_IP4_NBNS 4 /* RFC7296 */ #define IKEV2_CFG_INTERNAL_ADDRESS_EXPIRY 5 /* RFC4306 */ #define IKEV2_CFG_INTERNAL_IP4_DHCP 6 /* RFC7296 */ #define IKEV2_CFG_APPLICATION_VERSION 7 /* RFC7296 */ #define IKEV2_CFG_INTERNAL_IP6_ADDRESS 8 /* RFC7296 */ #define IKEV2_CFG_INTERNAL_IP6_DNS 10 /* RFC7296 */ #define IKEV2_CFG_INTERNAL_IP6_NBNS 11 /* RFC4306 */ #define IKEV2_CFG_INTERNAL_IP6_DHCP 12 /* RFC7296 */ #define IKEV2_CFG_INTERNAL_IP4_SUBNET 13 /* RFC7296 */ #define IKEV2_CFG_SUPPORTED_ATTRIBUTES 14 /* RFC7296 */ #define IKEV2_CFG_INTERNAL_IP6_SUBNET 15 /* RFC7296 */ #define IKEV2_CFG_MIP6_HOME_PREFIX 16 /* RFC5026 */ #define IKEV2_CFG_INTERNAL_IP6_LINK 17 /* RFC5739 */ #define IKEV2_CFG_INTERNAL_IP6_PREFIX 18 /* RFC5739 */ #define IKEV2_CFG_HOME_AGENT_ADDRESS 19 /* http://www.3gpp.org/ftp/Specs/html-info/24302.htm */ #define IKEV2_CFG_INTERNAL_IP4_SERVER 23456 /* MS-IKEE */ #define IKEV2_CFG_INTERNAL_IP6_SERVER 23457 /* MS-IKEE */ extern struct iked_constmap ikev2_cfg_map[]; /* IKEv1 payload types */ #define IKEV1_PAYLOAD_NONE 0 /* No payload */ #define IKEV1_PAYLOAD_PROPOSAL 2 /* Proposal */ #endif /* IKED_IKEV2_H */ openiked-7.4/iked/ikev2_msg.c000066400000000000000000001045371477554731100161600ustar00rootroot00000000000000/* $OpenBSD: ikev2_msg.c,v 1.103 2024/11/21 13:26:49 claudio Exp $ */ /* * Copyright (c) 2019 Tobias Heider * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" #include "ikev2.h" #include "eap.h" #include "dh.h" void ikev1_recv(struct iked *, struct iked_message *); void ikev2_msg_response_timeout(struct iked *, void *); void ikev2_msg_retransmit_timeout(struct iked *, void *); int ikev2_check_frag_oversize(struct iked_sa *, struct ibuf *); int ikev2_send_encrypted_fragments(struct iked *, struct iked_sa *, struct ibuf *, uint8_t, uint8_t, int); int ikev2_msg_encrypt_prepare(struct iked_sa *, struct ikev2_payload *, struct ibuf*, struct ibuf *, struct ike_header *, uint8_t, int); void ikev2_msg_cb(int fd, short event, void *arg) { struct iked_socket *sock = arg; struct iked *env = sock->sock_env; struct iked_message msg; struct ike_header hdr; uint32_t natt = 0x00000000; uint8_t buf[IKED_MSGBUF_MAX]; ssize_t len; off_t off; bzero(&msg, sizeof(msg)); bzero(buf, sizeof(buf)); msg.msg_peerlen = sizeof(msg.msg_peer); msg.msg_locallen = sizeof(msg.msg_local); msg.msg_parent = &msg; memcpy(&msg.msg_local, &sock->sock_addr, sizeof(sock->sock_addr)); if ((len = recvfromto(fd, buf, sizeof(buf), 0, (struct sockaddr *)&msg.msg_peer, &msg.msg_peerlen, (struct sockaddr *)&msg.msg_local, &msg.msg_locallen)) < (ssize_t)sizeof(natt)) return; if (socket_getport((struct sockaddr *)&msg.msg_local) == env->sc_nattport) { if (memcmp(&natt, buf, sizeof(natt)) != 0) return; msg.msg_natt = 1; off = sizeof(natt); } else off = 0; if ((size_t)(len - off) <= sizeof(hdr)) return; memcpy(&hdr, buf + off, sizeof(hdr)); if ((msg.msg_data = ibuf_new(buf + off, len - off)) == NULL) return; TAILQ_INIT(&msg.msg_proposals); SIMPLEQ_INIT(&msg.msg_certreqs); msg.msg_fd = fd; if (hdr.ike_version == IKEV1_VERSION) ikev1_recv(env, &msg); else ikev2_recv(env, &msg); ikev2_msg_cleanup(env, &msg); } void ikev1_recv(struct iked *env, struct iked_message *msg) { struct ike_header *hdr; if (ibuf_size(msg->msg_data) <= sizeof(*hdr)) { log_debug("%s: short message", __func__); return; } hdr = (struct ike_header *)ibuf_data(msg->msg_data); log_debug("%s: header ispi %s rspi %s" " nextpayload %u version 0x%02x exchange %u flags 0x%02x" " msgid %u length %u", __func__, print_spi(betoh64(hdr->ike_ispi), 8), print_spi(betoh64(hdr->ike_rspi), 8), hdr->ike_nextpayload, hdr->ike_version, hdr->ike_exchange, hdr->ike_flags, betoh32(hdr->ike_msgid), betoh32(hdr->ike_length)); log_debug("%s: IKEv1 not supported", __func__); } struct ibuf * ikev2_msg_init(struct iked *env, struct iked_message *msg, struct sockaddr_storage *peer, socklen_t peerlen, struct sockaddr_storage *local, socklen_t locallen, int response) { bzero(msg, sizeof(*msg)); memcpy(&msg->msg_peer, peer, peerlen); msg->msg_peerlen = peerlen; memcpy(&msg->msg_local, local, locallen); msg->msg_locallen = locallen; msg->msg_response = response ? 1 : 0; msg->msg_fd = -1; msg->msg_data = ibuf_static(); msg->msg_e = 0; msg->msg_parent = msg; /* has to be set */ TAILQ_INIT(&msg->msg_proposals); return (msg->msg_data); } struct iked_message * ikev2_msg_copy(struct iked *env, struct iked_message *msg) { struct iked_message *m = NULL; struct ibuf *buf; size_t len; void *ptr; if (ibuf_size(msg->msg_data) < msg->msg_offset) return (NULL); len = ibuf_size(msg->msg_data) - msg->msg_offset; if ((m = malloc(sizeof(*m))) == NULL) return (NULL); if ((ptr = ibuf_seek(msg->msg_data, msg->msg_offset, len)) == NULL || (buf = ikev2_msg_init(env, m, &msg->msg_peer, msg->msg_peerlen, &msg->msg_local, msg->msg_locallen, msg->msg_response)) == NULL || ibuf_add(buf, ptr, len)) { free(m); return (NULL); } m->msg_fd = msg->msg_fd; m->msg_msgid = msg->msg_msgid; m->msg_offset = msg->msg_offset; m->msg_sa = msg->msg_sa; return (m); } void ikev2_msg_cleanup(struct iked *env, struct iked_message *msg) { struct iked_certreq *cr; int i; if (msg == msg->msg_parent) { ibuf_free(msg->msg_nonce); ibuf_free(msg->msg_ke); ibuf_free(msg->msg_auth.id_buf); ibuf_free(msg->msg_peerid.id_buf); ibuf_free(msg->msg_localid.id_buf); ibuf_free(msg->msg_cert.id_buf); for (i = 0; i < IKED_SCERT_MAX; i++) ibuf_free(msg->msg_scert[i].id_buf); ibuf_free(msg->msg_cookie); ibuf_free(msg->msg_cookie2); ibuf_free(msg->msg_del_buf); free(msg->msg_eap.eam_user); free(msg->msg_cp_addr); free(msg->msg_cp_addr6); free(msg->msg_cp_dns); msg->msg_nonce = NULL; msg->msg_ke = NULL; msg->msg_auth.id_buf = NULL; msg->msg_peerid.id_buf = NULL; msg->msg_localid.id_buf = NULL; msg->msg_cert.id_buf = NULL; for (i = 0; i < IKED_SCERT_MAX; i++) msg->msg_scert[i].id_buf = NULL; msg->msg_cookie = NULL; msg->msg_cookie2 = NULL; msg->msg_del_buf = NULL; msg->msg_eap.eam_user = NULL; msg->msg_cp_addr = NULL; msg->msg_cp_addr6 = NULL; msg->msg_cp_dns = NULL; config_free_proposals(&msg->msg_proposals, 0); while ((cr = SIMPLEQ_FIRST(&msg->msg_certreqs))) { ibuf_free(cr->cr_data); SIMPLEQ_REMOVE_HEAD(&msg->msg_certreqs, cr_entry); free(cr); } } if (msg->msg_data != NULL) { ibuf_free(msg->msg_data); msg->msg_data = NULL; } } int ikev2_msg_valid_ike_sa(struct iked *env, struct ike_header *oldhdr, struct iked_message *msg) { if (msg->msg_sa != NULL && msg->msg_policy != NULL) { if (msg->msg_sa->sa_state == IKEV2_STATE_CLOSED) return (-1); /* * Only permit informational requests from initiator * on closing SAs (for DELETE). */ if (msg->msg_sa->sa_state == IKEV2_STATE_CLOSING) { if (((oldhdr->ike_flags & (IKEV2_FLAG_INITIATOR|IKEV2_FLAG_RESPONSE)) == IKEV2_FLAG_INITIATOR) && (oldhdr->ike_exchange == IKEV2_EXCHANGE_INFORMATIONAL)) return (0); return (-1); } return (0); } /* Always fail */ return (-1); } int ikev2_msg_send(struct iked *env, struct iked_message *msg) { struct iked_sa *sa = msg->msg_sa; struct ibuf *buf = msg->msg_data; uint32_t natt = 0x00000000; int isnatt = 0; uint8_t exchange, flags; struct ike_header *hdr; struct iked_message *m; if (buf == NULL || (hdr = ibuf_seek(msg->msg_data, msg->msg_offset, sizeof(*hdr))) == NULL) return (-1); isnatt = (msg->msg_natt || (sa && sa->sa_natt)); exchange = hdr->ike_exchange; flags = hdr->ike_flags; logit(exchange == IKEV2_EXCHANGE_INFORMATIONAL ? LOG_DEBUG : LOG_INFO, "%ssend %s %s %u peer %s local %s, %zu bytes%s", SPI_IH(hdr), print_map(exchange, ikev2_exchange_map), (flags & IKEV2_FLAG_RESPONSE) ? "res" : "req", betoh32(hdr->ike_msgid), print_addr(&msg->msg_peer), print_addr(&msg->msg_local), ibuf_size(buf), isnatt ? ", NAT-T" : ""); if (isnatt) { struct ibuf *new; if ((new = ibuf_new(&natt, sizeof(natt))) == NULL) { log_debug("%s: failed to set NAT-T", __func__); return (-1); } if (ibuf_add_ibuf(new, buf) == -1) { ibuf_free(new); log_debug("%s: failed to set NAT-T", __func__); return (-1); } ibuf_free(buf); buf = msg->msg_data = new; } if (sendtofrom(msg->msg_fd, ibuf_data(buf), ibuf_size(buf), 0, (struct sockaddr *)&msg->msg_peer, msg->msg_peerlen, (struct sockaddr *)&msg->msg_local, msg->msg_locallen) == -1) { log_warn("%s: sendtofrom", __func__); if (sa != NULL && errno == EADDRNOTAVAIL) { sa_state(env, sa, IKEV2_STATE_CLOSING); timer_del(env, &sa->sa_timer); timer_set(env, &sa->sa_timer, ikev2_ike_sa_timeout, sa); timer_add(env, &sa->sa_timer, IKED_IKE_SA_DELETE_TIMEOUT); } ikestat_inc(env, ikes_msg_send_failures); } else ikestat_inc(env, ikes_msg_sent); if (sa == NULL) return (0); if ((m = ikev2_msg_copy(env, msg)) == NULL) { log_debug("%s: failed to copy a message", __func__); return (-1); } m->msg_exchange = exchange; if (flags & IKEV2_FLAG_RESPONSE) { if (ikev2_msg_enqueue(env, &sa->sa_responses, m, IKED_RESPONSE_TIMEOUT) != 0) { ikev2_msg_cleanup(env, m); free(m); return (-1); } } else { if (ikev2_msg_enqueue(env, &sa->sa_requests, m, IKED_RETRANSMIT_TIMEOUT) != 0) { ikev2_msg_cleanup(env, m); free(m); return (-1); } } return (0); } uint32_t ikev2_msg_id(struct iked *env, struct iked_sa *sa) { uint32_t id = sa->sa_reqid; if (++sa->sa_reqid == UINT32_MAX) { /* XXX we should close and renegotiate the connection now */ log_debug("%s: IKEv2 message sequence overflow", __func__); } return (id); } /* * Calculate the final sizes of the IKEv2 header and the encrypted payload * header. This must be done before encryption to make sure the correct * headers are authenticated. */ int ikev2_msg_encrypt_prepare(struct iked_sa *sa, struct ikev2_payload *pld, struct ibuf *buf, struct ibuf *e, struct ike_header *hdr, uint8_t firstpayload, int fragmentation) { size_t len, ivlen, encrlen, integrlen, blocklen, pldlen, outlen; if (sa == NULL || sa->sa_encr == NULL || sa->sa_integr == NULL) { log_debug("%s: invalid SA", __func__); return (-1); } len = ibuf_size(e); blocklen = cipher_length(sa->sa_encr); integrlen = hash_length(sa->sa_integr); ivlen = cipher_ivlength(sa->sa_encr); encrlen = roundup(len + 1, blocklen); outlen = cipher_outlength(sa->sa_encr, encrlen); pldlen = ivlen + outlen + integrlen; if (ikev2_next_payload(pld, pldlen + (fragmentation ? sizeof(struct ikev2_frag_payload) : 0), firstpayload) == -1) return (-1); if (ikev2_set_header(hdr, ibuf_size(buf) + pldlen - sizeof(*hdr)) == -1) return (-1); return (0); } struct ibuf * ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src, struct ibuf *aad) { size_t len, encrlen, integrlen, blocklen, outlen; uint8_t *buf, pad = 0, *ptr; struct ibuf *encr, *dst = NULL, *out = NULL; buf = ibuf_data(src); len = ibuf_size(src); log_debug("%s: decrypted length %zu", __func__, len); print_hex(buf, 0, len); if (sa == NULL || sa->sa_encr == NULL || sa->sa_integr == NULL) { log_debug("%s: invalid SA", __func__); goto done; } if (sa->sa_hdr.sh_initiator) encr = sa->sa_key_iencr; else encr = sa->sa_key_rencr; blocklen = cipher_length(sa->sa_encr); integrlen = hash_length(sa->sa_integr); encrlen = roundup(len + sizeof(pad), blocklen); pad = encrlen - (len + sizeof(pad)); /* * Pad the payload and encrypt it */ if (pad) { if ((ptr = ibuf_reserve(src, pad)) == NULL) goto done; arc4random_buf(ptr, pad); } if (ibuf_add(src, &pad, sizeof(pad)) != 0) goto done; log_debug("%s: padded length %zu", __func__, ibuf_size(src)); print_hexbuf(src); cipher_setkey(sa->sa_encr, ibuf_data(encr), ibuf_size(encr)); cipher_setiv(sa->sa_encr, NULL, 0); /* XXX ivlen */ if (cipher_init_encrypt(sa->sa_encr) == -1) { log_info("%s: error initiating cipher.", __func__); goto done; } if ((dst = ibuf_dup(sa->sa_encr->encr_iv)) == NULL) goto done; if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr, encrlen))) == NULL) goto done; outlen = ibuf_size(out); /* Add AAD for AEAD ciphers */ if (sa->sa_integr->hash_isaead) cipher_aad(sa->sa_encr, ibuf_data(aad), ibuf_size(aad), &outlen); if (cipher_update(sa->sa_encr, ibuf_data(src), encrlen, ibuf_data(out), &outlen) == -1) { log_info("%s: error updating cipher.", __func__); goto done; } if (cipher_final(sa->sa_encr) == -1) { log_info("%s: encryption failed.", __func__); goto done; } if (outlen && ibuf_add(dst, ibuf_data(out), outlen) != 0) goto done; if ((ptr = ibuf_reserve(dst, integrlen)) == NULL) goto done; explicit_bzero(ptr, integrlen); log_debug("%s: length %zu, padding %d, output length %zu", __func__, len + sizeof(pad), pad, ibuf_size(dst)); print_hexbuf(dst); ibuf_free(src); ibuf_free(out); return (dst); done: ibuf_free(src); ibuf_free(out); ibuf_free(dst); return (NULL); } int ikev2_msg_integr(struct iked *env, struct iked_sa *sa, struct ibuf *src) { int ret = -1; size_t integrlen, tmplen; struct ibuf *integr, *tmp = NULL; uint8_t *ptr; log_debug("%s: message length %zu", __func__, ibuf_size(src)); print_hexbuf(src); if (sa == NULL || sa->sa_encr == NULL || sa->sa_integr == NULL) { log_debug("%s: invalid SA", __func__); return (-1); } integrlen = hash_length(sa->sa_integr); log_debug("%s: integrity checksum length %zu", __func__, integrlen); /* * Validate packet checksum */ if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL) goto done; if (!sa->sa_integr->hash_isaead) { if (sa->sa_hdr.sh_initiator) integr = sa->sa_key_iauth; else integr = sa->sa_key_rauth; hash_setkey(sa->sa_integr, ibuf_data(integr), ibuf_size(integr)); hash_init(sa->sa_integr); hash_update(sa->sa_integr, ibuf_data(src), ibuf_size(src) - integrlen); hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen); if (tmplen != integrlen) { log_debug("%s: hash failure", __func__); goto done; } } else { /* Append AEAD tag */ if (cipher_gettag(sa->sa_encr, ibuf_data(tmp), ibuf_size(tmp))) goto done; } if ((ptr = ibuf_seek(src, ibuf_size(src) - integrlen, integrlen)) == NULL) goto done; memcpy(ptr, ibuf_data(tmp), integrlen); print_hexbuf(tmp); ret = 0; done: ibuf_free(tmp); return (ret); } struct ibuf * ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa, struct ibuf *msg, struct ibuf *src) { ssize_t ivlen, encrlen, integrlen, blocklen, outlen, tmplen; uint8_t pad = 0, *ptr, *integrdata; struct ibuf *integr, *encr, *tmp = NULL, *out = NULL; off_t ivoff, encroff, integroff; if (sa == NULL || sa->sa_encr == NULL || sa->sa_integr == NULL) { log_debug("%s: invalid SA", __func__); print_hexbuf(src); goto done; } if (!sa->sa_hdr.sh_initiator) { encr = sa->sa_key_iencr; integr = sa->sa_key_iauth; } else { encr = sa->sa_key_rencr; integr = sa->sa_key_rauth; } blocklen = cipher_length(sa->sa_encr); ivlen = cipher_ivlength(sa->sa_encr); ivoff = 0; integrlen = hash_length(sa->sa_integr); integroff = ibuf_size(src) - integrlen; encroff = ivlen; encrlen = ibuf_size(src) - integrlen - ivlen; if (encrlen < 0 || integroff < 0) { log_debug("%s: invalid integrity value", __func__); goto done; } log_debug("%s: IV length %zd", __func__, ivlen); print_hex(ibuf_data(src), 0, ivlen); log_debug("%s: encrypted payload length %zd", __func__, encrlen); print_hex(ibuf_data(src), encroff, encrlen); log_debug("%s: integrity checksum length %zd", __func__, integrlen); print_hex(ibuf_data(src), integroff, integrlen); /* * Validate packet checksum */ if (!sa->sa_integr->hash_isaead) { if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL) goto done; hash_setkey(sa->sa_integr, ibuf_data(integr), ibuf_size(integr)); hash_init(sa->sa_integr); hash_update(sa->sa_integr, ibuf_data(msg), ibuf_size(msg) - integrlen); hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen); integrdata = ibuf_seek(src, integroff, integrlen); if (integrdata == NULL) goto done; if (memcmp(ibuf_data(tmp), integrdata, integrlen) != 0) { log_debug("%s: integrity check failed", __func__); goto done; } log_debug("%s: integrity check succeeded", __func__); print_hex(ibuf_data(tmp), 0, tmplen); ibuf_free(tmp); tmp = NULL; } /* * Decrypt the payload and strip any padding */ if ((encrlen % blocklen) != 0) { log_debug("%s: unaligned encrypted payload", __func__); goto done; } cipher_setkey(sa->sa_encr, ibuf_data(encr), ibuf_size(encr)); cipher_setiv(sa->sa_encr, ibuf_seek(src, ivoff, ivlen), ivlen); if (cipher_init_decrypt(sa->sa_encr) == -1) { log_info("%s: error initiating cipher.", __func__); goto done; } /* Set AEAD tag */ if (sa->sa_integr->hash_isaead) { integrdata = ibuf_seek(src, integroff, integrlen); if (integrdata == NULL) goto done; if (cipher_settag(sa->sa_encr, integrdata, integrlen)) { log_info("%s: failed to set tag.", __func__); goto done; } } if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr, encrlen))) == NULL) goto done; /* * Add additional authenticated data for AEAD ciphers */ if (sa->sa_integr->hash_isaead) { log_debug("%s: AAD length %zu", __func__, ibuf_size(msg) - ibuf_size(src)); print_hex(ibuf_data(msg), 0, ibuf_size(msg) - ibuf_size(src)); cipher_aad(sa->sa_encr, ibuf_data(msg), ibuf_size(msg) - ibuf_size(src), &outlen); } if ((outlen = ibuf_size(out)) != 0) { if (cipher_update(sa->sa_encr, ibuf_seek(src, encroff, encrlen), encrlen, ibuf_data(out), &outlen) == -1) { log_info("%s: error updating cipher.", __func__); goto done; } ptr = ibuf_seek(out, outlen - 1, 1); pad = *ptr; } if (cipher_final(sa->sa_encr) == -1) { log_info("%s: decryption failed.", __func__); goto done; } log_debug("%s: decrypted payload length %zd/%zd padding %d", __func__, outlen, encrlen, pad); print_hexbuf(out); /* Strip padding and padding length */ if (ibuf_setsize(out, outlen - pad - 1) != 0) goto done; ibuf_free(src); return (out); done: ibuf_free(tmp); ibuf_free(out); ibuf_free(src); return (NULL); } int ikev2_check_frag_oversize(struct iked_sa *sa, struct ibuf *buf) { size_t len = ibuf_length(buf); sa_family_t sa_fam; size_t max; size_t ivlen, integrlen, blocklen; if (sa == NULL || sa->sa_encr == NULL || sa->sa_integr == NULL) { log_debug("%s: invalid SA", __func__); return (-1); } sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family; max = sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG : IKEV2_MAXLEN_IPV6_FRAG; blocklen = cipher_length(sa->sa_encr); ivlen = cipher_ivlength(sa->sa_encr); integrlen = hash_length(sa->sa_integr); /* Estimated maximum packet size (with 0 < padding < blocklen) */ return ((len + ivlen + blocklen + integrlen) >= max) && sa->sa_frag; } int ikev2_msg_send_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf **ep, uint8_t exchange, uint8_t firstpayload, int response) { struct iked_message resp; struct ike_header *hdr; struct ikev2_payload *pld; struct ibuf *buf, *e = *ep; int ret = -1; /* Check if msg needs to be fragmented */ if (ikev2_check_frag_oversize(sa, e)) { return ikev2_send_encrypted_fragments(env, sa, e, exchange, firstpayload, response); } if ((buf = ikev2_msg_init(env, &resp, &sa->sa_peer.addr, SS_LEN(sa->sa_peer.addr), &sa->sa_local.addr, SS_LEN(sa->sa_local.addr), response)) == NULL) goto done; resp.msg_msgid = response ? sa->sa_msgid_current : ikev2_msg_id(env, sa); /* IKE header */ if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid, IKEV2_PAYLOAD_SK, exchange, response ? IKEV2_FLAG_RESPONSE : 0)) == NULL) goto done; if ((pld = ikev2_add_payload(buf)) == NULL) goto done; if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr, firstpayload, 0) == -1) goto done; /* Encrypt message and add as an E payload */ if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) { log_debug("%s: encryption failed", __func__); goto done; } if (ibuf_add_ibuf(buf, e) != 0) goto done; /* Add integrity checksum (HMAC) */ if (ikev2_msg_integr(env, sa, buf) != 0) { log_debug("%s: integrity checksum failed", __func__); goto done; } resp.msg_data = buf; resp.msg_sa = sa; resp.msg_fd = sa->sa_fd; TAILQ_INIT(&resp.msg_proposals); (void)ikev2_pld_parse(env, hdr, &resp, 0); ret = ikev2_msg_send(env, &resp); done: /* e is cleaned up by the calling function */ *ep = e; ikev2_msg_cleanup(env, &resp); return (ret); } int ikev2_send_encrypted_fragments(struct iked *env, struct iked_sa *sa, struct ibuf *in, uint8_t exchange, uint8_t firstpayload, int response) { struct iked_message resp; struct ibuf *buf, *e = NULL; struct ike_header *hdr; struct ikev2_payload *pld; struct ikev2_frag_payload *frag; sa_family_t sa_fam; size_t ivlen, integrlen, blocklen; size_t max_len, left, offset=0; size_t frag_num = 1, frag_total; uint8_t *data; uint32_t msgid; int ret = -1; if (sa == NULL || sa->sa_encr == NULL || sa->sa_integr == NULL) { log_debug("%s: invalid SA", __func__); ikestat_inc(env, ikes_frag_send_failures); return ret; } sa_fam = ((struct sockaddr *)&sa->sa_local.addr)->sa_family; left = ibuf_length(in); /* Calculate max allowed size of a fragments payload */ blocklen = cipher_length(sa->sa_encr); ivlen = cipher_ivlength(sa->sa_encr); integrlen = hash_length(sa->sa_integr); max_len = (sa_fam == AF_INET ? IKEV2_MAXLEN_IPV4_FRAG : IKEV2_MAXLEN_IPV6_FRAG) - ivlen - blocklen - integrlen; /* Total number of fragments to send */ frag_total = (left / max_len) + 1; msgid = response ? sa->sa_msgid_current : ikev2_msg_id(env, sa); while (frag_num <= frag_total) { if ((buf = ikev2_msg_init(env, &resp, &sa->sa_peer.addr, SS_LEN(sa->sa_peer.addr), &sa->sa_local.addr, SS_LEN(sa->sa_local.addr), response)) == NULL) goto done; resp.msg_msgid = msgid; /* IKE header */ if ((hdr = ikev2_add_header(buf, sa, resp.msg_msgid, IKEV2_PAYLOAD_SKF, exchange, response ? IKEV2_FLAG_RESPONSE : 0)) == NULL) goto done; /* Payload header */ if ((pld = ikev2_add_payload(buf)) == NULL) goto done; /* Fragment header */ if ((frag = ibuf_reserve(buf, sizeof(*frag))) == NULL) { log_debug("%s: failed to add SKF fragment header", __func__); goto done; } frag->frag_num = htobe16(frag_num); frag->frag_total = htobe16(frag_total); /* Encrypt message and add as an E payload */ data = ibuf_seek(in, offset, 0); if ((e = ibuf_new(data, MINIMUM(left, max_len))) == NULL) { goto done; } if (ikev2_msg_encrypt_prepare(sa, pld, buf, e, hdr, firstpayload, 1) == -1) goto done; if ((e = ikev2_msg_encrypt(env, sa, e, buf)) == NULL) { log_debug("%s: encryption failed", __func__); goto done; } if (ibuf_add_ibuf(buf, e) != 0) goto done; /* Add integrity checksum (HMAC) */ if (ikev2_msg_integr(env, sa, buf) != 0) { log_debug("%s: integrity checksum failed", __func__); goto done; } log_debug("%s: Fragment %zu of %zu has size of %zu bytes.", __func__, frag_num, frag_total, ibuf_size(buf) - sizeof(*hdr)); print_hexbuf(buf); resp.msg_data = buf; resp.msg_sa = sa; resp.msg_fd = sa->sa_fd; TAILQ_INIT(&resp.msg_proposals); if (ikev2_msg_send(env, &resp) == -1) goto done; ikestat_inc(env, ikes_frag_sent); offset += MINIMUM(left, max_len); left -= MINIMUM(left, max_len); frag_num++; /* MUST be zero after first fragment */ firstpayload = 0; ikev2_msg_cleanup(env, &resp); ibuf_free(e); e = NULL; } return 0; done: ikev2_msg_cleanup(env, &resp); ibuf_free(e); ikestat_inc(env, ikes_frag_send_failures); return ret; } struct ibuf * ikev2_msg_auth(struct iked *env, struct iked_sa *sa, int response) { struct ibuf *authmsg = NULL, *nonce, *prfkey, *buf; uint8_t *ptr; struct iked_id *id; size_t tmplen; /* * Create the payload to be signed/MAC'ed for AUTH */ if (!response) { if ((nonce = sa->sa_rnonce) == NULL || (sa->sa_iid.id_type == 0) || (prfkey = sa->sa_key_iprf) == NULL || (buf = sa->sa_1stmsg) == NULL) return (NULL); id = &sa->sa_iid; } else { if ((nonce = sa->sa_inonce) == NULL || (sa->sa_rid.id_type == 0) || (prfkey = sa->sa_key_rprf) == NULL || (buf = sa->sa_2ndmsg) == NULL) return (NULL); id = &sa->sa_rid; } if ((authmsg = ibuf_dup(buf)) == NULL) return (NULL); if (ibuf_add_ibuf(authmsg, nonce) != 0) goto fail; if ((hash_setkey(sa->sa_prf, ibuf_data(prfkey), ibuf_size(prfkey))) == NULL) goto fail; /* require non-truncating hash */ if (hash_keylength(sa->sa_prf) != hash_length(sa->sa_prf)) goto fail; if ((ptr = ibuf_reserve(authmsg, hash_keylength(sa->sa_prf))) == NULL) goto fail; hash_init(sa->sa_prf); hash_update(sa->sa_prf, ibuf_data(id->id_buf), ibuf_size(id->id_buf)); hash_final(sa->sa_prf, ptr, &tmplen); if (tmplen != hash_length(sa->sa_prf)) goto fail; log_debug("%s: %s auth data length %zu", __func__, response ? "responder" : "initiator", ibuf_size(authmsg)); print_hexbuf(authmsg); return (authmsg); fail: ibuf_free(authmsg); return (NULL); } int ikev2_msg_authverify(struct iked *env, struct iked_sa *sa, struct iked_auth *auth, uint8_t *buf, size_t len, struct ibuf *authmsg) { uint8_t *key, *psk = NULL; ssize_t keylen; struct iked_id *id; struct iked_dsa *dsa = NULL; int ret = -1; uint8_t keytype; if (sa->sa_hdr.sh_initiator) id = &sa->sa_rcert; else id = &sa->sa_icert; if ((dsa = dsa_verify_new(auth->auth_method, sa->sa_prf)) == NULL) { log_debug("%s: invalid auth method", __func__); return (-1); } switch (auth->auth_method) { case IKEV2_AUTH_SHARED_KEY_MIC: if (!auth->auth_length) { log_debug("%s: no pre-shared key found", __func__); goto done; } if ((keylen = ikev2_psk(sa, auth->auth_data, auth->auth_length, &psk)) == -1) { log_debug("%s: failed to get PSK", __func__); goto done; } key = psk; keytype = 0; break; default: if (!id->id_type || !ibuf_length(id->id_buf)) { log_debug("%s: no cert found", __func__); goto done; } key = ibuf_data(id->id_buf); keylen = ibuf_size(id->id_buf); keytype = id->id_type; break; } log_debug("%s: method %s keylen %zd type %s", __func__, print_map(auth->auth_method, ikev2_auth_map), keylen, print_map(id->id_type, ikev2_cert_map)); if (dsa_setkey(dsa, key, keylen, keytype) == NULL || dsa_init(dsa, buf, len) != 0 || dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) { log_debug("%s: failed to compute digital signature", __func__); goto done; } if ((ret = dsa_verify_final(dsa, buf, len)) == 0) { log_debug("%s: authentication successful", __func__); sa_state(env, sa, IKEV2_STATE_AUTH_SUCCESS); sa_stateflags(sa, IKED_REQ_AUTHVALID); } else { log_debug("%s: authentication failed", __func__); sa_state(env, sa, IKEV2_STATE_AUTH_REQUEST); } done: free(psk); dsa_free(dsa); return (ret); } int ikev2_msg_authsign(struct iked *env, struct iked_sa *sa, struct iked_auth *auth, struct ibuf *authmsg) { uint8_t *key, *psk = NULL; ssize_t keylen, siglen; struct iked_hash *prf = sa->sa_prf; struct iked_id *id; struct iked_dsa *dsa = NULL; struct ibuf *buf; int ret = -1; uint8_t keytype; if (sa->sa_hdr.sh_initiator) id = &sa->sa_icert; else id = &sa->sa_rcert; if ((dsa = dsa_sign_new(auth->auth_method, prf)) == NULL) { log_debug("%s: invalid auth method", __func__); return (-1); } switch (auth->auth_method) { case IKEV2_AUTH_SHARED_KEY_MIC: if (!auth->auth_length) { log_debug("%s: no pre-shared key found", __func__); goto done; } if ((keylen = ikev2_psk(sa, auth->auth_data, auth->auth_length, &psk)) == -1) { log_debug("%s: failed to get PSK", __func__); goto done; } key = psk; keytype = 0; break; default: if (id == NULL) { log_debug("%s: no cert found", __func__); goto done; } key = ibuf_data(id->id_buf); keylen = ibuf_size(id->id_buf); keytype = id->id_type; break; } if (dsa_setkey(dsa, key, keylen, keytype) == NULL || dsa_init(dsa, NULL, 0) != 0 || dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) { log_debug("%s: failed to compute digital signature", __func__); goto done; } ibuf_free(sa->sa_localauth.id_buf); sa->sa_localauth.id_buf = NULL; if ((buf = ibuf_new(NULL, dsa_length(dsa))) == NULL) { log_debug("%s: failed to get auth buffer", __func__); goto done; } if ((siglen = dsa_sign_final(dsa, ibuf_data(buf), ibuf_size(buf))) < 0) { log_debug("%s: failed to create auth signature", __func__); ibuf_free(buf); goto done; } if (ibuf_setsize(buf, siglen) < 0) { log_debug("%s: failed to set auth signature size to %zd", __func__, siglen); ibuf_free(buf); goto done; } sa->sa_localauth.id_type = auth->auth_method; sa->sa_localauth.id_buf = buf; ret = 0; done: free(psk); dsa_free(dsa); return (ret); } int ikev2_msg_frompeer(struct iked_message *msg) { struct iked_sa *sa = msg->msg_sa; struct ike_header *hdr; msg = msg->msg_parent; if (sa == NULL || (hdr = ibuf_seek(msg->msg_data, 0, sizeof(*hdr))) == NULL) return (0); if (!sa->sa_hdr.sh_initiator && (hdr->ike_flags & IKEV2_FLAG_INITIATOR)) return (1); else if (sa->sa_hdr.sh_initiator && (hdr->ike_flags & IKEV2_FLAG_INITIATOR) == 0) return (1); return (0); } struct iked_socket * ikev2_msg_getsocket(struct iked *env, int af, int natt) { switch (af) { case AF_INET: return (env->sc_sock4[natt ? 1 : 0]); case AF_INET6: return (env->sc_sock6[natt ? 1 : 0]); } log_debug("%s: af socket %d not available", __func__, af); return (NULL); } int ikev2_msg_enqueue(struct iked *env, struct iked_msgqueue *queue, struct iked_message *msg, int timeout) { struct iked_msg_retransmit *mr; if ((mr = ikev2_msg_lookup(env, queue, msg, msg->msg_exchange)) == NULL) { if ((mr = calloc(1, sizeof(*mr))) == NULL) return (-1); TAILQ_INIT(&mr->mrt_frags); mr->mrt_tries = 0; timer_set(env, &mr->mrt_timer, msg->msg_response ? ikev2_msg_response_timeout : ikev2_msg_retransmit_timeout, mr); timer_add(env, &mr->mrt_timer, timeout); TAILQ_INSERT_TAIL(queue, mr, mrt_entry); } TAILQ_INSERT_TAIL(&mr->mrt_frags, msg, msg_entry); return 0; } void ikev2_msg_prevail(struct iked *env, struct iked_msgqueue *queue, struct iked_message *msg) { struct iked_msg_retransmit *mr, *mrtmp; TAILQ_FOREACH_SAFE(mr, queue, mrt_entry, mrtmp) { if (TAILQ_FIRST(&mr->mrt_frags)->msg_msgid < msg->msg_msgid) ikev2_msg_dispose(env, queue, mr); } } void ikev2_msg_dispose(struct iked *env, struct iked_msgqueue *queue, struct iked_msg_retransmit *mr) { struct iked_message *m; while ((m = TAILQ_FIRST(&mr->mrt_frags)) != NULL) { TAILQ_REMOVE(&mr->mrt_frags, m, msg_entry); ikev2_msg_cleanup(env, m); free(m); } timer_del(env, &mr->mrt_timer); TAILQ_REMOVE(queue, mr, mrt_entry); free(mr); } void ikev2_msg_flushqueue(struct iked *env, struct iked_msgqueue *queue) { struct iked_msg_retransmit *mr = NULL; while ((mr = TAILQ_FIRST(queue)) != NULL) ikev2_msg_dispose(env, queue, mr); } struct iked_msg_retransmit * ikev2_msg_lookup(struct iked *env, struct iked_msgqueue *queue, struct iked_message *msg, uint8_t exchange) { struct iked_msg_retransmit *mr = NULL; TAILQ_FOREACH(mr, queue, mrt_entry) { if (TAILQ_FIRST(&mr->mrt_frags)->msg_msgid == msg->msg_msgid && TAILQ_FIRST(&mr->mrt_frags)->msg_exchange == exchange) break; } return (mr); } int ikev2_msg_retransmit_response(struct iked *env, struct iked_sa *sa, struct iked_message *msg, struct ike_header *hdr) { struct iked_msg_retransmit *mr = NULL; struct iked_message *m = NULL; if ((mr = ikev2_msg_lookup(env, &sa->sa_responses, msg, hdr->ike_exchange)) == NULL) return (-2); /* not found */ if (hdr->ike_nextpayload == IKEV2_PAYLOAD_SKF) { /* only retransmit for fragment number one */ if (ikev2_pld_parse_quick(env, hdr, msg, msg->msg_offset) != 0 || msg->msg_frag_num != 1) { log_debug("%s: ignoring fragment", SPI_SA(sa, __func__)); return (0); } log_debug("%s: first fragment", SPI_SA(sa, __func__)); } TAILQ_FOREACH(m, &mr->mrt_frags, msg_entry) { if (sendtofrom(m->msg_fd, ibuf_data(m->msg_data), ibuf_size(m->msg_data), 0, (struct sockaddr *)&m->msg_peer, m->msg_peerlen, (struct sockaddr *)&m->msg_local, m->msg_locallen) == -1) { log_warn("%s: sendtofrom", __func__); ikestat_inc(env, ikes_msg_send_failures); return (-1); } log_info("%sretransmit %s res %u local %s peer %s", SPI_SA(sa, NULL), print_map(hdr->ike_exchange, ikev2_exchange_map), m->msg_msgid, print_addr(&m->msg_local), print_addr(&m->msg_peer)); } timer_add(env, &mr->mrt_timer, IKED_RESPONSE_TIMEOUT); ikestat_inc(env, ikes_retransmit_response); return (0); } void ikev2_msg_response_timeout(struct iked *env, void *arg) { struct iked_msg_retransmit *mr = arg; struct iked_sa *sa; sa = TAILQ_FIRST(&mr->mrt_frags)->msg_sa; ikev2_msg_dispose(env, &sa->sa_responses, mr); } void ikev2_msg_retransmit_timeout(struct iked *env, void *arg) { struct iked_msg_retransmit *mr = arg; struct iked_message *msg = TAILQ_FIRST(&mr->mrt_frags); struct iked_sa *sa = msg->msg_sa; if (mr->mrt_tries < IKED_RETRANSMIT_TRIES) { TAILQ_FOREACH(msg, &mr->mrt_frags, msg_entry) { if (sendtofrom(msg->msg_fd, ibuf_data(msg->msg_data), ibuf_size(msg->msg_data), 0, (struct sockaddr *)&msg->msg_peer, msg->msg_peerlen, (struct sockaddr *)&msg->msg_local, msg->msg_locallen) == -1) { log_warn("%s: sendtofrom", __func__); ikev2_ike_sa_setreason(sa, "retransmit failed"); sa_free(env, sa); ikestat_inc(env, ikes_msg_send_failures); return; } log_info("%sretransmit %d %s req %u peer %s " "local %s", SPI_SA(sa, NULL), mr->mrt_tries + 1, print_map(msg->msg_exchange, ikev2_exchange_map), msg->msg_msgid, print_addr(&msg->msg_peer), print_addr(&msg->msg_local)); } /* Exponential timeout */ timer_add(env, &mr->mrt_timer, IKED_RETRANSMIT_TIMEOUT * (2 << (mr->mrt_tries++))); ikestat_inc(env, ikes_retransmit_request); } else { log_debug("%s: retransmit limit reached for req %u", __func__, msg->msg_msgid); ikev2_ike_sa_setreason(sa, "retransmit limit reached"); ikestat_inc(env, ikes_retransmit_limit); sa_free(env, sa); } } openiked-7.4/iked/ikev2_pld.c000066400000000000000000001632501477554731100161460ustar00rootroot00000000000000/* $OpenBSD: ikev2_pld.c,v 1.135 2024/04/02 19:58:28 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider * Copyright (c) 2010-2013 Reyk Floeter * Copyright (c) 2014 Hans-Joerg Hoexer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" #include "ikev2.h" #include "eap.h" #include "dh.h" int ikev2_validate_pld(struct iked_message *, size_t, size_t, struct ikev2_payload *); int ikev2_pld_payloads(struct iked *, struct iked_message *, size_t, size_t, unsigned int); int ikev2_validate_sa(struct iked_message *, size_t, size_t, struct ikev2_sa_proposal *); int ikev2_pld_sa(struct iked *, struct ikev2_payload *, struct iked_message *, size_t, size_t); int ikev2_validate_xform(struct iked_message *, size_t, size_t, struct ikev2_transform *); int ikev2_pld_xform(struct iked *, struct iked_message *, size_t, size_t); int ikev2_validate_attr(struct iked_message *, size_t, size_t, struct ikev2_attribute *); int ikev2_pld_attr(struct iked *, struct ikev2_transform *, struct iked_message *, size_t, size_t); int ikev2_validate_ke(struct iked_message *, size_t, size_t, struct ikev2_keyexchange *); int ikev2_pld_ke(struct iked *, struct ikev2_payload *, struct iked_message *, size_t, size_t); int ikev2_validate_id(struct iked_message *, size_t, size_t, struct ikev2_id *); int ikev2_pld_id(struct iked *, struct ikev2_payload *, struct iked_message *, size_t, size_t, unsigned int); int ikev2_validate_cert(struct iked_message *, size_t, size_t, struct ikev2_cert *); int ikev2_pld_cert(struct iked *, struct ikev2_payload *, struct iked_message *, size_t, size_t); int ikev2_validate_certreq(struct iked_message *, size_t, size_t, struct ikev2_cert *); int ikev2_pld_certreq(struct iked *, struct ikev2_payload *, struct iked_message *, size_t, size_t); int ikev2_pld_nonce(struct iked *, struct ikev2_payload *, struct iked_message *, size_t, size_t); int ikev2_validate_notify(struct iked_message *, size_t, size_t, struct ikev2_notify *); int ikev2_pld_notify(struct iked *, struct ikev2_payload *, struct iked_message *, size_t, size_t); int ikev2_validate_delete(struct iked_message *, size_t, size_t, struct ikev2_delete *); int ikev2_pld_delete(struct iked *, struct ikev2_payload *, struct iked_message *, size_t, size_t); int ikev2_validate_tss(struct iked_message *, size_t, size_t, struct ikev2_tsp *); int ikev2_pld_tss(struct iked *, struct ikev2_payload *, struct iked_message *, size_t, size_t); int ikev2_validate_ts(struct iked_message *, size_t, size_t, struct ikev2_ts *); int ikev2_pld_ts(struct iked *, struct ikev2_payload *, struct iked_message *, size_t, size_t, unsigned int); int ikev2_validate_auth(struct iked_message *, size_t, size_t, struct ikev2_auth *); int ikev2_pld_auth(struct iked *, struct ikev2_payload *, struct iked_message *, size_t, size_t); int ikev2_pld_e(struct iked *, struct ikev2_payload *, struct iked_message *, size_t, size_t); int ikev2_pld_ef(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left); int ikev2_frags_reassemble(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg); int ikev2_validate_cp(struct iked_message *, size_t, size_t, struct ikev2_cp *); int ikev2_pld_cp(struct iked *, struct ikev2_payload *, struct iked_message *, size_t, size_t); int ikev2_validate_eap(struct iked_message *, size_t, size_t, struct eap_header *); int ikev2_pld_eap(struct iked *, struct ikev2_payload *, struct iked_message *, size_t, size_t); int ikev2_pld_parse(struct iked *env, struct ike_header *hdr, struct iked_message *msg, size_t offset) { log_debug("%s: header ispi %s rspi %s" " nextpayload %s version 0x%02x exchange %s flags 0x%02x" " msgid %d length %u response %d", __func__, print_spi(betoh64(hdr->ike_ispi), 8), print_spi(betoh64(hdr->ike_rspi), 8), print_map(hdr->ike_nextpayload, ikev2_payload_map), hdr->ike_version, print_map(hdr->ike_exchange, ikev2_exchange_map), hdr->ike_flags, betoh32(hdr->ike_msgid), betoh32(hdr->ike_length), msg->msg_response); if (ibuf_size(msg->msg_data) < betoh32(hdr->ike_length)) { log_debug("%s: short message", __func__); return (-1); } offset += sizeof(*hdr); return (ikev2_pld_payloads(env, msg, offset, betoh32(hdr->ike_length), hdr->ike_nextpayload)); } int ikev2_validate_pld(struct iked_message *msg, size_t offset, size_t left, struct ikev2_payload *pld) { uint8_t *msgbuf = ibuf_data(msg->msg_data); size_t pld_length; /* We need at least the generic header. */ if (left < sizeof(*pld)) { log_debug("%s: malformed payload: too short for generic " "header (%zu < %zu)", __func__, left, sizeof(*pld)); return (-1); } memcpy(pld, msgbuf + offset, sizeof(*pld)); /* * We need at least the specified number of bytes. * pld_length is the full size of the payload including * the generic payload header. */ pld_length = betoh16(pld->pld_length); if (left < pld_length) { log_debug("%s: malformed payload: shorter than specified " "(%zu < %zu)", __func__, left, pld_length); return (-1); } /* * Sanity check the specified payload size, it must * be at least the size of the generic payload header. */ if (pld_length < sizeof(*pld)) { log_debug("%s: malformed payload: shorter than minimum " "header size (%zu < %zu)", __func__, pld_length, sizeof(*pld)); return (-1); } return (0); } int ikev2_pld_payloads(struct iked *env, struct iked_message *msg, size_t offset, size_t length, unsigned int payload) { struct ikev2_payload pld; unsigned int e; int ret; uint8_t *msgbuf = ibuf_data(msg->msg_data); size_t total, left; /* Check if message was decrypted in an E payload */ e = msg->msg_e ? IKED_E : 0; /* Bytes left in datagram. */ total = length - offset; while (payload != 0 && offset < length) { if (ikev2_validate_pld(msg, offset, total, &pld)) return (-1); log_debug("%s: %spayload %s" " nextpayload %s critical 0x%02x length %d", __func__, e ? "decrypted " : "", print_map(payload, ikev2_payload_map), print_map(pld.pld_nextpayload, ikev2_payload_map), pld.pld_reserved & IKEV2_CRITICAL_PAYLOAD, betoh16(pld.pld_length)); /* Skip over generic payload header. */ offset += sizeof(pld); total -= sizeof(pld); left = betoh16(pld.pld_length) - sizeof(pld); ret = 0; switch (payload | e) { case IKEV2_PAYLOAD_SA: case IKEV2_PAYLOAD_SA | IKED_E: ret = ikev2_pld_sa(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_KE: case IKEV2_PAYLOAD_KE | IKED_E: ret = ikev2_pld_ke(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_IDi | IKED_E: case IKEV2_PAYLOAD_IDr | IKED_E: ret = ikev2_pld_id(env, &pld, msg, offset, left, payload); break; case IKEV2_PAYLOAD_CERT | IKED_E: ret = ikev2_pld_cert(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_CERTREQ: case IKEV2_PAYLOAD_CERTREQ | IKED_E: ret = ikev2_pld_certreq(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_AUTH | IKED_E: ret = ikev2_pld_auth(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_NONCE: case IKEV2_PAYLOAD_NONCE | IKED_E: ret = ikev2_pld_nonce(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_NOTIFY: case IKEV2_PAYLOAD_NOTIFY | IKED_E: ret = ikev2_pld_notify(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_DELETE | IKED_E: ret = ikev2_pld_delete(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_TSi | IKED_E: case IKEV2_PAYLOAD_TSr | IKED_E: ret = ikev2_pld_tss(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_SK: ret = ikev2_pld_e(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_SKF: ret = ikev2_pld_ef(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_CP | IKED_E: ret = ikev2_pld_cp(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_EAP | IKED_E: ret = ikev2_pld_eap(env, &pld, msg, offset, left); break; default: print_hex(msgbuf, offset, betoh16(pld.pld_length) - sizeof(pld)); break; } if (ret != 0 && ikev2_msg_frompeer(msg)) { (void)ikev2_send_informational(env, msg); return (-1); } /* Encrypted payloads must appear last */ if ((payload == IKEV2_PAYLOAD_SK) || (payload == IKEV2_PAYLOAD_SKF)) return (0); payload = pld.pld_nextpayload; offset += left; total -= left; } return (0); } int ikev2_validate_sa(struct iked_message *msg, size_t offset, size_t left, struct ikev2_sa_proposal *sap) { uint8_t *msgbuf = ibuf_data(msg->msg_data); size_t sap_length; if (left < sizeof(*sap)) { log_debug("%s: malformed payload: too short for header " "(%zu < %zu)", __func__, left, sizeof(*sap)); return (-1); } memcpy(sap, msgbuf + offset, sizeof(*sap)); sap_length = betoh16(sap->sap_length); if (sap_length < sizeof(*sap)) { log_debug("%s: malformed payload: shorter than minimum header " "size (%zu < %zu)", __func__, sap_length, sizeof(*sap)); return (-1); } if (left < sap_length) { log_debug("%s: malformed payload: too long for actual payload " "size (%zu < %zu)", __func__, left, sap_length); return (-1); } /* * If there is only one proposal, sap_length must be the * total payload size. */ if (!sap->sap_more && left != sap_length) { log_debug("%s: malformed payload: SA payload length mismatches " "single proposal substructure length (%zu != %zu)", __func__, left, sap_length); return (-1); } /* * If there are more than one proposal, there must be bytes * left in the payload. */ if (sap->sap_more && left <= sap_length) { log_debug("%s: malformed payload: SA payload too small for " "further proposals (%zu <= %zu)", __func__, left, sap_length); return (-1); } return (0); } int ikev2_pld_sa(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct ikev2_sa_proposal sap; struct iked_proposal *prop = NULL; uint32_t spi32; uint64_t spi = 0, spi64; uint8_t *msgbuf = ibuf_data(msg->msg_data); int r; struct iked_proposals *props; size_t total; do { if (ikev2_validate_sa(msg, offset, left, &sap)) return (-1); /* Assumed size of the first proposals, including SPI if present. */ total = (betoh16(sap.sap_length) - sizeof(sap)); props = &msg->msg_parent->msg_proposals; offset += sizeof(sap); left -= sizeof(sap); if (sap.sap_spisize) { if (left < sap.sap_spisize) { log_debug("%s: malformed payload: SPI larger than " "actual payload (%zu < %d)", __func__, left, sap.sap_spisize); return (-1); } if (total < sap.sap_spisize) { log_debug("%s: malformed payload: SPI larger than " "proposal (%zu < %d)", __func__, total, sap.sap_spisize); return (-1); } switch (sap.sap_spisize) { case 4: memcpy(&spi32, msgbuf + offset, 4); spi = betoh32(spi32); break; case 8: memcpy(&spi64, msgbuf + offset, 8); spi = betoh64(spi64); break; default: log_debug("%s: unsupported SPI size %d", __func__, sap.sap_spisize); return (-1); } offset += sap.sap_spisize; left -= sap.sap_spisize; /* Assumed size of the proposal, now without SPI. */ total -= sap.sap_spisize; } /* * As we verified sanity of packet headers, this check will * be always false, but just to be sure we keep it. */ if (left < total) { log_debug("%s: malformed payload: too long for payload " "(%zu < %zu)", __func__, left, total); return (-1); } log_debug("%s: more %d reserved %d length %d" " proposal #%d protoid %s spisize %d xforms %d spi %s", __func__, sap.sap_more, sap.sap_reserved, betoh16(sap.sap_length), sap.sap_proposalnr, print_map(sap.sap_protoid, ikev2_saproto_map), sap.sap_spisize, sap.sap_transforms, print_spi(spi, sap.sap_spisize)); if (ikev2_msg_frompeer(msg)) { if ((msg->msg_parent->msg_prop = config_add_proposal(props, sap.sap_proposalnr, sap.sap_protoid)) == NULL) { log_debug("%s: invalid proposal", __func__); return (-1); } prop = msg->msg_parent->msg_prop; prop->prop_peerspi.spi = spi; prop->prop_peerspi.spi_protoid = sap.sap_protoid; prop->prop_peerspi.spi_size = sap.sap_spisize; prop->prop_localspi.spi_protoid = sap.sap_protoid; prop->prop_localspi.spi_size = sap.sap_spisize; } /* * Parse the attached transforms */ if (sap.sap_transforms) { r = ikev2_pld_xform(env, msg, offset, total); if ((r == -2) && ikev2_msg_frompeer(msg)) { log_debug("%s: invalid proposal transform", __func__); /* cleanup and ignore proposal */ config_free_proposal(props, prop); prop = msg->msg_parent->msg_prop = NULL; } else if (r != 0) { log_debug("%s: invalid proposal transforms", __func__); return (-1); } } offset += total; left -= total; } while (sap.sap_more); return (0); } int ikev2_validate_xform(struct iked_message *msg, size_t offset, size_t total, struct ikev2_transform *xfrm) { uint8_t *msgbuf = ibuf_data(msg->msg_data); size_t xfrm_length; if (total < sizeof(*xfrm)) { log_debug("%s: malformed payload: too short for header " "(%zu < %zu)", __func__, total, sizeof(*xfrm)); return (-1); } memcpy(xfrm, msgbuf + offset, sizeof(*xfrm)); xfrm_length = betoh16(xfrm->xfrm_length); if (xfrm_length < sizeof(*xfrm)) { log_debug("%s: malformed payload: shorter than minimum header " "size (%zu < %zu)", __func__, xfrm_length, sizeof(*xfrm)); return (-1); } if (total < xfrm_length) { log_debug("%s: malformed payload: too long for payload size " "(%zu < %zu)", __func__, total, xfrm_length); return (-1); } return (0); } int ikev2_pld_xform(struct iked *env, struct iked_message *msg, size_t offset, size_t total) { struct ikev2_transform xfrm; char id[BUFSIZ]; int ret = 0; int r; size_t xfrm_length; if (ikev2_validate_xform(msg, offset, total, &xfrm)) return (-1); xfrm_length = betoh16(xfrm.xfrm_length); switch (xfrm.xfrm_type) { case IKEV2_XFORMTYPE_ENCR: strlcpy(id, print_map(betoh16(xfrm.xfrm_id), ikev2_xformencr_map), sizeof(id)); break; case IKEV2_XFORMTYPE_PRF: strlcpy(id, print_map(betoh16(xfrm.xfrm_id), ikev2_xformprf_map), sizeof(id)); break; case IKEV2_XFORMTYPE_INTEGR: strlcpy(id, print_map(betoh16(xfrm.xfrm_id), ikev2_xformauth_map), sizeof(id)); break; case IKEV2_XFORMTYPE_DH: strlcpy(id, print_map(betoh16(xfrm.xfrm_id), ikev2_xformdh_map), sizeof(id)); break; case IKEV2_XFORMTYPE_ESN: strlcpy(id, print_map(betoh16(xfrm.xfrm_id), ikev2_xformesn_map), sizeof(id)); break; default: snprintf(id, sizeof(id), "<%d>", betoh16(xfrm.xfrm_id)); break; } log_debug("%s: more %d reserved %d length %zu" " type %s id %s", __func__, xfrm.xfrm_more, xfrm.xfrm_reserved, xfrm_length, print_map(xfrm.xfrm_type, ikev2_xformtype_map), id); /* * Parse transform attributes, if available */ msg->msg_attrlength = 0; if (xfrm_length > sizeof(xfrm)) { if (ikev2_pld_attr(env, &xfrm, msg, offset + sizeof(xfrm), xfrm_length - sizeof(xfrm)) != 0) { return (-1); } } if (ikev2_msg_frompeer(msg)) { r = config_add_transform(msg->msg_parent->msg_prop, xfrm.xfrm_type, betoh16(xfrm.xfrm_id), msg->msg_attrlength, msg->msg_attrlength); if (r == -1) { log_debug("%s: failed to add transform: alloc error", __func__); return (r); } else if (r == -2) { log_debug("%s: failed to add transform: unknown type", __func__); return (r); } } /* Next transform */ offset += xfrm_length; total -= xfrm_length; if (xfrm.xfrm_more == IKEV2_XFORM_MORE) ret = ikev2_pld_xform(env, msg, offset, total); else if (total != 0) { /* No more transforms but still some data left. */ log_debug("%s: less data than specified, %zu bytes left", __func__, total); ret = -1; } return (ret); } int ikev2_validate_attr(struct iked_message *msg, size_t offset, size_t total, struct ikev2_attribute *attr) { uint8_t *msgbuf = ibuf_data(msg->msg_data); if (total < sizeof(*attr)) { log_debug("%s: malformed payload: too short for header " "(%zu < %zu)", __func__, total, sizeof(*attr)); return (-1); } memcpy(attr, msgbuf + offset, sizeof(*attr)); return (0); } int ikev2_pld_attr(struct iked *env, struct ikev2_transform *xfrm, struct iked_message *msg, size_t offset, size_t total) { struct ikev2_attribute attr; unsigned int type; uint8_t *msgbuf = ibuf_data(msg->msg_data); int ret = 0; size_t attr_length; if (ikev2_validate_attr(msg, offset, total, &attr)) return (-1); type = betoh16(attr.attr_type) & ~IKEV2_ATTRAF_TV; log_debug("%s: attribute type %s length %d total %zu", __func__, print_map(type, ikev2_attrtype_map), betoh16(attr.attr_length), total); if (betoh16(attr.attr_type) & IKEV2_ATTRAF_TV) { /* Type-Value attribute */ offset += sizeof(attr); total -= sizeof(attr); if (type == IKEV2_ATTRTYPE_KEY_LENGTH) msg->msg_attrlength = betoh16(attr.attr_length); } else { /* Type-Length-Value attribute */ attr_length = betoh16(attr.attr_length); if (attr_length < sizeof(attr)) { log_debug("%s: malformed payload: shorter than " "minimum header size (%zu < %zu)", __func__, attr_length, sizeof(attr)); return (-1); } if (total < attr_length) { log_debug("%s: malformed payload: attribute larger " "than actual payload (%zu < %zu)", __func__, total, attr_length); return (-1); } print_hex(msgbuf, offset + sizeof(attr), attr_length - sizeof(attr)); offset += attr_length; total -= attr_length; } if (total > 0) { /* Next attribute */ ret = ikev2_pld_attr(env, xfrm, msg, offset, total); } return (ret); } int ikev2_validate_ke(struct iked_message *msg, size_t offset, size_t left, struct ikev2_keyexchange *kex) { uint8_t *msgbuf = ibuf_data(msg->msg_data); if (left < sizeof(*kex)) { log_debug("%s: malformed payload: too short for header " "(%zu < %zu)", __func__, left, sizeof(*kex)); return (-1); } memcpy(kex, msgbuf + offset, sizeof(*kex)); return (0); } int ikev2_pld_ke(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct ikev2_keyexchange kex; uint8_t *buf; size_t len; uint8_t *msgbuf = ibuf_data(msg->msg_data); if (ikev2_validate_ke(msg, offset, left, &kex)) return (-1); log_debug("%s: dh group %s reserved %d", __func__, print_map(betoh16(kex.kex_dhgroup), ikev2_xformdh_map), betoh16(kex.kex_reserved)); buf = msgbuf + offset + sizeof(kex); len = left - sizeof(kex); if (len == 0) { log_debug("%s: malformed payload: no KE data given", __func__); return (-1); } print_hex(buf, 0, len); if (ikev2_msg_frompeer(msg)) { if (msg->msg_parent->msg_ke != NULL) { log_info("%s: duplicate KE payload", __func__); return (-1); } if ((msg->msg_parent->msg_ke = ibuf_new(buf, len)) == NULL) { log_debug("%s: failed to get exchange", __func__); return (-1); } msg->msg_parent->msg_dhgroup = betoh16(kex.kex_dhgroup); } return (0); } int ikev2_validate_id(struct iked_message *msg, size_t offset, size_t left, struct ikev2_id *id) { uint8_t *msgbuf = ibuf_data(msg->msg_data); if (left < sizeof(*id)) { log_debug("%s: malformed payload: too short for header " "(%zu < %zu)", __func__, left, sizeof(*id)); return (-1); } memcpy(id, msgbuf + offset, sizeof(*id)); if (id->id_type == IKEV2_ID_NONE) { log_debug("%s: malformed payload: invalid ID type.", __func__); return (-1); } return (0); } int ikev2_pld_id(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left, unsigned int payload) { uint8_t *ptr; struct ikev2_id id; size_t len; struct iked_id *idp, idb; const struct iked_sa *sa = msg->msg_sa; uint8_t *msgbuf = ibuf_data(msg->msg_data); char idstr[IKED_ID_SIZE]; if (ikev2_validate_id(msg, offset, left, &id)) return (-1); bzero(&idb, sizeof(idb)); /* Don't strip the Id payload header */ ptr = msgbuf + offset; len = left; idb.id_type = id.id_type; idb.id_offset = sizeof(id); if ((idb.id_buf = ibuf_new(ptr, len)) == NULL) return (-1); if (ikev2_print_id(&idb, idstr, sizeof(idstr)) == -1) { ibuf_free(idb.id_buf); log_debug("%s: malformed id", __func__); return (-1); } log_debug("%s: id %s length %zu", __func__, idstr, len); if (!ikev2_msg_frompeer(msg)) { ibuf_free(idb.id_buf); return (0); } if (((sa->sa_hdr.sh_initiator && payload == IKEV2_PAYLOAD_IDr) || (!sa->sa_hdr.sh_initiator && payload == IKEV2_PAYLOAD_IDi))) idp = &msg->msg_parent->msg_peerid; else if (!sa->sa_hdr.sh_initiator && payload == IKEV2_PAYLOAD_IDr) idp = &msg->msg_parent->msg_localid; else { ibuf_free(idb.id_buf); log_debug("%s: unexpected id payload", __func__); return (0); } if (idp->id_type) { ibuf_free(idb.id_buf); log_debug("%s: duplicate id payload", __func__); return (-1); } idp->id_buf = idb.id_buf; idp->id_offset = idb.id_offset; idp->id_type = idb.id_type; return (0); } int ikev2_validate_cert(struct iked_message *msg, size_t offset, size_t left, struct ikev2_cert *cert) { uint8_t *msgbuf = ibuf_data(msg->msg_data); if (left < sizeof(*cert)) { log_debug("%s: malformed payload: too short for header " "(%zu < %zu)", __func__, left, sizeof(*cert)); return (-1); } memcpy(cert, msgbuf + offset, sizeof(*cert)); if (cert->cert_type == IKEV2_CERT_NONE) { log_debug("%s: malformed payload: invalid cert type", __func__); return (-1); } return (0); } int ikev2_pld_cert(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct ikev2_cert cert; uint8_t *buf; size_t len; struct iked_id *certid; uint8_t *msgbuf = ibuf_data(msg->msg_data); const struct iked_sa *sa = msg->msg_sa; int i; if (ikev2_validate_cert(msg, offset, left, &cert)) return (-1); offset += sizeof(cert); buf = msgbuf + offset; len = left - sizeof(cert); log_debug("%s: type %s length %zu", __func__, print_map(cert.cert_type, ikev2_cert_map), len); print_hex(buf, 0, len); if (!ikev2_msg_frompeer(msg)) return (0); /* do not accept internal encoding in the wire */ if (cert.cert_type == IKEV2_CERT_BUNDLE) { log_debug("%s: ignoring IKEV2_CERT_BUNDLE", SPI_SA(sa, __func__)); return (0); } certid = &msg->msg_parent->msg_cert; if (certid->id_type) { /* try to set supplemental certs */ for (i = 0; i < IKED_SCERT_MAX; i++) { certid = &msg->msg_parent->msg_scert[i]; if (!certid->id_type) break; } if (certid->id_type) { log_debug("%s: too many cert payloads, ignoring", SPI_SA(sa, __func__)); return (0); } } if ((certid->id_buf = ibuf_new(buf, len)) == NULL) { log_debug("%s: failed to save cert", __func__); return (-1); } certid->id_type = cert.cert_type; certid->id_offset = 0; return (0); } int ikev2_validate_certreq(struct iked_message *msg, size_t offset, size_t left, struct ikev2_cert *cert) { uint8_t *msgbuf = ibuf_data(msg->msg_data); if (left < sizeof(*cert)) { log_debug("%s: malformed payload: too short for header " "(%zu < %zu)", __func__, left, sizeof(*cert)); return (-1); } memcpy(cert, msgbuf + offset, sizeof(*cert)); return (0); } int ikev2_pld_certreq(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct ikev2_cert cert; struct iked_certreq *cr; uint8_t *buf; ssize_t len; uint8_t *msgbuf = ibuf_data(msg->msg_data); if (ikev2_validate_certreq(msg, offset, left, &cert)) return (-1); offset += sizeof(cert); buf = msgbuf + offset; len = left - sizeof(cert); log_debug("%s: type %s length %zd", __func__, print_map(cert.cert_type, ikev2_cert_map), len); print_hex(buf, 0, len); if (!ikev2_msg_frompeer(msg)) return (0); if (cert.cert_type == IKEV2_CERT_X509_CERT) { if (len == 0) { log_info("%s: invalid length 0", __func__); return (0); } if ((len % SHA_DIGEST_LENGTH) != 0) { log_info("%s: invalid certificate request", __func__); return (-1); } } if ((cr = calloc(1, sizeof(struct iked_certreq))) == NULL) { log_info("%s: failed to allocate certreq.", __func__); return (-1); } if ((cr->cr_data = ibuf_new(buf, len)) == NULL) { log_info("%s: failed to allocate buffer.", __func__); free(cr); return (-1); } cr->cr_type = cert.cert_type; SIMPLEQ_INSERT_TAIL(&msg->msg_parent->msg_certreqs, cr, cr_entry); return (0); } int ikev2_validate_auth(struct iked_message *msg, size_t offset, size_t left, struct ikev2_auth *auth) { uint8_t *msgbuf = ibuf_data(msg->msg_data); if (left < sizeof(*auth)) { log_debug("%s: malformed payload: too short for header " "(%zu < %zu)", __func__, left, sizeof(*auth)); return (-1); } memcpy(auth, msgbuf + offset, sizeof(*auth)); if (auth->auth_method == 0) { log_info("%s: malformed payload: invalid auth method", __func__); return (-1); } return (0); } int ikev2_pld_auth(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct ikev2_auth auth; struct iked_id *idp; uint8_t *buf; size_t len; uint8_t *msgbuf = ibuf_data(msg->msg_data); if (ikev2_validate_auth(msg, offset, left, &auth)) return (-1); offset += sizeof(auth); buf = msgbuf + offset; len = left - sizeof(auth); log_debug("%s: method %s length %zu", __func__, print_map(auth.auth_method, ikev2_auth_map), len); print_hex(buf, 0, len); if (!ikev2_msg_frompeer(msg)) return (0); idp = &msg->msg_parent->msg_auth; if (idp->id_type) { log_debug("%s: duplicate auth payload", __func__); return (-1); } ibuf_free(idp->id_buf); idp->id_type = auth.auth_method; idp->id_offset = 0; if ((idp->id_buf = ibuf_new(buf, len)) == NULL) return (-1); return (0); } int ikev2_pld_nonce(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { size_t len; uint8_t *buf; uint8_t *msgbuf = ibuf_data(msg->msg_data); buf = msgbuf + offset; len = left; if (len == 0) { log_debug("%s: malformed payload: no NONCE given", __func__); return (-1); } print_hex(buf, 0, len); if (ikev2_msg_frompeer(msg)) { if (msg->msg_parent->msg_nonce != NULL) { log_info("%s: duplicate NONCE payload", __func__); return (-1); } if ((msg->msg_nonce = ibuf_new(buf, len)) == NULL) { log_debug("%s: failed to get peer nonce", __func__); return (-1); } msg->msg_parent->msg_nonce = msg->msg_nonce; } return (0); } int ikev2_validate_notify(struct iked_message *msg, size_t offset, size_t left, struct ikev2_notify *n) { uint8_t *msgbuf = ibuf_data(msg->msg_data); if (left < sizeof(*n)) { log_debug("%s: malformed payload: too short for header " "(%zu < %zu)", __func__, left, sizeof(*n)); return (-1); } memcpy(n, msgbuf + offset, sizeof(*n)); return (0); } int ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct ikev2_notify n; const struct iked_sa *sa = msg->msg_sa; uint8_t *buf, md[SHA_DIGEST_LENGTH]; uint32_t spi32; uint64_t spi64; struct iked_spi *rekey; uint16_t type; uint16_t signature_hash; if (ikev2_validate_notify(msg, offset, left, &n)) return (-1); type = betoh16(n.n_type); log_debug("%s: protoid %s spisize %d type %s", __func__, print_map(n.n_protoid, ikev2_saproto_map), n.n_spisize, print_map(type, ikev2_n_map)); left -= sizeof(n); if ((buf = ibuf_seek(msg->msg_data, offset + sizeof(n), left)) == NULL) return (-1); print_hex(buf, 0, left); if (!ikev2_msg_frompeer(msg)) return (0); switch (type) { case IKEV2_N_NAT_DETECTION_SOURCE_IP: case IKEV2_N_NAT_DETECTION_DESTINATION_IP: if (left != sizeof(md)) { log_debug("%s: malformed payload: hash size mismatch" " (%zu != %zu)", __func__, left, sizeof(md)); return (-1); } if (ikev2_nat_detection(env, msg, md, sizeof(md), type, ikev2_msg_frompeer(msg)) == -1) return (-1); if (memcmp(buf, md, left) != 0) { log_debug("%s: %s detected NAT", __func__, print_map(type, ikev2_n_map)); if (type == IKEV2_N_NAT_DETECTION_SOURCE_IP) msg->msg_parent->msg_nat_detected |= IKED_MSG_NAT_SRC_IP; else msg->msg_parent->msg_nat_detected |= IKED_MSG_NAT_DST_IP; } print_hex(md, 0, sizeof(md)); /* remember for MOBIKE */ msg->msg_parent->msg_natt_rcvd = 1; break; case IKEV2_N_AUTHENTICATION_FAILED: if (!msg->msg_e) { log_debug("%s: AUTHENTICATION_FAILED not encrypted", __func__); return (-1); } /* * If we are the responder, then we only accept * AUTHENTICATION_FAILED from authenticated peers. * If we are the initiator, the peer cannot be authenticated. */ if (!sa->sa_hdr.sh_initiator) { if (!sa_stateok(sa, IKEV2_STATE_VALID)) { log_debug("%s: ignoring AUTHENTICATION_FAILED" " from unauthenticated initiator", __func__); return (-1); } } else { if (sa_stateok(sa, IKEV2_STATE_VALID)) { log_debug("%s: ignoring AUTHENTICATION_FAILED" " from authenticated responder", __func__); return (-1); } } msg->msg_parent->msg_flags |= IKED_MSG_FLAGS_AUTHENTICATION_FAILED; break; case IKEV2_N_INVALID_KE_PAYLOAD: if (sa_stateok(sa, IKEV2_STATE_VALID) && !msg->msg_e) { log_debug("%s: INVALID_KE_PAYLOAD not encrypted", __func__); return (-1); } if (left != sizeof(msg->msg_parent->msg_group)) { log_debug("%s: malformed payload: group size mismatch" " (%zu != %zu)", __func__, left, sizeof(msg->msg_parent->msg_group)); return (-1); } memcpy(&msg->msg_parent->msg_group, buf, left); msg->msg_parent->msg_flags |= IKED_MSG_FLAGS_INVALID_KE; break; case IKEV2_N_NO_ADDITIONAL_SAS: if (!msg->msg_e) { log_debug("%s: NO_ADDITIONAL_SAS not encrypted", __func__); return (-1); } msg->msg_parent->msg_flags |= IKED_MSG_FLAGS_NO_ADDITIONAL_SAS; break; case IKEV2_N_REKEY_SA: if (!msg->msg_e) { log_debug("%s: N_REKEY_SA not encrypted", __func__); return (-1); } if (left != n.n_spisize) { log_debug("%s: malformed notification", __func__); return (-1); } rekey = &msg->msg_parent->msg_rekey; if (rekey->spi != 0) { log_debug("%s: rekeying of multiple SAs not supported", __func__); return (-1); } switch (n.n_spisize) { case 4: memcpy(&spi32, buf, left); rekey->spi = betoh32(spi32); break; case 8: memcpy(&spi64, buf, left); rekey->spi = betoh64(spi64); break; default: log_debug("%s: invalid spi size %d", __func__, n.n_spisize); return (-1); } rekey->spi_size = n.n_spisize; rekey->spi_protoid = n.n_protoid; log_debug("%s: rekey %s spi %s", __func__, print_map(n.n_protoid, ikev2_saproto_map), print_spi(rekey->spi, n.n_spisize)); break; case IKEV2_N_TEMPORARY_FAILURE: if (!msg->msg_e) { log_debug("%s: IKEV2_N_TEMPORARY_FAILURE not encrypted", __func__); return (-1); } msg->msg_parent->msg_flags |= IKED_MSG_FLAGS_TEMPORARY_FAILURE; break; case IKEV2_N_IPCOMP_SUPPORTED: if (!msg->msg_e) { log_debug("%s: N_IPCOMP_SUPPORTED not encrypted", __func__); return (-1); } if (left < sizeof(msg->msg_parent->msg_cpi) + sizeof(msg->msg_parent->msg_transform)) { log_debug("%s: ignoring malformed ipcomp notification", __func__); return (0); } memcpy(&msg->msg_parent->msg_cpi, buf, sizeof(msg->msg_parent->msg_cpi)); memcpy(&msg->msg_parent->msg_transform, buf + sizeof(msg->msg_parent->msg_cpi), sizeof(msg->msg_parent->msg_transform)); log_debug("%s: %s cpi 0x%x, transform %s, length %zu", __func__, msg->msg_parent->msg_response ? "res" : "req", betoh16(msg->msg_parent->msg_cpi), print_map(msg->msg_parent->msg_transform, ikev2_ipcomp_map), left); msg->msg_parent->msg_flags |= IKED_MSG_FLAGS_IPCOMP_SUPPORTED; break; case IKEV2_N_CHILD_SA_NOT_FOUND: if (!msg->msg_e) { log_debug("%s: N_CHILD_SA_NOT_FOUND not encrypted", __func__); return (-1); } msg->msg_parent->msg_flags |= IKED_MSG_FLAGS_CHILD_SA_NOT_FOUND; break; case IKEV2_N_NO_PROPOSAL_CHOSEN: msg->msg_parent->msg_flags |= IKED_MSG_FLAGS_NO_PROPOSAL_CHOSEN; break; case IKEV2_N_MOBIKE_SUPPORTED: if (!msg->msg_e) { log_debug("%s: N_MOBIKE_SUPPORTED not encrypted", __func__); return (-1); } if (left != 0) { log_debug("%s: ignoring malformed mobike" " notification: %zu", __func__, left); return (0); } msg->msg_parent->msg_flags |= IKED_MSG_FLAGS_MOBIKE; break; case IKEV2_N_USE_TRANSPORT_MODE: if (!msg->msg_e) { log_debug("%s: N_USE_TRANSPORT_MODE not encrypted", __func__); return (-1); } if (left != 0) { log_debug("%s: ignoring malformed transport mode" " notification: %zu", __func__, left); return (0); } if (msg->msg_parent->msg_response) { if (!(msg->msg_policy->pol_flags & IKED_POLICY_TRANSPORT)) { log_debug("%s: ignoring transport mode" " notification (policy)", __func__); return (0); } } msg->msg_parent->msg_flags |= IKED_MSG_FLAGS_USE_TRANSPORT; break; case IKEV2_N_UPDATE_SA_ADDRESSES: if (!msg->msg_e) { log_debug("%s: N_UPDATE_SA_ADDRESSES not encrypted", __func__); return (-1); } if (!sa->sa_mobike) { log_debug("%s: ignoring update sa addresses" " notification w/o mobike: %zu", __func__, left); return (0); } if (left != 0) { log_debug("%s: ignoring malformed update sa addresses" " notification: %zu", __func__, left); return (0); } msg->msg_parent->msg_update_sa_addresses = 1; break; case IKEV2_N_COOKIE2: if (!msg->msg_e) { log_debug("%s: N_COOKIE2 not encrypted", __func__); return (-1); } if (!sa->sa_mobike) { log_debug("%s: ignoring cookie2 notification" " w/o mobike: %zu", __func__, left); return (0); } if (left < IKED_COOKIE2_MIN || left > IKED_COOKIE2_MAX) { log_debug("%s: ignoring malformed cookie2" " notification: %zu", __func__, left); return (0); } ibuf_free(msg->msg_cookie2); /* should not happen */ if ((msg->msg_cookie2 = ibuf_new(buf, left)) == NULL) { log_debug("%s: failed to get peer cookie2", __func__); return (-1); } msg->msg_parent->msg_cookie2 = msg->msg_cookie2; break; case IKEV2_N_COOKIE: if (msg->msg_e) { log_debug("%s: N_COOKIE encrypted", __func__); return (-1); } if (left < IKED_COOKIE_MIN || left > IKED_COOKIE_MAX) { log_debug("%s: ignoring malformed cookie" " notification: %zu", __func__, left); return (0); } log_debug("%s: received cookie, len %zu", __func__, left); print_hex(buf, 0, left); ibuf_free(msg->msg_cookie); if ((msg->msg_cookie = ibuf_new(buf, left)) == NULL) { log_debug("%s: failed to get peer cookie", __func__); return (-1); } msg->msg_parent->msg_cookie = msg->msg_cookie; break; case IKEV2_N_FRAGMENTATION_SUPPORTED: if (msg->msg_e) { log_debug("%s: N_FRAGMENTATION_SUPPORTED encrypted", __func__); return (-1); } if (left != 0) { log_debug("%s: ignoring malformed fragmentation" " notification: %zu", __func__, left); return (0); } msg->msg_parent->msg_flags |= IKED_MSG_FLAGS_FRAGMENTATION; break; case IKEV2_N_SIGNATURE_HASH_ALGORITHMS: if (msg->msg_e) { log_debug("%s: SIGNATURE_HASH_ALGORITHMS: encrypted", __func__); return (-1); } if (sa == NULL) { log_debug("%s: SIGNATURE_HASH_ALGORITHMS: no SA", __func__); return (-1); } if (sa->sa_sigsha2) { log_debug("%s: SIGNATURE_HASH_ALGORITHMS: " "duplicate notify", __func__); return (0); } if (left < sizeof(signature_hash) || left % sizeof(signature_hash)) { log_debug("%s: malformed signature hash notification" "(%zu bytes)", __func__, left); return (0); } while (left >= sizeof(signature_hash)) { memcpy(&signature_hash, buf, sizeof(signature_hash)); signature_hash = betoh16(signature_hash); log_debug("%s: signature hash %s (%x)", __func__, print_map(signature_hash, ikev2_sighash_map), signature_hash); left -= sizeof(signature_hash); buf += sizeof(signature_hash); if (signature_hash == IKEV2_SIGHASH_SHA2_256) msg->msg_parent->msg_flags |= IKED_MSG_FLAGS_SIGSHA2; } break; } return (0); } int ikev2_validate_delete(struct iked_message *msg, size_t offset, size_t left, struct ikev2_delete *del) { uint8_t *msgbuf = ibuf_data(msg->msg_data); if (left < sizeof(*del)) { log_debug("%s: malformed payload: too short for header " "(%zu < %zu)", __func__, left, sizeof(*del)); return (-1); } memcpy(del, msgbuf + offset, sizeof(*del)); if (del->del_protoid == 0) { log_info("%s: malformed payload: invalid protoid", __func__); return (-1); } return (0); } int ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct ikev2_delete del; uint8_t *buf, *msgbuf = ibuf_data(msg->msg_data); size_t cnt, sz, len; if (ikev2_validate_delete(msg, offset, left, &del)) return (-1); /* Skip if it's a response, then we don't have to deal with it */ if (ikev2_msg_frompeer(msg) && msg->msg_parent->msg_response) return (0); cnt = betoh16(del.del_nspi); sz = del.del_spisize; log_debug("%s: proto %s spisize %zu nspi %zu", __func__, print_map(del.del_protoid, ikev2_saproto_map), sz, cnt); if (msg->msg_parent->msg_del_protoid) { log_debug("%s: duplicate delete payload", __func__); return (0); } msg->msg_parent->msg_del_protoid = del.del_protoid; msg->msg_parent->msg_del_cnt = cnt; msg->msg_parent->msg_del_spisize = sz; buf = msgbuf + offset + sizeof(del); len = left - sizeof(del); if (len == 0 || sz == 0 || cnt == 0) return (0); if ((len / sz) != cnt) { log_debug("%s: invalid payload length %zu/%zu != %zu", __func__, len, sz, cnt); return (-1); } print_hex(buf, 0, len); msg->msg_parent->msg_del_buf = ibuf_new(buf, len); return (0); } int ikev2_validate_tss(struct iked_message *msg, size_t offset, size_t left, struct ikev2_tsp *tsp) { uint8_t *msgbuf = ibuf_data(msg->msg_data); if (left < sizeof(*tsp)) { log_debug("%s: malformed payload: too short for header " "(%zu < %zu)", __func__, left, sizeof(*tsp)); return (-1); } memcpy(tsp, msgbuf + offset, sizeof(*tsp)); return (0); } int ikev2_pld_tss(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct ikev2_tsp tsp; struct ikev2_ts ts; size_t ts_len, i; if (ikev2_validate_tss(msg, offset, left, &tsp)) return (-1); offset += sizeof(tsp); left -= sizeof(tsp); log_debug("%s: count %d length %zu", __func__, tsp.tsp_count, left); for (i = 0; i < tsp.tsp_count; i++) { if (ikev2_validate_ts(msg, offset, left, &ts)) return (-1); log_debug("%s: type %s protoid %u length %d " "startport %u endport %u", __func__, print_map(ts.ts_type, ikev2_ts_map), ts.ts_protoid, betoh16(ts.ts_length), betoh16(ts.ts_startport), betoh16(ts.ts_endport)); offset += sizeof(ts); left -= sizeof(ts); ts_len = betoh16(ts.ts_length) - sizeof(ts); if (ikev2_pld_ts(env, pld, msg, offset, ts_len, ts.ts_type)) return (-1); offset += ts_len; left -= ts_len; } return (0); } int ikev2_validate_ts(struct iked_message *msg, size_t offset, size_t left, struct ikev2_ts *ts) { uint8_t *msgbuf = ibuf_data(msg->msg_data); size_t ts_length; if (left < sizeof(*ts)) { log_debug("%s: malformed payload: too short for header " "(%zu < %zu)", __func__, left, sizeof(*ts)); return (-1); } memcpy(ts, msgbuf + offset, sizeof(*ts)); ts_length = betoh16(ts->ts_length); if (ts_length < sizeof(*ts)) { log_debug("%s: malformed payload: shorter than minimum header " "size (%zu < %zu)", __func__, ts_length, sizeof(*ts)); return (-1); } if (left < ts_length) { log_debug("%s: malformed payload: too long for payload size " "(%zu < %zu)", __func__, left, ts_length); return (-1); } return (0); } int ikev2_pld_ts(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left, unsigned int type) { struct sockaddr_in start4, end4; struct sockaddr_in6 start6, end6; uint8_t *msgbuf = ibuf_data(msg->msg_data); uint8_t *ptr; ptr = msgbuf + offset; switch (type) { case IKEV2_TS_IPV4_ADDR_RANGE: if (left < 2 * 4) { log_debug("%s: malformed payload: too short " "for ipv4 addr range (%zu < %u)", __func__, left, 2 * 4); return (-1); } bzero(&start4, sizeof(start4)); start4.sin_family = AF_INET; #ifdef HAVE_SOCKADDR_SA_LEN start4.sin_len = sizeof(start4); #endif memcpy(&start4.sin_addr.s_addr, ptr, 4); ptr += 4; left -= 4; bzero(&end4, sizeof(end4)); end4.sin_family = AF_INET; #ifdef HAVE_SOCKADDR_SA_LEN end4.sin_len = sizeof(end4); #endif memcpy(&end4.sin_addr.s_addr, ptr, 4); left -= 4; log_debug("%s: start %s end %s", __func__, print_addr(&start4), print_addr(&end4)); break; case IKEV2_TS_IPV6_ADDR_RANGE: if (left < 2 * 16) { log_debug("%s: malformed payload: too short " "for ipv6 addr range (%zu < %u)", __func__, left, 2 * 16); return (-1); } bzero(&start6, sizeof(start6)); start6.sin6_family = AF_INET6; #ifdef HAVE_SOCKADDR_SA_LEN start6.sin6_len = sizeof(start6); #endif memcpy(&start6.sin6_addr, ptr, 16); ptr += 16; left -= 16; bzero(&end6, sizeof(end6)); end6.sin6_family = AF_INET6; #ifdef HAVE_SOCKADDR_SA_LEN end6.sin6_len = sizeof(end6); #endif memcpy(&end6.sin6_addr, ptr, 16); left -= 16; log_debug("%s: start %s end %s", __func__, print_addr(&start6), print_addr(&end6)); break; default: log_debug("%s: ignoring unknown TS type %u", __func__, type); return (0); } if (left > 0) { log_debug("%s: malformed payload: left (%zu) > 0", __func__, left); return (-1); } return (0); } int ikev2_pld_ef(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct iked_sa *sa = msg->msg_sa; struct iked_frag *sa_frag = &sa->sa_fragments; struct iked_frag_entry *el; struct ikev2_frag_payload frag; uint8_t *msgbuf = ibuf_data(msg->msg_data); uint8_t *buf; struct ibuf *e = NULL; size_t frag_num, frag_total; size_t len; int ret = -1; int processed = 0; ssize_t elen; buf = msgbuf + offset; memcpy(&frag, buf, sizeof(frag)); frag_num = betoh16(frag.frag_num); frag_total = betoh16(frag.frag_total); offset += sizeof(frag); buf = msgbuf + offset; len = left - sizeof(frag); ikestat_inc(env, ikes_frag_rcvd); /* Limit number of total fragments to avoid DOS */ if (frag_total > IKED_FRAG_TOTAL_MAX ) { log_debug("%s: Total Fragments too big %zu", __func__, frag_total); goto dropall; } /* Check sanity of fragment header */ if (frag_num == 0 || frag_total == 0) { log_debug("%s: Malformed fragment received: %zu of %zu", __func__, frag_num, frag_total); goto done; } log_debug("%s: Received fragment: %zu of %zu", __func__, frag_num, frag_total); /* Drop fragment if frag_num and frag_total don't match */ if (frag_num > frag_total) goto done; /* Decrypt fragment */ if ((e = ibuf_new(buf, len)) == NULL) goto done; if ((e = ikev2_msg_decrypt(env, msg->msg_sa, msg->msg_data, e)) == NULL ) { log_debug("%s: Failed to decrypt fragment: %zu of %zu", __func__, frag_num, frag_total); goto done; } elen = ibuf_size(e); /* Check new fragmented message */ if (sa_frag->frag_arr == NULL) { sa_frag->frag_arr = recallocarray(NULL, 0, frag_total, sizeof(struct iked_frag_entry*)); if (sa_frag->frag_arr == NULL) { log_info("%s: recallocarray sa_frag->frag_arr.", __func__); goto done; } sa_frag->frag_total = frag_total; } else { /* Drop all fragments if frag_total doesn't match previous */ if (frag_total != sa_frag->frag_total) goto dropall; /* Silent drop if fragment already stored */ if (sa_frag->frag_arr[frag_num-1] != NULL) goto done; } /* The first fragments IKE header determines pld_nextpayload */ if (frag_num == 1) sa_frag->frag_nextpayload = pld->pld_nextpayload; /* Insert new list element */ el = calloc(1, sizeof(struct iked_frag_entry)); if (el == NULL) { log_info("%s: Failed allocating new fragment: %zu of %zu", __func__, frag_num, frag_total); goto done; } sa_frag->frag_arr[frag_num-1] = el; el->frag_size = elen; el->frag_data = calloc(1, elen); if (el->frag_data == NULL) { log_debug("%s: Failed allocating new fragment data: %zu of %zu", __func__, frag_num, frag_total); goto done; } /* Copy plaintext to fragment */ memcpy(el->frag_data, ibuf_seek(e, 0, 0), elen); sa_frag->frag_total_size += elen; sa_frag->frag_count++; /* If all frags are received start reassembly */ if (sa_frag->frag_count == sa_frag->frag_total) { log_debug("%s: All fragments received: %zu of %zu", __func__, frag_num, frag_total); ret = ikev2_frags_reassemble(env, pld, msg); } else { ret = 0; } processed = 1; done: if (!processed) ikestat_inc(env, ikes_frag_rcvd_drop); ibuf_free(e); return (ret); dropall: ikestat_add(env, ikes_frag_rcvd_drop, sa_frag->frag_count + 1); config_free_fragments(sa_frag); ibuf_free(e); return -1; } int ikev2_frags_reassemble(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg) { struct iked_frag *sa_frag = &msg->msg_sa->sa_fragments; struct ibuf *e = NULL; struct iked_frag_entry *el; uint8_t *ptr; size_t offset; size_t i; struct iked_message emsg; int ret = -1; int processed = 0; /* Reassemble fragments to single buffer */ if ((e = ibuf_new(NULL, sa_frag->frag_total_size)) == NULL) { log_debug("%s: Failed allocating SK buffer.", __func__); goto done; } /* Empty queue to new buffer */ offset = 0; for (i = 0; i < sa_frag->frag_total; i++) { if ((el = sa_frag->frag_arr[i]) == NULL) fatalx("Tried to reassemble shallow frag_arr"); ptr = ibuf_seek(e, offset, el->frag_size); if (ptr == NULL) { log_info("%s: failed to reassemble fragments", __func__); goto done; } memcpy(ptr, el->frag_data, el->frag_size); offset += el->frag_size; } log_debug("%s: Defragmented length %zd", __func__, sa_frag->frag_total_size); print_hex(ibuf_data(e), 0, sa_frag->frag_total_size); /* Drop the original request's packets from the retransmit queue */ if (msg->msg_response) ikev2_msg_dispose(env, &msg->msg_sa->sa_requests, ikev2_msg_lookup(env, &msg->msg_sa->sa_requests, msg, msg->msg_exchange)); /* * Parse decrypted payload */ bzero(&emsg, sizeof(emsg)); memcpy(&emsg, msg, sizeof(*msg)); emsg.msg_data = e; emsg.msg_e = 1; emsg.msg_parent = msg; TAILQ_INIT(&emsg.msg_proposals); ret = ikev2_pld_payloads(env, &emsg, 0, ibuf_size(e), sa_frag->frag_nextpayload); processed = 1; done: if (processed) ikestat_add(env, ikes_frag_reass_ok, sa_frag->frag_total); else ikestat_add(env, ikes_frag_reass_drop, sa_frag->frag_total); config_free_fragments(sa_frag); ibuf_free(e); return (ret); } int ikev2_pld_e(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct iked_sa *sa = msg->msg_sa; struct ibuf *e = NULL; uint8_t *msgbuf = ibuf_data(msg->msg_data); struct iked_message emsg; uint8_t *buf; size_t len; int ret = -1; if (sa->sa_fragments.frag_arr != NULL) { log_warn("%s: Received SK payload when SKFs are in queue.", __func__); config_free_fragments(&sa->sa_fragments); return (ret); } buf = msgbuf + offset; len = left; if ((e = ibuf_new(buf, len)) == NULL) goto done; if (ikev2_msg_frompeer(msg)) { e = ikev2_msg_decrypt(env, msg->msg_sa, msg->msg_data, e); } else { sa->sa_hdr.sh_initiator = sa->sa_hdr.sh_initiator ? 0 : 1; e = ikev2_msg_decrypt(env, msg->msg_sa, msg->msg_data, e); sa->sa_hdr.sh_initiator = sa->sa_hdr.sh_initiator ? 0 : 1; } if (e == NULL) goto done; /* * Parse decrypted payload */ bzero(&emsg, sizeof(emsg)); memcpy(&emsg, msg, sizeof(*msg)); emsg.msg_data = e; emsg.msg_e = 1; emsg.msg_parent = msg; TAILQ_INIT(&emsg.msg_proposals); ret = ikev2_pld_payloads(env, &emsg, 0, ibuf_size(e), pld->pld_nextpayload); done: ibuf_free(e); return (ret); } int ikev2_validate_cp(struct iked_message *msg, size_t offset, size_t left, struct ikev2_cp *cp) { uint8_t *msgbuf = ibuf_data(msg->msg_data); if (left < sizeof(*cp)) { log_debug("%s: malformed payload: too short for header " "(%zu < %zu)", __func__, left, sizeof(*cp)); return (-1); } memcpy(cp, msgbuf + offset, sizeof(*cp)); return (0); } int ikev2_pld_cp(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct ikev2_cp cp; struct ikev2_cfg *cfg; struct iked_addr *addr; struct sockaddr_in *in4; struct sockaddr_in6 *in6; uint8_t *msgbuf = ibuf_data(msg->msg_data); uint8_t *ptr; size_t len; int cfg_type; if (ikev2_validate_cp(msg, offset, left, &cp)) return (-1); ptr = msgbuf + offset + sizeof(cp); len = left - sizeof(cp); log_debug("%s: type %s length %zu", __func__, print_map(cp.cp_type, ikev2_cp_map), len); print_hex(ptr, 0, len); while (len > 0) { if (len < sizeof(*cfg)) { log_debug("%s: malformed payload: too short for cfg " "(%zu < %zu)", __func__, len, sizeof(*cfg)); return (-1); } cfg = (struct ikev2_cfg *)ptr; log_debug("%s: %s 0x%04x length %d", __func__, print_map(betoh16(cfg->cfg_type), ikev2_cfg_map), betoh16(cfg->cfg_type), betoh16(cfg->cfg_length)); ptr += sizeof(*cfg); len -= sizeof(*cfg); if (len < betoh16(cfg->cfg_length)) { log_debug("%s: malformed payload: too short for " "cfg_length (%zu < %u)", __func__, len, betoh16(cfg->cfg_length)); return (-1); } print_hex(ptr, sizeof(*cfg), betoh16(cfg->cfg_length)); cfg_type = betoh16(cfg->cfg_type); switch (cfg_type) { case IKEV2_CFG_INTERNAL_IP4_ADDRESS: case IKEV2_CFG_INTERNAL_IP4_DNS: if (!ikev2_msg_frompeer(msg)) break; if (betoh16(cfg->cfg_length) == 0) break; /* XXX multiple-valued */ if (betoh16(cfg->cfg_length) < 4) { log_debug("%s: malformed payload: too short " "for ipv4 addr (%u < %u)", __func__, betoh16(cfg->cfg_length), 4); return (-1); } switch(cfg_type) { case IKEV2_CFG_INTERNAL_IP4_ADDRESS: if (msg->msg_parent->msg_cp_addr != NULL) { log_debug("%s: address already set", __func__); goto skip; } break; case IKEV2_CFG_INTERNAL_IP4_DNS: if (msg->msg_parent->msg_cp_dns != NULL) { log_debug("%s: dns already set", __func__); goto skip; } break; default: break; } if ((addr = calloc(1, sizeof(*addr))) == NULL) { log_debug("%s: malloc failed", __func__); break; } addr->addr_af = AF_INET; in4 = (struct sockaddr_in *)&addr->addr; in4->sin_family = AF_INET; #ifdef HAVE_SOCKADDR_SA_LEN in4->sin_len = sizeof(*in4); #endif memcpy(&in4->sin_addr.s_addr, ptr, 4); switch(cfg_type) { case IKEV2_CFG_INTERNAL_IP4_ADDRESS: msg->msg_parent->msg_cp_addr = addr; log_debug("%s: IP4_ADDRESS %s", __func__, print_addr(&addr->addr)); break; case IKEV2_CFG_INTERNAL_IP4_DNS: msg->msg_parent->msg_cp_dns = addr; log_debug("%s: IP4_DNS %s", __func__, print_addr(&addr->addr)); break; default: log_debug("%s: cfg %s", __func__, print_addr(&addr->addr)); break; } break; case IKEV2_CFG_INTERNAL_IP6_ADDRESS: case IKEV2_CFG_INTERNAL_IP6_DNS: if (!ikev2_msg_frompeer(msg)) break; if (betoh16(cfg->cfg_length) == 0) break; /* XXX multiple-valued */ if (betoh16(cfg->cfg_length) < 16) { log_debug("%s: malformed payload: too short " "for ipv6 addr w/prefixlen (%u < %u)", __func__, betoh16(cfg->cfg_length), 16); return (-1); } switch(cfg_type) { case IKEV2_CFG_INTERNAL_IP6_ADDRESS: if (msg->msg_parent->msg_cp_addr6 != NULL) { log_debug("%s: address6 already set", __func__); goto skip; } break; case IKEV2_CFG_INTERNAL_IP6_DNS: if (msg->msg_parent->msg_cp_dns != NULL) { log_debug("%s: dns already set", __func__); goto skip; } break; } if ((addr = calloc(1, sizeof(*addr))) == NULL) { log_debug("%s: malloc failed", __func__); break; } addr->addr_af = AF_INET6; in6 = (struct sockaddr_in6 *)&addr->addr; in6->sin6_family = AF_INET6; #ifdef HAVE_SOCKADDR_SA_LEN in6->sin6_len = sizeof(*in6); #endif memcpy(&in6->sin6_addr, ptr, 16); switch(cfg_type) { case IKEV2_CFG_INTERNAL_IP6_ADDRESS: msg->msg_parent->msg_cp_addr6 = addr; log_debug("%s: IP6_ADDRESS %s", __func__, print_addr(&addr->addr)); break; case IKEV2_CFG_INTERNAL_IP6_DNS: msg->msg_parent->msg_cp_dns = addr; log_debug("%s: IP6_DNS %s", __func__, print_addr(&addr->addr)); break; default: log_debug("%s: cfg %s/%d", __func__, print_addr(&addr->addr), ptr[16]); break; } break; } skip: ptr += betoh16(cfg->cfg_length); len -= betoh16(cfg->cfg_length); } if (!ikev2_msg_frompeer(msg)) return (0); msg->msg_parent->msg_cp = cp.cp_type; return (0); } int ikev2_validate_eap(struct iked_message *msg, size_t offset, size_t left, struct eap_header *hdr) { uint8_t *msgbuf = ibuf_data(msg->msg_data); if (left < sizeof(*hdr)) { log_debug("%s: malformed payload: too short for header " "(%zu < %zu)", __func__, left, sizeof(*hdr)); return (-1); } memcpy(hdr, msgbuf + offset, sizeof(*hdr)); return (0); } int ikev2_pld_eap(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct eap_header hdr; struct eap_message *eap = NULL; const struct iked_sa *sa = msg->msg_sa; size_t eap_len; if (ikev2_validate_eap(msg, offset, left, &hdr)) return (-1); eap_len = betoh16(hdr.eap_length); if (left != eap_len) { log_info("%s: malformed payload: EAP length does not match" " payload length (%zu != %zu)", __func__, left, eap_len); return (-1); } if (eap_len < sizeof(*eap)) { log_info("%s: %s id %d length %d", SPI_SA(sa, __func__), print_map(hdr.eap_code, eap_code_map), hdr.eap_id, betoh16(hdr.eap_length)); } else { /* Now try to get the indicated length */ if ((eap = ibuf_seek(msg->msg_data, offset, eap_len)) == NULL) { log_debug("%s: invalid EAP length", __func__); return (-1); } log_info("%s: %s id %d length %d EAP-%s", SPI_SA(sa, __func__), print_map(eap->eap_code, eap_code_map), eap->eap_id, betoh16(eap->eap_length), print_map(eap->eap_type, eap_type_map)); if (eap_parse(env, sa, msg, eap, msg->msg_response) == -1) return (-1); msg->msg_parent->msg_eap.eam_found = 1; } return (0); } /* parser for the initial IKE_AUTH payload, does not require msg_sa */ int ikev2_pld_parse_quick(struct iked *env, struct ike_header *hdr, struct iked_message *msg, size_t offset) { struct ikev2_payload pld; struct ikev2_frag_payload frag; uint8_t *msgbuf = ibuf_data(msg->msg_data); uint8_t *buf; size_t len, total, left; size_t length; unsigned int payload; log_debug("%s: header ispi %s rspi %s" " nextpayload %s version 0x%02x exchange %s flags 0x%02x" " msgid %d length %u response %d", __func__, print_spi(betoh64(hdr->ike_ispi), 8), print_spi(betoh64(hdr->ike_rspi), 8), print_map(hdr->ike_nextpayload, ikev2_payload_map), hdr->ike_version, print_map(hdr->ike_exchange, ikev2_exchange_map), hdr->ike_flags, betoh32(hdr->ike_msgid), betoh32(hdr->ike_length), msg->msg_response); length = betoh32(hdr->ike_length); if (ibuf_size(msg->msg_data) < length) { log_debug("%s: short message", __func__); return (-1); } offset += sizeof(*hdr); /* Bytes left in datagram. */ total = length - offset; payload = hdr->ike_nextpayload; while (payload != 0 && offset < length) { if (ikev2_validate_pld(msg, offset, total, &pld)) return (-1); log_debug("%s: %spayload %s" " nextpayload %s critical 0x%02x length %d", __func__, msg->msg_e ? "decrypted " : "", print_map(payload, ikev2_payload_map), print_map(pld.pld_nextpayload, ikev2_payload_map), pld.pld_reserved & IKEV2_CRITICAL_PAYLOAD, betoh16(pld.pld_length)); /* Skip over generic payload header. */ offset += sizeof(pld); total -= sizeof(pld); left = betoh16(pld.pld_length) - sizeof(pld); switch (payload) { case IKEV2_PAYLOAD_SKF: len = left; buf = msgbuf + offset; if (len < sizeof(frag)) return (-1); memcpy(&frag, buf, sizeof(frag)); msg->msg_frag_num = betoh16(frag.frag_num); break; } payload = pld.pld_nextpayload; offset += left; total -= left; } return (0); } openiked-7.4/iked/imsg_util.c000066400000000000000000000046371477554731100162660ustar00rootroot00000000000000/* $OpenBSD: imsg_util.c,v 1.22 2023/12/12 15:52:58 claudio Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" /* * Extending the imsg buffer API for internal use */ struct ibuf * ibuf_new(const void *data, size_t len) { struct ibuf *buf; if ((buf = ibuf_dynamic(len, IKED_MSGBUF_MAX)) == NULL) return (NULL); if (len == 0) return (buf); if (data == NULL) { if (ibuf_add_zero(buf, len) != 0) { ibuf_free(buf); return (NULL); } } else { if (ibuf_add(buf, data, len) != 0) { ibuf_free(buf); return (NULL); } } return (buf); } struct ibuf * ibuf_static(void) { return ibuf_open(IKED_MSGBUF_MAX); } size_t ibuf_length(struct ibuf *buf) { if (buf == NULL) return (0); return (ibuf_size(buf)); } struct ibuf * ibuf_getdata(struct ibuf *buf, size_t len) { struct ibuf tmp; if (ibuf_get_ibuf(buf, len, &tmp) == -1) return (NULL); return (ibuf_new(ibuf_data(&tmp), ibuf_size(&tmp))); } struct ibuf * ibuf_dup(struct ibuf *buf) { if (buf == NULL) return (NULL); return (ibuf_new(ibuf_data(buf), ibuf_size(buf))); } struct ibuf * ibuf_random(size_t len) { struct ibuf *buf; void *ptr; if ((buf = ibuf_open(len)) == NULL) return (NULL); if ((ptr = ibuf_reserve(buf, len)) == NULL) { ibuf_free(buf); return (NULL); } arc4random_buf(ptr, len); return (buf); } int ibuf_setsize(struct ibuf *buf, size_t len) { if (len > buf->size) return (-1); buf->wpos = len; return (0); } openiked-7.4/iked/ipsec.c000066400000000000000000000037271477554731100153740ustar00rootroot00000000000000/* $OpenBSD: $ */ /* * Copyright (c) 2020-2021 Tobias Heider * Copyright (c) 2020 Markus Friedl * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "iked.h" int ipsec_couple(struct iked *env, struct iked_sas *sas, int couple) { return pfkey_couple(env, sas, couple); } int ipsec_sa_last_used(struct iked *env, struct iked_childsa *sa, uint64_t *last_used) { return pfkey_sa_last_used(env, sa, last_used); } int ipsec_flow_add(struct iked *env, struct iked_flow *flow) { return pfkey_flow_add(env, flow); } int ipsec_flow_delete(struct iked *env, struct iked_flow *flow) { return pfkey_flow_delete(env, flow); } int ipsec_sa_init(struct iked *env, struct iked_childsa *sa, uint32_t *spi) { return pfkey_sa_init(env, sa, spi); } int ipsec_sa_add(struct iked *env, struct iked_childsa *sa, struct iked_childsa *last) { return pfkey_sa_add(env, sa, last); } int ipsec_sa_update_addresses(struct iked *env, struct iked_childsa *sa) { return pfkey_sa_update_addresses(env, sa); } int ipsec_sa_delete(struct iked *env, struct iked_childsa *sa) { return pfkey_sa_delete(env, sa); } int ipsec_socket(struct iked *env) { return pfkey_socket(env); } void ipsec_init(struct iked *env, int fd) { pfkey_init(env, fd); } openiked-7.4/iked/log.c000066400000000000000000000103041477554731100150370ustar00rootroot00000000000000/* $OpenBSD: log.c,v 1.12 2017/03/21 12:06:55 bluhm Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include static int debug; static int verbose; const char *log_procname; void log_init(int, int); void log_procinit(const char *); void log_setverbose(int); int log_getverbose(void); void log_warn(const char *, ...) __attribute__((__format__ (printf, 1, 2))); void log_warnx(const char *, ...) __attribute__((__format__ (printf, 1, 2))); void log_info(const char *, ...) __attribute__((__format__ (printf, 1, 2))); void log_debug(const char *, ...) __attribute__((__format__ (printf, 1, 2))); void logit(int, const char *, ...) __attribute__((__format__ (printf, 2, 3))); void vlog(int, const char *, va_list) __attribute__((__format__ (printf, 2, 0))); __dead void fatal(const char *, ...) __attribute__((__format__ (printf, 1, 2))); __dead void fatalx(const char *, ...) __attribute__((__format__ (printf, 1, 2))); void log_init(int n_debug, int facility) { extern char *__progname; debug = n_debug; verbose = n_debug; log_procinit(__progname); if (!debug) openlog(__progname, LOG_PID | LOG_NDELAY, facility); tzset(); } void log_procinit(const char *procname) { if (procname != NULL) log_procname = procname; } void log_setverbose(int v) { verbose = v; } int log_getverbose(void) { return (verbose); } void logit(int pri, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vlog(pri, fmt, ap); va_end(ap); } void vlog(int pri, const char *fmt, va_list ap) { char *nfmt; int saved_errno = errno; if (debug) { /* best effort in out of mem situations */ if (asprintf(&nfmt, "%s\n", fmt) == -1) { vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); } else { vfprintf(stderr, nfmt, ap); free(nfmt); } fflush(stderr); } else vsyslog(pri, fmt, ap); errno = saved_errno; } void log_warn(const char *emsg, ...) { char *nfmt; va_list ap; int saved_errno = errno; /* best effort to even work in out of memory situations */ if (emsg == NULL) logit(LOG_ERR, "%s", strerror(saved_errno)); else { va_start(ap, emsg); if (asprintf(&nfmt, "%s: %s", emsg, strerror(saved_errno)) == -1) { /* we tried it... */ vlog(LOG_ERR, emsg, ap); logit(LOG_ERR, "%s", strerror(saved_errno)); } else { vlog(LOG_ERR, nfmt, ap); free(nfmt); } va_end(ap); } errno = saved_errno; } void log_warnx(const char *emsg, ...) { va_list ap; va_start(ap, emsg); vlog(LOG_ERR, emsg, ap); va_end(ap); } void log_info(const char *emsg, ...) { va_list ap; va_start(ap, emsg); vlog(LOG_INFO, emsg, ap); va_end(ap); } void log_debug(const char *emsg, ...) { va_list ap; if (verbose > 1) { va_start(ap, emsg); vlog(LOG_DEBUG, emsg, ap); va_end(ap); } } static void vfatalc(int code, const char *emsg, va_list ap) { static char s[BUFSIZ]; const char *sep; if (emsg != NULL) { (void)vsnprintf(s, sizeof(s), emsg, ap); sep = ": "; } else { s[0] = '\0'; sep = ""; } if (code) logit(LOG_CRIT, "%s: %s%s%s", log_procname, s, sep, strerror(code)); else logit(LOG_CRIT, "%s%s%s", log_procname, sep, s); } void fatal(const char *emsg, ...) { va_list ap; va_start(ap, emsg); vfatalc(errno, emsg, ap); va_end(ap); exit(1); } void fatalx(const char *emsg, ...) { va_list ap; va_start(ap, emsg); vfatalc(0, emsg, ap); va_end(ap); exit(1); } openiked-7.4/iked/ocsp.c000066400000000000000000000364471477554731100152420ustar00rootroot00000000000000/* $OpenBSD: ocsp.c,v 1.25 2024/01/17 08:25:02 claudio Exp $ */ /* * Copyright (c) 2014 Markus Friedl * Copyright (c) 2005 Marco Pfatschbacher * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" struct iked_ocsp { struct iked *ocsp_env; /* back pointer to env */ struct iked_sahdr ocsp_sh; /* ike sa */ uint8_t ocsp_type; /* auth type */ struct iked_socket *ocsp_sock; /* socket to ocsp responder */ BIO *ocsp_cbio; /* matching OpenSSL obj */ OCSP_CERTID *ocsp_id; /* ocsp-id for cert */ OCSP_REQUEST *ocsp_req; /* ocsp-request */ OCSP_REQ_CTX *ocsp_req_ctx; /* async ocsp-request */ }; struct ocsp_connect { struct iked_sahdr oc_sh; struct iked_socket oc_sock; char *oc_path; char *oc_url; }; #define OCSP_TIMEOUT 30 /* priv */ void ocsp_connect_cb(int, short, void *); int ocsp_connect_finish(struct iked *, int, struct ocsp_connect *); /* unpriv */ void ocsp_free(struct iked_ocsp *); void ocsp_callback(int, short, void *); void ocsp_parse_response(struct iked_ocsp *, OCSP_RESPONSE *); STACK_OF(X509) *ocsp_load_certs(const char *); int ocsp_validate_finish(struct iked_ocsp *, int); /* priv */ /* async connect to configure ocsp-responder */ int ocsp_connect(struct iked *env, struct imsg *imsg) { struct ocsp_connect *oc = NULL; struct iked_sahdr sh; struct addrinfo hints, *res0 = NULL, *res; struct timeval tv; uint8_t *ptr; size_t len; char *host = NULL, *port = NULL, *path = NULL; char *url, *freeme = NULL; int use_ssl, fd = -1, ret = -1, error; IMSG_SIZE_CHECK(imsg, &sh); ptr = (uint8_t *)imsg->data; len = IMSG_DATA_SIZE(imsg); memcpy(&sh, ptr, sizeof(sh)); ptr += sizeof(sh); len -= sizeof(sh); if (len > 0) url = freeme = get_string(ptr, len); else if (env->sc_ocsp_url) url = env->sc_ocsp_url; else { log_warnx("%s: no ocsp url", SPI_SH(&sh, __func__)); goto done; } if (!OCSP_parse_url(url, &host, &port, &path, &use_ssl)) { log_warnx("%s: error parsing OCSP-request-URL: %s", SPI_SH(&sh, __func__), url); goto done; } if (use_ssl) { log_warnx("%s: OCSP over SSL not supported: %s", SPI_SH(&sh, __func__), url); goto done; } if ((fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) { log_debug("%s: socket failed", SPI_SH(&sh, __func__)); goto done; } if ((oc = calloc(1, sizeof(*oc))) == NULL) { log_debug("%s: calloc failed", __func__); goto done; } bzero(&hints, sizeof(struct addrinfo)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(host, port, &hints, &res0); if (error) { log_warn("%s: getaddrinfo(%s, %s) failed", SPI_SH(&sh, __func__), host, port); goto done; } /* XXX just pick the first answer. we could loop instead */ for (res = res0; res; res = res->ai_next) if (res->ai_family == AF_INET) break; if (res == NULL) { log_debug("%s: no addr to connect to for %s:%s", SPI_SH(&sh, __func__), host, port); goto done; } oc->oc_sock.sock_fd = fd; oc->oc_sock.sock_env = env; oc->oc_sh = sh; oc->oc_path = path; oc->oc_url = strdup(url); if (oc->oc_url == NULL) { log_warn("%s: strdup failed", SPI_SH(&sh, __func__)); goto done; } path = NULL; log_debug("%s: connect(%s, %s)", __func__, host, port); if (connect(fd, res->ai_addr, res->ai_addrlen) == -1) { /* register callback for ansync connect */ if (errno == EINPROGRESS) { tv.tv_sec = OCSP_TIMEOUT; tv.tv_usec = 0; event_set(&oc->oc_sock.sock_ev, fd, EV_WRITE, ocsp_connect_cb, oc); event_add(&oc->oc_sock.sock_ev, &tv); ret = 0; } else log_warn("%s: connect(%s, %s)", SPI_SH(&oc->oc_sh, __func__), host, port); } else { ocsp_connect_finish(env, fd, oc); ret = 0; } done: if (res0) freeaddrinfo(res0); free(freeme); free(host); free(port); free(path); if (ret == -1) { ocsp_connect_finish(env, -1, oc); if (fd >= 0) close(fd); } return (ret); } /* callback triggered if connection to ocsp-responder completes/fails */ void ocsp_connect_cb(int fd, short event, void *arg) { struct ocsp_connect *oc = arg; int error, send_fd = -1; socklen_t len; if (event == EV_TIMEOUT) { log_info("%s: timeout, giving up", SPI_SH(&oc->oc_sh, __func__)); goto done; } len = sizeof(error); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) { log_warn("%s: getsockopt SOL_SOCKET SO_ERROR", SPI_SH(&oc->oc_sh, __func__)); } else if (error) { log_warnx("%s: error while connecting: %s", SPI_SH(&oc->oc_sh, __func__), strerror(error)); } else { send_fd = fd; } done: ocsp_connect_finish(oc->oc_sock.sock_env, send_fd, oc); /* if we did not send the fd, we need to close it ourself */ if (send_fd == -1) close(fd); } /* send FD+path or error back to CA process */ int ocsp_connect_finish(struct iked *env, int fd, struct ocsp_connect *oc) { struct iovec iov[2]; int iovcnt = 0, ret; iov[iovcnt].iov_base = &oc->oc_sh; iov[iovcnt].iov_len = sizeof(oc->oc_sh); iovcnt++; if (oc && fd >= 0) { /* the imsg framework will close the FD after send */ iov[iovcnt].iov_base = oc->oc_path; iov[iovcnt].iov_len = strlen(oc->oc_path); iovcnt++; ret = proc_composev_imsg(&env->sc_ps, PROC_CERT, -1, IMSG_OCSP_FD, -1, fd, iov, iovcnt); } else { if (oc) log_info("%s: connect failed for %s", SPI_SH(&oc->oc_sh, __func__), oc->oc_url ? oc->oc_url : "unknown"); else log_info("%s: connect failed", __func__); ret = proc_composev_imsg(&env->sc_ps, PROC_CERT, -1, IMSG_OCSP_FD, -1, -1, iov, iovcnt); if (fd >= 0) close(fd); } if (oc) { free(oc->oc_url); free(oc->oc_path); free(oc); } return (ret); } /* unpriv */ /* validate the certifcate stored in 'data' by querying the ocsp-responder */ int ocsp_validate_cert(struct iked *env, void *data, size_t len, struct iked_sahdr sh, uint8_t type, X509 *issuer) { struct iovec iov[2]; STACK_OF(OPENSSL_STRING) *aia; /* Authority Information Access */ struct iked_ocsp_entry *ioe; struct iked_ocsp *ocsp; OCSP_CERTID *id = NULL; char *url; BIO *rawcert = NULL; X509 *cert = NULL; int ret, iovcnt = 0; if (issuer == NULL) return (-1); if ((ioe = calloc(1, sizeof(*ioe))) == NULL) return (-1); if ((ocsp = calloc(1, sizeof(*ocsp))) == NULL) { free(ioe); return (-1); } ocsp->ocsp_env = env; ocsp->ocsp_sh = sh; ocsp->ocsp_type = type; if ((rawcert = BIO_new_mem_buf(data, len)) == NULL || (cert = d2i_X509_bio(rawcert, NULL)) == NULL || (ocsp->ocsp_cbio = BIO_new(BIO_s_socket())) == NULL || (ocsp->ocsp_req = OCSP_REQUEST_new()) == NULL || (id = OCSP_cert_to_id(NULL, cert, issuer)) == NULL || !OCSP_request_add0_id(ocsp->ocsp_req, id)) goto err; /* id is owned by and freed together with ocsp_req */ ocsp->ocsp_id = id; BIO_free(rawcert); X509_free(cert); ioe->ioe_ocsp = ocsp; TAILQ_INSERT_TAIL(&env->sc_ocsp, ioe, ioe_entry); /* pass SA header */ iov[iovcnt].iov_base = &ocsp->ocsp_sh; iov[iovcnt].iov_len = sizeof(ocsp->ocsp_sh); iovcnt++; /* pass optional ocsp-url from issuer */ if ((aia = X509_get1_ocsp(issuer)) != NULL) { url = sk_OPENSSL_STRING_value(aia, 0); log_debug("%s: aia %s", __func__, url); iov[iovcnt].iov_base = url; iov[iovcnt].iov_len = strlen(url); iovcnt++; } /* request connection to ocsp-responder */ ret = proc_composev(&env->sc_ps, PROC_PARENT, IMSG_OCSP_FD, iov, iovcnt); X509_email_free(aia); /* free stack of openssl strings */ return (ret); err: ca_sslerror(__func__); free(ioe); BIO_free(rawcert); X509_free(cert); OCSP_CERTID_free(id); ocsp_validate_finish(ocsp, 0); /* failed */ return (-1); } /* free ocsp query context */ void ocsp_free(struct iked_ocsp *ocsp) { if (ocsp != NULL) { if (ocsp->ocsp_sock != NULL) { close(ocsp->ocsp_sock->sock_fd); free(ocsp->ocsp_sock); } BIO_free_all(ocsp->ocsp_cbio); OCSP_REQ_CTX_free(ocsp->ocsp_req_ctx); OCSP_REQUEST_free(ocsp->ocsp_req); free(ocsp); } } /* we got a connection to the ocsp responder */ int ocsp_receive_fd(struct iked *env, struct imsg *imsg) { struct iked_ocsp_entry *ioe = NULL; struct iked_ocsp *ocsp = NULL, *ocsp_tmp; struct iked_socket *sock; struct iked_sahdr sh; struct timeval tv; uint8_t *ptr; char *path = NULL; size_t len; int fd, ret = -1; IMSG_SIZE_CHECK(imsg, &sh); ptr = (uint8_t *)imsg->data; len = IMSG_DATA_SIZE(imsg); memcpy(&sh, ptr, sizeof(sh)); ptr += sizeof(sh); len -= sizeof(sh); TAILQ_FOREACH(ioe, &env->sc_ocsp, ioe_entry) { ocsp_tmp = ioe->ioe_ocsp; if (memcmp(&ocsp_tmp->ocsp_sh, &sh, sizeof(sh)) == 0) break; } if (ioe == NULL) { log_debug("%s: no pending request found", __func__); if ((fd = imsg_get_fd(imsg)) != -1) /* XXX */ close(fd); return (-1); } TAILQ_REMOVE(&env->sc_ocsp, ioe, ioe_entry); ocsp = ioe->ioe_ocsp; free(ioe); if ((fd = imsg_get_fd(imsg)) == -1) goto done; if ((sock = calloc(1, sizeof(*sock))) == NULL) fatal("ocsp_receive_fd: calloc sock"); /* note that sock_addr is not set */ sock->sock_fd = fd; sock->sock_env = env; ocsp->ocsp_sock = sock; log_debug("%s: received socket fd %d", __func__, sock->sock_fd); /* fetch 'path' and 'fd' from imsg */ if ((path = get_string(ptr, len)) == NULL) goto done; BIO_set_fd(ocsp->ocsp_cbio, sock->sock_fd, BIO_NOCLOSE); if ((ocsp->ocsp_req_ctx = OCSP_sendreq_new(ocsp->ocsp_cbio, path, NULL, -1)) == NULL) goto done; if (!OCSP_REQ_CTX_set1_req(ocsp->ocsp_req_ctx, ocsp->ocsp_req)) goto done; tv.tv_sec = OCSP_TIMEOUT; tv.tv_usec = 0; event_set(&sock->sock_ev, sock->sock_fd, EV_WRITE, ocsp_callback, ocsp); event_add(&sock->sock_ev, &tv); ret = 0; done: if (ret == -1) ocsp_validate_finish(ocsp, 0); /* failed */ free(path); return (ret); } /* load a stack of x509 certificates */ STACK_OF(X509)* ocsp_load_certs(const char *file) { BIO *bio = NULL; STACK_OF(X509) *certs = NULL; STACK_OF(X509_INFO) *xis = NULL; X509_INFO *xi; int i; if ((bio = BIO_new_file(file, "r")) == NULL) { log_warn("%s: BIO_new_file failed for %s", __func__, file); return (NULL); } if ((xis = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL)) == NULL) { ca_sslerror(__func__); goto done; } if ((certs = sk_X509_new_null()) == NULL) { log_debug("%s: sk_X509_new_null failed for %s", __func__, file); goto done; } for (i = 0; i < sk_X509_INFO_num(xis); i++) { xi = sk_X509_INFO_value(xis, i); if (xi->x509) { if (!sk_X509_push(certs, xi->x509)) goto done; xi->x509 = NULL; } } done: BIO_free(bio); sk_X509_INFO_pop_free(xis, X509_INFO_free); if (sk_X509_num(certs) <= 0) { sk_X509_free(certs); certs = NULL; } return (certs); } /* read/write callback that sends the requests and reads the ocsp response */ void ocsp_callback(int fd, short event, void *arg) { struct iked_ocsp *ocsp = arg; struct iked_socket *sock = ocsp->ocsp_sock; struct timeval tv; OCSP_RESPONSE *resp = NULL; if (event == EV_TIMEOUT) { log_info("%s: timeout, giving up", SPI_SH(&ocsp->ocsp_sh, __func__)); ocsp_validate_finish(ocsp, 0); return; } /* * Only call OCSP_sendreq_nbio() if should_read/write is * either not requested or read/write can be called. */ if ((!BIO_should_read(ocsp->ocsp_cbio) || (event & EV_READ)) && (!BIO_should_write(ocsp->ocsp_cbio) || (event & EV_WRITE)) && OCSP_sendreq_nbio(&resp, ocsp->ocsp_req_ctx) != -1 ) { ocsp_parse_response(ocsp, resp); return; } if (BIO_should_read(ocsp->ocsp_cbio)) event_set(&sock->sock_ev, sock->sock_fd, EV_READ, ocsp_callback, ocsp); else if (BIO_should_write(ocsp->ocsp_cbio)) event_set(&sock->sock_ev, sock->sock_fd, EV_WRITE, ocsp_callback, ocsp); tv.tv_sec = OCSP_TIMEOUT; tv.tv_usec = 0; event_add(&sock->sock_ev, &tv); } /* parse the actual OCSP response */ void ocsp_parse_response(struct iked_ocsp *ocsp, OCSP_RESPONSE *resp) { struct iked *env = ocsp->ocsp_env; X509_STORE *store = NULL; STACK_OF(X509) *verify_other = NULL; OCSP_BASICRESP *bs = NULL; ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; const char *errstr; int reason = 0, valid = 0, verify_flags = 0; int status; if (!resp) { errstr = "error querying OCSP responder"; goto done; } status = OCSP_response_status(resp); if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { errstr = OCSP_response_status_str(status); goto done; } verify_other = ocsp_load_certs(IKED_OCSP_RESPCERT); verify_flags |= OCSP_TRUSTOTHER; if (!verify_other) { errstr = "no verify_other"; goto done; } bs = OCSP_response_get1_basic(resp); if (!bs) { errstr = "error parsing response"; goto done; } status = OCSP_check_nonce(ocsp->ocsp_req, bs); if (status <= 0) { if (status == -1) log_warnx("%s: no nonce in response", SPI_SH(&ocsp->ocsp_sh, __func__)); else { errstr = "nonce verify error"; goto done; } } store = X509_STORE_new(); status = OCSP_basic_verify(bs, verify_other, store, verify_flags); if (status < 0) status = OCSP_basic_verify(bs, NULL, store, 0); if (status <= 0) { ca_sslerror(__func__); errstr = "response verify failure"; goto done; } log_debug("%s: response verify ok", SPI_SH(&ocsp->ocsp_sh, __func__)); if (!OCSP_resp_find_status(bs, ocsp->ocsp_id, &status, &reason, &rev, &thisupd, &nextupd)) { errstr = "no status found"; goto done; } if (env->sc_ocsp_tolerate && !OCSP_check_validity(thisupd, nextupd, env->sc_ocsp_tolerate, env->sc_ocsp_maxage)) { ca_sslerror(SPI_SH(&ocsp->ocsp_sh, __func__)); errstr = "status times invalid"; goto done; } errstr = OCSP_cert_status_str(status); if (status == V_OCSP_CERTSTATUS_GOOD) { log_debug("%s: status: %s", SPI_SH(&ocsp->ocsp_sh, __func__), errstr); valid = 1; } done: if (!valid) { log_debug("%s: status: %s", __func__, errstr); } X509_STORE_free(store); sk_X509_pop_free(verify_other, X509_free); OCSP_RESPONSE_free(resp); OCSP_BASICRESP_free(bs); ocsp_validate_finish(ocsp, valid); } /* * finish the ocsp_validate_cert() RPC by sending the appropriate * message back to the IKEv2 process */ int ocsp_validate_finish(struct iked_ocsp *ocsp, int valid) { struct iked *env = ocsp->ocsp_env; struct iovec iov[2]; int iovcnt = 2, ret, cmd; iov[0].iov_base = &ocsp->ocsp_sh; iov[0].iov_len = sizeof(ocsp->ocsp_sh); iov[1].iov_base = &ocsp->ocsp_type; iov[1].iov_len = sizeof(ocsp->ocsp_type); cmd = valid ? IMSG_CERTVALID : IMSG_CERTINVALID; ret = proc_composev(&env->sc_ps, PROC_IKEV2, cmd, iov, iovcnt); ocsp_free(ocsp); return (ret); } openiked-7.4/iked/parse.y000066400000000000000000002255311477554731100154300ustar00rootroot00000000000000/* $OpenBSD: parse.y,v 1.148 2024/11/04 02:44:28 dlg Exp $ */ /* * Copyright (c) 2019-2021 Tobias Heider * Copyright (c) 2010-2013 Reyk Floeter * Copyright (c) 2004, 2005 Hans-Joerg Hoexer * Copyright (c) 2002, 2003, 2004 Henning Brauer * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. * Copyright (c) 2001 Theo de Raadt. All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ %{ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" #include "ikev2.h" #include "eap.h" TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); static struct file { TAILQ_ENTRY(file) entry; FILE *stream; char *name; size_t ungetpos; size_t ungetsize; u_char *ungetbuf; int eof_reached; int lineno; int errors; } *file, *topfile; struct file *pushfile(const char *, int); int popfile(void); int check_file_secrecy(int, const char *); int yyparse(void); int yylex(void); int yyerror(const char *, ...) __attribute__((__format__ (printf, 1, 2))) __attribute__((__nonnull__ (1))); int kw_cmp(const void *, const void *); int lookup(char *); int igetc(void); int lgetc(int); void lungetc(int); int findeol(void); TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); struct sym { TAILQ_ENTRY(sym) entry; int used; int persist; char *nam; char *val; }; int symset(const char *, const char *, int); char *symget(const char *); #define KEYSIZE_LIMIT 1024 static struct iked *env = NULL; static int debug = 0; static int rules = 0; static int passive = 0; static int decouple = 0; static int mobike = 1; static int enforcesingleikesa = 0; static int stickyaddress = 0; static int fragmentation = 0; static int vendorid = 1; static int dpd_interval = IKED_IKE_SA_ALIVE_TIMEOUT; static char *ocsp_url = NULL; static long ocsp_tolerate = 0; static long ocsp_maxage = -1; static int cert_partial_chain = 0; struct iked_transform ikev2_default_ike_transforms[] = { { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 256 }, { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 192 }, { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 128 }, { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_3DES }, { IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA2_256 }, { IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA2_384 }, { IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA2_512 }, { IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA1 }, { IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA2_256_128 }, { IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA2_384_192 }, { IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA2_512_256 }, { IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA1_96 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_CURVE25519 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_ECP_521 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_ECP_384 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_ECP_256 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_4096 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_3072 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_2048 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_1536 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_1024 }, { 0 } }; size_t ikev2_default_nike_transforms = ((sizeof(ikev2_default_ike_transforms) / sizeof(ikev2_default_ike_transforms[0])) - 1); struct iked_transform ikev2_default_ike_transforms_noauth[] = { { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_GCM_16, 128 }, { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_GCM_16, 256 }, { IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA2_256 }, { IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA2_384 }, { IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA2_512 }, { IKEV2_XFORMTYPE_PRF, IKEV2_XFORMPRF_HMAC_SHA1 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_CURVE25519 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_ECP_521 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_ECP_384 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_ECP_256 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_4096 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_3072 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_2048 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_1536 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_MODP_1024 }, { 0 } }; size_t ikev2_default_nike_transforms_noauth = ((sizeof(ikev2_default_ike_transforms_noauth) / sizeof(ikev2_default_ike_transforms_noauth[0])) - 1); struct iked_transform ikev2_default_esp_transforms[] = { { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 256 }, { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 192 }, { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_CBC, 128 }, /* XXX: Linux uses a non-standard truncated SHA256 with pfkey */ #if !defined(HAVE_LINUX_PFKEY_H) { IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA2_256_128 }, #endif { IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA2_384_192 }, { IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA2_512_256 }, { IKEV2_XFORMTYPE_INTEGR, IKEV2_XFORMAUTH_HMAC_SHA1_96 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_NONE }, #if defined(SADB_X_SAFLAGS_ESN) && !defined(HAVE_APPLE_NATT) { IKEV2_XFORMTYPE_ESN, IKEV2_XFORMESN_ESN }, #endif { IKEV2_XFORMTYPE_ESN, IKEV2_XFORMESN_NONE }, { 0 } }; size_t ikev2_default_nesp_transforms = ((sizeof(ikev2_default_esp_transforms) / sizeof(ikev2_default_esp_transforms[0])) - 1); struct iked_transform ikev2_default_esp_transforms_noauth[] = { { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_GCM_16, 128 }, { IKEV2_XFORMTYPE_ENCR, IKEV2_XFORMENCR_AES_GCM_16, 256 }, { IKEV2_XFORMTYPE_DH, IKEV2_XFORMDH_NONE }, #if defined(SADB_X_SAFLAGS_ESN) && !defined(HAVE_APPLE_NATT) { IKEV2_XFORMTYPE_ESN, IKEV2_XFORMESN_ESN }, #endif { IKEV2_XFORMTYPE_ESN, IKEV2_XFORMESN_NONE }, { 0 } }; size_t ikev2_default_nesp_transforms_noauth = ((sizeof(ikev2_default_esp_transforms_noauth) / sizeof(ikev2_default_esp_transforms_noauth[0])) - 1); const struct ipsec_xf authxfs[] = { { "hmac-md5", IKEV2_XFORMAUTH_HMAC_MD5_96, 16 }, { "hmac-sha1", IKEV2_XFORMAUTH_HMAC_SHA1_96, 20 }, { "hmac-sha2-256", IKEV2_XFORMAUTH_HMAC_SHA2_256_128, 32 }, { "hmac-sha2-384", IKEV2_XFORMAUTH_HMAC_SHA2_384_192, 48 }, { "hmac-sha2-512", IKEV2_XFORMAUTH_HMAC_SHA2_512_256, 64 }, { NULL } }; const struct ipsec_xf prfxfs[] = { { "hmac-md5", IKEV2_XFORMPRF_HMAC_MD5, 16 }, { "hmac-sha1", IKEV2_XFORMPRF_HMAC_SHA1, 20 }, { "hmac-sha2-256", IKEV2_XFORMPRF_HMAC_SHA2_256, 32 }, { "hmac-sha2-384", IKEV2_XFORMPRF_HMAC_SHA2_384, 48 }, { "hmac-sha2-512", IKEV2_XFORMPRF_HMAC_SHA2_512, 64 }, { NULL } }; const struct ipsec_xf *encxfs = NULL; const struct ipsec_xf ikeencxfs[] = { { "3des", IKEV2_XFORMENCR_3DES, 24 }, { "3des-cbc", IKEV2_XFORMENCR_3DES, 24 }, { "aes-128", IKEV2_XFORMENCR_AES_CBC, 16, 16 }, { "aes-192", IKEV2_XFORMENCR_AES_CBC, 24, 24 }, { "aes-256", IKEV2_XFORMENCR_AES_CBC, 32, 32 }, { "aes-128-gcm", IKEV2_XFORMENCR_AES_GCM_16, 16, 16, 4, 1 }, { "aes-256-gcm", IKEV2_XFORMENCR_AES_GCM_16, 32, 32, 4, 1 }, { "aes-128-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 16, 16, 4, 1 }, { "aes-256-gcm-12", IKEV2_XFORMENCR_AES_GCM_12, 32, 32, 4, 1 }, { NULL } }; const struct ipsec_xf ipsecencxfs[] = { { "3des", IKEV2_XFORMENCR_3DES, 24 }, { "3des-cbc", IKEV2_XFORMENCR_3DES, 24 }, { "aes-128", IKEV2_XFORMENCR_AES_CBC, 16, 16 }, { "aes-192", IKEV2_XFORMENCR_AES_CBC, 24, 24 }, { "aes-256", IKEV2_XFORMENCR_AES_CBC, 32, 32 }, { "aes-128-ctr", IKEV2_XFORMENCR_AES_CTR, 16, 16, 4 }, { "aes-192-ctr", IKEV2_XFORMENCR_AES_CTR, 24, 24, 4 }, { "aes-256-ctr", IKEV2_XFORMENCR_AES_CTR, 32, 32, 4 }, { "aes-128-gcm", IKEV2_XFORMENCR_AES_GCM_16, 16, 16, 4, 1 }, { "aes-192-gcm", IKEV2_XFORMENCR_AES_GCM_16, 24, 24, 4, 1 }, { "aes-256-gcm", IKEV2_XFORMENCR_AES_GCM_16, 32, 32, 4, 1 }, { "aes-128-gmac", IKEV2_XFORMENCR_NULL_AES_GMAC, 16, 16, 4, 1 }, { "aes-192-gmac", IKEV2_XFORMENCR_NULL_AES_GMAC, 24, 24, 4, 1 }, { "aes-256-gmac", IKEV2_XFORMENCR_NULL_AES_GMAC, 32, 32, 4, 1 }, { "blowfish", IKEV2_XFORMENCR_BLOWFISH, 20, 20 }, { "cast", IKEV2_XFORMENCR_CAST, 16, 16 }, { "chacha20-poly1305", IKEV2_XFORMENCR_CHACHA20_POLY1305, 32, 32, 4, 1 }, { "null", IKEV2_XFORMENCR_NULL, 0, 0 }, { NULL } }; const struct ipsec_xf groupxfs[] = { { "none", IKEV2_XFORMDH_NONE }, { "modp768", IKEV2_XFORMDH_MODP_768 }, { "grp1", IKEV2_XFORMDH_MODP_768 }, { "modp1024", IKEV2_XFORMDH_MODP_1024 }, { "grp2", IKEV2_XFORMDH_MODP_1024 }, { "modp1536", IKEV2_XFORMDH_MODP_1536 }, { "grp5", IKEV2_XFORMDH_MODP_1536 }, { "modp2048", IKEV2_XFORMDH_MODP_2048 }, { "grp14", IKEV2_XFORMDH_MODP_2048 }, { "modp3072", IKEV2_XFORMDH_MODP_3072 }, { "grp15", IKEV2_XFORMDH_MODP_3072 }, { "modp4096", IKEV2_XFORMDH_MODP_4096 }, { "grp16", IKEV2_XFORMDH_MODP_4096 }, { "modp6144", IKEV2_XFORMDH_MODP_6144 }, { "grp17", IKEV2_XFORMDH_MODP_6144 }, { "modp8192", IKEV2_XFORMDH_MODP_8192 }, { "grp18", IKEV2_XFORMDH_MODP_8192 }, { "ecp256", IKEV2_XFORMDH_ECP_256 }, { "grp19", IKEV2_XFORMDH_ECP_256 }, { "ecp384", IKEV2_XFORMDH_ECP_384 }, { "grp20", IKEV2_XFORMDH_ECP_384 }, { "ecp521", IKEV2_XFORMDH_ECP_521 }, { "grp21", IKEV2_XFORMDH_ECP_521 }, { "ecp192", IKEV2_XFORMDH_ECP_192 }, { "grp25", IKEV2_XFORMDH_ECP_192 }, { "ecp224", IKEV2_XFORMDH_ECP_224 }, { "grp26", IKEV2_XFORMDH_ECP_224 }, { "brainpool224", IKEV2_XFORMDH_BRAINPOOL_P224R1 }, { "grp27", IKEV2_XFORMDH_BRAINPOOL_P224R1 }, { "brainpool256", IKEV2_XFORMDH_BRAINPOOL_P256R1 }, { "grp28", IKEV2_XFORMDH_BRAINPOOL_P256R1 }, { "brainpool384", IKEV2_XFORMDH_BRAINPOOL_P384R1 }, { "grp29", IKEV2_XFORMDH_BRAINPOOL_P384R1 }, { "brainpool512", IKEV2_XFORMDH_BRAINPOOL_P512R1 }, { "grp30", IKEV2_XFORMDH_BRAINPOOL_P512R1 }, { "curve25519", IKEV2_XFORMDH_CURVE25519 }, { "grp31", IKEV2_XFORMDH_CURVE25519 }, { "sntrup761x25519", IKEV2_XFORMDH_X_SNTRUP761X25519 }, { NULL } }; const struct ipsec_xf esnxfs[] = { { "esn", IKEV2_XFORMESN_ESN }, { "noesn", IKEV2_XFORMESN_NONE }, { NULL } }; const struct ipsec_xf methodxfs[] = { { "none", IKEV2_AUTH_NONE }, { "rsa", IKEV2_AUTH_RSA_SIG }, { "ecdsa256", IKEV2_AUTH_ECDSA_256 }, { "ecdsa384", IKEV2_AUTH_ECDSA_384 }, { "ecdsa521", IKEV2_AUTH_ECDSA_521 }, { "rfc7427", IKEV2_AUTH_SIG }, { "signature", IKEV2_AUTH_SIG_ANY }, { NULL } }; const struct ipsec_xf saxfs[] = { { "esp", IKEV2_SAPROTO_ESP }, { "ah", IKEV2_SAPROTO_AH }, { NULL } }; const struct ipsec_xf cpxfs[] = { { "address", IKEV2_CFG_INTERNAL_IP4_ADDRESS, AF_INET }, { "netmask", IKEV2_CFG_INTERNAL_IP4_NETMASK, AF_INET }, { "name-server", IKEV2_CFG_INTERNAL_IP4_DNS, AF_INET }, { "netbios-server", IKEV2_CFG_INTERNAL_IP4_NBNS, AF_INET }, { "dhcp-server", IKEV2_CFG_INTERNAL_IP4_DHCP, AF_INET }, { "address", IKEV2_CFG_INTERNAL_IP6_ADDRESS, AF_INET6 }, { "name-server", IKEV2_CFG_INTERNAL_IP6_DNS, AF_INET6 }, { "netbios-server", IKEV2_CFG_INTERNAL_IP6_NBNS, AF_INET6 }, { "dhcp-server", IKEV2_CFG_INTERNAL_IP6_DHCP, AF_INET6 }, { "protected-subnet", IKEV2_CFG_INTERNAL_IP4_SUBNET, AF_INET }, { "protected-subnet", IKEV2_CFG_INTERNAL_IP6_SUBNET, AF_INET6 }, { "access-server", IKEV2_CFG_INTERNAL_IP4_SERVER, AF_INET }, { "access-server", IKEV2_CFG_INTERNAL_IP6_SERVER, AF_INET6 }, { NULL } }; const struct iked_lifetime deflifetime = { IKED_LIFETIME_BYTES, IKED_LIFETIME_SECONDS }; #define IPSEC_ADDR_ANY (0x1) #define IPSEC_ADDR_DYNAMIC (0x2) struct ipsec_addr_wrap { struct sockaddr_storage address; uint8_t mask; int netaddress; sa_family_t af; unsigned int type; unsigned int action; uint16_t port; char *name; struct ipsec_addr_wrap *next; struct ipsec_addr_wrap *tail; struct ipsec_addr_wrap *srcnat; }; struct ipsec_hosts { struct ipsec_addr_wrap *src; struct ipsec_addr_wrap *dst; }; struct ipsec_filters { char *tag; unsigned int tap; }; void copy_sockaddrtoipa(struct ipsec_addr_wrap *, struct sockaddr *); struct ipsec_addr_wrap *host(const char *); struct ipsec_addr_wrap *host_ip(const char *, int); struct ipsec_addr_wrap *host_dns(const char *, int); struct ipsec_addr_wrap *host_if(const char *, int); struct ipsec_addr_wrap *host_any(void); struct ipsec_addr_wrap *host_dynamic(void); void ifa_load(void); int ifa_exists(const char *); struct ipsec_addr_wrap *ifa_lookup(const char *ifa_name); struct ipsec_addr_wrap *ifa_grouplookup(const char *); void set_ipmask(struct ipsec_addr_wrap *, int); const struct ipsec_xf *parse_xf(const char *, unsigned int, const struct ipsec_xf *); void copy_transforms(unsigned int, const struct ipsec_xf **, unsigned int, struct iked_transform **, unsigned int *, struct iked_transform *, size_t); int create_ike(char *, int, struct ipsec_addr_wrap *, int, struct ipsec_hosts *, struct ipsec_hosts *, struct ipsec_mode *, struct ipsec_mode *, uint8_t, unsigned int, char *, char *, uint32_t, struct iked_lifetime *, struct iked_auth *, struct ipsec_filters *, struct ipsec_addr_wrap *, char *); int create_user(const char *, const char *); int get_id_type(char *); uint8_t x2i(unsigned char *); int parsekey(unsigned char *, size_t, struct iked_auth *); int parsekeyfile(char *, struct iked_auth *); void iaw_free(struct ipsec_addr_wrap *); static int create_flow(struct iked_policy *pol, int, struct ipsec_addr_wrap *ipa, struct ipsec_addr_wrap *ipb); static int expand_flows(struct iked_policy *, int, struct ipsec_addr_wrap *, struct ipsec_addr_wrap *); static struct ipsec_addr_wrap * expand_keyword(struct ipsec_addr_wrap *); struct ipsec_transforms *ipsec_transforms; struct ipsec_filters *ipsec_filters; struct ipsec_mode *ipsec_mode; /* interface lookup routintes */ struct ipsec_addr_wrap *iftab; typedef struct { union { int64_t number; unsigned int ikemode; uint8_t dir; uint8_t satype; char *string; uint16_t port; struct ipsec_hosts *hosts; struct ipsec_hosts peers; struct ipsec_addr_wrap *anyhost; struct ipsec_addr_wrap *host; struct ipsec_addr_wrap *cfg; struct ipsec_addr_wrap *proto; struct { char *srcid; char *dstid; } ids; char *id; uint8_t type; struct iked_lifetime lifetime; struct iked_auth ikeauth; struct iked_auth ikekey; struct ipsec_transforms *transforms; struct ipsec_filters *filters; struct ipsec_mode *mode; } v; int lineno; } YYSTYPE; %} %token FROM ESP AH IN PEER ON OUT TO SRCID DSTID PSK PORT %token FILENAME AUTHXF PRFXF ENCXF ERROR IKEV2 IKESA CHILDSA ESN NOESN %token PASSIVE ACTIVE ANY TAG TAP PROTO LOCAL GROUP NAME CONFIG EAP USER %token IKEV1 FLOW SA TCPMD5 TUNNEL TRANSPORT COUPLE DECOUPLE SET %token INCLUDE LIFETIME BYTES INET INET6 QUICK SKIP DEFAULT %token IPCOMP OCSP IKELIFETIME MOBIKE NOMOBIKE RDOMAIN %token FRAGMENTATION NOFRAGMENTATION DPD_CHECK_INTERVAL %token ENFORCESINGLEIKESA NOENFORCESINGLEIKESA %token STICKYADDRESS NOSTICKYADDRESS %token VENDORID NOVENDORID %token TOLERATE MAXAGE DYNAMIC %token CERTPARTIALCHAIN %token REQUEST IFACE %token NATT %token STRING %token NUMBER %type string %type satype %type proto proto_list protoval %type hosts hosts_list %type port %type portval af rdomain %type peers %type anyhost %type host host_spec %type ids %type id %type transforms %type filters %type ikeflags %type ikematch ikemode ipcomp tmode natt_force %type ikeauth %type keyspec %type ike_sas child_sas %type lifetime %type byte_spec time_spec ikelifetime %type name iface %type cfg ikecfg ikecfgvals %type transform_esn %% grammar : /* empty */ | grammar include '\n' | grammar '\n' | grammar set '\n' | grammar user '\n' | grammar ikev2rule '\n' | grammar varset '\n' | grammar otherrule skipline '\n' | grammar error '\n' { file->errors++; } ; comma : ',' | /* empty */ ; include : INCLUDE STRING { struct file *nfile; if ((nfile = pushfile($2, 1)) == NULL) { yyerror("failed to include file %s", $2); free($2); YYERROR; } free($2); file = nfile; lungetc('\n'); } ; set : SET ACTIVE { passive = 0; } | SET PASSIVE { passive = 1; } | SET COUPLE { decouple = 0; } | SET DECOUPLE { decouple = 1; } | SET FRAGMENTATION { fragmentation = 1; } | SET NOFRAGMENTATION { fragmentation = 0; } | SET MOBIKE { mobike = 1; } | SET NOMOBIKE { mobike = 0; } | SET VENDORID { vendorid = 1; } | SET NOVENDORID { vendorid = 0; } | SET ENFORCESINGLEIKESA { enforcesingleikesa = 1; } | SET NOENFORCESINGLEIKESA { enforcesingleikesa = 0; } | SET STICKYADDRESS { stickyaddress = 1; } | SET NOSTICKYADDRESS { stickyaddress = 0; } | SET OCSP STRING { ocsp_url = $3; } | SET OCSP STRING TOLERATE time_spec { ocsp_url = $3; ocsp_tolerate = $5; } | SET OCSP STRING TOLERATE time_spec MAXAGE time_spec { ocsp_url = $3; ocsp_tolerate = $5; ocsp_maxage = $7; } | SET CERTPARTIALCHAIN { cert_partial_chain = 1; } | SET DPD_CHECK_INTERVAL NUMBER { if ($3 < 0) { yyerror("timeout outside range"); YYERROR; } dpd_interval = $3; } ; user : USER STRING STRING { if (create_user($2, $3) == -1) YYERROR; free($2); freezero($3, strlen($3)); } ; ikev2rule : IKEV2 name ikeflags satype af proto rdomain hosts_list peers ike_sas child_sas ids ikelifetime lifetime ikeauth ikecfg iface filters { if (create_ike($2, $5, $6, $7, $8, &$9, $10, $11, $4, $3, $12.srcid, $12.dstid, $13, &$14, &$15, $18, $16, $17) == -1) { yyerror("create_ike failed"); YYERROR; } } ; ikecfg : /* empty */ { $$ = NULL; } | ikecfgvals { $$ = $1; } ; ikecfgvals : cfg { $$ = $1; } | ikecfgvals cfg { if ($2 == NULL) $$ = $1; else if ($1 == NULL) $$ = $2; else { $1->tail->next = $2; $1->tail = $2->tail; $$ = $1; } } ; cfg : CONFIG STRING host_spec { const struct ipsec_xf *xf; if ((xf = parse_xf($2, $3->af, cpxfs)) == NULL) { yyerror("not a valid ikecfg option"); free($2); free($3); YYERROR; } free($2); $$ = $3; $$->type = xf->id; $$->action = IKEV2_CP_REPLY; /* XXX */ } | REQUEST STRING anyhost { const struct ipsec_xf *xf; if ((xf = parse_xf($2, $3->af, cpxfs)) == NULL) { yyerror("not a valid ikecfg option"); free($2); free($3); YYERROR; } free($2); $$ = $3; $$->type = xf->id; $$->action = IKEV2_CP_REQUEST; /* XXX */ } ; name : /* empty */ { $$ = NULL; } | STRING { $$ = $1; } satype : /* empty */ { $$ = IKEV2_SAPROTO_ESP; } | ESP { $$ = IKEV2_SAPROTO_ESP; } | AH { $$ = IKEV2_SAPROTO_AH; } ; af : /* empty */ { $$ = AF_UNSPEC; } | INET { $$ = AF_INET; } | INET6 { $$ = AF_INET6; } ; proto : /* empty */ { $$ = NULL; } | PROTO protoval { $$ = $2; } | PROTO '{' proto_list '}' { $$ = $3; } ; proto_list : protoval { $$ = $1; } | proto_list comma protoval { if ($3 == NULL) $$ = $1; else if ($1 == NULL) $$ = $3; else { $1->tail->next = $3; $1->tail = $3->tail; $$ = $1; } } ; protoval : STRING { struct protoent *p; p = getprotobyname($1); if (p == NULL) { yyerror("unknown protocol: %s", $1); YYERROR; } if (($$ = calloc(1, sizeof(*$$))) == NULL) err(1, "protoval: calloc"); $$->type = p->p_proto; $$->tail = $$; free($1); } | NUMBER { if ($1 > 255 || $1 < 0) { yyerror("protocol outside range"); YYERROR; } if (($$ = calloc(1, sizeof(*$$))) == NULL) err(1, "protoval: calloc"); $$->type = $1; $$->tail = $$; } ; rdomain : /* empty */ { $$ = -1; } | RDOMAIN NUMBER { #ifdef SADB_X_EXT_RDOMAIN if ($2 > 255 || $2 < 0) { yyerror("rdomain outside range"); YYERROR; } $$ = $2; #else yyerror("'rdomain' is not supported on this platform"); YYERROR; #endif } hosts_list : hosts { $$ = $1; } | hosts_list comma hosts { if ($3 == NULL) $$ = $1; else if ($1 == NULL) $$ = $3; else { $1->src->tail->next = $3->src; $1->src->tail = $3->src->tail; $1->dst->tail->next = $3->dst; $1->dst->tail = $3->dst->tail; $$ = $1; free($3); } } ; hosts : FROM host port TO host port { struct ipsec_addr_wrap *ipa; for (ipa = $5; ipa; ipa = ipa->next) { if (ipa->srcnat) { yyerror("no flow NAT support for" " destination network: %s", ipa->name); YYERROR; } } if (($$ = calloc(1, sizeof(*$$))) == NULL) err(1, "hosts: calloc"); $$->src = $2; $$->src->port = $3; $$->dst = $5; $$->dst->port = $6; } | TO host port FROM host port { struct ipsec_addr_wrap *ipa; for (ipa = $2; ipa; ipa = ipa->next) { if (ipa->srcnat) { yyerror("no flow NAT support for" " destination network: %s", ipa->name); YYERROR; } } if (($$ = calloc(1, sizeof(*$$))) == NULL) err(1, "hosts: calloc"); $$->src = $5; $$->src->port = $6; $$->dst = $2; $$->dst->port = $3; } ; port : /* empty */ { $$ = 0; } | PORT portval { $$ = $2; } ; portval : STRING { struct servent *s; if ((s = getservbyname($1, "tcp")) != NULL || (s = getservbyname($1, "udp")) != NULL) { $$ = s->s_port; } else { yyerror("unknown port: %s", $1); YYERROR; } free($1); } | NUMBER { if ($1 > USHRT_MAX || $1 < 0) { yyerror("port outside range"); YYERROR; } $$ = htons($1); } ; peers : /* empty */ { $$.dst = NULL; $$.src = NULL; } | PEER anyhost LOCAL anyhost { $$.dst = $2; $$.src = $4; } | LOCAL anyhost PEER anyhost { $$.dst = $4; $$.src = $2; } | PEER anyhost { $$.dst = $2; $$.src = NULL; } | LOCAL anyhost { $$.dst = NULL; $$.src = $2; } ; anyhost : host_spec { $$ = $1; } | ANY { $$ = host_any(); } host_spec : STRING { if (($$ = host($1)) == NULL) { free($1); yyerror("could not parse host specification"); YYERROR; } free($1); } | STRING '/' NUMBER { char *buf; if (asprintf(&buf, "%s/%lld", $1, (long long)$3) == -1) err(1, "host: asprintf"); free($1); if (($$ = host(buf)) == NULL) { free(buf); yyerror("could not parse host specification"); YYERROR; } free(buf); } ; host : host_spec { $$ = $1; } | host_spec '(' host_spec ')' { if (($1->af != AF_UNSPEC) && ($3->af != AF_UNSPEC) && ($3->af != $1->af)) { yyerror("Flow NAT address family mismatch"); YYERROR; } $$ = $1; $$->srcnat = $3; } | ANY { $$ = host_any(); } | DYNAMIC { $$ = host_dynamic(); } ; ids : /* empty */ { $$.srcid = NULL; $$.dstid = NULL; } | SRCID id DSTID id { $$.srcid = $2; $$.dstid = $4; } | SRCID id { $$.srcid = $2; $$.dstid = NULL; } | DSTID id { $$.srcid = NULL; $$.dstid = $2; } ; id : STRING { $$ = $1; } ; transforms : { if ((ipsec_transforms = calloc(1, sizeof(struct ipsec_transforms))) == NULL) err(1, "transforms: calloc"); } transforms_l { $$ = ipsec_transforms; } | /* empty */ { $$ = NULL; } ; transforms_l : transforms_l transform | transform ; transform : AUTHXF STRING { const struct ipsec_xf **xfs = ipsec_transforms->authxf; size_t nxfs = ipsec_transforms->nauthxf; xfs = recallocarray(xfs, nxfs, nxfs + 1, sizeof(struct ipsec_xf *)); if (xfs == NULL) err(1, "transform: recallocarray"); if ((xfs[nxfs] = parse_xf($2, 0, authxfs)) == NULL) { yyerror("%s not a valid transform", $2); YYERROR; } free($2); ipsec_transforms->authxf = xfs; ipsec_transforms->nauthxf++; } | ENCXF STRING { const struct ipsec_xf **xfs = ipsec_transforms->encxf; size_t nxfs = ipsec_transforms->nencxf; xfs = recallocarray(xfs, nxfs, nxfs + 1, sizeof(struct ipsec_xf *)); if (xfs == NULL) err(1, "transform: recallocarray"); if ((xfs[nxfs] = parse_xf($2, 0, encxfs)) == NULL) { yyerror("%s not a valid transform", $2); YYERROR; } free($2); ipsec_transforms->encxf = xfs; ipsec_transforms->nencxf++; } | PRFXF STRING { const struct ipsec_xf **xfs = ipsec_transforms->prfxf; size_t nxfs = ipsec_transforms->nprfxf; xfs = recallocarray(xfs, nxfs, nxfs + 1, sizeof(struct ipsec_xf *)); if (xfs == NULL) err(1, "transform: recallocarray"); if ((xfs[nxfs] = parse_xf($2, 0, prfxfs)) == NULL) { yyerror("%s not a valid transform", $2); YYERROR; } free($2); ipsec_transforms->prfxf = xfs; ipsec_transforms->nprfxf++; } | GROUP STRING { const struct ipsec_xf **xfs = ipsec_transforms->groupxf; size_t nxfs = ipsec_transforms->ngroupxf; xfs = recallocarray(xfs, nxfs, nxfs + 1, sizeof(struct ipsec_xf *)); if (xfs == NULL) err(1, "transform: recallocarray"); if ((xfs[nxfs] = parse_xf($2, 0, groupxfs)) == NULL) { yyerror("%s not a valid transform", $2); YYERROR; } free($2); ipsec_transforms->groupxf = xfs; ipsec_transforms->ngroupxf++; } | transform_esn { const struct ipsec_xf **xfs = ipsec_transforms->esnxf; size_t nxfs = ipsec_transforms->nesnxf; xfs = recallocarray(xfs, nxfs, nxfs + 1, sizeof(struct ipsec_xf *)); if (xfs == NULL) err(1, "transform: recallocarray"); if ((xfs[nxfs] = parse_xf($1, 0, esnxfs)) == NULL) { yyerror("%s not a valid transform", $1); YYERROR; } ipsec_transforms->esnxf = xfs; ipsec_transforms->nesnxf++; } ; transform_esn : ESN { $$ = "esn"; } | NOESN { $$ = "noesn"; } ; ike_sas : { if ((ipsec_mode = calloc(1, sizeof(struct ipsec_mode))) == NULL) err(1, "ike_sas: calloc"); } ike_sas_l { $$ = ipsec_mode; } | /* empty */ { $$ = NULL; } ; ike_sas_l : ike_sas_l ike_sa | ike_sa ; ike_sa : IKESA { if ((ipsec_mode->xfs = recallocarray(ipsec_mode->xfs, ipsec_mode->nxfs, ipsec_mode->nxfs + 1, sizeof(struct ipsec_transforms *))) == NULL) err(1, "ike_sa: recallocarray"); ipsec_mode->nxfs++; encxfs = ikeencxfs; } transforms { ipsec_mode->xfs[ipsec_mode->nxfs - 1] = $3; } ; child_sas : { if ((ipsec_mode = calloc(1, sizeof(struct ipsec_mode))) == NULL) err(1, "child_sas: calloc"); } child_sas_l { $$ = ipsec_mode; } | /* empty */ { $$ = NULL; } ; child_sas_l : child_sas_l child_sa | child_sa ; child_sa : CHILDSA { if ((ipsec_mode->xfs = recallocarray(ipsec_mode->xfs, ipsec_mode->nxfs, ipsec_mode->nxfs + 1, sizeof(struct ipsec_transforms *))) == NULL) err(1, "child_sa: recallocarray"); ipsec_mode->nxfs++; encxfs = ipsecencxfs; } transforms { ipsec_mode->xfs[ipsec_mode->nxfs - 1] = $3; } ; ikeflags : ikematch ikemode ipcomp tmode natt_force { $$ = $1 | $2 | $3 | $4 | $5; } ; ikematch : /* empty */ { $$ = 0; } | QUICK { $$ = IKED_POLICY_QUICK; } | SKIP { $$ = IKED_POLICY_SKIP; } | DEFAULT { $$ = IKED_POLICY_DEFAULT; } ; ikemode : /* empty */ { $$ = IKED_POLICY_PASSIVE; } | PASSIVE { $$ = IKED_POLICY_PASSIVE; } | ACTIVE { $$ = IKED_POLICY_ACTIVE; } ; ipcomp : /* empty */ { $$ = 0; } | IPCOMP { $$ = IKED_POLICY_IPCOMP; } ; tmode : /* empty */ { $$ = 0; } | TUNNEL { $$ = 0; } | TRANSPORT { $$ = IKED_POLICY_TRANSPORT; } ; natt_force : /* empty */ { $$ = 0; } | NATT { $$ = IKED_POLICY_NATT_FORCE; } ; ikeauth : /* empty */ { $$.auth_method = IKEV2_AUTH_SIG_ANY; /* default */ $$.auth_eap = 0; $$.auth_length = 0; } | PSK keyspec { memcpy(&$$, &$2, sizeof($$)); $$.auth_method = IKEV2_AUTH_SHARED_KEY_MIC; $$.auth_eap = 0; explicit_bzero(&$2, sizeof($2)); } | EAP STRING { unsigned int i; for (i = 0; i < strlen($2); i++) if ($2[i] == '-') $2[i] = '_'; if (strcasecmp("mschap_v2", $2) != 0) { yyerror("unsupported EAP method: %s", $2); free($2); YYERROR; } free($2); $$.auth_method = IKEV2_AUTH_SIG_ANY; $$.auth_eap = EAP_TYPE_MSCHAP_V2; $$.auth_length = 0; } | STRING { const struct ipsec_xf *xf; if ((xf = parse_xf($1, 0, methodxfs)) == NULL || xf->id == IKEV2_AUTH_NONE) { yyerror("not a valid authentication mode"); free($1); YYERROR; } free($1); $$.auth_method = xf->id; $$.auth_eap = 0; $$.auth_length = 0; } ; byte_spec : NUMBER { $$ = $1; } | STRING { uint64_t bytes = 0; char unit = 0; if (sscanf($1, "%llu%c", (long long unsigned *)&bytes, &unit) != 2) { yyerror("invalid byte specification: %s", $1); YYERROR; } free($1); switch (toupper((unsigned char)unit)) { case 'K': bytes *= 1024; break; case 'M': bytes *= 1024 * 1024; break; case 'G': bytes *= 1024 * 1024 * 1024; break; default: yyerror("invalid byte unit"); YYERROR; } $$ = bytes; } ; time_spec : NUMBER { $$ = $1; } | STRING { uint64_t seconds = 0; char unit = 0; if (sscanf($1, "%llu%c", (long long unsigned *)&seconds, &unit) != 2) { yyerror("invalid time specification: %s", $1); YYERROR; } free($1); switch (tolower((unsigned char)unit)) { case 'm': seconds *= 60; break; case 'h': seconds *= 60 * 60; break; default: yyerror("invalid time unit"); YYERROR; } $$ = seconds; } ; lifetime : /* empty */ { $$ = deflifetime; } | LIFETIME time_spec { $$.lt_seconds = $2; $$.lt_bytes = deflifetime.lt_bytes; } | LIFETIME time_spec BYTES byte_spec { $$.lt_seconds = $2; $$.lt_bytes = $4; } ; ikelifetime : /* empty */ { $$ = 0; } | IKELIFETIME time_spec { $$ = $2; } keyspec : STRING { uint8_t *hex; bzero(&$$, sizeof($$)); hex = $1; if (strncmp(hex, "0x", 2) == 0) { hex += 2; if (parsekey(hex, strlen(hex), &$$) != 0) { free($1); YYERROR; } } else { if (strlen($1) > sizeof($$.auth_data)) { yyerror("psk too long"); free($1); YYERROR; } strlcpy($$.auth_data, $1, sizeof($$.auth_data)); $$.auth_length = strlen($1); } freezero($1, strlen($1)); } | FILENAME STRING { if (parsekeyfile($2, &$$) != 0) { free($2); YYERROR; } free($2); } ; filters : { if ((ipsec_filters = calloc(1, sizeof(struct ipsec_filters))) == NULL) err(1, "filters: calloc"); } filters_l { $$ = ipsec_filters; } | /* empty */ { $$ = NULL; } ; filters_l : filters_l filter | filter ; filter : TAG STRING { ipsec_filters->tag = $2; } | TAP STRING { const char *errstr = NULL; size_t len; len = strcspn($2, "0123456789"); if (strlen("enc") != len || strncmp("enc", $2, len) != 0) { yyerror("invalid tap interface name: %s", $2); free($2); YYERROR; } ipsec_filters->tap = strtonum($2 + len, 0, UINT_MAX, &errstr); free($2); if (errstr != NULL) { yyerror("invalid tap interface unit: %s", errstr); YYERROR; } } ; iface : { $$ = NULL; } | IFACE STRING { $$ = $2; } string : string STRING { if (asprintf(&$$, "%s %s", $1, $2) == -1) err(1, "string: asprintf"); free($1); free($2); } | STRING ; varset : STRING '=' string { char *s = $1; log_debug("%s = \"%s\"\n", $1, $3); while (*s++) { if (isspace((unsigned char)*s)) { yyerror("macro name cannot contain " "whitespace"); free($1); free($3); YYERROR; } } if (symset($1, $3, 0) == -1) err(1, "cannot store variable"); free($1); free($3); } ; /* * ignore IKEv1/manual keying rules in ipsec.conf */ otherrule : IKEV1 | sarule | FLOW | TCPMD5 ; /* manual keying SAs might start with the following keywords */ sarule : SA | FROM | TO | TUNNEL | TRANSPORT ; /* ignore everything to the end of the line */ skipline : { int c; while ((c = lgetc(0)) != '\n' && c != EOF) ; /* nothing */ if (c == '\n') lungetc(c); } ; %% struct keywords { const char *k_name; int k_val; }; void copy_sockaddrtoipa(struct ipsec_addr_wrap *ipa, struct sockaddr *sa) { if (sa->sa_family == AF_INET6) memcpy(&ipa->address, sa, sizeof(struct sockaddr_in6)); else if (sa->sa_family == AF_INET) memcpy(&ipa->address, sa, sizeof(struct sockaddr_in)); else warnx("unhandled af %d", sa->sa_family); } int yyerror(const char *fmt, ...) { va_list ap; file->errors++; va_start(ap, fmt); fprintf(stderr, "%s: %d: ", file->name, yylval.lineno); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); return (0); } int kw_cmp(const void *k, const void *e) { return (strcmp(k, ((const struct keywords *)e)->k_name)); } int lookup(char *s) { /* this has to be sorted always */ static const struct keywords keywords[] = { { "active", ACTIVE }, { "ah", AH }, { "any", ANY }, { "auth", AUTHXF }, { "bytes", BYTES }, { "cert_partial_chain", CERTPARTIALCHAIN }, { "childsa", CHILDSA }, { "config", CONFIG }, { "couple", COUPLE }, { "decouple", DECOUPLE }, { "default", DEFAULT }, { "dpd_check_interval", DPD_CHECK_INTERVAL }, { "dstid", DSTID }, { "dynamic", DYNAMIC }, { "eap", EAP }, { "enc", ENCXF }, { "enforcesingleikesa", ENFORCESINGLEIKESA }, { "esn", ESN }, { "esp", ESP }, { "file", FILENAME }, { "flow", FLOW }, { "fragmentation", FRAGMENTATION }, { "from", FROM }, { "group", GROUP }, { "iface", IFACE }, { "ike", IKEV1 }, { "ikelifetime", IKELIFETIME }, { "ikesa", IKESA }, { "ikev2", IKEV2 }, { "include", INCLUDE }, { "inet", INET }, { "inet6", INET6 }, { "ipcomp", IPCOMP }, { "lifetime", LIFETIME }, { "local", LOCAL }, { "maxage", MAXAGE }, { "mobike", MOBIKE }, { "name", NAME }, { "natt", NATT }, { "noenforcesingleikesa", NOENFORCESINGLEIKESA }, { "noesn", NOESN }, { "nofragmentation", NOFRAGMENTATION }, { "nomobike", NOMOBIKE }, { "nostickyaddress", NOSTICKYADDRESS }, { "novendorid", NOVENDORID }, { "ocsp", OCSP }, { "passive", PASSIVE }, { "peer", PEER }, { "port", PORT }, { "prf", PRFXF }, { "proto", PROTO }, { "psk", PSK }, { "quick", QUICK }, { "rdomain", RDOMAIN }, { "request", REQUEST }, { "sa", SA }, { "set", SET }, { "skip", SKIP }, { "srcid", SRCID }, { "stickyaddress", STICKYADDRESS }, { "tag", TAG }, { "tap", TAP }, { "tcpmd5", TCPMD5 }, { "to", TO }, { "tolerate", TOLERATE }, { "transport", TRANSPORT }, { "tunnel", TUNNEL }, { "user", USER }, { "vendorid", VENDORID } }; const struct keywords *p; p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), sizeof(keywords[0]), kw_cmp); if (p) { if (debug > 1) fprintf(stderr, "%s: %d\n", s, p->k_val); return (p->k_val); } else { if (debug > 1) fprintf(stderr, "string: %s\n", s); return (STRING); } } #define START_EXPAND 1 #define DONE_EXPAND 2 static int expanding; int igetc(void) { int c; while (1) { if (file->ungetpos > 0) c = file->ungetbuf[--file->ungetpos]; else c = getc(file->stream); if (c == START_EXPAND) expanding = 1; else if (c == DONE_EXPAND) expanding = 0; else break; } return (c); } int lgetc(int quotec) { int c, next; if (quotec) { if ((c = igetc()) == EOF) { yyerror("reached end of file while parsing " "quoted string"); if (file == topfile || popfile() == EOF) return (EOF); return (quotec); } return (c); } while ((c = igetc()) == '\\') { next = igetc(); if (next != '\n') { c = next; break; } yylval.lineno = file->lineno; file->lineno++; } while (c == EOF) { /* * Fake EOL when hit EOF for the first time. This gets line * count right if last line in included file is syntactically * invalid and has no newline. */ if (file->eof_reached == 0) { file->eof_reached = 1; return ('\n'); } while (c == EOF) { if (file == topfile || popfile() == EOF) return (EOF); c = igetc(); } } return (c); } void lungetc(int c) { if (c == EOF) return; if (file->ungetpos >= file->ungetsize) { void *p = reallocarray(file->ungetbuf, file->ungetsize, 2); if (p == NULL) err(1, "lungetc"); file->ungetbuf = p; file->ungetsize *= 2; } file->ungetbuf[file->ungetpos++] = c; } int findeol(void) { int c; /* skip to either EOF or the first real EOL */ while (1) { c = lgetc(0); if (c == '\n') { file->lineno++; break; } if (c == EOF) break; } return (ERROR); } int yylex(void) { char buf[8096]; char *p, *val; int quotec, next, c; int token; top: p = buf; while ((c = lgetc(0)) == ' ' || c == '\t') ; /* nothing */ yylval.lineno = file->lineno; if (c == '#') while ((c = lgetc(0)) != '\n' && c != EOF) ; /* nothing */ if (c == '$' && !expanding) { while (1) { if ((c = lgetc(0)) == EOF) return (0); if (p + 1 >= buf + sizeof(buf) - 1) { yyerror("string too long"); return (findeol()); } if (isalnum(c) || c == '_') { *p++ = c; continue; } *p = '\0'; lungetc(c); break; } val = symget(buf); if (val == NULL) { yyerror("macro '%s' not defined", buf); return (findeol()); } p = val + strlen(val) - 1; lungetc(DONE_EXPAND); while (p >= val) { lungetc((unsigned char)*p); p--; } lungetc(START_EXPAND); goto top; } switch (c) { case '\'': case '"': quotec = c; while (1) { if ((c = lgetc(quotec)) == EOF) return (0); if (c == '\n') { file->lineno++; continue; } else if (c == '\\') { if ((next = lgetc(quotec)) == EOF) return (0); if (next == quotec || next == ' ' || next == '\t') c = next; else if (next == '\n') { file->lineno++; continue; } else lungetc(next); } else if (c == quotec) { *p = '\0'; break; } else if (c == '\0') { yyerror("syntax error"); return (findeol()); } if (p + 1 >= buf + sizeof(buf) - 1) { yyerror("string too long"); return (findeol()); } *p++ = c; } yylval.v.string = strdup(buf); if (yylval.v.string == NULL) err(1, "%s", __func__); return (STRING); } #define allowed_to_end_number(x) \ (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') if (c == '-' || isdigit(c)) { do { *p++ = c; if ((size_t)(p-buf) >= sizeof(buf)) { yyerror("string too long"); return (findeol()); } } while ((c = lgetc(0)) != EOF && isdigit(c)); lungetc(c); if (p == buf + 1 && buf[0] == '-') goto nodigits; if (c == EOF || allowed_to_end_number(c)) { const char *errstr = NULL; *p = '\0'; yylval.v.number = strtonum(buf, LLONG_MIN, LLONG_MAX, &errstr); if (errstr) { yyerror("\"%s\" invalid number: %s", buf, errstr); return (findeol()); } return (NUMBER); } else { nodigits: while (p > buf + 1) lungetc((unsigned char)*--p); c = (unsigned char)*--p; if (c == '-') return (c); } } #define allowed_in_string(x) \ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ x != '{' && x != '}' && x != '<' && x != '>' && \ x != '!' && x != '=' && x != '/' && x != '#' && \ x != ',')) if (isalnum(c) || c == ':' || c == '_' || c == '*') { do { *p++ = c; if ((size_t)(p-buf) >= sizeof(buf)) { yyerror("string too long"); return (findeol()); } } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); lungetc(c); *p = '\0'; if ((token = lookup(buf)) == STRING) if ((yylval.v.string = strdup(buf)) == NULL) err(1, "%s", __func__); return (token); } if (c == '\n') { yylval.lineno = file->lineno; file->lineno++; } if (c == EOF) return (0); return (c); } int check_file_secrecy(int fd, const char *fname) { struct stat st; if (fstat(fd, &st)) { warn("cannot stat %s", fname); return (-1); } if (st.st_uid != 0 && st.st_uid != getuid()) { warnx("%s: owner not root or current user", fname); return (-1); } if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { warnx("%s: group writable or world read/writable", fname); return (-1); } return (0); } struct file * pushfile(const char *name, int secret) { struct file *nfile; if ((nfile = calloc(1, sizeof(struct file))) == NULL) { warn("%s", __func__); return (NULL); } if ((nfile->name = strdup(name)) == NULL) { warn("%s", __func__); free(nfile); return (NULL); } if (TAILQ_FIRST(&files) == NULL && strcmp(nfile->name, "-") == 0) { nfile->stream = stdin; free(nfile->name); if ((nfile->name = strdup("stdin")) == NULL) { warn("%s", __func__); free(nfile); return (NULL); } } else if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { warn("%s: %s", __func__, nfile->name); free(nfile->name); free(nfile); return (NULL); } else if (secret && check_file_secrecy(fileno(nfile->stream), nfile->name)) { fclose(nfile->stream); free(nfile->name); free(nfile); return (NULL); } nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0; nfile->ungetsize = 16; nfile->ungetbuf = malloc(nfile->ungetsize); if (nfile->ungetbuf == NULL) { warn("%s", __func__); fclose(nfile->stream); free(nfile->name); free(nfile); return (NULL); } TAILQ_INSERT_TAIL(&files, nfile, entry); return (nfile); } int popfile(void) { struct file *prev; if ((prev = TAILQ_PREV(file, files, entry)) != NULL) prev->errors += file->errors; TAILQ_REMOVE(&files, file, entry); fclose(file->stream); free(file->name); free(file->ungetbuf); free(file); file = prev; return (file ? 0 : EOF); } int parse_config(const char *filename, struct iked *x_env) { struct sym *sym; int errors = 0; env = x_env; rules = 0; if ((file = pushfile(filename, 1)) == NULL) return (-1); topfile = file; free(ocsp_url); mobike = 1; enforcesingleikesa = stickyaddress = 0; cert_partial_chain = decouple = passive = 0; ocsp_tolerate = 0; ocsp_url = NULL; ocsp_maxage = -1; fragmentation = 0; dpd_interval = IKED_IKE_SA_ALIVE_TIMEOUT; decouple = passive = 0; ocsp_url = NULL; if (env->sc_opts & IKED_OPT_PASSIVE) passive = 1; yyparse(); errors = file->errors; popfile(); env->sc_passive = passive ? 1 : 0; env->sc_decoupled = decouple ? 1 : 0; env->sc_mobike = mobike; env->sc_enforcesingleikesa = enforcesingleikesa; env->sc_stickyaddress = stickyaddress; env->sc_frag = fragmentation; env->sc_alive_timeout = dpd_interval; env->sc_ocsp_url = ocsp_url; env->sc_ocsp_tolerate = ocsp_tolerate; env->sc_ocsp_maxage = ocsp_maxage; env->sc_cert_partial_chain = cert_partial_chain; env->sc_vendorid = vendorid; if (!rules) log_warnx("%s: no valid configuration rules found", filename); else log_debug("%s: loaded %d configuration rules", filename, rules); /* Free macros and check which have not been used. */ while ((sym = TAILQ_FIRST(&symhead))) { if (!sym->used) log_debug("warning: macro '%s' not " "used\n", sym->nam); free(sym->nam); free(sym->val); TAILQ_REMOVE(&symhead, sym, entry); free(sym); } iaw_free(iftab); iftab = NULL; return (errors ? -1 : 0); } int symset(const char *nam, const char *val, int persist) { struct sym *sym; TAILQ_FOREACH(sym, &symhead, entry) { if (strcmp(nam, sym->nam) == 0) break; } if (sym != NULL) { if (sym->persist == 1) return (0); else { free(sym->nam); free(sym->val); TAILQ_REMOVE(&symhead, sym, entry); free(sym); } } if ((sym = calloc(1, sizeof(*sym))) == NULL) return (-1); sym->nam = strdup(nam); if (sym->nam == NULL) { free(sym); return (-1); } sym->val = strdup(val); if (sym->val == NULL) { free(sym->nam); free(sym); return (-1); } sym->used = 0; sym->persist = persist; TAILQ_INSERT_TAIL(&symhead, sym, entry); return (0); } int cmdline_symset(char *s) { char *sym, *val; int ret; if ((val = strrchr(s, '=')) == NULL) return (-1); sym = strndup(s, val - s); if (sym == NULL) err(1, "%s", __func__); ret = symset(sym, val + 1, 1); free(sym); return (ret); } char * symget(const char *nam) { struct sym *sym; TAILQ_FOREACH(sym, &symhead, entry) { if (strcmp(nam, sym->nam) == 0) { sym->used = 1; return (sym->val); } } return (NULL); } uint8_t x2i(unsigned char *s) { char ss[3]; ss[0] = s[0]; ss[1] = s[1]; ss[2] = 0; if (!isxdigit(s[0]) || !isxdigit(s[1])) { yyerror("keys need to be specified in hex digits"); return (-1); } return ((uint8_t)strtoul(ss, NULL, 16)); } int parsekey(unsigned char *hexkey, size_t len, struct iked_auth *auth) { unsigned int i; bzero(auth, sizeof(*auth)); if ((len / 2) > sizeof(auth->auth_data)) return (-1); auth->auth_length = len / 2; for (i = 0; i < auth->auth_length; i++) auth->auth_data[i] = x2i(hexkey + 2 * i); return (0); } int parsekeyfile(char *filename, struct iked_auth *auth) { struct stat sb; int fd, ret; unsigned char *hex; if ((fd = open(filename, O_RDONLY)) == -1) err(1, "open %s", filename); if (check_file_secrecy(fd, filename) == -1) exit(1); if (fstat(fd, &sb) == -1) err(1, "parsekeyfile: stat %s", filename); if ((sb.st_size > KEYSIZE_LIMIT) || (sb.st_size == 0)) errx(1, "%s: key too %s", filename, sb.st_size ? "large" : "small"); if ((hex = calloc(sb.st_size, sizeof(unsigned char))) == NULL) err(1, "parsekeyfile: calloc"); if (read(fd, hex, sb.st_size) < sb.st_size) err(1, "parsekeyfile: read"); close(fd); ret = parsekey(hex, sb.st_size, auth); free(hex); return (ret); } int get_id_type(char *string) { struct in6_addr ia; if (string == NULL) return (IKEV2_ID_NONE); if (*string == '/') return (IKEV2_ID_ASN1_DN); else if (inet_pton(AF_INET, string, &ia) == 1) return (IKEV2_ID_IPV4); else if (inet_pton(AF_INET6, string, &ia) == 1) return (IKEV2_ID_IPV6); else if (strchr(string, '@')) return (IKEV2_ID_UFQDN); else return (IKEV2_ID_FQDN); } struct ipsec_addr_wrap * host(const char *s) { struct ipsec_addr_wrap *ipa = NULL; int mask = -1; char *p, *ps; const char *errstr; if ((ps = strdup(s)) == NULL) err(1, "%s: strdup", __func__); if ((p = strchr(ps, '/')) != NULL) { mask = strtonum(p+1, 0, 128, &errstr); if (errstr) { fprintf(stderr, "netmask is %s: %s\n", errstr, p); goto error; } p[0] = '\0'; } if ((ipa = host_if(ps, mask)) == NULL && (ipa = host_ip(ps, mask)) == NULL && (ipa = host_dns(ps, mask)) == NULL) fprintf(stderr, "no IP address found for %s\n", s); error: free(ps); return (ipa); } struct ipsec_addr_wrap * host_ip(const char *s, int mask) { struct ipsec_addr_wrap *ipa = NULL; struct addrinfo hints, *res; char hbuf[NI_MAXHOST]; bzero(&hints, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; /*dummy*/ hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(s, NULL, &hints, &res)) return (NULL); if (res->ai_next) err(1, "%s: %s expanded to multiple item", __func__, s); ipa = calloc(1, sizeof(struct ipsec_addr_wrap)); if (ipa == NULL) err(1, "%s", __func__); ipa->af = res->ai_family; copy_sockaddrtoipa(ipa, res->ai_addr); ipa->next = NULL; ipa->tail = ipa; set_ipmask(ipa, mask); if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) { errx(1, "could not get a numeric hostname"); } if (mask > -1) { ipa->netaddress = 1; if (asprintf(&ipa->name, "%s/%d", hbuf, mask) == -1) err(1, "%s", __func__); } else { if ((ipa->name = strdup(hbuf)) == NULL) err(1, "%s", __func__); } freeaddrinfo(res); return (ipa); } struct ipsec_addr_wrap * host_dns(const char *s, int mask) { struct ipsec_addr_wrap *ipa = NULL, *head = NULL; struct addrinfo hints, *res0, *res; int error; char hbuf[NI_MAXHOST]; bzero(&hints, sizeof(struct addrinfo)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_ADDRCONFIG; error = getaddrinfo(s, NULL, &hints, &res0); if (error) return (NULL); for (res = res0; res; res = res->ai_next) { if (res->ai_family != AF_INET && res->ai_family != AF_INET6) continue; ipa = calloc(1, sizeof(struct ipsec_addr_wrap)); if (ipa == NULL) err(1, "%s", __func__); copy_sockaddrtoipa(ipa, res->ai_addr); error = getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST); if (error) err(1, "host_dns: getnameinfo"); ipa->name = strdup(hbuf); if (ipa->name == NULL) err(1, "%s", __func__); ipa->af = res->ai_family; ipa->next = NULL; ipa->tail = ipa; if (head == NULL) head = ipa; else { head->tail->next = ipa; head->tail = ipa; } /* * XXX for now, no netmask support for IPv6. * but since there's no way to specify address family, once you * have IPv6 address on a host, you cannot use dns/netmask * syntax. */ if (ipa->af == AF_INET) set_ipmask(ipa, mask == -1 ? 32 : mask); else if (mask != -1) err(1, "host_dns: cannot apply netmask " "on non-IPv4 address"); } freeaddrinfo(res0); return (head); } struct ipsec_addr_wrap * host_if(const char *s, int mask) { struct ipsec_addr_wrap *ipa = NULL; if (ifa_exists(s)) ipa = ifa_lookup(s); return (ipa); } struct ipsec_addr_wrap * host_any(void) { struct ipsec_addr_wrap *ipa; ipa = calloc(1, sizeof(struct ipsec_addr_wrap)); if (ipa == NULL) err(1, "%s", __func__); ipa->af = AF_UNSPEC; ipa->netaddress = 1; ipa->tail = ipa; ipa->type = IPSEC_ADDR_ANY; return (ipa); } struct ipsec_addr_wrap * host_dynamic(void) { struct ipsec_addr_wrap *ipa; ipa = calloc(1, sizeof(struct ipsec_addr_wrap)); if (ipa == NULL) err(1, "%s", __func__); ipa->af = AF_UNSPEC; ipa->tail = ipa; ipa->type = IPSEC_ADDR_DYNAMIC; return (ipa); } void ifa_load(void) { struct ifaddrs *ifap, *ifa; struct ipsec_addr_wrap *n = NULL, *h = NULL; struct sockaddr_in *sa_in; struct sockaddr_in6 *sa_in6; if (getifaddrs(&ifap) == -1) err(1, "ifa_load: getifaddrs"); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL || !(ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6 || ifa->ifa_addr->sa_family == AF_LINK)) continue; n = calloc(1, sizeof(struct ipsec_addr_wrap)); if (n == NULL) err(1, "%s", __func__); n->af = ifa->ifa_addr->sa_family; if ((n->name = strdup(ifa->ifa_name)) == NULL) err(1, "%s", __func__); if (n->af == AF_INET) { sa_in = (struct sockaddr_in *)ifa->ifa_addr; memcpy(&n->address, sa_in, sizeof(*sa_in)); sa_in = (struct sockaddr_in *)ifa->ifa_netmask; n->mask = mask2prefixlen((struct sockaddr *)sa_in); } else if (n->af == AF_INET6) { sa_in6 = (struct sockaddr_in6 *)ifa->ifa_addr; memcpy(&n->address, sa_in6, sizeof(*sa_in6)); sa_in6 = (struct sockaddr_in6 *)ifa->ifa_netmask; n->mask = mask2prefixlen6((struct sockaddr *)sa_in6); } n->next = NULL; n->tail = n; if (h == NULL) h = n; else { h->tail->next = n; h->tail = n; } } iftab = h; freeifaddrs(ifap); } int ifa_exists(const char *ifa_name) { struct ipsec_addr_wrap *n; #ifdef __OpenBSD__ struct ifgroupreq ifgr; #endif int s; if (iftab == NULL) ifa_load(); /* check wether this is a group */ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) err(1, "ifa_exists: socket"); #ifdef __OpenBSD__ bzero(&ifgr, sizeof(ifgr)); strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name)); if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == 0) { close(s); return (1); } #endif close(s); for (n = iftab; n; n = n->next) { if (n->af == AF_LINK && !strncmp(n->name, ifa_name, IFNAMSIZ)) return (1); } return (0); } #ifdef __OpenBSD__ struct ipsec_addr_wrap * ifa_grouplookup(const char *ifa_name) { struct ifg_req *ifg; struct ifgroupreq ifgr; int s; size_t len; struct ipsec_addr_wrap *n, *h = NULL, *hn; if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1) err(1, "socket"); bzero(&ifgr, sizeof(ifgr)); strlcpy(ifgr.ifgr_name, ifa_name, sizeof(ifgr.ifgr_name)); if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) { close(s); return (NULL); } len = ifgr.ifgr_len; if ((ifgr.ifgr_groups = calloc(1, len)) == NULL) err(1, "%s", __func__); if (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) == -1) err(1, "ioctl"); for (ifg = ifgr.ifgr_groups; ifg && len >= sizeof(struct ifg_req); ifg++) { len -= sizeof(struct ifg_req); if ((n = ifa_lookup(ifg->ifgrq_member)) == NULL) continue; if (h == NULL) h = n; else { for (hn = h; hn->next != NULL; hn = hn->next) ; /* nothing */ hn->next = n; n->tail = hn; } } free(ifgr.ifgr_groups); close(s); return (h); } #endif struct ipsec_addr_wrap * ifa_lookup(const char *ifa_name) { struct ipsec_addr_wrap *p = NULL, *h = NULL, *n = NULL; struct sockaddr_in6 *in6; uint8_t *s6; if (iftab == NULL) ifa_load(); #ifdef __OpenBSD__ if ((n = ifa_grouplookup(ifa_name)) != NULL) return (n); #endif for (p = iftab; p; p = p->next) { if (p->af != AF_INET && p->af != AF_INET6) continue; if (strncmp(p->name, ifa_name, IFNAMSIZ)) continue; n = calloc(1, sizeof(struct ipsec_addr_wrap)); if (n == NULL) err(1, "%s", __func__); memcpy(n, p, sizeof(struct ipsec_addr_wrap)); if ((n->name = strdup(p->name)) == NULL) err(1, "%s", __func__); switch (n->af) { case AF_INET: set_ipmask(n, 32); break; case AF_INET6: in6 = (struct sockaddr_in6 *)&n->address; s6 = (uint8_t *)&in6->sin6_addr.s6_addr; /* route/show.c and bgpd/util.c give KAME credit */ if (IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) { uint16_t tmp16; /* for now we can not handle link local, * therefore bail for now */ free(n->name); free(n); continue; memcpy(&tmp16, &s6[2], sizeof(tmp16)); /* use this when we support link-local * n->??.scopeid = ntohs(tmp16); */ s6[2] = 0; s6[3] = 0; } set_ipmask(n, 128); break; } n->next = NULL; n->tail = n; if (h == NULL) h = n; else { h->tail->next = n; h->tail = n; } } return (h); } void set_ipmask(struct ipsec_addr_wrap *address, int b) { if (b == -1) address->mask = address->af == AF_INET ? 32 : 128; else address->mask = b; } const struct ipsec_xf * parse_xf(const char *name, unsigned int length, const struct ipsec_xf xfs[]) { int i; for (i = 0; xfs[i].name != NULL; i++) { if (strncmp(name, xfs[i].name, strlen(name))) continue; if (length == 0 || length == xfs[i].length) return &xfs[i]; } return (NULL); } int encxf_noauth(unsigned int id) { int i; for (i = 0; ikeencxfs[i].name != NULL; i++) if (ikeencxfs[i].id == id) return ikeencxfs[i].noauth; return (0); } size_t keylength_xf(unsigned int saproto, unsigned int type, unsigned int id) { int i; const struct ipsec_xf *xfs; switch (type) { case IKEV2_XFORMTYPE_ENCR: if (saproto == IKEV2_SAPROTO_IKE) xfs = ikeencxfs; else xfs = ipsecencxfs; break; case IKEV2_XFORMTYPE_INTEGR: xfs = authxfs; break; default: return (0); } for (i = 0; xfs[i].name != NULL; i++) { if (xfs[i].id == id) return (xfs[i].length * 8); } return (0); } size_t noncelength_xf(unsigned int type, unsigned int id) { const struct ipsec_xf *xfs = ipsecencxfs; int i; if (type != IKEV2_XFORMTYPE_ENCR) return (0); for (i = 0; xfs[i].name != NULL; i++) if (xfs[i].id == id) return (xfs[i].nonce * 8); return (0); } void copy_transforms(unsigned int type, const struct ipsec_xf **xfs, unsigned int nxfs, struct iked_transform **dst, unsigned int *ndst, struct iked_transform *src, size_t nsrc) { unsigned int i; struct iked_transform *a, *b; const struct ipsec_xf *xf; if (nxfs) { for (i = 0; i < nxfs; i++) { xf = xfs[i]; *dst = recallocarray(*dst, *ndst, *ndst + 1, sizeof(struct iked_transform)); if (*dst == NULL) err(1, "%s", __func__); b = *dst + (*ndst)++; b->xform_type = type; b->xform_id = xf->id; b->xform_keylength = xf->length * 8; b->xform_length = xf->keylength * 8; } return; } for (i = 0; i < nsrc; i++) { a = src + i; if (a->xform_type != type) continue; *dst = recallocarray(*dst, *ndst, *ndst + 1, sizeof(struct iked_transform)); if (*dst == NULL) err(1, "%s", __func__); b = *dst + (*ndst)++; memcpy(b, a, sizeof(*b)); } } int create_ike(char *name, int af, struct ipsec_addr_wrap *ipproto, int rdomain, struct ipsec_hosts *hosts, struct ipsec_hosts *peers, struct ipsec_mode *ike_sa, struct ipsec_mode *ipsec_sa, uint8_t saproto, unsigned int flags, char *srcid, char *dstid, uint32_t ikelifetime, struct iked_lifetime *lt, struct iked_auth *authtype, struct ipsec_filters *filter, struct ipsec_addr_wrap *ikecfg, char *iface) { char idstr[IKED_ID_SIZE]; struct ipsec_addr_wrap *ipa, *ipb, *ipp; struct iked_auth *ikeauth; struct iked_policy pol; struct iked_proposal *p, *ptmp; struct iked_transform *xf; unsigned int i, j, xfi, noauth, auth; unsigned int ikepropid = 1, ipsecpropid = 1; struct iked_flow *flow, *ftmp; static unsigned int policy_id = 0; struct iked_cfg *cfg; int ret = -1; bzero(&pol, sizeof(pol)); bzero(idstr, sizeof(idstr)); pol.pol_id = ++policy_id; pol.pol_certreqtype = env->sc_certreqtype; pol.pol_af = af; pol.pol_saproto = saproto; for (i = 0, ipp = ipproto; ipp; ipp = ipp->next, i++) { if (i >= IKED_IPPROTO_MAX) { yyerror("too many protocols"); return (-1); } pol.pol_ipproto[i] = ipp->type; pol.pol_nipproto++; } pol.pol_flags = flags; pol.pol_rdomain = rdomain; memcpy(&pol.pol_auth, authtype, sizeof(struct iked_auth)); explicit_bzero(authtype, sizeof(*authtype)); if (name != NULL) { if (strlcpy(pol.pol_name, name, sizeof(pol.pol_name)) >= sizeof(pol.pol_name)) { yyerror("name too long"); return (-1); } } else { snprintf(pol.pol_name, sizeof(pol.pol_name), "policy%d", policy_id); } if (iface != NULL) { /* sec(4) */ if (strncmp("sec", iface, strlen("sec")) == 0) pol.pol_flags |= IKED_POLICY_ROUTING; pol.pol_iface = if_nametoindex(iface); if (pol.pol_iface == 0) { yyerror("invalid iface"); return (-1); } } if (srcid) { pol.pol_localid.id_type = get_id_type(srcid); pol.pol_localid.id_length = strlen(srcid); if (strlcpy((char *)pol.pol_localid.id_data, srcid, IKED_ID_SIZE) >= IKED_ID_SIZE) { yyerror("srcid too long"); return (-1); } } if (dstid) { pol.pol_peerid.id_type = get_id_type(dstid); pol.pol_peerid.id_length = strlen(dstid); if (strlcpy((char *)pol.pol_peerid.id_data, dstid, IKED_ID_SIZE) >= IKED_ID_SIZE) { yyerror("dstid too long"); return (-1); } } if (filter != NULL) { if (filter->tag) strlcpy(pol.pol_tag, filter->tag, sizeof(pol.pol_tag)); pol.pol_tap = filter->tap; } if (peers == NULL) { if (pol.pol_flags & IKED_POLICY_ACTIVE) { yyerror("active mode requires peer specification"); return (-1); } pol.pol_flags |= IKED_POLICY_DEFAULT|IKED_POLICY_SKIP; } if (peers && peers->src && peers->dst && (peers->src->af != AF_UNSPEC) && (peers->dst->af != AF_UNSPEC) && (peers->src->af != peers->dst->af)) fatalx("create_ike: peer address family mismatch"); if (peers && (pol.pol_af != AF_UNSPEC) && ((peers->src && (peers->src->af != AF_UNSPEC) && (peers->src->af != pol.pol_af)) || (peers->dst && (peers->dst->af != AF_UNSPEC) && (peers->dst->af != pol.pol_af)))) fatalx("create_ike: policy address family mismatch"); ipa = ipb = NULL; if (peers) { if (peers->src) ipa = peers->src; if (peers->dst) ipb = peers->dst; if (ipa == NULL && ipb == NULL) { if (hosts->src && hosts->src->next == NULL) ipa = hosts->src; if (hosts->dst && hosts->dst->next == NULL) ipb = hosts->dst; } } if (ipa == NULL && ipb == NULL) { yyerror("could not get local/peer specification"); return (-1); } if (pol.pol_flags & IKED_POLICY_ACTIVE) { if (ipb == NULL || ipb->netaddress || (ipa != NULL && ipa->netaddress)) { yyerror("active mode requires local/peer address"); return (-1); } } if (ipa) { memcpy(&pol.pol_local.addr, &ipa->address, sizeof(ipa->address)); pol.pol_local.addr_af = ipa->af; pol.pol_local.addr_mask = ipa->mask; pol.pol_local.addr_net = ipa->netaddress; if (pol.pol_af == AF_UNSPEC) pol.pol_af = ipa->af; } if (ipb) { memcpy(&pol.pol_peer.addr, &ipb->address, sizeof(ipb->address)); pol.pol_peer.addr_af = ipb->af; pol.pol_peer.addr_mask = ipb->mask; pol.pol_peer.addr_net = ipb->netaddress; if (pol.pol_af == AF_UNSPEC) pol.pol_af = ipb->af; } if (ikelifetime) pol.pol_rekey = ikelifetime; if (lt) pol.pol_lifetime = *lt; else pol.pol_lifetime = deflifetime; TAILQ_INIT(&pol.pol_proposals); RB_INIT(&pol.pol_flows); if (ike_sa == NULL || ike_sa->nxfs == 0) { /* AES-GCM proposal */ if ((p = calloc(1, sizeof(*p))) == NULL) err(1, "%s", __func__); p->prop_id = ikepropid++; p->prop_protoid = IKEV2_SAPROTO_IKE; p->prop_nxforms = ikev2_default_nike_transforms_noauth; p->prop_xforms = ikev2_default_ike_transforms_noauth; TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry); pol.pol_nproposals++; /* Non GCM proposal */ if ((p = calloc(1, sizeof(*p))) == NULL) err(1, "%s", __func__); p->prop_id = ikepropid++; p->prop_protoid = IKEV2_SAPROTO_IKE; p->prop_nxforms = ikev2_default_nike_transforms; p->prop_xforms = ikev2_default_ike_transforms; TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry); pol.pol_nproposals++; } else { for (i = 0; i < ike_sa->nxfs; i++) { noauth = auth = 0; for (j = 0; j < ike_sa->xfs[i]->nencxf; j++) { if (ike_sa->xfs[i]->encxf[j]->noauth) noauth++; else auth++; } for (j = 0; j < ike_sa->xfs[i]->ngroupxf; j++) { if (ike_sa->xfs[i]->groupxf[j]->id == IKEV2_XFORMDH_NONE) { yyerror("IKE group can not be \"none\"."); goto done; } } if (ike_sa->xfs[i]->nauthxf) auth++; if (ike_sa->xfs[i]->nesnxf) { yyerror("cannot use ESN with ikesa."); goto done; } if (noauth && noauth != ike_sa->xfs[i]->nencxf) { yyerror("cannot mix encryption transforms with " "implicit and non-implicit authentication"); goto done; } if (noauth && ike_sa->xfs[i]->nauthxf) { yyerror("authentication is implicit for given " "encryption transforms"); goto done; } if (!auth) { if ((p = calloc(1, sizeof(*p))) == NULL) err(1, "%s", __func__); xf = NULL; xfi = 0; copy_transforms(IKEV2_XFORMTYPE_ENCR, ike_sa->xfs[i]->encxf, ike_sa->xfs[i]->nencxf, &xf, &xfi, ikev2_default_ike_transforms_noauth, ikev2_default_nike_transforms_noauth); copy_transforms(IKEV2_XFORMTYPE_DH, ike_sa->xfs[i]->groupxf, ike_sa->xfs[i]->ngroupxf, &xf, &xfi, ikev2_default_ike_transforms_noauth, ikev2_default_nike_transforms_noauth); copy_transforms(IKEV2_XFORMTYPE_PRF, ike_sa->xfs[i]->prfxf, ike_sa->xfs[i]->nprfxf, &xf, &xfi, ikev2_default_ike_transforms_noauth, ikev2_default_nike_transforms_noauth); p->prop_id = ikepropid++; p->prop_protoid = IKEV2_SAPROTO_IKE; p->prop_xforms = xf; p->prop_nxforms = xfi; TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry); pol.pol_nproposals++; } if (!noauth) { if ((p = calloc(1, sizeof(*p))) == NULL) err(1, "%s", __func__); xf = NULL; xfi = 0; copy_transforms(IKEV2_XFORMTYPE_INTEGR, ike_sa->xfs[i]->authxf, ike_sa->xfs[i]->nauthxf, &xf, &xfi, ikev2_default_ike_transforms, ikev2_default_nike_transforms); copy_transforms(IKEV2_XFORMTYPE_ENCR, ike_sa->xfs[i]->encxf, ike_sa->xfs[i]->nencxf, &xf, &xfi, ikev2_default_ike_transforms, ikev2_default_nike_transforms); copy_transforms(IKEV2_XFORMTYPE_DH, ike_sa->xfs[i]->groupxf, ike_sa->xfs[i]->ngroupxf, &xf, &xfi, ikev2_default_ike_transforms, ikev2_default_nike_transforms); copy_transforms(IKEV2_XFORMTYPE_PRF, ike_sa->xfs[i]->prfxf, ike_sa->xfs[i]->nprfxf, &xf, &xfi, ikev2_default_ike_transforms, ikev2_default_nike_transforms); p->prop_id = ikepropid++; p->prop_protoid = IKEV2_SAPROTO_IKE; p->prop_xforms = xf; p->prop_nxforms = xfi; TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry); pol.pol_nproposals++; } } } if (ipsec_sa == NULL || ipsec_sa->nxfs == 0) { /* XXX: Linux pfkey does not support AES-GCM */ #if !defined(HAVE_LINUX_PFKEY_H) if ((p = calloc(1, sizeof(*p))) == NULL) err(1, "%s", __func__); p->prop_id = ipsecpropid++; p->prop_protoid = saproto; p->prop_nxforms = ikev2_default_nesp_transforms_noauth; p->prop_xforms = ikev2_default_esp_transforms_noauth; TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry); pol.pol_nproposals++; #endif if ((p = calloc(1, sizeof(*p))) == NULL) err(1, "%s", __func__); p->prop_id = ipsecpropid++; p->prop_protoid = saproto; p->prop_nxforms = ikev2_default_nesp_transforms; p->prop_xforms = ikev2_default_esp_transforms; TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry); pol.pol_nproposals++; } else { for (i = 0; i < ipsec_sa->nxfs; i++) { noauth = auth = 0; for (j = 0; j < ipsec_sa->xfs[i]->nencxf; j++) { if (ipsec_sa->xfs[i]->encxf[j]->noauth) noauth++; else auth++; } if (ipsec_sa->xfs[i]->nauthxf) auth++; if (noauth && noauth != ipsec_sa->xfs[i]->nencxf) { yyerror("cannot mix encryption transforms with " "implicit and non-implicit authentication"); goto done; } if (noauth && ipsec_sa->xfs[i]->nauthxf) { yyerror("authentication is implicit for given " "encryption transforms"); goto done; } if (!auth) { #if !defined(HAVE_LINUX_PFKEY_H) if ((p = calloc(1, sizeof(*p))) == NULL) err(1, "%s", __func__); xf = NULL; xfi = 0; copy_transforms(IKEV2_XFORMTYPE_ENCR, ipsec_sa->xfs[i]->encxf, ipsec_sa->xfs[i]->nencxf, &xf, &xfi, ikev2_default_esp_transforms_noauth, ikev2_default_nesp_transforms_noauth); copy_transforms(IKEV2_XFORMTYPE_DH, ipsec_sa->xfs[i]->groupxf, ipsec_sa->xfs[i]->ngroupxf, &xf, &xfi, ikev2_default_esp_transforms_noauth, ikev2_default_nesp_transforms_noauth); copy_transforms(IKEV2_XFORMTYPE_ESN, ipsec_sa->xfs[i]->esnxf, ipsec_sa->xfs[i]->nesnxf, &xf, &xfi, ikev2_default_esp_transforms_noauth, ikev2_default_nesp_transforms_noauth); p->prop_id = ipsecpropid++; p->prop_protoid = saproto; p->prop_xforms = xf; p->prop_nxforms = xfi; TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry); pol.pol_nproposals++; #endif } if (!noauth) { if ((p = calloc(1, sizeof(*p))) == NULL) err(1, "%s", __func__); xf = NULL; xfi = 0; copy_transforms(IKEV2_XFORMTYPE_INTEGR, ipsec_sa->xfs[i]->authxf, ipsec_sa->xfs[i]->nauthxf, &xf, &xfi, ikev2_default_esp_transforms, ikev2_default_nesp_transforms); copy_transforms(IKEV2_XFORMTYPE_ENCR, ipsec_sa->xfs[i]->encxf, ipsec_sa->xfs[i]->nencxf, &xf, &xfi, ikev2_default_esp_transforms, ikev2_default_nesp_transforms); copy_transforms(IKEV2_XFORMTYPE_DH, ipsec_sa->xfs[i]->groupxf, ipsec_sa->xfs[i]->ngroupxf, &xf, &xfi, ikev2_default_esp_transforms, ikev2_default_nesp_transforms); copy_transforms(IKEV2_XFORMTYPE_ESN, ipsec_sa->xfs[i]->esnxf, ipsec_sa->xfs[i]->nesnxf, &xf, &xfi, ikev2_default_esp_transforms, ikev2_default_nesp_transforms); p->prop_id = ipsecpropid++; p->prop_protoid = saproto; p->prop_xforms = xf; p->prop_nxforms = xfi; TAILQ_INSERT_TAIL(&pol.pol_proposals, p, prop_entry); pol.pol_nproposals++; } } } for (ipa = hosts->src, ipb = hosts->dst; ipa && ipb; ipa = ipa->next, ipb = ipb->next) { for (j = 0; j < pol.pol_nipproto; j++) if (expand_flows(&pol, pol.pol_ipproto[j], ipa, ipb)) fatalx("create_ike: invalid flow"); if (pol.pol_nipproto == 0) if (expand_flows(&pol, 0, ipa, ipb)) fatalx("create_ike: invalid flow"); } for (j = 0, ipa = ikecfg; ipa; ipa = ipa->next, j++) { if (j >= IKED_CFG_MAX) break; cfg = &pol.pol_cfg[j]; pol.pol_ncfg++; cfg->cfg_action = ipa->action; cfg->cfg_type = ipa->type; memcpy(&cfg->cfg.address.addr, &ipa->address, sizeof(ipa->address)); cfg->cfg.address.addr_mask = ipa->mask; cfg->cfg.address.addr_net = ipa->netaddress; cfg->cfg.address.addr_af = ipa->af; } if (dstid) strlcpy(idstr, dstid, sizeof(idstr)); else if (!pol.pol_peer.addr_net) strlcpy(idstr, print_addr(&pol.pol_peer.addr), sizeof(idstr)); ikeauth = &pol.pol_auth; switch (ikeauth->auth_method) { case IKEV2_AUTH_RSA_SIG: pol.pol_certreqtype = IKEV2_CERT_RSA_KEY; break; case IKEV2_AUTH_ECDSA_256: case IKEV2_AUTH_ECDSA_384: case IKEV2_AUTH_ECDSA_521: pol.pol_certreqtype = IKEV2_CERT_ECDSA; break; default: pol.pol_certreqtype = IKEV2_CERT_NONE; break; } log_debug("%s: using %s for peer %s", __func__, print_xf(ikeauth->auth_method, 0, methodxfs), idstr); config_setpolicy(env, &pol, PROC_IKEV2); config_setflow(env, &pol, PROC_IKEV2); rules++; ret = 0; done: if (ike_sa) { for (i = 0; i < ike_sa->nxfs; i++) { free(ike_sa->xfs[i]->authxf); free(ike_sa->xfs[i]->encxf); free(ike_sa->xfs[i]->groupxf); free(ike_sa->xfs[i]->prfxf); free(ike_sa->xfs[i]); } free(ike_sa->xfs); free(ike_sa); } if (ipsec_sa) { for (i = 0; i < ipsec_sa->nxfs; i++) { free(ipsec_sa->xfs[i]->authxf); free(ipsec_sa->xfs[i]->encxf); free(ipsec_sa->xfs[i]->groupxf); free(ipsec_sa->xfs[i]->prfxf); free(ipsec_sa->xfs[i]->esnxf); free(ipsec_sa->xfs[i]); } free(ipsec_sa->xfs); free(ipsec_sa); } TAILQ_FOREACH_SAFE(p, &pol.pol_proposals, prop_entry, ptmp) { if (p->prop_xforms != ikev2_default_ike_transforms && p->prop_xforms != ikev2_default_ike_transforms_noauth && p->prop_xforms != ikev2_default_esp_transforms && p->prop_xforms != ikev2_default_esp_transforms_noauth) free(p->prop_xforms); free(p); } if (peers != NULL) { iaw_free(peers->src); iaw_free(peers->dst); /* peers is static, cannot be freed */ } if (hosts != NULL) { iaw_free(hosts->src); iaw_free(hosts->dst); free(hosts); } iaw_free(ikecfg); iaw_free(ipproto); RB_FOREACH_SAFE(flow, iked_flows, &pol.pol_flows, ftmp) { RB_REMOVE(iked_flows, &pol.pol_flows, flow); free(flow); } free(name); free(srcid); free(dstid); return (ret); } static int create_flow(struct iked_policy *pol, int proto, struct ipsec_addr_wrap *ipa, struct ipsec_addr_wrap *ipb) { struct iked_flow *flow; struct ipsec_addr_wrap *ippn; if (ipa->af != ipb->af) { yyerror("cannot mix different address families."); return (-1); } if ((flow = calloc(1, sizeof(struct iked_flow))) == NULL) fatalx("%s: failed to alloc flow.", __func__); memcpy(&flow->flow_src.addr, &ipa->address, sizeof(ipa->address)); flow->flow_src.addr_af = ipa->af; flow->flow_src.addr_mask = ipa->mask; flow->flow_src.addr_net = ipa->netaddress; flow->flow_src.addr_port = ipa->port; memcpy(&flow->flow_dst.addr, &ipb->address, sizeof(ipb->address)); flow->flow_dst.addr_af = ipb->af; flow->flow_dst.addr_mask = ipb->mask; flow->flow_dst.addr_net = ipb->netaddress; flow->flow_dst.addr_port = ipb->port; ippn = ipa->srcnat; if (ippn) { memcpy(&flow->flow_prenat.addr, &ippn->address, sizeof(ippn->address)); flow->flow_prenat.addr_af = ippn->af; flow->flow_prenat.addr_mask = ippn->mask; flow->flow_prenat.addr_net = ippn->netaddress; } else { flow->flow_prenat.addr_af = 0; } flow->flow_dir = IPSP_DIRECTION_OUT; flow->flow_ipproto = proto; flow->flow_saproto = pol->pol_saproto; flow->flow_rdomain = pol->pol_rdomain; flow->flow_transport = pol->pol_flags & IKED_POLICY_TRANSPORT; if (RB_INSERT(iked_flows, &pol->pol_flows, flow) == NULL) pol->pol_nflows++; else { warnx("create_ike: duplicate flow"); free(flow); } return (0); } static int expand_flows(struct iked_policy *pol, int proto, struct ipsec_addr_wrap *src, struct ipsec_addr_wrap *dst) { struct ipsec_addr_wrap *ipa = NULL, *ipb = NULL; int ret = -1; int srcaf, dstaf; srcaf = src->af; dstaf = dst->af; if (src->af == AF_UNSPEC && dst->af == AF_UNSPEC) { /* Need both IPv4 and IPv6 flows */ src->af = dst->af = AF_INET; ipa = expand_keyword(src); ipb = expand_keyword(dst); if (!ipa || !ipb) goto done; if (create_flow(pol, proto, ipa, ipb)) goto done; iaw_free(ipa); iaw_free(ipb); src->af = dst->af = AF_INET6; ipa = expand_keyword(src); ipb = expand_keyword(dst); if (!ipa || !ipb) goto done; if (create_flow(pol, proto, ipa, ipb)) goto done; } else if (src->af == AF_UNSPEC) { src->af = dst->af; ipa = expand_keyword(src); if (!ipa) goto done; if (create_flow(pol, proto, ipa, dst)) goto done; } else if (dst->af == AF_UNSPEC) { dst->af = src->af; ipa = expand_keyword(dst); if (!ipa) goto done; if (create_flow(pol, proto, src, ipa)) goto done; } else if (create_flow(pol, proto, src, dst)) goto done; ret = 0; done: src->af = srcaf; dst->af = dstaf; iaw_free(ipa); iaw_free(ipb); return (ret); } static struct ipsec_addr_wrap * expand_keyword(struct ipsec_addr_wrap *ip) { switch(ip->af) { case AF_INET: switch(ip->type) { case IPSEC_ADDR_ANY: return (host("0.0.0.0/0")); case IPSEC_ADDR_DYNAMIC: return (host("0.0.0.0")); } break; case AF_INET6: switch(ip->type) { case IPSEC_ADDR_ANY: return (host("::/0")); case IPSEC_ADDR_DYNAMIC: return (host("::")); } } return (NULL); } int create_user(const char *user, const char *pass) { struct iked_user usr; bzero(&usr, sizeof(usr)); if (*user == '\0' || (strlcpy(usr.usr_name, user, sizeof(usr.usr_name)) >= sizeof(usr.usr_name))) { yyerror("invalid user name"); return (-1); } if (*pass == '\0' || (strlcpy(usr.usr_pass, pass, sizeof(usr.usr_pass)) >= sizeof(usr.usr_pass))) { yyerror("invalid password"); explicit_bzero(&usr, sizeof usr); /* zap partial password */ return (-1); } config_setuser(env, &usr, PROC_IKEV2); rules++; explicit_bzero(&usr, sizeof usr); return (0); } void iaw_free(struct ipsec_addr_wrap *head) { struct ipsec_addr_wrap *n, *cur; if (head == NULL) return; for (n = head; n != NULL; ) { cur = n; n = n->next; if (cur->srcnat != NULL) { free(cur->srcnat->name); free(cur->srcnat); } free(cur->name); free(cur); } } openiked-7.4/iked/pfkey.c000066400000000000000000002104031477554731100153760ustar00rootroot00000000000000/* $OpenBSD: pfkey.c,v 1.84 2023/08/14 12:02:02 tobhe Exp $ */ /* * Copyright (c) 2020-2021 Tobias Heider * Copyright (c) 2016 Marcel Moolenaar * Copyright (c) 2010-2013 Reyk Floeter * Copyright (c) 2004, 2005 Hans-Joerg Hoexer * Copyright (c) 2003, 2004 Henning Brauer * Copyright (c) 2003, 2004 Markus Friedl * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" #include "ikev2.h" #define ROUNDUP(x) (((x) + (PFKEYV2_CHUNK - 1)) & ~(PFKEYV2_CHUNK - 1)) #define IOV_CNT 28 #define PFKEYV2_CHUNK sizeof(uint64_t) #define PFKEY_REPLY_TIMEOUT 1000 /* only used internally */ #define IKED_SADB_UPDATE_SA_ADDRESSES 0xff static uint32_t sadb_msg_seq = 0; static unsigned int sadb_decoupled = 0; static int iked_rdomain = 0; static struct event pfkey_timer_ev; static struct timeval pfkey_timer_tv; struct pfkey_message { SIMPLEQ_ENTRY(pfkey_message) pm_entry; uint8_t *pm_data; ssize_t pm_length; }; SIMPLEQ_HEAD(, pfkey_message) pfkey_retry, pfkey_postponed = SIMPLEQ_HEAD_INITIALIZER(pfkey_postponed); struct pfkey_constmap { uint8_t pfkey_id; unsigned int pfkey_ikeid; unsigned int pfkey_fixedkey; }; static const struct pfkey_constmap pfkey_encr[] = { { SADB_EALG_3DESCBC, IKEV2_XFORMENCR_3DES }, #ifdef SADB_X_EALG_CAST { SADB_X_EALG_CAST, IKEV2_XFORMENCR_CAST }, #endif #ifdef SADB_X_EALG_BLF { SADB_X_EALG_BLF, IKEV2_XFORMENCR_BLOWFISH }, #endif { SADB_EALG_NULL, IKEV2_XFORMENCR_NULL }, #ifdef SADB_X_EALG_AES { SADB_X_EALG_AES, IKEV2_XFORMENCR_AES_CBC }, #endif #ifdef SADB_X_EALG_AESCTR { SADB_X_EALG_AESCTR, IKEV2_XFORMENCR_AES_CTR }, #endif #ifdef SADB_X_EALG_AESGCM16 { SADB_X_EALG_AESGCM16, IKEV2_XFORMENCR_AES_GCM_16 }, #endif #ifdef SADB_X_EALG_AES_GCM { SADB_X_EALG_AES_GCM, IKEV2_XFORMENCR_AES_GCM_16 }, #endif #ifdef SADB_X_EALG_AESGMAC { SADB_X_EALG_AESGMAC, IKEV2_XFORMENCR_NULL_AES_GMAC }, #endif #ifdef SADB_X_EALG_CHACHA20POLY1305 { SADB_X_EALG_CHACHA20POLY1305, IKEV2_XFORMENCR_CHACHA20_POLY1305 }, #endif { 0 } }; static const struct pfkey_constmap pfkey_integr[] = { { SADB_AALG_MD5HMAC, IKEV2_XFORMAUTH_HMAC_MD5_96 }, { SADB_AALG_SHA1HMAC, IKEV2_XFORMAUTH_HMAC_SHA1_96 }, /* XXX: Linux uses a non-standard truncated SHA256 with pfkey */ #if defined(SADB_X_AALG_SHA2_256) && !defined(HAVE_LINUX_PFKEY_H) { SADB_X_AALG_SHA2_256, IKEV2_XFORMAUTH_HMAC_SHA2_256_128 }, #endif #if defined(SADB_X_AALG_SHA2_384) { SADB_X_AALG_SHA2_384, IKEV2_XFORMAUTH_HMAC_SHA2_384_192 }, #endif #if defined(SADB_X_AALG_SHA2_512) { SADB_X_AALG_SHA2_512, IKEV2_XFORMAUTH_HMAC_SHA2_512_256 }, #endif { 0 } }; static const struct pfkey_constmap pfkey_satype[] = { { SADB_SATYPE_AH, IKEV2_SAPROTO_AH }, { SADB_SATYPE_ESP, IKEV2_SAPROTO_ESP }, { SADB_X_SATYPE_IPCOMP, IKEV2_SAPROTO_IPCOMP }, { 0 } }; int pfkey_map(const struct pfkey_constmap *, uint16_t, uint8_t *); int pfkey_flow(struct iked *, uint8_t, uint8_t, struct iked_flow *); int pfkey_sa(struct iked *, uint8_t, uint8_t, struct iked_childsa *); int pfkey_sa_getspi(struct iked *, uint8_t, struct iked_childsa *, uint32_t *); int pfkey_sagroup(struct iked *, uint8_t, uint8_t, struct iked_childsa *, struct iked_childsa *); int pfkey_write(struct iked *, struct sadb_msg *, struct iovec *, int, uint8_t **, ssize_t *); int pfkey_reply(int, uint8_t **, ssize_t *); void pfkey_dispatch(int, short, void *); int pfkey_sa_lookup(struct iked *, struct iked_childsa *, uint64_t *); int pfkey_sa_check_exists(struct iked *, struct iked_childsa *); struct sadb_ident * pfkey_id2ident(struct iked_id *, unsigned int); void *pfkey_find_ext(uint8_t *, ssize_t, int); void pfkey_timer_cb(int, short, void *); int pfkey_process(struct iked *, struct pfkey_message *); int pfkey_couple(struct iked *env, struct iked_sas *sas, int couple) { struct iked_sa *sa; struct iked_flow *flow; struct iked_childsa *csa, *ipcomp; const char *mode[] = { "coupled", "decoupled" }; /* Socket is not ready */ if (env->sc_pfkey == -1) return (-1); if (sadb_decoupled == !couple) return (0); log_debug("%s: kernel %s -> %s", __func__, mode[sadb_decoupled], mode[!sadb_decoupled]); /* Allow writes to the PF_KEY socket */ sadb_decoupled = 0; RB_FOREACH(sa, iked_sas, sas) { TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) { if (!csa->csa_loaded && couple) (void)pfkey_sa_add(env, csa, NULL); else if (csa->csa_loaded && !couple) (void)pfkey_sa_delete(env, csa); if ((ipcomp = csa->csa_bundled) != NULL) { if (!ipcomp->csa_loaded && couple) (void)pfkey_sa_add(env, ipcomp, csa); else if (ipcomp->csa_loaded && !couple) (void)pfkey_sa_delete(env, ipcomp); } } TAILQ_FOREACH(flow, &sa->sa_flows, flow_entry) { if (!flow->flow_loaded && couple) (void)pfkey_flow_add(env, flow); else if (flow->flow_loaded && !couple) (void)pfkey_flow_delete(env, flow); } } sadb_decoupled = !couple; return (0); } int pfkey_map(const struct pfkey_constmap *map, uint16_t alg, uint8_t *pfkalg) { int i; for (i = 0; map[i].pfkey_id != 0; i++) if (map[i].pfkey_ikeid == alg) { *pfkalg = map[i].pfkey_id; return (0); } return (-1); } int pfkey_flow(struct iked *env, uint8_t satype, uint8_t action, struct iked_flow *flow) { #ifdef __OpenBSD__ struct sadb_msg smsg; struct iked_addr *flow_src, *flow_dst; struct sadb_address sa_src, sa_dst, sa_local, sa_peer, sa_smask, sa_dmask; struct sadb_protocol sa_flowtype, sa_protocol; #ifdef SADB_X_EXT_RDOMAIN struct sadb_x_rdomain sa_rdomain; #endif struct sadb_ident *sa_srcid, *sa_dstid; struct sockaddr_storage ssrc, sdst, slocal, speer, smask, dmask; struct iovec iov[IOV_CNT]; int iov_cnt, ret = -1; uint64_t pad = 0; size_t padlen; sa_srcid = sa_dstid = NULL; flow_src = &flow->flow_src; flow_dst = &flow->flow_dst; if (flow->flow_prenat.addr_af == flow_src->addr_af) { if (flow->flow_dir == IPSP_DIRECTION_IN) flow_dst = &flow->flow_prenat; else flow_src = &flow->flow_prenat; } bzero(&ssrc, sizeof(ssrc)); bzero(&smask, sizeof(smask)); memcpy(&ssrc, &flow_src->addr, sizeof(ssrc)); memcpy(&smask, &flow_src->addr, sizeof(smask)); socket_af((struct sockaddr *)&ssrc, flow_src->addr_port); socket_af((struct sockaddr *)&smask, flow_src->addr_port ? 0xffff : 0); switch (flow_src->addr_af) { case AF_INET: ((struct sockaddr_in *)&smask)->sin_addr.s_addr = prefixlen2mask(flow_src->addr_net ? flow_src->addr_mask : 32); break; case AF_INET6: prefixlen2mask6(flow_src->addr_net ? flow_src->addr_mask : 128, (uint32_t *)((struct sockaddr_in6 *) &smask)->sin6_addr.s6_addr); break; default: log_warnx("%s: unsupported address family %d", __func__, flow_src->addr_af); return (-1); } smask.ss_len = ssrc.ss_len; bzero(&sdst, sizeof(sdst)); bzero(&dmask, sizeof(dmask)); memcpy(&sdst, &flow_dst->addr, sizeof(sdst)); memcpy(&dmask, &flow_dst->addr, sizeof(dmask)); socket_af((struct sockaddr *)&sdst, flow_dst->addr_port); socket_af((struct sockaddr *)&dmask, flow_dst->addr_port ? 0xffff : 0); switch (flow_dst->addr_af) { case AF_INET: ((struct sockaddr_in *)&dmask)->sin_addr.s_addr = prefixlen2mask(flow_dst->addr_net ? flow_dst->addr_mask : 32); break; case AF_INET6: prefixlen2mask6(flow_dst->addr_net ? flow_dst->addr_mask : 128, (uint32_t *)((struct sockaddr_in6 *) &dmask)->sin6_addr.s6_addr); break; default: log_warnx("%s: unsupported address family %d", __func__, flow_dst->addr_af); return (-1); } dmask.ss_len = sdst.ss_len; bzero(&slocal, sizeof(slocal)); bzero(&speer, sizeof(speer)); if (action != SADB_X_DELFLOW && flow->flow_local != NULL) { memcpy(&slocal, &flow->flow_local->addr, sizeof(slocal)); socket_af((struct sockaddr *)&slocal, 0); memcpy(&speer, &flow->flow_peer->addr, sizeof(speer)); socket_af((struct sockaddr *)&speer, 0); } bzero(&smsg, sizeof(smsg)); smsg.sadb_msg_version = PF_KEY_V2; smsg.sadb_msg_seq = ++sadb_msg_seq; smsg.sadb_msg_pid = getpid(); smsg.sadb_msg_len = sizeof(smsg) / 8; smsg.sadb_msg_type = action; smsg.sadb_msg_satype = satype; bzero(&sa_flowtype, sizeof(sa_flowtype)); sa_flowtype.sadb_protocol_exttype = SADB_X_EXT_FLOW_TYPE; sa_flowtype.sadb_protocol_len = sizeof(sa_flowtype) / 8; sa_flowtype.sadb_protocol_direction = flow->flow_dir; sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_REQUIRE; bzero(&sa_protocol, sizeof(sa_protocol)); sa_protocol.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL; sa_protocol.sadb_protocol_len = sizeof(sa_protocol) / 8; sa_protocol.sadb_protocol_direction = 0; sa_protocol.sadb_protocol_proto = flow->flow_ipproto; bzero(&sa_src, sizeof(sa_src)); sa_src.sadb_address_exttype = SADB_X_EXT_SRC_FLOW; sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8; bzero(&sa_smask, sizeof(sa_smask)); sa_smask.sadb_address_exttype = SADB_X_EXT_SRC_MASK; sa_smask.sadb_address_len = (sizeof(sa_smask) + ROUNDUP(smask.ss_len)) / 8; bzero(&sa_dst, sizeof(sa_dst)); sa_dst.sadb_address_exttype = SADB_X_EXT_DST_FLOW; sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8; bzero(&sa_dmask, sizeof(sa_dmask)); sa_dmask.sadb_address_exttype = SADB_X_EXT_DST_MASK; sa_dmask.sadb_address_len = (sizeof(sa_dmask) + ROUNDUP(dmask.ss_len)) / 8; if (action != SADB_X_DELFLOW && flow->flow_local != NULL) { /* local address */ bzero(&sa_local, sizeof(sa_local)); sa_local.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; sa_local.sadb_address_len = (sizeof(sa_local) + ROUNDUP(slocal.ss_len)) / 8; /* peer address */ bzero(&sa_peer, sizeof(sa_peer)); sa_peer.sadb_address_exttype = SADB_EXT_ADDRESS_DST; sa_peer.sadb_address_len = (sizeof(sa_peer) + ROUNDUP(speer.ss_len)) / 8; /* local id */ sa_srcid = pfkey_id2ident(IKESA_SRCID(flow->flow_ikesa), SADB_EXT_IDENTITY_SRC); /* peer id */ sa_dstid = pfkey_id2ident(IKESA_DSTID(flow->flow_ikesa), SADB_EXT_IDENTITY_DST); } #ifdef SADB_X_EXT_RDOMAIN if (flow->flow_rdomain >= 0) { /* install flow in specific rdomain */ bzero(&sa_rdomain, sizeof(sa_rdomain)); sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN; sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8; sa_rdomain.sadb_x_rdomain_dom1 = flow->flow_rdomain; } #endif #define PAD(len) \ padlen = ROUNDUP((len)) - (len); \ if (padlen) { \ iov[iov_cnt].iov_base = &pad; \ iov[iov_cnt].iov_len = padlen; \ iov_cnt++; \ } iov_cnt = 0; /* header */ iov[iov_cnt].iov_base = &smsg; iov[iov_cnt].iov_len = sizeof(smsg); iov_cnt++; /* add flow type */ iov[iov_cnt].iov_base = &sa_flowtype; iov[iov_cnt].iov_len = sizeof(sa_flowtype); smsg.sadb_msg_len += sa_flowtype.sadb_protocol_len; iov_cnt++; if (action != SADB_X_DELFLOW && flow->flow_local != NULL) { /* remote peer */ iov[iov_cnt].iov_base = &sa_peer; iov[iov_cnt].iov_len = sizeof(sa_peer); iov_cnt++; iov[iov_cnt].iov_base = &speer; iov[iov_cnt].iov_len = speer.ss_len; smsg.sadb_msg_len += sa_peer.sadb_address_len; iov_cnt++; PAD(speer.ss_len); } /* src addr */ iov[iov_cnt].iov_base = &sa_src; iov[iov_cnt].iov_len = sizeof(sa_src); iov_cnt++; iov[iov_cnt].iov_base = &ssrc; iov[iov_cnt].iov_len = ssrc.ss_len; smsg.sadb_msg_len += sa_src.sadb_address_len; iov_cnt++; PAD(ssrc.ss_len); /* src mask */ iov[iov_cnt].iov_base = &sa_smask; iov[iov_cnt].iov_len = sizeof(sa_smask); iov_cnt++; iov[iov_cnt].iov_base = &smask; iov[iov_cnt].iov_len = smask.ss_len; smsg.sadb_msg_len += sa_smask.sadb_address_len; iov_cnt++; PAD(smask.ss_len); /* dest addr */ iov[iov_cnt].iov_base = &sa_dst; iov[iov_cnt].iov_len = sizeof(sa_dst); iov_cnt++; iov[iov_cnt].iov_base = &sdst; iov[iov_cnt].iov_len = sdst.ss_len; smsg.sadb_msg_len += sa_dst.sadb_address_len; iov_cnt++; PAD(sdst.ss_len); /* dst mask */ iov[iov_cnt].iov_base = &sa_dmask; iov[iov_cnt].iov_len = sizeof(sa_dmask); iov_cnt++; iov[iov_cnt].iov_base = &dmask; iov[iov_cnt].iov_len = dmask.ss_len; smsg.sadb_msg_len += sa_dmask.sadb_address_len; iov_cnt++; PAD(dmask.ss_len); /* add protocol */ iov[iov_cnt].iov_base = &sa_protocol; iov[iov_cnt].iov_len = sizeof(sa_protocol); smsg.sadb_msg_len += sa_protocol.sadb_protocol_len; iov_cnt++; if (sa_srcid) { /* src identity */ iov[iov_cnt].iov_base = sa_srcid; iov[iov_cnt].iov_len = sa_srcid->sadb_ident_len * 8; smsg.sadb_msg_len += sa_srcid->sadb_ident_len; iov_cnt++; } if (sa_dstid) { /* dst identity */ iov[iov_cnt].iov_base = sa_dstid; iov[iov_cnt].iov_len = sa_dstid->sadb_ident_len * 8; smsg.sadb_msg_len += sa_dstid->sadb_ident_len; iov_cnt++; } #ifdef SADB_X_EXT_RDOMAIN if (flow->flow_rdomain >= 0) { iov[iov_cnt].iov_base = &sa_rdomain; iov[iov_cnt].iov_len = sizeof(sa_rdomain); smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len; iov_cnt++; } #endif #undef PAD ret = pfkey_write(env, &smsg, iov, iov_cnt, NULL, NULL); free(sa_srcid); free(sa_dstid); #else /* __OpenBSD__ */ struct sadb_msg smsg; struct sadb_address sa_src, sa_dst; struct sadb_x_ipsecrequest sa_ipsec; struct sadb_x_policy sa_policy; struct sadb_x_sa2 sa_2; struct sockaddr_storage ssrc, sdst, slocal, speer; struct iovec iov[IOV_CNT]; int iov_cnt, ret = -1; in_port_t sport, dport; uint8_t smask, dmask; uint8_t zeropad[8]; uint64_t pad = 0; size_t padlen; bzero(&ssrc, sizeof(ssrc)); memcpy(&ssrc, &flow->flow_src.addr, sizeof(ssrc)); sport = flow->flow_src.addr_port; socket_af((struct sockaddr *)&ssrc, sport); switch (flow->flow_src.addr_af) { case AF_INET: smask = flow->flow_src.addr_net ? flow->flow_src.addr_mask : 32; break; case AF_INET6: smask = flow->flow_src.addr_net ? flow->flow_src.addr_mask : 128; break; default: log_warnx("%s: unsupported address family %d", __func__, flow->flow_src.addr_af); return (-1); } bzero(&sdst, sizeof(sdst)); memcpy(&sdst, &flow->flow_dst.addr, sizeof(sdst)); dport = flow->flow_dst.addr_port; socket_af((struct sockaddr *)&sdst, dport); switch (flow->flow_dst.addr.ss_family) { case AF_INET: dmask = flow->flow_dst.addr_net ? flow->flow_dst.addr_mask : 32; break; case AF_INET6: dmask = flow->flow_dst.addr_net ? flow->flow_dst.addr_mask : 128; break; default: log_warnx("%s: unsupported address family %d", __func__, flow->flow_dst.addr.ss_family); return (-1); } bzero(&slocal, sizeof(slocal)); bzero(&speer, sizeof(speer)); bzero(&zeropad, sizeof(zeropad)); if (flow->flow_local == NULL) { slocal.ss_family = flow->flow_src.addr_af; speer.ss_family = flow->flow_dst.addr_af; } else if (flow->flow_dir == IPSEC_DIR_OUTBOUND) { memcpy(&slocal, &flow->flow_local->addr, sizeof(slocal)); memcpy(&speer, &flow->flow_peer->addr, sizeof(speer)); } else { memcpy(&speer, &flow->flow_local->addr, sizeof(slocal)); memcpy(&slocal, &flow->flow_peer->addr, sizeof(speer)); } socket_af((struct sockaddr *)&slocal, 0); socket_af((struct sockaddr *)&speer, 0); bzero(&smsg, sizeof(smsg)); smsg.sadb_msg_version = PF_KEY_V2; smsg.sadb_msg_seq = ++sadb_msg_seq; smsg.sadb_msg_pid = getpid(); smsg.sadb_msg_len = sizeof(smsg) / 8; smsg.sadb_msg_type = action; smsg.sadb_msg_satype = SADB_SATYPE_UNSPEC; bzero(&sa_2, sizeof(sa_2)); sa_2.sadb_x_sa2_exttype = SADB_X_EXT_SA2; sa_2.sadb_x_sa2_len = sizeof(sa_2) / 8; sa_2.sadb_x_sa2_mode = IPSEC_MODE_ANY; bzero(&sa_src, sizeof(sa_src)); sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; sa_src.sadb_address_proto = flow->flow_ipproto ? flow->flow_ipproto : IPSEC_ULPROTO_ANY; sa_src.sadb_address_prefixlen = smask; sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(SS_LEN(ssrc))) / 8; bzero(&sa_dst, sizeof(sa_dst)); sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST; sa_dst.sadb_address_proto = flow->flow_ipproto ? flow->flow_ipproto : IPSEC_ULPROTO_ANY; sa_dst.sadb_address_prefixlen = dmask; sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(SS_LEN(sdst))) / 8; bzero(&sa_policy, sizeof(sa_policy)); sa_policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY; sa_policy.sadb_x_policy_dir = flow->flow_dir; sa_policy.sadb_x_policy_type = IPSEC_POLICY_IPSEC; bzero(&sa_ipsec, sizeof(sa_ipsec)); sa_ipsec.sadb_x_ipsecrequest_proto = satype == SADB_SATYPE_AH ? IPPROTO_AH : IPPROTO_ESP; sa_ipsec.sadb_x_ipsecrequest_mode = (flow->flow_transport) ? IPSEC_MODE_TRANSPORT : IPSEC_MODE_TUNNEL; /* XXX: Always use IPSEC_LEVEL_REQUIRE */ sa_ipsec.sadb_x_ipsecrequest_level = IPSEC_LEVEL_REQUIRE; sa_ipsec.sadb_x_ipsecrequest_len = sizeof(sa_ipsec); if (!flow->flow_transport) sa_ipsec.sadb_x_ipsecrequest_len += ROUNDUP(SS_LEN(slocal) + SS_LEN(speer)); sa_policy.sadb_x_policy_len = (sizeof(sa_policy) + sa_ipsec.sadb_x_ipsecrequest_len) / 8; iov_cnt = 0; #define PAD(len) \ padlen = ROUNDUP((len)) - (len); \ if (padlen) { \ iov[iov_cnt].iov_base = &pad; \ iov[iov_cnt].iov_len = padlen; \ iov_cnt++; \ } /* header */ iov[iov_cnt].iov_base = &smsg; iov[iov_cnt].iov_len = sizeof(smsg); iov_cnt++; /* add flow SA2 */ iov[iov_cnt].iov_base = &sa_2; iov[iov_cnt].iov_len = sizeof(sa_2); smsg.sadb_msg_len += sa_2.sadb_x_sa2_len; iov_cnt++; /* add source address */ iov[iov_cnt].iov_base = &sa_src; iov[iov_cnt].iov_len = sizeof(sa_src); iov_cnt++; iov[iov_cnt].iov_base = &ssrc; iov[iov_cnt].iov_len = SS_LEN(ssrc); smsg.sadb_msg_len += sa_src.sadb_address_len; iov_cnt++; PAD(SS_LEN(ssrc)); /* add destination address */ iov[iov_cnt].iov_base = &sa_dst; iov[iov_cnt].iov_len = sizeof(sa_dst); iov_cnt++; iov[iov_cnt].iov_base = &sdst; iov[iov_cnt].iov_len = SS_LEN(sdst); smsg.sadb_msg_len += sa_dst.sadb_address_len; iov_cnt++; PAD(SS_LEN(sdst)); /* add policy extension */ iov[iov_cnt].iov_base = &sa_policy; iov[iov_cnt].iov_len = sizeof(sa_policy); smsg.sadb_msg_len += sa_policy.sadb_x_policy_len; iov_cnt++; if (sa_policy.sadb_x_policy_type == IPSEC_POLICY_IPSEC) { iov[iov_cnt].iov_base = &sa_ipsec; iov[iov_cnt].iov_len = sizeof(sa_ipsec); iov_cnt++; if (sa_ipsec.sadb_x_ipsecrequest_mode == IPSEC_MODE_TUNNEL) { iov[iov_cnt].iov_base = &slocal; iov[iov_cnt].iov_len = SS_LEN(slocal); iov_cnt++; iov[iov_cnt].iov_base = &speer; iov[iov_cnt].iov_len = SS_LEN(speer); iov_cnt++; PAD(SS_LEN(slocal) + SS_LEN(speer)); } } #undef PAD ret = pfkey_write(env, &smsg, iov, iov_cnt, NULL, NULL); #endif /* __OpenBSD__ */ return (ret); } int pfkey_sa(struct iked *env, uint8_t satype, uint8_t action, struct iked_childsa *sa) { struct sadb_msg smsg; struct sadb_sa sadb; struct sadb_address sa_src, sa_dst, sa_pxy; struct sadb_key sa_authkey, sa_enckey; struct sadb_lifetime sa_ltime_hard, sa_ltime_soft; #if defined(SADB_X_EXT_UDPENCAP) struct sadb_x_udpencap udpencap; #elif defined(HAVE_APPLE_NATT) struct sadb_sa_natt natt; #else struct sadb_x_nat_t_type nat_type; struct sadb_x_nat_t_port nat_sport, nat_dport; #endif #ifdef SADB_X_EXT_TAP struct sadb_x_tag sa_tag; char *tag = NULL; #endif #ifdef SADB_X_EXT_IFACE char iface[IF_NAMESIZE]; struct sadb_x_iface sa_iface; const char *errstr = NULL; uint32_t ifminor; #endif #ifdef SADB_X_EXT_TAP struct sadb_x_tap sa_tap; int dotap = 0; #endif #ifdef SADB_X_EXT_RDOMAIN struct sadb_x_rdomain sa_rdomain; #endif struct sockaddr_storage ssrc, sdst, spxy; #ifndef __OpenBSD__ struct sadb_x_sa2 sa_2; #endif struct sadb_ident *sa_srcid, *sa_dstid; struct iked_lifetime *lt; struct iked_policy *pol; struct iked_addr *dst; struct iovec iov[IOV_CNT]; uint32_t jitter; int iov_cnt; int ret; uint64_t pad = 0; size_t padlen; sa_srcid = sa_dstid = NULL; if (sa->csa_ikesa == NULL || sa->csa_ikesa->sa_policy == NULL) { log_warn("%s: invalid SA and policy", __func__); return (-1); } pol = sa->csa_ikesa->sa_policy; lt = &pol->pol_lifetime; bzero(&ssrc, sizeof(ssrc)); memcpy(&ssrc, &sa->csa_local->addr, sizeof(ssrc)); if (socket_af((struct sockaddr *)&ssrc, 0) == -1) { log_warn("%s: invalid address", __func__); return (-1); } #ifdef __OpenBSD__ dst = (action == IKED_SADB_UPDATE_SA_ADDRESSES && sa->csa_dir == IPSP_DIRECTION_OUT) ? &sa->csa_ikesa->sa_peer_loaded : sa->csa_peer; #else dst = sa->csa_peer; #endif bzero(&sdst, sizeof(sdst)); memcpy(&sdst, &dst->addr, sizeof(sdst)); if (socket_af((struct sockaddr *)&sdst, 0) == -1) { log_warn("%s: invalid address", __func__); return (-1); } bzero(&spxy, sizeof(spxy)); if (dst != sa->csa_peer) { memcpy(&spxy, &sa->csa_peer->addr, sizeof(spxy)); if (socket_af((struct sockaddr *)&spxy, 0) == -1) { log_warn("%s: invalid address", __func__); return (-1); } } bzero(&smsg, sizeof(smsg)); smsg.sadb_msg_version = PF_KEY_V2; smsg.sadb_msg_seq = ++sadb_msg_seq; smsg.sadb_msg_pid = getpid(); smsg.sadb_msg_len = sizeof(smsg) / 8; smsg.sadb_msg_type = action; smsg.sadb_msg_satype = satype; bzero(&sadb, sizeof(sadb)); sadb.sadb_sa_len = sizeof(sadb) / 8; sadb.sadb_sa_exttype = SADB_EXT_SA; sadb.sadb_sa_spi = htonl(sa->csa_spi.spi); sadb.sadb_sa_state = SADB_SASTATE_MATURE; sadb.sadb_sa_replay = 64; #ifdef __OpenBSD__ if (!sa->csa_transport) sadb.sadb_sa_flags |= SADB_X_SAFLAGS_TUNNEL; #else bzero(&sa_2, sizeof(sa_2)); sa_2.sadb_x_sa2_exttype = SADB_X_EXT_SA2; sa_2.sadb_x_sa2_len = sizeof(sa_2) / 8; sa_2.sadb_x_sa2_mode = (sa->csa_transport) ? IPSEC_MODE_TRANSPORT : IPSEC_MODE_TUNNEL; #endif if (sa->csa_esn) { #ifdef SADB_X_SAFLAGS_ESN sadb.sadb_sa_flags |= SADB_X_SAFLAGS_ESN; #else log_warnx("%s: kernel has no support for" " extended sequence numbers (ESN)", __func__); return (-1); #endif } bzero(&sa_src, sizeof(sa_src)); sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(SS_LEN(ssrc))) / 8; sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; bzero(&sa_dst, sizeof(sa_dst)); sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(SS_LEN(sdst))) / 8; sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST; bzero(&sa_pxy, sizeof(sa_pxy)); if (dst != sa->csa_peer) { sa_pxy.sadb_address_len = (sizeof(sa_pxy) + ROUNDUP(SS_LEN(spxy))) / 8; sa_pxy.sadb_address_exttype = SADB_EXT_ADDRESS_PROXY; } bzero(&sa_authkey, sizeof(sa_authkey)); bzero(&sa_enckey, sizeof(sa_enckey)); bzero(&sa_ltime_hard, sizeof(sa_ltime_hard)); bzero(&sa_ltime_soft, sizeof(sa_ltime_soft)); #ifdef SADB_X_EXT_IFACE bzero(&sa_iface, sizeof(sa_iface)); #endif #if defined(SADB_X_EXT_UDPENCAP) bzero(&udpencap, sizeof udpencap); #elif defined(HAVE_APPLE_NATT) bzero(&natt, sizeof(natt)); #else bzero(&nat_type, sizeof(nat_type)); #endif #ifdef SADB_X_EXT_RDOMAIN if (pol->pol_rdomain >= 0) { bzero(&sa_rdomain, sizeof(sa_rdomain)); sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN; sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8; if (satype == SADB_X_SATYPE_IPCOMP) { /* IPCOMP SAs are always in the pol_rdomain */ sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain; sa_rdomain.sadb_x_rdomain_dom2 = pol->pol_rdomain; } else if (sa->csa_dir == IPSP_DIRECTION_OUT) { /* switch rdomain on encrypt/decrypt */ sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain; sa_rdomain.sadb_x_rdomain_dom2 = iked_rdomain; } else { sa_rdomain.sadb_x_rdomain_dom1 = iked_rdomain; sa_rdomain.sadb_x_rdomain_dom2 = pol->pol_rdomain; } } #endif if (action == SADB_DELETE) goto send; if (satype == SADB_SATYPE_ESP && sa->csa_ikesa->sa_udpencap && sa->csa_ikesa->sa_natt) { #if defined(SADB_X_EXT_UDPENCAP) sadb.sadb_sa_flags |= SADB_X_SAFLAGS_UDPENCAP; udpencap.sadb_x_udpencap_exttype = SADB_X_EXT_UDPENCAP; udpencap.sadb_x_udpencap_len = sizeof(udpencap) / 8; udpencap.sadb_x_udpencap_port = sa->csa_ikesa->sa_peer.addr_port; log_debug("%s: udpencap port %d", __func__, ntohs(udpencap.sadb_x_udpencap_port)); #elif defined(HAVE_APPLE_NATT) sadb.sadb_sa_flags |= SADB_X_EXT_NATT; natt.sadb_sa_natt_port = ntohs(sa->csa_ikesa->sa_peer.addr_port); natt.sadb_sa_natt_src_port = sa->csa_ikesa->sa_local.addr_port; log_debug("%s: udpencap port %u", __func__, natt.sadb_sa_natt_port); #else nat_type.sadb_x_nat_t_type_len = sizeof(nat_type) / 8; nat_type.sadb_x_nat_t_type_exttype = SADB_X_EXT_NAT_T_TYPE; nat_type.sadb_x_nat_t_type_type = UDP_ENCAP_ESPINUDP; bzero(&nat_sport, sizeof(nat_sport)); nat_sport.sadb_x_nat_t_port_len = sizeof(nat_sport) / 8; nat_sport.sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT; nat_sport.sadb_x_nat_t_port_port = sa->csa_dir == IPSP_DIRECTION_OUT ? sa->csa_ikesa->sa_local.addr_port : sa->csa_ikesa->sa_peer.addr_port; bzero(&nat_dport, sizeof(nat_dport)); nat_dport.sadb_x_nat_t_port_len = sizeof(nat_dport) / 8; nat_dport.sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT; nat_dport.sadb_x_nat_t_port_port = sa->csa_dir == IPSP_DIRECTION_OUT ? sa->csa_ikesa->sa_peer.addr_port : sa->csa_ikesa->sa_local.addr_port; log_debug("%s: NAT-T: type=%s (%d) sport=%d dport=%d", __func__, (nat_type.sadb_x_nat_t_type_type == UDP_ENCAP_ESPINUDP) ? "UDP encap" : "unknown", nat_type.sadb_x_nat_t_type_type, ntohs(nat_sport.sadb_x_nat_t_port_port), ntohs(nat_dport.sadb_x_nat_t_port_port)); #endif } if (action == IKED_SADB_UPDATE_SA_ADDRESSES) { smsg.sadb_msg_type = SADB_UPDATE; goto send; } if ((action == SADB_ADD || action == SADB_UPDATE) && !sa->csa_persistent && (lt->lt_bytes || lt->lt_seconds)) { sa_ltime_hard.sadb_lifetime_exttype = SADB_EXT_LIFETIME_HARD; sa_ltime_hard.sadb_lifetime_len = sizeof(sa_ltime_hard) / 8; sa_ltime_hard.sadb_lifetime_bytes = lt->lt_bytes; sa_ltime_hard.sadb_lifetime_addtime = lt->lt_seconds; /* double the lifetime for ipcomp; disable byte lifetime */ if (satype == SADB_X_SATYPE_IPCOMP) { sa_ltime_hard.sadb_lifetime_addtime *= 2; sa_ltime_hard.sadb_lifetime_bytes = 0; } sa_ltime_soft.sadb_lifetime_exttype = SADB_EXT_LIFETIME_SOFT; sa_ltime_soft.sadb_lifetime_len = sizeof(sa_ltime_soft) / 8; /* set randomly to 85-95% */ jitter = 850 + arc4random_uniform(100); sa_ltime_soft.sadb_lifetime_bytes = (sa_ltime_hard.sadb_lifetime_bytes * jitter) / 1000; sa_ltime_soft.sadb_lifetime_addtime = (sa_ltime_hard.sadb_lifetime_addtime * jitter) / 1000; } /* XXX handle NULL encryption or NULL auth or combined encr/auth */ if (action == SADB_ADD && !ibuf_length(sa->csa_integrkey) && !ibuf_length(sa->csa_encrkey) && satype != SADB_X_SATYPE_IPCOMP #ifdef SADB_X_SATYPE_IPIP && satype != SADB_X_SATYPE_IPIP #endif ) { log_warnx("%s: no key specified", __func__); return (-1); } if (sa->csa_integrid) if (pfkey_map(pfkey_integr, sa->csa_integrid, &sadb.sadb_sa_auth) == -1) { log_warnx("%s: unsupported integrity algorithm %s", __func__, print_map(sa->csa_integrid, ikev2_xformauth_map)); return (-1); } if (sa->csa_encrid) if (pfkey_map(pfkey_encr, sa->csa_encrid, &sadb.sadb_sa_encrypt) == -1) { log_warnx("%s: unsupported encryption algorithm %s", __func__, print_map(sa->csa_encrid, ikev2_xformencr_map)); return (-1); } if (ibuf_length(sa->csa_integrkey)) { sa_authkey.sadb_key_len = (sizeof(sa_authkey) + ROUNDUP(ibuf_size(sa->csa_integrkey))) / 8; sa_authkey.sadb_key_exttype = SADB_EXT_KEY_AUTH; sa_authkey.sadb_key_bits = 8 * ibuf_size(sa->csa_integrkey); } if (ibuf_length(sa->csa_encrkey)) { sa_enckey.sadb_key_len = (sizeof(sa_enckey) + ROUNDUP(ibuf_size(sa->csa_encrkey))) / 8; sa_enckey.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT; sa_enckey.sadb_key_bits = 8 * ibuf_size(sa->csa_encrkey); } /* we only support deflate */ if (satype == SADB_X_SATYPE_IPCOMP) sadb.sadb_sa_encrypt = SADB_X_CALG_DEFLATE; #if defined __OpenBSD__ /* Note that we need to swap the IDs for incoming SAs (SADB_UPDATE) */ if (action != SADB_UPDATE) { sa_srcid = pfkey_id2ident( IKESA_SRCID(sa->csa_ikesa), SADB_EXT_IDENTITY_SRC); sa_dstid = pfkey_id2ident( IKESA_DSTID(sa->csa_ikesa), SADB_EXT_IDENTITY_DST); } else { sa_srcid = pfkey_id2ident( IKESA_DSTID(sa->csa_ikesa), SADB_EXT_IDENTITY_SRC); sa_dstid = pfkey_id2ident( IKESA_SRCID(sa->csa_ikesa), SADB_EXT_IDENTITY_DST); } #endif #ifdef SADB_X_EXT_TAG tag = sa->csa_ikesa->sa_tag; if (tag != NULL && *tag != '\0') { bzero(&sa_tag, sizeof(sa_tag)); sa_tag.sadb_x_tag_exttype = SADB_X_EXT_TAG; sa_tag.sadb_x_tag_len = (ROUNDUP(strlen(tag) + 1) + sizeof(sa_tag)) / 8; sa_tag.sadb_x_tag_taglen = strlen(tag) + 1; } else tag = NULL; #endif #ifdef SADB_X_EXT_TAP if (pol->pol_tap != 0) { dotap = 1; bzero(&sa_tap, sizeof(sa_tap)); sa_tap.sadb_x_tap_exttype = SADB_X_EXT_TAP; sa_tap.sadb_x_tap_len = sizeof(sa_tap) / 8; sa_tap.sadb_x_tap_unit = pol->pol_tap; } #endif #ifdef SADB_X_EXT_IFACE if (pol->pol_flags & IKED_POLICY_ROUTING) { sa_iface.sadb_x_iface_exttype = SADB_X_EXT_IFACE; sa_iface.sadb_x_iface_len = sizeof(sa_iface) / 8; if (if_indextoname(pol->pol_iface, iface) == 0) { log_warn("%s: unsupported interface %d", __func__, pol->pol_iface); return (-1); } ifminor = strtonum(iface + strlen("sec"), 0, UINT_MAX, &errstr); if (errstr != NULL) { log_warnx("%s: unsupported interface %s", __func__, iface); return (-1); } sa_iface.sadb_x_iface_unit = ifminor; sa_iface.sadb_x_iface_direction = sa->csa_dir; } #endif send: #define PAD(len) \ padlen = ROUNDUP((len)) - (len); \ if (padlen) { \ iov[iov_cnt].iov_base = &pad; \ iov[iov_cnt].iov_len = padlen; \ iov_cnt++; \ } iov_cnt = 0; /* header */ iov[iov_cnt].iov_base = &smsg; iov[iov_cnt].iov_len = sizeof(smsg); iov_cnt++; /* sa */ iov[iov_cnt].iov_base = &sadb; iov[iov_cnt].iov_len = sizeof(sadb); #if defined(HAVE_APPLE_NATT) if (sa->csa_ikesa->sa_udpencap && sa->csa_ikesa->sa_natt) { iov_cnt++; iov[iov_cnt].iov_base = &natt; iov[iov_cnt].iov_len = sizeof(natt); sadb.sadb_sa_len += sizeof(natt) / 8; } #endif smsg.sadb_msg_len += sadb.sadb_sa_len; iov_cnt++; #if !defined __OpenBSD__ /* sa2 */ iov[iov_cnt].iov_base = &sa_2; iov[iov_cnt].iov_len = sizeof(sa_2); smsg.sadb_msg_len += sa_2.sadb_x_sa2_len; iov_cnt++; #endif /* src addr */ iov[iov_cnt].iov_base = &sa_src; iov[iov_cnt].iov_len = sizeof(sa_src); iov_cnt++; iov[iov_cnt].iov_base = &ssrc; iov[iov_cnt].iov_len = SS_LEN(ssrc); smsg.sadb_msg_len += sa_src.sadb_address_len; iov_cnt++; PAD(SS_LEN(ssrc)); /* dst addr */ iov[iov_cnt].iov_base = &sa_dst; iov[iov_cnt].iov_len = sizeof(sa_dst); iov_cnt++; iov[iov_cnt].iov_base = &sdst; iov[iov_cnt].iov_len = SS_LEN(sdst); smsg.sadb_msg_len += sa_dst.sadb_address_len; iov_cnt++; PAD(SS_LEN(sdst)); if (dst != sa->csa_peer) { /* pxy addr */ iov[iov_cnt].iov_base = &sa_pxy; iov[iov_cnt].iov_len = sizeof(sa_pxy); iov_cnt++; iov[iov_cnt].iov_base = &spxy; iov[iov_cnt].iov_len = SS_LEN(spxy); smsg.sadb_msg_len += sa_pxy.sadb_address_len; iov_cnt++; PAD(SS_LEN(spxy)); } if (sa_ltime_soft.sadb_lifetime_len) { /* soft lifetime */ iov[iov_cnt].iov_base = &sa_ltime_soft; iov[iov_cnt].iov_len = sizeof(sa_ltime_soft); smsg.sadb_msg_len += sa_ltime_soft.sadb_lifetime_len; iov_cnt++; } if (sa_ltime_hard.sadb_lifetime_len) { /* hard lifetime */ iov[iov_cnt].iov_base = &sa_ltime_hard; iov[iov_cnt].iov_len = sizeof(sa_ltime_hard); smsg.sadb_msg_len += sa_ltime_hard.sadb_lifetime_len; iov_cnt++; } #if defined(SADB_X_EXT_UDPENCAP) if (udpencap.sadb_x_udpencap_len) { iov[iov_cnt].iov_base = &udpencap; iov[iov_cnt].iov_len = sizeof(udpencap); smsg.sadb_msg_len += udpencap.sadb_x_udpencap_len; iov_cnt++; } #elif !defined(HAVE_APPLE_NATT) if (nat_type.sadb_x_nat_t_type_len) { iov[iov_cnt].iov_base = &nat_type; iov[iov_cnt].iov_len = sizeof(nat_type); smsg.sadb_msg_len += nat_type.sadb_x_nat_t_type_len; iov_cnt++; iov[iov_cnt].iov_base = &nat_sport; iov[iov_cnt].iov_len = sizeof(nat_sport); smsg.sadb_msg_len += nat_sport.sadb_x_nat_t_port_len; iov_cnt++; iov[iov_cnt].iov_base = &nat_dport; iov[iov_cnt].iov_len = sizeof(nat_dport); smsg.sadb_msg_len += nat_dport.sadb_x_nat_t_port_len; iov_cnt++; } #endif if (sa_enckey.sadb_key_len) { /* encryption key */ iov[iov_cnt].iov_base = &sa_enckey; iov[iov_cnt].iov_len = sizeof(sa_enckey); iov_cnt++; iov[iov_cnt].iov_base = ibuf_data(sa->csa_encrkey); iov[iov_cnt].iov_len = ibuf_size(sa->csa_encrkey); smsg.sadb_msg_len += sa_enckey.sadb_key_len; iov_cnt++; PAD(ibuf_size(sa->csa_encrkey)); } if (sa_authkey.sadb_key_len) { /* authentication key */ iov[iov_cnt].iov_base = &sa_authkey; iov[iov_cnt].iov_len = sizeof(sa_authkey); iov_cnt++; iov[iov_cnt].iov_base = ibuf_data(sa->csa_integrkey); iov[iov_cnt].iov_len = ibuf_size(sa->csa_integrkey); smsg.sadb_msg_len += sa_authkey.sadb_key_len; iov_cnt++; PAD(ibuf_size(sa->csa_integrkey)); } #if defined __OpenBSD__ if (sa_srcid) { /* src identity */ iov[iov_cnt].iov_base = sa_srcid; iov[iov_cnt].iov_len = sa_srcid->sadb_ident_len * 8; smsg.sadb_msg_len += sa_srcid->sadb_ident_len; iov_cnt++; } if (sa_dstid) { /* dst identity */ iov[iov_cnt].iov_base = sa_dstid; iov[iov_cnt].iov_len = sa_dstid->sadb_ident_len * 8; smsg.sadb_msg_len += sa_dstid->sadb_ident_len; iov_cnt++; } #endif #ifdef SADB_X_EXT_TAG if (tag != NULL) { /* tag identity */ iov[iov_cnt].iov_base = &sa_tag; iov[iov_cnt].iov_len = sizeof(sa_tag); iov_cnt++; iov[iov_cnt].iov_base = tag; iov[iov_cnt].iov_len = strlen(tag) + 1; smsg.sadb_msg_len += sa_tag.sadb_x_tag_len; iov_cnt++; PAD(strlen(tag) + 1); } #endif #ifdef SADB_X_EXT_IFACE if (sa_iface.sadb_x_iface_len) { iov[iov_cnt].iov_base = &sa_iface; iov[iov_cnt].iov_len = sa_iface.sadb_x_iface_len * 8; smsg.sadb_msg_len += sa_iface.sadb_x_iface_len; iov_cnt++; } #endif #ifdef SADB_X_EXT_TAP if (dotap != 0) { /* enc(4) device tap unit */ iov[iov_cnt].iov_base = &sa_tap; iov[iov_cnt].iov_len = sizeof(sa_tap); smsg.sadb_msg_len += sa_tap.sadb_x_tap_len; iov_cnt++; } #endif #ifdef SADB_X_EXT_RDOMAIN if (pol->pol_rdomain >= 0) { iov[iov_cnt].iov_base = &sa_rdomain; iov[iov_cnt].iov_len = sizeof(sa_rdomain); smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len; iov_cnt++; } #endif #undef PAD ret = pfkey_write(env, &smsg, iov, iov_cnt, NULL, NULL); free(sa_srcid); free(sa_dstid); return ret; } int pfkey_sa_lookup(struct iked *env, struct iked_childsa *sa, uint64_t *last_used) { struct sadb_msg *msg, smsg; struct sadb_address sa_src, sa_dst; struct sadb_sa sadb; #ifdef SADB_X_EXT_RDOMAIN struct iked_policy *pol = sa->csa_ikesa->sa_policy; struct sadb_x_rdomain sa_rdomain; int rdomain; #endif #ifdef SADB_X_EXT_LIFETIME_LASTUSE struct sadb_lifetime *sa_life; #endif struct sockaddr_storage ssrc, sdst; struct iovec iov[IOV_CNT]; uint64_t pad = 0; size_t padlen; uint8_t *data; ssize_t n; int iov_cnt, ret = -1; uint8_t satype; if (last_used) *last_used = 0; if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1) return (-1); bzero(&ssrc, sizeof(ssrc)); memcpy(&ssrc, &sa->csa_local->addr, sizeof(ssrc)); if (socket_af((struct sockaddr *)&ssrc, 0) == -1) { log_warn("%s: invalid address", __func__); return (-1); } bzero(&sdst, sizeof(sdst)); memcpy(&sdst, &sa->csa_peer->addr, sizeof(sdst)); if (socket_af((struct sockaddr *)&sdst, 0) == -1) { log_warn("%s: invalid address", __func__); return (-1); } bzero(&smsg, sizeof(smsg)); smsg.sadb_msg_version = PF_KEY_V2; smsg.sadb_msg_seq = ++sadb_msg_seq; smsg.sadb_msg_pid = getpid(); smsg.sadb_msg_len = sizeof(smsg) / 8; smsg.sadb_msg_type = SADB_GET; smsg.sadb_msg_satype = satype; bzero(&sadb, sizeof(sadb)); sadb.sadb_sa_len = sizeof(sadb) / 8; sadb.sadb_sa_exttype = SADB_EXT_SA; sadb.sadb_sa_spi = htonl(sa->csa_spi.spi); sadb.sadb_sa_state = SADB_SASTATE_MATURE; sadb.sadb_sa_replay = 64; #ifdef SADB_X_EXT_RDOMAIN if (pol->pol_rdomain >= 0) { rdomain = (sa->csa_dir == IPSP_DIRECTION_IN) ? iked_rdomain : pol->pol_rdomain; bzero(&sa_rdomain, sizeof(sa_rdomain)); sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN; sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8; sa_rdomain.sadb_x_rdomain_dom1 = rdomain; } #endif bzero(&sa_src, sizeof(sa_src)); sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(SS_LEN(ssrc))) / 8; sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; bzero(&sa_dst, sizeof(sa_dst)); sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(SS_LEN(sdst))) / 8; sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST; iov_cnt = 0; #define PAD(len) \ padlen = ROUNDUP((len)) - (len); \ if (padlen) { \ iov[iov_cnt].iov_base = &pad; \ iov[iov_cnt].iov_len = padlen; \ iov_cnt++; \ } /* header */ iov[iov_cnt].iov_base = &smsg; iov[iov_cnt].iov_len = sizeof(smsg); iov_cnt++; /* sa */ iov[iov_cnt].iov_base = &sadb; iov[iov_cnt].iov_len = sizeof(sadb); smsg.sadb_msg_len += sadb.sadb_sa_len; iov_cnt++; /* src addr */ iov[iov_cnt].iov_base = &sa_src; iov[iov_cnt].iov_len = sizeof(sa_src); iov_cnt++; iov[iov_cnt].iov_base = &ssrc; iov[iov_cnt].iov_len = SS_LEN(ssrc); smsg.sadb_msg_len += sa_src.sadb_address_len; iov_cnt++; PAD(SS_LEN(ssrc)); /* dst addr */ iov[iov_cnt].iov_base = &sa_dst; iov[iov_cnt].iov_len = sizeof(sa_dst); iov_cnt++; iov[iov_cnt].iov_base = &sdst; iov[iov_cnt].iov_len = SS_LEN(sdst); smsg.sadb_msg_len += sa_dst.sadb_address_len; iov_cnt++; PAD(SS_LEN(sdst)); #ifdef SADB_X_EXT_RDOMAIN if (pol->pol_rdomain >= 0) { iov[iov_cnt].iov_base = &sa_rdomain; iov[iov_cnt].iov_len = sizeof(sa_rdomain); smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len; iov_cnt++; } #endif if ((ret = pfkey_write(env, &smsg, iov, iov_cnt, &data, &n)) != 0) return (-1); msg = (struct sadb_msg *)data; if (msg->sadb_msg_errno != 0) { errno = msg->sadb_msg_errno; ret = -1; if (errno == ESRCH) log_debug("%s: not found", __func__); else log_warn("%s: message", __func__); goto done; } #ifdef SADB_X_EXT_LIFETIME_LASTUSE if (last_used) { if ((sa_life = pfkey_find_ext(data, n, SADB_X_EXT_LIFETIME_LASTUSE)) == NULL) { /* has never been used */ ret = -1; goto done; } *last_used = sa_life->sadb_lifetime_usetime; log_debug("%s: last_used %llu", __func__, *last_used); } #endif #undef PAD done: freezero(data, n); return (ret); } int pfkey_sa_last_used(struct iked *env, struct iked_childsa *sa, uint64_t *last_used) { return pfkey_sa_lookup(env, sa, last_used); } int pfkey_sa_check_exists(struct iked *env, struct iked_childsa *sa) { return pfkey_sa_lookup(env, sa, NULL); } int pfkey_sa_getspi(struct iked *env, uint8_t satype, struct iked_childsa *sa, uint32_t *spip) { struct sadb_msg *msg, smsg; struct sadb_address sa_src, sa_dst; struct sadb_sa *sa_ext; struct sadb_spirange sa_spirange; struct sockaddr_storage ssrc, sdst; struct iovec iov[IOV_CNT]; uint64_t pad = 0; size_t padlen; uint8_t *data; ssize_t n; int iov_cnt, ret = -1; bzero(&ssrc, sizeof(ssrc)); memcpy(&ssrc, &sa->csa_local->addr, sizeof(ssrc)); if (socket_af((struct sockaddr *)&ssrc, 0) == -1) { log_warn("%s: invalid address", __func__); return (-1); } bzero(&sdst, sizeof(sdst)); memcpy(&sdst, &sa->csa_peer->addr, sizeof(sdst)); if (socket_af((struct sockaddr *)&sdst, 0) == -1) { log_warn("%s: invalid address", __func__); return (-1); } bzero(&smsg, sizeof(smsg)); smsg.sadb_msg_version = PF_KEY_V2; smsg.sadb_msg_seq = ++sadb_msg_seq; smsg.sadb_msg_pid = getpid(); smsg.sadb_msg_len = sizeof(smsg) / 8; smsg.sadb_msg_type = SADB_GETSPI; smsg.sadb_msg_satype = satype; bzero(&sa_spirange, sizeof(sa_spirange)); sa_spirange.sadb_spirange_exttype = SADB_EXT_SPIRANGE; sa_spirange.sadb_spirange_len = sizeof(sa_spirange) / 8; sa_spirange.sadb_spirange_min = 0x100; sa_spirange.sadb_spirange_max = (satype == SADB_X_SATYPE_IPCOMP) ? (CPI_PRIVATE_MIN - 1) : 0xffffffff; sa_spirange.sadb_spirange_reserved = 0; bzero(&sa_src, sizeof(sa_src)); sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(SS_LEN(ssrc))) / 8; sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; bzero(&sa_dst, sizeof(sa_dst)); sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(SS_LEN(sdst))) / 8; sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST; #define PAD(len) \ padlen = ROUNDUP((len)) - (len); \ if (padlen) { \ iov[iov_cnt].iov_base = &pad; \ iov[iov_cnt].iov_len = padlen; \ iov_cnt++; \ } iov_cnt = 0; /* header */ iov[iov_cnt].iov_base = &smsg; iov[iov_cnt].iov_len = sizeof(smsg); iov_cnt++; /* SPI range */ iov[iov_cnt].iov_base = &sa_spirange; iov[iov_cnt].iov_len = sizeof(sa_spirange); smsg.sadb_msg_len += sa_spirange.sadb_spirange_len; iov_cnt++; /* src addr */ iov[iov_cnt].iov_base = &sa_src; iov[iov_cnt].iov_len = sizeof(sa_src); iov_cnt++; iov[iov_cnt].iov_base = &ssrc; iov[iov_cnt].iov_len = SS_LEN(ssrc); smsg.sadb_msg_len += sa_src.sadb_address_len; iov_cnt++; PAD(SS_LEN(ssrc)); /* dst addr */ iov[iov_cnt].iov_base = &sa_dst; iov[iov_cnt].iov_len = sizeof(sa_dst); iov_cnt++; iov[iov_cnt].iov_base = &sdst; iov[iov_cnt].iov_len = SS_LEN(sdst); smsg.sadb_msg_len += sa_dst.sadb_address_len; iov_cnt++; PAD(SS_LEN(sdst)); *spip = 0; if ((ret = pfkey_write(env, &smsg, iov, iov_cnt, &data, &n)) != 0) return (-1); msg = (struct sadb_msg *)data; if (msg->sadb_msg_errno != 0) { errno = msg->sadb_msg_errno; log_warn("%s: message", __func__); goto done; } if ((sa_ext = pfkey_find_ext(data, n, SADB_EXT_SA)) == NULL) { log_debug("%s: erroneous reply", __func__); goto done; } *spip = ntohl(sa_ext->sadb_sa_spi); log_debug("%s: spi 0x%08x", __func__, *spip); #undef PAD done: freezero(data, n); return (ret); } #ifdef __OpenBSD__ int pfkey_sagroup(struct iked *env, uint8_t satype1, uint8_t action, struct iked_childsa *sa1, struct iked_childsa *sa2) { struct sadb_msg smsg; struct sadb_sa sadb1, sadb2; struct sadb_address sa_dst1, sa_dst2; struct sockaddr_storage sdst1, sdst2; struct sadb_protocol sa_proto; #ifdef SADB_X_EXT_RDOMAIN struct sadb_x_rdomain sa_rdomain; #endif struct iked_policy *pol; struct iovec iov[IOV_CNT]; uint64_t pad = 0; size_t padlen; int iov_cnt; int group_rdomain; uint8_t satype2; if (pfkey_map(pfkey_satype, sa2->csa_saproto, &satype2) == -1) return (-1); bzero(&sdst1, sizeof(sdst1)); memcpy(&sdst1, &sa1->csa_peer->addr, sizeof(sdst1)); if (socket_af((struct sockaddr *)&sdst1, 0) == -1) { log_warnx("%s: unsupported address family %d", __func__, sdst1.ss_family); return (-1); } bzero(&sdst2, sizeof(sdst2)); memcpy(&sdst2, &sa2->csa_peer->addr, sizeof(sdst2)); if (socket_af((struct sockaddr *)&sdst2, 0) == -1) { log_warnx("%s: unsupported address family %d", __func__, sdst2.ss_family); return (-1); } bzero(&smsg, sizeof(smsg)); smsg.sadb_msg_version = PF_KEY_V2; smsg.sadb_msg_seq = ++sadb_msg_seq; smsg.sadb_msg_pid = getpid(); smsg.sadb_msg_len = sizeof(smsg) / 8; smsg.sadb_msg_type = action; smsg.sadb_msg_satype = satype1; bzero(&sadb1, sizeof(sadb1)); sadb1.sadb_sa_len = sizeof(sadb1) / 8; sadb1.sadb_sa_exttype = SADB_EXT_SA; sadb1.sadb_sa_spi = htonl(sa1->csa_spi.spi); sadb1.sadb_sa_state = SADB_SASTATE_MATURE; bzero(&sadb2, sizeof(sadb2)); sadb2.sadb_sa_len = sizeof(sadb2) / 8; sadb2.sadb_sa_exttype = SADB_X_EXT_SA2; sadb2.sadb_sa_spi = htonl(sa2->csa_spi.spi); sadb2.sadb_sa_state = SADB_SASTATE_MATURE; /* Incoming SA1 (IPCOMP) and SA2 (ESP) are in different/other rdomain */ #ifdef SADB_X_EXT_RDOMAIN group_rdomain = (pol = sa1->csa_ikesa->sa_policy) != NULL && pol->pol_rdomain >= 0 && satype1 == SADB_X_SATYPE_IPCOMP && satype2 == SADB_SATYPE_ESP; if (group_rdomain) { bzero(&sa_rdomain, sizeof(sa_rdomain)); sa_rdomain.sadb_x_rdomain_exttype = SADB_X_EXT_RDOMAIN; sa_rdomain.sadb_x_rdomain_len = sizeof(sa_rdomain) / 8; if (sa1->csa_dir == IPSP_DIRECTION_IN) { /* only ESP SA is iked's rdomain */ sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain; sa_rdomain.sadb_x_rdomain_dom2 = iked_rdomain; } else { /* both SAs are in pol_rdomain */ sa_rdomain.sadb_x_rdomain_dom1 = pol->pol_rdomain; sa_rdomain.sadb_x_rdomain_dom2 = pol->pol_rdomain; } } #endif iov_cnt = 0; bzero(&sa_dst1, sizeof(sa_dst1)); sa_dst1.sadb_address_exttype = SADB_EXT_ADDRESS_DST; sa_dst1.sadb_address_len = (sizeof(sa_dst1) + ROUNDUP(SS_LEN(sdst1))) / 8; bzero(&sa_dst2, sizeof(sa_dst2)); sa_dst2.sadb_address_exttype = SADB_X_EXT_DST2; sa_dst2.sadb_address_len = (sizeof(sa_dst2) + ROUNDUP(SS_LEN(sdst2))) / 8; bzero(&sa_proto, sizeof(sa_proto)); sa_proto.sadb_protocol_exttype = SADB_X_EXT_SATYPE2; sa_proto.sadb_protocol_len = sizeof(sa_proto) / 8; sa_proto.sadb_protocol_direction = 0; sa_proto.sadb_protocol_proto = satype2; #define PAD(len) \ padlen = ROUNDUP((len)) - (len); \ if (padlen) { \ iov[iov_cnt].iov_base = &pad; \ iov[iov_cnt].iov_len = padlen; \ iov_cnt++; \ } /* header */ iov[iov_cnt].iov_base = &smsg; iov[iov_cnt].iov_len = sizeof(smsg); iov_cnt++; /* sa */ iov[iov_cnt].iov_base = &sadb1; iov[iov_cnt].iov_len = sizeof(sadb1); smsg.sadb_msg_len += sadb1.sadb_sa_len; iov_cnt++; /* dst addr */ iov[iov_cnt].iov_base = &sa_dst1; iov[iov_cnt].iov_len = sizeof(sa_dst1); iov_cnt++; iov[iov_cnt].iov_base = &sdst1; iov[iov_cnt].iov_len = SS_LEN(sdst1); smsg.sadb_msg_len += sa_dst1.sadb_address_len; iov_cnt++; PAD(sdst1.ss_len); /* second sa */ iov[iov_cnt].iov_base = &sadb2; iov[iov_cnt].iov_len = sizeof(sadb2); smsg.sadb_msg_len += sadb2.sadb_sa_len; iov_cnt++; /* second dst addr */ iov[iov_cnt].iov_base = &sa_dst2; iov[iov_cnt].iov_len = sizeof(sa_dst2); iov_cnt++; iov[iov_cnt].iov_base = &sdst2; iov[iov_cnt].iov_len = SS_LEN(sdst2); smsg.sadb_msg_len += sa_dst2.sadb_address_len; iov_cnt++; PAD(sdst2.ss_len); /* SA type */ iov[iov_cnt].iov_base = &sa_proto; iov[iov_cnt].iov_len = sizeof(sa_proto); smsg.sadb_msg_len += sa_proto.sadb_protocol_len; iov_cnt++; #ifdef SADB_X_EXT_RDOMAIN /* SA1 and SA2 are from different rdomains */ if (group_rdomain) { iov[iov_cnt].iov_base = &sa_rdomain; iov[iov_cnt].iov_len = sizeof(sa_rdomain); smsg.sadb_msg_len += sa_rdomain.sadb_x_rdomain_len; iov_cnt++; } #endif #undef PAD return (pfkey_write(env, &smsg, iov, iov_cnt, NULL, NULL)); } #endif int pfkey_write(struct iked *env, struct sadb_msg *smsg, struct iovec *iov, int iov_cnt, uint8_t **datap, ssize_t *lenp) { ssize_t n, len = smsg->sadb_msg_len * 8; int ret = -1; if (sadb_decoupled) { switch (smsg->sadb_msg_type) { case SADB_GETSPI: /* we need to get a new SPI from the kernel */ break; default: if (datap || lenp) { log_warnx("%s: pfkey not coupled", __func__); return (-1); } /* ignore request */ return (0); } } /* Delete event to poll() in pfkey_reply() */ event_del(&env->sc_pfkeyev); if ((n = writev(env->sc_pfkey, iov, iov_cnt)) == -1) { log_warn("%s: writev failed: type %u len %zd", __func__, smsg->sadb_msg_type, len); goto done; } else if (n != len) { log_warn("%s: short write", __func__); goto done; } ret = pfkey_reply(env->sc_pfkey, datap, lenp); done: event_add(&env->sc_pfkeyev, NULL); return (ret); } /* wait for pfkey response and returns 0 for ok, -1 for error, -2 for timeout */ int pfkey_reply(int fd, uint8_t **datap, ssize_t *lenp) { struct pfkey_message *pm; struct sadb_msg hdr; ssize_t len; uint8_t *data; struct pollfd pfd[1]; int n; pfd[0].fd = fd; pfd[0].events = POLLIN; for (;;) { /* * We should actually expect the reply to get lost * as PF_KEY is an unreliable service per the specs. * Currently we do this by setting a short timeout, * and if it is not readable in that time, we fail * the read. */ n = poll(pfd, 1, PFKEY_REPLY_TIMEOUT / 1000); if (n == -1) { log_warn("%s: poll() failed", __func__); return (-1); } if (n == 0) { log_warnx("%s: no reply from PF_KEY", __func__); return (-2); /* retry */ } if (recv(fd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) { log_warn("%s: short recv", __func__); return (-1); } if (hdr.sadb_msg_version != PF_KEY_V2) { log_warnx("%s: wrong pfkey version", __func__); return (-1); } if ((data = reallocarray(NULL, hdr.sadb_msg_len, PFKEYV2_CHUNK)) == NULL) { log_warn("%s: malloc", __func__); return (-1); } len = hdr.sadb_msg_len * PFKEYV2_CHUNK; if (read(fd, data, len) != len) { log_warnx("%s: short read", __func__); free(data); return (-1); } /* XXX: Only one message can be outstanding. */ if (hdr.sadb_msg_seq == sadb_msg_seq && hdr.sadb_msg_pid == (uint32_t)getpid()) break; /* ignore messages for other processes */ if (hdr.sadb_msg_pid != 0 && hdr.sadb_msg_pid != (uint32_t)getpid()) { free(data); continue; } /* not the reply, enqueue */ if ((pm = malloc(sizeof(*pm))) == NULL) { log_warn("%s: malloc", __func__); free(data); return (-1); } pm->pm_data = data; pm->pm_length = len; SIMPLEQ_INSERT_TAIL(&pfkey_postponed, pm, pm_entry); evtimer_add(&pfkey_timer_ev, &pfkey_timer_tv); } if (datap) { *datap = data; if (lenp) *lenp = len; } else free(data); if (datap == NULL && hdr.sadb_msg_errno != 0) { errno = hdr.sadb_msg_errno; if (errno != EEXIST) { if (errno == ESRCH) log_debug("%s: not found", __func__); else log_warn("%s: message", __func__); return (-1); } } return (0); } int pfkey_flow_add(struct iked *env, struct iked_flow *flow) { uint8_t satype; if (flow->flow_loaded) return (0); if (pfkey_map(pfkey_satype, flow->flow_saproto, &satype) == -1) return (-1); if (pfkey_flow(env, satype, SADB_X_ADDFLOW, flow) == -1) return (-1); flow->flow_loaded = 1; return (0); } int pfkey_flow_delete(struct iked *env, struct iked_flow *flow) { uint8_t satype; if (!flow->flow_loaded) return (0); if (pfkey_map(pfkey_satype, flow->flow_saproto, &satype) == -1) return (-1); if (pfkey_flow(env, satype, SADB_X_DELFLOW, flow) == -1) return (-1); flow->flow_loaded = 0; return (0); } int pfkey_sa_init(struct iked *env, struct iked_childsa *sa, uint32_t *spi) { uint8_t satype; if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1) return (-1); if (pfkey_sa_getspi(env, satype, sa, spi) == -1) return (-1); log_debug("%s: new spi 0x%08x", __func__, *spi); return (0); } int pfkey_sa_add(struct iked *env, struct iked_childsa *sa, struct iked_childsa *last) { uint8_t satype; unsigned int cmd; int rval; if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1) return (-1); if (sa->csa_allocated || sa->csa_loaded) cmd = SADB_UPDATE; else cmd = SADB_ADD; log_debug("%s: %s spi %s", __func__, cmd == SADB_ADD ? "add": "update", print_spi(sa->csa_spi.spi, 4)); rval = pfkey_sa(env, satype, cmd, sa); if (rval != 0) { if (cmd == SADB_ADD) { if (rval == -2) { /* timeout: check for existence */ if (pfkey_sa_check_exists(env, sa) == 0) { log_debug("%s: SA exists after timeout", __func__); goto loaded; } } (void)pfkey_sa_delete(env, sa); return (-1); } if (sa->csa_allocated && !sa->csa_loaded && errno == ESRCH) { /* Needed for recoupling local SAs */ log_debug("%s: SADB_UPDATE on local SA returned ESRCH," " trying SADB_ADD", __func__); if (pfkey_sa(env, satype, SADB_ADD, sa) == -1) return (-1); } else { return (-1); } } loaded: if (last != NULL) { #ifdef __OpenBSD__ if (pfkey_sagroup(env, satype, SADB_X_GRPSPIS, sa, last) == -1) { (void)pfkey_sa_delete(env, sa); return (-1); } #endif } sa->csa_loaded = 1; return (0); } int pfkey_sa_update_addresses(struct iked *env, struct iked_childsa *sa) { uint8_t satype; if (!sa->csa_ikesa) return (-1); /* check if peer has changed */ if (sa->csa_ikesa->sa_peer_loaded.addr.ss_family == AF_UNSPEC || memcmp(&sa->csa_ikesa->sa_peer_loaded, &sa->csa_ikesa->sa_peer, sizeof(sa->csa_ikesa->sa_peer_loaded)) == 0) return (0); if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1) return (-1); log_debug("%s: spi %s", __func__, print_spi(sa->csa_spi.spi, 4)); return pfkey_sa(env, satype, IKED_SADB_UPDATE_SA_ADDRESSES, sa); } int pfkey_sa_delete(struct iked *env, struct iked_childsa *sa) { uint8_t satype; if (!sa->csa_loaded || sa->csa_spi.spi == 0) return (0); if (pfkey_map(pfkey_satype, sa->csa_saproto, &satype) == -1) return (-1); if (pfkey_sa(env, satype, SADB_DELETE, sa) == -1 && pfkey_sa_check_exists(env, sa) == 0) return (-1); sa->csa_loaded = 0; return (0); } int pfkey_flush(struct iked *env) { struct sadb_msg smsg; struct iovec iov[IOV_CNT]; int iov_cnt; bzero(&smsg, sizeof(smsg)); smsg.sadb_msg_version = PF_KEY_V2; smsg.sadb_msg_seq = ++sadb_msg_seq; smsg.sadb_msg_pid = getpid(); smsg.sadb_msg_len = sizeof(smsg) / 8; smsg.sadb_msg_type = SADB_FLUSH; smsg.sadb_msg_satype = SADB_SATYPE_UNSPEC; iov_cnt = 0; iov[iov_cnt].iov_base = &smsg; iov[iov_cnt].iov_len = sizeof(smsg); iov_cnt++; return (pfkey_write(env, &smsg, iov, iov_cnt, NULL, NULL)); } struct sadb_ident * pfkey_id2ident(struct iked_id *id, unsigned int exttype) { char idstr[IKED_ID_SIZE]; unsigned int type; size_t len; struct sadb_ident *sa_id; switch (id->id_type) { case IKEV2_ID_FQDN: type = SADB_IDENTTYPE_FQDN; break; case IKEV2_ID_UFQDN: type = SADB_IDENTTYPE_USERFQDN; break; case IKEV2_ID_IPV4: case IKEV2_ID_IPV6: type = SADB_IDENTTYPE_PREFIX; break; #ifdef SADB_IDENTTYPE_ASN1_DN case IKEV2_ID_ASN1_DN: type = SADB_IDENTTYPE_ASN1_DN; break; #endif case IKEV2_ID_ASN1_GN: case IKEV2_ID_KEY_ID: case IKEV2_ID_NONE: default: /* XXX not implemented/supported by PFKEY */ return (NULL); } bzero(&idstr, sizeof(idstr)); if (ikev2_print_id(id, idstr, sizeof(idstr)) == -1) return (NULL); len = ROUNDUP(strlen(idstr) + 1) + sizeof(*sa_id); if ((sa_id = calloc(1, len)) == NULL) return (NULL); strlcpy((char *)(sa_id + 1), idstr, ROUNDUP(strlen(idstr) + 1)); sa_id->sadb_ident_type = type; sa_id->sadb_ident_len = len / 8; sa_id->sadb_ident_exttype = exttype; return (sa_id); } int pfkey_socket(struct iked *env) { int fd; if (privsep_process != PROC_PARENT) fatal("pfkey_socket: called from unprivileged process"); if ((fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) == -1) fatal("pfkey_socket: failed to open PF_KEY socket"); return (fd); } void pfkey_init(struct iked *env, int fd) { struct sadb_msg smsg; struct iovec iov; iked_rdomain = getrtable(); /* Set up a timer to process messages deferred by the pfkey_reply */ pfkey_timer_tv.tv_sec = 1; pfkey_timer_tv.tv_usec = 0; evtimer_set(&pfkey_timer_ev, pfkey_timer_cb, env); /* Register the pfkey socket event handler */ env->sc_pfkey = fd; event_set(&env->sc_pfkeyev, env->sc_pfkey, EV_READ|EV_PERSIST, pfkey_dispatch, env); event_add(&env->sc_pfkeyev, NULL); pfkey_flush(env); /* Register it to get ESP and AH acquires from the kernel */ bzero(&smsg, sizeof(smsg)); smsg.sadb_msg_version = PF_KEY_V2; smsg.sadb_msg_seq = ++sadb_msg_seq; smsg.sadb_msg_pid = getpid(); smsg.sadb_msg_len = sizeof(smsg) / 8; smsg.sadb_msg_type = SADB_REGISTER; smsg.sadb_msg_satype = SADB_SATYPE_ESP; iov.iov_base = &smsg; iov.iov_len = sizeof(smsg); if (pfkey_write(env, &smsg, &iov, 1, NULL, NULL)) fatal("pfkey_init: failed to set up ESP acquires"); bzero(&smsg, sizeof(smsg)); smsg.sadb_msg_version = PF_KEY_V2; smsg.sadb_msg_seq = ++sadb_msg_seq; smsg.sadb_msg_pid = getpid(); smsg.sadb_msg_len = sizeof(smsg) / 8; smsg.sadb_msg_type = SADB_REGISTER; smsg.sadb_msg_satype = SADB_SATYPE_AH; iov.iov_base = &smsg; iov.iov_len = sizeof(smsg); if (pfkey_write(env, &smsg, &iov, 1, NULL, NULL)) fatal("pfkey_init: failed to set up AH acquires"); } void * pfkey_find_ext(uint8_t *data, ssize_t len, int type) { struct sadb_ext *ext = (struct sadb_ext *)(data + sizeof(struct sadb_msg)); while (ext && ((uint8_t *)ext - data < len)) { if (ext->sadb_ext_type == type) return (ext); ext = (struct sadb_ext *)((uint8_t *)ext + ext->sadb_ext_len * PFKEYV2_CHUNK); } return (NULL); } void pfkey_dispatch(int fd, short event, void *arg) { struct iked *env = (struct iked *)arg; struct pfkey_message pm, *pmp; struct sadb_msg hdr; ssize_t len; uint8_t *data; if (recv(fd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) { log_warn("%s: short recv", __func__); return; } if (hdr.sadb_msg_version != PF_KEY_V2) { log_warnx("%s: wrong pfkey version", __func__); return; } if ((data = reallocarray(NULL, hdr.sadb_msg_len, PFKEYV2_CHUNK)) == NULL) { log_warn("%s: malloc", __func__); return; } len = hdr.sadb_msg_len * PFKEYV2_CHUNK; if (read(fd, data, len) != len) { log_warn("%s: short read", __func__); free(data); return; } /* Try postponed requests first, so we do in-order processing */ if (!SIMPLEQ_EMPTY(&pfkey_postponed)) pfkey_timer_cb(0, 0, env); pm.pm_data = data; pm.pm_length = len; if (pfkey_process(env, &pm) == -1 && (pmp = calloc(1, sizeof(*pmp))) != NULL) { pmp->pm_data = data; pmp->pm_length = len; log_debug("%s: pfkey_process is busy, retry later", __func__); SIMPLEQ_INSERT_TAIL(&pfkey_postponed, pmp, pm_entry); evtimer_add(&pfkey_timer_ev, &pfkey_timer_tv); } else { free(data); } } void pfkey_timer_cb(int unused, short event, void *arg) { struct iked *env = arg; struct pfkey_message *pm; SIMPLEQ_INIT(&pfkey_retry); while (!SIMPLEQ_EMPTY(&pfkey_postponed)) { pm = SIMPLEQ_FIRST(&pfkey_postponed); SIMPLEQ_REMOVE_HEAD(&pfkey_postponed, pm_entry); if (pfkey_process(env, pm) == -1) { log_debug("%s: pfkey_process is busy, retry later", __func__); SIMPLEQ_INSERT_TAIL(&pfkey_retry, pm, pm_entry); } else { free(pm->pm_data); free(pm); } } /* move from retry to postponed */ SIMPLEQ_CONCAT(&pfkey_postponed, &pfkey_retry); if (!SIMPLEQ_EMPTY(&pfkey_postponed)) evtimer_add(&pfkey_timer_ev, &pfkey_timer_tv); } /* * pfkey_process returns 0 if the message has been processed and -1 if * the system is busy and the message should be passed again, later. */ int pfkey_process(struct iked *env, struct pfkey_message *pm) { struct iked_spi spi; struct sadb_sa *sa; struct sadb_lifetime *sa_ltime; struct sadb_msg *hdr; struct sadb_msg smsg; struct iked_addr peer; struct iked_flow flow; struct sadb_address *sa_addr; struct sockaddr *ssrc, *sdst, *speer; #ifdef __OpenBSD__ struct sadb_protocol *sa_proto; struct sadb_x_policy sa_pol; struct sockaddr *smask, *dmask; const char *errmsg = NULL; #else struct sadb_x_policy *sa_pol; #endif struct iovec iov[IOV_CNT]; int ret = 0, iov_cnt; uint8_t *reply; ssize_t rlen; uint8_t *data = pm->pm_data; ssize_t len = pm->pm_length; size_t slen; if (!env || !data || !len) return (0); hdr = (struct sadb_msg *)data; switch (hdr->sadb_msg_type) { case SADB_ACQUIRE: bzero(&flow, sizeof(flow)); bzero(&peer, sizeof(peer)); if ((sa_addr = pfkey_find_ext(data, len, SADB_EXT_ADDRESS_DST)) == NULL) { log_debug("%s: no peer address", __func__); return (0); } speer = (struct sockaddr *)(sa_addr + 1); peer.addr_af = speer->sa_family; peer.addr_port = htons(socket_getport(speer)); if ((slen = SA_LEN(speer)) > sizeof(peer.addr)) { log_debug("%s: invalid peer address len", __func__); return (0); } memcpy(&peer.addr, speer, slen); if (socket_af((struct sockaddr *)&peer.addr, peer.addr_port) == -1) { log_debug("%s: invalid address", __func__); return (0); } flow.flow_peer = &peer; log_debug("%s: acquire request (peer %s)", __func__, print_addr(speer)); #ifdef __OpenBSD__ /* get the matching flow */ bzero(&smsg, sizeof(smsg)); smsg.sadb_msg_version = PF_KEY_V2; smsg.sadb_msg_seq = ++sadb_msg_seq; smsg.sadb_msg_pid = getpid(); smsg.sadb_msg_len = sizeof(smsg) / 8; smsg.sadb_msg_type = SADB_X_ASKPOLICY; iov_cnt = 0; iov[iov_cnt].iov_base = &smsg; iov[iov_cnt].iov_len = sizeof(smsg); iov_cnt++; bzero(&sa_pol, sizeof(sa_pol)); sa_pol.sadb_x_policy_exttype = SADB_X_EXT_POLICY; sa_pol.sadb_x_policy_len = sizeof(sa_pol) / 8; sa_pol.sadb_x_policy_seq = hdr->sadb_msg_seq; iov[iov_cnt].iov_base = &sa_pol; iov[iov_cnt].iov_len = sizeof(sa_pol); smsg.sadb_msg_len += sizeof(sa_pol) / 8; iov_cnt++; if (pfkey_write(env, &smsg, iov, iov_cnt, &reply, &rlen)) { log_warnx("%s: failed to get a policy", __func__); return (0); } if ((sa_addr = pfkey_find_ext(reply, rlen, SADB_X_EXT_SRC_FLOW)) == NULL) { errmsg = "flow source address"; goto out; } ssrc = (struct sockaddr *)(sa_addr + 1); flow.flow_src.addr_af = ssrc->sa_family; flow.flow_src.addr_port = htons(socket_getport(ssrc)); if ((slen = ssrc->sa_len) > sizeof(flow.flow_src.addr)) { log_debug("%s: invalid src address len", __func__); goto out; } memcpy(&flow.flow_src.addr, ssrc, slen); if (socket_af((struct sockaddr *)&flow.flow_src.addr, flow.flow_src.addr_port) == -1) { log_debug("%s: invalid address", __func__); goto out; } if ((sa_addr = pfkey_find_ext(reply, rlen, SADB_X_EXT_DST_FLOW)) == NULL) { errmsg = "flow destination address"; goto out; } sdst = (struct sockaddr *)(sa_addr + 1); flow.flow_dst.addr_af = sdst->sa_family; flow.flow_dst.addr_port = htons(socket_getport(sdst)); if ((slen = sdst->sa_len) > sizeof(flow.flow_dst.addr)) { log_debug("%s: invalid dst address len", __func__); goto out; } memcpy(&flow.flow_dst.addr, sdst, slen); if (socket_af((struct sockaddr *)&flow.flow_dst.addr, flow.flow_dst.addr_port) == -1) { log_debug("%s: invalid address", __func__); goto out; } if ((sa_addr = pfkey_find_ext(reply, rlen, SADB_X_EXT_SRC_MASK)) == NULL) { errmsg = "flow source mask"; goto out; } smask = (struct sockaddr *)(sa_addr + 1); switch (smask->sa_family) { case AF_INET: flow.flow_src.addr_mask = mask2prefixlen((struct sockaddr *)smask); if (flow.flow_src.addr_mask != 32) flow.flow_src.addr_net = 1; break; case AF_INET6: flow.flow_src.addr_mask = mask2prefixlen6((struct sockaddr *)smask); if (flow.flow_src.addr_mask != 128) flow.flow_src.addr_net = 1; break; default: log_debug("%s: bad address family", __func__); goto out; } if ((sa_addr = pfkey_find_ext(reply, rlen, SADB_X_EXT_DST_MASK)) == NULL) { errmsg = "flow destination mask"; goto out; } dmask = (struct sockaddr *)(sa_addr + 1); switch (dmask->sa_family) { case AF_INET: flow.flow_dst.addr_mask = mask2prefixlen((struct sockaddr *)dmask); if (flow.flow_src.addr_mask != 32) flow.flow_src.addr_net = 1; break; case AF_INET6: flow.flow_dst.addr_mask = mask2prefixlen6((struct sockaddr *)dmask); if (flow.flow_src.addr_mask != 128) flow.flow_src.addr_net = 1; break; default: log_debug("%s: bad address family", __func__); goto out; } switch (hdr->sadb_msg_satype) { case SADB_SATYPE_AH: flow.flow_saproto = IKEV2_SAPROTO_AH; break; case SADB_SATYPE_ESP: flow.flow_saproto = IKEV2_SAPROTO_ESP; break; case SADB_X_SATYPE_IPCOMP: flow.flow_saproto = IKEV2_SAPROTO_IPCOMP; break; } if ((sa_proto = pfkey_find_ext(reply, rlen, SADB_X_EXT_FLOW_TYPE)) == NULL) { errmsg = "flow protocol"; goto out; } flow.flow_dir = sa_proto->sadb_protocol_direction; flow.flow_rdomain = -1; /* XXX get from kernel */ log_debug("%s: flow %s from %s/%s to %s/%s via %s", __func__, flow.flow_dir == IPSP_DIRECTION_IN ? "in" : "out", print_addr(ssrc), print_addr(smask), print_addr(sdst), print_addr(dmask), print_addr(speer)); ret = ikev2_child_sa_acquire(env, &flow); out: if (errmsg) log_warnx("%s: %s wasn't found", __func__, errmsg); free(reply); break; #else /* * The SADB_ACQUIRE message only have the local and peer * addresses. We need to get the flow addresses via a * SADB_X_SPDGET message. */ sa_pol = pfkey_find_ext(data, len, SADB_X_EXT_POLICY); if (sa_pol == NULL) { log_debug("%s: no policy extension", __func__); break; } flow.flow_dir = sa_pol->sadb_x_policy_dir; flow.flow_rdomain = -1; iov_cnt = 0; bzero(&smsg, sizeof(smsg)); smsg.sadb_msg_version = PF_KEY_V2; smsg.sadb_msg_seq = ++sadb_msg_seq; smsg.sadb_msg_pid = getpid(); smsg.sadb_msg_len = (sizeof(smsg) + sizeof(*sa_pol))/ 8; smsg.sadb_msg_type = SADB_X_SPDGET; smsg.sadb_msg_satype = SADB_SATYPE_UNSPEC; iov[iov_cnt].iov_base = &smsg; iov[iov_cnt].iov_len = sizeof(smsg); iov_cnt++; sa_pol->sadb_x_policy_len = sizeof(*sa_pol) / 8; iov[iov_cnt].iov_base = sa_pol; iov[iov_cnt].iov_len = sizeof(*sa_pol); iov_cnt++; if (pfkey_write(env, &smsg, iov, iov_cnt, &reply, &rlen)) { log_warnx("%s: failed to get a policy", __func__); break; } sa_addr = pfkey_find_ext(reply, rlen, SADB_EXT_ADDRESS_SRC); if (sa_addr == NULL) { log_debug("%s: no src in ext_policy upcall", __func__); free(reply); break; } ssrc = (struct sockaddr*)(sa_addr + 1); memcpy(&flow.flow_src.addr, ssrc, sizeof(*ssrc)); flow.flow_src.addr_port = htons(socket_getport(ssrc)); flow.flow_src.addr_mask = sa_addr->sadb_address_prefixlen; if (socket_af((struct sockaddr *)&flow.flow_src.addr, flow.flow_src.addr_port) == -1) { log_debug("%s: invalid src address", __func__); free(reply); break; } sa_addr = pfkey_find_ext(reply, rlen, SADB_EXT_ADDRESS_DST); if (sa_addr == NULL) { log_debug("%s: no dst in ext_policy upcall", __func__); free(reply); break; } sdst = (struct sockaddr*)(sa_addr + 1); memcpy(&flow.flow_dst.addr, sdst, sizeof(*sdst)); flow.flow_dst.addr_port = htons(socket_getport(sdst)); flow.flow_dst.addr_mask = sa_addr->sadb_address_prefixlen; if (socket_af((struct sockaddr *)&flow.flow_dst.addr, flow.flow_dst.addr_port) == -1) { log_debug("%s: invalid dst address", __func__); free(reply); break; } switch (hdr->sadb_msg_satype) { case SADB_SATYPE_AH: flow.flow_saproto = IKEV2_SAPROTO_AH; break; case SADB_SATYPE_ESP: flow.flow_saproto = IKEV2_SAPROTO_ESP; break; case SADB_X_SATYPE_IPCOMP: flow.flow_saproto = IKEV2_SAPROTO_IPCOMP; break; } log_debug("%s: flow %s from %s/%d to %s/%d via %s", __func__, flow.flow_dir == IPSP_DIRECTION_IN ? "in" : "out", print_addr(ssrc), flow.flow_src.addr_mask, print_addr(sdst), flow.flow_dst.addr_mask, print_addr(speer)); /* Free after the debug log above! */ free(reply); ret = ikev2_child_sa_acquire(env, &flow); break; #endif /* __OpenBSD__ */ case SADB_EXPIRE: if ((sa = pfkey_find_ext(data, len, SADB_EXT_SA)) == NULL) { log_warnx("%s: SA extension wasn't found", __func__); return (0); } if ((sa_ltime = pfkey_find_ext(data, len, SADB_EXT_LIFETIME_SOFT)) == NULL && (sa_ltime = pfkey_find_ext(data, len, SADB_EXT_LIFETIME_HARD)) == NULL) { log_warnx("%s: lifetime extension wasn't found", __func__); return (0); } spi.spi = ntohl(sa->sadb_sa_spi); spi.spi_size = 4; switch (hdr->sadb_msg_satype) { case SADB_SATYPE_AH: spi.spi_protoid = IKEV2_SAPROTO_AH; break; case SADB_SATYPE_ESP: spi.spi_protoid = IKEV2_SAPROTO_ESP; break; case SADB_X_SATYPE_IPCOMP: spi.spi_size = 2; spi.spi_protoid = IKEV2_SAPROTO_IPCOMP; break; default: log_warnx("%s: unsupported SA type %d spi %s", __func__, hdr->sadb_msg_satype, print_spi(spi.spi, spi.spi_size)); return (0); } log_debug("%s: SA %s is expired, pending %s", __func__, print_spi(spi.spi, spi.spi_size), sa_ltime->sadb_lifetime_exttype == SADB_EXT_LIFETIME_SOFT ? "rekeying" : "deletion"); if (sa_ltime->sadb_lifetime_exttype == SADB_EXT_LIFETIME_SOFT) ret = ikev2_child_sa_rekey(env, &spi); else ret = ikev2_child_sa_drop(env, &spi); break; } return (ret); } openiked-7.4/iked/policy.c000066400000000000000000001027761477554731100155740ustar00rootroot00000000000000/* $OpenBSD: policy.c,v 1.98 2024/02/03 00:54:14 jsg Exp $ */ /* * Copyright (c) 2020-2021 Tobias Heider * Copyright (c) 2010-2013 Reyk Floeter * Copyright (c) 2001 Daniel Hartmeier * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" #include "ikev2.h" static __inline int sa_cmp(struct iked_sa *, struct iked_sa *); static __inline int sa_dstid_cmp(struct iked_sa *, struct iked_sa *); static __inline int user_cmp(struct iked_user *, struct iked_user *); static __inline int childsa_cmp(struct iked_childsa *, struct iked_childsa *); static __inline int flow_cmp(struct iked_flow *, struct iked_flow *); static __inline int addr_cmp(struct iked_addr *, struct iked_addr *, int); static __inline int ts_insert_unique(struct iked_addr *, struct iked_tss *, int); static int policy_test_flows(struct iked_policy *, struct iked_policy *); static int proposals_match(struct iked_proposal *, struct iked_proposal *, struct iked_transform **, int, int); void policy_init(struct iked *env) { TAILQ_INIT(&env->sc_policies); TAILQ_INIT(&env->sc_ocsp); RB_INIT(&env->sc_users); RB_INIT(&env->sc_sas); RB_INIT(&env->sc_dstid_sas); RB_INIT(&env->sc_activesas); RB_INIT(&env->sc_activeflows); } /* * Lookup an iked policy matching the IKE_AUTH message msg * and store a pointer to the found policy in msg. If no policy * matches a pointer to the default policy is stored in msg. * If 'proposals' is not NULL policy_lookup only returns policies * compatible with 'proposals'. * * Returns 0 on success and -1 if no matching policy was * found and no default exists. */ int policy_lookup(struct iked *env, struct iked_message *msg, struct iked_proposals *proposals, struct iked_flows *flows, int nflows) { struct iked_policy pol; char *s, idstr[IKED_ID_SIZE]; if (msg->msg_sa != NULL && msg->msg_sa->sa_policy != NULL) { /* Existing SA with policy */ msg->msg_policy = msg->msg_sa->sa_policy; return (0); } bzero(&pol, sizeof(pol)); if (proposals != NULL) pol.pol_proposals = *proposals; pol.pol_af = msg->msg_peer.ss_family; if (flows) pol.pol_flows = *flows; pol.pol_nflows = nflows; if (msg->msg_flags & IKED_MSG_FLAGS_USE_TRANSPORT) pol.pol_flags |= IKED_POLICY_TRANSPORT; memcpy(&pol.pol_peer.addr, &msg->msg_peer, sizeof(msg->msg_peer)); memcpy(&pol.pol_local.addr, &msg->msg_local, sizeof(msg->msg_local)); if (msg->msg_peerid.id_type && ikev2_print_id(&msg->msg_peerid, idstr, IKED_ID_SIZE) == 0 && (s = strchr(idstr, '/')) != NULL) { pol.pol_peerid.id_type = msg->msg_peerid.id_type; pol.pol_peerid.id_length = strlen(s+1); strlcpy(pol.pol_peerid.id_data, s+1, sizeof(pol.pol_peerid.id_data)); log_debug("%s: peerid '%s'", __func__, s+1); } if (msg->msg_localid.id_type && ikev2_print_id(&msg->msg_localid, idstr, IKED_ID_SIZE) == 0 && (s = strchr(idstr, '/')) != NULL) { pol.pol_localid.id_type = msg->msg_localid.id_type; pol.pol_localid.id_length = strlen(s+1); strlcpy(pol.pol_localid.id_data, s+1, sizeof(pol.pol_localid.id_data)); log_debug("%s: localid '%s'", __func__, s+1); } /* Try to find a matching policy for this message */ if ((msg->msg_policy = policy_test(env, &pol)) != NULL) { log_debug("%s: setting policy '%s'", __func__, msg->msg_policy->pol_name); return (0); } /* No matching policy found, try the default */ if ((msg->msg_policy = env->sc_defaultcon) != NULL) return (0); /* No policy found */ return (-1); } /* * Lookup an iked policy matching the SA sa and store a pointer * to the found policy in SA. * * Returns 0 on success and -1 if no matching policy was * found */ int policy_lookup_sa(struct iked *env, struct iked_sa *sa) { struct iked_policy pol, *pol_found; struct iked_id *lid, *pid; char *s, idstr[IKED_ID_SIZE]; /* * The SA should never be without policy. In the case of * 'ikectl reload' the policy is no longer in sc_policies * but is kept alive by the reference from the sa. */ if (sa->sa_policy == NULL) { log_warn("%s: missing SA policy.", SPI_SA(sa, __func__)); return (-1); } bzero(&pol, sizeof(pol)); pol.pol_proposals = sa->sa_proposals; pol.pol_af = sa->sa_peer.addr_af; if (sa->sa_used_transport_mode) pol.pol_flags |= IKED_POLICY_TRANSPORT; memcpy(&pol.pol_peer.addr, &sa->sa_peer.addr, sizeof(sa->sa_peer.addr)); memcpy(&pol.pol_local.addr, &sa->sa_local.addr, sizeof(sa->sa_local.addr)); pol.pol_flows = sa->sa_policy->pol_flows; pol.pol_nflows = sa->sa_policy->pol_nflows; if (sa->sa_hdr.sh_initiator) { lid = &sa->sa_iid; pid = &sa->sa_rid; } else { lid = &sa->sa_rid; pid = &sa->sa_iid; } if (pid->id_type && ikev2_print_id(pid, idstr, IKED_ID_SIZE) == 0 && (s = strchr(idstr, '/')) != NULL) { pol.pol_peerid.id_type = pid->id_type; pol.pol_peerid.id_length = strlen(s+1); strlcpy(pol.pol_peerid.id_data, s+1, sizeof(pol.pol_peerid.id_data)); log_debug("%s: peerid '%s'", __func__, s+1); } if (lid->id_type && ikev2_print_id(lid, idstr, IKED_ID_SIZE) == 0 && (s = strchr(idstr, '/')) != NULL) { pol.pol_localid.id_type = lid->id_type; pol.pol_localid.id_length = strlen(s+1); strlcpy(pol.pol_localid.id_data, s+1, sizeof(pol.pol_localid.id_data)); log_debug("%s: localid '%s'", __func__, s+1); } /* Try to find a matching policy for this message */ if ((pol_found = policy_test(env, &pol)) != NULL) { log_debug("%s: found policy '%s'", SPI_SA(sa, __func__), pol_found->pol_name); sa->sa_policy = pol_found; return (0); } /* No policy found */ return (-1); } /* * Find a policy matching the query policy key in the global env. * If multiple matching policies are found the policy with the highest * priority is selected. * * Returns a pointer to a matching policy, or NULL if no policy matches. */ struct iked_policy * policy_test(struct iked *env, struct iked_policy *key) { struct iked_policy *p = NULL, *pol = NULL; p = TAILQ_FIRST(&env->sc_policies); while (p != NULL) { if (p->pol_flags & IKED_POLICY_SKIP) p = p->pol_skip[IKED_SKIP_FLAGS]; else if (key->pol_af && p->pol_af && key->pol_af != p->pol_af) p = p->pol_skip[IKED_SKIP_AF]; else if (sockaddr_cmp((struct sockaddr *)&key->pol_peer.addr, (struct sockaddr *)&p->pol_peer.addr, p->pol_peer.addr_mask) != 0) p = p->pol_skip[IKED_SKIP_DST_ADDR]; else if (sockaddr_cmp((struct sockaddr *)&key->pol_local.addr, (struct sockaddr *)&p->pol_local.addr, p->pol_local.addr_mask) != 0) p = p->pol_skip[IKED_SKIP_SRC_ADDR]; else { /* * Check if flows are requested and if they * are compatible. */ if (key->pol_nflows && policy_test_flows(key, p)) { p = TAILQ_NEXT(p, pol_entry); continue; } /* make sure the peer ID matches */ if (key->pol_peerid.id_type && p->pol_peerid.id_type && (key->pol_peerid.id_type != p->pol_peerid.id_type || memcmp(key->pol_peerid.id_data, p->pol_peerid.id_data, sizeof(key->pol_peerid.id_data)) != 0)) { p = TAILQ_NEXT(p, pol_entry); continue; } /* make sure the local ID matches */ if (key->pol_localid.id_type && p->pol_localid.id_type && (key->pol_localid.id_type != p->pol_localid.id_type || memcmp(key->pol_localid.id_data, p->pol_localid.id_data, sizeof(key->pol_localid.id_data)) != 0)) { log_info("%s: localid mismatch", __func__); p = TAILQ_NEXT(p, pol_entry); continue; } /* check transport mode */ if ((key->pol_flags & IKED_POLICY_TRANSPORT) && !(p->pol_flags & IKED_POLICY_TRANSPORT)) { p = TAILQ_NEXT(p, pol_entry); continue; } /* Make sure the proposals are compatible */ if (TAILQ_FIRST(&key->pol_proposals) && proposals_negotiate(NULL, &p->pol_proposals, &key->pol_proposals, 0, -1) == -1) { p = TAILQ_NEXT(p, pol_entry); continue; } /* Policy matched */ pol = p; if (pol->pol_flags & IKED_POLICY_QUICK) break; /* Continue to find last matching policy */ p = TAILQ_NEXT(p, pol_entry); } } return (pol); } static int policy_test_flows(struct iked_policy *key, struct iked_policy *p) { struct iked_flow *f; for (f = RB_MIN(iked_flows, &key->pol_flows); f != NULL; f = RB_NEXT(iked_flows, &key->pol_flows, f)) if (RB_FIND(iked_flows, &p->pol_flows, f) == NULL) return (-1); return (0); } #define IKED_SET_SKIP_STEPS(i) \ do { \ while (head[i] != cur) { \ head[i]->pol_skip[i] = cur; \ head[i] = TAILQ_NEXT(head[i], pol_entry); \ } \ } while (0) /* This code is derived from pf_calc_skip_steps() from pf.c */ void policy_calc_skip_steps(struct iked_policies *policies) { struct iked_policy *head[IKED_SKIP_COUNT], *cur, *prev; int i; cur = TAILQ_FIRST(policies); prev = cur; for (i = 0; i < IKED_SKIP_COUNT; ++i) head[i] = cur; while (cur != NULL) { if (cur->pol_flags & IKED_POLICY_SKIP) IKED_SET_SKIP_STEPS(IKED_SKIP_FLAGS); if (cur->pol_af != AF_UNSPEC && prev->pol_af != AF_UNSPEC && cur->pol_af != prev->pol_af) IKED_SET_SKIP_STEPS(IKED_SKIP_AF); if (IKED_ADDR_NEQ(&cur->pol_peer, &prev->pol_peer)) IKED_SET_SKIP_STEPS(IKED_SKIP_DST_ADDR); if (IKED_ADDR_NEQ(&cur->pol_local, &prev->pol_local)) IKED_SET_SKIP_STEPS(IKED_SKIP_SRC_ADDR); prev = cur; cur = TAILQ_NEXT(cur, pol_entry); } for (i = 0; i < IKED_SKIP_COUNT; ++i) IKED_SET_SKIP_STEPS(i); } void policy_ref(struct iked *env, struct iked_policy *pol) { if (pol->pol_flags & IKED_POLICY_REFCNT) pol->pol_refcnt++; } void policy_unref(struct iked *env, struct iked_policy *pol) { if (pol == NULL || (pol->pol_flags & IKED_POLICY_REFCNT) == 0) return; if (--(pol->pol_refcnt) <= 0) config_free_policy(env, pol); else { struct iked_sa *tmp; int count = 0; TAILQ_FOREACH(tmp, &pol->pol_sapeers, sa_peer_entry) count++; if (count != pol->pol_refcnt) log_warnx("%s: ERROR pol %p pol_refcnt %d != count %d", __func__, pol, pol->pol_refcnt, count); } } void sa_state(struct iked *env, struct iked_sa *sa, int state) { const char *a; const char *b; int ostate = sa->sa_state; a = print_map(ostate, ikev2_state_map); b = print_map(state, ikev2_state_map); sa->sa_state = state; if (ostate != IKEV2_STATE_INIT && !sa_stateok(sa, state)) { log_debug("%s: cannot switch: %s -> %s", SPI_SA(sa, __func__), a, b); sa->sa_state = ostate; } else if (ostate != sa->sa_state) { switch (state) { case IKEV2_STATE_ESTABLISHED: case IKEV2_STATE_CLOSED: log_debug("%s: %s -> %s from %s to %s policy '%s'", SPI_SA(sa, __func__), a, b, print_addr(&sa->sa_peer.addr), print_addr(&sa->sa_local.addr), sa->sa_policy ? sa->sa_policy->pol_name : ""); break; default: log_debug("%s: %s -> %s", SPI_SA(sa, __func__), a, b); break; } } if (ostate != sa->sa_state) { switch (sa->sa_state) { case IKEV2_STATE_ESTABLISHED: ikestat_inc(env, ikes_sa_established_total); ikestat_inc(env, ikes_sa_established_current); break; case IKEV2_STATE_CLOSED: case IKEV2_STATE_CLOSING: switch (ostate) { case IKEV2_STATE_ESTABLISHED: ikestat_dec(env, ikes_sa_established_current); break; case IKEV2_STATE_CLOSED: case IKEV2_STATE_CLOSING: break; default: ikestat_inc(env, ikes_sa_established_failures); break; } break; } } } void sa_stateflags(struct iked_sa *sa, unsigned int flags) { unsigned int require; if (sa->sa_state > IKEV2_STATE_SA_INIT) require = sa->sa_statevalid; else require = sa->sa_stateinit; log_debug("%s: 0x%04x -> 0x%04x %s (required 0x%04x %s)", __func__, sa->sa_stateflags, sa->sa_stateflags | flags, print_bits(sa->sa_stateflags | flags, IKED_REQ_BITS), require, print_bits(require, IKED_REQ_BITS)); sa->sa_stateflags |= flags; } int sa_stateok(const struct iked_sa *sa, int state) { unsigned int require; if (sa->sa_state < state) return (0); if (state == IKEV2_STATE_SA_INIT) require = sa->sa_stateinit; else require = sa->sa_statevalid; if (state == IKEV2_STATE_SA_INIT || state == IKEV2_STATE_VALID || state == IKEV2_STATE_EAP_VALID) { log_debug("%s: %s flags 0x%04x, require 0x%04x %s", __func__, print_map(state, ikev2_state_map), (sa->sa_stateflags & require), require, print_bits(require, IKED_REQ_BITS)); if ((sa->sa_stateflags & require) != require) return (0); /* not ready, ignore */ } return (1); } struct iked_sa * sa_new(struct iked *env, uint64_t ispi, uint64_t rspi, unsigned int initiator, struct iked_policy *pol) { struct iked_sa *sa; struct iked_sa *old; struct iked_id *localid; unsigned int diff; if ((ispi == 0 && rspi == 0) || (sa = sa_lookup(env, ispi, rspi, initiator)) == NULL) { /* Create new SA */ if (!initiator && ispi == 0) { log_debug("%s: cannot create responder IKE SA w/o ispi", __func__); return (NULL); } sa = config_new_sa(env, initiator); if (sa == NULL) { log_debug("%s: failed to allocate IKE SA", __func__); return (NULL); } if (!initiator) sa->sa_hdr.sh_ispi = ispi; old = RB_INSERT(iked_sas, &env->sc_sas, sa); if (old && old != sa) { log_warnx("%s: duplicate IKE SA", __func__); config_free_sa(env, sa); return (NULL); } } /* Update rspi in the initator case */ if (initiator && sa->sa_hdr.sh_rspi == 0 && rspi) sa->sa_hdr.sh_rspi = rspi; if (pol == NULL && sa->sa_policy == NULL) fatalx("%s: sa %p no policy", __func__, sa); else if (sa->sa_policy == NULL) { policy_ref(env, pol); sa->sa_policy = pol; TAILQ_INSERT_TAIL(&pol->pol_sapeers, sa, sa_peer_entry); } else pol = sa->sa_policy; sa->sa_statevalid = IKED_REQ_AUTH|IKED_REQ_AUTHVALID|IKED_REQ_SA; if (pol != NULL && pol->pol_auth.auth_eap) { sa->sa_statevalid |= IKED_REQ_CERT|IKED_REQ_EAPVALID; } else if (pol != NULL && pol->pol_auth.auth_method != IKEV2_AUTH_SHARED_KEY_MIC) { sa->sa_statevalid |= IKED_REQ_CERTVALID|IKED_REQ_CERT; } if (initiator) { localid = &sa->sa_iid; diff = IKED_REQ_CERTVALID|IKED_REQ_AUTHVALID|IKED_REQ_SA| IKED_REQ_EAPVALID; sa->sa_stateinit = sa->sa_statevalid & ~diff; sa->sa_statevalid = sa->sa_statevalid & diff; } else localid = &sa->sa_rid; if (pol != NULL && ikev2_policy2id(&pol->pol_localid, localid, 1) != 0) { log_debug("%s: failed to get local id", __func__); ikev2_ike_sa_setreason(sa, "failed to get local id"); sa_free(env, sa); return (NULL); } return (sa); } int policy_generate_ts(struct iked_policy *pol) { struct iked_flow *flow; /* Generate list of traffic selectors from flows */ RB_FOREACH(flow, iked_flows, &pol->pol_flows) { if (ts_insert_unique(&flow->flow_src, &pol->pol_tssrc, flow->flow_ipproto) == 1) pol->pol_tssrc_count++; if (ts_insert_unique(&flow->flow_dst, &pol->pol_tsdst, flow->flow_ipproto) == 1) pol->pol_tsdst_count++; } if (pol->pol_tssrc_count > IKEV2_MAXNUM_TSS || pol->pol_tsdst_count > IKEV2_MAXNUM_TSS) return (-1); return (0); } int ts_insert_unique(struct iked_addr *addr, struct iked_tss *tss, int ipproto) { struct iked_ts *ts; /* Remove duplicates */ TAILQ_FOREACH(ts, tss, ts_entry) { if (addr_cmp(addr, &ts->ts_addr, 1) == 0) return (0); } if ((ts = calloc(1, sizeof(*ts))) == NULL) return (-1); ts->ts_ipproto = ipproto; ts->ts_addr = *addr; TAILQ_INSERT_TAIL(tss, ts, ts_entry); return (1); } void sa_free(struct iked *env, struct iked_sa *sa) { struct iked_sa *osa; if (sa->sa_reason) log_info("%s: %s", SPI_SA(sa, __func__), sa->sa_reason); else log_debug("%s: ispi %s rspi %s", SPI_SA(sa, __func__), print_spi(sa->sa_hdr.sh_ispi, 8), print_spi(sa->sa_hdr.sh_rspi, 8)); /* IKE rekeying running? (old sa freed before new sa) */ if (sa->sa_nexti) { RB_REMOVE(iked_sas, &env->sc_sas, sa->sa_nexti); if (sa->sa_nexti->sa_dstid_entry_valid) { log_info("%s: nexti established? %s", SPI_SA(sa, __func__), SPI_SA(sa->sa_nexti, NULL)); sa_dstid_remove(env, sa->sa_nexti); } config_free_sa(env, sa->sa_nexti); } if (sa->sa_nextr) { RB_REMOVE(iked_sas, &env->sc_sas, sa->sa_nextr); if (sa->sa_nextr->sa_dstid_entry_valid) { log_info("%s: nextr established? %s", SPI_SA(sa, __func__), SPI_SA(sa->sa_nextr, NULL)); sa_dstid_remove(env, sa->sa_nextr); } config_free_sa(env, sa->sa_nextr); } /* reset matching backpointers (new sa freed before old sa) */ if ((osa = sa->sa_previ) != NULL) { if (osa->sa_nexti == sa) { log_debug("%s: resetting: sa %p == osa->sa_nexti %p" " (osa %p)", SPI_SA(sa, __func__), osa, sa, osa->sa_nexti); osa->sa_nexti = NULL; } else { log_info("%s: inconsistent: sa %p != osa->sa_nexti %p" " (osa %p)", SPI_SA(sa, __func__), osa, sa, osa->sa_nexti); } } if ((osa = sa->sa_prevr) != NULL) { if (osa->sa_nextr == sa) { log_debug("%s: resetting: sa %p == osa->sa_nextr %p" " (osa %p)", SPI_SA(sa, __func__), osa, sa, osa->sa_nextr); osa->sa_nextr = NULL; } else { log_info("%s: inconsistent: sa %p != osa->sa_nextr %p" " (osa %p)", SPI_SA(sa, __func__), osa, sa, osa->sa_nextr); } } RB_REMOVE(iked_sas, &env->sc_sas, sa); if (sa->sa_dstid_entry_valid) sa_dstid_remove(env, sa); config_free_sa(env, sa); } void sa_free_flows(struct iked *env, struct iked_saflows *head) { struct iked_flow *flow, *flowtmp; TAILQ_FOREACH_SAFE(flow, head, flow_entry, flowtmp) { log_debug("%s: free %p", __func__, flow); if (flow->flow_loaded) RB_REMOVE(iked_flows, &env->sc_activeflows, flow); TAILQ_REMOVE(head, flow, flow_entry); (void)ipsec_flow_delete(env, flow); flow_free(flow); } } int sa_address(struct iked_sa *sa, struct iked_addr *addr, struct sockaddr *peer) { bzero(addr, sizeof(*addr)); addr->addr_af = peer->sa_family; addr->addr_port = htons(socket_getport(peer)); memcpy(&addr->addr, peer, SA_LEN(peer)); if (socket_af((struct sockaddr *)&addr->addr, addr->addr_port) == -1) { log_debug("%s: invalid address", __func__); return (-1); } return (0); } int sa_configure_iface(struct iked *env, struct iked_sa *sa, int add) { #if defined(HAVE_VROUTE) struct iked_flow *saflow; struct sockaddr *caddr; int rdomain; if (sa->sa_policy == NULL || sa->sa_policy->pol_iface == 0) return (0); if (!sa->sa_cp_addr && !sa->sa_cp_addr6) return (0); if (sa->sa_cp_addr) { if (vroute_setaddr(env, add, (struct sockaddr *)&sa->sa_cp_addr->addr, sa->sa_cp_addr->addr_mask, sa->sa_policy->pol_iface) != 0) return (-1); } if (sa->sa_cp_addr6) { if (vroute_setaddr(env, add, (struct sockaddr *)&sa->sa_cp_addr6->addr, sa->sa_cp_addr6->addr_mask, sa->sa_policy->pol_iface) != 0) return (-1); } if (add) { /* Add direct route to peer */ if (vroute_setcloneroute(env, getrtable(), (struct sockaddr *)&sa->sa_peer.addr, 0, NULL)) return (-1); } else { if (vroute_setdelroute(env, getrtable(), (struct sockaddr *)&sa->sa_peer.addr, 0, NULL)) return (-1); } TAILQ_FOREACH(saflow, &sa->sa_flows, flow_entry) { rdomain = saflow->flow_rdomain == -1 ? getrtable() : saflow->flow_rdomain; switch(saflow->flow_src.addr_af) { case AF_INET: if (sa->sa_cp_addr == NULL) continue; caddr = (struct sockaddr *)&sa->sa_cp_addr->addr; break; case AF_INET6: if (sa->sa_cp_addr6 == NULL) continue; caddr = (struct sockaddr *)&sa->sa_cp_addr6->addr; break; default: return (-1); } if (sockaddr_cmp((struct sockaddr *)&saflow->flow_src.addr, caddr, -1) != 0) continue; if (add) { if (vroute_setaddroute(env, rdomain, (struct sockaddr *)&saflow->flow_dst.addr, saflow->flow_dst.addr_mask, caddr)) return (-1); } else { if (vroute_setdelroute(env, rdomain, (struct sockaddr *)&saflow->flow_dst.addr, saflow->flow_dst.addr_mask, caddr)) return (-1); } } if (sa->sa_cp_dns) { if (vroute_setdns(env, add, (struct sockaddr *)&sa->sa_cp_dns->addr, sa->sa_policy->pol_iface) != 0) return (-1); } #endif /* defined(HAVE_VROUTE) */ return (0); } void childsa_free(struct iked_childsa *csa) { struct iked_childsa *csb; if (csa == NULL) return; if (csa->csa_loaded) log_info("%s: CHILD SA spi %s is still loaded", csa->csa_ikesa ? SPI_SA(csa->csa_ikesa, __func__) : __func__, print_spi(csa->csa_spi.spi, csa->csa_spi.spi_size)); if ((csb = csa->csa_bundled) != NULL) csb->csa_bundled = NULL; if ((csb = csa->csa_peersa) != NULL) csb->csa_peersa = NULL; ibuf_free(csa->csa_encrkey); ibuf_free(csa->csa_integrkey); free(csa); } struct iked_childsa * childsa_lookup(struct iked_sa *sa, uint64_t spi, uint8_t protoid) { struct iked_childsa *csa; if (sa == NULL || spi == 0 || protoid == 0) return (NULL); TAILQ_FOREACH(csa, &sa->sa_childsas, csa_entry) { if (csa->csa_spi.spi_protoid == protoid && (csa->csa_spi.spi == spi)) break; } return (csa); } void flow_free(struct iked_flow *flow) { free(flow); } struct iked_sa * sa_lookup(struct iked *env, uint64_t ispi, uint64_t rspi, unsigned int initiator) { struct iked_sa *sa, key; key.sa_hdr.sh_ispi = ispi; key.sa_hdr.sh_initiator = initiator; if ((sa = RB_FIND(iked_sas, &env->sc_sas, &key)) != NULL) { gettimeofday(&sa->sa_timeused, NULL); /* Validate if SPIr matches */ if ((sa->sa_hdr.sh_rspi != 0) && (rspi != 0) && (sa->sa_hdr.sh_rspi != rspi)) return (NULL); } return (sa); } static __inline int sa_cmp(struct iked_sa *a, struct iked_sa *b) { if (a->sa_hdr.sh_initiator > b->sa_hdr.sh_initiator) return (-1); if (a->sa_hdr.sh_initiator < b->sa_hdr.sh_initiator) return (1); if (a->sa_hdr.sh_ispi > b->sa_hdr.sh_ispi) return (-1); if (a->sa_hdr.sh_ispi < b->sa_hdr.sh_ispi) return (1); return (0); } static struct iked_id * sa_dstid_checked(struct iked_sa *sa) { struct iked_id *id; id = IKESA_DSTID(sa); if (id == NULL || id->id_buf == NULL || ibuf_data(id->id_buf) == NULL) return (NULL); if (ibuf_size(id->id_buf) <= id->id_offset) return (NULL); return (id); } struct iked_sa * sa_dstid_lookup(struct iked *env, struct iked_sa *key) { struct iked_sa *sa; if (sa_dstid_checked(key) == NULL) fatalx("%s: no id for key %p", __func__, key); sa = RB_FIND(iked_dstid_sas, &env->sc_dstid_sas, key); if (sa != NULL && !sa->sa_dstid_entry_valid) fatalx("%s: sa %p not estab (key %p)", __func__, sa, key); return (sa); } struct iked_sa * sa_dstid_insert(struct iked *env, struct iked_sa *sa) { struct iked_sa *osa; if (sa->sa_dstid_entry_valid) fatalx("%s: sa %p is estab", __func__, sa); if (sa_dstid_checked(sa) == NULL) fatalx("%s: no id for sa %p", __func__, sa); osa = RB_FIND(iked_dstid_sas, &env->sc_dstid_sas, sa); if (osa == NULL) { osa = RB_INSERT(iked_dstid_sas, &env->sc_dstid_sas, sa); if (osa && osa != sa) { log_warnx("%s: duplicate IKE SA", SPI_SA(sa, __func__)); return (osa); } sa->sa_dstid_entry_valid = 1; return (NULL); } if (!osa->sa_dstid_entry_valid) fatalx("%s: osa %p not estab (sa %p)", __func__, osa, sa); return (osa); } void sa_dstid_remove(struct iked *env, struct iked_sa *sa) { if (!sa->sa_dstid_entry_valid) fatalx("%s: sa %p is not estab", __func__, sa); if (sa_dstid_checked(sa) == NULL) fatalx("%s: no id for sa %p", __func__, sa); RB_REMOVE(iked_dstid_sas, &env->sc_dstid_sas, sa); sa->sa_dstid_entry_valid = 0; } static __inline int sa_dstid_cmp(struct iked_sa *a, struct iked_sa *b) { struct iked_id *aid = NULL, *bid = NULL; size_t alen, blen; uint8_t *aptr, *bptr; aid = sa_dstid_checked(a); bid = sa_dstid_checked(b); if (aid == NULL || bid == NULL) fatalx("corrupt IDs"); if (aid->id_type > bid->id_type) return (-1); else if (aid->id_type < bid->id_type) return (1); alen = ibuf_size(aid->id_buf); blen = ibuf_size(bid->id_buf); aptr = ibuf_data(aid->id_buf); bptr = ibuf_data(bid->id_buf); if (aptr == NULL || bptr == NULL) fatalx("corrupt ID bufs"); if (alen <= aid->id_offset || blen <= bid->id_offset) fatalx("corrupt ID lens"); aptr += aid->id_offset; alen -= aid->id_offset; bptr += bid->id_offset; blen -= bid->id_offset; if (alen > blen) return (-1); if (alen < blen) return (1); return (memcmp(aptr, bptr, alen)); } static __inline int sa_addrpool_cmp(struct iked_sa *a, struct iked_sa *b) { return (sockaddr_cmp((struct sockaddr *)&a->sa_addrpool->addr, (struct sockaddr *)&b->sa_addrpool->addr, -1)); } static __inline int sa_addrpool6_cmp(struct iked_sa *a, struct iked_sa *b) { return (sockaddr_cmp((struct sockaddr *)&a->sa_addrpool6->addr, (struct sockaddr *)&b->sa_addrpool6->addr, -1)); } struct iked_user * user_lookup(struct iked *env, const char *user) { struct iked_user key; if (strlcpy(key.usr_name, user, sizeof(key.usr_name)) >= sizeof(key.usr_name)) return (NULL); return (RB_FIND(iked_users, &env->sc_users, &key)); } static __inline int user_cmp(struct iked_user *a, struct iked_user *b) { return (strcmp(a->usr_name, b->usr_name)); } /* * Find a matching subset of the proposal lists 'local' and 'peer'. * The resulting proposal is stored in 'result' if 'result' is not NULL. * The 'rekey' parameter indicates a CREATE_CHILD_SA exchange where * an extra group is necessary for PFS. For the initial IKE_AUTH exchange * the ESP SA proposal never includes an explicit DH group. * * Return 0 if a matching subset was found and -1 if no subset was found * or an error occured. */ int proposals_negotiate(struct iked_proposals *result, struct iked_proposals *local, struct iked_proposals *peer, int rekey, int groupid) { struct iked_proposal *ppeer = NULL, *plocal, *prop, vpeer, vlocal; struct iked_transform chosen[IKEV2_XFORMTYPE_MAX]; struct iked_transform *valid[IKEV2_XFORMTYPE_MAX]; struct iked_transform *match[IKEV2_XFORMTYPE_MAX]; unsigned int i, score, chosen_score = 0; uint8_t protoid = 0; bzero(valid, sizeof(valid)); bzero(&vlocal, sizeof(vlocal)); bzero(&vpeer, sizeof(vpeer)); if (TAILQ_EMPTY(peer)) { log_debug("%s: peer did not send %s proposals", __func__, print_map(protoid, ikev2_saproto_map)); return (-1); } TAILQ_FOREACH(plocal, local, prop_entry) { TAILQ_FOREACH(ppeer, peer, prop_entry) { if (ppeer->prop_protoid != plocal->prop_protoid) continue; bzero(match, sizeof(match)); score = proposals_match(plocal, ppeer, match, rekey, groupid); log_debug("%s: score %d", __func__, score); if (score && (!chosen_score || score < chosen_score)) { chosen_score = score; for (i = 0; i < IKEV2_XFORMTYPE_MAX; i++) { if ((valid[i] = match[i])) memcpy(&chosen[i], match[i], sizeof(chosen[0])); } memcpy(&vpeer, ppeer, sizeof(vpeer)); memcpy(&vlocal, plocal, sizeof(vlocal)); } } if (chosen_score != 0) break; } if (chosen_score == 0) return (-1); else if (result == NULL) return (0); (void)config_free_proposals(result, vpeer.prop_protoid); prop = config_add_proposal(result, vpeer.prop_id, vpeer.prop_protoid); if (vpeer.prop_localspi.spi_size) { prop->prop_localspi.spi_size = vpeer.prop_localspi.spi_size; prop->prop_peerspi = vpeer.prop_peerspi; } if (vlocal.prop_localspi.spi_size) { prop->prop_localspi.spi_size = vlocal.prop_localspi.spi_size; prop->prop_localspi.spi = vlocal.prop_localspi.spi; } for (i = 0; i < IKEV2_XFORMTYPE_MAX; i++) { if (valid[i] == NULL) continue; print_debug("%s: score %d: %s %s", __func__, chosen[i].xform_score, print_map(i, ikev2_xformtype_map), print_map(chosen[i].xform_id, chosen[i].xform_map)); if (chosen[i].xform_length) print_debug(" %d", chosen[i].xform_length); print_debug("\n"); if (config_add_transform(prop, chosen[i].xform_type, chosen[i].xform_id, chosen[i].xform_length, chosen[i].xform_keylength) != 0) break; } return (0); } static int proposals_match(struct iked_proposal *local, struct iked_proposal *peer, struct iked_transform **xforms, int rekey, int dhgroup) { struct iked_transform *tpeer, *tlocal; unsigned int i, j, type, score, requiredh = 0, nodh = 0, noauth = 0; unsigned int dhforced = 0; uint8_t protoid = peer->prop_protoid; uint8_t peerxfs[IKEV2_XFORMTYPE_MAX]; bzero(peerxfs, sizeof(peerxfs)); for (i = 0; i < peer->prop_nxforms; i++) { tpeer = peer->prop_xforms + i; /* If any of the ENC transforms is an AEAD, ignore auth */ if (tpeer->xform_type == IKEV2_XFORMTYPE_ENCR && encxf_noauth(tpeer->xform_id)) noauth = 1; } for (i = 0; i < peer->prop_nxforms; i++) { tpeer = peer->prop_xforms + i; if (tpeer->xform_type >= IKEV2_XFORMTYPE_MAX) continue; if (noauth && tpeer->xform_type == IKEV2_XFORMTYPE_INTEGR) return (0); /* * Record all transform types from the peer's proposal, * because if we want this proposal we have to select * a transform for each proposed transform type. */ peerxfs[tpeer->xform_type] = 1; for (j = 0; j < local->prop_nxforms; j++) { tlocal = local->prop_xforms + j; /* * We require a DH group for ESP if there is any * local proposal with DH enabled. */ if (rekey && requiredh == 0 && protoid == IKEV2_SAPROTO_ESP && tlocal->xform_type == IKEV2_XFORMTYPE_DH && tlocal->xform_id != IKEV2_XFORMDH_NONE) requiredh = 1; /* * If none is an explicit option, don't require * DH group. Overrides requiredh = 1. */ if (rekey && nodh == 0 && protoid == IKEV2_SAPROTO_ESP && tlocal->xform_type == IKEV2_XFORMTYPE_DH && tlocal->xform_id == IKEV2_XFORMDH_NONE) nodh = 1; /* Compare peer and local proposals */ if (tpeer->xform_type != tlocal->xform_type || tpeer->xform_id != tlocal->xform_id || tpeer->xform_length != tlocal->xform_length) continue; type = tpeer->xform_type; if (nodh == 0 && dhgroup >= 0 && type == IKEV2_XFORMTYPE_DH) { if (dhforced) continue; /* reset xform, so this xform w/matching group is enforced */ if (tlocal->xform_id == dhgroup) { xforms[type] = NULL; dhforced = 1; } } if (xforms[type] == NULL || tlocal->xform_score < xforms[type]->xform_score) { xforms[type] = tlocal; } else continue; print_debug("%s: xform %d <-> %d (%d): %s %s " "(keylength %d <-> %d)", __func__, peer->prop_id, local->prop_id, tlocal->xform_score, print_map(type, ikev2_xformtype_map), print_map(tpeer->xform_id, tpeer->xform_map), tpeer->xform_keylength, tlocal->xform_keylength); if (tpeer->xform_length) print_debug(" %d", tpeer->xform_length); print_debug("\n"); } } for (i = score = 0; i < IKEV2_XFORMTYPE_MAX; i++) { if (protoid == IKEV2_SAPROTO_IKE && xforms[i] == NULL && (i == IKEV2_XFORMTYPE_ENCR || i == IKEV2_XFORMTYPE_PRF || (!noauth && i == IKEV2_XFORMTYPE_INTEGR) || i == IKEV2_XFORMTYPE_DH)) { score = 0; break; } else if (protoid == IKEV2_SAPROTO_AH && xforms[i] == NULL && (i == IKEV2_XFORMTYPE_INTEGR || i == IKEV2_XFORMTYPE_ESN)) { score = 0; break; } else if (protoid == IKEV2_SAPROTO_ESP && xforms[i] == NULL && (i == IKEV2_XFORMTYPE_ENCR || i == IKEV2_XFORMTYPE_ESN || (requiredh && !nodh && i == IKEV2_XFORMTYPE_DH))) { score = 0; break; } else if (peerxfs[i] && xforms[i] == NULL) { score = 0; break; } else if (xforms[i] == NULL) continue; score += xforms[i]->xform_score; } return (score); } static __inline int childsa_cmp(struct iked_childsa *a, struct iked_childsa *b) { if (a->csa_spi.spi > b->csa_spi.spi) return (1); if (a->csa_spi.spi < b->csa_spi.spi) return (-1); return (0); } static __inline int addr_cmp(struct iked_addr *a, struct iked_addr *b, int useports) { int diff = 0; diff = sockaddr_cmp((struct sockaddr *)&a->addr, (struct sockaddr *)&b->addr, 128); if (!diff) diff = (int)a->addr_mask - (int)b->addr_mask; if (!diff && useports) diff = a->addr_port - b->addr_port; return (diff); } static __inline int flow_cmp(struct iked_flow *a, struct iked_flow *b) { int diff = 0; if (!diff) diff = a->flow_rdomain - b->flow_rdomain; if (!diff) diff = (int)a->flow_ipproto - (int)b->flow_ipproto; if (!diff) diff = (int)a->flow_saproto - (int)b->flow_saproto; if (!diff) diff = (int)a->flow_dir - (int)b->flow_dir; if (!diff) diff = addr_cmp(&a->flow_dst, &b->flow_dst, 1); if (!diff) diff = addr_cmp(&a->flow_src, &b->flow_src, 1); if (!diff) diff = addr_cmp(&a->flow_prenat, &b->flow_prenat, 0); return (diff); } int flow_equal(struct iked_flow *a, struct iked_flow *b) { return (flow_cmp(a, b) == 0); } RB_GENERATE(iked_sas, iked_sa, sa_entry, sa_cmp); RB_GENERATE(iked_dstid_sas, iked_sa, sa_dstid_entry, sa_dstid_cmp); RB_GENERATE(iked_addrpool, iked_sa, sa_addrpool_entry, sa_addrpool_cmp); RB_GENERATE(iked_addrpool6, iked_sa, sa_addrpool6_entry, sa_addrpool6_cmp); RB_GENERATE(iked_users, iked_user, usr_entry, user_cmp); RB_GENERATE(iked_activesas, iked_childsa, csa_node, childsa_cmp); RB_GENERATE(iked_flows, iked_flow, flow_node, flow_cmp); openiked-7.4/iked/print.c000066400000000000000000000151101477554731100154120ustar00rootroot00000000000000/* $OpenBSD: print.c,v 1.6 2024/12/26 18:25:51 sthen Exp $ */ /* * Copyright (c) 2019-2021 Tobias Heider * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "iked.h" #include "ikev2.h" #include "eap.h" const char * print_xf(unsigned int id, unsigned int length, const struct ipsec_xf xfs[]) { int i; for (i = 0; xfs[i].name != NULL; i++) { if (xfs[i].id == id) { if (length == 0 || length == xfs[i].length) return (xfs[i].name); } } return ("unknown"); } void print_user(struct iked_user *usr) { print_verbose("user \"%s\" \"%s\"\n", usr->usr_name, usr->usr_pass); } void print_policy(struct iked_policy *pol) { struct iked_proposal *pp; struct iked_transform *xform; struct iked_flow *flow; struct iked_cfg *cfg; unsigned int i, j; const struct ipsec_xf *xfs = NULL; char iface[IF_NAMESIZE]; print_verbose("ikev2"); if (pol->pol_name[0] != '\0') print_verbose(" \"%s\"", pol->pol_name); if (pol->pol_flags & IKED_POLICY_DEFAULT) print_verbose(" default"); else if (pol->pol_flags & IKED_POLICY_QUICK) print_verbose(" quick"); else if (pol->pol_flags & IKED_POLICY_SKIP) print_verbose(" skip"); if (pol->pol_flags & IKED_POLICY_ACTIVE) print_verbose(" active"); else print_verbose(" passive"); if (pol->pol_flags & IKED_POLICY_IPCOMP) print_verbose(" ipcomp"); if (pol->pol_flags & IKED_POLICY_TRANSPORT) print_verbose(" transport"); else print_verbose(" tunnel"); if (pol->pol_flags & IKED_POLICY_NATT_FORCE) print_verbose(" natt"); print_verbose(" %s", print_xf(pol->pol_saproto, 0, saxfs)); if (pol->pol_nipproto > 0) { print_verbose(" proto {"); for (i = 0; i < pol->pol_nipproto; i++) { if (i == 0) print_verbose(" %s", print_proto(pol->pol_ipproto[i])); else print_verbose(", %s", print_proto(pol->pol_ipproto[i])); } print_verbose(" }"); } if (pol->pol_af) { if (pol->pol_af == AF_INET) print_verbose(" inet"); else print_verbose(" inet6"); } if (pol->pol_rdomain >= 0) print_verbose(" rdomain %d", pol->pol_rdomain); RB_FOREACH(flow, iked_flows, &pol->pol_flows) { print_verbose(" from %s", print_addr(&flow->flow_src.addr)); if (flow->flow_src.addr_af != AF_UNSPEC && flow->flow_src.addr_net) print_verbose("/%d", flow->flow_src.addr_mask); if (flow->flow_src.addr_port) print_verbose(" port %d", ntohs(flow->flow_src.addr_port)); print_verbose(" to %s", print_addr(&flow->flow_dst.addr)); if (flow->flow_dst.addr_af != AF_UNSPEC && flow->flow_dst.addr_net) print_verbose("/%d", flow->flow_dst.addr_mask); if (flow->flow_dst.addr_port) print_verbose(" port %d", ntohs(flow->flow_dst.addr_port)); } if ((pol->pol_flags & IKED_POLICY_DEFAULT) == 0) { print_verbose(" local %s", print_addr(&pol->pol_local.addr)); if (pol->pol_local.addr.ss_family != AF_UNSPEC && pol->pol_local.addr_net) print_verbose("/%d", pol->pol_local.addr_mask); print_verbose(" peer %s", print_addr(&pol->pol_peer.addr)); if (pol->pol_peer.addr.ss_family != AF_UNSPEC && pol->pol_peer.addr_net) print_verbose("/%d", pol->pol_peer.addr_mask); } TAILQ_FOREACH(pp, &pol->pol_proposals, prop_entry) { if (!pp->prop_nxforms) continue; if (pp->prop_protoid == IKEV2_SAPROTO_IKE) print_verbose(" ikesa"); else print_verbose(" childsa"); for (j = 0; ikev2_xformtype_map[j].cm_type != 0; j++) { xfs = NULL; for (i = 0; i < pp->prop_nxforms; i++) { xform = pp->prop_xforms + i; if (xform->xform_type != ikev2_xformtype_map[j].cm_type) continue; switch (xform->xform_type) { case IKEV2_XFORMTYPE_INTEGR: print_verbose(" auth "); xfs = authxfs; break; case IKEV2_XFORMTYPE_ENCR: print_verbose(" enc "); if (pp->prop_protoid == IKEV2_SAPROTO_IKE) xfs = ikeencxfs; else xfs = ipsecencxfs; break; case IKEV2_XFORMTYPE_PRF: print_verbose(" prf "); xfs = prfxfs; break; case IKEV2_XFORMTYPE_DH: print_verbose(" group "); xfs = groupxfs; break; case IKEV2_XFORMTYPE_ESN: print_verbose(" "); xfs = esnxfs; break; default: continue; } print_verbose("%s", print_xf(xform->xform_id, xform->xform_length / 8, xfs)); } } } if (pol->pol_localid.id_length != 0) print_verbose(" srcid %s", pol->pol_localid.id_data); if (pol->pol_peerid.id_length != 0) print_verbose(" dstid %s", pol->pol_peerid.id_data); if (pol->pol_rekey) print_verbose(" ikelifetime %u", pol->pol_rekey); print_verbose(" lifetime %llu bytes %llu", (long long unsigned)pol->pol_lifetime.lt_seconds, (long long unsigned)pol->pol_lifetime.lt_bytes); switch (pol->pol_auth.auth_method) { case IKEV2_AUTH_NONE: print_verbose (" none"); break; case IKEV2_AUTH_SHARED_KEY_MIC: print_verbose(" psk 0x"); for (i = 0; i < pol->pol_auth.auth_length; i++) print_verbose("%02x", pol->pol_auth.auth_data[i]); break; default: if (pol->pol_auth.auth_eap) print_verbose(" eap \"%s\"", print_map(pol->pol_auth.auth_eap, eap_type_map)); else print_verbose(" %s", print_xf(pol->pol_auth.auth_method, 0, methodxfs)); } for (i = 0; i < pol->pol_ncfg; i++) { cfg = &pol->pol_cfg[i]; print_verbose(" %s %s %s", cfg->cfg_action == IKEV2_CP_REPLY ? "config" : "request", print_xf(cfg->cfg_type, cfg->cfg.address.addr_af, cpxfs), print_addr(&cfg->cfg.address.addr)); } if (pol->pol_iface != 0 && if_indextoname(pol->pol_iface, iface) != NULL) print_verbose(" iface %s", iface); if (pol->pol_tag[0] != '\0') print_verbose(" tag \"%s\"", pol->pol_tag); if (pol->pol_tap != 0) print_verbose(" tap \"enc%u\"", pol->pol_tap); print_verbose("\n"); } openiked-7.4/iked/proc.c000066400000000000000000000523061477554731100152310ustar00rootroot00000000000000/* $OpenBSD: proc.c,v 1.51 2024/11/21 13:35:20 claudio Exp $ */ /* * Copyright (c) 2010 - 2016 Reyk Floeter * Copyright (c) 2008 Pierre-Yves Ritschard * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_GRP_H #include #endif #include "iked.h" enum privsep_procid privsep_process; void proc_exec(struct privsep *, struct privsep_proc *, unsigned int, int, char **); void proc_setup(struct privsep *, struct privsep_proc *, unsigned int); void proc_open(struct privsep *, int, int); void proc_accept(struct privsep *, int, enum privsep_procid, unsigned int); void proc_close(struct privsep *); void proc_shutdown(struct privsep_proc *); void proc_sig_handler(int, short, void *); void proc_range(struct privsep *, enum privsep_procid, int *, int *); int proc_dispatch_null(int, struct privsep_proc *, struct imsg *); enum privsep_procid proc_getid(struct privsep_proc *procs, unsigned int nproc, const char *proc_name) { struct privsep_proc *p; unsigned int proc; for (proc = 0; proc < nproc; proc++) { p = &procs[proc]; if (strcmp(p->p_title, proc_name)) continue; return (p->p_id); } return (PROC_MAX); } void proc_exec(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc, int argc, char **argv) { unsigned int proc, nargc, i, proc_i; char **nargv; struct privsep_proc *p; char num[32]; int fd; /* Prepare the new process argv. */ nargv = calloc(argc + 5, sizeof(char *)); if (nargv == NULL) fatal("%s: calloc", __func__); /* Copy call argument first. */ nargc = 0; nargv[nargc++] = argv[0]; /* Set process name argument and save the position. */ nargv[nargc++] = "-P"; proc_i = nargc; nargc++; /* Point process instance arg to stack and copy the original args. */ nargv[nargc++] = "-I"; nargv[nargc++] = num; for (i = 1; i < (unsigned int) argc; i++) nargv[nargc++] = argv[i]; nargv[nargc] = NULL; for (proc = 0; proc < nproc; proc++) { p = &procs[proc]; /* Update args with process title. */ nargv[proc_i] = (char *)(uintptr_t)p->p_title; /* Fire children processes. */ for (i = 0; i < ps->ps_instances[p->p_id]; i++) { /* Update the process instance number. */ snprintf(num, sizeof(num), "%u", i); fd = ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0]; ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0] = -1; switch (fork()) { case -1: fatal("%s: fork", __func__); break; case 0: /* Prepare parent socket. */ if (fd != PROC_PARENT_SOCK_FILENO) { if (dup2(fd, PROC_PARENT_SOCK_FILENO) == -1) fatal("dup2"); } else if (fcntl(fd, F_SETFD, 0) == -1) fatal("fcntl"); execvp(argv[0], nargv); fatal("%s: execvp", __func__); break; default: /* Close child end. */ close(fd); break; } } } free(nargv); } void proc_connect(struct privsep *ps, void (*connected)(struct privsep *)) { struct imsgev *iev; unsigned int src, dst, inst; /* Don't distribute any sockets if we are not really going to run. */ if (ps->ps_noaction) { if (connected == NULL) fatalx("%s: missing callback", __func__); connected(ps); return; } ps->ps_connected = connected; for (dst = 0; dst < PROC_MAX; dst++) { /* We don't communicate with ourselves. */ if (dst == PROC_PARENT) continue; for (inst = 0; inst < ps->ps_instances[dst]; inst++) { iev = &ps->ps_ievs[dst][inst]; if (imsgbuf_init(&iev->ibuf, ps->ps_pp->pp_pipes[dst][inst]) == -1) fatal("%s: imsgbuf_init", __func__); imsgbuf_allow_fdpass(&iev->ibuf); event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); event_add(&iev->ev, NULL); } } /* Distribute the socketpair()s for everyone. */ for (src = 0; src < PROC_MAX; src++) for (dst = src; dst < PROC_MAX; dst++) { /* Parent already distributed its fds. */ if (src == PROC_PARENT || dst == PROC_PARENT) continue; proc_open(ps, src, dst); } /* * Finally, send a ready message to everyone: * When this message is processed by the receiver, it has * already processed all IMSG_CTL_PROCFD messages and all * pipes are ready. */ for (dst = 0; dst < PROC_MAX; dst++) { if (dst == PROC_PARENT) continue; for (inst = 0; inst < ps->ps_instances[dst]; inst++) { if (proc_compose_imsg(ps, dst, inst, IMSG_CTL_PROCREADY, -1, -1, NULL, 0) == -1) fatal("%s: proc_compose_imsg", __func__); ps->ps_connecting++; #if DEBUG log_debug("%s: #%d %s %d", __func__, ps->ps_connecting, ps->ps_title[dst], inst + 1); #endif } } } void proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc, int debug, int argc, char **argv, enum privsep_procid proc_id) { struct privsep_proc *p = NULL; struct privsep_pipes *pa, *pb; unsigned int proc; unsigned int dst; int fds[2]; /* Don't initiate anything if we are not really going to run. */ if (ps->ps_noaction) return; if (proc_id == PROC_PARENT) { privsep_process = PROC_PARENT; proc_setup(ps, procs, nproc); if (!debug && daemon(0, 0) == -1) fatal("failed to daemonize"); /* * Create the children sockets so we can use them * to distribute the rest of the socketpair()s using * proc_connect() later. */ for (dst = 0; dst < PROC_MAX; dst++) { /* Don't create socket for ourselves. */ if (dst == PROC_PARENT) continue; for (proc = 0; proc < ps->ps_instances[dst]; proc++) { pa = &ps->ps_pipes[PROC_PARENT][0]; pb = &ps->ps_pipes[dst][proc]; #if defined(__APPLE__) if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) == -1) fatal("%s: socketpair", __func__); #else if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, PF_UNSPEC, fds) == -1) fatal("%s: socketpair", __func__); #endif pa->pp_pipes[dst][proc] = fds[0]; pb->pp_pipes[PROC_PARENT][0] = fds[1]; } } /* Engage! */ proc_exec(ps, procs, nproc, argc, argv); return; } /* Initialize a child */ for (proc = 0; proc < nproc; proc++) { if (procs[proc].p_id != proc_id) continue; p = &procs[proc]; break; } if (p == NULL || p->p_init == NULL) fatalx("%s: process %d missing process initialization", __func__, proc_id); p->p_init(ps, p); fatalx("failed to initiate child process"); } void proc_accept(struct privsep *ps, int fd, enum privsep_procid dst, unsigned int n) { struct privsep_pipes *pp = ps->ps_pp; struct imsgev *iev; if (ps->ps_ievs[dst] == NULL) { #if DEBUG > 1 log_debug("%s: %s src %d %d to dst %d %d not connected", __func__, ps->ps_title[privsep_process], privsep_process, ps->ps_instance + 1, dst, n + 1); #endif close(fd); return; } if (pp->pp_pipes[dst][n] != -1) { log_warnx("%s: duplicated descriptor", __func__); close(fd); return; } else pp->pp_pipes[dst][n] = fd; iev = &ps->ps_ievs[dst][n]; if (imsgbuf_init(&iev->ibuf, fd) == -1) fatal("%s: imsgbuf_init", __func__); imsgbuf_allow_fdpass(&iev->ibuf); event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); event_add(&iev->ev, NULL); } void proc_setup(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc) { unsigned int i, j, src, dst, id; struct privsep_pipes *pp; /* Initialize parent title, ps_instances and procs. */ ps->ps_title[PROC_PARENT] = "parent"; for (src = 0; src < PROC_MAX; src++) /* Default to 1 process instance */ if (ps->ps_instances[src] < 1) ps->ps_instances[src] = 1; for (src = 0; src < nproc; src++) { procs[src].p_ps = ps; if (procs[src].p_cb == NULL) procs[src].p_cb = proc_dispatch_null; id = procs[src].p_id; ps->ps_title[id] = procs[src].p_title; if ((ps->ps_ievs[id] = calloc(ps->ps_instances[id], sizeof(struct imsgev))) == NULL) fatal("%s: calloc", __func__); /* With this set up, we are ready to call imsgbuf_init(). */ for (i = 0; i < ps->ps_instances[id]; i++) { ps->ps_ievs[id][i].handler = proc_dispatch; ps->ps_ievs[id][i].events = EV_READ; ps->ps_ievs[id][i].proc = &procs[src]; ps->ps_ievs[id][i].data = &ps->ps_ievs[id][i]; } } /* * Allocate pipes for all process instances (incl. parent) * * - ps->ps_pipes: N:M mapping * N source processes connected to M destination processes: * [src][instances][dst][instances], for example * [PROC_RELAY][3][PROC_CA][3] * * - ps->ps_pp: per-process 1:M part of ps->ps_pipes * Each process instance has a destination array of socketpair fds: * [dst][instances], for example * [PROC_PARENT][0] */ for (src = 0; src < PROC_MAX; src++) { /* Allocate destination array for each process */ if ((ps->ps_pipes[src] = calloc(ps->ps_instances[src], sizeof(struct privsep_pipes))) == NULL) fatal("%s: calloc", __func__); for (i = 0; i < ps->ps_instances[src]; i++) { pp = &ps->ps_pipes[src][i]; for (dst = 0; dst < PROC_MAX; dst++) { /* Allocate maximum fd integers */ if ((pp->pp_pipes[dst] = calloc(ps->ps_instances[dst], sizeof(int))) == NULL) fatal("%s: calloc", __func__); /* Mark fd as unused */ for (j = 0; j < ps->ps_instances[dst]; j++) pp->pp_pipes[dst][j] = -1; } } } ps->ps_pp = &ps->ps_pipes[privsep_process][ps->ps_instance]; } void proc_kill(struct privsep *ps) { char *cause; pid_t pid; int len, status; if (privsep_process != PROC_PARENT) return; proc_close(ps); do { pid = waitpid(WAIT_ANY, &status, 0); if (pid <= 0) continue; if (WIFSIGNALED(status)) { len = asprintf(&cause, "terminated; signal %d", WTERMSIG(status)); } else if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) len = asprintf(&cause, "exited abnormally"); else len = 0; } else len = -1; if (len == 0) { /* child exited OK, don't print a warning message */ } else if (len != -1) { log_warnx("lost child: pid %u %s", pid, cause); free(cause); } else log_warnx("lost child: pid %u", pid); } while (pid != -1 || (pid == -1 && errno == EINTR)); } void proc_open(struct privsep *ps, int src, int dst) { struct privsep_pipes *pa, *pb; struct privsep_fd pf; int fds[2]; unsigned int i, j; /* Exchange pipes between process. */ for (i = 0; i < ps->ps_instances[src]; i++) { for (j = 0; j < ps->ps_instances[dst]; j++) { /* Don't create sockets for ourself. */ if (src == dst && i == j) continue; pa = &ps->ps_pipes[src][i]; pb = &ps->ps_pipes[dst][j]; if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, PF_UNSPEC, fds) == -1) fatal("%s: socketpair", __func__); pa->pp_pipes[dst][j] = fds[0]; pb->pp_pipes[src][i] = fds[1]; pf.pf_procid = src; pf.pf_instance = i; if (proc_compose_imsg(ps, dst, j, IMSG_CTL_PROCFD, -1, pb->pp_pipes[src][i], &pf, sizeof(pf)) == -1) fatal("%s: proc_compose_imsg", __func__); pf.pf_procid = dst; pf.pf_instance = j; if (proc_compose_imsg(ps, src, i, IMSG_CTL_PROCFD, -1, pa->pp_pipes[dst][j], &pf, sizeof(pf)) == -1) fatal("%s: proc_compose_imsg", __func__); /* * We have to flush to send the descriptors and close * them to avoid the fd ramp on startup. */ if (proc_flush_imsg(ps, src, i) == -1 || proc_flush_imsg(ps, dst, j) == -1) fatal("%s: proc_flush_imsg", __func__); } } } void proc_close(struct privsep *ps) { unsigned int dst, n; struct privsep_pipes *pp; if (ps == NULL) return; pp = ps->ps_pp; for (dst = 0; dst < PROC_MAX; dst++) { if (ps->ps_ievs[dst] == NULL) continue; for (n = 0; n < ps->ps_instances[dst]; n++) { if (pp->pp_pipes[dst][n] == -1) continue; /* Cancel the fd, close and invalidate the fd */ event_del(&(ps->ps_ievs[dst][n].ev)); imsgbuf_clear(&(ps->ps_ievs[dst][n].ibuf)); close(pp->pp_pipes[dst][n]); pp->pp_pipes[dst][n] = -1; } free(ps->ps_ievs[dst]); } } void proc_shutdown(struct privsep_proc *p) { struct privsep *ps = p->p_ps; if (p->p_shutdown != NULL) (*p->p_shutdown)(); proc_close(ps); log_info("%s exiting, pid %d", p->p_title, getpid()); exit(0); } void proc_sig_handler(int sig, short event, void *arg) { struct privsep_proc *p = arg; switch (sig) { case SIGINT: case SIGTERM: proc_shutdown(p); break; case SIGCHLD: case SIGHUP: case SIGPIPE: case SIGUSR1: /* ignore */ break; default: fatalx("%s: unexpected signal", __func__); /* NOTREACHED */ } } void proc_run(struct privsep *ps, struct privsep_proc *p, struct privsep_proc *procs, unsigned int nproc, void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg) { struct passwd *pw; const char *root; log_procinit(p->p_title); if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) { if (control_init(ps, &ps->ps_csock) == -1) fatalx("%s: control_init", __func__); } /* Use non-standard user */ if (p->p_pw != NULL) pw = p->p_pw; else pw = ps->ps_pw; /* Change root directory */ if (p->p_chroot != NULL) root = p->p_chroot; else root = pw->pw_dir; if (chroot(root) == -1) fatal("%s: chroot", __func__); if (chdir("/") == -1) fatal("%s: chdir(\"/\")", __func__); privsep_process = p->p_id; setproctitle("%s", p->p_title); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("%s: cannot drop privileges", __func__); event_init(); signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p); signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p); signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p); signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p); signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p); signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p); signal_add(&ps->ps_evsigint, NULL); signal_add(&ps->ps_evsigterm, NULL); signal_add(&ps->ps_evsigchld, NULL); signal_add(&ps->ps_evsighup, NULL); signal_add(&ps->ps_evsigpipe, NULL); signal_add(&ps->ps_evsigusr1, NULL); proc_setup(ps, procs, nproc); proc_accept(ps, PROC_PARENT_SOCK_FILENO, PROC_PARENT, 0); if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) { if (control_listen(&ps->ps_csock) == -1) fatalx("%s: control_listen", __func__); } #if DEBUG log_debug("%s: %s %d/%d, pid %d", __func__, p->p_title, ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid()); #endif if (run != NULL) run(ps, p, arg); event_dispatch(); proc_shutdown(p); } void proc_dispatch(int fd, short event, void *arg) { struct imsgev *iev = arg; struct privsep_proc *p = iev->proc; struct privsep *ps = p->p_ps; struct imsgbuf *ibuf; struct imsg imsg; ssize_t n; int verbose; const char *title; struct privsep_fd pf; title = ps->ps_title[privsep_process]; ibuf = &iev->ibuf; if (event & EV_READ) { if ((n = imsgbuf_read(ibuf)) == -1) fatal("%s: imsgbuf_read", __func__); if (n == 0) { /* this pipe is dead, so remove the event handler */ event_del(&iev->ev); event_loopexit(NULL); return; } } if (event & EV_WRITE) { if (imsgbuf_write(ibuf) == -1) { if (errno == EPIPE) { /* Connection closed. */ event_del(&iev->ev); event_loopexit(NULL); return; } else fatal("imsgbuf_write"); } } for (;;) { if ((n = imsg_get(ibuf, &imsg)) == -1) fatal("%s: imsg_get", __func__); if (n == 0) break; #if DEBUG > 1 log_debug("%s: %s %d got imsg %d peerid %d from %s %d", __func__, title, ps->ps_instance + 1, imsg.hdr.type, imsg.hdr.peerid, p->p_title, imsg.hdr.pid); #endif /* * Check the message with the program callback */ if ((p->p_cb)(fd, p, &imsg) == 0) { /* Message was handled by the callback, continue */ imsg_free(&imsg); continue; } /* * Generic message handling */ switch (imsg.hdr.type) { case IMSG_CTL_VERBOSE: IMSG_SIZE_CHECK(&imsg, &verbose); memcpy(&verbose, imsg.data, sizeof(verbose)); log_setverbose(verbose); break; case IMSG_CTL_PROCFD: IMSG_SIZE_CHECK(&imsg, &pf); memcpy(&pf, imsg.data, sizeof(pf)); proc_accept(ps, imsg_get_fd(&imsg), pf.pf_procid, pf.pf_instance); break; case IMSG_CTL_PROCREADY: #if DEBUG log_debug("%s: ready-%s: #%d %s %d -> %s %d", __func__, p->p_id == PROC_PARENT ? "req" : "ack", ps->ps_connecting, p->p_title, imsg.hdr.pid, title, ps->ps_instance + 1); #endif if (p->p_id == PROC_PARENT) { /* ack that we are ready */ if (proc_compose_imsg(ps, PROC_PARENT, 0, IMSG_CTL_PROCREADY, -1, -1, NULL, 0) == -1) fatal("%s: proc_compose_imsg", __func__); } else { /* parent received ack */ if (ps->ps_connecting == 0) fatalx("%s: wrong acks", __func__); if (ps->ps_instance != 0) fatalx("%s: wrong instance %d", __func__, ps->ps_instance); if (ps->ps_connected == NULL) fatalx("%s: missing callback", __func__); if (--ps->ps_connecting == 0) { log_debug("%s: all connected", __func__); ps->ps_connected(ps); } } break; default: fatalx("%s: %s %d got invalid imsg %d peerid %d " "from %s %d", __func__, title, ps->ps_instance + 1, imsg.hdr.type, imsg.hdr.peerid, p->p_title, imsg.hdr.pid); } imsg_free(&imsg); } imsg_event_add(iev); } int proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg) { return (-1); } /* * imsg helper functions */ void imsg_event_add(struct imsgev *iev) { if (iev->handler == NULL) { imsgbuf_flush(&iev->ibuf); return; } iev->events = EV_READ; if (imsgbuf_queuelen(&iev->ibuf) > 0) iev->events |= EV_WRITE; event_del(&iev->ev); event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); event_add(&iev->ev, NULL); } int imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid, pid_t pid, int fd, void *data, uint16_t datalen) { int ret; if ((ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data, datalen)) == -1) return (ret); imsg_event_add(iev); return (ret); } int imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid, pid_t pid, int fd, const struct iovec *iov, int iovcnt) { int ret; if ((ret = imsg_composev(&iev->ibuf, type, peerid, pid, fd, iov, iovcnt)) == -1) return (ret); imsg_event_add(iev); return (ret); } void proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m) { if (*n == -1) { /* Use a range of all target instances */ *n = 0; *m = ps->ps_instances[id]; } else { /* Use only a single slot of the specified peer process */ *m = *n + 1; } } int proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n, uint16_t type, uint32_t peerid, int fd, void *data, uint16_t datalen) { int m; proc_range(ps, id, &n, &m); for (; n < m; n++) { if (imsg_compose_event(&ps->ps_ievs[id][n], type, peerid, ps->ps_instance + 1, fd, data, datalen) == -1) return (-1); } return (0); } int proc_compose(struct privsep *ps, enum privsep_procid id, uint16_t type, void *data, uint16_t datalen) { return (proc_compose_imsg(ps, id, -1, type, -1, -1, data, datalen)); } int proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n, uint16_t type, uint32_t peerid, int fd, const struct iovec *iov, int iovcnt) { int m; proc_range(ps, id, &n, &m); for (; n < m; n++) if (imsg_composev_event(&ps->ps_ievs[id][n], type, peerid, ps->ps_instance + 1, fd, iov, iovcnt) == -1) return (-1); return (0); } int proc_composev(struct privsep *ps, enum privsep_procid id, uint16_t type, const struct iovec *iov, int iovcnt) { return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt)); } int proc_forward_imsg(struct privsep *ps, struct imsg *imsg, enum privsep_procid id, int n) { return (proc_compose_imsg(ps, id, n, imsg->hdr.type, imsg->hdr.peerid, -1, imsg->data, IMSG_DATA_SIZE(imsg))); } struct imsgbuf * proc_ibuf(struct privsep *ps, enum privsep_procid id, int n) { int m; proc_range(ps, id, &n, &m); return (&ps->ps_ievs[id][n].ibuf); } struct imsgev * proc_iev(struct privsep *ps, enum privsep_procid id, int n) { int m; proc_range(ps, id, &n, &m); return (&ps->ps_ievs[id][n]); } /* This function should only be called with care as it breaks async I/O */ int proc_flush_imsg(struct privsep *ps, enum privsep_procid id, int n) { struct imsgbuf *ibuf; int m, ret = 0; proc_range(ps, id, &n, &m); for (; n < m; n++) { if ((ibuf = proc_ibuf(ps, id, n)) == NULL) return (-1); if ((ret = imsgbuf_flush(ibuf)) == -1) break; imsg_event_add(&ps->ps_ievs[id][n]); } return (ret); } openiked-7.4/iked/smult_curve25519_ref.c000066400000000000000000000153221477554731100200750ustar00rootroot00000000000000/* $OpenBSD: smult_curve25519_ref.c,v 1.1 2014/08/27 10:28:57 reyk Exp $ */ /* version 20081011 Matthew Dempsky Public domain. Derived from public domain code by D. J. Bernstein. */ int crypto_scalarmult_curve25519(unsigned char *, const unsigned char *, const unsigned char *); static void add(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) { unsigned int j; unsigned int u; u = 0; for (j = 0;j < 31;++j) { u += a[j] + b[j]; out[j] = u & 255; u >>= 8; } u += a[31] + b[31]; out[31] = u; } static void sub(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) { unsigned int j; unsigned int u; u = 218; for (j = 0;j < 31;++j) { u += a[j] + 65280 - b[j]; out[j] = u & 255; u >>= 8; } u += a[31] - b[31]; out[31] = u; } static void squeeze(unsigned int a[32]) { unsigned int j; unsigned int u; u = 0; for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; } u += a[31]; a[31] = u & 127; u = 19 * (u >> 7); for (j = 0;j < 31;++j) { u += a[j]; a[j] = u & 255; u >>= 8; } u += a[31]; a[31] = u; } static const unsigned int minusp[32] = { 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 } ; static void freeze(unsigned int a[32]) { unsigned int aorig[32]; unsigned int j; unsigned int negative; for (j = 0;j < 32;++j) aorig[j] = a[j]; add(a,a,minusp); negative = -((a[31] >> 7) & 1); for (j = 0;j < 32;++j) a[j] ^= negative & (aorig[j] ^ a[j]); } static void mult(unsigned int out[32],const unsigned int a[32],const unsigned int b[32]) { unsigned int i; unsigned int j; unsigned int u; for (i = 0;i < 32;++i) { u = 0; for (j = 0;j <= i;++j) u += a[j] * b[i - j]; for (j = i + 1;j < 32;++j) u += 38 * a[j] * b[i + 32 - j]; out[i] = u; } squeeze(out); } static void mult121665(unsigned int out[32],const unsigned int a[32]) { unsigned int j; unsigned int u; u = 0; for (j = 0;j < 31;++j) { u += 121665 * a[j]; out[j] = u & 255; u >>= 8; } u += 121665 * a[31]; out[31] = u & 127; u = 19 * (u >> 7); for (j = 0;j < 31;++j) { u += out[j]; out[j] = u & 255; u >>= 8; } u += out[j]; out[j] = u; } static void square(unsigned int out[32],const unsigned int a[32]) { unsigned int i; unsigned int j; unsigned int u; for (i = 0;i < 32;++i) { u = 0; for (j = 0;j < i - j;++j) u += a[j] * a[i - j]; for (j = i + 1;j < i + 32 - j;++j) u += 38 * a[j] * a[i + 32 - j]; u *= 2; if ((i & 1) == 0) { u += a[i / 2] * a[i / 2]; u += 38 * a[i / 2 + 16] * a[i / 2 + 16]; } out[i] = u; } squeeze(out); } static void select(unsigned int p[64],unsigned int q[64],const unsigned int r[64],const unsigned int s[64],unsigned int b) { unsigned int j; unsigned int t; unsigned int bminus1; bminus1 = b - 1; for (j = 0;j < 64;++j) { t = bminus1 & (r[j] ^ s[j]); p[j] = s[j] ^ t; q[j] = r[j] ^ t; } } static void mainloop(unsigned int work[64],const unsigned char e[32]) { unsigned int xzm1[64]; unsigned int xzm[64]; unsigned int xzmb[64]; unsigned int xzm1b[64]; unsigned int xznb[64]; unsigned int xzn1b[64]; unsigned int a0[64]; unsigned int a1[64]; unsigned int b0[64]; unsigned int b1[64]; unsigned int c1[64]; unsigned int r[32]; unsigned int s[32]; unsigned int t[32]; unsigned int u[32]; unsigned int j; unsigned int b; int pos; for (j = 0;j < 32;++j) xzm1[j] = work[j]; xzm1[32] = 1; for (j = 33;j < 64;++j) xzm1[j] = 0; xzm[0] = 1; for (j = 1;j < 64;++j) xzm[j] = 0; for (pos = 254;pos >= 0;--pos) { b = e[pos / 8] >> (pos & 7); b &= 1; select(xzmb,xzm1b,xzm,xzm1,b); add(a0,xzmb,xzmb + 32); sub(a0 + 32,xzmb,xzmb + 32); add(a1,xzm1b,xzm1b + 32); sub(a1 + 32,xzm1b,xzm1b + 32); square(b0,a0); square(b0 + 32,a0 + 32); mult(b1,a1,a0 + 32); mult(b1 + 32,a1 + 32,a0); add(c1,b1,b1 + 32); sub(c1 + 32,b1,b1 + 32); square(r,c1 + 32); sub(s,b0,b0 + 32); mult121665(t,s); add(u,t,b0); mult(xznb,b0,b0 + 32); mult(xznb + 32,s,u); square(xzn1b,c1); mult(xzn1b + 32,r,work); select(xzm,xzm1,xznb,xzn1b,b); } for (j = 0;j < 64;++j) work[j] = xzm[j]; } static void recip(unsigned int out[32],const unsigned int z[32]) { unsigned int z2[32]; unsigned int z9[32]; unsigned int z11[32]; unsigned int z2_5_0[32]; unsigned int z2_10_0[32]; unsigned int z2_20_0[32]; unsigned int z2_50_0[32]; unsigned int z2_100_0[32]; unsigned int t0[32]; unsigned int t1[32]; int i; /* 2 */ square(z2,z); /* 4 */ square(t1,z2); /* 8 */ square(t0,t1); /* 9 */ mult(z9,t0,z); /* 11 */ mult(z11,z9,z2); /* 22 */ square(t0,z11); /* 2^5 - 2^0 = 31 */ mult(z2_5_0,t0,z9); /* 2^6 - 2^1 */ square(t0,z2_5_0); /* 2^7 - 2^2 */ square(t1,t0); /* 2^8 - 2^3 */ square(t0,t1); /* 2^9 - 2^4 */ square(t1,t0); /* 2^10 - 2^5 */ square(t0,t1); /* 2^10 - 2^0 */ mult(z2_10_0,t0,z2_5_0); /* 2^11 - 2^1 */ square(t0,z2_10_0); /* 2^12 - 2^2 */ square(t1,t0); /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t0,t1); square(t1,t0); } /* 2^20 - 2^0 */ mult(z2_20_0,t1,z2_10_0); /* 2^21 - 2^1 */ square(t0,z2_20_0); /* 2^22 - 2^2 */ square(t1,t0); /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { square(t0,t1); square(t1,t0); } /* 2^40 - 2^0 */ mult(t0,t1,z2_20_0); /* 2^41 - 2^1 */ square(t1,t0); /* 2^42 - 2^2 */ square(t0,t1); /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { square(t1,t0); square(t0,t1); } /* 2^50 - 2^0 */ mult(z2_50_0,t0,z2_10_0); /* 2^51 - 2^1 */ square(t0,z2_50_0); /* 2^52 - 2^2 */ square(t1,t0); /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); } /* 2^100 - 2^0 */ mult(z2_100_0,t1,z2_50_0); /* 2^101 - 2^1 */ square(t1,z2_100_0); /* 2^102 - 2^2 */ square(t0,t1); /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { square(t1,t0); square(t0,t1); } /* 2^200 - 2^0 */ mult(t1,t0,z2_100_0); /* 2^201 - 2^1 */ square(t0,t1); /* 2^202 - 2^2 */ square(t1,t0); /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { square(t0,t1); square(t1,t0); } /* 2^250 - 2^0 */ mult(t0,t1,z2_50_0); /* 2^251 - 2^1 */ square(t1,t0); /* 2^252 - 2^2 */ square(t0,t1); /* 2^253 - 2^3 */ square(t1,t0); /* 2^254 - 2^4 */ square(t0,t1); /* 2^255 - 2^5 */ square(t1,t0); /* 2^255 - 21 */ mult(out,t1,z11); } int crypto_scalarmult_curve25519(unsigned char *q, const unsigned char *n, const unsigned char *p) { unsigned int work[96]; unsigned char e[32]; unsigned int i; for (i = 0;i < 32;++i) e[i] = n[i]; e[0] &= 248; e[31] &= 127; e[31] |= 64; for (i = 0;i < 32;++i) work[i] = p[i]; mainloop(work,e); recip(work + 32,work + 32); mult(work + 64,work,work + 32); freeze(work + 64); for (i = 0;i < 32;++i) q[i] = work[64 + i]; return 0; } openiked-7.4/iked/sntrup761.c000066400000000000000000000624511477554731100160610ustar00rootroot00000000000000/* $OpenBSD: sntrup761.c,v 1.1 2021/05/28 18:01:39 tobhe Exp $ */ /* * Public Domain, Authors: * - Daniel J. Bernstein * - Chitchanok Chuengsatiansup * - Tanja Lange * - Christine van Vredendaal */ #include #include "crypto_api.h" #define int8 crypto_int8 #define uint8 crypto_uint8 #define int16 crypto_int16 #define uint16 crypto_uint16 #define int32 crypto_int32 #define uint32 crypto_uint32 #define int64 crypto_int64 #define uint64 crypto_uint64 /* from supercop-20201130/crypto_sort/int32/portable4/int32_minmax.inc */ #define int32_MINMAX(a,b) \ do { \ int64_t ab = (int64_t)b ^ (int64_t)a; \ int64_t c = (int64_t)b - (int64_t)a; \ c ^= ab & (c ^ b); \ c >>= 31; \ c &= ab; \ a ^= c; \ b ^= c; \ } while(0) /* from supercop-20201130/crypto_sort/int32/portable4/sort.c */ static void crypto_sort_int32(void *array,long long n) { long long top,p,q,r,i,j; int32 *x = array; if (n < 2) return; top = 1; while (top < n - top) top += top; for (p = top;p >= 1;p >>= 1) { i = 0; while (i + 2 * p <= n) { for (j = i;j < i + p;++j) int32_MINMAX(x[j],x[j+p]); i += 2 * p; } for (j = i;j < n - p;++j) int32_MINMAX(x[j],x[j+p]); i = 0; j = 0; for (q = top;q > p;q >>= 1) { if (j != i) for (;;) { if (j == n - q) goto done; int32 a = x[j + p]; for (r = q;r > p;r >>= 1) int32_MINMAX(a,x[j + r]); x[j + p] = a; ++j; if (j == i + p) { i += 2 * p; break; } } while (i + p <= n - q) { for (j = i;j < i + p;++j) { int32 a = x[j + p]; for (r = q;r > p;r >>= 1) int32_MINMAX(a,x[j+r]); x[j + p] = a; } i += 2 * p; } /* now i + p > n - q */ j = i; while (j < n - q) { int32 a = x[j + p]; for (r = q;r > p;r >>= 1) int32_MINMAX(a,x[j+r]); x[j + p] = a; ++j; } done: ; } } } /* from supercop-20201130/crypto_sort/uint32/useint32/sort.c */ /* can save time by vectorizing xor loops */ /* can save time by integrating xor loops with int32_sort */ static void crypto_sort_uint32(void *array,long long n) { crypto_uint32 *x = array; long long j; for (j = 0;j < n;++j) x[j] ^= 0x80000000; crypto_sort_int32(array,n); for (j = 0;j < n;++j) x[j] ^= 0x80000000; } /* from supercop-20201130/crypto_kem/sntrup761/ref/uint32.c */ /* CPU division instruction typically takes time depending on x. This software is designed to take time independent of x. Time still varies depending on m; user must ensure that m is constant. Time also varies on CPUs where multiplication is variable-time. There could be more CPU issues. There could also be compiler issues. */ static void uint32_divmod_uint14(uint32 *q,uint16 *r,uint32 x,uint16 m) { uint32 v = 0x80000000; uint32 qpart; uint32 mask; v /= m; /* caller guarantees m > 0 */ /* caller guarantees m < 16384 */ /* vm <= 2^31 <= vm+m-1 */ /* xvm <= 2^31 x <= xvm+x(m-1) */ *q = 0; qpart = (x*(uint64)v)>>31; /* 2^31 qpart <= xv <= 2^31 qpart + 2^31-1 */ /* 2^31 qpart m <= xvm <= 2^31 qpart m + (2^31-1)m */ /* 2^31 qpart m <= 2^31 x <= 2^31 qpart m + (2^31-1)m + x(m-1) */ /* 0 <= 2^31 newx <= (2^31-1)m + x(m-1) */ /* 0 <= newx <= (1-1/2^31)m + x(m-1)/2^31 */ /* 0 <= newx <= (1-1/2^31)(2^14-1) + (2^32-1)((2^14-1)-1)/2^31 */ x -= qpart*m; *q += qpart; /* x <= 49146 */ qpart = (x*(uint64)v)>>31; /* 0 <= newx <= (1-1/2^31)m + x(m-1)/2^31 */ /* 0 <= newx <= m + 49146(2^14-1)/2^31 */ /* 0 <= newx <= m + 0.4 */ /* 0 <= newx <= m */ x -= qpart*m; *q += qpart; /* x <= m */ x -= m; *q += 1; mask = -(x>>31); x += mask&(uint32)m; *q += mask; /* x < m */ *r = x; } static uint16 uint32_mod_uint14(uint32 x,uint16 m) { uint32 q; uint16 r; uint32_divmod_uint14(&q,&r,x,m); return r; } /* from supercop-20201130/crypto_kem/sntrup761/ref/int32.c */ static void int32_divmod_uint14(int32 *q,uint16 *r,int32 x,uint16 m) { uint32 uq,uq2; uint16 ur,ur2; uint32 mask; uint32_divmod_uint14(&uq,&ur,0x80000000+(uint32)x,m); uint32_divmod_uint14(&uq2,&ur2,0x80000000,m); ur -= ur2; uq -= uq2; mask = -(uint32)(ur>>15); ur += mask&m; uq += mask; *r = ur; *q = uq; } static uint16 int32_mod_uint14(int32 x,uint16 m) { int32 q; uint16 r; int32_divmod_uint14(&q,&r,x,m); return r; } /* from supercop-20201130/crypto_kem/sntrup761/ref/paramsmenu.h */ /* pick one of these three: */ #define SIZE761 #undef SIZE653 #undef SIZE857 /* pick one of these two: */ #define SNTRUP /* Streamlined NTRU Prime */ #undef LPR /* NTRU LPRime */ /* from supercop-20201130/crypto_kem/sntrup761/ref/params.h */ #ifndef params_H #define params_H /* menu of parameter choices: */ /* what the menu means: */ #if defined(SIZE761) #define p 761 #define q 4591 #define Rounded_bytes 1007 #ifndef LPR #define Rq_bytes 1158 #define w 286 #else #define w 250 #define tau0 2156 #define tau1 114 #define tau2 2007 #define tau3 287 #endif #elif defined(SIZE653) #define p 653 #define q 4621 #define Rounded_bytes 865 #ifndef LPR #define Rq_bytes 994 #define w 288 #else #define w 252 #define tau0 2175 #define tau1 113 #define tau2 2031 #define tau3 290 #endif #elif defined(SIZE857) #define p 857 #define q 5167 #define Rounded_bytes 1152 #ifndef LPR #define Rq_bytes 1322 #define w 322 #else #define w 281 #define tau0 2433 #define tau1 101 #define tau2 2265 #define tau3 324 #endif #else #error "no parameter set defined" #endif #ifdef LPR #define I 256 #endif #endif /* from supercop-20201130/crypto_kem/sntrup761/ref/Decode.h */ #ifndef Decode_H #define Decode_H /* Decode(R,s,M,len) */ /* assumes 0 < M[i] < 16384 */ /* produces 0 <= R[i] < M[i] */ #endif /* from supercop-20201130/crypto_kem/sntrup761/ref/Decode.c */ static void Decode(uint16 *out,const unsigned char *S,const uint16 *M,long long len) { if (len == 1) { if (M[0] == 1) *out = 0; else if (M[0] <= 256) *out = uint32_mod_uint14(S[0],M[0]); else *out = uint32_mod_uint14(S[0]+(((uint16)S[1])<<8),M[0]); } if (len > 1) { uint16 R2[(len+1)/2]; uint16 M2[(len+1)/2]; uint16 bottomr[len/2]; uint32 bottomt[len/2]; long long i; for (i = 0;i < len-1;i += 2) { uint32 m = M[i]*(uint32) M[i+1]; if (m > 256*16383) { bottomt[i/2] = 256*256; bottomr[i/2] = S[0]+256*S[1]; S += 2; M2[i/2] = (((m+255)>>8)+255)>>8; } else if (m >= 16384) { bottomt[i/2] = 256; bottomr[i/2] = S[0]; S += 1; M2[i/2] = (m+255)>>8; } else { bottomt[i/2] = 1; bottomr[i/2] = 0; M2[i/2] = m; } } if (i < len) M2[i/2] = M[i]; Decode(R2,S,M2,(len+1)/2); for (i = 0;i < len-1;i += 2) { uint32 r = bottomr[i/2]; uint32 r1; uint16 r0; r += bottomt[i/2]*R2[i/2]; uint32_divmod_uint14(&r1,&r0,r,M[i]); r1 = uint32_mod_uint14(r1,M[i+1]); /* only needed for invalid inputs */ *out++ = r0; *out++ = r1; } if (i < len) *out++ = R2[i/2]; } } /* from supercop-20201130/crypto_kem/sntrup761/ref/Encode.h */ #ifndef Encode_H #define Encode_H /* Encode(s,R,M,len) */ /* assumes 0 <= R[i] < M[i] < 16384 */ #endif /* from supercop-20201130/crypto_kem/sntrup761/ref/Encode.c */ /* 0 <= R[i] < M[i] < 16384 */ static void Encode(unsigned char *out,const uint16 *R,const uint16 *M,long long len) { if (len == 1) { uint16 r = R[0]; uint16 m = M[0]; while (m > 1) { *out++ = r; r >>= 8; m = (m+255)>>8; } } if (len > 1) { uint16 R2[(len+1)/2]; uint16 M2[(len+1)/2]; long long i; for (i = 0;i < len-1;i += 2) { uint32 m0 = M[i]; uint32 r = R[i]+R[i+1]*m0; uint32 m = M[i+1]*m0; while (m >= 16384) { *out++ = r; r >>= 8; m = (m+255)>>8; } R2[i/2] = r; M2[i/2] = m; } if (i < len) { R2[i/2] = R[i]; M2[i/2] = M[i]; } Encode(out,R2,M2,(len+1)/2); } } /* from supercop-20201130/crypto_kem/sntrup761/ref/kem.c */ #ifdef LPR #endif /* ----- masks */ #ifndef LPR /* return -1 if x!=0; else return 0 */ static int int16_nonzero_mask(int16 x) { uint16 u = x; /* 0, else 1...65535 */ uint32 v = u; /* 0, else 1...65535 */ v = -v; /* 0, else 2^32-65535...2^32-1 */ v >>= 31; /* 0, else 1 */ return -v; /* 0, else -1 */ } #endif /* return -1 if x<0; otherwise return 0 */ static int int16_negative_mask(int16 x) { uint16 u = x; u >>= 15; return -(int) u; /* alternative with gcc -fwrapv: */ /* x>>15 compiles to CPU's arithmetic right shift */ } /* ----- arithmetic mod 3 */ typedef int8 small; /* F3 is always represented as -1,0,1 */ /* so ZZ_fromF3 is a no-op */ /* x must not be close to top int16 */ static small F3_freeze(int16 x) { return int32_mod_uint14(x+1,3)-1; } /* ----- arithmetic mod q */ #define q12 ((q-1)/2) typedef int16 Fq; /* always represented as -q12...q12 */ /* so ZZ_fromFq is a no-op */ /* x must not be close to top int32 */ static Fq Fq_freeze(int32 x) { return int32_mod_uint14(x+q12,q)-q12; } #ifndef LPR static Fq Fq_recip(Fq a1) { int i = 1; Fq ai = a1; while (i < q-2) { ai = Fq_freeze(a1*(int32)ai); i += 1; } return ai; } #endif /* ----- Top and Right */ #ifdef LPR #define tau 16 static int8 Top(Fq C) { return (tau1*(int32)(C+tau0)+16384)>>15; } static Fq Right(int8 T) { return Fq_freeze(tau3*(int32)T-tau2); } #endif /* ----- small polynomials */ #ifndef LPR /* 0 if Weightw_is(r), else -1 */ static int Weightw_mask(small *r) { int weight = 0; int i; for (i = 0;i < p;++i) weight += r[i]&1; return int16_nonzero_mask(weight-w); } /* R3_fromR(R_fromRq(r)) */ static void R3_fromRq(small *out,const Fq *r) { int i; for (i = 0;i < p;++i) out[i] = F3_freeze(r[i]); } /* h = f*g in the ring R3 */ static void R3_mult(small *h,const small *f,const small *g) { small fg[p+p-1]; small result; int i,j; for (i = 0;i < p;++i) { result = 0; for (j = 0;j <= i;++j) result = F3_freeze(result+f[j]*g[i-j]); fg[i] = result; } for (i = p;i < p+p-1;++i) { result = 0; for (j = i-p+1;j < p;++j) result = F3_freeze(result+f[j]*g[i-j]); fg[i] = result; } for (i = p+p-2;i >= p;--i) { fg[i-p] = F3_freeze(fg[i-p]+fg[i]); fg[i-p+1] = F3_freeze(fg[i-p+1]+fg[i]); } for (i = 0;i < p;++i) h[i] = fg[i]; } /* returns 0 if recip succeeded; else -1 */ static int R3_recip(small *out,const small *in) { small f[p+1],g[p+1],v[p+1],r[p+1]; int i,loop,delta; int sign,swap,t; for (i = 0;i < p+1;++i) v[i] = 0; for (i = 0;i < p+1;++i) r[i] = 0; r[0] = 1; for (i = 0;i < p;++i) f[i] = 0; f[0] = 1; f[p-1] = f[p] = -1; for (i = 0;i < p;++i) g[p-1-i] = in[i]; g[p] = 0; delta = 1; for (loop = 0;loop < 2*p-1;++loop) { for (i = p;i > 0;--i) v[i] = v[i-1]; v[0] = 0; sign = -g[0]*f[0]; swap = int16_negative_mask(-delta) & int16_nonzero_mask(g[0]); delta ^= swap&(delta^-delta); delta += 1; for (i = 0;i < p+1;++i) { t = swap&(f[i]^g[i]); f[i] ^= t; g[i] ^= t; t = swap&(v[i]^r[i]); v[i] ^= t; r[i] ^= t; } for (i = 0;i < p+1;++i) g[i] = F3_freeze(g[i]+sign*f[i]); for (i = 0;i < p+1;++i) r[i] = F3_freeze(r[i]+sign*v[i]); for (i = 0;i < p;++i) g[i] = g[i+1]; g[p] = 0; } sign = f[0]; for (i = 0;i < p;++i) out[i] = sign*v[p-1-i]; return int16_nonzero_mask(delta); } #endif /* ----- polynomials mod q */ /* h = f*g in the ring Rq */ static void Rq_mult_small(Fq *h,const Fq *f,const small *g) { Fq fg[p+p-1]; Fq result; int i,j; for (i = 0;i < p;++i) { result = 0; for (j = 0;j <= i;++j) result = Fq_freeze(result+f[j]*(int32)g[i-j]); fg[i] = result; } for (i = p;i < p+p-1;++i) { result = 0; for (j = i-p+1;j < p;++j) result = Fq_freeze(result+f[j]*(int32)g[i-j]); fg[i] = result; } for (i = p+p-2;i >= p;--i) { fg[i-p] = Fq_freeze(fg[i-p]+fg[i]); fg[i-p+1] = Fq_freeze(fg[i-p+1]+fg[i]); } for (i = 0;i < p;++i) h[i] = fg[i]; } #ifndef LPR /* h = 3f in Rq */ static void Rq_mult3(Fq *h,const Fq *f) { int i; for (i = 0;i < p;++i) h[i] = Fq_freeze(3*f[i]); } /* out = 1/(3*in) in Rq */ /* returns 0 if recip succeeded; else -1 */ static int Rq_recip3(Fq *out,const small *in) { Fq f[p+1],g[p+1],v[p+1],r[p+1]; int i,loop,delta; int swap,t; int32 f0,g0; Fq scale; for (i = 0;i < p+1;++i) v[i] = 0; for (i = 0;i < p+1;++i) r[i] = 0; r[0] = Fq_recip(3); for (i = 0;i < p;++i) f[i] = 0; f[0] = 1; f[p-1] = f[p] = -1; for (i = 0;i < p;++i) g[p-1-i] = in[i]; g[p] = 0; delta = 1; for (loop = 0;loop < 2*p-1;++loop) { for (i = p;i > 0;--i) v[i] = v[i-1]; v[0] = 0; swap = int16_negative_mask(-delta) & int16_nonzero_mask(g[0]); delta ^= swap&(delta^-delta); delta += 1; for (i = 0;i < p+1;++i) { t = swap&(f[i]^g[i]); f[i] ^= t; g[i] ^= t; t = swap&(v[i]^r[i]); v[i] ^= t; r[i] ^= t; } f0 = f[0]; g0 = g[0]; for (i = 0;i < p+1;++i) g[i] = Fq_freeze(f0*g[i]-g0*f[i]); for (i = 0;i < p+1;++i) r[i] = Fq_freeze(f0*r[i]-g0*v[i]); for (i = 0;i < p;++i) g[i] = g[i+1]; g[p] = 0; } scale = Fq_recip(f[0]); for (i = 0;i < p;++i) out[i] = Fq_freeze(scale*(int32)v[p-1-i]); return int16_nonzero_mask(delta); } #endif /* ----- rounded polynomials mod q */ static void Round(Fq *out,const Fq *a) { int i; for (i = 0;i < p;++i) out[i] = a[i]-F3_freeze(a[i]); } /* ----- sorting to generate short polynomial */ static void Short_fromlist(small *out,const uint32 *in) { uint32 L[p]; int i; for (i = 0;i < w;++i) L[i] = in[i]&(uint32)-2; for (i = w;i < p;++i) L[i] = (in[i]&(uint32)-3)|1; crypto_sort_uint32(L,p); for (i = 0;i < p;++i) out[i] = (L[i]&3)-1; } /* ----- underlying hash function */ #define Hash_bytes 32 /* e.g., b = 0 means out = Hash0(in) */ static void Hash_prefix(unsigned char *out,int b,const unsigned char *in,int inlen) { unsigned char x[inlen+1]; unsigned char h[64]; int i; x[0] = b; for (i = 0;i < inlen;++i) x[i+1] = in[i]; crypto_hash_sha512(h,x,inlen+1); for (i = 0;i < 32;++i) out[i] = h[i]; } /* ----- higher-level randomness */ static uint32 urandom32(void) { unsigned char c[4]; uint32 out[4]; randombytes(c,4); out[0] = (uint32)c[0]; out[1] = ((uint32)c[1])<<8; out[2] = ((uint32)c[2])<<16; out[3] = ((uint32)c[3])<<24; return out[0]+out[1]+out[2]+out[3]; } static void Short_random(small *out) { uint32 L[p]; int i; for (i = 0;i < p;++i) L[i] = urandom32(); Short_fromlist(out,L); } #ifndef LPR static void Small_random(small *out) { int i; for (i = 0;i < p;++i) out[i] = (((urandom32()&0x3fffffff)*3)>>30)-1; } #endif /* ----- Streamlined NTRU Prime Core */ #ifndef LPR /* h,(f,ginv) = KeyGen() */ static void KeyGen(Fq *h,small *f,small *ginv) { small g[p]; Fq finv[p]; for (;;) { Small_random(g); if (R3_recip(ginv,g) == 0) break; } Short_random(f); Rq_recip3(finv,f); /* always works */ Rq_mult_small(h,finv,g); } /* c = Encrypt(r,h) */ static void Encrypt(Fq *c,const small *r,const Fq *h) { Fq hr[p]; Rq_mult_small(hr,h,r); Round(c,hr); } /* r = Decrypt(c,(f,ginv)) */ static void Decrypt(small *r,const Fq *c,const small *f,const small *ginv) { Fq cf[p]; Fq cf3[p]; small e[p]; small ev[p]; int mask; int i; Rq_mult_small(cf,c,f); Rq_mult3(cf3,cf); R3_fromRq(e,cf3); R3_mult(ev,e,ginv); mask = Weightw_mask(ev); /* 0 if weight w, else -1 */ for (i = 0;i < w;++i) r[i] = ((ev[i]^1)&~mask)^1; for (i = w;i < p;++i) r[i] = ev[i]&~mask; } #endif /* ----- NTRU LPRime Core */ #ifdef LPR /* (G,A),a = KeyGen(G); leaves G unchanged */ static void KeyGen(Fq *A,small *a,const Fq *G) { Fq aG[p]; Short_random(a); Rq_mult_small(aG,G,a); Round(A,aG); } /* B,T = Encrypt(r,(G,A),b) */ static void Encrypt(Fq *B,int8 *T,const int8 *r,const Fq *G,const Fq *A,const small *b) { Fq bG[p]; Fq bA[p]; int i; Rq_mult_small(bG,G,b); Round(B,bG); Rq_mult_small(bA,A,b); for (i = 0;i < I;++i) T[i] = Top(Fq_freeze(bA[i]+r[i]*q12)); } /* r = Decrypt((B,T),a) */ static void Decrypt(int8 *r,const Fq *B,const int8 *T,const small *a) { Fq aB[p]; int i; Rq_mult_small(aB,B,a); for (i = 0;i < I;++i) r[i] = -int16_negative_mask(Fq_freeze(Right(T[i])-aB[i]+4*w+1)); } #endif /* ----- encoding I-bit inputs */ #ifdef LPR #define Inputs_bytes (I/8) typedef int8 Inputs[I]; /* passed by reference */ static void Inputs_encode(unsigned char *s,const Inputs r) { int i; for (i = 0;i < Inputs_bytes;++i) s[i] = 0; for (i = 0;i < I;++i) s[i>>3] |= r[i]<<(i&7); } #endif /* ----- Expand */ #ifdef LPR static const unsigned char aes_nonce[16] = {0}; static void Expand(uint32 *L,const unsigned char *k) { int i; crypto_stream_aes256ctr((unsigned char *) L,4*p,aes_nonce,k); for (i = 0;i < p;++i) { uint32 L0 = ((unsigned char *) L)[4*i]; uint32 L1 = ((unsigned char *) L)[4*i+1]; uint32 L2 = ((unsigned char *) L)[4*i+2]; uint32 L3 = ((unsigned char *) L)[4*i+3]; L[i] = L0+(L1<<8)+(L2<<16)+(L3<<24); } } #endif /* ----- Seeds */ #ifdef LPR #define Seeds_bytes 32 static void Seeds_random(unsigned char *s) { randombytes(s,Seeds_bytes); } #endif /* ----- Generator, HashShort */ #ifdef LPR /* G = Generator(k) */ static void Generator(Fq *G,const unsigned char *k) { uint32 L[p]; int i; Expand(L,k); for (i = 0;i < p;++i) G[i] = uint32_mod_uint14(L[i],q)-q12; } /* out = HashShort(r) */ static void HashShort(small *out,const Inputs r) { unsigned char s[Inputs_bytes]; unsigned char h[Hash_bytes]; uint32 L[p]; Inputs_encode(s,r); Hash_prefix(h,5,s,sizeof s); Expand(L,h); Short_fromlist(out,L); } #endif /* ----- NTRU LPRime Expand */ #ifdef LPR /* (S,A),a = XKeyGen() */ static void XKeyGen(unsigned char *S,Fq *A,small *a) { Fq G[p]; Seeds_random(S); Generator(G,S); KeyGen(A,a,G); } /* B,T = XEncrypt(r,(S,A)) */ static void XEncrypt(Fq *B,int8 *T,const int8 *r,const unsigned char *S,const Fq *A) { Fq G[p]; small b[p]; Generator(G,S); HashShort(b,r); Encrypt(B,T,r,G,A,b); } #define XDecrypt Decrypt #endif /* ----- encoding small polynomials (including short polynomials) */ #define Small_bytes ((p+3)/4) /* these are the only functions that rely on p mod 4 = 1 */ static void Small_encode(unsigned char *s,const small *f) { small x; int i; for (i = 0;i < p/4;++i) { x = *f++ + 1; x += (*f++ + 1)<<2; x += (*f++ + 1)<<4; x += (*f++ + 1)<<6; *s++ = x; } x = *f++ + 1; *s++ = x; } static void Small_decode(small *f,const unsigned char *s) { unsigned char x; int i; for (i = 0;i < p/4;++i) { x = *s++; *f++ = ((small)(x&3))-1; x >>= 2; *f++ = ((small)(x&3))-1; x >>= 2; *f++ = ((small)(x&3))-1; x >>= 2; *f++ = ((small)(x&3))-1; } x = *s++; *f++ = ((small)(x&3))-1; } /* ----- encoding general polynomials */ #ifndef LPR static void Rq_encode(unsigned char *s,const Fq *r) { uint16 R[p],M[p]; int i; for (i = 0;i < p;++i) R[i] = r[i]+q12; for (i = 0;i < p;++i) M[i] = q; Encode(s,R,M,p); } static void Rq_decode(Fq *r,const unsigned char *s) { uint16 R[p],M[p]; int i; for (i = 0;i < p;++i) M[i] = q; Decode(R,s,M,p); for (i = 0;i < p;++i) r[i] = ((Fq)R[i])-q12; } #endif /* ----- encoding rounded polynomials */ static void Rounded_encode(unsigned char *s,const Fq *r) { uint16 R[p],M[p]; int i; for (i = 0;i < p;++i) R[i] = ((r[i]+q12)*10923)>>15; for (i = 0;i < p;++i) M[i] = (q+2)/3; Encode(s,R,M,p); } static void Rounded_decode(Fq *r,const unsigned char *s) { uint16 R[p],M[p]; int i; for (i = 0;i < p;++i) M[i] = (q+2)/3; Decode(R,s,M,p); for (i = 0;i < p;++i) r[i] = R[i]*3-q12; } /* ----- encoding top polynomials */ #ifdef LPR #define Top_bytes (I/2) static void Top_encode(unsigned char *s,const int8 *T) { int i; for (i = 0;i < Top_bytes;++i) s[i] = T[2*i]+(T[2*i+1]<<4); } static void Top_decode(int8 *T,const unsigned char *s) { int i; for (i = 0;i < Top_bytes;++i) { T[2*i] = s[i]&15; T[2*i+1] = s[i]>>4; } } #endif /* ----- Streamlined NTRU Prime Core plus encoding */ #ifndef LPR typedef small Inputs[p]; /* passed by reference */ #define Inputs_random Short_random #define Inputs_encode Small_encode #define Inputs_bytes Small_bytes #define Ciphertexts_bytes Rounded_bytes #define SecretKeys_bytes (2*Small_bytes) #define PublicKeys_bytes Rq_bytes /* pk,sk = ZKeyGen() */ static void ZKeyGen(unsigned char *pk,unsigned char *sk) { Fq h[p]; small f[p],v[p]; KeyGen(h,f,v); Rq_encode(pk,h); Small_encode(sk,f); sk += Small_bytes; Small_encode(sk,v); } /* C = ZEncrypt(r,pk) */ static void ZEncrypt(unsigned char *C,const Inputs r,const unsigned char *pk) { Fq h[p]; Fq c[p]; Rq_decode(h,pk); Encrypt(c,r,h); Rounded_encode(C,c); } /* r = ZDecrypt(C,sk) */ static void ZDecrypt(Inputs r,const unsigned char *C,const unsigned char *sk) { small f[p],v[p]; Fq c[p]; Small_decode(f,sk); sk += Small_bytes; Small_decode(v,sk); Rounded_decode(c,C); Decrypt(r,c,f,v); } #endif /* ----- NTRU LPRime Expand plus encoding */ #ifdef LPR #define Ciphertexts_bytes (Rounded_bytes+Top_bytes) #define SecretKeys_bytes Small_bytes #define PublicKeys_bytes (Seeds_bytes+Rounded_bytes) static void Inputs_random(Inputs r) { unsigned char s[Inputs_bytes]; int i; randombytes(s,sizeof s); for (i = 0;i < I;++i) r[i] = 1&(s[i>>3]>>(i&7)); } /* pk,sk = ZKeyGen() */ static void ZKeyGen(unsigned char *pk,unsigned char *sk) { Fq A[p]; small a[p]; XKeyGen(pk,A,a); pk += Seeds_bytes; Rounded_encode(pk,A); Small_encode(sk,a); } /* c = ZEncrypt(r,pk) */ static void ZEncrypt(unsigned char *c,const Inputs r,const unsigned char *pk) { Fq A[p]; Fq B[p]; int8 T[I]; Rounded_decode(A,pk+Seeds_bytes); XEncrypt(B,T,r,pk,A); Rounded_encode(c,B); c += Rounded_bytes; Top_encode(c,T); } /* r = ZDecrypt(C,sk) */ static void ZDecrypt(Inputs r,const unsigned char *c,const unsigned char *sk) { small a[p]; Fq B[p]; int8 T[I]; Small_decode(a,sk); Rounded_decode(B,c); Top_decode(T,c+Rounded_bytes); XDecrypt(r,B,T,a); } #endif /* ----- confirmation hash */ #define Confirm_bytes 32 /* h = HashConfirm(r,pk,cache); cache is Hash4(pk) */ static void HashConfirm(unsigned char *h,const unsigned char *r,const unsigned char *pk,const unsigned char *cache) { #ifndef LPR unsigned char x[Hash_bytes*2]; int i; Hash_prefix(x,3,r,Inputs_bytes); for (i = 0;i < Hash_bytes;++i) x[Hash_bytes+i] = cache[i]; #else unsigned char x[Inputs_bytes+Hash_bytes]; int i; for (i = 0;i < Inputs_bytes;++i) x[i] = r[i]; for (i = 0;i < Hash_bytes;++i) x[Inputs_bytes+i] = cache[i]; #endif Hash_prefix(h,2,x,sizeof x); } /* ----- session-key hash */ /* k = HashSession(b,y,z) */ static void HashSession(unsigned char *k,int b,const unsigned char *y,const unsigned char *z) { #ifndef LPR unsigned char x[Hash_bytes+Ciphertexts_bytes+Confirm_bytes]; int i; Hash_prefix(x,3,y,Inputs_bytes); for (i = 0;i < Ciphertexts_bytes+Confirm_bytes;++i) x[Hash_bytes+i] = z[i]; #else unsigned char x[Inputs_bytes+Ciphertexts_bytes+Confirm_bytes]; int i; for (i = 0;i < Inputs_bytes;++i) x[i] = y[i]; for (i = 0;i < Ciphertexts_bytes+Confirm_bytes;++i) x[Inputs_bytes+i] = z[i]; #endif Hash_prefix(k,b,x,sizeof x); } /* ----- Streamlined NTRU Prime and NTRU LPRime */ /* pk,sk = KEM_KeyGen() */ static void KEM_KeyGen(unsigned char *pk,unsigned char *sk) { int i; ZKeyGen(pk,sk); sk += SecretKeys_bytes; for (i = 0;i < PublicKeys_bytes;++i) *sk++ = pk[i]; randombytes(sk,Inputs_bytes); sk += Inputs_bytes; Hash_prefix(sk,4,pk,PublicKeys_bytes); } /* c,r_enc = Hide(r,pk,cache); cache is Hash4(pk) */ static void Hide(unsigned char *c,unsigned char *r_enc,const Inputs r,const unsigned char *pk,const unsigned char *cache) { Inputs_encode(r_enc,r); ZEncrypt(c,r,pk); c += Ciphertexts_bytes; HashConfirm(c,r_enc,pk,cache); } /* c,k = Encap(pk) */ static void Encap(unsigned char *c,unsigned char *k,const unsigned char *pk) { Inputs r; unsigned char r_enc[Inputs_bytes]; unsigned char cache[Hash_bytes]; Hash_prefix(cache,4,pk,PublicKeys_bytes); Inputs_random(r); Hide(c,r_enc,r,pk,cache); HashSession(k,1,r_enc,c); } /* 0 if matching ciphertext+confirm, else -1 */ static int Ciphertexts_diff_mask(const unsigned char *c,const unsigned char *c2) { uint16 differentbits = 0; int len = Ciphertexts_bytes+Confirm_bytes; while (len-- > 0) differentbits |= (*c++)^(*c2++); return (1&((differentbits-1)>>8))-1; } /* k = Decap(c,sk) */ static void Decap(unsigned char *k,const unsigned char *c,const unsigned char *sk) { const unsigned char *pk = sk + SecretKeys_bytes; const unsigned char *rho = pk + PublicKeys_bytes; const unsigned char *cache = rho + Inputs_bytes; Inputs r; unsigned char r_enc[Inputs_bytes]; unsigned char cnew[Ciphertexts_bytes+Confirm_bytes]; int mask; int i; ZDecrypt(r,c,sk); Hide(cnew,r_enc,r,pk,cache); mask = Ciphertexts_diff_mask(c,cnew); for (i = 0;i < Inputs_bytes;++i) r_enc[i] ^= mask&(r_enc[i]^rho[i]); HashSession(k,1+mask,r_enc,c); } /* ----- crypto_kem API */ int crypto_kem_sntrup761_keypair(unsigned char *pk,unsigned char *sk) { KEM_KeyGen(pk,sk); return 0; } int crypto_kem_sntrup761_enc(unsigned char *c,unsigned char *k,const unsigned char *pk) { Encap(c,k,pk); return 0; } int crypto_kem_sntrup761_dec(unsigned char *k,const unsigned char *c,const unsigned char *sk) { Decap(k,c,sk); return 0; } openiked-7.4/iked/sntrup761.sh000066400000000000000000000053551477554731100162510ustar00rootroot00000000000000#!/bin/sh # $OpenBSD: sntrup761.sh,v 1.1 2021/05/28 18:01:39 tobhe Exp $ # Placed in the Public Domain. # AUTHOR="supercop-20201130/crypto_kem/sntrup761/ref/implementors" FILES=" supercop-20201130/crypto_sort/int32/portable4/int32_minmax.inc supercop-20201130/crypto_sort/int32/portable4/sort.c supercop-20201130/crypto_sort/uint32/useint32/sort.c supercop-20201130/crypto_kem/sntrup761/ref/uint32.c supercop-20201130/crypto_kem/sntrup761/ref/int32.c supercop-20201130/crypto_kem/sntrup761/ref/paramsmenu.h supercop-20201130/crypto_kem/sntrup761/ref/params.h supercop-20201130/crypto_kem/sntrup761/ref/Decode.h supercop-20201130/crypto_kem/sntrup761/ref/Decode.c supercop-20201130/crypto_kem/sntrup761/ref/Encode.h supercop-20201130/crypto_kem/sntrup761/ref/Encode.c supercop-20201130/crypto_kem/sntrup761/ref/kem.c " ### set -e cd $1 echo -n '/* $' echo 'OpenBSD: $ */' echo echo '/*' echo ' * Public Domain, Authors:' sed -e '/Alphabetical order:/d' -e 's/^/ * - /' < $AUTHOR echo ' */' echo echo '#include ' echo '#include "crypto_api.h"' echo # Map the types used in this code to the ones in crypto_api.h. We use #define # instead of typedef since some systems have existing intXX types and do not # permit multiple typedefs even if they do not conflict. for t in int8 uint8 int16 uint16 int32 uint32 int64 uint64; do echo "#define $t crypto_${t}" done echo for i in $FILES; do echo "/* from $i */" # Changes to all files: # - remove all includes, we inline everything required. # - make functions not required elsewhere static. # - rename the functions we do use. # - remove unneccesary defines and externs. sed -e "/#include/d" \ -e "s/crypto_kem_/crypto_kem_sntrup761_/g" \ -e "s/^void /static void /g" \ -e "s/^int16 /static int16 /g" \ -e "s/^uint16 /static uint16 /g" \ -e "/^extern /d" \ -e '/CRYPTO_NAMESPACE/d' \ -e "/^#define int32 crypto_int32/d" \ $i | \ case "$i" in # Use int64_t for intermediate values in int32_MINMAX to prevent signed # 32-bit integer overflow when called by crypto_sort_uint32. */int32_minmax.inc) sed -e "s/int32 ab = b ^ a/int64_t ab = (int64_t)b ^ (int64_t)a/" \ -e "s/int32 c = b - a/int64_t c = (int64_t)b - (int64_t)a/" ;; */int32/portable4/sort.c) sed -e "s/void crypto_sort/void crypto_sort_int32/g" ;; */uint32/useint32/sort.c) sed -e "s/void crypto_sort/void crypto_sort_uint32/g" ;; # Remove unused function to prevent warning. */crypto_kem/sntrup761/ref/int32.c) sed -e '/ int32_div_uint14/,/^}$/d' ;; # Remove unused function to prevent warning. */crypto_kem/sntrup761/ref/uint32.c) sed -e '/ uint32_div_uint14/,/^}$/d' ;; # Default: pass through. *) cat ;; esac echo done openiked-7.4/iked/timer.c000066400000000000000000000036431477554731100154060ustar00rootroot00000000000000/* $OpenBSD: timer.c,v 1.13 2016/09/13 10:49:52 mikeb Exp $ */ /* * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" void timer_callback(int, short, void *); void timer_set(struct iked *env, struct iked_timer *tmr, void (*cb)(struct iked *, void *), void *arg) { if (evtimer_initialized(&tmr->tmr_ev) && evtimer_pending(&tmr->tmr_ev, NULL)) evtimer_del(&tmr->tmr_ev); tmr->tmr_env = env; tmr->tmr_cb = cb; tmr->tmr_cbarg = arg; evtimer_set(&tmr->tmr_ev, timer_callback, tmr); } void timer_add(struct iked *env, struct iked_timer *tmr, int timeout) { struct timeval tv = { timeout }; evtimer_add(&tmr->tmr_ev, &tv); } void timer_del(struct iked *env, struct iked_timer *tmr) { if (tmr->tmr_env == env && tmr->tmr_cb && evtimer_initialized(&tmr->tmr_ev)) evtimer_del(&tmr->tmr_ev); } void timer_callback(int fd, short event, void *arg) { struct iked_timer *tmr = arg; if (tmr->tmr_cb) tmr->tmr_cb(tmr->tmr_env, tmr->tmr_cbarg); } openiked-7.4/iked/types.h000066400000000000000000000073251477554731100154400ustar00rootroot00000000000000/* $OpenBSD: types.h,v 1.54 2024/02/15 20:10:45 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef IKED_TYPES_H #define IKED_TYPES_H #ifndef IKED_USER #define IKED_USER "_iked" #endif #ifndef IKED_CONFIG #define IKED_CONFIG "/etc/iked.conf" #endif #define IKED_SOCKET "/var/run/iked.sock" #ifndef IKED_CA #define IKED_CA "/etc/iked/" #endif #define IKED_CA_DIR "ca/" #define IKED_CRL_DIR "crls/" #define IKED_CERT_DIR "certs/" #define IKED_PUBKEY_DIR "pubkeys/" #define IKED_PRIVKEY IKED_CA "private/local.key" #define IKED_PUBKEY "local.pub" #define IKED_VENDOR_ID "OpenIKED-" #define IKED_OCSP_RESPCERT "ocsp/responder.crt" #define IKED_OPT_VERBOSE 0x00000001 #define IKED_OPT_NOACTION 0x00000002 #define IKED_OPT_PASSIVE 0x00000004 #define IKED_IKE_PORT 500 #define IKED_NATT_PORT 4500 #define IKED_NONCE_MIN 16 /* XXX 128 bits */ #define IKED_NONCE_SIZE 32 /* XXX 256 bits */ #define IKED_COOKIE_MIN 1 /* min 1 bytes */ #define IKED_COOKIE_MAX 64 /* max 64 bytes */ #define IKED_COOKIE2_MIN 8 /* min 8 bytes */ #define IKED_COOKIE2_MAX 64 /* max 64 bytes */ #define IKED_ID_SIZE 1024 /* XXX should be dynamic */ #define IKED_PSK_SIZE 1024 /* XXX should be dynamic */ #define IKED_MSGBUF_MAX 8192 #define IKED_CFG_MAX 16 /* maximum CP attributes */ #define IKED_IPPROTO_MAX 16 #define IKED_TAG_SIZE 64 #define IKED_CYCLE_BUFFERS 8 /* # of static buffers for mapping */ #define IKED_PASSWORD_SIZE 256 /* limited by most EAP types */ #define IKED_LIFETIME_BYTES 4294967296ULL /* 4 GB */ #define IKED_LIFETIME_SECONDS 10800 /* 3 hours */ #define IKED_E 0x1000 /* Decrypted flag */ struct iked_constmap { unsigned int cm_type; const char *cm_name; const char *cm_descr; }; struct iked_transform { uint8_t xform_type; uint16_t xform_id; uint16_t xform_length; uint16_t xform_keylength; unsigned int xform_score; struct iked_constmap *xform_map; }; enum imsg_type { IMSG_NONE, IMSG_CTL_OK, IMSG_CTL_FAIL, IMSG_CTL_VERBOSE, IMSG_CTL_NOTIFY, IMSG_CTL_RELOAD, IMSG_CTL_RESET, IMSG_CTL_COUPLE, IMSG_CTL_DECOUPLE, IMSG_CTL_ACTIVE, IMSG_CTL_PASSIVE, IMSG_CTL_RESET_ID, IMSG_CTL_SHOW_SA, IMSG_CTL_STATIC, IMSG_COMPILE, IMSG_UDP_SOCKET, IMSG_PFKEY_SOCKET, IMSG_IKE_MESSAGE, IMSG_CFG_POLICY, IMSG_CFG_FLOW, IMSG_CFG_USER, IMSG_CERTREQ, IMSG_CERT, IMSG_CERTVALID, IMSG_CERTINVALID, IMSG_SCERT, IMSG_IF_ADDADDR, IMSG_IF_DELADDR, IMSG_VROUTE_ADD, IMSG_VROUTE_DEL, IMSG_VROUTE_CLONE, IMSG_VDNS_ADD, IMSG_VDNS_DEL, IMSG_OCSP_FD, IMSG_OCSP_CFG, IMSG_AUTH, IMSG_PRIVKEY, IMSG_PUBKEY, IMSG_CTL_SHOW_CERTSTORE, IMSG_CTL_SHOW_STATS, IMSG_CTL_PROCFD, IMSG_CTL_PROCREADY, }; enum privsep_procid { PROC_PARENT = 0, PROC_CONTROL, PROC_CERT, PROC_IKEV2, PROC_MAX }; enum flushmode { RESET_RELOAD = 0, RESET_ALL, RESET_CA, RESET_POLICY, RESET_SA, RESET_USER, }; #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif #endif /* IKED_TYPES_H */ openiked-7.4/iked/util.c000066400000000000000000000435721477554731100152500ustar00rootroot00000000000000/* $OpenBSD: util.c,v 1.45 2024/07/01 14:15:15 yasuoka Exp $ */ /* * Copyright (c) 2021 Tobias Heider * Copyright (c) 2010-2013 Reyk Floeter * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iked.h" #include "ikev2.h" int socket_af(struct sockaddr *sa, in_port_t port) { errno = 0; switch (sa->sa_family) { case AF_INET: ((struct sockaddr_in *)sa)->sin_port = port; #ifdef HAVE_SOCKADDR_SA_LEN ((struct sockaddr_in *)sa)->sin_len = sizeof(struct sockaddr_in); #endif break; case AF_INET6: ((struct sockaddr_in6 *)sa)->sin6_port = port; #ifdef HAVE_SOCKADDR_SA_LEN ((struct sockaddr_in6 *)sa)->sin6_len = sizeof(struct sockaddr_in6); #endif break; default: errno = EPFNOSUPPORT; return (-1); } return (0); } in_port_t socket_getport(struct sockaddr *sa) { switch (sa->sa_family) { case AF_INET: return (ntohs(((struct sockaddr_in *)sa)->sin_port)); case AF_INET6: return (ntohs(((struct sockaddr_in6 *)sa)->sin6_port)); default: return (0); } /* NOTREACHED */ return (0); } int socket_setport(struct sockaddr *sa, in_port_t port) { switch (sa->sa_family) { case AF_INET: ((struct sockaddr_in *)sa)->sin_port = htons(port); break; case AF_INET6: ((struct sockaddr_in6 *)sa)->sin6_port = htons(port); break; default: return (-1); } return (0); } int socket_getaddr(int s, struct sockaddr_storage *ss) { socklen_t sslen = sizeof(*ss); return (getsockname(s, (struct sockaddr *)ss, &sslen)); } int socket_bypass(int s, struct sockaddr *sa) { #if defined(__OpenBSD__) int v, *a; int a4[] = { IPPROTO_IP, IP_AUTH_LEVEL, IP_ESP_TRANS_LEVEL, IP_ESP_NETWORK_LEVEL, #ifdef IPV6_IPCOMP_LEVEL IP_IPCOMP_LEVEL #endif }; int a6[] = { IPPROTO_IPV6, IPV6_AUTH_LEVEL, IPV6_ESP_TRANS_LEVEL, IPV6_ESP_NETWORK_LEVEL, #ifdef IPV6_IPCOMP_LEVEL IPV6_IPCOMP_LEVEL #endif }; switch (sa->sa_family) { case AF_INET: a = a4; break; case AF_INET6: a = a6; break; default: log_warn("%s: invalid address family", __func__); return (-1); } v = IPSEC_LEVEL_BYPASS; if (setsockopt(s, a[0], a[1], &v, sizeof(v)) == -1) { log_warn("%s: AUTH_LEVEL", __func__); return (-1); } if (setsockopt(s, a[0], a[2], &v, sizeof(v)) == -1) { log_warn("%s: ESP_TRANS_LEVEL", __func__); return (-1); } if (setsockopt(s, a[0], a[3], &v, sizeof(v)) == -1) { log_warn("%s: ESP_NETWORK_LEVEL", __func__); return (-1); } #ifdef IP_IPCOMP_LEVEL if (setsockopt(s, a[0], a[4], &v, sizeof(v)) == -1) { log_warn("%s: IPCOMP_LEVEL", __func__); return (-1); } #endif #else /* __OpenBSD__ */ int *a; int a4[] = { IPPROTO_IP, IP_IPSEC_POLICY }; int a6[] = { IPPROTO_IPV6, IPV6_IPSEC_POLICY, }; struct sadb_x_policy pol = { SADB_UPDATE, SADB_EXT_SENSITIVITY, IPSEC_POLICY_BYPASS, 0, 0, 0, 0 }; switch (sa->sa_family) { case AF_INET: a = a4; break; case AF_INET6: a = a6; break; default: log_warn("%s: invalid address family", __func__); return (-1); } pol.sadb_x_policy_dir = IPSEC_DIR_INBOUND; if (setsockopt(s, a[0], a[1], &pol, sizeof(pol)) == -1) { log_warn("%s: IPSEC_DIR_INBOUND", __func__); return (-1); } pol.sadb_x_policy_dir = IPSEC_DIR_OUTBOUND; if (setsockopt(s, a[0], a[1], &pol, sizeof(pol)) == -1) { log_warn("%s: IPSEC_DIR_OUTBOUND", __func__); return (-1); } #endif /* !__OpenBSD__ */ return (0); } int udp_bind(struct sockaddr *sa, in_port_t port) { int s, val; if (socket_af(sa, port) == -1) { log_warn("%s: failed to set UDP port", __func__); return (-1); } if ((s = socket(sa->sa_family, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP)) == -1) { log_warn("%s: failed to get UDP socket", __func__); return (-1); } /* Skip IPsec processing (don't encrypt) for IKE messages */ if (socket_bypass(s, sa) == -1) { log_warn("%s: failed to bypass IPsec on IKE socket", __func__); goto bad; } val = 1; if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(int)) == -1) { log_warn("%s: failed to set reuseport", __func__); goto bad; } val = 1; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int)) == -1) { log_warn("%s: failed to set reuseaddr", __func__); goto bad; } if (sa->sa_family == AF_INET) { #if defined(IP_RECVORIGDSTADDR) val = 1; if (setsockopt(s, IPPROTO_IP, IP_RECVORIGDSTADDR, &val, sizeof(int)) == -1) { log_warn("%s: failed to set IPv4 packet info", __func__); goto bad; } #elif defined(IP_RECVDSTADDR) val = 1; if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &val, sizeof(int)) == -1) { log_warn("%s: failed to set IPv4 packet info", __func__); goto bad; } #endif } else { #ifdef IPV6_RECVPKTINFO val = 1; if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(int)) == -1) { log_warn("%s: failed to set IPv6 packet info", __func__); goto bad; } #endif } if (bind(s, sa, SA_LEN(sa)) == -1) { log_warn("%s: failed to bind UDP socket", __func__); goto bad; } return (s); bad: close(s); return (-1); } int sockaddr_cmp(struct sockaddr *a, struct sockaddr *b, int prefixlen) { struct sockaddr_in *a4, *b4; struct sockaddr_in6 *a6, *b6; uint32_t av[4], bv[4], mv[4]; if (a->sa_family == AF_UNSPEC || b->sa_family == AF_UNSPEC) return (0); else if (a->sa_family > b->sa_family) return (1); else if (a->sa_family < b->sa_family) return (-1); if (prefixlen == -1) memset(&mv, 0xff, sizeof(mv)); switch (a->sa_family) { case AF_INET: a4 = (struct sockaddr_in *)a; b4 = (struct sockaddr_in *)b; av[0] = a4->sin_addr.s_addr; bv[0] = b4->sin_addr.s_addr; if (prefixlen != -1) mv[0] = prefixlen2mask(prefixlen); if ((av[0] & mv[0]) > (bv[0] & mv[0])) return (1); if ((av[0] & mv[0]) < (bv[0] & mv[0])) return (-1); break; case AF_INET6: a6 = (struct sockaddr_in6 *)a; b6 = (struct sockaddr_in6 *)b; memcpy(&av, &a6->sin6_addr.s6_addr, 16); memcpy(&bv, &b6->sin6_addr.s6_addr, 16); if (prefixlen != -1) prefixlen2mask6(prefixlen, mv); if ((av[3] & mv[3]) > (bv[3] & mv[3])) return (1); if ((av[3] & mv[3]) < (bv[3] & mv[3])) return (-1); if ((av[2] & mv[2]) > (bv[2] & mv[2])) return (1); if ((av[2] & mv[2]) < (bv[2] & mv[2])) return (-1); if ((av[1] & mv[1]) > (bv[1] & mv[1])) return (1); if ((av[1] & mv[1]) < (bv[1] & mv[1])) return (-1); if ((av[0] & mv[0]) > (bv[0] & mv[0])) return (1); if ((av[0] & mv[0]) < (bv[0] & mv[0])) return (-1); break; } return (0); } ssize_t sendtofrom(int s, void *buf, size_t len, int flags, struct sockaddr *to, socklen_t tolen, struct sockaddr *from, socklen_t fromlen) { struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; #ifdef IP_SENDSRCADDR struct sockaddr_in *in; #endif #ifdef IPV6_PKTINFO struct in6_pktinfo *pkt6; struct sockaddr_in6 *in6; #endif union { struct cmsghdr hdr; char inbuf[CMSG_SPACE(sizeof(struct in_addr))]; char in6buf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; } cmsgbuf; bzero(&msg, sizeof(msg)); bzero(&cmsgbuf, sizeof(cmsgbuf)); iov.iov_base = buf; iov.iov_len = len; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_name = to; msg.msg_namelen = tolen; msg.msg_controllen = 0; switch (to->sa_family) { case AF_INET: #ifdef IP_SENDSRCADDR in = (struct sockaddr_in *)from; if (in->sin_addr.s_addr == INADDR_ANY) break; msg.msg_control = &cmsgbuf; msg.msg_controllen += sizeof(cmsgbuf.inbuf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); cmsg->cmsg_level = IPPROTO_IP; cmsg->cmsg_type = IP_SENDSRCADDR; memcpy(CMSG_DATA(cmsg), &in->sin_addr, sizeof(struct in_addr)); #endif break; case AF_INET6: #ifdef IPV6_PKTINFO msg.msg_control = &cmsgbuf; msg.msg_controllen += sizeof(cmsgbuf.in6buf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; in6 = (struct sockaddr_in6 *)from; pkt6 = (struct in6_pktinfo *)CMSG_DATA(cmsg); pkt6->ipi6_addr = in6->sin6_addr; #endif break; } return sendmsg(s, &msg, flags); } ssize_t recvfromto(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen, struct sockaddr *to, socklen_t *tolen) { struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; #if !defined(IP_RECVORIGDSTADDR) && defined(IP_RECVDSTADDR) struct sockaddr_in *in; #endif #ifdef IPV6_PKTINFO struct in6_pktinfo *pkt6; struct sockaddr_in6 *in6; #endif ssize_t ret; union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(struct sockaddr_storage))]; } cmsgbuf; bzero(&msg, sizeof(msg)); bzero(&cmsgbuf.buf, sizeof(cmsgbuf.buf)); iov.iov_base = buf; iov.iov_len = len; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_name = from; msg.msg_namelen = *fromlen; msg.msg_control = &cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); if ((ret = recvmsg(s, &msg, flags)) == -1) return (-1); *fromlen = SA_LEN(from); if (getsockname(s, to, tolen) != 0) *tolen = 0; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { switch (from->sa_family) { case AF_INET: #if defined(IP_RECVORIGDSTADDR) if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVORIGDSTADDR) { memcpy(to, CMSG_DATA(cmsg), sizeof(struct sockaddr_in)); } #elif defined(IP_RECVDSTADDR) if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { in = (struct sockaddr_in *)to; in->sin_family = AF_INET; #ifdef HAVE_SOCKADDR_SA_LEN in->sin_len = *tolen = sizeof(*in); #endif memcpy(&in->sin_addr, CMSG_DATA(cmsg), sizeof(struct in_addr)); } #endif /* defined(IP_RECVDSTADDR) */ break; case AF_INET6: #ifdef IPV6_PKTINFO if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { in6 = (struct sockaddr_in6 *)to; in6->sin6_family = AF_INET6; #ifdef HAVE_SOCKADDR_SA_LEN in6->sin6_len = *tolen = sizeof(*in6); #endif pkt6 = (struct in6_pktinfo *)CMSG_DATA(cmsg); memcpy(&in6->sin6_addr, &pkt6->ipi6_addr, sizeof(struct in6_addr)); if (IN6_IS_ADDR_LINKLOCAL(&in6->sin6_addr)) in6->sin6_scope_id = pkt6->ipi6_ifindex; } #endif break; } } return (ret); } const char * print_spi(uint64_t spi, int size) { static char buf[IKED_CYCLE_BUFFERS][32]; static int i = 0; char *ptr; ptr = buf[i]; switch (size) { case 2: snprintf(ptr, 32, "0x%04x", (uint16_t)spi); break; case 4: snprintf(ptr, 32, "0x%08x", (uint32_t)spi); break; case 8: snprintf(ptr, 32, "0x%016llx", (long long unsigned)spi); break; default: snprintf(ptr, 32, "%llu", (long long unsigned)spi); break; } if (++i >= IKED_CYCLE_BUFFERS) i = 0; return (ptr); } const char * print_map(unsigned int type, struct iked_constmap *map) { unsigned int i; static char buf[IKED_CYCLE_BUFFERS][32]; static int idx = 0; const char *name = NULL; if (idx >= IKED_CYCLE_BUFFERS) idx = 0; bzero(buf[idx], sizeof(buf[idx])); for (i = 0; map[i].cm_name != NULL; i++) { if (map[i].cm_type == type) name = map[i].cm_name; } if (name == NULL) snprintf(buf[idx], sizeof(buf[idx]), "", type); else strlcpy(buf[idx], name, sizeof(buf[idx])); return (buf[idx++]); } void lc_idtype(char *str) { for (; *str != '\0' && *str != '/'; str++) *str = tolower((unsigned char)*str); } void print_hex(const uint8_t *buf, off_t offset, size_t length) { unsigned int i; if (log_getverbose() < 3 || !length) return; for (i = 0; i < length; i++) { if (i && (i % 4) == 0) { if ((i % 32) == 0) print_debug("\n"); else print_debug(" "); } print_debug("%02x", buf[offset + i]); } print_debug("\n"); } void print_hexval(const uint8_t *buf, off_t offset, size_t length) { unsigned int i; if (log_getverbose() < 2 || !length) return; print_debug("0x"); for (i = 0; i < length; i++) print_debug("%02x", buf[offset + i]); print_debug("\n"); } void print_hexbuf(struct ibuf *ibuf) { print_hex(ibuf_data(ibuf), 0, ibuf_size(ibuf)); } const char * print_bits(unsigned short v, unsigned char *bits) { static char buf[IKED_CYCLE_BUFFERS][BUFSIZ]; static int idx = 0; unsigned int i, any = 0, j = 0; unsigned char c; if (!bits) return (""); if (++idx >= IKED_CYCLE_BUFFERS) idx = 0; bzero(buf[idx], sizeof(buf[idx])); bits++; while ((i = *bits++)) { if (v & (1 << (i-1))) { if (any) { buf[idx][j++] = ','; if (j >= sizeof(buf[idx])) return (buf[idx]); } any = 1; for (; (c = *bits) > 32; bits++) { buf[idx][j++] = tolower((unsigned char)c); if (j >= sizeof(buf[idx])) return (buf[idx]); } } else for (; *bits > 32; bits++) ; } return (buf[idx]); } uint8_t mask2prefixlen(struct sockaddr *sa) { struct sockaddr_in *sa_in = (struct sockaddr_in *)sa; in_addr_t ina = sa_in->sin_addr.s_addr; if (ina == 0) return (0); else return (33 - ffs(ntohl(ina))); } uint8_t mask2prefixlen6(struct sockaddr *sa) { struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)sa; uint8_t *ap, *ep; unsigned int l = 0; /* * sin6_len is the size of the sockaddr so substract the offset of * the possibly truncated sin6_addr struct. */ ap = (uint8_t *)&sa_in6->sin6_addr; ep = (uint8_t *)sa_in6 + SA_LEN(sa); for (; ap < ep; ap++) { /* this "beauty" is adopted from sbin/route/show.c ... */ switch (*ap) { case 0xff: l += 8; break; case 0xfe: l += 7; goto done; case 0xfc: l += 6; goto done; case 0xf8: l += 5; goto done; case 0xf0: l += 4; goto done; case 0xe0: l += 3; goto done; case 0xc0: l += 2; goto done; case 0x80: l += 1; goto done; case 0x00: goto done; default: fatalx("non contiguous inet6 netmask"); } } done: if (l > sizeof(struct in6_addr) * 8) fatalx("%s: prefixlen %d out of bound", __func__, l); return (l); } uint32_t prefixlen2mask(uint8_t prefixlen) { if (prefixlen == 0) return (0); if (prefixlen > 32) prefixlen = 32; return (htonl(0xffffffff << (32 - prefixlen))); } struct in6_addr * prefixlen2mask6(uint8_t prefixlen, uint32_t *mask) { static struct in6_addr s6; int i; if (prefixlen > 128) prefixlen = 128; bzero(&s6, sizeof(s6)); for (i = 0; i < prefixlen / 8; i++) s6.s6_addr[i] = 0xff; i = prefixlen % 8; if (i) s6.s6_addr[prefixlen / 8] = 0xff00 >> i; memcpy(mask, &s6, sizeof(s6)); return (&s6); } const char * print_addr(void *addr) { static char sbuf[IKED_CYCLE_BUFFERS][NI_MAXHOST + 9]; static int idx; struct sockaddr *sa = addr; char *buf, *hbuf; size_t len, hlen; char pbuf[7]; in_port_t port; hbuf = buf = sbuf[idx]; hlen = len = sizeof(sbuf[idx]); if (++idx >= IKED_CYCLE_BUFFERS) idx = 0; if (sa->sa_family == AF_UNSPEC) { strlcpy(buf, "any", len); return (buf); } if ((port = socket_getport(sa)) != 0 && sa->sa_family == AF_INET6) { /* surround [] */ *(hbuf++) = '['; hlen--; } if (getnameinfo(sa, SA_LEN(sa), hbuf, hlen, NULL, 0, NI_NUMERICHOST) != 0) { strlcpy(buf, "unknown", len); return (buf); } if (port != 0) { if (sa->sa_family == AF_INET6) (void)strlcat(buf, "]", len); snprintf(pbuf, sizeof(pbuf), ":%d", port); (void)strlcat(buf, pbuf, len); } return (buf); } char * get_string(uint8_t *ptr, size_t len) { size_t i; for (i = 0; i < len; i++) if (!isprint(ptr[i])) break; return strndup(ptr, i); } const char * print_proto(uint8_t proto) { struct protoent *p; static char buf[IKED_CYCLE_BUFFERS][BUFSIZ]; static int idx = 0; if (idx >= IKED_CYCLE_BUFFERS) idx = 0; if ((p = getprotobynumber(proto)) != NULL) strlcpy(buf[idx], p->p_name, sizeof(buf[idx])); else snprintf(buf[idx], sizeof(buf[idx]), "%u", proto); return (buf[idx++]); } int expand_string(char *label, size_t len, const char *srch, const char *repl) { char *tmp; char *p, *q; if ((tmp = calloc(1, len)) == NULL) { log_debug("%s: calloc", __func__); return (-1); } p = label; while ((q = strstr(p, srch)) != NULL) { *q = '\0'; if ((strlcat(tmp, p, len) >= len) || (strlcat(tmp, repl, len) >= len)) { log_debug("%s: string too long", __func__); free(tmp); return (-1); } q += strlen(srch); p = q; } if (strlcat(tmp, p, len) >= len) { log_debug("%s: string too long", __func__); free(tmp); return (-1); } strlcpy(label, tmp, len); /* always fits */ free(tmp); return (0); } uint8_t * string2unicode(const char *ascii, size_t *outlen) { uint8_t *uc = NULL; size_t i, len = strlen(ascii); if ((uc = calloc(1, (len * 2) + 2)) == NULL) return (NULL); for (i = 0; i < len; i++) { /* XXX what about the byte order? */ uc[i * 2] = ascii[i]; } *outlen = len * 2; return (uc); } void print_debug(const char *emsg, ...) { va_list ap; if (log_getverbose() > 2) { va_start(ap, emsg); vfprintf(stderr, emsg, ap); va_end(ap); } } void print_verbose(const char *emsg, ...) { va_list ap; if (log_getverbose()) { va_start(ap, emsg); vfprintf(stderr, emsg, ap); va_end(ap); } } openiked-7.4/iked/version.h000066400000000000000000000001341477554731100157500ustar00rootroot00000000000000/* $OpenBSD: version.h,v 1.4 2024/03/02 15:55:58 tobhe Exp $ */ #define IKED_VERSION "7.4" openiked-7.4/iked/vroute-netlink.c000066400000000000000000000553601477554731100172570ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2021 Tobias Heider * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #ifdef WITH_SYSTEMD #include "systemd/sd-bus.h" #endif #include "iked.h" int vroute_setroute(struct iked *, uint32_t, struct sockaddr *, uint8_t, struct sockaddr *, int); int vroute_doroute(struct iked *, uint8_t, struct sockaddr *, struct sockaddr *, struct sockaddr *, int); int vroute_doaddr(struct iked *, int, struct sockaddr *, struct sockaddr *, int); int vroute_dodns(struct iked *, int, unsigned int); void vroute_cleanup(struct iked *); void vroute_insertaddr(struct iked *, int, struct sockaddr *, struct sockaddr *); void vroute_removeaddr(struct iked *, int, struct sockaddr *, struct sockaddr *); void vroute_insertdns(struct iked *, int, struct sockaddr *); void vroute_removedns(struct iked *, int, struct sockaddr *); void vroute_insertroute(struct iked *, struct sockaddr *); void vroute_removeroute(struct iked *, struct sockaddr *); #ifdef WITH_SYSTEMD int vroute_dbus_default_route(struct iked *, int, sd_bus_error *, int, const char *, const char *, const char *); int vroute_dbus_dns(struct iked *, int, sd_bus_error *, int, const char *, const char *, const char *); #endif struct vroute_addr { int va_ifidx; struct sockaddr_storage va_addr; struct sockaddr_storage va_mask; TAILQ_ENTRY(vroute_addr) va_entry; }; TAILQ_HEAD(vroute_addrs, vroute_addr); struct vroute_route { struct sockaddr_storage vr_dest; TAILQ_ENTRY(vroute_route) vr_entry; }; TAILQ_HEAD(vroute_routes, vroute_route); struct vroute_dns { struct sockaddr_storage vd_addr; int vd_ifidx; TAILQ_ENTRY(vroute_dns) vd_entry; }; TAILQ_HEAD(vroute_dnss, vroute_dns); static int nl_addattr(struct nlmsghdr *, int, void *, size_t); static int nl_dorule(struct iked *, int, uint32_t, int, int); struct iked_vroute_sc { struct vroute_addrs ivr_addrs; struct vroute_dnss ivr_dnss; struct vroute_routes ivr_routes; int ivr_rtsock; #ifdef WITH_SYSTEMD sd_bus *ivr_bus; #endif }; #define NL_BUFLEN 1024 #define IKED_RT_TABLE 210 #define IKED_RT_PRIO 210 void vroute_init(struct iked *env) { struct iked_vroute_sc *ivr; ivr = calloc(1, sizeof(*ivr)); if (ivr == NULL) fatal("%s: calloc.", __func__); if ((ivr->ivr_rtsock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) == -1) fatal("%s: failed to create netlink socket", __func__); TAILQ_INIT(&ivr->ivr_addrs); TAILQ_INIT(&ivr->ivr_dnss); TAILQ_INIT(&ivr->ivr_routes); env->sc_vroute = ivr; nl_dorule(env, IKED_RT_TABLE, IKED_RT_PRIO, AF_INET, 1); nl_dorule(env, IKED_RT_TABLE, IKED_RT_PRIO, AF_INET6, 1); } void vroute_cleanup(struct iked *env) { struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_addr *addr; struct vroute_route *route; struct vroute_dns *dns; while ((addr = TAILQ_FIRST(&ivr->ivr_addrs))) { vroute_doaddr(env, addr->va_ifidx, (struct sockaddr *)&addr->va_addr, (struct sockaddr *)&addr->va_mask, 0); TAILQ_REMOVE(&ivr->ivr_addrs, addr, va_entry); free(addr); } while ((route = TAILQ_FIRST(&ivr->ivr_routes))) { vroute_doroute(env, RTM_DELROUTE, (struct sockaddr *)&route->vr_dest, NULL, NULL, 1); TAILQ_REMOVE(&ivr->ivr_routes, route, vr_entry); free(route); } while ((dns = TAILQ_FIRST(&ivr->ivr_dnss))) { vroute_dodns(env, 0, dns->vd_ifidx); TAILQ_REMOVE(&ivr->ivr_dnss, dns, vd_entry); free(dns); } } int vroute_setaddr(struct iked *env, int add, struct sockaddr *addr, int mask, unsigned int ifidx) { struct iovec iov[4]; int iovcnt; struct sockaddr_in mask4; struct sockaddr_in6 mask6; iovcnt = 0; iov[0].iov_base = addr; iov[0].iov_len = SA_LEN(addr); iovcnt++; switch(addr->sa_family) { case AF_INET: bzero(&mask, sizeof(mask)); mask4.sin_addr.s_addr = prefixlen2mask(mask ? mask : 32); mask4.sin_family = AF_INET; iov[1].iov_base = &mask4; iov[1].iov_len = sizeof(mask4); iovcnt++; break; case AF_INET6: bzero(&mask6, sizeof(mask6)); prefixlen2mask6(mask ? mask : 128, (uint32_t *)&mask6.sin6_addr.s6_addr); mask6.sin6_family = AF_INET6; iov[1].iov_base = &mask6; iov[1].iov_len = sizeof(mask6); iovcnt++; break; default: return -1; } iov[2].iov_base = &ifidx; iov[2].iov_len = sizeof(ifidx); iovcnt++; return (proc_composev(&env->sc_ps, PROC_PARENT, add ? IMSG_IF_ADDADDR : IMSG_IF_DELADDR, iov, iovcnt)); } int vroute_getaddr(struct iked *env, struct imsg *imsg) { struct sockaddr *addr, *mask; uint8_t *ptr; size_t left; int af, add; unsigned int ifidx; ptr = imsg->data; left = IMSG_DATA_SIZE(imsg); if (left < sizeof(*addr)) fatalx("bad length imsg received"); addr = (struct sockaddr *) ptr; af = addr->sa_family; if (left < SA_LEN(addr)) fatalx("bad length imsg received"); ptr += SA_LEN(addr); left -= SA_LEN(addr); if (left < sizeof(*mask)) fatalx("bad length imsg received"); mask = (struct sockaddr *) ptr; if (mask->sa_family != af) return (-1); if (left < SA_LEN(mask)) fatalx("bad length imsg received"); ptr += SA_LEN(mask); left -= SA_LEN(mask); if (left != sizeof(ifidx)) fatalx("bad length imsg received"); memcpy(&ifidx, ptr, sizeof(ifidx)); ptr += sizeof(ifidx); left -= sizeof(ifidx); add = (imsg->hdr.type == IMSG_IF_ADDADDR); /* Store address for cleanup */ if (add) vroute_insertaddr(env, ifidx, addr, mask); else vroute_removeaddr(env, ifidx, addr, mask); return (vroute_doaddr(env, ifidx, addr, mask, imsg->hdr.type == IMSG_IF_ADDADDR)); } int vroute_setdns(struct iked *env, int add, struct sockaddr *addr, unsigned int ifidx) { struct iovec iov[2]; if (addr == NULL) return (0); iov[0].iov_base = addr; iov[0].iov_len = SA_LEN(addr); iov[1].iov_base = &ifidx; iov[1].iov_len = sizeof(ifidx); return (proc_composev(&env->sc_ps, PROC_PARENT, add ? IMSG_VDNS_ADD: IMSG_VDNS_DEL, iov, 2)); } int vroute_getdns(struct iked *env, struct imsg *imsg) { struct sockaddr *dns; uint8_t *ptr; size_t left; int add; unsigned int ifidx; ptr = imsg->data; left = IMSG_DATA_SIZE(imsg); if (left < sizeof(*dns)) fatalx("bad length imsg received"); dns = (struct sockaddr *) ptr; if (left < SA_LEN(dns)) fatalx("bad length imsg received"); ptr += SA_LEN(dns); left -= SA_LEN(dns); if (left != sizeof(ifidx)) fatalx("bad length imsg received"); memcpy(&ifidx, ptr, sizeof(ifidx)); ptr += sizeof(ifidx); left -= sizeof(ifidx); add = (imsg->hdr.type == IMSG_VDNS_ADD); if (add) { vroute_insertdns(env, ifidx, dns); } else { vroute_removedns(env, ifidx, dns); } return (vroute_dodns(env, add, ifidx)); } void vroute_insertroute(struct iked *env, struct sockaddr *dest) { struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_route *route; route = calloc(1, sizeof(*route)); if (route == NULL) fatalx("%s: calloc.", __func__); memcpy(&route->vr_dest, dest, SA_LEN(dest)); TAILQ_INSERT_TAIL(&ivr->ivr_routes, route, vr_entry); } void vroute_removeroute(struct iked *env, struct sockaddr *dest) { struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_route *route, *troute; TAILQ_FOREACH_SAFE(route, &ivr->ivr_routes, vr_entry, troute) { if (sockaddr_cmp(dest, (struct sockaddr *)&route->vr_dest, -1)) continue; TAILQ_REMOVE(&ivr->ivr_routes, route, vr_entry); free(route); } } void vroute_insertdns(struct iked *env, int ifidx, struct sockaddr *addr) { struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_dns *dns; dns = calloc(1, sizeof(*dns)); if (dns == NULL) fatalx("%s: calloc.", __func__); memcpy(&dns->vd_addr, addr, SA_LEN(addr)); dns->vd_ifidx = ifidx; TAILQ_INSERT_TAIL(&ivr->ivr_dnss, dns, vd_entry); } void vroute_removedns(struct iked *env, int ifidx, struct sockaddr *addr) { struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_dns *dns, *tdns; TAILQ_FOREACH_SAFE(dns, &ivr->ivr_dnss, vd_entry, tdns) { if (ifidx != dns->vd_ifidx) continue; if (sockaddr_cmp(addr, (struct sockaddr *) &dns->vd_addr, -1)) continue; TAILQ_REMOVE(&ivr->ivr_dnss, dns, vd_entry); free(dns); } } void vroute_insertaddr(struct iked *env, int ifidx, struct sockaddr *addr, struct sockaddr *mask) { struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_addr *vaddr; vaddr = calloc(1, sizeof(*vaddr)); if (vaddr == NULL) fatalx("%s: calloc.", __func__); memcpy(&vaddr->va_addr, addr, SA_LEN(addr)); memcpy(&vaddr->va_mask, mask, SA_LEN(mask)); vaddr->va_ifidx = ifidx; TAILQ_INSERT_TAIL(&ivr->ivr_addrs, vaddr, va_entry); } void vroute_removeaddr(struct iked *env, int ifidx, struct sockaddr *addr, struct sockaddr *mask) { struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_addr *vaddr, *tvaddr; TAILQ_FOREACH_SAFE(vaddr, &ivr->ivr_addrs, va_entry, tvaddr) { if (sockaddr_cmp(addr, (struct sockaddr *)&vaddr->va_addr, -1)) continue; if (sockaddr_cmp(mask, (struct sockaddr *)&vaddr->va_mask, -1)) continue; if (ifidx != vaddr->va_ifidx) continue; TAILQ_REMOVE(&ivr->ivr_addrs, vaddr, va_entry); free(vaddr); } } int vroute_setaddroute(struct iked *env, uint8_t rdomain, struct sockaddr *dst, uint8_t mask, struct sockaddr *ifa) { return (vroute_setroute(env, rdomain, dst, mask, ifa, IMSG_VROUTE_ADD)); } int vroute_setcloneroute(struct iked *env, uint8_t rdomain, struct sockaddr *dst, uint8_t mask, struct sockaddr *addr) { return (vroute_setroute(env, rdomain, dst, mask, addr, IMSG_VROUTE_CLONE)); } int vroute_setdelroute(struct iked *env, uint8_t rdomain, struct sockaddr *dst, uint8_t mask, struct sockaddr *addr) { return (vroute_setroute(env, rdomain, dst, mask, addr, IMSG_VROUTE_DEL)); } int vroute_setroute(struct iked *env, uint32_t rdomain, struct sockaddr *dst, uint8_t mask, struct sockaddr *addr, int type) { struct sockaddr_storage sa; struct sockaddr_in *in; struct sockaddr_in6 *in6; struct iovec iov[5]; int iovcnt = 0; uint8_t af; if (addr && dst->sa_family != addr->sa_family) return (-1); af = dst->sa_family; iov[iovcnt].iov_base = &rdomain; iov[iovcnt].iov_len = sizeof(rdomain); iovcnt++; iov[iovcnt].iov_base = dst; iov[iovcnt].iov_len = SA_LEN(dst); iovcnt++; if (type != IMSG_VROUTE_CLONE && addr) { bzero(&sa, sizeof(sa)); switch(af) { case AF_INET: in = (struct sockaddr_in *)&sa; in->sin_addr.s_addr = prefixlen2mask(mask); in->sin_family = af; iov[iovcnt].iov_base = in; iov[iovcnt].iov_len = sizeof(*in); iovcnt++; break; case AF_INET6: in6 = (struct sockaddr_in6 *)&sa; prefixlen2mask6(mask, (uint32_t *)in6->sin6_addr.s6_addr); in6->sin6_family = af; iov[iovcnt].iov_base = in6; iov[iovcnt].iov_len = sizeof(*in6); iovcnt++; break; } iov[iovcnt].iov_base = addr; iov[iovcnt].iov_len = SA_LEN(addr); iovcnt++; } return (proc_composev(&env->sc_ps, PROC_PARENT, type, iov, iovcnt)); } int vroute_getroute(struct iked *env, struct imsg *imsg) { struct sockaddr *dest, *mask = NULL, *gateway = NULL; uint8_t *ptr; size_t left; int type = 0; uint32_t rdomain; int host = 1; ptr = (uint8_t *)imsg->data; left = IMSG_DATA_SIZE(imsg); if (left < sizeof(rdomain)) return (-1); rdomain = *ptr; ptr += sizeof(rdomain); left -= sizeof(rdomain); if (left < sizeof(struct sockaddr)) return (-1); dest = (struct sockaddr *)ptr; if (left < SA_LEN(dest)) return (-1); socket_setport(dest, 0); ptr += SA_LEN(dest); left -= SA_LEN(dest); if (left != 0) { if (left < sizeof(*mask)) return (-1); mask = (struct sockaddr *)ptr; if (left < SA_LEN(mask)) return (-1); socket_setport(mask, 0); ptr += SA_LEN(mask); left -= SA_LEN(mask); if (left < sizeof(*gateway)) return (-1); gateway = (struct sockaddr *)ptr; if (left < SA_LEN(gateway)) return (-1); socket_setport(gateway, 0); ptr += SA_LEN(gateway); left -= SA_LEN(gateway); /* Flow routes are sent with mask + gateway */ host = 0; } switch(imsg->hdr.type) { case IMSG_VROUTE_ADD: type = RTM_NEWROUTE; break; case IMSG_VROUTE_DEL: vroute_removeroute(env, dest); type = RTM_DELROUTE; break; } return (vroute_doroute(env, type, dest, mask, gateway, host)); } int vroute_getcloneroute(struct iked *env, struct imsg *imsg) { struct sockaddr *dst; struct sockaddr_storage mask; uint8_t *ptr; size_t left; uint32_t rdomain; ptr = (uint8_t *)imsg->data; left = IMSG_DATA_SIZE(imsg); if (left < sizeof(rdomain)) return (-1); rdomain = *ptr; ptr += sizeof(rdomain); left -= sizeof(rdomain); bzero(&mask, sizeof(mask)); if (left < sizeof(struct sockaddr)) return (-1); dst = (struct sockaddr *)ptr; if (left < SA_LEN(dst)) return (-1); ptr += SA_LEN(dst); left -= SA_LEN(dst); vroute_insertroute(env, dst); /* * With rtnetlink(7) the host route is not cloned. Instead, we * add a RTN_THROW route for the host in the IKED routing table. */ return (vroute_doroute(env, RTM_NEWROUTE, dst, NULL, NULL, 1)); } int vroute_doroute(struct iked *env, uint8_t type, struct sockaddr *dest, struct sockaddr *mask, struct sockaddr *addr, int host) { char dest_buf[NI_MAXHOST]; char addr_buf[NI_MAXHOST]; struct iked_vroute_sc *ivr = env->sc_vroute; struct sockaddr_in *in; struct sockaddr_in6 *in6; int af; struct { struct nlmsghdr hdr; char buf[NL_BUFLEN]; } req; struct rtmsg *rtm; if (dest == NULL) return (-1); bzero(&req, sizeof(req)); af = dest->sa_family; req.hdr.nlmsg_type = type; req.hdr.nlmsg_flags = NLM_F_REQUEST; if (type == RTM_NEWROUTE) req.hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); rtm = NLMSG_DATA(&req.hdr); rtm->rtm_family = af; rtm->rtm_protocol = RTPROT_STATIC; if (host) rtm->rtm_type = RTN_THROW; else rtm->rtm_type = RTN_UNICAST; rtm->rtm_scope = RT_SCOPE_UNIVERSE; rtm->rtm_table = IKED_RT_TABLE; bzero(addr_buf, sizeof(addr_buf)); switch (af) { case AF_INET: rtm->rtm_dst_len = mask ? mask2prefixlen(mask) : 32; in = (struct sockaddr_in *) dest; nl_addattr(&req.hdr, RTA_DST, &in->sin_addr.s_addr, 4); inet_ntop(af, &((struct sockaddr_in *)dest)->sin_addr, dest_buf, sizeof(dest_buf)); if (type == RTM_DELROUTE) goto send; if (addr) { inet_ntop(af, &((struct sockaddr_in *)addr)->sin_addr, addr_buf, sizeof(addr_buf)); in = (struct sockaddr_in *) addr; nl_addattr(&req.hdr, RTA_GATEWAY, &in->sin_addr.s_addr, 4); } break; case AF_INET6: rtm->rtm_dst_len = mask ? mask2prefixlen6(mask) : 128; in6 = (struct sockaddr_in6 *) dest; nl_addattr(&req.hdr, RTA_DST, &in6->sin6_addr.s6_addr, 16); inet_ntop(af, &((struct sockaddr_in6 *)dest)->sin6_addr, dest_buf, sizeof(dest_buf)); if (type == RTM_DELROUTE) goto send; if (addr) { inet_ntop(af, &((struct sockaddr_in6 *)addr)->sin6_addr, addr_buf, sizeof(addr_buf)); in6 = (struct sockaddr_in6 *) dest; nl_addattr(&req.hdr, RTA_GATEWAY, &in6->sin6_addr.s6_addr, 16); } break; default: return (-1); } send: log_debug("%s: len: %u type: %s dst %s/%d gateway %s flags %x", __func__, req.hdr.nlmsg_len, type == RTM_NEWROUTE ? "RTM_NEWROUTE" : type == RTM_DELROUTE ? "RTM_DELROUTE" : type == RTM_GETROUTE ? "RTM_GETROUTE" : "unknown", dest_buf, rtm->rtm_dst_len, addr_buf, req.hdr.nlmsg_flags); if (send(ivr->ivr_rtsock, &req, req.hdr.nlmsg_len, 0) == -1) log_warn("%s: send", __func__); return (0); } int vroute_doaddr(struct iked *env, int ifidx, struct sockaddr *addr, struct sockaddr *mask, int add) { struct iked_vroute_sc *ivr = env->sc_vroute; struct sockaddr_in *in; struct sockaddr_in6 *in6; int af; char addr_buf[NI_MAXHOST]; char mask_buf[NI_MAXHOST]; struct { struct nlmsghdr hdr; struct ifaddrmsg ifa; uint8_t buf[NL_BUFLEN]; } req; bzero(&req, sizeof(req)); af = addr->sa_family; req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); req.hdr.nlmsg_type = add ? RTM_NEWADDR : RTM_DELADDR; req.hdr.nlmsg_flags = NLM_F_REQUEST; if (add) req.hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; req.ifa.ifa_family = af; req.ifa.ifa_scope = RT_SCOPE_UNIVERSE; req.ifa.ifa_index = ifidx; switch(af) { case AF_INET: inet_ntop(af, &((struct sockaddr_in *)addr)->sin_addr, addr_buf, sizeof(addr_buf)); inet_ntop(af, &((struct sockaddr_in *)mask)->sin_addr, mask_buf, sizeof(mask_buf)); log_debug("%s: %s inet %s netmask %s", __func__, add ? "add" : "del",addr_buf, mask_buf); req.ifa.ifa_prefixlen = mask2prefixlen(mask); in = (struct sockaddr_in *) addr; nl_addattr(&req.hdr, IFA_LOCAL, &in->sin_addr.s_addr, 4); break; case AF_INET6: inet_ntop(af, &((struct sockaddr_in6 *)addr)->sin6_addr, addr_buf, sizeof(addr_buf)); inet_ntop(af, &((struct sockaddr_in6 *)mask)->sin6_addr, mask_buf, sizeof(mask_buf)); log_debug("%s: %s inet6 %s netmask %s", __func__, add ? "add" : "del",addr_buf, mask_buf); req.ifa.ifa_prefixlen = mask2prefixlen6(mask); in6 = (struct sockaddr_in6 *) addr; nl_addattr(&req.hdr, IFA_LOCAL, &in6->sin6_addr.s6_addr, 16); break; } if (send(ivr->ivr_rtsock, &req, req.hdr.nlmsg_len, 0) == -1) log_warn("%s: send", __func__); return (0); } int vroute_dodns(struct iked *env, int add, unsigned int ifindex) { #ifdef WITH_SYSTEMD struct iked_vroute_sc *ivr = env->sc_vroute; const char *destination = "org.freedesktop.resolve1"; const char *path = "/org/freedesktop/resolve1"; const char *interface = "org.freedesktop.resolve1.Manager"; sd_bus_error error = SD_BUS_ERROR_NULL; int ret; if (ivr->ivr_bus != NULL) { log_warnx("%s: vr_bus already set, internal error", __func__); return (0); } if (sd_bus_open_system(&ivr->ivr_bus) < 0) { log_warn("%s: sd_bus_open_system failed", __func__); ivr->ivr_bus = NULL; return (0); } ret = vroute_dbus_dns(env, ifindex, &error, add, destination, path, interface); if (ret < 0 && sd_bus_error_has_name(&error, "org.freedesktop.resolve1.LinkBusy")) { /* * Interface is managed by networkd, so we need to send * our messages there instead. */ destination = "org.freedesktop.network1"; path = "/org/freedesktop/network1"; interface = "org.freedesktop.network1.Manager"; sd_bus_error_free(&error); ret = vroute_dbus_dns(env, ifindex, &error, add, destination, path, interface); } if (ret < 0) { log_info("%s: vroute_dbus_dns: %s: %s", __func__, error.name, error.message); sd_bus_error_free(&error); } ret = vroute_dbus_default_route(env, ifindex, &error, add, destination, path, interface); if (ret < 0) { log_info("%s: vroute_dbus_default_route: %s: %s", __func__, error.name, error.message); sd_bus_error_free(&error); } sd_bus_flush_close_unref(ivr->ivr_bus); ivr->ivr_bus = NULL; #endif return (0); } static int nl_dorule(struct iked *env, int table, uint32_t prio, int family, int add) { struct iked_vroute_sc *ivr = env->sc_vroute; struct { struct nlmsghdr hdr; uint8_t buf[NL_BUFLEN]; } req; struct rtmsg *rtm; bzero(&req, sizeof(req)); req.hdr.nlmsg_flags = NLM_F_REQUEST; req.hdr.nlmsg_type = add ? RTM_NEWRULE : RTM_DELRULE; req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); if (add) req.hdr.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; rtm = NLMSG_DATA(&req.hdr); rtm->rtm_family = family; rtm->rtm_protocol = RTPROT_BOOT; rtm->rtm_scope = RT_SCOPE_UNIVERSE; rtm->rtm_type = RTN_UNICAST; rtm->rtm_table = IKED_RT_TABLE; nl_addattr(&req.hdr, RTA_PRIORITY, &prio, sizeof(prio)); if (send(ivr->ivr_rtsock, &req, req.hdr.nlmsg_len, 0) == -1) log_warn("%s: send", __func__); return (0); } static int nl_addattr(struct nlmsghdr *hdr, int type, void *data, size_t len) { struct rtattr *rta; if ((NLMSG_ALIGN(hdr->nlmsg_len) + RTA_LENGTH(len)) > NL_BUFLEN) { log_info("%s: buffer too small.", __func__); return (-1); } rta = (struct rtattr *)(((char *)hdr) + NLMSG_ALIGN(hdr->nlmsg_len)); rta->rta_type = type; rta->rta_len = RTA_LENGTH(len); memcpy(RTA_DATA(rta), data, len); hdr->nlmsg_len = NLMSG_ALIGN(hdr->nlmsg_len) + RTA_ALIGN(rta->rta_len); return (0); } #ifdef WITH_SYSTEMD int vroute_dbus_default_route(struct iked *env, int ifidx, sd_bus_error *error, int add, const char *destination, const char *path, const char *interface) { struct iked_vroute_sc *ivr = env->sc_vroute; sd_bus *bus = ivr->ivr_bus; int ret; ret = sd_bus_call_method(bus, destination, path, interface, "SetLinkDefaultRoute", error, NULL, "ib", ifidx, add); return (ret); } int vroute_dbus_dns(struct iked *env, int ifidx, sd_bus_error *error, int add, const char *destination, const char *path, const char *interface) { struct sockaddr *dns; struct sockaddr_in *in; struct sockaddr_in6 *in6; struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_dns *vdns; sd_bus_message *req = NULL; sd_bus *bus = ivr->ivr_bus; int ret; ret = sd_bus_message_new_method_call(bus, &req, destination, path, interface, "SetLinkDNS"); if (ret < 0) return (-1); ret = sd_bus_message_append(req, "i", ifidx); if (ret < 0) return (-1); ret = sd_bus_message_open_container(req, 'a', "(iay)"); if (ret < 0) return (-1); /* Empty list means remove all */ if (add) { TAILQ_FOREACH(vdns, &ivr->ivr_dnss, vd_entry) { if (vdns->vd_ifidx != ifidx) continue; ret = sd_bus_message_open_container(req, 'r', "iay"); if (ret < 0) return (-1); dns = (struct sockaddr *)&vdns->vd_addr; ret = sd_bus_message_append(req, "i", dns->sa_family); if (ret < 0) return (-1); switch (dns->sa_family) { case AF_INET: in = (struct sockaddr_in *)dns; ret = sd_bus_message_append_array(req, 'y', &in->sin_addr, 4); if (ret < 0) return (-1); break; case AF_INET6: in6 = (struct sockaddr_in6 *)dns; ret = sd_bus_message_append_array(req, 'y', &in6->sin6_addr, 16); if (ret < 0) return (-1); break; } ret = sd_bus_message_close_container(req); if (ret < 0) return (-1); } } ret = sd_bus_message_close_container(req); if (ret < 0) return (-1); ret = sd_bus_call(bus, req, 0, error, NULL); return (ret); } #endif /* WITH_SYSTEMD */ openiked-7.4/iked/vroute.c000066400000000000000000000650201477554731100156070ustar00rootroot00000000000000/* $OpenBSD: vroute.c,v 1.20 2024/07/14 13:13:33 tobhe Exp $ */ /* * Copyright (c) 2021 Tobias Heider * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ROUTE_SOCKET_BUF_SIZE 16384 #define IKED_VROUTE_PRIO 6 #define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) int vroute_setroute(struct iked *, uint32_t, struct sockaddr *, uint8_t, struct sockaddr *, int); int vroute_doroute(struct iked *, int, int, int, uint8_t, struct sockaddr *, struct sockaddr *, struct sockaddr *, int *); int vroute_doaddr(struct iked *, char *, struct sockaddr *, struct sockaddr *, int); #ifdef __OpenBSD__ int vroute_dodns(struct iked *, struct sockaddr *, int, unsigned int); void vroute_rtmsg_cb(int, short, void *); #endif #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) static int vroute_dodefaultroute(struct iked *, int, int, uint8_t, struct sockaddr *); #endif void vroute_cleanup(struct iked *); void vroute_insertaddr(struct iked *, int, struct sockaddr *, struct sockaddr *); void vroute_removeaddr(struct iked *, int, struct sockaddr *, struct sockaddr *); void vroute_insertdns(struct iked *, int, struct sockaddr *); void vroute_removedns(struct iked *, int, struct sockaddr *); void vroute_insertroute(struct iked *, int, struct sockaddr *, struct sockaddr *); void vroute_removeroute(struct iked *, int, struct sockaddr *, struct sockaddr *); struct vroute_addr { int va_ifidx; struct sockaddr_storage va_addr; struct sockaddr_storage va_mask; TAILQ_ENTRY(vroute_addr) va_entry; }; TAILQ_HEAD(vroute_addrs, vroute_addr); struct vroute_route { int vr_rdomain; int vr_flags; struct sockaddr_storage vr_dest; struct sockaddr_storage vr_mask; TAILQ_ENTRY(vroute_route) vr_entry; }; TAILQ_HEAD(vroute_routes, vroute_route); struct vroute_dns { struct sockaddr_storage vd_addr; int vd_ifidx; TAILQ_ENTRY(vroute_dns) vd_entry; }; TAILQ_HEAD(vroute_dnss, vroute_dns); struct iked_vroute_sc { struct vroute_addrs ivr_addrs; struct vroute_routes ivr_routes; struct vroute_dnss ivr_dnss; struct event ivr_routeev; int ivr_iosock; int ivr_iosock6; int ivr_rtsock; int ivr_rtseq; pid_t ivr_pid; }; struct vroute_msg { struct rt_msghdr vm_rtm; uint8_t vm_space[512]; }; int vroute_process(struct iked *, int msglen, struct vroute_msg *, struct sockaddr *, struct sockaddr *, struct sockaddr *, int *); #ifdef __OpenBSD__ void vroute_rtmsg_cb(int fd, short events, void *arg) { struct iked *env = (struct iked *) arg; struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_dns *dns; static uint8_t *buf; struct rt_msghdr *rtm; ssize_t n; if (buf == NULL) { buf = malloc(ROUTE_SOCKET_BUF_SIZE); if (buf == NULL) fatal("malloc"); } rtm = (struct rt_msghdr *)buf; if ((n = read(fd, buf, ROUTE_SOCKET_BUF_SIZE)) == -1) { if (errno == EAGAIN || errno == EINTR) return; log_warn("%s: read error", __func__); return; } if (n == 0) fatal("routing socket closed"); if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) { log_warnx("partial rtm of %zd in buffer", n); return; } if (rtm->rtm_version != RTM_VERSION) return; switch(rtm->rtm_type) { case RTM_PROPOSAL: if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) { TAILQ_FOREACH(dns, &ivr->ivr_dnss, vd_entry) { log_debug("%s: got solicit", __func__); vroute_dodns(env, (struct sockaddr *) &dns->vd_addr, 1, dns->vd_ifidx); } } break; default: log_debug("%s: unexpected RTM: %d", __func__, rtm->rtm_type); break; } } #endif void vroute_init(struct iked *env) { struct iked_vroute_sc *ivr; ivr = calloc(1, sizeof(*ivr)); if (ivr == NULL) fatal("%s: calloc.", __func__); if ((ivr->ivr_iosock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) fatal("%s: failed to create ioctl socket", __func__); if ((ivr->ivr_iosock6 = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) fatal("%s: failed to create ioctl socket", __func__); if ((ivr->ivr_rtsock = socket(AF_ROUTE, SOCK_RAW, AF_UNSPEC)) == -1) fatal("%s: failed to create routing socket", __func__); #ifdef __OpenBSD__ int rtfilter; rtfilter = ROUTE_FILTER(RTM_GET) | ROUTE_FILTER(RTM_PROPOSAL); if (setsockopt(ivr->ivr_rtsock, AF_ROUTE, ROUTE_MSGFILTER, &rtfilter, sizeof(rtfilter)) == -1) fatal("%s: setsockopt(ROUTE_MSGFILTER)", __func__); #endif TAILQ_INIT(&ivr->ivr_addrs); TAILQ_INIT(&ivr->ivr_dnss); TAILQ_INIT(&ivr->ivr_routes); ivr->ivr_pid = getpid(); env->sc_vroute = ivr; #ifdef __OpenBSD__ event_set(&ivr->ivr_routeev, ivr->ivr_rtsock, EV_READ | EV_PERSIST, vroute_rtmsg_cb, env); event_add(&ivr->ivr_routeev, NULL); #endif } void vroute_cleanup(struct iked *env) { char ifname[IF_NAMESIZE]; struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_addr *addr; struct vroute_route *route; struct vroute_dns *dns; while ((addr = TAILQ_FIRST(&ivr->ivr_addrs))) { if_indextoname(addr->va_ifidx, ifname); vroute_doaddr(env, ifname, (struct sockaddr *)&addr->va_addr, (struct sockaddr *)&addr->va_mask, 0); TAILQ_REMOVE(&ivr->ivr_addrs, addr, va_entry); free(addr); } while ((route = TAILQ_FIRST(&ivr->ivr_routes))) { vroute_doroute(env, RTF_UP | RTF_GATEWAY | RTF_STATIC, route->vr_flags, route->vr_rdomain, RTM_DELETE, (struct sockaddr *)&route->vr_dest, (struct sockaddr *)&route->vr_mask, NULL, NULL); TAILQ_REMOVE(&ivr->ivr_routes, route, vr_entry); free(route); } #ifdef __OpenBSD__ while ((dns = TAILQ_FIRST(&ivr->ivr_dnss))) { vroute_dodns(env, (struct sockaddr *)&dns->vd_addr, 0, dns->vd_ifidx); TAILQ_REMOVE(&ivr->ivr_dnss, dns, vd_entry); free(dns); } #endif } int vroute_setaddr(struct iked *env, int add, struct sockaddr *addr, int mask, unsigned int ifidx) { struct iovec iov[4]; int iovcnt; struct sockaddr_in mask4; struct sockaddr_in6 mask6; iovcnt = 0; iov[0].iov_base = addr; iov[0].iov_len = addr->sa_len; iovcnt++; switch(addr->sa_family) { case AF_INET: bzero(&mask, sizeof(mask)); mask4.sin_addr.s_addr = prefixlen2mask(mask ? mask : 32); mask4.sin_family = AF_INET; mask4.sin_len = sizeof(mask4); iov[1].iov_base = &mask4; iov[1].iov_len = sizeof(mask4); iovcnt++; break; case AF_INET6: bzero(&mask6, sizeof(mask6)); prefixlen2mask6(mask ? mask : 128, (uint32_t *)&mask6.sin6_addr.s6_addr); mask6.sin6_family = AF_INET6; mask6.sin6_len = sizeof(mask6); iov[1].iov_base = &mask6; iov[1].iov_len = sizeof(mask6); iovcnt++; break; default: return -1; } iov[2].iov_base = &ifidx; iov[2].iov_len = sizeof(ifidx); iovcnt++; return (proc_composev(&env->sc_ps, PROC_PARENT, add ? IMSG_IF_ADDADDR : IMSG_IF_DELADDR, iov, iovcnt)); } int vroute_getaddr(struct iked *env, struct imsg *imsg) { char ifname[IF_NAMESIZE]; struct sockaddr *addr, *mask; uint8_t *ptr; size_t left; int af, add; unsigned int ifidx; ptr = imsg->data; left = IMSG_DATA_SIZE(imsg); if (left < sizeof(*addr)) fatalx("bad length imsg received"); addr = (struct sockaddr *) ptr; af = addr->sa_family; if (left < addr->sa_len) fatalx("bad length imsg received"); ptr += addr->sa_len; left -= addr->sa_len; if (left < sizeof(*mask)) fatalx("bad length imsg received"); mask = (struct sockaddr *) ptr; if (mask->sa_family != af) return (-1); if (left < mask->sa_len) fatalx("bad length imsg received"); ptr += mask->sa_len; left -= mask->sa_len; if (left != sizeof(ifidx)) fatalx("bad length imsg received"); memcpy(&ifidx, ptr, sizeof(ifidx)); ptr += sizeof(ifidx); left -= sizeof(ifidx); add = (imsg->hdr.type == IMSG_IF_ADDADDR); /* Store address for cleanup */ if (add) vroute_insertaddr(env, ifidx, addr, mask); else vroute_removeaddr(env, ifidx, addr, mask); if_indextoname(ifidx, ifname); return (vroute_doaddr(env, ifname, addr, mask, add)); } int vroute_setdns(struct iked *env, int add, struct sockaddr *addr, unsigned int ifidx) { struct iovec iov[2]; iov[0].iov_base = addr; iov[0].iov_len = addr->sa_len; iov[1].iov_base = &ifidx; iov[1].iov_len = sizeof(ifidx); return (proc_composev(&env->sc_ps, PROC_PARENT, add ? IMSG_VDNS_ADD: IMSG_VDNS_DEL, iov, 2)); } int vroute_getdns(struct iked *env, struct imsg *imsg) { #ifdef __OpenBSD__ struct sockaddr *dns; uint8_t *ptr; size_t left; int add; unsigned int ifidx; ptr = imsg->data; left = IMSG_DATA_SIZE(imsg); if (left < sizeof(*dns)) fatalx("bad length imsg received"); dns = (struct sockaddr *) ptr; if (left < dns->sa_len) fatalx("bad length imsg received"); ptr += dns->sa_len; left -= dns->sa_len; if (left != sizeof(ifidx)) fatalx("bad length imsg received"); memcpy(&ifidx, ptr, sizeof(ifidx)); ptr += sizeof(ifidx); left -= sizeof(ifidx); add = (imsg->hdr.type == IMSG_VDNS_ADD); if (add) { vroute_insertdns(env, ifidx, dns); } else { vroute_removedns(env, ifidx, dns); } return (vroute_dodns(env, dns, add, ifidx)); #else /* __OpenBSD__ */ return (0); #endif } void vroute_insertroute(struct iked *env, int rdomain, struct sockaddr *dest, struct sockaddr *mask) { struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_route *route; route = calloc(1, sizeof(*route)); if (route == NULL) fatalx("%s: calloc.", __func__); if (dest != NULL) { route->vr_flags |= RTA_DST; memcpy(&route->vr_dest, dest, dest->sa_len); } if (mask != NULL) { route->vr_flags |= RTA_NETMASK; memcpy(&route->vr_mask, mask, mask->sa_len); } route->vr_rdomain = rdomain; TAILQ_INSERT_TAIL(&ivr->ivr_routes, route, vr_entry); } void vroute_removeroute(struct iked *env, int rdomain, struct sockaddr *dest, struct sockaddr *mask) { struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_route *route, *troute; TAILQ_FOREACH_SAFE(route, &ivr->ivr_routes, vr_entry, troute) { if (sockaddr_cmp(dest, (struct sockaddr *)&route->vr_dest, -1)) continue; if (mask && !(route->vr_flags & RTA_NETMASK)) continue; if (mask && sockaddr_cmp(mask, (struct sockaddr *)&route->vr_mask, -1)) continue; if (rdomain != route->vr_rdomain) continue; TAILQ_REMOVE(&ivr->ivr_routes, route, vr_entry); free(route); } } void vroute_insertdns(struct iked *env, int ifidx, struct sockaddr *addr) { struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_dns *dns; dns = calloc(1, sizeof(*dns)); if (dns == NULL) fatalx("%s: calloc.", __func__); memcpy(&dns->vd_addr, addr, addr->sa_len); dns->vd_ifidx = ifidx; TAILQ_INSERT_TAIL(&ivr->ivr_dnss, dns, vd_entry); } void vroute_removedns(struct iked *env, int ifidx, struct sockaddr *addr) { struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_dns *dns, *tdns; TAILQ_FOREACH_SAFE(dns, &ivr->ivr_dnss, vd_entry, tdns) { if (ifidx != dns->vd_ifidx) continue; if (sockaddr_cmp(addr, (struct sockaddr *) &dns->vd_addr, -1)) continue; TAILQ_REMOVE(&ivr->ivr_dnss, dns, vd_entry); free(dns); } } void vroute_insertaddr(struct iked *env, int ifidx, struct sockaddr *addr, struct sockaddr *mask) { struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_addr *vaddr; vaddr = calloc(1, sizeof(*vaddr)); if (vaddr == NULL) fatalx("%s: calloc.", __func__); memcpy(&vaddr->va_addr, addr, addr->sa_len); memcpy(&vaddr->va_mask, mask, mask->sa_len); vaddr->va_ifidx = ifidx; TAILQ_INSERT_TAIL(&ivr->ivr_addrs, vaddr, va_entry); } void vroute_removeaddr(struct iked *env, int ifidx, struct sockaddr *addr, struct sockaddr *mask) { struct iked_vroute_sc *ivr = env->sc_vroute; struct vroute_addr *vaddr, *tvaddr; TAILQ_FOREACH_SAFE(vaddr, &ivr->ivr_addrs, va_entry, tvaddr) { if (sockaddr_cmp(addr, (struct sockaddr *)&vaddr->va_addr, -1)) continue; if (sockaddr_cmp(mask, (struct sockaddr *)&vaddr->va_mask, -1)) continue; if (ifidx != vaddr->va_ifidx) continue; TAILQ_REMOVE(&ivr->ivr_addrs, vaddr, va_entry); free(vaddr); } } int vroute_setaddroute(struct iked *env, uint8_t rdomain, struct sockaddr *dst, uint8_t mask, struct sockaddr *ifa) { return (vroute_setroute(env, rdomain, dst, mask, ifa, IMSG_VROUTE_ADD)); } int vroute_setcloneroute(struct iked *env, uint8_t rdomain, struct sockaddr *dst, uint8_t mask, struct sockaddr *addr) { return (vroute_setroute(env, rdomain, dst, mask, addr, IMSG_VROUTE_CLONE)); } int vroute_setdelroute(struct iked *env, uint8_t rdomain, struct sockaddr *dst, uint8_t mask, struct sockaddr *addr) { return (vroute_setroute(env, rdomain, dst, mask, addr, IMSG_VROUTE_DEL)); } int vroute_setroute(struct iked *env, uint32_t rdomain, struct sockaddr *dst, uint8_t mask, struct sockaddr *addr, int type) { struct sockaddr_storage sa; struct sockaddr_in *in; struct sockaddr_in6 *in6; struct iovec iov[5]; int iovcnt = 0; uint8_t af; if (addr && dst->sa_family != addr->sa_family) return (-1); af = dst->sa_family; iov[iovcnt].iov_base = &rdomain; iov[iovcnt].iov_len = sizeof(rdomain); iovcnt++; iov[iovcnt].iov_base = dst; iov[iovcnt].iov_len = dst->sa_len; iovcnt++; if (type != IMSG_VROUTE_CLONE && addr) { bzero(&sa, sizeof(sa)); switch(af) { case AF_INET: in = (struct sockaddr_in *)&sa; in->sin_addr.s_addr = prefixlen2mask(mask); in->sin_family = af; in->sin_len = sizeof(*in); iov[iovcnt].iov_base = in; iov[iovcnt].iov_len = sizeof(*in); iovcnt++; break; case AF_INET6: in6 = (struct sockaddr_in6 *)&sa; prefixlen2mask6(mask, (uint32_t *)in6->sin6_addr.s6_addr); in6->sin6_family = af; in6->sin6_len = sizeof(*in6); iov[iovcnt].iov_base = in6; iov[iovcnt].iov_len = sizeof(*in6); iovcnt++; break; } iov[iovcnt].iov_base = addr; iov[iovcnt].iov_len = addr->sa_len; iovcnt++; } return (proc_composev(&env->sc_ps, PROC_PARENT, type, iov, iovcnt)); } int vroute_getroute(struct iked *env, struct imsg *imsg) { struct sockaddr *dest, *mask = NULL, *gateway = NULL; uint8_t *ptr; size_t left; int addrs = 0; int type, flags; uint32_t rdomain; ptr = (uint8_t *)imsg->data; left = IMSG_DATA_SIZE(imsg); if (left < sizeof(rdomain)) return (-1); rdomain = *ptr; ptr += sizeof(rdomain); left -= sizeof(rdomain); if (left < sizeof(struct sockaddr)) return (-1); dest = (struct sockaddr *)ptr; if (left < dest->sa_len) return (-1); socket_setport(dest, 0); ptr += dest->sa_len; left -= dest->sa_len; addrs |= RTA_DST; flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; if (left != 0) { if (left < sizeof(struct sockaddr)) return (-1); mask = (struct sockaddr *)ptr; if (left < mask->sa_len) return (-1); socket_setport(mask, 0); ptr += mask->sa_len; left -= mask->sa_len; addrs |= RTA_NETMASK; if (left < sizeof(struct sockaddr)) return (-1); gateway = (struct sockaddr *)ptr; if (left < gateway->sa_len) return (-1); socket_setport(gateway, 0); ptr += gateway->sa_len; left -= gateway->sa_len; addrs |= RTA_GATEWAY; } else { flags |= RTF_HOST; } switch(imsg->hdr.type) { case IMSG_VROUTE_ADD: type = RTM_ADD; break; case IMSG_VROUTE_DEL: type = RTM_DELETE; break; default: return (-1); } #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) if (mask && mask2prefixlen(mask) == 0) { return (vroute_dodefaultroute(env, flags, addrs, type, gateway)); } #endif if (type == RTM_ADD) vroute_insertroute(env, rdomain, dest, mask); else vroute_removeroute(env, rdomain, dest, mask); return (vroute_doroute(env, flags, addrs, rdomain, type, dest, mask, gateway, NULL)); } int vroute_getcloneroute(struct iked *env, struct imsg *imsg) { struct sockaddr *dst; struct sockaddr_storage dest; struct sockaddr_storage mask; struct sockaddr_storage addr; uint8_t *ptr; size_t left; uint32_t rdomain; int flags; int addrs; int need_gw; ptr = (uint8_t *)imsg->data; left = IMSG_DATA_SIZE(imsg); if (left < sizeof(rdomain)) return (-1); rdomain = *ptr; ptr += sizeof(rdomain); left -= sizeof(rdomain); bzero(&dest, sizeof(dest)); bzero(&mask, sizeof(mask)); bzero(&addr, sizeof(addr)); if (left < sizeof(struct sockaddr)) return (-1); dst = (struct sockaddr *)ptr; if (left < dst->sa_len) return (-1); memcpy(&dest, dst, dst->sa_len); ptr += dst->sa_len; left -= dst->sa_len; /* Get route to peer */ flags = RTF_UP | RTF_HOST | RTF_STATIC; if (vroute_doroute(env, flags, RTA_DST, rdomain, RTM_GET, (struct sockaddr *)&dest, (struct sockaddr *)&mask, (struct sockaddr *)&addr, &need_gw)) return (-1); if (need_gw) flags |= RTF_GATEWAY; memcpy(&dest, dst, dst->sa_len); socket_setport((struct sockaddr *)&dest, 0); vroute_insertroute(env, rdomain, (struct sockaddr *)&dest, NULL); /* Set explicit route to peer with gateway addr*/ addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; return (vroute_doroute(env, flags, addrs, rdomain, RTM_ADD, (struct sockaddr *)&dest, (struct sockaddr *)&mask, (struct sockaddr *)&addr, NULL)); } #ifdef __OpenBSD__ int vroute_dodns(struct iked *env, struct sockaddr *dns, int add, unsigned int ifidx) { struct vroute_msg m_rtmsg; struct sockaddr_in *in; struct sockaddr_in6 *in6; struct sockaddr_rtdns rtdns; struct iked_vroute_sc *ivr = env->sc_vroute; struct iovec iov[3]; int i; long pad = 0; int iovcnt = 0, padlen; bzero(&m_rtmsg, sizeof(m_rtmsg)); #define rtm m_rtmsg.vm_rtm rtm.rtm_version = RTM_VERSION; rtm.rtm_type = RTM_PROPOSAL; rtm.rtm_seq = ++ivr->ivr_rtseq; rtm.rtm_priority = RTP_PROPOSAL_STATIC; rtm.rtm_flags = RTF_UP; rtm.rtm_addrs = RTA_DNS; rtm.rtm_index = ifidx; iov[iovcnt].iov_base = &rtm; iov[iovcnt].iov_len = sizeof(rtm); iovcnt++; bzero(&rtdns, sizeof(rtdns)); rtdns.sr_family = dns->sa_family; rtdns.sr_len = 2; if (add) { switch(dns->sa_family) { case AF_INET: rtdns.sr_family = AF_INET; rtdns.sr_len += sizeof(struct in_addr); in = (struct sockaddr_in *)dns; memcpy(rtdns.sr_dns, &in->sin_addr, sizeof(struct in_addr)); break; case AF_INET6: rtdns.sr_family = AF_INET6; rtdns.sr_len += sizeof(struct in6_addr); in6 = (struct sockaddr_in6 *)dns; memcpy(rtdns.sr_dns, &in6->sin6_addr, sizeof(struct in6_addr)); break; default: return (-1); } } iov[iovcnt].iov_base = &rtdns; iov[iovcnt++].iov_len = sizeof(rtdns); padlen = ROUNDUP(sizeof(rtdns)) - sizeof(rtdns); if (padlen > 0) { iov[iovcnt].iov_base = &pad; iov[iovcnt++].iov_len = padlen; } for (i = 0; i < iovcnt; i++) rtm.rtm_msglen += iov[i].iov_len; #undef rtm if (writev(ivr->ivr_rtsock, iov, iovcnt) == -1) log_warn("failed to send route message"); return (0); } #endif /* __OpenBSD__ */ int vroute_doroute(struct iked *env, int flags, int addrs, int rdomain, uint8_t type, struct sockaddr *dest, struct sockaddr *mask, struct sockaddr *addr, int *need_gw) { struct vroute_msg m_rtmsg; struct iovec iov[7]; struct iked_vroute_sc *ivr = env->sc_vroute; ssize_t len; int iovcnt = 0; int i; long pad = 0; size_t padlen; bzero(&m_rtmsg, sizeof(m_rtmsg)); #define rtm m_rtmsg.vm_rtm rtm.rtm_version = RTM_VERSION; rtm.rtm_type = type; rtm.rtm_seq = ++ivr->ivr_rtseq; #ifdef __OpenBSD__ rtm.rtm_tableid = rdomain; if (type != RTM_GET) rtm.rtm_priority = IKED_VROUTE_PRIO; #endif rtm.rtm_flags = flags; rtm.rtm_addrs = addrs; iov[iovcnt].iov_base = &rtm; iov[iovcnt].iov_len = sizeof(rtm); iovcnt++; if (rtm.rtm_addrs & RTA_DST) { iov[iovcnt].iov_base = dest; iov[iovcnt].iov_len = dest->sa_len; iovcnt++; padlen = ROUNDUP(dest->sa_len) - dest->sa_len; if (padlen > 0) { iov[iovcnt].iov_base = &pad; iov[iovcnt].iov_len = padlen; iovcnt++; } } if (rtm.rtm_addrs & RTA_GATEWAY) { iov[iovcnt].iov_base = addr; iov[iovcnt].iov_len = addr->sa_len; iovcnt++; padlen = ROUNDUP(addr->sa_len) - addr->sa_len; if (padlen > 0) { iov[iovcnt].iov_base = &pad; iov[iovcnt].iov_len = padlen; iovcnt++; } } if (rtm.rtm_addrs & RTA_NETMASK) { iov[iovcnt].iov_base = mask; iov[iovcnt].iov_len = mask->sa_len; iovcnt++; padlen = ROUNDUP(mask->sa_len) - mask->sa_len; if (padlen > 0) { iov[iovcnt].iov_base = &pad; iov[iovcnt].iov_len = padlen; iovcnt++; } } for (i = 0; i < iovcnt; i++) rtm.rtm_msglen += iov[i].iov_len; log_debug("%s: len: %u type: %s rdomain: %d flags %x (%s%s)" " addrs %x (dst %s mask %s gw %s)", __func__, rtm.rtm_msglen, type == RTM_ADD ? "RTM_ADD" : type == RTM_DELETE ? "RTM_DELETE" : type == RTM_GET ? "RTM_GET" : "unknown", rdomain, flags, flags & RTF_HOST ? "H" : "", flags & RTF_GATEWAY ? "G" : "", addrs, addrs & RTA_DST ? print_addr(dest) : "<>", addrs & RTA_NETMASK ? print_addr(mask) : "<>", addrs & RTA_GATEWAY ? print_addr(addr) : "<>"); if (writev(ivr->ivr_rtsock, iov, iovcnt) == -1) { if ((type == RTM_ADD && errno != EEXIST) || (type == RTM_DELETE && errno != ESRCH)) { log_warn("%s: write %d", __func__, rtm.rtm_errno); return (0); } } if (type == RTM_GET) { do { len = read(ivr->ivr_rtsock, &m_rtmsg, sizeof(m_rtmsg)); } while(len > 0 && (rtm.rtm_version != RTM_VERSION || rtm.rtm_seq != ivr->ivr_rtseq || rtm.rtm_pid != ivr->ivr_pid)); return (vroute_process(env, len, &m_rtmsg, dest, mask, addr, need_gw)); } #undef rtm return (0); } #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) static int vroute_dodefaultroute(struct iked *env, int flags, int addrs, uint8_t type, struct sockaddr *gateway) { struct sockaddr_storage dest, mask; struct sockaddr_in *in; struct sockaddr_in6 *in6; int ret; int af; bzero(&dest, sizeof(dest)); bzero(&mask, sizeof(mask)); /* 0/1 */ af = gateway->sa_family; switch (af) { case AF_INET: in = (struct sockaddr_in *)&dest; in->sin_addr.s_addr = INADDR_ANY; in->sin_family = af; in->sin_len = sizeof(*in); in = (struct sockaddr_in *)&mask; in->sin_addr.s_addr = prefixlen2mask(1); in->sin_family = af; in->sin_len = sizeof(*in); break; case AF_INET6: in6 = (struct sockaddr_in6 *)&dest; in6->sin6_addr = in6addr_any; in6->sin6_family = af; in6->sin6_len = sizeof(*in6); in6 = (struct sockaddr_in6 *)&mask; prefixlen2mask6(1, (uint32_t *)in6->sin6_addr.s6_addr); in6->sin6_family = af; in6->sin6_len = sizeof(*in6); break; default: return (-1); } if (type == RTM_ADD) vroute_insertroute(env, 0, (struct sockaddr *)&dest, (struct sockaddr *)&mask); else vroute_removeroute(env, 0, (struct sockaddr *)&dest, (struct sockaddr *)&mask); ret = vroute_doroute(env, flags, addrs, 0, type, (struct sockaddr *)&dest, (struct sockaddr *)&mask, gateway, NULL); if (ret) return (-1); /* 128/1 */ bzero(&dest, sizeof(dest)); switch (af) { case AF_INET: in = (struct sockaddr_in *)&dest; in->sin_addr.s_addr = htonl(0x80000000); in->sin_family = af; in->sin_len = sizeof(*in); break; case AF_INET6: in6 = (struct sockaddr_in6 *)&dest; in6->sin6_addr.s6_addr[0] = 0x80; in6->sin6_family = af; in6->sin6_len = sizeof(*in6); break; } if (type == RTM_ADD) vroute_insertroute(env, 0, (struct sockaddr *)&dest, (struct sockaddr *)&mask); else vroute_removeroute(env, 0, (struct sockaddr *)&dest, (struct sockaddr *)&mask); return (vroute_doroute(env, flags, addrs, 0, type, (struct sockaddr *)&dest, (struct sockaddr *)&mask, gateway, NULL)); } #endif int vroute_process(struct iked *env, int msglen, struct vroute_msg *m_rtmsg, struct sockaddr *dest, struct sockaddr *mask, struct sockaddr *addr, int *need_gw) { struct sockaddr *sa; char *cp; int i; #define rtm m_rtmsg->vm_rtm if (rtm.rtm_version != RTM_VERSION) { warnx("routing message version %u not understood", rtm.rtm_version); return (-1); } if (rtm.rtm_msglen > msglen) { warnx("message length mismatch, in packet %u, returned %d", rtm.rtm_msglen, msglen); return (-1); } if (rtm.rtm_errno) { warnx("RTM_GET: %s (errno %d)", strerror(rtm.rtm_errno), rtm.rtm_errno); return (-1); } cp = m_rtmsg->vm_space; *need_gw = rtm.rtm_flags & RTF_GATEWAY; if(rtm.rtm_addrs) { for (i = 1; i; i <<= 1) { if (i & rtm.rtm_addrs) { sa = (struct sockaddr *)cp; switch(i) { case RTA_DST: memcpy(dest, cp, sa->sa_len); break; case RTA_NETMASK: memcpy(mask, cp, sa->sa_len); break; case RTA_GATEWAY: memcpy(addr, cp, sa->sa_len); break; } cp += ROUNDUP(sa->sa_len); } } } #undef rtm return (0); } int vroute_doaddr(struct iked *env, char *ifname, struct sockaddr *addr, struct sockaddr *mask, int add) { struct iked_vroute_sc *ivr = env->sc_vroute; struct ifaliasreq req; struct in6_aliasreq req6; unsigned long ioreq; int af; af = addr->sa_family; switch (af) { case AF_INET: bzero(&req, sizeof(req)); strncpy(req.ifra_name, ifname, sizeof(req.ifra_name)); memcpy(&req.ifra_addr, addr, sizeof(req.ifra_addr)); if (add) memcpy(&req.ifra_mask, mask, sizeof(req.ifra_addr)); log_debug("%s: %s inet %s netmask %s", __func__, add ? "add" : "del", print_addr(addr), print_addr(mask)); ioreq = add ? SIOCAIFADDR : SIOCDIFADDR; if (ioctl(ivr->ivr_iosock, ioreq, &req) == -1) { log_warn("%s: req: %lu", __func__, ioreq); return (-1); } break; case AF_INET6: bzero(&req6, sizeof(req6)); strncpy(req6.ifra_name, ifname, sizeof(req6.ifra_name)); req6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME; req6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME; memcpy(&req6.ifra_addr, addr, sizeof(req6.ifra_addr)); if (add) memcpy(&req6.ifra_prefixmask, mask, sizeof(req6.ifra_prefixmask)); log_debug("%s: %s inet6 %s netmask %s", __func__, add ? "add" : "del", print_addr(addr), print_addr(mask)); ioreq = add ? SIOCAIFADDR_IN6 : SIOCDIFADDR_IN6; if (ioctl(ivr->ivr_iosock6, ioreq, &req6) == -1) { log_warn("%s: req: %lu", __func__, ioreq); return (-1); } break; default: return (-1); } return (0); } openiked-7.4/linux/000077500000000000000000000000001477554731100143375ustar00rootroot00000000000000openiked-7.4/linux/iked.apparmor000066400000000000000000000047111477554731100170210ustar00rootroot00000000000000# Last Modified: Thu Apr 14 17:48:19 2022 abi , include profile iked /usr/sbin/iked { include capability kill, # address/route configuration capability net_admin, network netlink dgram, # config file include /etc/iked.conf r, /etc/iked/** r, /run/iked.sock rw, # systemd-resolved unix bind type=stream addr=@*/bus/iked/system, dbus send bus=system path=/org/freedesktop/resolve1 interface=org.freedesktop.resolve1.Manager member=SetLinkDNS peer=(name=(org.freedesktop.resolve1)), dbus send bus=system path=/org/freedesktop/resolve1 interface=org.freedesktop.resolve1.Manager member=SetLinkDefaultRoute peer=(name=(org.freedesktop.resolve1)), dbus send bus=system path=/org/freedesktop/network1 interface=org.freedesktop.network1.Manager member=SetLinkDNS peer=(name=(org.freedesktop.network1)), dbus send bus=system path=/org/freedesktop/network1 interface=org.freedesktop.network1.Manager member=SetLinkDefaultRoute peer=(name=(org.freedesktop.network1)), # reexec /usr/sbin/iked ix, # priv dropping capability setuid, capability setgid, capability sys_chroot, # switch profile owner @{PROC}/@{tid}/mounts r, owner @{PROC}/@{tid}/attr/current w, change_profile -> iked//ca, change_profile -> iked//control, change_profile -> iked//ikev2, signal (send) peer=iked//ca, signal (send) peer=iked//control, signal (send) peer=iked//ikev2, signal (send) peer=iked//resolvectl, unix (send, receive) type=stream peer=(label=iked//control), unix (send, receive) type=stream peer=(label=iked//ikev2), unix (send, receive) type=stream peer=(label=iked//ca), owner /run/iked.sock w, network key raw, profile ca { include # privsep signal (receive) peer=iked, unix (send, receive) type=stream peer=(label=iked), # certs/keys /etc/iked/** r, } profile control { include # privsep signal (receive) peer=iked, unix (send, receive) type=stream peer=(label=iked), # ikectl control sock network unix raw, } profile ikev2 { include # privsep signal (receive) peer=iked, unix (send, receive) type=stream peer=(label=iked), # IKEv2 network inet dgram, network inet6 dgram, # PFKEY network key raw, } } openiked-7.4/linux/openiked.service000066400000000000000000000003341477554731100175170ustar00rootroot00000000000000[Unit] Description=OpenIKED IKEv2 daemon Documentation=man:iked(8) Requires=network-online.target [Service] Type=forking ExecStart=/usr/sbin/iked ExecReload=/usr/sbin/ikectl reload [Install] WantedBy=multi-user.target openiked-7.4/regress/000077500000000000000000000000001477554731100146525ustar00rootroot00000000000000openiked-7.4/regress/Makefile000066400000000000000000000001711477554731100163110ustar00rootroot00000000000000# $OpenBSD: Makefile,v 1.3 2020/01/16 11:41:14 bluhm Exp $ SUBDIR= test_helper dh parser live .include openiked-7.4/regress/Makefile.inc000066400000000000000000000007621477554731100170670ustar00rootroot00000000000000# $OpenBSD: Makefile.inc,v 1.1 2017/05/29 20:57:21 markus Exp $ CDIAGFLAGS= -Wall #CDIAGFLAGS+= -Werror CDIAGFLAGS+= -Wextra CDIAGFLAGS+= -Wpointer-arith CDIAGFLAGS+= -Wstrict-prototypes CDIAGFLAGS+= -Wmissing-prototypes CDIAGFLAGS+= -Wunused CDIAGFLAGS+= -Wsign-compare CDIAGFLAGS+= -Wshadow CDIAGFLAGS+= -Wpointer-sign CDIAGFLAGS+= -Wno-unused-parameter #-Wno-error=unused-parameter CDIAGFLAGS+= -Wuninitialized .if (${CC:L} == "gcc" || ${CC:L} == "cc") CDIAGFLAGS+= -Wbounded .endif DEBUG= -g openiked-7.4/regress/dh/000077500000000000000000000000001477554731100152455ustar00rootroot00000000000000openiked-7.4/regress/dh/CMakeLists.txt000066400000000000000000000024351477554731100200110ustar00rootroot00000000000000# Copyright (c) 2020-2021 Tobias Heider # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. set(SRCS) list(APPEND SRCS dhtest.c ${CMAKE_CURRENT_SOURCE_DIR}/../../iked/crypto_hash.c ${CMAKE_CURRENT_SOURCE_DIR}/../../iked/dh.c ${CMAKE_CURRENT_SOURCE_DIR}/../../iked/smult_curve25519_ref.c ${CMAKE_CURRENT_SOURCE_DIR}/../../iked/sntrup761.c ${CMAKE_CURRENT_SOURCE_DIR}/../../iked/imsg_util.c ) add_executable(dhtest ${SRCS}) target_include_directories(dhtest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../iked ) target_link_libraries(dhtest PRIVATE util crypto compat ) target_compile_options(dhtest PRIVATE ${CFLAGS}) openiked-7.4/regress/dh/Makefile000066400000000000000000000006661477554731100167150ustar00rootroot00000000000000# $OpenBSD: Makefile,v 1.4 2021/05/28 21:09:01 tobhe Exp $ # Test DH: PROG= dhtest SRCS= dh.c dhtest.c smult_curve25519_ref.c imsg_util.c SRCS+= sntrup761.c crypto_hash.c TOPSRC= ${.CURDIR}/../../../../sbin/iked TOPOBJ!= cd ${TOPSRC}; printf "all:\n\t@pwd\n" |${MAKE} -f- .PATH: ${TOPSRC} ${TOPOBJ} CFLAGS+= -I${TOPSRC} -I${TOPOBJ} -Wall NOMAN= LDADD+= -lcrypto -lutil DPADD+= ${LIBCRYPTO} DEBUG= -g .include openiked-7.4/regress/dh/dhtest.c000066400000000000000000000054071477554731100167120ustar00rootroot00000000000000/* $OpenBSD: dhtest.c,v 1.5 2021/05/28 21:09:01 tobhe Exp $ */ /* $EOM: dhtest.c,v 1.1 1998/07/18 21:14:20 provos Exp $ */ /* * Copyright (c) 2020 Tobias Heider * Copyright (c) 2010 Reyk Floeter * Copyright (c) 1998 Niels Provos. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 code was written under funding by Ericsson Radio Systems. */ /* * This module does a Diffie-Hellman Exchange */ #include #include #include #include #include #include #include #include #include #include "dh.h" #include "iked.h" int main(void) { int id; struct ibuf *buf, *buf2; struct ibuf *sec, *sec2; uint8_t *raw, *raw2; struct dh_group *group, *group2; const char *name[] = { "MODP", "ECP", "CURVE25519" }; group_init(); for (id = 0; id < 0xffff; id++) { if (((group = group_get(id)) == NULL || (group2 = group_get(id)) == NULL) || group->spec->type == GROUP_SNTRUP761X25519) continue; dh_create_exchange(group, &buf, NULL); dh_create_exchange(group2, &buf2, NULL); printf ("Testing group %d (%s-%d, length %zu): ", id, name[group->spec->type], group->spec->bits, ibuf_length(buf) * 8); dh_create_shared(group, &sec, buf2); dh_create_shared(group2, &sec2, buf); raw = ibuf_data(sec); raw2 = ibuf_data(sec2); if (memcmp (raw, raw2, ibuf_length(sec))) { printf("FAILED\n"); return (1); } else printf("OKAY\n"); group_free(group); group_free(group2); } return (0); } openiked-7.4/regress/live/000077500000000000000000000000001477554731100156115ustar00rootroot00000000000000openiked-7.4/regress/live/Makefile000066400000000000000000000465431477554731100172650ustar00rootroot00000000000000# $OpenBSD: Makefile,v 1.34 2021/12/21 13:50:35 tobhe Exp $ # Copyright (c) 2020 Tobias Heider # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. REGRESS_SETUP_ONCE = setup REGRESS_CLEANUP = cleanup CLEANFILES = *.conf *.cnf *.csr *.key *.crt *.srl LEFT_SSH ?= RIGHT_SSH ?= LEFT_ADDR ?= RIGHT_ADDR ?= .if empty(LEFT_SSH) || empty(RIGHT_SSH) || empty(LEFT_ADDR) || empty(RIGHT_ADDR) regress: @echo this test needs two remote machines to operate @echo LEFT_SSH RIGHT_SSH RIGHT_ADDR LEFT_ADDR are not defined @echo SKIPPED .endif TEST_FLOWS = \ [ -z $$tmode ] && tmode=tunnel; \ _ret=1; \ count=0; \ dynamic=${RIGHT_ADDR}; \ if [ -n "$$config_address" ]; then \ dynamic="172.16.13.[0-9]+"; \ fi; \ [ -z "$$maxwait" ] && maxwait=3; \ while [[ $$count -le $$maxwait ]]; do \ ipsecctlleft=`ssh ${LEFT_SSH} ipsecctl -sa`; \ ipsecctlright=`ssh ${RIGHT_SSH} ipsecctl -sa`; \ flowleft=`echo "$$ipsecctlleft" \ | sed -E -n "/^flow $$flowtype in from $$dynamic\ to ${LEFT_ADDR} peer ${RIGHT_ADDR} srcid (FQDN|UFQDN|ASN1_DN)\/[^ ]*\ dstid (FQDN|UFQDN|ASN1_DN)\/[^ ]*/p"`; \ flowright=`echo "$$ipsecctlright" \ | sed -E -n "/^flow $$flowtype in from ${LEFT_ADDR}\ to $$dynamic peer ${LEFT_ADDR} srcid (FQDN|UFQDN|ASN1_DN)\/[^ ]*\ dstid (FQDN|UFQDN|ASN1_DN)\/[^ ]*/p"`; \ saleft_rtol=`echo "$$ipsecctlleft" \ | sed -n "/^$$flowtype $$tmode from ${RIGHT_ADDR} to ${LEFT_ADDR}/p"`; \ saleft_ltor=`echo "$$ipsecctlleft" \ | sed -n "/^$$flowtype $$tmode from ${LEFT_ADDR} to ${RIGHT_ADDR}/p"`; \ saright_rtol=`echo "$$ipsecctlright" \ | sed -n "/^$$flowtype $$tmode from ${RIGHT_ADDR} to ${LEFT_ADDR}/p"`; \ saright_ltor=`echo "$$ipsecctlright" \ | sed -n "/^$$flowtype $$tmode from ${LEFT_ADDR} to ${RIGHT_ADDR}/p"`; \ if [[ -n "$$saleft_ltor" && -n "$$saleft_rtol" && \ -n "$$saright_ltor" && -n "$$saright_rtol" && \ -n "$$flowleft" && -n "$$flowright" ]]; then \ _ret=0; \ break; \ fi; \ let count=$$count+1; \ done; \ if [[ "$${_ret}" -ne 0 ]]; then \ echo "SAs not found:\n$$ipsecctlleft\n$$ipsecctlright"; \ fi TEST_PING = \ _ret=1; \ if [[ "${IPV}" == "6" ]]; then ping="ping6"; else ping="ping"; fi; \ dump=`ssh ${LEFT_SSH} "tcpdump -n -c2 -i enc0 -w /tmp/test.pcap > /dev/null & \ $$ping -w 1 -n -c 5 ${RIGHT_ADDR} > /dev/null && \ tcpdump -n -r /tmp/test.pcap && rm -f /tmp/test.pcap; \ kill -9 \\$$! > /dev/null 2>&1 || true"`; \ rtol=`echo "$$dump" \ | sed -n "/(authentic,confidential): SPI 0x[0-9a-f]\{8\}: ${LEFT_ADDR} > ${RIGHT_ADDR}/p"`; \ ltor=`echo "$$dump" \ | sed -n "/(authentic,confidential): SPI 0x[0-9a-f]\{8\}: ${RIGHT_ADDR} > ${LEFT_ADDR}/p"`; \ if [[ -z "$$rtol" || -z "$$ltor" ]]; then \ _ret=1; \ else \ _ret=0; \ fi; \ echo "$$dump" TEST_SINGLEIKESA = \ count=`ssh ${LEFT_SSH} "ikectl show sa | grep -c iked_sas"`; \ if [[ "$$count" != "1" ]]; then \ echo "error: too many IKE SAs."; \ exit 1; \ fi SETUP_CONFIG = \ from=$$local; \ to=$$peer; \ if [[ -z "$$mode" ]]; then mode="active"; fi; \ authstr=""; \ if [[ "$$auth" = "psk" ]]; then \ authstr="psk $$psk"; \ fi; \ ipcomp=""; \ if [[ "$$flowtype" = "ipcomp" ]]; then \ ipcomp="ipcomp"; \ fi; \ global=""; \ if [ "$$fragmentation" = true ]; then \ global="$${global}set fragmentation\n"; \ fi; \ if [ "$$singleikesa" = true ]; then \ global="$${global}set enforcesingleikesa\n"; \ fi; \ if [ "$$intermediate" = true ]; then \ global="$${global}set cert_partial_chain\n"; \ fi; \ confstr=""; \ if [ -n "$$config_address" ]; then \ if [ "$$side" = left ]; then \ mode=passive; \ confstr="config address $$config_address"; \ if [[ "$$config_address" == */* ]]; then \ to="dynamic"; \ else \ to="$$config_address"; \ fi; \ else \ mode=active; \ confstr="request address any"; \ if [[ "$$config_address" == */* ]]; then \ from="dynamic"; \ else \ from="$$config_address"; \ fi; \ fi; \ fi; \ echo "MODE=\"$$mode\"" >> $@_$$side.conf; \ echo "TMODE=\"$$tmode\"" >> $@_$$side.conf; \ echo "FROM=\"$$from\"" >> $@_$$side.conf; \ echo "TO=\"$$to\"" >> $@_$$side.conf; \ echo "LOCAL_ADDR=\"$$local\"" >> $@_$$side.conf; \ echo "PEER_ADDR=\"$$peer\"" >> $@_$$side.conf; \ echo "IPCOMP=\"$$ipcomp\"" >> $@_$$side.conf; \ echo "SRCID=\"\\\"$$srcid\\\"\"" >> $@_$$side.conf; \ echo "DSTID=\"$$dstid\"" >> $@_$$side.conf; \ echo "AUTH=\"$$authstr\"" >> $@_$$side.conf; \ echo "CONFIG=\"$$confstr\"" >> $@_$$side.conf; \ echo "IKESA=\"$$ikesa\"" >> $@_$$side.conf; \ echo "$$global" >> $@_$$side.conf; \ cat ${.CURDIR}/iked.in >> $@_$$side.conf DEPLOY_CONFIGS = \ chmod 0600 $@_left.conf; \ echo "cd /tmp\nput $@_left.conf test.conf" | sftp -q ${LEFT_SSH}; \ chmod 0600 $@_right.conf; \ echo "cd /tmp\nput $@_right.conf test.conf" | sftp -q ${RIGHT_SSH}; \ rm -f $@_left.conf $@_right.conf SETUP_CONFIGS = \ if [[ "$$auth" = "psk" ]]; then \ psk=`openssl rand -hex 20`; \ fi; \ side=left; \ srcid=$$leftid; \ local=${LEFT_ADDR}; \ peer=${RIGHT_ADDR}; \ ${SETUP_CONFIG}; \ side=right; \ srcid=$$rightid; \ local=${RIGHT_ADDR}; \ peer=${LEFT_ADDR}; \ ${SETUP_CONFIG}; \ ${DEPLOY_CONFIGS} SETUP_SYSCTL = \ ssh ${LEFT_SSH} "sysctl $$sysctl"; \ ssh ${RIGHT_SSH} "sysctl $$sysctl" SETUP_START = \ ssh ${LEFT_SSH} "ipsecctl -F; pkill iked; iked $$iked_flags -f /tmp/test.conf"; \ ssh ${RIGHT_SSH} "ipsecctl -F; pkill iked; iked $$iked_flags -f /tmp/test.conf" SETUP_RELOAD_RIGHT = \ ssh ${RIGHT_SSH} "ikectl reload" SETUP_CERT = \ echo "ALTNAME = $$name-from-$$caname" > $$name-from-$$caname.cnf; \ cat ${.CURDIR}/crt.in >> $$name-from-$$caname.cnf; \ openssl req -config $$name-from-$$caname.cnf -new -key $$name.key -nodes \ -out $$name-from-$$caname.csr; \ openssl x509 -extfile $$name-from-$$caname.cnf -extensions req_cert_extensions \ -req -in $$name-from-$$caname.csr -CA $$caname.crt -CAkey $$caname.key \ -CAcreateserial -out $$name-from-$$caname.crt SETUP_INTERMEDIATE = \ echo "ALTNAME = $$name-from-$$caname" > $$name-from-$$caname.cnf; \ cat ${.CURDIR}/crt.in >> $$name-from-$$caname.cnf; \ openssl genrsa -out $$name-from-$$caname.key 2048; \ openssl req -config $$name-from-$$caname.cnf -new -key $$name-from-$$caname.key -nodes \ -out $$name-from-$$caname.csr; \ openssl x509 -extfile $$name-from-$$caname.cnf -extensions v3_intermediate_ca \ -req -in $$name-from-$$caname.csr -CA $$caname.crt -CAkey $$caname.key \ -CAcreateserial -out $$name-from-$$caname.crt SETUP_CA = \ openssl genrsa -out $$caname.key 2048; \ openssl req -subj "/C=DE/ST=Bavaria/L=Munich/O=iked/CN=$$caname" \ -new -x509 -key $$caname.key -out $$caname.crt cleanup: -ssh ${LEFT_SSH} 'rm -f /tmp/test.conf; ipsecctl -F; pkill iked; \ rm -f /etc/iked/ca/*; rm -f /etc/iked/certs/*; rm -f /etc/iked/private/*; \ sysctl "net.inet.esp.udpencap_port=4500"; \ rm -f /tmp/pf.conf; pfctl -d; pfctl -f /etc/pf.conf;' -ssh ${RIGHT_SSH} 'rm -f /tmp/test.conf; ipsecctl -F; pkill iked; \ rm -f /etc/iked/ca/*; rm -f /etc/iked/certs/*; rm -f /etc/iked/private/*; \ sysctl "net.inet.esp.udpencap_port=4500"; \ rm -f /tmp/pf.conf; pfctl -d; pfctl -f /etc/pf.conf;' setup_certs: ca-both.crt left-from-ca-both.crt left.key right-from-ca-both.crt \ right.key ca-left.crt right-from-ca-left.crt ca-right.crt left-from-ca-right.crt \ ca-none.crt left-from-ca-none.crt right-from-ca-none.crt \ intermediate-from-ca-none.crt left-from-intermediate-from-ca-none.crt \ right-from-intermediate-from-ca-none.crt echo "cd /etc/iked\n \ put left-from-ca-both.crt certs\n \ put left-from-ca-right.crt certs\n \ put left-from-ca-none.crt certs\n \ put left-from-intermediate-from-ca-none.crt certs\n \ put right-from-ca-none.crt certs\n \ put left.key private/local.key\n \ put intermediate-from-ca-none.crt ca\n \ put ca-left.crt ca\n \ put ca-both.crt ca\n" | sftp ${LEFT_SSH} -q; \ echo "cd /etc/iked\n \ put right-from-ca-both.crt certs\n \ put right-from-ca-left.crt certs\n \ put right-from-ca-none.crt certs\n \ put right-from-intermediate-from-ca-none.crt certs\n \ put left-from-ca-none.crt certs\n \ put right.key private/local.key\n \ put intermediate-from-ca-none.crt ca\n \ put ca-right.crt ca\n \ put ca-both.crt ca\n" | sftp ${RIGHT_SSH} -q; \ ssh ${LEFT_SSH} "openssl rsa -in /etc/iked/private/local.key -pubout > /etc/iked/local.pub"; \ ssh ${RIGHT_SSH} "openssl rsa -in /etc/iked/private/local.key -pubout > /etc/iked/local.pub" setup_pf: pf.in echo "cd /tmp\nput ${.CURDIR}/pf.in pf.conf" | sftp -q ${LEFT_SSH} echo "cd /tmp\nput ${.CURDIR}/pf.in pf.conf" | sftp -q ${RIGHT_SSH} -ssh ${LEFT_SSH} "pfctl -f /tmp/pf.conf; pfctl -e" -ssh ${RIGHT_SSH} "pfctl -f /tmp/pf.conf; pfctl -e" setup: setup_pf setup_certs .PHONY: setup_certs test_flows: ${TEST_FLOWS} left.key right.key: openssl genrsa -out $@ 2048 ca-both.crt ca-both.key: caname=ca-both; ${SETUP_CA} left-from-ca-both.crt: ca-both.crt ca-both.key left.key caname=ca-both; name=left; ${SETUP_CERT} right-from-ca-both.crt: ca-both.crt ca-both.key right.key caname=ca-both; name=right; ${SETUP_CERT} ca-left.crt ca-left.key: caname=ca-left; ${SETUP_CA} right-from-ca-left.crt right.key: ca-left.crt ca-left.key caname=ca-left; name=right; ${SETUP_CERT} ca-right.crt ca-right.key: caname=ca-right; ${SETUP_CA} left-from-ca-right.crt left.key: ca-right.crt ca-right.key caname=ca-right; name=left; ${SETUP_CERT} ca-none.crt ca-none.key: caname=ca-none; ${SETUP_CA} left-from-ca-none.crt left.key: ca-none.crt ca-none.key caname=ca-none; name=left; ${SETUP_CERT} right-from-ca-none.crt right.key: ca-none.crt ca-none.key caname=ca-none; name=right; ${SETUP_CERT} intermediate-from-ca-none.crt intermediate-from-ca-none.key: caname=ca-none name=intermediate; ${SETUP_INTERMEDIATE} left-from-intermediate-from-ca-none.crt left.key: \ intermediate-from-ca-none.crt intermediate-from-ca-none.key caname=intermediate-from-ca-none; name=left; ${SETUP_CERT} right-from-intermediate-from-ca-none.crt right.key: \ intermediate-from-ca-none.crt intermediate-from-ca-none.key caname=intermediate-from-ca-none; name=right; ${SETUP_CERT} REGRESS_TARGETS = run-ping-fail run-ping-fail: ssh ${LEFT_SSH} "ipsecctl -F; pkill iked || true" ssh ${RIGHT_SSH} "ipsecctl -F; pkill iked || true" ${TEST_PING}; \ if [[ $$_ret -ne 1 ]]; then exit 1; fi REGRESS_TARGETS += run-cert-single-ca run-cert-single-ca: leftid=left-from-ca-both; \ rightid=right-from-ca-both; \ ${SETUP_CONFIGS} ${SETUP_START} flowtype=esp; ${TEST_FLOWS}; if [[ $$_ret -ne 0 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-cert-single-ca-asn1dn run-cert-single-ca-asn1dn: leftid="/C=DE/ST=Bavaria/L=Munich/O=iked/CN=left-from-ca-both"; \ rightid="/C=DE/ST=Bavaria/L=Munich/O=iked/CN=right-from-ca-both"; \ ${SETUP_CONFIGS} ${SETUP_START} flowtype=esp; ${TEST_FLOWS}; if [[ $$_ret -ne 0 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-cert-no-ca run-cert-no-ca: leftid=left-from-ca-none; \ rightid=right-from-ca-none; \ ${SETUP_CONFIGS} ${SETUP_START} flowtype=esp; ${TEST_FLOWS}; if [[ $$_ret -ne 0 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-config-address run-config-address: flowtype=esp; \ config_address=172.16.13.36; \ leftid=left-from-ca-both; \ rightid=right-from-ca-both; \ ${SETUP_CONFIGS} ${SETUP_START} config_address=172.16.13.36; \ flowtype=esp; ${TEST_FLOWS}; if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-config-address-pool run-config-address-pool: flowtype=esp; \ config_address=172.16.13.36/31; \ leftid=left-from-ca-both; \ rightid=right-from-ca-both; \ ${SETUP_CONFIGS} ${SETUP_START} config_address=172.16.13.36/31; \ flowtype=esp; ${TEST_FLOWS}; if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-dstid-fail run-dstid-fail: leftid=left-from-ca-both; \ rightid=right-from-ca-both; \ side=left; \ srcid=$$leftid; \ local=${LEFT_ADDR}; \ peer=${RIGHT_ADDR}; \ ${SETUP_CONFIG}; \ side=right; \ mode=passive; \ srcid=$$rightid; \ local=${RIGHT_ADDR}; \ peer=${LEFT_ADDR}; \ dstid="dstid invalid"; \ ${SETUP_CONFIG}; \ ${DEPLOY_CONFIGS} ${SETUP_START} flowtype=esp; ${TEST_FLOWS}; if [[ $$_ret -ne 1 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 1 ]]; then exit 1; fi REGRESS_TARGETS += run-dstid run-dstid: flowtype=esp; \ leftid=left-from-ca-both; \ rightid=right-from-ca-both; \ side=left; \ srcid=$$leftid; \ local=${LEFT_ADDR}; \ peer=${RIGHT_ADDR}; \ dstid="dstid $$rightid"; \ ${SETUP_CONFIG}; \ side=right; \ srcid=$$rightid; \ local=${RIGHT_ADDR}; \ peer=${LEFT_ADDR}; \ dstid="dstid $$leftid"; \ ${SETUP_CONFIG}; \ ${DEPLOY_CONFIGS} ${SETUP_START} flowtype=esp; ${TEST_FLOWS}; if [[ $$_ret -ne 0 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-dstid-multi run-dstid-multi: flowtype=esp; \ leftid=left-from-ca-both; \ rightid=right-from-ca-both; \ side=left; srcid=$$leftid; local=${LEFT_ADDR}; peer=${RIGHT_ADDR}; \ dstid="dstid $$rightid"; \ ${SETUP_CONFIG}; \ side=right; mode=passive; srcid=$$rightid; local=${RIGHT_ADDR}; \ peer=${LEFT_ADDR}; dstid="dstid $$leftid"; \ ${SETUP_CONFIG}; \ dstid="dstid roflol"; \ ${SETUP_CONFIG}; \ ${DEPLOY_CONFIGS} ${SETUP_START} flowtype=esp; ${TEST_FLOWS}; if [[ $$_ret -ne 0 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-srcid-multi run-srcid-multi: flowtype=esp; \ leftid=left-from-ca-both; \ rightid=right-from-ca-both; \ side=left; srcid=$$leftid; local=${LEFT_ADDR}; peer=${RIGHT_ADDR}; \ dstid="dstid $$rightid"; \ ${SETUP_CONFIG}; \ side=right; mode=passive; srcid="borked"; local=${RIGHT_ADDR}; \ peer=${LEFT_ADDR}; dstid=""; \ ${SETUP_CONFIG}; \ srcid=$$rightid; \ ${SETUP_CONFIG}; \ srcid="roflol"; \ ${SETUP_CONFIG}; \ ${DEPLOY_CONFIGS} ${SETUP_START} flowtype=esp; ${TEST_FLOWS}; if [[ $$_ret -ne 0 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-cert-multi-ca run-cert-multi-ca: flowtype=esp; \ leftid=left-from-ca-right; \ rightid=right-from-ca-left; \ ${SETUP_CONFIGS} ${SETUP_START} flowtype=esp; ${TEST_FLOWS}; if [[ $$_ret -ne 0 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-cert-second-altname run-cert-second-altname: flowtype=esp; \ leftid=left-from-ca-both-alternative; \ rightid=right-from-ca-both@openbsd.org; \ ${SETUP_CONFIGS} ${SETUP_START} flowtype=esp; ${TEST_FLOWS}; if [[ $$_ret -ne 0 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-invalid-ke run-invalid-ke: flowtype=esp; \ leftid=left-from-ca-both; \ rightid=right-from-ca-both; \ side=left; srcid=$$leftid; local=${LEFT_ADDR}; peer=${RIGHT_ADDR}; \ dstid="dstid $$rightid"; \ ikesa="ikesa group ecp256 group curve25519"; \ ${SETUP_CONFIG}; \ side=right; mode=passive; srcid=$$rightid; local=${RIGHT_ADDR}; \ peer=${LEFT_ADDR}; dstid="dstid $$leftid"; \ ikesa="ikesa group curve25519"; \ ${SETUP_CONFIG}; \ ${DEPLOY_CONFIGS} ${SETUP_START} flowtype=esp; maxwait=6; ${TEST_FLOWS}; if [[ $$_ret -ne 0 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-psk-fail run-psk-fail: auth=psk; \ leftid=left-from-ca-both; \ rightid=right-from-ca-both; \ flowtype=esp; \ side=left; \ srcid=$$leftid; \ local=${LEFT_ADDR}; \ peer=${RIGHT_ADDR}; \ dstid="dstid $$rightid"; \ psk=`openssl rand -hex 20`; \ ${SETUP_CONFIG}; \ side=right; \ srcid=$$rightid; \ local=${RIGHT_ADDR}; \ peer=${LEFT_ADDR}; \ dstid="dstid $$leftid"; \ psk=`openssl rand -hex 20`; \ ${SETUP_CONFIG}; \ ${DEPLOY_CONFIGS} ${SETUP_START} flowtype=esp; ${TEST_FLOWS}; if [[ $$_ret -ne 1 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 1 ]]; then exit 1; fi REGRESS_TARGETS += run-psk run-psk: auth=psk; \ leftid=left; \ rightid=right; \ flowtype=esp; \ ${SETUP_CONFIGS} ${SETUP_START} flowtype=esp; ${TEST_FLOWS}; \ if [[ $$_ret -ne 0 ]]; then exit 1; fi ${TEST_PING}; \ if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-intermediate-fail run-intermediate-fail: leftid=left-from-intermediate-from-ca-none; \ rightid=right-from-intermediate-from-ca-none; \ ${SETUP_CONFIGS} ${SETUP_START} flowtype=esp; ${TEST_FLOWS}; if [[ $$_ret -ne 1 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 1 ]]; then exit 1; fi REGRESS_TARGETS += run-intermediate run-intermediate: intermediate=true; \ leftid=left-from-intermediate-from-ca-none; \ rightid=right-from-intermediate-from-ca-none; \ ${SETUP_CONFIGS} ${SETUP_START} if [[ $$_ret -ne 0 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-fragmentation run-fragmentation: flowtype=esp; \ fragmentation=true; \ leftid=left-from-ca-both; \ rightid=right-from-ca-both; \ ${SETUP_CONFIGS} ${SETUP_START} flowtype=esp; ${TEST_FLOWS}; \ if [[ $$_ret -ne 0 ]]; then exit 1; fi ${TEST_PING}; \ if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-transport run-transport: flowtype=esp; \ tmode=transport; \ leftid=left-from-ca-both; \ rightid=right-from-ca-both; \ ${SETUP_CONFIGS} ${SETUP_START} tmode=transport; flowtype=esp; \ ${TEST_FLOWS}; if [[ $$_ret -ne 0 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-singleikesa run-singleikesa: flowtype=esp; \ singleikesa=true; \ leftid=left-from-ca-both; \ rightid=right-from-ca-both; \ ${SETUP_CONFIGS} ${SETUP_START} sleep 1; ${SETUP_RELOAD_RIGHT}; \ sleep 3; ${TEST_SINGLEIKESA} REGRESS_TARGETS += run-ipcomp run-ipcomp: flowtype=ipcomp; \ leftid=left-from-ca-both; \ rightid=right-from-ca-both; \ ${SETUP_CONFIGS} sysctl="net.inet.ipcomp.enable=1"; \ ${SETUP_SYSCTL} ${SETUP_START} flowtype=ipcomp; ${TEST_FLOWS}; if [[ $$_ret -ne 0 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 0 ]]; then exit 1; fi REGRESS_TARGETS += run-udpencap-port run-udpencap-port: flowtype=esp; \ leftid=left-from-ca-both; \ rightid=right-from-ca-both; \ ${SETUP_CONFIGS}; \ sysctl="net.inet.esp.udpencap_port=9999"; \ ${SETUP_SYSCTL}; iked_flags=-p9999; \ ${SETUP_START}; flowtype=esp; ${TEST_FLOWS}; if [[ $$_ret -ne 0 ]]; then exit 1; fi ${TEST_PING}; if [[ $$_ret -ne 0 ]]; then exit 1; fi sysctl="net.inet.esp.udpencap_port=4500"; \ ${SETUP_SYSCTL}; .include openiked-7.4/regress/live/crt.in000066400000000000000000000014171477554731100167340ustar00rootroot00000000000000# $OpenBSD: crt.in,v 1.3 2021/12/21 13:50:35 tobhe Exp $ [ req ] default_bits = 2048 # default strength of client certificates default_md = sha256 encrypt_key = yes # "no" is equivalent to -nodes prompt = no string_mask = utf8only distinguished_name = dn # root certificate name req_extensions = req_cert_extensions [dn] C=DE ST=Bavaria L=Munich O=iked CN=${ENV::ALTNAME} [ req_cert_extensions ] subjectAltName = @alt_names #;otherName = ${ENV::ALTNAME}-other [ v3_intermediate_ca ] basicConstraints = critical, CA:true, pathlen:0 [ alt_names ] DNS.1=${ENV::ALTNAME} DNS.2=${ENV::ALTNAME}-alternative email= ${ENV::ALTNAME}@openbsd.org openiked-7.4/regress/live/iked.in000066400000000000000000000002001477554731100170450ustar00rootroot00000000000000ikev2 "test" $MODE $IPCOMP $TMODE esp from $FROM to $TO \ peer $PEER_ADDR \ $IKESA \ srcid $SRCID $DSTID \ $AUTH \ $CONFIG openiked-7.4/regress/live/pf.in000066400000000000000000000003041477554731100165430ustar00rootroot00000000000000block inet proto icmp all icmp-type echoreq pass on enc0 inet proto icmp all icmp-type echoreq block inet6 proto icmp6 all icmp6-type echoreq pass on enc0 inet6 proto icmp6 all icmp6-type echoreq openiked-7.4/regress/live/test_live.pl000077500000000000000000000551641477554731100201620ustar00rootroot00000000000000#!/usr/bin/perl # Copyright (c) 2020 - 2022 Tobias Heider # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. use strict; use Getopt::Std; my %options=(); getopts("hlt:s", \%options); usage() if defined $options{h}; my $LEFT_SSH = $ENV{'LEFT_SSH'}; my $RIGHT_SSH = $ENV{'RIGHT_SSH'}; my $LEFT_ADDR = $ENV{'LEFT_ADDR'}; my $RIGHT_ADDR = $ENV{'RIGHT_ADDR'}; if (!defined $LEFT_SSH or !defined $RIGHT_SSH or !defined $LEFT_ADDR or !defined $RIGHT_ADDR) { usage(); } my %left = (); my %right = (); $left{'ssh'} = $LEFT_SSH; $right{'ssh'} = $RIGHT_SSH; $left{'addr'} = $LEFT_ADDR; $right{'addr'} = $RIGHT_ADDR; $right{'name'} = "right"; $left{'name'} = "left"; my @tests = ( "test_single_ca", "test_single_ca_asn1dn", "test_altname", "test_multi_ca", "test_no_ca", "test_pubkey", "test_psk", "test_invalid_ke", "test_ikesa_all", "test_childsa_all", "test_group_sntrup761x25519", "test_transport", "test_fragmentation", "test_singleikesa", "test_config_addr", "test_config_addrpool", "test_lifetime", "test_dstid_fail", ); if (defined $options{l}) { print("tests:\n"); for my $test (@tests) { print("\t$test\n"); } exit 0; } my $BUILDDIR = (defined $ENV{'BUILDDIR'}) ? $ENV{'BUILDDIR'} : "obj"; if (-e $BUILDDIR and !-d $BUILDDIR) { print("error: BUILDDIR is not a directory\n"); exit 1; } if (!-e $BUILDDIR) { mkdir $BUILDDIR; } init_osdep(\%left); init_osdep(\%right); if (defined $options{s}) { # Generate CAs and certs setup_ca("ca-none"); setup_ca("ca-right"); setup_ca("ca-left"); setup_ca("ca-both"); setup_key("left"); setup_key("right"); setup_cert("left", "ca-both"); setup_cert("right", "ca-both"); setup_cert("right", "ca-left"); setup_cert("left", "ca-right"); setup_cert("right", "ca-none"); setup_cert("left", "ca-none"); deploy_certs(); exit 0; } if (defined $options{t}) { if ( grep { $options{t} eq $_ } @tests ) { print("$options{t}: "); eval "$options{t}()"; cleanup(); exit 0; } print("error: no such test $options{t}\n"); exit 1; } # run all tests for my $test (@tests) { print("$test: "); eval "$test()"; cleanup(); } cleanup(); exit 0; sub usage { print <<~DOC; usage: LEFT_SSH= RIGHT_SSH= LEFT_ADDR= RIGHT_ADDR= test_live.pl [-hls] [-t test] -h Print usage -l List tests -s Setup CAs and certs -t "name" Run subtest "name" DOC exit 1; } sub test_single_ca { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'srcid' => "$left{'name'}-from-ca-both", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'srcid' => "$right{'name'}-from-ca-both", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $left{'addr'}"], ); test_basic(\%lconf, \%rconf); } sub test_single_ca_asn1dn { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'srcid' => "\\\"/C=DE/ST=Bavaria/L=Munich/O=iked/CN=$left{'name'}-from-ca-both\\\"", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'srcid' => "\\\"/C=DE/ST=Bavaria/L=Munich/O=iked/CN=$right{'name'}-from-ca-both\\\"", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $left{'addr'}"], ); test_basic(\%lconf, \%rconf); } sub test_altname { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'srcid' => "$left{'name'}-from-ca-both-alternative", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'srcid' => "$right{'name'}-from-ca-both\@openbsd.org", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $left{'addr'}"], ); test_basic(\%lconf, \%rconf); } sub test_multi_ca { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'srcid' => "$left{'name'}-from-ca-right", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'srcid' => "$right{'name'}-from-ca-left", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $left{'addr'}"], ); test_basic(\%lconf, \%rconf); } sub test_no_ca { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'srcid' => "$left{'name'}-from-ca-none", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'srcid' => "$right{'name'}-from-ca-none", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $left{'addr'}"], ); test_basic(\%lconf, \%rconf); } sub test_pubkey { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'srcid' => "$left{'name'}-pub", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'srcid' => "$right{'name'}-pub", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $left{'addr'}"], ); test_basic(\%lconf, \%rconf); } sub test_psk { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'srcid' => "$left{'name'}-psk", 'auth' => "psk mekmitasdigoat", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'srcid' => "$right{'name'}-psk", 'auth' => "psk mekmitasdigoat", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $left{'addr'}"], ); test_basic(\%lconf, \%rconf); } sub test_group_sntrup761x25519 { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'srcid' => "$left{'name'}-from-ca-both", 'mode' => "active", 'ikesa' => "ikesa group sntrup761x25519", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}.*group sntrup761x25519"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'srcid' => "$right{'name'}-from-ca-both", 'mode' => "active", 'ikesa' => "ikesa group sntrup761x25519", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $left{'addr'}.*group sntrup761x25519"], ); test_basic(\%lconf, \%rconf); } sub test_ikesa_all { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'srcid' => "$left{'name'}-from-ca-both", 'mode' => "active", 'ikesa' => "ikesa group curve25519 group brainpool512" . " group brainpool384 group brainpool256 group brainpool224" . " group ecp224 group ecp192 group ecp521 group ecp384" . " group ecp256 group modp8192 group modp6144 group modp4096" . " group modp3072 group modp2048 group modp1536 group modp1024" . " group modp768 group sntrup761x25519" . " enc aes-256-gcm-12 enc aes-128-gcm-12 enc aes-256-gcm" . " enc aes-128-gcm" . " ikesa group curve25519 group brainpool512" . " group brainpool384 group brainpool256 group brainpool224" . " group ecp224 group ecp192 group ecp521 group ecp384" . " group ecp256 group modp8192 group modp6144 group modp4096" . " group modp3072 group modp2048 group modp1536 group modp1024" . " group modp768 group sntrup761x25519" . " enc aes-256 enc aes-192 enc aes-128 enc 3des" . " auth hmac-sha2-512 auth hmac-sha2-384 auth hmac-sha2-256" . " auth hmac-sha1 auth hmac-md5", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'srcid' => "$right{'name'}-from-ca-both", 'mode' => "active", 'ikesa' => "ikesa group curve25519 group brainpool512" . " group brainpool384 group brainpool256 group brainpool224" . " group ecp224 group ecp192 group ecp521 group ecp384" . " group ecp256 group modp8192 group modp6144 group modp4096" . " group modp3072 group modp2048 group modp1536 group modp1024" . " group modp768 group sntrup761x25519" . " enc aes-256-gcm-12 enc aes-128-gcm-12 enc aes-256-gcm" . " enc aes-128-gcm" . " ikesa group curve25519 group brainpool512" . " group brainpool384 group brainpool256 group brainpool224" . " group ecp224 group ecp192 group ecp521 group ecp384" . " group ecp256 group modp8192 group modp6144 group modp4096" . " group modp3072 group modp2048 group modp1536 group modp1024" . " group modp768 group sntrup761x25519" . " enc aes-256 enc aes-192 enc aes-128 enc 3des" . " auth hmac-sha2-512 auth hmac-sha2-384 auth hmac-sha2-256" . " auth hmac-sha1 auth hmac-md5", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $left{'addr'}"], ); test_basic(\%lconf, \%rconf); } sub test_childsa_all { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'srcid' => "$left{'name'}-from-ca-both", 'mode' => "active", 'ikesa' => "childsa group curve25519 group brainpool512" . " group brainpool384 group brainpool256 group brainpool224" . " group ecp224 group ecp192 group ecp521 group ecp384" . " group ecp256 group modp8192 group modp6144 group modp4096" . " group modp3072 group modp2048 group modp1536 group modp1024" . " group modp768 group sntrup761x25519 group none" . " enc chacha20-poly1305 enc aes-256-gcm enc aes-192-gcm" . " enc aes-128-gcm noesn esn" . " childsa group curve25519 group brainpool512" . " group brainpool384 group brainpool256 group brainpool224" . " group ecp224 group ecp192 group ecp521 group ecp384" . " group ecp256 group modp8192 group modp6144 group modp4096" . " group modp3072 group modp2048 group modp1536 group modp1024" . " group modp768 group sntrup761x25519 group none" . " enc aes-256 enc aes-192 enc aes-128 enc 3des enc aes-256-ctr" . " enc aes-192-ctr enc aes-128-ctr enc cast enc blowfish" . " auth hmac-sha2-512 auth hmac-sha2-384 auth hmac-sha2-256 auth hmac-sha1" . " auth hmac-md5 " . " prf hmac-sha2-512 prf hmac-sha2-384 prf hmac-sha2-256 prf hmac-sha1" . " prf hmac-md5 " . " noesn esn", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'srcid' => "$right{'name'}-from-ca-both", 'mode' => "active", 'ikesa' => "childsa group curve25519 group brainpool512" . " group brainpool384 group brainpool256 group brainpool224" . " group ecp224 group ecp192 group ecp521 group ecp384" . " group ecp256 group modp8192 group modp6144 group modp4096" . " group modp3072 group modp2048 group modp1536 group modp1024" . " group modp768 group sntrup761x25519 group none" . " enc chacha20-poly1305 enc aes-256-gcm enc aes-192-gcm" . " enc aes-128-gcm noesn esn" . " childsa group curve25519 group brainpool512" . " group brainpool384 group brainpool256 group brainpool224" . " group ecp224 group ecp192 group ecp521 group ecp384" . " group ecp256 group modp8192 group modp6144 group modp4096" . " group modp3072 group modp2048 group modp1536 group modp1024" . " group modp768 group sntrup761x25519 group none" . " enc aes-256 enc aes-192 enc aes-128 enc 3des enc aes-256-ctr" . " enc aes-192-ctr enc aes-128-ctr enc cast enc blowfish" . " auth hmac-sha2-512 auth hmac-sha2-384 auth hmac-sha2-256 auth hmac-sha1" . " auth hmac-md5 " . " prf hmac-sha2-512 prf hmac-sha2-384 prf hmac-sha2-256 prf hmac-sha1" . " prf hmac-md5 " . " noesn esn", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $left{'addr'}"], ); test_basic(\%lconf, \%rconf); } sub test_invalid_ke { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'srcid' => "$left{'name'}-from-ca-both", 'mode' => "passive", 'ikesa' => "ikesa group curve25519", 'expect' => [ "want dh CURVE25519, KE has ECP_256", "failed to negotiate IKE SA", "spi=0x[0-9a-f]{16}: established peer $right{'addr'}" ], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'srcid' => "$right{'name'}-from-ca-both", 'mode' => "active", 'ikesa' => "ikesa group ecp256 group curve25519", 'expect' => [ "reinitiating with new DH group", "spi=0x[0-9a-f]{16}: established peer $left{'addr'}", ], ); test_basic(\%lconf, \%rconf); } sub test_config_addr { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'srcid' => "$left{'name'}-from-ca-both", 'mode' => "passive", 'config' => "config address 172.16.13.36", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'srcid' => "$right{'name'}-from-ca-both", 'mode' => "active", 'config' => "request address any", 'expect' => [ "spi=0x[0-9a-f]{16}: established peer $left{'addr'}", "obtained lease: 172.16.13.36" ], ); test_basic(\%lconf, \%rconf); } sub test_config_addrpool { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'srcid' => "$left{'name'}-from-ca-both", 'mode' => "passive", 'config' => "config address 172.16.13.36/24", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'srcid' => "$right{'name'}-from-ca-both", 'mode' => "active", 'config' => "request address any", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $left{'addr'}"], ); test_basic(\%lconf, \%rconf); } sub test_transport { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'tmode' => "transport", 'srcid' => "$left{'name'}-from-ca-both", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'tmode' => "transport", 'srcid' => "$right{'name'}-from-ca-both", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $left{'addr'}"], ); test_basic(\%lconf, \%rconf); } sub test_fragmentation { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'fragmentation' => 1, 'srcid' => "$left{'name'}-from-ca-both", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'fragmentation' => 1, 'srcid' => "$right{'name'}-from-ca-both", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $left{'addr'}"], ); test_basic(\%lconf, \%rconf); } sub test_singleikesa { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'singleikesa' => 1, 'srcid' => "$left{'name'}-from-ca-both", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'singleikesa' => 1, 'srcid' => "$right{'name'}-from-ca-both", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $left{'addr'}"], ); test_basic(\%lconf, \%rconf); } sub test_lifetime { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'srcid' => "$left{'name'}-from-ca-both", 'mode' => "active", 'config' => "ikelifetime 30 lifetime 20 bytes 500K", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $right{'addr'}"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'srcid' => "$right{'name'}-from-ca-both", 'mode' => "active", 'config' => "ikelifetime 30 lifetime 20 bytes 500K", 'expect' => ["spi=0x[0-9a-f]{16}: established peer $left{'addr'}"], ); # XXX: wait and check rekey log message test_basic(\%lconf, \%rconf); } sub test_dstid_fail { my %lconf = ( 'from' => $left{'addr'}, 'to' => $right{'addr'}, 'srcid' => "$left{'name'}-from-ca-both", 'mode' => "active", 'expect' => ["spi=0x[0-9a-f]{16}: sa_free: authentication failed notification from peer"], ); my %rconf = ( 'from' => $right{'addr'}, 'to' => $left{'addr'}, 'srcid' => "$right{'name'}-from-ca-both", 'dstid' => "dstid INVALID_ID", 'mode' => "passive", 'expect' => ["spi=0x[0-9a-f]{16}: ikev2_ike_auth_recv: no compatible policy found"], ); test_basic(\%lconf, \%rconf); } sub test_basic { my ($lconf, $rconf) = @_; my $sub_name = (caller(0))[3]; setup_config("$sub_name-left", $lconf); system("chmod 0600 $BUILDDIR/$sub_name-left.conf"); system <<~DOC; echo "rename $left{'etc_dir'}/iked.conf $left{'etc_dir'}/iked.conf.old put $BUILDDIR/$sub_name-left.conf $left{'etc_dir'}/iked.conf" | sftp -q $left{'ssh'} -q > /dev/null; DOC setup_config("$sub_name-right", $rconf); system("chmod 0600 $BUILDDIR/$sub_name-right.conf"); system <<~DOC; echo "rename $right{'etc_dir'}/iked.conf $right{'etc_dir'}/iked.conf.old put $BUILDDIR/$sub_name-right.conf $right{'etc_dir'}/iked.conf" | sftp -q $right{'ssh'} -q > /dev/null; DOC setup_start(\%left); setup_start(\%right); for (1..5) { sleep(1); if (check_log(\%left, $lconf->{'expect'}) && check_log(\%right, $rconf->{'expect'})) { print("SUCCESS\n"); return; } } print("FAIL\n"); } sub cleanup { setup_stop(\%left); setup_stop(\%right); system("ssh -q $left{'ssh'} \"mv $left{'etc_dir'}/iked.conf.old $left{'etc_dir'}/iked.conf; cat /tmp/test.log >> /tmp/iked-live.log; rm /tmp/test.log\" 2>/dev/null"); system("ssh -q $right{'ssh'} \"mv $right{'etc_dir'}/iked.conf.old $right{'etc_dir'}/iked.conf; cat /tmp/test.log >> /tmp/iked-live.log; rm /tmp/test.log\" 2>/dev/null"); } sub setup_start { my ($peer) = @_; system("ssh -q $peer->{'ssh'} \"$peer->{'cmd_flush'}; pkill iked; iked -dv 2> /tmp/test.log\&\"&") } sub setup_stop { my ($peer) = @_; system("ssh -q $peer->{'ssh'} \"$peer->{'cmd_flush'}; pkill iked\""); } sub setup_ca { my ($ca) = @_; setup_key($ca); system("openssl req -subj \"/C=DE/ST=Bavaria/L=Munich/O=iked/CN=$ca\" ". "-new -x509 -key $BUILDDIR/$ca.key -out $BUILDDIR/$ca.crt"); } sub setup_key { my ($name) = @_; system("openssl genrsa -out $BUILDDIR/$name.key 2048"); system("openssl rsa -in $BUILDDIR/$name.key -pubout > $BUILDDIR/$name.pub"); } sub setup_cert { my ($name, $ca) = @_; open(my $dest, '>', "$BUILDDIR/$name-from-$ca.cnf") || die $!; open(my $src, '<', "crt.in") || die $!; print $dest "ALTNAME = $name-from-$ca\n"; while(<$src>){ print $dest $_; } close($dest); close($src); system("openssl req -config $BUILDDIR/$name-from-$ca.cnf -new ". "-key $BUILDDIR/$name.key -nodes " . "-out $BUILDDIR/$name-from-$ca.csr"); system("openssl x509 -extfile $BUILDDIR/$name-from-$ca.cnf ". "-extensions req_cert_extensions -req ". "-in $BUILDDIR/$name-from-$ca.csr ". "-CA $BUILDDIR/$ca.crt -CAkey $BUILDDIR/$ca.key ". "-CAcreateserial -out $BUILDDIR/$name-from-$ca.crt"); } sub setup_config { my ($name, $conf) = @_; open(my $dest, '>', "$BUILDDIR/$name.conf") || die $!; open(my $src, '<', "iked.in") || die $!; my $globals = ""; if (defined $conf->{'fragmentation'}) { $globals = $globals . "set fragmentation\n"; } if (defined $conf->{'singleikesa'}) { $globals = $globals . "set enforcesingleikesa\n"; } print $dest "FROM=\"$conf->{'from'}\"\n"; print $dest "TO=\"$conf->{'to'}\"\n"; print $dest "PEER_ADDR=\"$conf->{'to'}\"\n"; print $dest "IKESA=\"$conf->{'ikesa'}\"\n"; print $dest "MODE=\"$conf->{'mode'}\"\n"; print $dest "TMODE=\"$conf->{'tmode'}\"\n"; print $dest "AUTH=\"$conf->{'auth'}\"\n"; print $dest "IPCOMP=\"$conf->{'ipcomp'}\"\n"; print $dest "SRCID=\"$conf->{'srcid'}\"\n"; print $dest "DSTID=\"$conf->{'dstid'}\"\n"; print $dest "CONFIG=\"$conf->{'config'}\"\n"; print $dest "$globals"; while(<$src>){ print $dest $_; } } # XXX: needs less globals sub deploy_certs { system <<~DOC; echo "cd $left{'etc_dir'}/iked\n mkdir certs\n put $BUILDDIR/left-from-ca-both.crt certs/\n put $BUILDDIR/left-from-ca-right.crt certs/\n put $BUILDDIR/left-from-ca-none.crt certs/\n put $BUILDDIR/right-from-ca-none.crt certs/\n mkdir private\n put $BUILDDIR/left.key private/local.key\n put $BUILDDIR/left.pub local.pub\n mkdir pubkeys\n mkdir pubkeys/fqdn\n put $BUILDDIR/right.pub pubkeys/fqdn/right-pub\n mkdir ca\n put $BUILDDIR/ca-left.crt ca/\n put $BUILDDIR/ca-both.crt ca/\n" | sftp -q $left{'ssh'} -q > /dev/null; DOC system <<~"DOC"; echo "cd $right{'etc_dir'}/iked\n mkdir certs\n put $BUILDDIR/right-from-ca-both.crt certs/\n put $BUILDDIR/right-from-ca-left.crt certs/\n put $BUILDDIR/right-from-ca-none.crt certs/\n put $BUILDDIR/left-from-ca-none.crt certs/\n mkdir private\n put $BUILDDIR/right.key private/local.key\n put $BUILDDIR/right.pub local.pub\n mkdir pubkeys\n mkdir pubkeys/fqdn\n put $BUILDDIR/left.pub pubkeys/fqdn/left-pub\n mkdir ca\n put $BUILDDIR/ca-right.crt ca/\n put $BUILDDIR/ca-both.crt ca/\n" | sftp -q $right{'ssh'} -q > /dev/null; DOC } sub init_osdep { my ($peer) = @_; $peer->{'os'} = `ssh -q $peer->{'ssh'} uname`; $peer->{'etc_dir'} = "/etc"; if (($peer->{'os'} cmp "OpenBSD\n") == 0) { $peer->{'cmd_flush'} = "ipsecctl -F"; } elsif (($peer->{'os'} cmp "Linux\n") == 0) { $peer->{'cmd_flush'} = "ip x p f; ip x s f"; } elsif (($peer->{'os'} cmp "FreeBSD\n") == 0) { $peer->{'cmd_flush'} = "setkey -PF; setkey -F"; $peer->{'etc_dir'} = "/usr/local/etc"; } else { print("error: unsupported OS $peer->{'os'}\n"); exit 1; } } sub check_log { my ($peer, $res) = @_; my $log = `ssh -q $peer->{'ssh'} \"cat /tmp/test.log\"`; foreach my $regex (@$res) { return 0 if !($log =~ /$regex/); } return 1; } sub check_ping { my ($left, $right) = @_; my $out = `ssh -q $left->{'ssh'} \"tcpdump -n -c2 -i enc0 \\ -w /tmp/test.pcap > /dev/null& \\ ping -w 1 -n -c 5 $right->{'addr'} > /dev/null; \\ tcpdump -n -r /tmp/test.pcap; rm -f /tmp/test.pcap; \\ kill -9 \\\$! > /dev/null 2>&1 || true\"`; my $ltor = ($out =~ /\(authentic,confidential\):\sSPI\s0x[0-9a-f]{8}:\s $right->{'addr'}\s>\s$left->{'addr'}/x); my $rtol = ($out =~ /\(authentic,confidential\):\sSPI\s0x[0-9a-f]{8}:\s $left->{'addr'}\s>\s$right->{'addr'}/x); if ($ltor && $rtol) { print("Ping werks\n"); return 0; } print("Ping failed\n"); return 1; } openiked-7.4/regress/parser-libfuzzer/000077500000000000000000000000001477554731100201605ustar00rootroot00000000000000openiked-7.4/regress/parser-libfuzzer/CMakeLists.txt000066400000000000000000000032701477554731100227220ustar00rootroot00000000000000# Copyright (c) 2022-2023 David Linder # Copyright (c) 2020-2021 Tobias Heider # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. set(SRCS) list(APPEND SRCS common.c test_parser_fuzz.c ) set(CMAKE_C_COMPILER clang) add_executable(test_libfuzzer ${SRCS}) target_include_directories(test_libfuzzer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../iked ) # when using clusterfuzz we need static linking and clusterfuzz' compiler arguments if (DEFINED ENV{CLUSTERFUZZLITE}) target_link_libraries(test_libfuzzer "-lm -Wl,-Bstatic -lssl -lcrypto -levent" compat iked-shared $ENV{LIB_FUZZING_ENGINE} ) else() string(APPEND CMAKE_C_FLAGS " -fsanitize=fuzzer") string(APPEND CMAKE_EXE_LINKER_FLAGS " -fsanitize=fuzzer") target_link_libraries(test_libfuzzer PRIVATE event crypto ssl compat iked-shared ) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/corpus) configure_file(run_test.sh run_test.sh COPYONLY) configure_file(test_libfuzzer.dict fuzz.dict COPYONLY) endif() target_compile_options(test_libfuzzer PRIVATE ${CFLAGS}) openiked-7.4/regress/parser-libfuzzer/common.c000066400000000000000000000160111477554731100216130ustar00rootroot00000000000000/* $OpenBSD: common.c,v 1.9 2020/11/26 22:29:32 tobhe Exp $ */ /* * A bunch of stub functions so we can compile and link ikev2_pld.c * in a standalone program for testing purposes. * * Placed in the public domain */ #include #include #include #include #include #include #include "iked.h" #include "types.h" #define IKEV2_FLAG_INITIATOR 0x08 /* Sent by the initiator */ int eap_parse(struct iked *, const struct iked_sa *, struct iked_message *, void *, int); int ikev2_msg_frompeer(struct iked_message *); int ikev2_send_ike_e(struct iked *, struct iked_sa *, struct ibuf *, u_int8_t, u_int8_t, int); void ikev2_ikesa_recv_delete(struct iked *, struct iked_sa *); struct iked_childsa * childsa_lookup(struct iked_sa *, u_int64_t, u_int8_t); int ikev2_childsa_delete(struct iked *, struct iked_sa *, u_int8_t, u_int64_t, u_int64_t *, int); int sa_stateok(const struct iked_sa *, int); void sa_state(struct iked *, struct iked_sa *, int); void ikev2_disable_rekeying(struct iked *, struct iked_sa *); void ikev2_init_ike_sa(struct iked *, void *); struct dh_group * group_get(u_int32_t); void timer_set(struct iked *, struct iked_timer *, void (*)(struct iked *, void *), void *); void timer_add(struct iked *, struct iked_timer *, int); void timer_del(struct iked *, struct iked_timer *); ssize_t ikev2_nat_detection(struct iked *, struct iked_message *, void *, size_t, u_int, int); int ca_setreq(struct iked *, struct iked_sa *, struct iked_static_id *, u_int8_t, u_int8_t, u_int8_t *, size_t, enum privsep_procid); int ikev2_print_id(struct iked_id *, char *, size_t); int config_add_transform(struct iked_proposal *, u_int, u_int, u_int, u_int); struct iked_proposal * config_add_proposal(struct iked_proposals *, u_int, u_int); void config_free_proposal(struct iked_proposals *, struct iked_proposal *); int ikev2_send_informational(struct iked *, struct iked_message *); struct ibuf * ikev2_msg_decrypt(struct iked *, struct iked_sa *, struct ibuf *, struct ibuf *); void ikev2_msg_cleanup(struct iked *, struct iked_message *); int eap_parse(struct iked *env, const struct iked_sa *sa, struct iked_message *msg, void *data, int response) { return (0); } /* Copied from ikev2_msg.c for better coverage */ int ikev2_msg_frompeer(struct iked_message *msg) { struct iked_sa *sa = msg->msg_sa; struct ike_header *hdr; msg = msg->msg_parent; if (sa == NULL || (hdr = ibuf_seek(msg->msg_data, 0, sizeof(*hdr))) == NULL) return (0); if (!sa->sa_hdr.sh_initiator && (hdr->ike_flags & IKEV2_FLAG_INITIATOR)) return (1); else if (sa->sa_hdr.sh_initiator && (hdr->ike_flags & IKEV2_FLAG_INITIATOR) == 0) return (1); return (0); } int ikev2_send_ike_e(struct iked *env, struct iked_sa *sa, struct ibuf *buf, u_int8_t firstpayload, u_int8_t exchange, int response) { return (0); } void ikev2_ikesa_recv_delete(struct iked *env, struct iked_sa *sa) { } const char * ikev2_ikesa_info(uint64_t spi, const char *msg) { return ""; } struct iked_childsa * childsa_lookup(struct iked_sa *a, u_int64_t b, u_int8_t c) { return (NULL); } int ikev2_childsa_delete(struct iked *a, struct iked_sa *b, u_int8_t c, u_int64_t d, u_int64_t *e , int f) { return (0); } int sa_stateok(const struct iked_sa *a, int b) { return (0); } void sa_state(struct iked * a, struct iked_sa *b, int c) { } void ikev2_disable_rekeying(struct iked *a, struct iked_sa *b) { } void ikev2_init_ike_sa(struct iked *a, void *b) { } const struct group_id * group_getid(u_int32_t id) { return (NULL); } void timer_set(struct iked *env, struct iked_timer *tmr, void (*cb)(struct iked *, void *), void *arg) { } void timer_add(struct iked *env, struct iked_timer *tmr, int timeout) { } void timer_del(struct iked *env, struct iked_timer *tmr) { } ssize_t ikev2_nat_detection(struct iked *env, struct iked_message *msg, void *ptr, size_t len, u_int type, int frompeer) { bzero(ptr, len); return (0); } int ca_setreq(struct iked *env, struct iked_sa *sh, struct iked_static_id *localid, u_int8_t type, u_int8_t more, u_int8_t *data, size_t len, enum privsep_procid procid) { return (0); } int ikev2_print_id(struct iked_id *id, char *idstr, size_t idstrlen) { return (0); } int config_add_transform(struct iked_proposal *prop, u_int type, u_int id, u_int length, u_int keylength) { return (0); } struct iked_proposal * config_add_proposal(struct iked_proposals *head, u_int id, u_int proto) { return (NULL); } void config_free_proposal(struct iked_proposals *head, struct iked_proposal *prop) { return; } void config_free_fragments(struct iked_frag *frag) { return; } int ikev2_send_informational(struct iked *env, struct iked_message *msg) { return (0); } struct ibuf * ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa, struct ibuf *msg, struct ibuf *src) { if (src == NULL){ fprintf(stderr, "%s\n", "msg_decrypt: src == NULL!"); exit(-1); } /* * Free src as caller uses ikev2_msg_decrypt() like this: * src = ikev2_msg_decrypt(..., src); */ ibuf_free(src); return (NULL); } void ikev2_ike_sa_setreason(struct iked_sa *sa, char *r) { } void ikev2_msg_dispose(struct iked *env, struct iked_msgqueue *queue, struct iked_msg_retransmit *mr) { } struct iked_msg_retransmit * ikev2_msg_lookup(struct iked *env, struct iked_msgqueue *queue, struct iked_message *msg, uint8_t exchange) { return NULL; } /* copied from ikev2_msg.c */ void ikev2_msg_cleanup(struct iked *env, struct iked_message *msg) { struct iked_certreq *cr; struct iked_proposal *prop, *proptmp; int i; if (msg == msg->msg_parent) { ibuf_free(msg->msg_nonce); ibuf_free(msg->msg_ke); ibuf_free(msg->msg_auth.id_buf); ibuf_free(msg->msg_peerid.id_buf); ibuf_free(msg->msg_localid.id_buf); ibuf_free(msg->msg_cert.id_buf); for (i = 0; i < IKED_SCERT_MAX; i++) ibuf_free(msg->msg_scert[i].id_buf); ibuf_free(msg->msg_cookie); ibuf_free(msg->msg_cookie2); ibuf_free(msg->msg_del_buf); free(msg->msg_eap.eam_user); free(msg->msg_cp_addr); free(msg->msg_cp_addr6); free(msg->msg_cp_dns); TAILQ_FOREACH_SAFE(prop, &msg->msg_proposals, prop_entry, proptmp) { TAILQ_REMOVE(&msg->msg_proposals, prop, prop_entry); if (prop->prop_nxforms) free(prop->prop_xforms); free(prop); } msg->msg_nonce = NULL; msg->msg_ke = NULL; msg->msg_auth.id_buf = NULL; msg->msg_peerid.id_buf = NULL; msg->msg_localid.id_buf = NULL; msg->msg_cert.id_buf = NULL; for (i = 0; i < IKED_SCERT_MAX; i++) msg->msg_scert[i].id_buf = NULL; msg->msg_cookie = NULL; msg->msg_cookie2 = NULL; msg->msg_del_buf = NULL; msg->msg_eap.eam_user = NULL; msg->msg_cp_addr = NULL; msg->msg_cp_addr6 = NULL; msg->msg_cp_dns = NULL; while ((cr = SIMPLEQ_FIRST(&msg->msg_certreqs))) { ibuf_free(cr->cr_data); SIMPLEQ_REMOVE_HEAD(&msg->msg_certreqs, cr_entry); free(cr); } } if (msg->msg_data != NULL) { ibuf_free(msg->msg_data); msg->msg_data = NULL; } } openiked-7.4/regress/parser-libfuzzer/run_test.sh000066400000000000000000000006241477554731100223610ustar00rootroot00000000000000#!/bin/sh # script to run the parser-fuzzer for 5 minutes with the right options # use repo github.com/openiked/openiked-fuzzing/corpus/test_libfuzzer as corpus for faster results # ASAN-option to help finding the source of memory leaks export ASAN_OPTIONS=fast_unwind_on_malloc=0 $(dirname "$0")/test_libfuzzer -dict=$(dirname "$0")/fuzz.dict -max_len=8164 -max_total_time=300 $(dirname "$0")/corpus openiked-7.4/regress/parser-libfuzzer/test_libfuzzer.dict000066400000000000000000000241401477554731100241010ustar00rootroot00000000000000# dict for pars-fuzzer # notify_pld key1="\x00\x00\x00\x1c\x00\x00\x40\x04\xc7\xa0\x68\x68\x09\x0a\x7f\x12\x0b\x13\xd3\x2f\xde\x64\x8b\xf1\xc3\x3c\x79\x8f\x00\x00\x00\x1c\x00\x00\x40\x05\x9f\xbc\x8c\xd0\x91\x5e\xa0\x87\x81\xab\x4f\xa1\x8a\xa7\xa8\xf9\xeb\xdf\x9f\x2c" # ke_pld key2="\x00\x00\x01\x08\x00\x0e\x00\x00\x16\xcb\x68\xaf\x63\xfe\xb0\x58\x49\x0e\x7f\x85\x60\x53\x80\xae\x3f\x82\xf3\x35\x21\xd5\xae\x09\x1c\xfa\x68\xc2\xfb\x4b\xb3\x84\xda\xaf\x6e\xe2\x5e\xc5\xb6\x8c\x35\x3c\xec\x58\x7f\xa9\xf8\xa4\x24\xf3\xf8\xf4\x65\x59\x8c\x15\x4d\x2c\xf1\x5d\xeb\x57\x68\xfe\x75\x61\x5a\x80\x96\xa4\x0a\xad\x75\x71\xd8\xe0\x06\xbc\xde\x16\x6d\x1e\xd9\x5d\x2c\x00\x66\x43\x82\xe4\x6f\x5f\x95\xe7\x9b\xfd\xf2\xe2\xcb\xc5\xf1\x52\xdd\x3b\xed\x88\xd4\xa9\x13\x4e\x42\xe8\x60\x2d\x3c\xf6\xc8\xf0\x70\x42\xfa\x33\x7f\x28\xdf\x6b\x79\x2c\x79\x8f\xc0\x5d\x81\x7a\x62\xdb\xd4\x44\x3a\x3c\x21\xbf\x85\xc8\x0b\x8c\x77\x72\xe9\xfb\x50\x5c\x03\xa6\xb2\x3f\x17\x4a\xd1\xb3\x01\x30\xad\xe4\xfa\xe2\xba\x6f\x22\x83\xf4\xde\x38\x43\xe8\x27\x00\xb8\x95\xbe\x03\x8f\xcd\xd3\x72\xed\xa5\xed\x8d\xf4\x68\x98\xef\x59\xcc\xfb\x54\x89\xde\xa9\xd4\x88\xcd\xb9\xca\x09\xd3\xd5\x25\xb1\x8c\x58\x12\x9c\x69\x03\x72\x00\xc9\xca\x95\x8a\xce\x0d\xd2\xc8\x25\xe7\x7c\xed\x5e\xee\x35\x01\xfc\x00\x56\xed\xf3\x8d\x81\x6c\x3e\x86\x6a\x40\xac\xc7\x9c\x7a\xbf\x9f\x8e\x1f\xd8\x60" # generic header key3="\x00\x20\x22\x08\x00\x00\x00\x00\x00\x00\x00\x00" # nonce pld key4="\x00\x00\x00\x0c\x01\x00\x00\x00\xac\x12\x7d\x01" # certreq pld key5="\x00\x00\x00\x05\x0b" # cookies key6="\xde\xad\xbe\xef\xca\xfe\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00" # sa_pld key7="\x00\x00\x00\x0c\x00\x00\x00\x08\x01\x01\x00\x00" # saxform_pld key8="\x00\x00\x00\x40\x00\x00\x00\x3c\x01\x01\x06\x03\x00\x08\x03\x00\x00\x0c\x03\x00\x00\x0c\x01\x00\x00\x0c\x80\x0e\x00\xc0\x03\x00\x00\x08\x04\x00\x00\x0e\x03\x00\x00\x02\x00\x00\x05\x03\x00\x00\x08\x02\x00\x00\x02\x00\x00\x00\x08\x02\x00\x00\x01" # id_pld key9="\x00\x00\x00\x0c\x01\x00\x00\x00\x0ac\x12\x7d\x01" # vendor_pld key10="\x00\x00\x00\x08\x11\x22\x33\x44" # delete_pld key11="\x2a\x00\x00\x10\x01\x08\x00\x01\xde\xad\xbe\xef\xca\xfe\xaf\xfe\x00\x00\x00\x10\x03\x04\x00\x02\xaa\xbb\xcc\xdd\xee\xff\x00\x11" # ts_pld key12="\x00\x00\x00\x18\x01\x00\x00\x00\x07\x00\x00\x10\x00\x00\xff\xff\xac\x28\x7d\x00\xac\x28\x7d\xff" # cp_pld key13="\x2f\x00\x00\x0c\x01\x00\x00\x00\x00\x01\x00\x00\x2f\x00\x00\x10\x02\x00\x00\x00\x00\x01\x00\x04\xaa\xbb\xcc\xdd\x2f\x00\x00\x08\x03\x00\x00\x00\x2f\x00\x00\x24\x02\x00\x00\x00\x00\x01\x00\x04\xaa\xaa\xaa\xaa\x00\x02\x00\x04\xbb\xbb\xbb\xbb\x00\x03\x00\x04\xcc\xcc\xcc\xcc\x00\x08\x00\x00\x00\x00\x00\x08\x04\x00\x00\x00" # eap_pld key14="\x30\x00\x00\x09\x01\x00\x00\x05\x01\x30\x00\x00\x0c\x02\x00\x00\x05\x01\xfa\xfb\xfc\x30\x00\x00\x08\x03\x00\x00\x04\x00\x00\x00\x08\x04\x00\x00\x04" # cert_pld key15="\x00\x00\x01\x10\x0b\x00\x00\x00\x30\x82\x01\x0c\x02\x82\x01\x01\x00\x8a\x26\xf8\x9e\xe8\x09\x11\x6b\x3d\x00\xd3\x25\xf8\x9f\xe8\x09\x11\x6b\x3d\x10\xd3\x0b\x9a\xb0\xb7\xe4\x3e\x40\x59\xd7\x51\x03\xaf\x09\x79\x1b\x0d\x63\x66\x28\xaa\x97\xc8\x20\x4b\x28\x9b\x5e\x8c\xa9\x8f\x73\x81\xb4\xfa\xdd\x05\x69\x0b\x71\x72\xd8\xbb\xac\x4b\x6d\x67\x5a\xa2\x63\x5d\x6d\x27\xc5\xf4\xe6\x0a\xbd\x2b\x0a\x64\xb2\xcf\x59\x63\x9b\x5c\x4f\x26\x36\xe3\x10\x70\x3c\x39\x77\x55\x07\x1c\x12\xde\x60\x53\xa1\x70\xf4\xda\xfc\xcc\xec\xad\x6d\x34\xad\xe2\x36\x10\x93\x59\x0c\x81\x8d\x22\x7e\x57\xeb\x89\x26\xdb\x6e\x99\x9a\xde\xbe\xad\xef\xca\xaf\xfe\xfe\x99\x9a\xde\xbe\xad\xef\xca\xaf\xfe\xfe\x6f\xd4\xe4\x63\x6c\x3e\x83\x09\xf4\x32\x78\x3b\x71\xe9\x36\xb6\x92\xf6\xa8\x31\x4d\x7c\xd0\xa1\x30\x55\xb6\x6b\x9e\xb7\x41\xa8\x77\x6c\x96\xb8\xa2\x0c\x7d\x70\xca\x51\xb9\xad\xc5\x75\xa7\xf1\x1e\x0e\xca\x51\xb9\xad\xc5\x75\xa7\xf1\x1e\x0e\xf2\xcf\x69\xbf\x20\xe9\x97\x05\xdd\xf3\xf2\xcf\x69\xbf\x20\xe9\x97\x05\xdd\xf3\x32\x58\x37\x8c\x5d\x02\x05\x00\xd1\x76\x67\x01\x67\x75\x3b\xba\x45\xc2\xa2\x77\x3b\x7e\xb4\x03\x88\x08\x93\xfe\x07\x51\x8e\xca" # auth_pld key16="\x00\x00\x01\x08\x01\x00\x00\x00\x2a\x34\x80\x52\x3c\x86\x1c\xfa\x9a\x2b\x8b\xff\xbb\xb5\x0d\x6b\xa1\x62\x58\xd8\x16\xaa\x15\xe4\x34\x24\xca\xc3\x09\x08\x51\x69\x69\xef\xbd\xb7\xd4\xc5\x4f\x6c\x12\xd5\xd0\x0b\xc7\x66\x0d\xcb\x6d\x01\x7b\x8c\xec\x3d\x98\xe5\x2a\xac\x11\xde\x88\x2e\xf2\x22\x98\x13\x73\xa3\x38\xd0\x43\xf4\xc6\xf0\xc1\x24\x1a\x7a\x9f\xba\x03\x25\x49\xe5\x8e\xb7\x5d\x79\x76\xfd\x22\x5c\xba\x82\xb8\x75\x81\xc6\x79\xb3\x56\x44\x82\x80\x5a\x3c\xe8\x21\xe4\xdb\xfd\x1c\xd3\x18\xbd\x74\x22\x25\x44\xde\x0b\x7e\x6e\xdb\xe3\x3b\x17\xc1\x4d\x5e\x51\x87\xb0\x5a\xce\x5f\x23\xce\x18\x61\x03\x02\x7e\x4b\x36\xb0\x7c\x90\xcf\xac\x81\xc4\x45\xa3\x50\x01\x2e\x0a\xce\x62\x7a\xe0\xa7\xc0\x45\x5e\x90\xe2\x2e\xc6\x90\xe9\xbe\x8f\xe9\x31\xa9\xc9\x44\x62\x31\xb6\x13\xaf\xd5\x9a\x55\x9b\x14\xf9\x80\xcc\x73\xe3\x51\xdf\x2a\x04\x79\x0d\x04\xee\x4c\xa8\x9d\xaa\x67\x2f\x77\x87\x5e\x2d\x05\x95\xbe\x53\x45\x96\x8b\x89\x79\x5b\x48\xe2\x6f\x3a\xc9\xef\x83\x81\xcc\x4c\xfe\xb7\x40\x2d\xa5\xa5\x51\xb7\xad\x2f\x29\xd8\xc8\x02\xbe\x18\x09\xd0\xba\x71\x77\xfe\x2c\x6d" # skf_1of1_pld key17="\x21\x00\x01\x98\x00\x01\x00\x01\x14\x77\x25\x7b\x82\xc0\xdb\x0b\x24\x36\x36\x13\x36\xe4\x99\xad\xf5\xaf\x26\x6f\x47\xd2\x0d\x65\xe1\xa8\xcb\x35\x1e\x53\xce\x6d\x8e\xf9\xe4\x51\xe3\x27\x10\x43\x38\x84\x54\x1d\x7a\x1a\x89\x34\x06\xb3\x62\x86\x98\x3b\x39\x91\x6e\xe8\x65\x3e\x31\xa8\x08\xfe\x83\x56\x30\xd3\xe0\xfd\x73\x92\x85\x2d\xae\x1d\x7d\xdb\x47\x05\x57\xe7\x8e\xc5\xa5\x1b\x0e\x85\x1f\x12\x6d\xe6\xdb\x3a\x3e\x99\xd1\x23\x41\xa4\x1c\x46\x38\xd1\xa8\x84\x96\x13\xdb\x2a\x1d\x3b\xb8\xd2\x04\xb3\x0d\xb4\x71\x90\xdb\xf6\x2d\x60\x01\xc2\xb2\x89\xbd\xe9\x95\x7b\x53\xa4\x94\x7e\x12\xe9\x5f\xfc\x51\x17\x94\x3e\xba\xc2\xa5\x4d\x3a\x4d\x4b\x95\x6d\x91\xc2\xb0\x2d\xb7\x24\xe8\x3b\xbd\xe0\xcc\x09\x50\x11\x83\xc0\xcd\x29\x33\xd5\x8f\x8a\xd1\xe3\xe8\x4f\x6a\x10\x4a\x64\x97\x0f\x38\x58\x8d\x7f\x5d\xb4\x6b\xa0\x42\x5e\x95\xe6\x08\x3e\x01\xf8\x82\x90\x81\xd4\x70\xb5\xb2\x8c\x64\xa9\x56\xdd\xc2\xda\xe1\xd3\xad\xf8\x5b\x99\x0b\x19\x5e\x88\x0d\x81\x04\x4d\xc1\x43\x41\xf1\xd3\x45\x65\x62\x70\x2f\xfa\x62\xbe\x7d\xf4\x94\x91\xe0\xbb\xb1\xbc\xe5\x27\xc8\x15\xd4\xcb\x82\x97\x15\x46\x82\xbb\x48\xbb\x16\x25\xbe\x82\xe4\x27\x80\xf3\xc2\x92\x3b\xd6\xc3\x65\x20\xec\x50\xdb\x6a\xcb\x47\x73\xf7\x98\xf1\x66\x5e\xc4\xe9\x87\xf8\xcb\x1e\x06\xa7\x67\xf5\xec\x73\xe5\xc7\x4d\xc2\x90\xe4\xdf\x9d\x1f\x05\x67\x99\xd6\xf0\xc4\x20\xbc\xf8\xf5\x3e\x19\xe9\x3a\x12\xe1\xcc\x9f\x81\x55\x1e\xad\xc8\xa3\xe5\x98\xbe\xe0\x4d\xb7\x6b\xd5\xbe\x6a\x3d\x76\xb6\xe2\xa5\xa7\x96\x68\xeb\x91\xee\x02\xfc\xe4\x01\xc3\x24\xda\x4c\xff\x10\x27\x78\xb0\x0b\x55\x5c\xce\x62\x7d\x33\x2b\x25\x99\xaa\x99\xea\xa3\x1d\xd8\x2b\x57\xb5\xe4\x04\x21\x75\xd9\xc4\xd0\x3d\xa1\xa5\x8f" # sk_pld key18="\x21\x00\x01\x94\x14\x77\x25\x7b\x82\xc0\xdb\x0b\x24\x36\x36\x13\x36\xe4\x99\xad\xf5\xaf\x26\x6f\x47\xd2\x0d\x65\xe1\xa8\xcb\x35\x1e\x53\xce\x6d\x8e\xf9\xe4\x51\xe3\x27\x10\x43\x38\x84\x54\x1d\x7a\x1a\x89\x34\x06\xb3\x62\x86\x98\x3b\x39\x91\x6e\xe8\x65\x3e\x31\xa8\x08\xfe\x83\x56\x30\xd3\xe0\xfd\x73\x92\x85\x2d\xae\x1d\x7d\xdb\x47\x05\x57\xe7\x8e\xc5\xa5\x1b\x0e\x85\x1f\x12\x6d\xe6\xdb\x3a\x3e\x99\xd1\x23\x41\xa4\x1c\x46\x38\xd1\xa8\x84\x96\x13\xdb\x2a\x1d\x3b\xb8\xd2\x04\xb3\x0d\xb4\x71\x90\xdb\xf6\x2d\x60\x01\xc2\xb2\x89\xbd\xe9\x95\x7b\x53\xa4\x94\x7e\x12\xe9\x5f\xfc\x51\x17\x94\x3e\xba\xc2\xa5\x4d\x3a\x4d\x4b\x95\x6d\x91\xc2\xb0\x2d\xb7\x24\xe8\x3b\xbd\xe0\xcc\x09\x50\x11\x83\xc0\xcd\x29\x33\xd5\x8f\x8a\xd1\xe3\xe8\x4f\x6a\x10\x4a\x64\x97\x0f\x38\x58\x8d\x7f\x5d\xb4\x6b\xa0\x42\x5e\x95\xe6\x08\x3e\x01\xf8\x82\x90\x81\xd4\x70\xb5\xb2\x8c\x64\xa9\x56\xdd\xc2\xda\xe1\xd3\xad\xf8\x5b\x99\x0b\x19\x5e\x88\x0d\x81\x04\x4d\xc1\x43\x41\xf1\xd3\x45\x65\x62\x70\x2f\xfa\x62\xbe\x7d\xf4\x94\x91\xe0\xbb\xb1\xbc\xe5\x27\xc8\x15\xd4\xcb\x82\x97\x15\x46\x82\xbb\x48\xbb\x16\x25\xbe\x82\xe4\x27\x80\xf3\xc2\x92\x3b\xd6\xc3\x65\x20\xec\x50\xdb\x6a\xcb\x47\x73\xf7\x98\xf1\x66\x5e\xc4\xe9\x87\xf8\xcb\x1e\x06\xa7\x67\xf5\xec\x73\xe5\xc7\x4d\xc2\x90\xe4\xdf\x9d\x1f\x05\x67\x99\xd6\xf0\xc4\x20\xbc\xf8\xf5\x3e\x19\xe9\x3a\x12\xe1\xcc\x9f\x81\x55\x1e\xad\xc8\xa3\xe5\x98\xbe\xe0\x4d\xb7\x6b\xd5\xbe\x6a\x3d\x76\xb6\xe2\xa5\xa7\x96\x68\xeb\x91\xee\x02\xfc\xe4\x01\xc3\x24\xda\x4c\xff\x10\x27\x78\xb0\x0b\x55\x5c\xce\x62\x7d\x33\x2b\x25\x99\xaa\x99\xea\xa3\x1d\xd8\x2b\x57\xb5\xe4\x04\x21\x75\xd9\xc4\xd0\x3d\xa1\xa5\x8f" # valid_packet key19="\xde\xad\xbe\xef\xca\xfe\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x21\x20\x22\x08\x00\x00\x00\x00\x00\x00\x01\xc0\x22\x00\x00\x40\x00\x00\x00\x3c\x01\x01\x00\x06\x03\x00\x00\x08\x03\x00\x00\x0c\x03\x00\x00\x0c\x01\x00\x00\x0c\x80\x0e\x00\xc0\x03\x00\x00\x08\x04\x00\x00\x0e\x03\x00\x00\x08\x02\x00\x00\x05\x03\x00\x00\x08\x02\x00\x00\x02\x00\x00\x00\x08\x02\x00\x00\x01\x28\x00\x01\x08\x00\x0e\x00\x00\x16\xcb\x68\xaf\x63\xfe\xb0\x58\x49\x0e\x7f\x85\x60\x53\x80\xae\x3f\x82\xf3\x35\x21\xd5\xae\x09\x1c\xfa\x68\xc2\xfb\x4b\xb3\x84\xda\xaf\x6e\xe2\x5e\xc5\xb6\x8c\x35\x3c\xec\x58\x7f\xa9\xf8\xa4\x24\xf3\xf8\xf4\x65\x59\x8c\x15\x4d\x2c\xf1\x5d\xeb\x57\x68\xfe\x75\x61\x5a\x80\x96\xa4\x0a\xad\x75\x71\xd8\xe0\x06\xbc\xde\x16\x6d\x1e\xd9\x5d\x2c\x00\x66\x43\x82\xe4\x6f\x5f\x95\xe7\x9b\xfd\xf2\xe2\xcb\xc5\xf1\x52\xdd\x3b\xed\x88\xd4\xa9\x13\x4e\x42\xe8\x60\x2d\x3c\xf6\xc8\xf0\x70\x42\xfa\x33\x7f\x28\xdf\x6b\x79\x2c\x79\x8f\xc0\x5d\x81\x7a\x62\xdb\xd4\x44\x3a\x3c\x21\xbf\x85\xc8\x0b\x8c\x77\x72\xe9\xfb\x50\x5c\x03\xa6\xb2\x3f\x17\x4a\xd1\xb3\x01\x30\xad\xe4\xfa\xe2\xba\x6f\x22\x83\xf4\xde\x38\x43\xe8\x27\x00\xb8\x95\xbe\x03\x8f\xcd\xd3\x72\xed\xa5\xed\x8d\xf4\x68\x98\xef\x59\xcc\xfb\x54\x89\xde\xa9\xd4\x88\xcd\xb9\xca\x09\xd3\xd5\x25\xb1\x8c\x58\x12\x9c\x69\x03\x72\x00\xc9\xca\x95\x8a\xce\x0d\xd2\xc8\x25\xe7\x7c\xed\x5e\xee\x35\x01\xfc\x00\x56\xed\xf3\x8d\x81\x6c\x3e\x86\x6a\x40\xac\xc7\x9c\x7a\xbf\x9f\x8e\x1f\xd8\x60\x29\x00\x00\x24\x5f\x61\x42\x72\x7d\xb2\xa8\xc1\xfe\xb1\x38\x2e\xb8\x75\xa7\xc1\x1d\x8a\xa7\xb7\x9b\x92\xe2\x0e\x3a\x18\x20\xb6\x16\xf3\x35\x67\x29\x00\x00\x1c\x00\x00\x40\x04\xc7\xa0\x68\x68\x09\x0a\x7f\x12\x0b\x13\xd3\x2f\xde\x64\x8b\xf1\xc3\x3c\x79\x8f\x00\x00\x00\x1c\x00\x00\x40\x05\x9f\xbc\x8c\xd0\x91\x5e\xa0\x87\x81\xab\x4f\xa1\x8a\xa7\xa8\xf9\xeb\xdf\x9f\x2c" #recommended by libfuzzer "\x01\x00" "\x0e\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x1c" "\x00\x00\x00\x00" "\x1d\x00\x00\x00\x00\x00\x00\x00" "\x00\x03" "\x01\x00\x00\x32" openiked-7.4/regress/parser-libfuzzer/test_libfuzzer.options000066400000000000000000000000331477554731100246440ustar00rootroot00000000000000[libfuzzer] max_len = 8164 openiked-7.4/regress/parser-libfuzzer/test_parser_fuzz.c000066400000000000000000000053471477554731100237460ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Fuzz tests for payload parsing * * Placed in the public domain */ #include #include #include #include #include #include #include "iked.h" #include "ikev2.h" u_int8_t cookies[] = { 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x00, 0x01, /* initator cookie */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* responder cookie */ }; u_int8_t genhdr[] = { 0x00, 0x20, 0x22, 0x08, /* next, major/minor, exchange type, flags */ 0x00, 0x00, 0x00, 0x00, /* message ID */ 0x00, 0x00, 0x00, 0x00 /* total length */ }; #define OFFSET_ICOOKIE 0 #define OFFSET_RCOOKIE 8 #define OFFSET_NEXTPAYLOAD (0 + sizeof(cookies)) #define OFFSET_VERSION (1 + sizeof(cookies)) #define OFFSET_EXCHANGE (2 + sizeof(cookies)) #define OFFSET_LENGTH (8 + sizeof(cookies)) static u_int8_t * get_icookie(u_int8_t *data) { return &data[OFFSET_ICOOKIE]; } static u_int8_t * get_rcookie(u_int8_t *data) { return &data[OFFSET_RCOOKIE]; } static u_int8_t get_nextpayload(u_int8_t *data) { return data[OFFSET_NEXTPAYLOAD]; } static u_int8_t get_version(u_int8_t *data) { return data[OFFSET_VERSION]; } static u_int8_t get_exchange(u_int8_t *data) { return data[OFFSET_EXCHANGE]; } static u_int32_t get_length(u_int8_t *data) { return *(u_int32_t *)&data[OFFSET_LENGTH]; } static void prepare_header(struct ike_header *hdr, struct ibuf *data) { bzero(hdr, sizeof(*hdr)); bcopy(get_icookie(ibuf_data(data)), &hdr->ike_ispi, sizeof(hdr->ike_ispi)); bcopy(get_rcookie(ibuf_data(data)), &hdr->ike_rspi, sizeof(hdr->ike_rspi)); hdr->ike_nextpayload = get_nextpayload(ibuf_data(data)); hdr->ike_version = get_version(ibuf_data(data)); hdr->ike_exchange = get_exchange(ibuf_data(data)); hdr->ike_length = get_length(ibuf_data(data)); } static void prepare_message(struct iked_message *msg, struct ibuf *data) { static struct iked_sa sa; bzero(&sa, sizeof(sa)); bzero(msg, sizeof(*msg)); msg->msg_sa = &sa; msg->msg_data = data; msg->msg_e = 1; msg->msg_parent = msg; TAILQ_INIT(&msg->msg_proposals); SIMPLEQ_INIT(&msg->msg_certreqs); } /* Entry-Point for libFuzzer */ int LLVMFuzzerTestOneInput(const char *data, size_t size) { struct ibuf *fuzzed; struct ike_header hdr; struct iked_message msg; bzero(&hdr, sizeof(hdr)); bzero(&msg, sizeof(msg)); fuzzed = ibuf_new(data, size); if (fuzzed == NULL){ fprintf(stderr, "%s\n", "ERROR: fuzzed == NULL! " "(hint: fuzz-input too long?)"); return -1; } /* size too small? */ if (size < sizeof(cookies) + sizeof(genhdr)){ ibuf_free(fuzzed); return 0; } prepare_header(&hdr, fuzzed); prepare_message(&msg, fuzzed); ikev2_pld_parse(NULL, &hdr, &msg, 0); ikev2_msg_cleanup(NULL, &msg); return 0; } openiked-7.4/regress/parser/000077500000000000000000000000001477554731100161465ustar00rootroot00000000000000openiked-7.4/regress/parser/CMakeLists.txt000066400000000000000000000022311477554731100207040ustar00rootroot00000000000000# Copyright (c) 2020-2021 Tobias Heider # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. set(SRCS) list(APPEND SRCS tests.c common.c test_parser_fuzz.c ) add_executable(test_parser ${SRCS}) target_include_directories(test_parser PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../test_helper PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../../iked ) target_link_libraries(test_parser PRIVATE util event crypto ssl test_helper compat iked-shared ) target_compile_options(test_parser PRIVATE ${CFLAGS}) openiked-7.4/regress/parser/Makefile000066400000000000000000000014571477554731100176150ustar00rootroot00000000000000# $OpenBSD: Makefile,v 1.3 2022/02/23 22:50:32 bluhm Exp $ WARNINGS=Yes PROG= test_parser SRCS= tests.c common.c test_parser_fuzz.c IKEOBJS= ikev2_pld.o imsg_util.o log.o util.o \ ikev2_map.o eap_map.o CLEANFILES= ${IKEOBJS} ${PROG}.log LDADD+= -lutil -lcrypto ${IKEOBJS} DPADD+= ${LIBUTIL} ${LIBCRYPTO} CFLAGS+= -I${.CURDIR}/../../../../sbin/iked CFLAGS+= -Wno-missing-field-initializers run-regress-${PROG}: ${PROG} ./${PROG} >${PROG}.log 2>&1 test_parser: ${IKEOBJS} ${IKEOBJS}: cd ${.CURDIR}/../../../../sbin/iked && make $@ ln -sf ${.OBJDIR}/../../../../sbin/iked/$@ . LDADD+= -L${.OBJDIR} -ltest_helper DPADD+= libtest_helper.a CFLAGS+= -I${.CURDIR}/../test_helper libtest_helper.a: cd ${.CURDIR}/../test_helper && make $@ ln -sf ${.OBJDIR}/../test_helper/$@ . .include openiked-7.4/regress/parser/common.c000066400000000000000000000114321477554731100176030ustar00rootroot00000000000000/* $OpenBSD: common.c,v 1.13 2022/12/03 22:34:35 tobhe Exp $ */ /* * A bunch of stub functions so we can compile and link ikev2_pld.c * in a standalone program for testing purposes. * * Placed in the public domain */ #include #include #include #include #include #include "iked.h" #include "types.h" #include "test_helper.h" int eap_parse(struct iked *, const struct iked_sa *, struct iked_message *, void *, int); int ikev2_msg_frompeer(struct iked_message *); int ikev2_send_ike_e(struct iked *, struct iked_sa *, struct ibuf *, uint8_t, uint8_t, int); void ikev2_ikesa_recv_delete(struct iked *, struct iked_sa *); struct iked_childsa * childsa_lookup(struct iked_sa *, uint64_t, uint8_t); int ikev2_childsa_delete(struct iked *, struct iked_sa *, uint8_t, uint64_t, uint64_t *, int); int sa_stateok(const struct iked_sa *, int); void sa_state(struct iked *, struct iked_sa *, int); void ikev2_disable_rekeying(struct iked *, struct iked_sa *); void ikev2_init_ike_sa(struct iked *, void *); struct dh_group * group_get(uint32_t); void timer_set(struct iked *, struct iked_timer *, void (*)(struct iked *, void *), void *); void timer_add(struct iked *, struct iked_timer *, int); void timer_del(struct iked *, struct iked_timer *); ssize_t ikev2_nat_detection(struct iked *, struct iked_message *, void *, size_t, u_int, int); int ca_setreq(struct iked *, struct iked_sa *, struct iked_static_id *, uint8_t, uint8_t, uint8_t *, size_t, enum privsep_procid); int ikev2_print_id(struct iked_id *, char *, size_t); int config_add_transform(struct iked_proposal *, u_int, u_int, u_int, u_int); struct iked_proposal * config_add_proposal(struct iked_proposals *, u_int, u_int); void config_free_proposal(struct iked_proposals *, struct iked_proposal *); int ikev2_send_informational(struct iked *, struct iked_message *); struct ibuf * ikev2_msg_decrypt(struct iked *, struct iked_sa *, struct ibuf *, struct ibuf *); int eap_parse(struct iked *env, const struct iked_sa *sa, struct iked_message *msg, void *data, int response) { return (0); } int ikev2_msg_frompeer(struct iked_message *msg) { return (0); } int ikev2_send_ike_e(struct iked *env, struct iked_sa *sa, struct ibuf *buf, uint8_t firstpayload, uint8_t exchange, int response) { return (0); } void ikev2_ikesa_recv_delete(struct iked *env, struct iked_sa *sa) { } const char * ikev2_ikesa_info(uint64_t spi, const char *msg) { return ""; } struct iked_childsa * childsa_lookup(struct iked_sa *a, uint64_t b, uint8_t c) { return (NULL); } int ikev2_childsa_delete(struct iked *a, struct iked_sa *b, uint8_t c, uint64_t d, uint64_t *e , int f) { return (0); } int sa_stateok(const struct iked_sa *a, int b) { return (0); } void sa_state(struct iked * a, struct iked_sa *b, int c) { } void ikev2_disable_rekeying(struct iked *a, struct iked_sa *b) { } void ikev2_init_ike_sa(struct iked *a, void *b) { } const struct group_id * group_getid(uint32_t id) { return (NULL); } void timer_set(struct iked *env, struct iked_timer *tmr, void (*cb)(struct iked *, void *), void *arg) { } void timer_add(struct iked *env, struct iked_timer *tmr, int timeout) { } void timer_del(struct iked *env, struct iked_timer *tmr) { } ssize_t ikev2_nat_detection(struct iked *env, struct iked_message *msg, void *ptr, size_t len, u_int type, int frompeer) { return (0); } int ca_setreq(struct iked *env, struct iked_sa *sh, struct iked_static_id *localid, uint8_t type, uint8_t more, uint8_t *data, size_t len, enum privsep_procid procid) { return (0); } int ikev2_print_id(struct iked_id *id, char *idstr, size_t idstrlen) { return (0); } int config_add_transform(struct iked_proposal *prop, u_int type, u_int id, u_int length, u_int keylength) { return (0); } struct iked_proposal * config_add_proposal(struct iked_proposals *head, u_int id, u_int proto) { return (NULL); } void config_free_proposal(struct iked_proposals *head, struct iked_proposal *prop) { return; } void config_free_fragments(struct iked_frag *frag) { return; } int ikev2_send_informational(struct iked *env, struct iked_message *msg) { return (0); } struct ibuf * ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa, struct ibuf *msg, struct ibuf *src) { ASSERT_PTR_NE(src, NULL); /* * Free src as caller uses ikev2_msg_decrypt() like this: * src = ikev2_msg_decrypt(..., src); */ ibuf_free(src); return (NULL); } void ikev2_ike_sa_setreason(struct iked_sa *sa, char *r) { } void ikev2_msg_dispose(struct iked *env, struct iked_msgqueue *queue, struct iked_msg_retransmit *mr) { } struct iked_msg_retransmit * ikev2_msg_lookup(struct iked *env, struct iked_msgqueue *queue, struct iked_message *msg, uint8_t exchange) { return NULL; } openiked-7.4/regress/parser/test_parser_fuzz.c000066400000000000000000001013551477554731100217300ustar00rootroot00000000000000/* $OpenBSD: test_parser_fuzz.c,v 1.8 2022/12/04 00:23:03 tobhe Exp $ */ /* * Fuzz tests for payload parsing * * Placed in the public domain */ #include #include #include #include #include #include #include #include #include "iked.h" #include "ikev2.h" #include "test_helper.h" extern int ikev2_pld_payloads(struct iked *, struct iked_message *, size_t, size_t, u_int); void parser_fuzz_tests(void); uint8_t cookies[] = { 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x00, 0x01, /* initator cookie */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* responder cookie */ }; uint8_t genhdr[] = { 0x00, 0x20, 0x22, 0x08, /* next, major/minor, exchange type, flags */ 0x00, 0x00, 0x00, 0x00, /* message ID */ 0x00, 0x00, 0x00, 0x00 /* total length */ }; uint8_t sa_pld[] = { 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00 }; uint8_t saxform_pld[] = { 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3c, 0x01, 0x01, 0x00, 0x06, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x0c, 0x80, 0x0e, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x01 }; uint8_t ke_pld[] = { 0x00, 0x00, 0x01, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x16, 0xcb, 0x68, 0xaf, 0x63, 0xfe, 0xb0, 0x58, 0x49, 0x0e, 0x7f, 0x85, 0x60, 0x53, 0x80, 0xae, 0x3f, 0x82, 0xf3, 0x35, 0x21, 0xd5, 0xae, 0x09, 0x1c, 0xfa, 0x68, 0xc2, 0xfb, 0x4b, 0xb3, 0x84, 0xda, 0xaf, 0x6e, 0xe2, 0x5e, 0xc5, 0xb6, 0x8c, 0x35, 0x3c, 0xec, 0x58, 0x7f, 0xa9, 0xf8, 0xa4, 0x24, 0xf3, 0xf8, 0xf4, 0x65, 0x59, 0x8c, 0x15, 0x4d, 0x2c, 0xf1, 0x5d, 0xeb, 0x57, 0x68, 0xfe, 0x75, 0x61, 0x5a, 0x80, 0x96, 0xa4, 0x0a, 0xad, 0x75, 0x71, 0xd8, 0xe0, 0x06, 0xbc, 0xde, 0x16, 0x6d, 0x1e, 0xd9, 0x5d, 0x2c, 0x00, 0x66, 0x43, 0x82, 0xe4, 0x6f, 0x5f, 0x95, 0xe7, 0x9b, 0xfd, 0xf2, 0xe2, 0xcb, 0xc5, 0xf1, 0x52, 0xdd, 0x3b, 0xed, 0x88, 0xd4, 0xa9, 0x13, 0x4e, 0x42, 0xe8, 0x60, 0x2d, 0x3c, 0xf6, 0xc8, 0xf0, 0x70, 0x42, 0xfa, 0x33, 0x7f, 0x28, 0xdf, 0x6b, 0x79, 0x2c, 0x79, 0x8f, 0xc0, 0x5d, 0x81, 0x7a, 0x62, 0xdb, 0xd4, 0x44, 0x3a, 0x3c, 0x21, 0xbf, 0x85, 0xc8, 0x0b, 0x8c, 0x77, 0x72, 0xe9, 0xfb, 0x50, 0x5c, 0x03, 0xa6, 0xb2, 0x3f, 0x17, 0x4a, 0xd1, 0xb3, 0x01, 0x30, 0xad, 0xe4, 0xfa, 0xe2, 0xba, 0x6f, 0x22, 0x83, 0xf4, 0xde, 0x38, 0x43, 0xe8, 0x27, 0x00, 0xb8, 0x95, 0xbe, 0x03, 0x8f, 0xcd, 0xd3, 0x72, 0xed, 0xa5, 0xed, 0x8d, 0xf4, 0x68, 0x98, 0xef, 0x59, 0xcc, 0xfb, 0x54, 0x89, 0xde, 0xa9, 0xd4, 0x88, 0xcd, 0xb9, 0xca, 0x09, 0xd3, 0xd5, 0x25, 0xb1, 0x8c, 0x58, 0x12, 0x9c, 0x69, 0x03, 0x72, 0x00, 0xc9, 0xca, 0x95, 0x8a, 0xce, 0x0d, 0xd2, 0xc8, 0x25, 0xe7, 0x7c, 0xed, 0x5e, 0xee, 0x35, 0x01, 0xfc, 0x00, 0x56, 0xed, 0xf3, 0x8d, 0x81, 0x6c, 0x3e, 0x86, 0x6a, 0x40, 0xac, 0xc7, 0x9c, 0x7a, 0xbf, 0x9f, 0x8e, 0x1f, 0xd8, 0x60 }; uint8_t nonce_pld[] = { 0x00, 0x00, 0x00, 0x24, 0x5f, 0x61, 0x42, 0x72, 0x7d, 0xb2, 0xa8, 0xc1, 0xfe, 0xb1, 0x38, 0x2e, 0xb8, 0x75, 0xa7, 0xc1, 0x1d, 0x8a, 0xa7, 0xb7, 0x9b, 0x92, 0xe2, 0x0e, 0x3a, 0x18, 0x20, 0xb6, 0x16, 0xf3, 0x35, 0x67, }; uint8_t notify_pld[] = { 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x40, 0x04, 0xc7, 0xa0, 0x68, 0x68, 0x09, 0x0a, 0x7f, 0x12, 0x0b, 0x13, 0xd3, 0x2f, 0xde, 0x64, 0x8b, 0xf1, 0xc3, 0x3c, 0x79, 0x8f, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x40, 0x05, 0x9f, 0xbc, 0x8c, 0xd0, 0x91, 0x5e, 0xa0, 0x87, 0x81, 0xab, 0x4f, 0xa1, 0x8a, 0xa7, 0xa8, 0xf9, 0xeb, 0xdf, 0x9f, 0x2c }; uint8_t id_pld[] = { 0x00, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x00, 0xac, 0x12, 0x7d, 0x01 }; uint8_t cert_pld[] = { 0x00, 0x00, 0x01, 0x10, 0x0b, 0x00, 0x00, 0x00, 0x30, 0x82, 0x01, 0x0c, 0x02, 0x82, 0x01, 0x01, 0x00, 0x8a, 0x26, 0xf8, 0x9e, 0xe8, 0x09, 0x11, 0x6b, 0x3d, 0x00, 0xd3, 0x25, 0xf8, 0x9f, 0xe8, 0x09, 0x11, 0x6b, 0x3d, 0x10, 0xd3, 0x0b, 0x9a, 0xb0, 0xb7, 0xe4, 0x3e, 0x40, 0x59, 0xd7, 0x51, 0x03, 0xaf, 0x09, 0x79, 0x1b, 0x0d, 0x63, 0x66, 0x28, 0xaa, 0x97, 0xc8, 0x20, 0x4b, 0x28, 0x9b, 0x5e, 0x8c, 0xa9, 0x8f, 0x73, 0x81, 0xb4, 0xfa, 0xf4, 0xdd, 0x05, 0x69, 0x0b, 0x71, 0x72, 0xd8, 0xbb, 0xac, 0x4b, 0x6d, 0x67, 0x5a, 0xa2, 0x63, 0x5d, 0x6d, 0x27, 0xc5, 0xf4, 0xe6, 0x0a, 0xbd, 0x2b, 0x0a, 0x64, 0xb2, 0xcf, 0x59, 0x63, 0x9b, 0x5c, 0x4f, 0x26, 0x36, 0xe3, 0x10, 0x70, 0x3c, 0x39, 0x77, 0x55, 0x07, 0x1c, 0x12, 0xde, 0x60, 0x53, 0xa1, 0x70, 0xf4, 0xda, 0xfc, 0xcc, 0xec, 0xad, 0x6d, 0x34, 0xad, 0xe2, 0x36, 0x10, 0x93, 0x59, 0x0c, 0x81, 0x8d, 0x22, 0x7e, 0x57, 0xeb, 0x89, 0x26, 0xdb, 0x6e, 0x99, 0x9a, 0xde, 0xbe, 0xad, 0xef, 0xca, 0xaf, 0xfe, 0xfe, 0x99, 0x9a, 0xde, 0xbe, 0xad, 0xef, 0xca, 0xaf, 0xfe, 0xfe, 0x6f, 0xd4, 0xe4, 0x63, 0x6c, 0x3e, 0x83, 0x09, 0xf4, 0x32, 0x78, 0x3b, 0x71, 0xe9, 0x36, 0xb6, 0x92, 0xf6, 0xa8, 0x31, 0x4d, 0x7c, 0xd0, 0xa1, 0x30, 0x55, 0xb6, 0x6b, 0x9e, 0xb7, 0x41, 0xa8, 0x77, 0x6c, 0x96, 0xb8, 0xa2, 0x0c, 0x7d, 0x70, 0xca, 0x51, 0xb9, 0xad, 0xc5, 0x75, 0xa7, 0xf1, 0x1e, 0x0e, 0xca, 0x51, 0xb9, 0xad, 0xc5, 0x75, 0xa7, 0xf1, 0x1e, 0x0e, 0xf2, 0xcf, 0x69, 0xbf, 0x20, 0xe9, 0x97, 0x05, 0xdd, 0xf3, 0xf2, 0xcf, 0x69, 0xbf, 0x20, 0xe9, 0x97, 0x05, 0xdd, 0xf3, 0x32, 0x58, 0x37, 0x8c, 0x5d, 0x02, 0x05, 0x00, 0xd1, 0x76, 0x67, 0x01, 0x67, 0x75, 0x3b, 0xba, 0x45, 0xc2, 0xa2, 0x77, 0x3b, 0x7e, 0xb4, 0x03, 0x88, 0x08, 0x93, 0xfe, 0x07, 0x51, 0x8e, 0xcf }; uint8_t certreq_pld[] = { 0x00, 0x00, 0x00, 0x05, 0x0b }; uint8_t auth_pld[] = { 0x00, 0x00, 0x01, 0x08, 0x01, 0x00, 0x00, 0x00, 0x2a, 0x34, 0x80, 0x52, 0x3c, 0x86, 0x1c, 0xfa, 0x9a, 0x2b, 0x8b, 0xff, 0xbb, 0xb5, 0x0d, 0x6b, 0xa1, 0x62, 0x58, 0xd8, 0x16, 0xaa, 0x15, 0xe4, 0x34, 0x24, 0xca, 0xc3, 0x09, 0x08, 0x51, 0x69, 0x69, 0xef, 0xbd, 0xb7, 0xd4, 0xc5, 0x4f, 0x6c, 0x12, 0xd5, 0xd0, 0x0b, 0xc7, 0x66, 0x0d, 0xcb, 0x6d, 0x01, 0x7b, 0x8c, 0xec, 0x3d, 0x98, 0xe5, 0x2a, 0xac, 0x11, 0xde, 0x88, 0x2e, 0xf2, 0x22, 0x98, 0x13, 0x73, 0xa3, 0x38, 0xd0, 0x43, 0xf4, 0xc6, 0xf0, 0xc1, 0x24, 0x1a, 0x7a, 0x9f, 0xba, 0x03, 0x25, 0x49, 0xe5, 0x8e, 0xb7, 0x5d, 0x79, 0x76, 0xfd, 0x22, 0x5c, 0xba, 0x82, 0xb8, 0x75, 0x81, 0xc6, 0x79, 0xb3, 0x56, 0x44, 0x82, 0x80, 0x5a, 0x3c, 0xe8, 0x21, 0xe4, 0xdb, 0xfd, 0x1c, 0xd3, 0x18, 0xbd, 0x74, 0x22, 0x25, 0x44, 0xde, 0x0b, 0x7e, 0x6e, 0xdb, 0xe3, 0x3b, 0x17, 0xc1, 0x4d, 0x5e, 0x51, 0x87, 0xb0, 0x5a, 0xce, 0x5f, 0x23, 0xce, 0x18, 0x61, 0x03, 0x02, 0x7e, 0x4b, 0x36, 0xb0, 0x7c, 0x90, 0xcf, 0xac, 0x81, 0xc4, 0x45, 0xa3, 0x50, 0x01, 0x2e, 0x0a, 0xce, 0x62, 0x7a, 0xe0, 0xa7, 0xc0, 0x45, 0x5e, 0x90, 0xe2, 0x2e, 0xc6, 0x90, 0xe9, 0xbe, 0x8f, 0xe9, 0x31, 0xa9, 0xc9, 0x44, 0x62, 0x31, 0xb6, 0x13, 0xaf, 0xd5, 0x9a, 0x55, 0x9b, 0x14, 0xf9, 0x80, 0xcc, 0x73, 0xe3, 0x51, 0xdf, 0x2a, 0x04, 0x79, 0x0d, 0x04, 0xee, 0x4c, 0xa8, 0x9d, 0xaa, 0x67, 0x2f, 0x77, 0x87, 0x5e, 0x2d, 0x05, 0x95, 0xbe, 0x53, 0x45, 0x96, 0x8b, 0x89, 0x79, 0x5b, 0x48, 0xe2, 0x6f, 0x3a, 0xc9, 0xef, 0x83, 0x81, 0xcc, 0x4c, 0xfe, 0xb7, 0x40, 0x2d, 0xa5, 0xa5, 0x51, 0xb7, 0xad, 0x2f, 0x29, 0xd8, 0xc8, 0x02, 0xbe, 0x18, 0x09, 0xd0, 0xba, 0x71, 0x77, 0xfe, 0x2c, 0x6d }; uint8_t delete_pld[] = { 0x2a, 0x00, 0x00, 0x10, 0x01, 0x08, 0x00, 0x01, /* IKE SA */ 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xaf, 0xfe, 0x00, 0x00, 0x00, 0x10, 0x03, 0x04, 0x00, 0x02, /* ESP SA */ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11 }; uint8_t vendor_pld[] = { 0x00, 0x00, 0x00, 0x08, 0x11, 0x22, 0x33, 0x44 }; uint8_t ts_pld[] = { 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x10, 0x00, 0x00, 0xff, 0xff, 0xac, 0x28, 0x7d, 0x00, 0xac, 0x28, 0x7d, 0xff }; uint8_t skf_1of1_pld[] = { 0x21, 0x00, 0x01, 0x98, 0x00, 0x01, 0x00, 0x01, 0x14, 0x77, 0x25, 0x7b, 0x82, 0xc0, 0xdb, 0x0b, 0x24, 0x36, 0x36, 0x13, 0x36, 0xe4, 0x99, 0xad, 0xf5, 0xaf, 0x26, 0x6f, 0x47, 0xd2, 0x0d, 0x65, 0xe1, 0xa8, 0xcb, 0x35, 0x1e, 0x53, 0xce, 0x6d, 0x8e, 0xf9, 0xe4, 0x51, 0xe3, 0x27, 0x10, 0x43, 0x38, 0x84, 0x54, 0x1d, 0x7a, 0x1a, 0x89, 0x34, 0x06, 0xb3, 0x62, 0x86, 0x98, 0x3b, 0x39, 0x91, 0x6e, 0xe8, 0x65, 0x3e, 0x31, 0xa8, 0x08, 0xfe, 0x83, 0x56, 0x30, 0xd3, 0xe0, 0xfd, 0x73, 0x92, 0x85, 0x2d, 0xae, 0x1d, 0x7d, 0xdb, 0x47, 0x05, 0x57, 0xe7, 0x8e, 0xc5, 0xa5, 0x1b, 0x0e, 0x85, 0x1f, 0x12, 0x6d, 0xe6, 0xdb, 0x3a, 0x3e, 0x99, 0xd1, 0x23, 0x41, 0xa4, 0x1c, 0x46, 0x38, 0xd1, 0xa8, 0x84, 0x96, 0x13, 0xdb, 0x2a, 0x1d, 0x3b, 0xb8, 0xd2, 0x04, 0xb3, 0x0d, 0xb4, 0x71, 0x90, 0xdb, 0xf6, 0x2d, 0x60, 0x01, 0xc2, 0xb2, 0x89, 0xbd, 0xe9, 0x95, 0x7b, 0x53, 0xa4, 0x94, 0x7e, 0x12, 0xe9, 0x5f, 0xfc, 0x51, 0x17, 0x94, 0x3e, 0xba, 0xc2, 0xa5, 0x4d, 0x3a, 0x4d, 0x4b, 0x95, 0x6d, 0x91, 0xc2, 0xb0, 0x2d, 0xb7, 0x24, 0xe8, 0x3b, 0xbd, 0xe0, 0xcc, 0x09, 0x50, 0x11, 0x83, 0xc0, 0xcd, 0x29, 0x33, 0xd5, 0x8f, 0x8a, 0xd1, 0xe3, 0xe8, 0x4f, 0x6a, 0x10, 0x4a, 0x64, 0x97, 0x0f, 0x38, 0x58, 0x8d, 0x7f, 0x5d, 0xb4, 0x6b, 0xa0, 0x42, 0x5e, 0x95, 0xe6, 0x08, 0x3e, 0x01, 0xf8, 0x82, 0x90, 0x81, 0xd4, 0x70, 0xb5, 0xb2, 0x8c, 0x64, 0xa9, 0x56, 0xdd, 0xc2, 0xda, 0xe1, 0xd3, 0xad, 0xf8, 0x5b, 0x99, 0x0b, 0x19, 0x5e, 0x88, 0x0d, 0x81, 0x04, 0x4d, 0xc1, 0x43, 0x41, 0xf1, 0xd3, 0x45, 0x65, 0x62, 0x70, 0x2f, 0xfa, 0x62, 0xbe, 0x7d, 0xf4, 0x94, 0x91, 0xe0, 0xbb, 0xb1, 0xbc, 0xe5, 0x27, 0xc8, 0x15, 0xd4, 0xcb, 0x82, 0x97, 0x15, 0x46, 0x82, 0xbb, 0x48, 0xbb, 0x16, 0x25, 0xbe, 0x82, 0xe4, 0x27, 0x80, 0xf3, 0xc2, 0x92, 0x3b, 0xd6, 0xc3, 0x65, 0x20, 0xec, 0x50, 0xdb, 0x6a, 0xcb, 0x47, 0x73, 0xf7, 0x98, 0xf1, 0x66, 0x5e, 0xc4, 0xe9, 0x87, 0xf8, 0xcb, 0x1e, 0x06, 0xa7, 0x67, 0xf5, 0xec, 0x73, 0xe5, 0xc7, 0x4d, 0xc2, 0x90, 0xe4, 0xdf, 0x9d, 0x1f, 0x05, 0x67, 0x99, 0xd6, 0xf0, 0xc4, 0x20, 0xbc, 0xf8, 0xf5, 0x3e, 0x19, 0xe9, 0x3a, 0x12, 0xe1, 0xcc, 0x9f, 0x81, 0x55, 0x1e, 0xad, 0xc8, 0xa3, 0xe5, 0x98, 0xbe, 0xe0, 0x4d, 0xb7, 0x6b, 0xd5, 0xbe, 0x6a, 0x3d, 0x76, 0xb6, 0xe2, 0xa5, 0xa7, 0x96, 0x68, 0xeb, 0x91, 0xee, 0x02, 0xfc, 0xe4, 0x01, 0xc3, 0x24, 0xda, 0x4c, 0xff, 0x10, 0x27, 0x78, 0xb0, 0x0b, 0x55, 0x5c, 0xce, 0x62, 0x7d, 0x33, 0x2b, 0x25, 0x99, 0xaa, 0x99, 0xea, 0xa3, 0x1d, 0xd8, 0x2b, 0x57, 0xb5, 0xe4, 0x04, 0x21, 0x75, 0xd9, 0xc4, 0xd0, 0x3d, 0xa1, 0xa5, 0x8f }; uint8_t sk_pld[] = { 0x21, 0x00, 0x01, 0x94, 0x14, 0x77, 0x25, 0x7b, 0x82, 0xc0, 0xdb, 0x0b, 0x24, 0x36, 0x36, 0x13, 0x36, 0xe4, 0x99, 0xad, 0xf5, 0xaf, 0x26, 0x6f, 0x47, 0xd2, 0x0d, 0x65, 0xe1, 0xa8, 0xcb, 0x35, 0x1e, 0x53, 0xce, 0x6d, 0x8e, 0xf9, 0xe4, 0x51, 0xe3, 0x27, 0x10, 0x43, 0x38, 0x84, 0x54, 0x1d, 0x7a, 0x1a, 0x89, 0x34, 0x06, 0xb3, 0x62, 0x86, 0x98, 0x3b, 0x39, 0x91, 0x6e, 0xe8, 0x65, 0x3e, 0x31, 0xa8, 0x08, 0xfe, 0x83, 0x56, 0x30, 0xd3, 0xe0, 0xfd, 0x73, 0x92, 0x85, 0x2d, 0xae, 0x1d, 0x7d, 0xdb, 0x47, 0x05, 0x57, 0xe7, 0x8e, 0xc5, 0xa5, 0x1b, 0x0e, 0x85, 0x1f, 0x12, 0x6d, 0xe6, 0xdb, 0x3a, 0x3e, 0x99, 0xd1, 0x23, 0x41, 0xa4, 0x1c, 0x46, 0x38, 0xd1, 0xa8, 0x84, 0x96, 0x13, 0xdb, 0x2a, 0x1d, 0x3b, 0xb8, 0xd2, 0x04, 0xb3, 0x0d, 0xb4, 0x71, 0x90, 0xdb, 0xf6, 0x2d, 0x60, 0x01, 0xc2, 0xb2, 0x89, 0xbd, 0xe9, 0x95, 0x7b, 0x53, 0xa4, 0x94, 0x7e, 0x12, 0xe9, 0x5f, 0xfc, 0x51, 0x17, 0x94, 0x3e, 0xba, 0xc2, 0xa5, 0x4d, 0x3a, 0x4d, 0x4b, 0x95, 0x6d, 0x91, 0xc2, 0xb0, 0x2d, 0xb7, 0x24, 0xe8, 0x3b, 0xbd, 0xe0, 0xcc, 0x09, 0x50, 0x11, 0x83, 0xc0, 0xcd, 0x29, 0x33, 0xd5, 0x8f, 0x8a, 0xd1, 0xe3, 0xe8, 0x4f, 0x6a, 0x10, 0x4a, 0x64, 0x97, 0x0f, 0x38, 0x58, 0x8d, 0x7f, 0x5d, 0xb4, 0x6b, 0xa0, 0x42, 0x5e, 0x95, 0xe6, 0x08, 0x3e, 0x01, 0xf8, 0x82, 0x90, 0x81, 0xd4, 0x70, 0xb5, 0xb2, 0x8c, 0x64, 0xa9, 0x56, 0xdd, 0xc2, 0xda, 0xe1, 0xd3, 0xad, 0xf8, 0x5b, 0x99, 0x0b, 0x19, 0x5e, 0x88, 0x0d, 0x81, 0x04, 0x4d, 0xc1, 0x43, 0x41, 0xf1, 0xd3, 0x45, 0x65, 0x62, 0x70, 0x2f, 0xfa, 0x62, 0xbe, 0x7d, 0xf4, 0x94, 0x91, 0xe0, 0xbb, 0xb1, 0xbc, 0xe5, 0x27, 0xc8, 0x15, 0xd4, 0xcb, 0x82, 0x97, 0x15, 0x46, 0x82, 0xbb, 0x48, 0xbb, 0x16, 0x25, 0xbe, 0x82, 0xe4, 0x27, 0x80, 0xf3, 0xc2, 0x92, 0x3b, 0xd6, 0xc3, 0x65, 0x20, 0xec, 0x50, 0xdb, 0x6a, 0xcb, 0x47, 0x73, 0xf7, 0x98, 0xf1, 0x66, 0x5e, 0xc4, 0xe9, 0x87, 0xf8, 0xcb, 0x1e, 0x06, 0xa7, 0x67, 0xf5, 0xec, 0x73, 0xe5, 0xc7, 0x4d, 0xc2, 0x90, 0xe4, 0xdf, 0x9d, 0x1f, 0x05, 0x67, 0x99, 0xd6, 0xf0, 0xc4, 0x20, 0xbc, 0xf8, 0xf5, 0x3e, 0x19, 0xe9, 0x3a, 0x12, 0xe1, 0xcc, 0x9f, 0x81, 0x55, 0x1e, 0xad, 0xc8, 0xa3, 0xe5, 0x98, 0xbe, 0xe0, 0x4d, 0xb7, 0x6b, 0xd5, 0xbe, 0x6a, 0x3d, 0x76, 0xb6, 0xe2, 0xa5, 0xa7, 0x96, 0x68, 0xeb, 0x91, 0xee, 0x02, 0xfc, 0xe4, 0x01, 0xc3, 0x24, 0xda, 0x4c, 0xff, 0x10, 0x27, 0x78, 0xb0, 0x0b, 0x55, 0x5c, 0xce, 0x62, 0x7d, 0x33, 0x2b, 0x25, 0x99, 0xaa, 0x99, 0xea, 0xa3, 0x1d, 0xd8, 0x2b, 0x57, 0xb5, 0xe4, 0x04, 0x21, 0x75, 0xd9, 0xc4, 0xd0, 0x3d, 0xa1, 0xa5, 0x8f }; uint8_t cp_pld[] = { 0x2f, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x00, /* REQUEST */ 0x00, 0x01, 0x00, 0x00, /* INTERNAL_IP4_ADDRESS */ 0x2f, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, /* REPLY */ 0x00, 0x01, 0x00, 0x04, /* INTERNAL_IP4_ADDRESS */ 0xaa, 0xbb, 0xcc, 0xdd, /* 170.187.204.221 */ 0x2f, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x00, /* SET (empty) */ 0x2f, 0x00, 0x00, 0x24, 0x02, 0x00, 0x00, 0x00, /* REPLY */ 0x00, 0x01, 0x00, 0x04, /* INTERNAL_IP4_ADDRESS */ 0xaa, 0xaa, 0xaa, 0xaa, /* 170.170.170.170 */ 0x00, 0x02, 0x00, 0x04, /* INTERNAL_IP4_NETMASK */ 0xbb, 0xbb, 0xbb, 0xbb, /* 187.187.187.187 */ 0x00, 0x03, 0x00, 0x04, /* INTERNAL_IP4_DNS */ 0xcc, 0xcc, 0xcc, 0xcc, /* 204.204.204.204 */ 0x00, 0x08, 0x00, 0x00, /* INTERNAL_IP6_ADDRESS */ 0x00, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x00, /* ACK (empty) */ }; uint8_t eap_pld[] = { 0x30, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x05, 0x01, 0x30, 0x00, 0x00, 0x0c, 0x02, 0x00, 0x00, 0x05, 0x01, 0xfa, 0xfb, 0xfc, 0x30, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x04 }; /* Valid initator packet */ uint8_t valid_packet[] = { 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x20, 0x22, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x22, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3c, 0x01, 0x01, 0x00, 0x06, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x0c, 0x80, 0x0e, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x0e, 0x03, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x01, 0x28, 0x00, 0x01, 0x08, 0x00, 0x0e, 0x00, 0x00, 0x16, 0xcb, 0x68, 0xaf, 0x63, 0xfe, 0xb0, 0x58, 0x49, 0x0e, 0x7f, 0x85, 0x60, 0x53, 0x80, 0xae, 0x3f, 0x82, 0xf3, 0x35, 0x21, 0xd5, 0xae, 0x09, 0x1c, 0xfa, 0x68, 0xc2, 0xfb, 0x4b, 0xb3, 0x84, 0xda, 0xaf, 0x6e, 0xe2, 0x5e, 0xc5, 0xb6, 0x8c, 0x35, 0x3c, 0xec, 0x58, 0x7f, 0xa9, 0xf8, 0xa4, 0x24, 0xf3, 0xf8, 0xf4, 0x65, 0x59, 0x8c, 0x15, 0x4d, 0x2c, 0xf1, 0x5d, 0xeb, 0x57, 0x68, 0xfe, 0x75, 0x61, 0x5a, 0x80, 0x96, 0xa4, 0x0a, 0xad, 0x75, 0x71, 0xd8, 0xe0, 0x06, 0xbc, 0xde, 0x16, 0x6d, 0x1e, 0xd9, 0x5d, 0x2c, 0x00, 0x66, 0x43, 0x82, 0xe4, 0x6f, 0x5f, 0x95, 0xe7, 0x9b, 0xfd, 0xf2, 0xe2, 0xcb, 0xc5, 0xf1, 0x52, 0xdd, 0x3b, 0xed, 0x88, 0xd4, 0xa9, 0x13, 0x4e, 0x42, 0xe8, 0x60, 0x2d, 0x3c, 0xf6, 0xc8, 0xf0, 0x70, 0x42, 0xfa, 0x33, 0x7f, 0x28, 0xdf, 0x6b, 0x79, 0x2c, 0x79, 0x8f, 0xc0, 0x5d, 0x81, 0x7a, 0x62, 0xdb, 0xd4, 0x44, 0x3a, 0x3c, 0x21, 0xbf, 0x85, 0xc8, 0x0b, 0x8c, 0x77, 0x72, 0xe9, 0xfb, 0x50, 0x5c, 0x03, 0xa6, 0xb2, 0x3f, 0x17, 0x4a, 0xd1, 0xb3, 0x01, 0x30, 0xad, 0xe4, 0xfa, 0xe2, 0xba, 0x6f, 0x22, 0x83, 0xf4, 0xde, 0x38, 0x43, 0xe8, 0x27, 0x00, 0xb8, 0x95, 0xbe, 0x03, 0x8f, 0xcd, 0xd3, 0x72, 0xed, 0xa5, 0xed, 0x8d, 0xf4, 0x68, 0x98, 0xef, 0x59, 0xcc, 0xfb, 0x54, 0x89, 0xde, 0xa9, 0xd4, 0x88, 0xcd, 0xb9, 0xca, 0x09, 0xd3, 0xd5, 0x25, 0xb1, 0x8c, 0x58, 0x12, 0x9c, 0x69, 0x03, 0x72, 0x00, 0xc9, 0xca, 0x95, 0x8a, 0xce, 0x0d, 0xd2, 0xc8, 0x25, 0xe7, 0x7c, 0xed, 0x5e, 0xee, 0x35, 0x01, 0xfc, 0x00, 0x56, 0xed, 0xf3, 0x8d, 0x81, 0x6c, 0x3e, 0x86, 0x6a, 0x40, 0xac, 0xc7, 0x9c, 0x7a, 0xbf, 0x9f, 0x8e, 0x1f, 0xd8, 0x60, 0x29, 0x00, 0x00, 0x24, 0x5f, 0x61, 0x42, 0x72, 0x7d, 0xb2, 0xa8, 0xc1, 0xfe, 0xb1, 0x38, 0x2e, 0xb8, 0x75, 0xa7, 0xc1, 0x1d, 0x8a, 0xa7, 0xb7, 0x9b, 0x92, 0xe2, 0x0e, 0x3a, 0x18, 0x20, 0xb6, 0x16, 0xf3, 0x35, 0x67, 0x29, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x40, 0x04, 0xc7, 0xa0, 0x68, 0x68, 0x09, 0x0a, 0x7f, 0x12, 0x0b, 0x13, 0xd3, 0x2f, 0xde, 0x64, 0x8b, 0xf1, 0xc3, 0x3c, 0x79, 0x8f, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x40, 0x05, 0x9f, 0xbc, 0x8c, 0xd0, 0x91, 0x5e, 0xa0, 0x87, 0x81, 0xab, 0x4f, 0xa1, 0x8a, 0xa7, 0xa8, 0xf9, 0xeb, 0xdf, 0x9f, 0x2c }; #define OFFSET_ICOOKIE 0 #define OFFSET_RCOOKIE 8 #define OFFSET_NEXTPAYLOAD (0 + sizeof(cookies)) #define OFFSET_VERSION (1 + sizeof(cookies)) #define OFFSET_EXCHANGE (2 + sizeof(cookies)) #define OFFSET_LENGTH (8 + sizeof(cookies)) static uint8_t * get_icookie(uint8_t *data) { return &data[OFFSET_ICOOKIE]; } static uint8_t * get_rcookie(uint8_t *data) { return &data[OFFSET_RCOOKIE]; } static uint8_t get_nextpayload(uint8_t *data) { return data[OFFSET_NEXTPAYLOAD]; } static uint8_t get_version(uint8_t *data) { return data[OFFSET_VERSION]; } static uint8_t get_exchange(uint8_t *data) { return data[OFFSET_EXCHANGE]; } static uint32_t get_length(uint8_t *data) { return *(uint32_t *)&data[OFFSET_LENGTH]; } static void set_length(uint8_t *data, uint32_t length) { uint32_t *p; p = (uint32_t *)&data[OFFSET_LENGTH]; *p = htobe32(length); } static void set_nextpayload(uint8_t *data, uint8_t next) { data[OFFSET_NEXTPAYLOAD] = next; } static void prepare_header(struct ike_header *hdr, struct ibuf *data) { bzero(hdr, sizeof(*hdr)); bcopy(get_icookie(ibuf_data(data)), &hdr->ike_ispi, sizeof(hdr->ike_ispi)); bcopy(get_rcookie(ibuf_data(data)), &hdr->ike_rspi, sizeof(hdr->ike_rspi)); hdr->ike_nextpayload = get_nextpayload(ibuf_data(data)); hdr->ike_version = get_version(ibuf_data(data)); hdr->ike_exchange = get_exchange(ibuf_data(data)); hdr->ike_length = get_length(ibuf_data(data)); } static void prepare_message(struct iked_message *msg, struct ibuf *data) { static struct iked_sa sa; bzero(&sa, sizeof(sa)); bzero(msg, sizeof(*msg)); msg->msg_sa = &sa; msg->msg_data = data; msg->msg_e = 1; msg->msg_parent = msg; } static void perform_test(struct fuzz *fuzz) { struct ibuf *fuzzed; struct ike_header hdr; struct iked_message msg; bzero(&hdr, sizeof(hdr)); bzero(&msg, sizeof(msg)); for (; !fuzz_done(fuzz); fuzz_next(fuzz)) { ASSERT_PTR_NE(fuzzed = ibuf_new(fuzz_ptr(fuzz), fuzz_len(fuzz)), NULL); print_hex(ibuf_data(fuzzed), 0, ibuf_size(fuzzed)); /* We need at least cookies and generic header. */ if (ibuf_size(fuzzed) < sizeof(cookies) + sizeof(genhdr)) { ibuf_free(fuzzed); continue; } prepare_header(&hdr, fuzzed); prepare_message(&msg, fuzzed); ikev2_pld_parse(NULL, &hdr, &msg, 0); ibuf_free(fuzzed); } } void parser_fuzz_tests(void) { struct fuzz *fuzz; struct ike_header hdr; struct iked_message msg; struct ibuf *data; log_init(1, LOG_DAEMON); log_setverbose(0); TEST_START("fuzz generic header"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); set_length(ibuf_data(data), ibuf_size(data)); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz skf_1of1 payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, skf_1of1_pld, sizeof(skf_1of1_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_SKF); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz sa payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, sa_pld, sizeof(sa_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_SA); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz sa and xform payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, saxform_pld, sizeof(saxform_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_SA); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz ke payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, ke_pld, sizeof(ke_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_KE); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz nonce payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, nonce_pld, sizeof(nonce_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_NONCE); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz notify payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, notify_pld, sizeof(notify_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_NOTIFY); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz id payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, id_pld, sizeof(id_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_IDi); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz cert payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, cert_pld, sizeof(cert_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_CERT); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz certreq payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, certreq_pld, sizeof(certreq_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_CERTREQ); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz auth payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, auth_pld, sizeof(auth_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_AUTH); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz delete notify payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, delete_pld, sizeof(delete_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_DELETE); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz vendor id payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, vendor_pld, sizeof(vendor_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_VENDOR); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz traffic selector initiator payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, ts_pld, sizeof(ts_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_TSi); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz traffic selector responder payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, ts_pld, sizeof(ts_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_TSr); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz configuration payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, cp_pld, sizeof(cp_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_CP); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz eap payload"); ASSERT_PTR_NE(data = ibuf_new(cookies, sizeof(cookies)), NULL); ASSERT_INT_EQ(ibuf_add(data, genhdr, sizeof(genhdr)), 0); ASSERT_INT_EQ(ibuf_add(data, eap_pld, sizeof(eap_pld)), 0); set_length(ibuf_data(data), ibuf_size(data)); set_nextpayload(ibuf_data(data), IKEV2_PAYLOAD_EAP); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); TEST_START("fuzz full valid packet"); ASSERT_PTR_NE(data = ibuf_new(valid_packet, sizeof(valid_packet)), NULL); set_length(ibuf_data(data), ibuf_size(data)); print_hex(ibuf_data(data), 0, ibuf_size(data)); prepare_header(&hdr, data); prepare_message(&msg, data); ASSERT_INT_EQ(ikev2_pld_parse(NULL, &hdr, &msg, 0), 0); fuzz = fuzz_begin(FUZZ_1_BIT_FLIP | FUZZ_2_BIT_FLIP | FUZZ_1_BYTE_FLIP | FUZZ_2_BYTE_FLIP | FUZZ_TRUNCATE_START | FUZZ_TRUNCATE_END | FUZZ_BASE64, ibuf_data(data), ibuf_size(data)); ibuf_free(data); perform_test(fuzz); TEST_DONE(); } openiked-7.4/regress/parser/tests.c000066400000000000000000000003661477554731100174610ustar00rootroot00000000000000/* $OpenBSD: tests.c,v 1.1 2017/05/29 20:59:28 markus Exp $ */ /* * Regress test for iked payload parser * * Placed in the public domain */ #include "test_helper.h" void parser_fuzz_tests(void); void tests(void) { parser_fuzz_tests(); } openiked-7.4/regress/test_helper/000077500000000000000000000000001477554731100171705ustar00rootroot00000000000000openiked-7.4/regress/test_helper/CMakeLists.txt000066400000000000000000000017431477554731100217350ustar00rootroot00000000000000# Copyright (c) 2020-2021 Tobias Heider # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. set(SRCS) list(APPEND SRCS test_helper.c fuzz.c ) add_library(test_helper ${SRCS}) target_include_directories(test_helper PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(test_helper PRIVATE compat ) openiked-7.4/regress/test_helper/Makefile000066400000000000000000000002661477554731100206340ustar00rootroot00000000000000# $OpenBSD: Makefile,v 1.1 2017/05/29 20:59:32 markus Exp $ LIB= test_helper SRCS= test_helper.c DEBUGLIBS= no NOPROFILE= yes NOPIC= yes install: @echo -n .include openiked-7.4/regress/test_helper/fuzz.c000066400000000000000000000216451477554731100203420ustar00rootroot00000000000000/* $OpenBSD */ /* * Copyright (c) 2011 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Utility functions/framework for fuzz tests */ #include #include #include #include #include #include #include #include "test_helper.h" /* #define FUZZ_DEBUG */ #ifdef FUZZ_DEBUG # define FUZZ_DBG(x) do { \ printf("%s:%d %s: ", __FILE__, __LINE__, __func__); \ printf x; \ printf("\n"); \ fflush(stdout); \ } while (0) #else # define FUZZ_DBG(x) #endif /* For brevity later */ typedef unsigned long long fuzz_ullong; /* For base-64 fuzzing */ static const char fuzz_b64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; struct fuzz { /* Fuzz method currently in use */ int strategy; /* Fuzz methods remaining */ int strategies; /* Original seed data blob */ void *seed; size_t slen; /* Current working copy of seed with fuzz mutations applied */ u_char *fuzzed; /* Used by fuzz methods */ size_t o1, o2; }; static const char * fuzz_ntop(u_int n) { switch (n) { case 0: return "NONE"; case FUZZ_1_BIT_FLIP: return "FUZZ_1_BIT_FLIP"; case FUZZ_2_BIT_FLIP: return "FUZZ_2_BIT_FLIP"; case FUZZ_1_BYTE_FLIP: return "FUZZ_1_BYTE_FLIP"; case FUZZ_2_BYTE_FLIP: return "FUZZ_2_BYTE_FLIP"; case FUZZ_TRUNCATE_START: return "FUZZ_TRUNCATE_START"; case FUZZ_TRUNCATE_END: return "FUZZ_TRUNCATE_END"; case FUZZ_BASE64: return "FUZZ_BASE64"; default: abort(); } } void fuzz_dump(struct fuzz *fuzz) { u_char *p = fuzz_ptr(fuzz); size_t i, j, len = fuzz_len(fuzz); switch (fuzz->strategy) { case FUZZ_1_BIT_FLIP: fprintf(stderr, "%s case %zu of %zu (bit: %zu)\n", fuzz_ntop(fuzz->strategy), fuzz->o1, fuzz->slen * 8, fuzz->o1); break; case FUZZ_2_BIT_FLIP: fprintf(stderr, "%s case %llu of %llu (bits: %zu, %zu)\n", fuzz_ntop(fuzz->strategy), (((fuzz_ullong)fuzz->o2) * fuzz->slen * 8) + fuzz->o1, ((fuzz_ullong)fuzz->slen * 8) * fuzz->slen * 8, fuzz->o1, fuzz->o2); break; case FUZZ_1_BYTE_FLIP: fprintf(stderr, "%s case %zu of %zu (byte: %zu)\n", fuzz_ntop(fuzz->strategy), fuzz->o1, fuzz->slen, fuzz->o1); break; case FUZZ_2_BYTE_FLIP: fprintf(stderr, "%s case %llu of %llu (bytes: %zu, %zu)\n", fuzz_ntop(fuzz->strategy), (((fuzz_ullong)fuzz->o2) * fuzz->slen) + fuzz->o1, ((fuzz_ullong)fuzz->slen) * fuzz->slen, fuzz->o1, fuzz->o2); break; case FUZZ_TRUNCATE_START: fprintf(stderr, "%s case %zu of %zu (offset: %zu)\n", fuzz_ntop(fuzz->strategy), fuzz->o1, fuzz->slen, fuzz->o1); break; case FUZZ_TRUNCATE_END: fprintf(stderr, "%s case %zu of %zu (offset: %zu)\n", fuzz_ntop(fuzz->strategy), fuzz->o1, fuzz->slen, fuzz->o1); break; case FUZZ_BASE64: assert(fuzz->o2 < sizeof(fuzz_b64chars) - 1); fprintf(stderr, "%s case %llu of %llu (offset: %zu char: %c)\n", fuzz_ntop(fuzz->strategy), (fuzz->o1 * (fuzz_ullong)64) + fuzz->o2, fuzz->slen * (fuzz_ullong)64, fuzz->o1, fuzz_b64chars[fuzz->o2]); break; default: abort(); } fprintf(stderr, "fuzz context %p len = %zu\n", fuzz, len); for (i = 0; i < len; i += 16) { fprintf(stderr, "%.4zd: ", i); for (j = i; j < i + 16; j++) { if (j < len) fprintf(stderr, "%02x ", p[j]); else fprintf(stderr, " "); } fprintf(stderr, " "); for (j = i; j < i + 16; j++) { if (j < len) { if (isascii(p[j]) && isprint(p[j])) fprintf(stderr, "%c", p[j]); else fprintf(stderr, "."); } } fprintf(stderr, "\n"); } } struct fuzz * fuzz_begin(u_int strategies, void *p, size_t l) { struct fuzz *ret = calloc(sizeof(*ret), 1); assert(p != NULL); assert(ret != NULL); ret->seed = malloc(l); assert(ret->seed != NULL); memcpy(ret->seed, p, l); ret->slen = l; ret->strategies = strategies; assert(ret->slen < SIZE_MAX / 8); assert(ret->strategies <= (FUZZ_MAX|(FUZZ_MAX-1))); FUZZ_DBG(("begin, ret = %p", ret)); fuzz_next(ret); return ret; } void fuzz_cleanup(struct fuzz *fuzz) { FUZZ_DBG(("cleanup, fuzz = %p", fuzz)); assert(fuzz != NULL); assert(fuzz->seed != NULL); assert(fuzz->fuzzed != NULL); free(fuzz->seed); free(fuzz->fuzzed); free(fuzz); } static int fuzz_strategy_done(struct fuzz *fuzz) { FUZZ_DBG(("fuzz = %p, strategy = %s, o1 = %zu, o2 = %zu, slen = %zu", fuzz, fuzz_ntop(fuzz->strategy), fuzz->o1, fuzz->o2, fuzz->slen)); switch (fuzz->strategy) { case FUZZ_1_BIT_FLIP: return fuzz->o1 >= fuzz->slen * 8; case FUZZ_2_BIT_FLIP: return fuzz->o2 >= fuzz->slen * 8; case FUZZ_2_BYTE_FLIP: return fuzz->o2 >= fuzz->slen; case FUZZ_1_BYTE_FLIP: case FUZZ_TRUNCATE_START: case FUZZ_TRUNCATE_END: case FUZZ_BASE64: return fuzz->o1 >= fuzz->slen; default: abort(); } } void fuzz_next(struct fuzz *fuzz) { u_int i; FUZZ_DBG(("start, fuzz = %p, strategy = %s, strategies = 0x%lx, " "o1 = %zu, o2 = %zu, slen = %zu", fuzz, fuzz_ntop(fuzz->strategy), (u_long)fuzz->strategies, fuzz->o1, fuzz->o2, fuzz->slen)); if (fuzz->strategy == 0 || fuzz_strategy_done(fuzz)) { /* If we are just starting out, we need to allocate too */ if (fuzz->fuzzed == NULL) { FUZZ_DBG(("alloc")); fuzz->fuzzed = calloc(fuzz->slen, 1); } /* Pick next strategy */ FUZZ_DBG(("advance")); for (i = 1; i <= FUZZ_MAX; i <<= 1) { if ((fuzz->strategies & i) != 0) { fuzz->strategy = i; break; } } FUZZ_DBG(("selected = %u", fuzz->strategy)); if (fuzz->strategy == 0) { FUZZ_DBG(("done, no more strategies")); return; } fuzz->strategies &= ~(fuzz->strategy); fuzz->o1 = fuzz->o2 = 0; } assert(fuzz->fuzzed != NULL); switch (fuzz->strategy) { case FUZZ_1_BIT_FLIP: assert(fuzz->o1 / 8 < fuzz->slen); memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen); fuzz->fuzzed[fuzz->o1 / 8] ^= 1 << (fuzz->o1 % 8); fuzz->o1++; break; case FUZZ_2_BIT_FLIP: assert(fuzz->o1 / 8 < fuzz->slen); assert(fuzz->o2 / 8 < fuzz->slen); memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen); fuzz->fuzzed[fuzz->o1 / 8] ^= 1 << (fuzz->o1 % 8); fuzz->fuzzed[fuzz->o2 / 8] ^= 1 << (fuzz->o2 % 8); fuzz->o1++; if (fuzz->o1 >= fuzz->slen * 8) { fuzz->o1 = 0; fuzz->o2++; } break; case FUZZ_1_BYTE_FLIP: assert(fuzz->o1 < fuzz->slen); memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen); fuzz->fuzzed[fuzz->o1] ^= 0xff; fuzz->o1++; break; case FUZZ_2_BYTE_FLIP: assert(fuzz->o1 < fuzz->slen); assert(fuzz->o2 < fuzz->slen); memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen); fuzz->fuzzed[fuzz->o1] ^= 0xff; fuzz->fuzzed[fuzz->o2] ^= 0xff; fuzz->o1++; if (fuzz->o1 >= fuzz->slen) { fuzz->o1 = 0; fuzz->o2++; } break; case FUZZ_TRUNCATE_START: case FUZZ_TRUNCATE_END: assert(fuzz->o1 < fuzz->slen); memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen); fuzz->o1++; break; case FUZZ_BASE64: assert(fuzz->o1 < fuzz->slen); assert(fuzz->o2 < sizeof(fuzz_b64chars) - 1); memcpy(fuzz->fuzzed, fuzz->seed, fuzz->slen); fuzz->fuzzed[fuzz->o1] = fuzz_b64chars[fuzz->o2]; fuzz->o2++; if (fuzz->o2 >= sizeof(fuzz_b64chars) - 1) { fuzz->o2 = 0; fuzz->o1++; } break; default: abort(); } FUZZ_DBG(("done, fuzz = %p, strategy = %s, strategies = 0x%lx, " "o1 = %zu, o2 = %zu, slen = %zu", fuzz, fuzz_ntop(fuzz->strategy), (u_long)fuzz->strategies, fuzz->o1, fuzz->o2, fuzz->slen)); } int fuzz_done(struct fuzz *fuzz) { FUZZ_DBG(("fuzz = %p, strategies = 0x%lx", fuzz, (u_long)fuzz->strategies)); return fuzz_strategy_done(fuzz) && fuzz->strategies == 0; } size_t fuzz_len(struct fuzz *fuzz) { assert(fuzz->fuzzed != NULL); switch (fuzz->strategy) { case FUZZ_1_BIT_FLIP: case FUZZ_2_BIT_FLIP: case FUZZ_1_BYTE_FLIP: case FUZZ_2_BYTE_FLIP: case FUZZ_BASE64: return fuzz->slen; case FUZZ_TRUNCATE_START: case FUZZ_TRUNCATE_END: assert(fuzz->o1 <= fuzz->slen); return fuzz->slen - fuzz->o1; default: abort(); } } u_char * fuzz_ptr(struct fuzz *fuzz) { assert(fuzz->fuzzed != NULL); switch (fuzz->strategy) { case FUZZ_1_BIT_FLIP: case FUZZ_2_BIT_FLIP: case FUZZ_1_BYTE_FLIP: case FUZZ_2_BYTE_FLIP: case FUZZ_BASE64: return fuzz->fuzzed; case FUZZ_TRUNCATE_START: assert(fuzz->o1 <= fuzz->slen); return fuzz->fuzzed + fuzz->o1; case FUZZ_TRUNCATE_END: assert(fuzz->o1 <= fuzz->slen); return fuzz->fuzzed; default: abort(); } } openiked-7.4/regress/test_helper/test_helper.c000066400000000000000000000235661477554731100216660ustar00rootroot00000000000000/* $OpenBSD */ /* * Copyright (c) 2011 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Utility functions/framework for regress tests */ #include #include #include #include #include #include #include #include #include #include #include #include "test_helper.h" #define TEST_CHECK_INT(r, pred) do { \ switch (pred) { \ case TEST_EQ: \ if (r == 0) \ return; \ break; \ case TEST_NE: \ if (r != 0) \ return; \ break; \ case TEST_LT: \ if (r < 0) \ return; \ break; \ case TEST_LE: \ if (r <= 0) \ return; \ break; \ case TEST_GT: \ if (r > 0) \ return; \ break; \ case TEST_GE: \ if (r >= 0) \ return; \ break; \ default: \ abort(); \ } \ } while (0) #define TEST_CHECK(x1, x2, pred) do { \ switch (pred) { \ case TEST_EQ: \ if (x1 == x2) \ return; \ break; \ case TEST_NE: \ if (x1 != x2) \ return; \ break; \ case TEST_LT: \ if (x1 < x2) \ return; \ break; \ case TEST_LE: \ if (x1 <= x2) \ return; \ break; \ case TEST_GT: \ if (x1 > x2) \ return; \ break; \ case TEST_GE: \ if (x1 >= x2) \ return; \ break; \ default: \ abort(); \ } \ } while (0) extern char *__progname; static int verbose_mode = 0; static int quiet_mode = 0; static char *active_test_name = NULL; static u_int test_number = 0; static test_onerror_func_t *test_onerror = NULL; static void *onerror_ctx = NULL; static const char *data_dir = NULL; int main(int argc, char **argv) { int ch; while ((ch = getopt(argc, argv, "vqd:")) != -1) { switch (ch) { case 'd': data_dir = optarg; break; case 'q': verbose_mode = 0; quiet_mode = 1; break; case 'v': verbose_mode = 1; quiet_mode = 0; break; default: fprintf(stderr, "Unrecognised command line option\n"); fprintf(stderr, "Usage: %s [-v]\n", __progname); exit(1); } } setvbuf(stdout, NULL, _IONBF, 0); if (!quiet_mode) printf("%s: ", __progname); if (verbose_mode) printf("\n"); tests(); if (!quiet_mode) printf(" %u tests ok\n", test_number); return 0; } const char * test_data_file(const char *name) { static char ret[PATH_MAX]; if (data_dir != NULL) snprintf(ret, sizeof(ret), "%s/%s", data_dir, name); else strlcpy(ret, name, sizeof(ret)); if (access(ret, F_OK) != 0) { fprintf(stderr, "Cannot access data file %s: %s\n", ret, strerror(errno)); exit(1); } return ret; } void test_start(const char *n) { assert(active_test_name == NULL); assert((active_test_name = strdup(n)) != NULL); if (verbose_mode) printf("test %u - \"%s\": ", test_number, active_test_name); test_number++; } void set_onerror_func(test_onerror_func_t *f, void *ctx) { test_onerror = f; onerror_ctx = ctx; } void test_done(void) { assert(active_test_name != NULL); free(active_test_name); active_test_name = NULL; if (verbose_mode) printf("OK\n"); else if (!quiet_mode) { printf("."); fflush(stdout); } } static const char * pred_name(enum test_predicate p) { switch (p) { case TEST_EQ: return "EQ"; case TEST_NE: return "NE"; case TEST_LT: return "LT"; case TEST_LE: return "LE"; case TEST_GT: return "GT"; case TEST_GE: return "GE"; default: return "UNKNOWN"; } } static void test_die(void) { if (test_onerror != NULL) test_onerror(onerror_ctx); abort(); } static void test_header(const char *file, int line, const char *a1, const char *a2, const char *name, enum test_predicate pred) { fprintf(stderr, "\n%s:%d test #%u \"%s\"\n", file, line, test_number, active_test_name); fprintf(stderr, "ASSERT_%s_%s(%s%s%s) failed:\n", name, pred_name(pred), a1, a2 != NULL ? ", " : "", a2 != NULL ? a2 : ""); } void assert_string(const char *file, int line, const char *a1, const char *a2, const char *aa1, const char *aa2, enum test_predicate pred) { int r = strcmp(aa1, aa2); TEST_CHECK_INT(r, pred); test_header(file, line, a1, a2, "STRING", pred); fprintf(stderr, "%12s = %s (len %zu)\n", a1, aa1, strlen(aa1)); fprintf(stderr, "%12s = %s (len %zu)\n", a2, aa2, strlen(aa2)); test_die(); } static char * tohex(const void *_s, size_t l) { uint8_t *s = (uint8_t *)_s; size_t i, j; const char *hex = "0123456789abcdef"; char *r = malloc((l * 2) + 1); assert(r != NULL); for (i = j = 0; i < l; i++) { r[j++] = hex[(s[i] >> 4) & 0xf]; r[j++] = hex[s[i] & 0xf]; } r[j] = '\0'; return r; } void assert_mem(const char *file, int line, const char *a1, const char *a2, const void *aa1, const void *aa2, size_t l, enum test_predicate pred) { int r = memcmp(aa1, aa2, l); TEST_CHECK_INT(r, pred); test_header(file, line, a1, a2, "STRING", pred); fprintf(stderr, "%12s = %s (len %zu)\n", a1, tohex(aa1, MINIMUM(l, 256)), l); fprintf(stderr, "%12s = %s (len %zu)\n", a2, tohex(aa2, MINIMUM(l, 256)), l); test_die(); } static int memvalcmp(const uint8_t *s, u_char v, size_t l, size_t *where) { size_t i; for (i = 0; i < l; i++) { if (s[i] != v) { *where = i; return 1; } } return 0; } void assert_mem_filled(const char *file, int line, const char *a1, const void *aa1, u_char v, size_t l, enum test_predicate pred) { size_t where = -1; int r = memvalcmp(aa1, v, l, &where); char tmp[64]; if (l == 0) return; TEST_CHECK_INT(r, pred); test_header(file, line, a1, NULL, "MEM_ZERO", pred); fprintf(stderr, "%20s = %s%s (len %zu)\n", a1, tohex(aa1, MINIMUM(l, 20)), l > 20 ? "..." : "", l); snprintf(tmp, sizeof(tmp), "(%s)[%zu]", a1, where); fprintf(stderr, "%20s = 0x%02x (expected 0x%02x)\n", tmp, ((u_char *)aa1)[where], v); test_die(); } void assert_int(const char *file, int line, const char *a1, const char *a2, int aa1, int aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "INT", pred); fprintf(stderr, "%12s = %d\n", a1, aa1); fprintf(stderr, "%12s = %d\n", a2, aa2); test_die(); } void assert_size_t(const char *file, int line, const char *a1, const char *a2, size_t aa1, size_t aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "SIZE_T", pred); fprintf(stderr, "%12s = %zu\n", a1, aa1); fprintf(stderr, "%12s = %zu\n", a2, aa2); test_die(); } void assert_u_int(const char *file, int line, const char *a1, const char *a2, u_int aa1, u_int aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "U_INT", pred); fprintf(stderr, "%12s = %u / 0x%x\n", a1, aa1, aa1); fprintf(stderr, "%12s = %u / 0x%x\n", a2, aa2, aa2); test_die(); } void assert_long_long(const char *file, int line, const char *a1, const char *a2, long long aa1, long long aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "LONG LONG", pred); fprintf(stderr, "%12s = %lld / 0x%llx\n", a1, aa1, aa1); fprintf(stderr, "%12s = %lld / 0x%llx\n", a2, aa2, aa2); test_die(); } void assert_char(const char *file, int line, const char *a1, const char *a2, char aa1, char aa2, enum test_predicate pred) { char buf[8]; TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "CHAR", pred); fprintf(stderr, "%12s = '%s' / 0x02%x\n", a1, vis(buf, aa1, VIS_SAFE|VIS_NL|VIS_TAB|VIS_OCTAL, 0), aa1); fprintf(stderr, "%12s = '%s' / 0x02%x\n", a1, vis(buf, aa2, VIS_SAFE|VIS_NL|VIS_TAB|VIS_OCTAL, 0), aa2); test_die(); } void assert_u8(const char *file, int line, const char *a1, const char *a2, uint8_t aa1, uint8_t aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "U8", pred); fprintf(stderr, "%12s = 0x%02x %u\n", a1, aa1, aa1); fprintf(stderr, "%12s = 0x%02x %u\n", a2, aa2, aa2); test_die(); } void assert_u16(const char *file, int line, const char *a1, const char *a2, uint16_t aa1, uint16_t aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "U16", pred); fprintf(stderr, "%12s = 0x%04x %u\n", a1, aa1, aa1); fprintf(stderr, "%12s = 0x%04x %u\n", a2, aa2, aa2); test_die(); } void assert_u32(const char *file, int line, const char *a1, const char *a2, uint32_t aa1, uint32_t aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "U32", pred); fprintf(stderr, "%12s = 0x%08x %u\n", a1, aa1, aa1); fprintf(stderr, "%12s = 0x%08x %u\n", a2, aa2, aa2); test_die(); } void assert_u64(const char *file, int line, const char *a1, const char *a2, uint64_t aa1, uint64_t aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "U64", pred); fprintf(stderr, "%12s = 0x%016llx %llu\n", a1, (unsigned long long)aa1, (unsigned long long)aa1); fprintf(stderr, "%12s = 0x%016llx %llu\n", a2, (unsigned long long)aa2, (unsigned long long)aa2); test_die(); } void assert_ptr(const char *file, int line, const char *a1, const char *a2, const void *aa1, const void *aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "PTR", pred); fprintf(stderr, "%12s = %p\n", a1, aa1); fprintf(stderr, "%12s = %p\n", a2, aa2); test_die(); } openiked-7.4/regress/test_helper/test_helper.h000066400000000000000000000264341477554731100216700ustar00rootroot00000000000000/* $OpenBSD */ /* * Copyright (c) 2011 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Utility functions/framework for regress tests */ #ifndef _TEST_HELPER_H #define _TEST_HELPER_H #include #include #include #include #define MINIMUM(a,b) (((a)<(b))?(a):(b)) enum test_predicate { TEST_EQ, TEST_NE, TEST_LT, TEST_LE, TEST_GT, TEST_GE }; typedef void (test_onerror_func_t)(void *); /* Supplied by test suite */ void tests(void); const char *test_data_file(const char *name); void test_start(const char *n); void set_onerror_func(test_onerror_func_t *f, void *ctx); void test_done(void); void assert_string(const char *file, int line, const char *a1, const char *a2, const char *aa1, const char *aa2, enum test_predicate pred); void assert_mem(const char *file, int line, const char *a1, const char *a2, const void *aa1, const void *aa2, size_t l, enum test_predicate pred); void assert_mem_filled(const char *file, int line, const char *a1, const void *aa1, u_char v, size_t l, enum test_predicate pred); void assert_int(const char *file, int line, const char *a1, const char *a2, int aa1, int aa2, enum test_predicate pred); void assert_size_t(const char *file, int line, const char *a1, const char *a2, size_t aa1, size_t aa2, enum test_predicate pred); void assert_u_int(const char *file, int line, const char *a1, const char *a2, u_int aa1, u_int aa2, enum test_predicate pred); void assert_long_long(const char *file, int line, const char *a1, const char *a2, long long aa1, long long aa2, enum test_predicate pred); void assert_char(const char *file, int line, const char *a1, const char *a2, char aa1, char aa2, enum test_predicate pred); void assert_ptr(const char *file, int line, const char *a1, const char *a2, const void *aa1, const void *aa2, enum test_predicate pred); void assert_u8(const char *file, int line, const char *a1, const char *a2, uint8_t aa1, uint8_t aa2, enum test_predicate pred); void assert_u16(const char *file, int line, const char *a1, const char *a2, uint16_t aa1, uint16_t aa2, enum test_predicate pred); void assert_u32(const char *file, int line, const char *a1, const char *a2, uint32_t aa1, uint32_t aa2, enum test_predicate pred); void assert_u64(const char *file, int line, const char *a1, const char *a2, uint64_t aa1, uint64_t aa2, enum test_predicate pred); #define TEST_START(n) test_start(n) #define TEST_DONE() test_done() #define TEST_ONERROR(f, c) set_onerror_func(f, c) #define ASSERT_STRING_EQ(a1, a2) \ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_MEM_EQ(a1, a2, l) \ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_EQ) #define ASSERT_MEM_FILLED_EQ(a1, c, l) \ assert_mem_filled(__FILE__, __LINE__, #a1, a1, c, l, TEST_EQ) #define ASSERT_MEM_ZERO_EQ(a1, l) \ assert_mem_filled(__FILE__, __LINE__, #a1, a1, '\0', l, TEST_EQ) #define ASSERT_INT_EQ(a1, a2) \ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_SIZE_T_EQ(a1, a2) \ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_U_INT_EQ(a1, a2) \ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_LONG_LONG_EQ(a1, a2) \ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_CHAR_EQ(a1, a2) \ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_PTR_EQ(a1, a2) \ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_U8_EQ(a1, a2) \ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_U16_EQ(a1, a2) \ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_U32_EQ(a1, a2) \ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_U64_EQ(a1, a2) \ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_STRING_NE(a1, a2) \ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_MEM_NE(a1, a2, l) \ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_NE) #define ASSERT_MEM_ZERO_NE(a1, l) \ assert_mem_filled(__FILE__, __LINE__, #a1, a1, '\0', l, TEST_NE) #define ASSERT_INT_NE(a1, a2) \ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_SIZE_T_NE(a1, a2) \ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_U_INT_NE(a1, a2) \ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_LONG_LONG_NE(a1, a2) \ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_CHAR_NE(a1, a2) \ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_PTR_NE(a1, a2) \ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_U8_NE(a1, a2) \ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_U16_NE(a1, a2) \ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_U32_NE(a1, a2) \ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_U64_NE(a1, a2) \ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_STRING_LT(a1, a2) \ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_MEM_LT(a1, a2, l) \ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_LT) #define ASSERT_INT_LT(a1, a2) \ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_SIZE_T_LT(a1, a2) \ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_U_INT_LT(a1, a2) \ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_LONG_LONG_LT(a1, a2) \ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_CHAR_LT(a1, a2) \ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_PTR_LT(a1, a2) \ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_U8_LT(a1, a2) \ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_U16_LT(a1, a2) \ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_U32_LT(a1, a2) \ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_U64_LT(a1, a2) \ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_STRING_LE(a1, a2) \ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_MEM_LE(a1, a2, l) \ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_LE) #define ASSERT_INT_LE(a1, a2) \ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_SIZE_T_LE(a1, a2) \ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_U_INT_LE(a1, a2) \ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_LONG_LONG_LE(a1, a2) \ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_CHAR_LE(a1, a2) \ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_PTR_LE(a1, a2) \ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_U8_LE(a1, a2) \ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_U16_LE(a1, a2) \ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_U32_LE(a1, a2) \ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_U64_LE(a1, a2) \ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_STRING_GT(a1, a2) \ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_MEM_GT(a1, a2, l) \ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_GT) #define ASSERT_INT_GT(a1, a2) \ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_SIZE_T_GT(a1, a2) \ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_U_INT_GT(a1, a2) \ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_LONG_LONG_GT(a1, a2) \ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_CHAR_GT(a1, a2) \ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_PTR_GT(a1, a2) \ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_U8_GT(a1, a2) \ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_U16_GT(a1, a2) \ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_U32_GT(a1, a2) \ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_U64_GT(a1, a2) \ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_STRING_GE(a1, a2) \ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_MEM_GE(a1, a2, l) \ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_GE) #define ASSERT_INT_GE(a1, a2) \ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_SIZE_T_GE(a1, a2) \ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_U_INT_GE(a1, a2) \ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_LONG_LONG_GE(a1, a2) \ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_CHAR_GE(a1, a2) \ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_PTR_GE(a1, a2) \ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_U8_GE(a1, a2) \ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_U16_GE(a1, a2) \ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_U32_GE(a1, a2) \ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_U64_GE(a1, a2) \ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) /* Fuzzing support */ struct fuzz; #define FUZZ_1_BIT_FLIP 0x00000001 /* Flip one bit at a time */ #define FUZZ_2_BIT_FLIP 0x00000002 /* Flip two bits at a time */ #define FUZZ_1_BYTE_FLIP 0x00000004 /* Flip one byte at a time */ #define FUZZ_2_BYTE_FLIP 0x00000008 /* Flip two bytes at a time */ #define FUZZ_TRUNCATE_START 0x00000010 /* Truncate from beginning */ #define FUZZ_TRUNCATE_END 0x00000020 /* Truncate from end */ #define FUZZ_BASE64 0x00000040 /* Try all base64 chars */ #define FUZZ_MAX FUZZ_BASE64 /* Start fuzzing a blob of data with selected strategies (bitmask) */ struct fuzz *fuzz_begin(u_int strategies, void *p, size_t l); /* Free a fuzz context */ void fuzz_cleanup(struct fuzz *fuzz); /* Prepare the next fuzz case in the series */ void fuzz_next(struct fuzz *fuzz); /* Determine whether the current fuzz sequence is exhausted (nonzero = yes) */ int fuzz_done(struct fuzz *fuzz); /* Return the length and a pointer to the current fuzzed case */ size_t fuzz_len(struct fuzz *fuzz); u_char *fuzz_ptr(struct fuzz *fuzz); /* Dump the current fuzz case to stderr */ void fuzz_dump(struct fuzz *fuzz); #endif /* _TEST_HELPER_H */ openiked-7.4/useradd.sh000077500000000000000000000001571477554731100151710ustar00rootroot00000000000000#!/bin/sh set -e groupadd _iked useradd -M -d /var/empty -s $(which nologin) -c "IKEv2 Daemon" -g _iked _iked